Compare commits
541 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b70f121e46 | ||
|
|
641765858f | ||
|
|
ba362e2fe0 | ||
|
|
84d5ce0d1e | ||
|
|
419623d158 | ||
|
|
1dd0c599c6 | ||
|
|
c7934ca246 | ||
|
|
27852eafd7 | ||
|
|
d4bde5008d | ||
|
|
74a525a672 | ||
|
|
e9982dc447 | ||
|
|
7028baaf8f | ||
|
|
7837c76c74 | ||
|
|
e5ad70c093 | ||
|
|
7fce688769 | ||
|
|
90c874777c | ||
|
|
70795134af | ||
|
|
8f2e9c6b94 | ||
|
|
ce34ca8f1f | ||
|
|
7c632cf165 | ||
|
|
b3c5a8db80 | ||
|
|
1d2961e047 | ||
|
|
b43f0979d9 | ||
|
|
539a1aee2c | ||
|
|
8e323b9dfa | ||
|
|
53b7d9eec9 | ||
|
|
89d3451767 | ||
|
|
aa98a7e7d6 | ||
|
|
614850ab1d | ||
|
|
140a15f9cb | ||
|
|
ec4a8745e7 | ||
|
|
673329ddb7 | ||
|
|
6a421dd8b0 | ||
|
|
de6c460a51 | ||
|
|
75a94fd0b3 | ||
|
|
7573c64087 | ||
|
|
cbb422f69d | ||
|
|
06f198bc57 | ||
|
|
6586657658 | ||
|
|
a0e598b97e | ||
|
|
8a6ea29c45 | ||
|
|
e1b0ba466b | ||
|
|
f35d6287ab | ||
|
|
53028a9c2a | ||
|
|
33fc2ed10c | ||
|
|
bc02fb3754 | ||
|
|
48b2379fe5 | ||
|
|
7de693eb23 | ||
|
|
7bb9c00356 | ||
|
|
9aacfff35d | ||
|
|
2e728c7051 | ||
|
|
7e973a6da6 | ||
|
|
cb014095ad | ||
|
|
6fe8f64835 | ||
|
|
6111f72b24 | ||
|
|
43041971b7 | ||
|
|
3505cc3ba0 | ||
|
|
05ba5f4358 | ||
|
|
bc616ca7d8 | ||
|
|
99131131af | ||
|
|
44b945a5f3 | ||
|
|
3bff923331 | ||
|
|
a6ccf95076 | ||
|
|
665f319a0e | ||
|
|
fe31afce6b | ||
|
|
ac07d2dfc8 | ||
|
|
71165c6984 | ||
|
|
eab3bff78b | ||
|
|
81dba11ab1 | ||
|
|
388fa5baa9 | ||
|
|
fe3241c07c | ||
|
|
0559ddca2a | ||
|
|
98a046500f | ||
|
|
58cd0d1669 | ||
|
|
6421fe10f8 | ||
|
|
99c85459a7 | ||
|
|
958bf51648 | ||
|
|
e3aa85e2a2 | ||
|
|
29ced36a79 | ||
|
|
f9a5c2d341 | ||
|
|
dde03718e1 | ||
|
|
60d34bea70 | ||
|
|
6fb3b61441 | ||
|
|
11ca168175 | ||
|
|
f02c0eacd8 | ||
|
|
72a566d2f8 | ||
|
|
b56ae28c45 | ||
|
|
cd89d71e0c | ||
|
|
902cd5c3ea | ||
|
|
f9eadc8e6a | ||
|
|
7b8c8fdda7 | ||
|
|
1f3de74cbd | ||
|
|
770ead9c05 | ||
|
|
fe371ff1d1 | ||
|
|
2f783f0aef | ||
|
|
92b85d4ba6 | ||
|
|
e702fe5c68 | ||
|
|
ed92e1b4f4 | ||
|
|
f2b63d1689 | ||
|
|
d51defed06 | ||
|
|
851ea2c45b | ||
|
|
ec97ee5d41 | ||
|
|
78b83ca1e1 | ||
|
|
ea0130d114 | ||
|
|
e66e7c5034 | ||
|
|
ff010678c3 | ||
|
|
5abc72cc8b | ||
|
|
fac8866fb4 | ||
|
|
d58e91303b | ||
|
|
8bd0317e9c | ||
|
|
76b1167842 | ||
|
|
7537686d8f | ||
|
|
6fc340c4db | ||
|
|
38f25af47d | ||
|
|
91df533834 | ||
|
|
9a05ca0667 | ||
|
|
ccf581d86f | ||
|
|
1dc546ac36 | ||
|
|
372b4bca46 | ||
|
|
5c89029462 | ||
|
|
a475a8a899 | ||
|
|
47b5ae7984 | ||
|
|
f5d9a67f36 | ||
|
|
1f90dbec20 | ||
|
|
d713456e29 | ||
|
|
d9d90d1ae8 | ||
|
|
62b61107e0 | ||
|
|
1e0fa56786 | ||
|
|
97d5bf7e97 | ||
|
|
75302ab716 | ||
|
|
742de9e77c | ||
|
|
921046e886 | ||
|
|
781f4afe25 | ||
|
|
10158f62e0 | ||
|
|
42d6749501 | ||
|
|
2fa46e6b6c | ||
|
|
47ec5eb6c6 | ||
|
|
799035c692 | ||
|
|
6c07256dc5 | ||
|
|
20c6a327ea | ||
|
|
bcfdc812f9 | ||
|
|
373b08a91e | ||
|
|
a7154968d8 | ||
|
|
46e432c4d9 | ||
|
|
44a61fa72c | ||
|
|
299df50066 | ||
|
|
335330690f | ||
|
|
f30cee7655 | ||
|
|
b943afb7c7 | ||
|
|
30f3818c66 | ||
|
|
13624568b5 | ||
|
|
e4f64581ec | ||
|
|
d132227860 | ||
|
|
45abb4703d | ||
|
|
a66b93c96f | ||
|
|
e5ea95db8d | ||
|
|
8938331915 | ||
|
|
749767539a | ||
|
|
399b50b4d7 | ||
|
|
82f3731c97 | ||
|
|
658258c8c3 | ||
|
|
d14cce2374 | ||
|
|
f411fb1eb3 | ||
|
|
6a30ccc907 | ||
|
|
1e1426cab0 | ||
|
|
2e750671ca | ||
|
|
147e47c8eb | ||
|
|
7208916230 | ||
|
|
43556f6df0 | ||
|
|
c0c33392f1 | ||
|
|
77ce5a9586 | ||
|
|
8665722367 | ||
|
|
57b52fbe90 | ||
|
|
b052b62b16 | ||
|
|
4df7e94d56 | ||
|
|
fcf2e2db05 | ||
|
|
1c52de9ab1 | ||
|
|
9cd762d88a | ||
|
|
eddcdaabef | ||
|
|
2c76943d35 | ||
|
|
24e3e1794e | ||
|
|
99055b553a | ||
|
|
576f13df60 | ||
|
|
004201abda | ||
|
|
6899c6b63c | ||
|
|
7f5c56a2a8 | ||
|
|
92e07d2be1 | ||
|
|
aff1fdc6aa | ||
|
|
b0b1efcc49 | ||
|
|
eb575b9882 | ||
|
|
e667abb143 | ||
|
|
347ee0211b | ||
|
|
4f4feba6fc | ||
|
|
8cb4dfef62 | ||
|
|
de452bb2c2 | ||
|
|
e8d8b09e52 | ||
|
|
631674db29 | ||
|
|
bc3ae52c7a | ||
|
|
4c795018b4 | ||
|
|
651b7b2f59 | ||
|
|
5cb207d8b3 | ||
|
|
de5de0cfaf | ||
|
|
b8ef367824 | ||
|
|
f1458b772e | ||
|
|
4181c9fab2 | ||
|
|
1fbc7f9842 | ||
|
|
73ed08a412 | ||
|
|
7cac13aefa | ||
|
|
2e114df4c7 | ||
|
|
36ab590c4e | ||
|
|
20da3427c1 | ||
|
|
2811660fa7 | ||
|
|
ef8eb935d4 | ||
|
|
11832b524b | ||
|
|
503e45eae1 | ||
|
|
041ec06dbc | ||
|
|
3841b29db8 | ||
|
|
1c94624958 | ||
|
|
c6f3700de1 | ||
|
|
186bba9d75 | ||
|
|
a3e83d59be | ||
|
|
54166b91eb | ||
|
|
1e5bb2d3da | ||
|
|
8d9a759a7d | ||
|
|
f32b035287 | ||
|
|
a8e6930d19 | ||
|
|
7fa4cb7ee8 | ||
|
|
df5855dbda | ||
|
|
e7f5ba6ca1 | ||
|
|
bcb68fcc3d | ||
|
|
e5ee918063 | ||
|
|
4be320abc8 | ||
|
|
300f5f817f | ||
|
|
574a4d0758 | ||
|
|
a02bd46094 | ||
|
|
cab80d3fa2 | ||
|
|
98d8b00d7a | ||
|
|
40608dc8d4 | ||
|
|
3b7d4a7b36 | ||
|
|
f704fcb41d | ||
|
|
8570f119c0 | ||
|
|
1656d9d7ad | ||
|
|
50c64b8512 | ||
|
|
83b9c6184c | ||
|
|
0ad479498c | ||
|
|
2dd1f6e880 | ||
|
|
da0018efdf | ||
|
|
16f768197b | ||
|
|
d6fe5b5aad | ||
|
|
dfe4fbfdc5 | ||
|
|
9444e62df9 | ||
|
|
dddffb01a6 | ||
|
|
4520bedf8d | ||
|
|
126d7bba57 | ||
|
|
575245c62f | ||
|
|
59264c0aa5 | ||
|
|
ee1bd9e006 | ||
|
|
dfd9e43405 | ||
|
|
8f06ef965a | ||
|
|
ae7cf15c70 | ||
|
|
469d2bd104 | ||
|
|
682b2ada4c | ||
|
|
f4e5426e97 | ||
|
|
6bae789bd6 | ||
|
|
ff19db0084 | ||
|
|
f10d7c05f9 | ||
|
|
f80dff851b | ||
|
|
63cbeb9a3c | ||
|
|
773d3f81fd | ||
|
|
4ab3e23b1f | ||
|
|
d588b18c39 | ||
|
|
97b899f4a3 | ||
|
|
6a913bc4cc | ||
|
|
bf46c4b5c1 | ||
|
|
69a0725c30 | ||
|
|
0c9740fe52 | ||
|
|
43f0b6c28d | ||
|
|
2776beb842 | ||
|
|
d7fa6c0ade | ||
|
|
025412aac0 | ||
|
|
bfa7d3cf41 | ||
|
|
9990780b82 | ||
|
|
dff2e73842 | ||
|
|
e2000859b6 | ||
|
|
3a6aee72a3 | ||
|
|
3fb2e451a2 | ||
|
|
7875b96956 | ||
|
|
d96c9e00b7 | ||
|
|
379c252b89 | ||
|
|
307cb56ef5 | ||
|
|
62e6ca02f9 | ||
|
|
c6fcbe20e1 | ||
|
|
a1b71f0440 | ||
|
|
dc08c26d9f | ||
|
|
669023914a | ||
|
|
e4a677ceea | ||
|
|
8aadc99f1d | ||
|
|
1ea397a807 | ||
|
|
b348f54c33 | ||
|
|
721cf20cf7 | ||
|
|
6ed9a99832 | ||
|
|
1163d14ea1 | ||
|
|
b83631fb20 | ||
|
|
902b08e657 | ||
|
|
b5fdde08aa | ||
|
|
4962c3df11 | ||
|
|
8de3498e07 | ||
|
|
26fdf83a48 | ||
|
|
32af047925 | ||
|
|
282633c877 | ||
|
|
77de570aa4 | ||
|
|
c9df19ca30 | ||
|
|
7c1cd18a06 | ||
|
|
0ca2356be5 | ||
|
|
8329d222cb | ||
|
|
1c3df1cdd7 | ||
|
|
b149805b9e | ||
|
|
99348ec309 | ||
|
|
c6976c0f92 | ||
|
|
cacc7f3193 | ||
|
|
4b9cf0952e | ||
|
|
6d37684e9c | ||
|
|
743412de33 | ||
|
|
11c1ee4481 | ||
|
|
f34703a279 | ||
|
|
5cce8ddd7d | ||
|
|
ff63eacf2c | ||
|
|
8121dce2a4 | ||
|
|
4d910f6bfe | ||
|
|
51c00fce57 | ||
|
|
1ab14ea519 | ||
|
|
c5c7c1913a | ||
|
|
3fc969b38b | ||
|
|
7ed38d6c6c | ||
|
|
fa68fa211c | ||
|
|
69cf2c36bc | ||
|
|
640c637ca8 | ||
|
|
0ad4427f83 | ||
|
|
1bfdea7527 | ||
|
|
6c36d067d7 | ||
|
|
1e60eeef34 | ||
|
|
005570c90a | ||
|
|
c070fbec62 | ||
|
|
f9d44c93fd | ||
|
|
fab5ca9440 | ||
|
|
7b128a9f00 | ||
|
|
fd14869ddc | ||
|
|
07d7d3b13b | ||
|
|
7c10683e46 | ||
|
|
3ffea2d987 | ||
|
|
770665a7b8 | ||
|
|
93ff049e54 | ||
|
|
f3b848537a | ||
|
|
27b971cbfa | ||
|
|
a1ceeb697a | ||
|
|
4a8aa0acbd | ||
|
|
6fa00b5b55 | ||
|
|
c2218faf47 | ||
|
|
21c61b6e3a | ||
|
|
b065e1cd53 | ||
|
|
7d6ce119f5 | ||
|
|
62c23166fa | ||
|
|
25afc11168 | ||
|
|
9e713406d3 | ||
|
|
f33f641f11 | ||
|
|
ca2ddfeed0 | ||
|
|
11cd42379b | ||
|
|
40d3345cd5 | ||
|
|
035e214ef5 | ||
|
|
f630a8cc1a | ||
|
|
0bfb08e464 | ||
|
|
5f6ae3857b | ||
|
|
750544dd2a | ||
|
|
660860bccf | ||
|
|
841aaa78b4 | ||
|
|
93f46a4142 | ||
|
|
a86db1bde8 | ||
|
|
4163437d5a | ||
|
|
a6535c28ea | ||
|
|
2efe95f2fb | ||
|
|
56992570d8 | ||
|
|
39934208c3 | ||
|
|
193bb313fd | ||
|
|
e8334f9b67 | ||
|
|
280ff8b5d0 | ||
|
|
172bb9e96a | ||
|
|
c04f1dea48 | ||
|
|
9114c982e0 | ||
|
|
f644a76281 | ||
|
|
63bb993c02 | ||
|
|
c50291cec8 | ||
|
|
2fe79b5fc3 | ||
|
|
b4fab5a806 | ||
|
|
35d0042be1 | ||
|
|
75dcc3f276 | ||
|
|
9c43974747 | ||
|
|
4be5fc6d43 | ||
|
|
f03336b3a2 | ||
|
|
a5db117ef6 | ||
|
|
142e0c2c3a | ||
|
|
1c8cd85f6c | ||
|
|
c547f67c54 | ||
|
|
c26e9436b4 | ||
|
|
b3239abea1 | ||
|
|
81edd4592f | ||
|
|
7484433e2b | ||
|
|
cb79e83510 | ||
|
|
cac52c0537 | ||
|
|
f5c23fbb16 | ||
|
|
a64a765f32 | ||
|
|
eecfeb2d03 | ||
|
|
54a0313d72 | ||
|
|
7cf6e77f4d | ||
|
|
4968fa0024 | ||
|
|
5e55625733 | ||
|
|
65f64e428e | ||
|
|
d2f5291412 | ||
|
|
9dc1c339ef | ||
|
|
c86304b18e | ||
|
|
7c83a1fb8e | ||
|
|
f02728aab3 | ||
|
|
48283c4dbc | ||
|
|
6bdd7f3a3f | ||
|
|
6e0dd371a0 | ||
|
|
5b1df8c4b3 | ||
|
|
5fa68e253c | ||
|
|
3e14ef634f | ||
|
|
adb5fcf708 | ||
|
|
136463c92e | ||
|
|
ef56193c44 | ||
|
|
4077040d03 | ||
|
|
de440a8c92 | ||
|
|
c75f74f681 | ||
|
|
46c78ad338 | ||
|
|
2b7a8875c0 | ||
|
|
2ee131c52c | ||
|
|
f3901cd8b8 | ||
|
|
7f0536b51d | ||
|
|
af76659830 | ||
|
|
fb658c4918 | ||
|
|
85cc4a80d0 | ||
|
|
6fe85c5779 | ||
|
|
b38a56e7d3 | ||
|
|
cbf18e5c3a | ||
|
|
96bd0d267c | ||
|
|
ec67752db4 | ||
|
|
ad4c17fbb6 | ||
|
|
672979c515 | ||
|
|
efcc2b81cd | ||
|
|
640f29fe0f | ||
|
|
13a0085408 | ||
|
|
74440d503e | ||
|
|
f4769de5c4 | ||
|
|
b843b76b7a | ||
|
|
182afe3b7d | ||
|
|
920e0c6b56 | ||
|
|
b6ce6b7cdb | ||
|
|
a0b5a24853 | ||
|
|
fa11d6bdd4 | ||
|
|
a9aef2bc84 | ||
|
|
bf2b73706a | ||
|
|
83ebce86b6 | ||
|
|
1967518fa2 | ||
|
|
5928d64d7a | ||
|
|
c7ec5a13a5 | ||
|
|
01aeb7515d | ||
|
|
d54c3369b3 | ||
|
|
d24e6100a7 | ||
|
|
cd0e45b70e | ||
|
|
0f0018abe4 | ||
|
|
a95c5e26e6 | ||
|
|
70b6cc8e55 | ||
|
|
13cbff7eab | ||
|
|
86166dbf25 | ||
|
|
b2130c2a48 | ||
|
|
5585e83fd6 | ||
|
|
7b921fc767 | ||
|
|
cfd67c8337 | ||
|
|
dc498d4de8 | ||
|
|
2c67106f26 | ||
|
|
b105c33b79 | ||
|
|
2ccc238119 | ||
|
|
e693b7d33b | ||
|
|
aca0de06cd | ||
|
|
85f4bdbe0b | ||
|
|
1257ba165f | ||
|
|
1c33d2a2ed | ||
|
|
99bd3d3b1a | ||
|
|
d8aed0ac4f | ||
|
|
5ebd4bb2a4 | ||
|
|
c934e06171 | ||
|
|
8a0685a3e9 | ||
|
|
2b018be392 | ||
|
|
4d19c437e0 | ||
|
|
44c274b9e9 | ||
|
|
66f6399b8a | ||
|
|
53a1be78cc | ||
|
|
77394ba914 | ||
|
|
d13173942d | ||
|
|
afbadd9ea2 | ||
|
|
c25888288a | ||
|
|
bfb3164a0d | ||
|
|
7e97f16f41 | ||
|
|
ce1c8aac4c | ||
|
|
72ceceb7ae | ||
|
|
cf63b588bc | ||
|
|
4e8f7f0a1b | ||
|
|
579816a04f | ||
|
|
cc04872933 | ||
|
|
48a4835819 | ||
|
|
3cf3c0ea99 | ||
|
|
ac61055d43 | ||
|
|
65eb93793c | ||
|
|
3ff02da314 | ||
|
|
df048a4f42 | ||
|
|
21c36880f1 | ||
|
|
c2658dc6da | ||
|
|
30dac8ea41 | ||
|
|
0d28404aad | ||
|
|
2f99bb025c | ||
|
|
9fd6e18d59 | ||
|
|
d8a9475460 | ||
|
|
9e85be11fe | ||
|
|
0b833bd2f3 | ||
|
|
7c93450aa7 | ||
|
|
cae32d6a00 | ||
|
|
836f6c1d5b | ||
|
|
644559b7f7 | ||
|
|
bb95ed3ad0 | ||
|
|
3947390877 | ||
|
|
e7f1e32ee3 | ||
|
|
5f8cc3c64b | ||
|
|
c0dd94c8a3 | ||
|
|
f65675836c | ||
|
|
f324c9591d | ||
|
|
3347f830c7 | ||
|
|
568abef5b8 | ||
|
|
703efdb22d | ||
|
|
2f45f0cfed | ||
|
|
112d398175 | ||
|
|
95b31146b5 |
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
||||
# Resolved all lints and formatted the codebase
|
||||
9444e62df9820d6bfd96dbd8849e177bc5cecc2e
|
||||
52
.github/actions/setup-rust/action.yml
vendored
Normal file
52
.github/actions/setup-rust/action.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: 'Setup Rust'
|
||||
inputs:
|
||||
rust-version:
|
||||
required: true
|
||||
type: string
|
||||
targets:
|
||||
required: true
|
||||
type: string
|
||||
components:
|
||||
required: false
|
||||
default:
|
||||
cache-context:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
id: toolchain
|
||||
with:
|
||||
toolchain: ${{ inputs.rust-version }}
|
||||
targets: ${{ inputs.targets }}
|
||||
components: ${{ inputs.components }}
|
||||
|
||||
- name: Install i686 dependencies
|
||||
if: "contains(inputs.targets,'i686')"
|
||||
shell: bash
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install libssl-dev:i386 gcc-multilib clang -y
|
||||
echo "CC=clang" >> $GITHUB_ENV
|
||||
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ inputs.cache-context }}_${{ inputs.targets }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
# Remove build artifacts for the current crate, since it will be rebuilt every
|
||||
# run anyway, but keep dependency artifacts to cache them.
|
||||
# Must be placed after actions/cache so its post step runs first.
|
||||
- uses: pyTooling/Actions/with-post-step@v0.4.6
|
||||
with:
|
||||
main: bash ./.github/actions/setup-rust/cleanup.sh
|
||||
post: bash ./.github/actions/setup-rust/cleanup.sh
|
||||
13
.github/actions/setup-rust/cleanup.sh
vendored
Executable file
13
.github/actions/setup-rust/cleanup.sh
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
echo Cleanup workspace build artifacts and extra target output
|
||||
|
||||
# clean just the direct members of the current workspace, use cargo metadata to generalize to all rust projects
|
||||
cargo clean -p `cargo metadata --no-deps --offline --format-version 1 | jq -r '[.workspace_members[]|split(" ")|.[0]]|join(" ")'`
|
||||
|
||||
# remove directories in /target/ that are not named `debug` or `release`
|
||||
before=`du -s target | awk '{print $1}'`
|
||||
find ./target -maxdepth 1 -type d ! -name debug ! -name release ! -name target -exec rm -r {} \;
|
||||
after=`du -s target | awk '{print $1}'`
|
||||
echo Deleted $(($before - $after)) bytes from target directory
|
||||
165
.github/workflows/ci.yml
vendored
165
.github/workflows/ci.yml
vendored
@@ -10,101 +10,81 @@ on:
|
||||
- cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
rust-version: stable
|
||||
targets: x86_64-unknown-linux-gnu
|
||||
components: clippy, rustfmt
|
||||
cache-context: style
|
||||
|
||||
- name: Check formatting
|
||||
run: cargo fmt --check
|
||||
- name: Check clippy
|
||||
run: cargo clippy --no-deps --all-targets
|
||||
|
||||
build-test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { os: windows-latest, rust-version: stable, shell: 'msys2 {0}', target: 'x86_64-pc-windows-gnu'}
|
||||
- { os: macos-11, rust-version: stable, shell: bash, target: 'x86_64-apple-darwin' }
|
||||
- { os: ubuntu-20.04, rust-version: stable, shell: bash, extra: true, target: 'x86_64-unknown-linux-gnu' }
|
||||
- { os: ubuntu-20.04, rust-version: stable, shell: bash, target: 'i686-unknown-linux-gnu' }
|
||||
- { os: ubuntu-20.04, rust-version: 1.65, shell: bash, target: 'x86_64-unknown-linux-gnu'}
|
||||
- { os: ubuntu-20.04, rust-version: beta, shell: bash, target: 'x86_64-unknown-linux-gnu'}
|
||||
- { os: ubuntu-20.04, rust-version: nightly, shell: bash, target: 'x86_64-unknown-linux-gnu'}
|
||||
# operating systems
|
||||
- { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc', publish: true }
|
||||
- { os: macos-11, rust-version: stable, target: 'x86_64-apple-darwin', publish: true }
|
||||
- { os: ubuntu-20.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
||||
# architectures
|
||||
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
||||
- { os: ubuntu-22.04, rust-version: stable, target: 'i686-unknown-linux-gnu', publish: true }
|
||||
# FIXME(issue #2138): run wasm tests, failing to run since https://github.com/mthom/scryer-prolog/pull/2137 removed wasm-pack
|
||||
- { os: ubuntu-22.04, rust-version: nightly, target: 'wasm32-unknown-unknown', publish: true, args: '--no-default-features' , test-args: '--no-run --no-default-features' }
|
||||
# rust versions
|
||||
- { os: ubuntu-22.04, rust-version: "1.70", target: 'x86_64-unknown-linux-gnu'}
|
||||
- { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'}
|
||||
- { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu'}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.shell }}
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
if: "!contains(matrix.os,'windows')"
|
||||
id: toolchain
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
toolchain: ${{ matrix.rust-version }}
|
||||
rust-version: ${{ matrix.rust-version }}
|
||||
targets: ${{ matrix.target }}
|
||||
components: clippy, rustfmt
|
||||
- name: Install i686 dependencies
|
||||
if: "contains(matrix.target,'i686')"
|
||||
run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install libssl-dev:i386 gcc-multilib clang -y && echo "CC=clang" >> $GITHUB_ENV && echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
|
||||
- uses: msys2/setup-msys2@v2
|
||||
if: contains(matrix.os,'windows')
|
||||
with:
|
||||
update: true
|
||||
install: >-
|
||||
base-devel
|
||||
mingw-w64-x86_64-rust
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ matrix.os }}_${{ matrix.target }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
cache-context: ${{ matrix.os }}
|
||||
|
||||
# Build and test.
|
||||
- name: Build library
|
||||
run: cargo rustc --target ${{ matrix.target }} --verbose --lib -- -D warnings
|
||||
run: cargo build --all-targets --target ${{ matrix.target }} ${{ matrix.args }} --verbose
|
||||
- name: Test
|
||||
if: "!matrix.extra"
|
||||
run: cargo test --target ${{ matrix.target }} --all --verbose
|
||||
|
||||
# Extra steps only run once to avoid duplication, when matrix.extra is true
|
||||
- name: Test and report
|
||||
if: matrix.extra
|
||||
run: |
|
||||
cargo install cargo2junit --force
|
||||
RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml
|
||||
- name: Publish cargo test results artifact
|
||||
if: matrix.extra
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cargo-test-results
|
||||
path: cargo_test_results.xml
|
||||
- name: Publish cargo test summary
|
||||
if: matrix.extra
|
||||
uses: EnricoMi/publish-unit-test-result-action/composite@master
|
||||
with:
|
||||
check_name: Cargo test summary
|
||||
files: cargo_test_results.xml
|
||||
fail_on: nothing
|
||||
comment_mode: off
|
||||
- name: Check formatting
|
||||
if: matrix.extra
|
||||
run: cargo fmt --check || echo "::warning ::cargo fmt found some formatting changes that may improve readability"
|
||||
- name: Check clippy
|
||||
if: matrix.extra
|
||||
run: cargo clippy --no-deps || echo "::warning ::cargo clippy found some code style changes that may be more idiomatic"
|
||||
run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
|
||||
|
||||
# On stable rust builds, build a binary and publish as a github actions
|
||||
# artifact. These binaries could be useful for testing the pipeline but
|
||||
# are only retained by github for 90 days.
|
||||
- name: Build release binary
|
||||
if: contains(matrix.rust-version,'stable')
|
||||
if: matrix.publish
|
||||
run: |
|
||||
cargo rustc --target ${{ matrix.target }} --verbose --bin scryer-prolog --release -- -D warnings
|
||||
cargo rustc --target ${{ matrix.target }} ${{ matrix.args }} --verbose --bin scryer-prolog --release
|
||||
echo "$PWD/target/release" >> $GITHUB_PATH
|
||||
- name: Publish release binary artifact
|
||||
if: contains(matrix.rust-version,'stable')
|
||||
if: matrix.publish
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: target/${{ matrix.target }}/release/scryer-prolog*
|
||||
name: scryer-prolog_${{ matrix.os }}_${{ matrix.target }}
|
||||
|
||||
logtalk-test:
|
||||
# if: false # uncomment to disable job
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [build-test]
|
||||
steps:
|
||||
@@ -118,7 +98,7 @@ jobs:
|
||||
- name: Install Logtalk
|
||||
uses: logtalk-actions/setup-logtalk@master
|
||||
with:
|
||||
logtalk-version: git
|
||||
logtalk-version: "3.70.0"
|
||||
logtalk-tool-dependencies: false
|
||||
|
||||
# Run logtalk tests.
|
||||
@@ -150,6 +130,53 @@ jobs:
|
||||
fail_on: nothing
|
||||
comment_mode: off
|
||||
|
||||
report:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
rust-version: stable
|
||||
targets: x86_64-unknown-linux-gnu
|
||||
cache-context: report
|
||||
- run: |
|
||||
cargo install cargo2junit --force
|
||||
# cargo install iai-callgrind-runner --force --version `cargo metadata --format-version 1 | jq -r '.resolve.nodes[].id|split(" ")|select(.[0]=="iai-callgrind")|.[1]'`
|
||||
cargo install iai-callgrind-runner --force --git https://github.com/iai-callgrind/iai-callgrind --rev c77bc3c83d7f4e976cc42d4597236a8db259e772
|
||||
sudo apt install valgrind -y
|
||||
|
||||
- name: Test and report
|
||||
run: |
|
||||
RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml
|
||||
- name: Publish cargo test results artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cargo-test-results
|
||||
path: cargo_test_results.xml
|
||||
- name: Publish cargo test summary
|
||||
uses: EnricoMi/publish-unit-test-result-action/composite@master
|
||||
with:
|
||||
check_name: Cargo test summary
|
||||
files: cargo_test_results.xml
|
||||
fail_on: nothing
|
||||
comment_mode: off
|
||||
|
||||
- run: cargo build --all-targets --release
|
||||
- run: cargo test --bench setup --release
|
||||
- run: cargo bench --bench run_iai -- --save-summary=json
|
||||
- run: cargo bench --bench run_criterion
|
||||
- run: cargo bench --bench run_criterion -- --profile-time 60
|
||||
|
||||
- name: Publish benchmark results
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: benchmark-results
|
||||
path: |
|
||||
target/criterion/*
|
||||
target/iai/*
|
||||
target/benchmark_inference_counts.json
|
||||
|
||||
# Publish binaries when building for a tag
|
||||
release:
|
||||
runs-on: ubuntu-20.04
|
||||
@@ -161,11 +188,15 @@ jobs:
|
||||
run: |
|
||||
zip scryer-prolog_macos-11.zip ./scryer-prolog_macos-11_x86_64-apple-darwin/scryer-prolog
|
||||
zip scryer-prolog_ubuntu-20.04.zip ./scryer-prolog_ubuntu-20.04_x86_64-unknown-linux-gnu/scryer-prolog
|
||||
zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest_x86_64-pc-windows-gnu/scryer-prolog.exe
|
||||
zip scryer-prolog_ubuntu-22.04.zip ./scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu/scryer-prolog
|
||||
zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest_x86_64-pc-windows-msvc/scryer-prolog.exe
|
||||
zip scryer-prolog_wasm32.zip ./scryer-prolog_ubuntu-22.04_wasm32-unknown-unknown/scryer-prolog.wasm
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
scryer-prolog_macos-11.zip
|
||||
scryer-prolog_ubuntu-20.04.zip
|
||||
scryer-prolog_ubuntu-22.04.zip
|
||||
scryer-prolog_windows-latest.zip
|
||||
scryer-prolog_wasm32.zip
|
||||
|
||||
1830
Cargo.lock
generated
1830
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
116
Cargo.toml
116
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "scryer-prolog"
|
||||
version = "0.9.2"
|
||||
version = "0.9.4"
|
||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "A modern Prolog implementation written mostly in Rust."
|
||||
@@ -10,15 +10,20 @@ license = "BSD-3-Clause"
|
||||
keywords = ["prolog", "prolog-interpreter", "prolog-system"]
|
||||
categories = ["command-line-utilities"]
|
||||
build = "build/main.rs"
|
||||
rust-version = "1.63"
|
||||
rust-version = "1.70"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["ffi", "repl", "hostname", "tls", "http"]
|
||||
default = ["ffi", "repl", "hostname", "tls", "http", "crypto-full"]
|
||||
ffi = ["dep:libffi"]
|
||||
repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"]
|
||||
hostname = ["dep:hostname"]
|
||||
tls = ["dep:native-tls"]
|
||||
http = ["dep:hyper", "dep:reqwest"]
|
||||
http = ["dep:warp", "dep:reqwest"]
|
||||
rust_beta_channel = []
|
||||
crypto-full = []
|
||||
|
||||
[build-dependencies]
|
||||
indexmap = "1.0.2"
|
||||
@@ -26,74 +31,111 @@ proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
strum = "0.23"
|
||||
strum_macros = "0.23"
|
||||
syn = { version = "1.0.88", features = ['full', 'visit', 'extra-traits'] }
|
||||
to-syn-value = "0.1.0"
|
||||
to-syn-value_derive = "0.1.0"
|
||||
syn = { version = "2.0.32", features = ['full', 'visit', 'extra-traits'] }
|
||||
to-syn-value = "0.1.1"
|
||||
to-syn-value_derive = "0.1.1"
|
||||
walkdir = "2"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.12.3"
|
||||
bit-set = "0.5.3"
|
||||
bitvec = "1"
|
||||
blake2 = "0.8.1"
|
||||
bytes = "1"
|
||||
chrono = "0.4.11"
|
||||
cpu-time = "1.0.0"
|
||||
crrl = "0.6.0"
|
||||
dashu = "0.4.0"
|
||||
derive_deref = "1.1.1"
|
||||
dirs-next = "2.0.0"
|
||||
divrem = "0.1.0"
|
||||
futures = "0.3"
|
||||
fxhash = "0.2.1"
|
||||
git-version = "0.3.4"
|
||||
indexmap = "1.0.2"
|
||||
lazy_static = "1.4.0"
|
||||
lexical = "5.2.2"
|
||||
libc = "0.2.62"
|
||||
modular-bitfield = "0.11.2"
|
||||
libloading = "0.7"
|
||||
scryer-modular-bitfield = "0.11.4"
|
||||
num-order = { version = "1.2.0" }
|
||||
ordered-float = "2.6.0"
|
||||
phf = { version = "0.9", features = ["macros"] }
|
||||
rand = "0.8.5"
|
||||
ref_thread_local = "0.0.0"
|
||||
regex = "1.9.1"
|
||||
ring = { version = "0.17.5", features = ["wasm32_unknown_unknown_js"] }
|
||||
ripemd160 = "0.8.0"
|
||||
sha3 = "0.8.2"
|
||||
blake2 = "0.8.1"
|
||||
crrl = "0.6.0"
|
||||
chrono = "0.4.11"
|
||||
select = "0.6.0"
|
||||
roxmltree = "0.11.0"
|
||||
base64 = "0.12.3"
|
||||
ryu = "1.0.9"
|
||||
select = "0.6.0"
|
||||
sha3 = "0.8.2"
|
||||
smallvec = "1.8.0"
|
||||
static_assertions = "1.1.0"
|
||||
ryu = "1.0.9"
|
||||
futures = "0.3"
|
||||
libloading = "0.7"
|
||||
derive_deref = "1.1.1"
|
||||
http-body-util = "0.1.0-rc.2"
|
||||
bytes = "1"
|
||||
dashu = { git = "https://github.com/coasys/dashu.git", version = "0.3.1" }
|
||||
rand = "0.8.5"
|
||||
|
||||
serde_json = "1.0.95"
|
||||
serde = "1.0.159"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
libffi = { git = "https://github.com/coasys/libffi-rs.git", branch = "windows-space", optional = true, version = "3.2.0" }
|
||||
hostname = { version = "0.3.1", optional = true }
|
||||
crossterm = { version = "0.20.0", optional = true }
|
||||
ctrlc = { version = "3.2.2", optional = true }
|
||||
rustyline = { version = "12.0.0", optional = true }
|
||||
hostname = { version = "0.3.1", optional = true }
|
||||
libffi = { version = "3.2.0", optional = true }
|
||||
native-tls = { version = "0.2.4", optional = true }
|
||||
hyper = { version = "=1.0.0-rc.3", features = ["full"], optional = true }
|
||||
reqwest = { version = "0.11.18", features = ["blocking"], optional = true }
|
||||
reqwest = { version = "0.11.18", optional = true }
|
||||
rustyline = { version = "12.0.0", optional = true }
|
||||
tokio = { version = "1.28.2", features = ["full"] }
|
||||
warp = { version = "=0.3.5", features = ["tls"], optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
getrandom = { version = "0.2.10", features = ["js"] }
|
||||
tokio = { version = "1.28.2", features = ["sync", "macros", "io-util", "rt", "time"] }
|
||||
tokio = { version = "1.28.2", features = [
|
||||
"sync",
|
||||
"macros",
|
||||
"io-util",
|
||||
"rt",
|
||||
"time",
|
||||
] }
|
||||
|
||||
[target.'cfg(target_os = "wasi")'.dependencies]
|
||||
ring-wasi = { version = "0.16.25" }
|
||||
|
||||
[target.'cfg(not(target_os = "wasi"))'.dependencies]
|
||||
ring = { version = "0.16.13" }
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
wasm-bindgen = "0.2.87"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
serde-wasm-bindgen = "0.5"
|
||||
web-sys = { version = "0.3", features = [
|
||||
"Document",
|
||||
"Window",
|
||||
"Element",
|
||||
"Performance",
|
||||
] }
|
||||
js-sys = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "1.0.3"
|
||||
maplit = "1.0.2"
|
||||
predicates-core = "1.0.2"
|
||||
serial_test = "2.0.0"
|
||||
|
||||
[patch.crates-io]
|
||||
modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" }
|
||||
[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies]
|
||||
assert_cmd = "1.0.3"
|
||||
criterion = "0.5.1"
|
||||
iai-callgrind = "0.9.0"
|
||||
trycmd = "0.14.19"
|
||||
|
||||
[target.'cfg(not(any(target_os = "windows", all(target_arch = "wasm32", target_os = "unknown"))))'.dev-dependencies]
|
||||
pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] }
|
||||
|
||||
[profile.bench]
|
||||
lto = true
|
||||
opt-level = 3
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
lto = true
|
||||
opt-level = 3
|
||||
|
||||
[[bench]]
|
||||
name = "run_criterion"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "run_iai"
|
||||
harness = false
|
||||
|
||||
@@ -20,7 +20,12 @@ COPY --from=cacher /scryer-prolog/target target
|
||||
COPY --from=cacher $CARGO_HOME $CARGO_HOME
|
||||
RUN cargo build --release --bin scryer-prolog
|
||||
|
||||
FROM debian:stable-slim
|
||||
# Newer versions of Debian (i.e. bookworm) contain libssl3 instead of libssl1.1
|
||||
# which we depend on.
|
||||
FROM debian:bullseye-slim
|
||||
COPY --from=builder /scryer-prolog/target/release/scryer-prolog /usr/local/bin
|
||||
ENV RUST_BACKTRACE=1
|
||||
# Sanity check the binary: if it can't be executed (e.g. if there are missing libraries)
|
||||
# then fail the build
|
||||
RUN scryer-prolog --version
|
||||
ENTRYPOINT ["/usr/local/bin/scryer-prolog"]
|
||||
|
||||
22
INDEX.dj
22
INDEX.dj
@@ -5,13 +5,6 @@
|
||||
X = "Scryer Prolog!".
|
||||
```
|
||||
|
||||
``` =html
|
||||
<div style="border: solid #00007f 3px;padding-left: 15px;padding-right: 15px;font-style: italic;background-color: #00007f30;">
|
||||
<h4>Scryer Prolog Meetup 2023</h4>
|
||||
<p>The first annual Scryer Prolog meetup is going to happen in Düsseldorf (Germany) on the 9th and 10th of November 2023. Join us to discover the present and future of Scryer Prolog! Participation is free, registration not required. <a href="https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2023/">More details here.</a></p>
|
||||
</div>
|
||||
```
|
||||
|
||||
{width=128 style=float:right;} [Scryer Prolog](https://github.com/mthom/scryer-prolog) is a free software ISO Prolog system intended to be an industrial
|
||||
strength production environment *and* a testbed for bleeding edge research in
|
||||
logic and constraint programming.
|
||||
@@ -26,9 +19,14 @@ Some of the Scryer Prolog features are:
|
||||
* Compact string representation
|
||||
* Network libraries ([TCP sockets](/sockets.html), [HTTP server](/http/http_server.html), [HTTP client](/http/http_open.html), ...)
|
||||
* [Cryptographical predicates](/crypto.html)
|
||||
* [Foreign Function Interface](/ffi.html)
|
||||
* WebAssembly support
|
||||
* Usable as a library
|
||||
* WAM based engine, cross-platform made in Rust
|
||||
* _and more..._
|
||||
|
||||
Try Scryer Prolog without any installation! Use [Scryer Playground](https://play.scryer.pl), which uses the WASM version of Scryer Prolog.
|
||||
|
||||
## What is Prolog?
|
||||
|
||||
Prolog is a logic programming language created by [Alain Colmerauer](https://en.wikipedia.org/wiki/Alain_Colmerauer) and [Robert Kowalski](https://en.wikipedia.org/wiki/Robert_Kowalski) in 1972.
|
||||
@@ -57,9 +55,13 @@ the builtin Prolog modules and libraries in Scryer, check the documentation site
|
||||
|
||||
## Downloads
|
||||
|
||||
The latest version of Scryer Prolog is *0.9.1*. And it's already useful for lots of tasks.
|
||||
The latest version of Scryer Prolog is *0.9.3*. And it's already useful for lots of tasks.
|
||||
|
||||
Scryer Prolog can be compiled from source, instructions are on the [GitHub README](https://github.com/mthom/scryer-prolog). It runs on Linux, macOS and Windows. Other operating systems may work but they're not regularly tested.
|
||||
| Windows | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.3/scryer-prolog_windows-latest.zip) |
|
||||
| macOS (Intel) | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.3/scryer-prolog_macos-11.zip) |
|
||||
| Linux | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.3/scryer-prolog_ubuntu-20.04.zip) |
|
||||
|
||||
Scryer Prolog can also be compiled from source, instructions are on the [GitHub README](https://github.com/mthom/scryer-prolog). It runs on Linux, macOS and Windows. Other operating systems may work but they're not regularly tested.
|
||||
|
||||
If you're in Linux, maybe your distribution already has an Scryer Prolog package.
|
||||
|
||||
@@ -72,4 +74,4 @@ an [issue](https://github.com/mthom/scryer-prolog/issues).
|
||||
|
||||
To get in touch with the Scryer Prolog community, participate in
|
||||
[discussions](https://github.com/mthom/scryer-prolog/discussions)
|
||||
or visit our #scryer IRC channel on [Libera](https://libera.chat)!
|
||||
or visit our #scryer IRC channel on [Libera](https://libera.chat)!
|
||||
|
||||
137
README.md
137
README.md
@@ -6,7 +6,10 @@ source industrial strength production environment that is also a
|
||||
testbed for bleeding edge research in logic and constraint
|
||||
programming, which is itself written in a high-level language.
|
||||
|
||||
As of July 2023, **Scryer Prolog passes all [syntactic conformity tests](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing)**.
|
||||
**Scryer Prolog passes all tests** of
|
||||
[syntactic conformity](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing),
|
||||
[`variable_names/1`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/variable_names) and
|
||||
[`dif/2`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dif).
|
||||
|
||||
The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl)
|
||||
|
||||
@@ -105,7 +108,14 @@ strings.
|
||||
|
||||
## Installing Scryer Prolog
|
||||
|
||||
### Native Install
|
||||
### Binaries
|
||||
|
||||
Precompiled binaries for several platforms are available for download
|
||||
at:
|
||||
|
||||
**https://github.com/mthom/scryer-prolog/releases/tag/v0.9.3**
|
||||
|
||||
### Native Compilation
|
||||
|
||||
First, install the latest stable version of
|
||||
[Rust](https://www.rust-lang.org/en-US/install.html) using your
|
||||
@@ -143,7 +153,78 @@ light.exe scryer-prolog.wixobj
|
||||
```
|
||||
It will generate a very basic MSI file which installs the main executable and a shortcut in the Start Menu. It can be installed with a double-click. To uninstall, go to the Control Panel and uninstall as usual.
|
||||
|
||||
Scryer Prolog must be built with **Rust 1.63 and up**.
|
||||
Scryer Prolog must be built with **Rust 1.70 and up**.
|
||||
|
||||
### Building WebAssembly
|
||||
|
||||
Scryer Prolog has basic WebAssembly support. You can follow `wasm-pack`'s [official instructions](https://rustwasm.github.io/docs/wasm-pack/quickstart.html) to install `wasm-pack` and build it in any way you like.
|
||||
|
||||
However, none of the [default features](https://doc.rust-lang.org/cargo/reference/features.html#the-default-feature) are currently supported. The preferred way of disabling them is passing [extra options](https://rustwasm.github.io/wasm-pack/book/commands/build.html#extra-options) to `wasm-pack`.
|
||||
|
||||
For example, if you want a minimal working package without using any bundler like `webpack`, you can do this:
|
||||
```
|
||||
wasm-pack build --target web -- --no-default-features
|
||||
```
|
||||
Then a `pkg` directory will be created, containing everything you need for a webapp. You can test whether the package is successfully built by creating an html file, adapted from `wasm-bindgen`'s [official example](https://rustwasm.github.io/wasm-bindgen/examples/without-a-bundler.html) like this:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Scryer Prolog - Sudoku Solver Example</title>
|
||||
<script type="module">
|
||||
import init, { eval_code } from './pkg/scryer_prolog.js';
|
||||
|
||||
const run = async () => {
|
||||
await init("./pkg/scryer_prolog_bg.wasm");
|
||||
let code = `
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(clpz)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
sudoku(Rows) :-
|
||||
length(Rows, 9), maplist(same_length(Rows), Rows),
|
||||
append(Rows, Vs), Vs ins 1..9,
|
||||
maplist(all_distinct, Rows),
|
||||
transpose(Rows, Columns),
|
||||
maplist(all_distinct, Columns),
|
||||
Rows = [As,Bs,Cs,Ds,Es,Fs,Gs,Hs,Is],
|
||||
blocks(As, Bs, Cs),
|
||||
blocks(Ds, Es, Fs),
|
||||
blocks(Gs, Hs, Is).
|
||||
|
||||
blocks([], [], []).
|
||||
blocks([N1,N2,N3|Ns1], [N4,N5,N6|Ns2], [N7,N8,N9|Ns3]) :-
|
||||
all_distinct([N1,N2,N3,N4,N5,N6,N7,N8,N9]),
|
||||
blocks(Ns1, Ns2, Ns3).
|
||||
|
||||
problem(1, [[_,_,_,_,_,_,_,_,_],
|
||||
[_,_,_,_,_,3,_,8,5],
|
||||
[_,_,1,_,2,_,_,_,_],
|
||||
[_,_,_,5,_,7,_,_,_],
|
||||
[_,_,4,_,_,_,1,_,_],
|
||||
[_,9,_,_,_,_,_,_,_],
|
||||
[5,_,_,_,_,_,_,7,3],
|
||||
[_,_,2,_,1,_,_,_,_],
|
||||
[_,_,_,_,4,_,_,_,9]]).
|
||||
|
||||
main :-
|
||||
problem(1, Rows), sudoku(Rows), maplist(portray_clause, Rows).
|
||||
|
||||
:- initialization(main).
|
||||
`;
|
||||
const result = eval_code(code);
|
||||
document.write(`<p>Sudoku solver returns:</p><pre>${result}</pre>`);
|
||||
}
|
||||
run();
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Then you can serve it with your favorite http server like `python -m http.server` or `npx serve`, and access the page with your browser.
|
||||
|
||||
### Docker Install
|
||||
|
||||
@@ -235,6 +316,35 @@ To quit Scryer Prolog, use the standard predicate `halt/0`:
|
||||
?- halt.
|
||||
```
|
||||
|
||||
### Starting Scryer Prolog
|
||||
|
||||
Scryer Prolog can be started from the command line by specifying
|
||||
options, files and additional arguments. All components are optional:
|
||||
|
||||
<pre>
|
||||
scryer-prolog [OPTIONS] [FILES] [-- ARGUMENTS]
|
||||
</pre>
|
||||
|
||||
The supported options are:
|
||||
|
||||
```
|
||||
-h, --help Display help message
|
||||
-v, --version Print version information and exit
|
||||
-g, --goal GOAL Run the query GOAL after consulting files
|
||||
-f Fast startup. Do not load initialization file (~/.scryerrc)
|
||||
--no-add-history Prevent adding input to history file (~/.scryer_history)
|
||||
```
|
||||
|
||||
All specified Prolog files are consulted.
|
||||
|
||||
After Prolog files, application-specific arguments can be specified on
|
||||
the command line. These arguments can be accessed from within Prolog
|
||||
applications with the predicate `argv/1`, which yields the list
|
||||
of arguments represented as strings.
|
||||
|
||||
Prolog files can also be turned into *shell scripts* as explained in
|
||||
https://github.com/mthom/scryer-prolog/issues/2170#issuecomment-1821713993.
|
||||
|
||||
### Dynamic operators
|
||||
|
||||
Scryer supports dynamic operators. Using the built-in
|
||||
@@ -285,14 +395,14 @@ innovations of Scryer Prolog. This means that terms which appear as
|
||||
lists of characters to Prolog programs are stored in packed
|
||||
UTF-8 encoding by the engine.
|
||||
|
||||
Without this innovation, storing a list of characters in memory
|
||||
would use one memory cell per character, one memory cell per
|
||||
list constructor, and one memory cell for each tail that occurs
|
||||
in the list. Since one memory cell takes 8 bytes on 64-bit
|
||||
machines, the packed representation used by Scryer Prolog yields
|
||||
an up to **24-fold reduction** of memory usage, and
|
||||
corresponding reduction of memory accesses when creating and
|
||||
processing strings.
|
||||
Without this innovation, storing a list of characters in memory would
|
||||
use one WAM memory cell per character, one cell per list
|
||||
constructor, and one cell for each tail that occurs in the list. Since
|
||||
one cell takes 8 bytes in the WAM as implemented by
|
||||
Scryer Prolog, the packed representation yields an up to
|
||||
**24-fold reduction** of memory usage, and corresponding
|
||||
reduction of memory accesses when creating and processing
|
||||
strings.
|
||||
|
||||
Scryer Prolog's compact internal string representation makes it
|
||||
ideally suited for the use case Prolog was originally developed for:
|
||||
@@ -546,7 +656,7 @@ The modules that ship with Scryer Prolog are also called
|
||||
Probabilistic predicates and random number generators.
|
||||
* [`http/http_open`](src/lib/http/http_open.pl) Open a stream to
|
||||
read answers from web servers. HTTPS is also supported.
|
||||
* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.1 and HTTP/2.0 web server. Uses [Hyper](https://hyper.rs) as a backend. Supports some query and form handling.
|
||||
* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.1 and HTTP/2.0 web server. Uses [Warp](https://github.com/seanmonstar/warp) as a backend. Supports some query and form handling.
|
||||
* [`sgml`](src/lib/sgml.pl)
|
||||
`load_html/3` and `load_xml/3` represent HTML and XML documents
|
||||
as Prolog terms for convenient and efficient reasoning. Use
|
||||
@@ -695,7 +805,8 @@ by the Austrian Federal Computing Center, and parts of the
|
||||
[precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog)
|
||||
package for the analysis of dose-escalation trials in the
|
||||
safety-critical and highly regulated domain of oncology
|
||||
trial design.
|
||||
trial design, described in [*An Executable Specification of
|
||||
Oncology Dose-Escalation Protocols with Prolog*](https://arxiv.org/abs/2402.08334).
|
||||
|
||||
Scryer Prolog is also very well suited for teaching and learning
|
||||
Prolog, and for testing syntactic conformance and hence portability of
|
||||
|
||||
95
benches/README.md
Normal file
95
benches/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# About benches
|
||||
|
||||
The `benches` directory contains benchmarks that test scryer-prolog performance.
|
||||
|
||||
Benchmarks are run via two harnesses:
|
||||
|
||||
* `criterion` - criterion performs statistical analysis of benchmark runs and is
|
||||
great for benchmarking locally.
|
||||
* `iai-callgrind` - this runs the benchmark with callgrind, which is able to
|
||||
precisely track the number of instructions executed during the run. This is
|
||||
especially helpful in a public CI runner context where neighboring VMs can
|
||||
cause a very high wall time variance. While instructions executed is only
|
||||
correlated with the desired metric (wall time), this is a good tradeoff for CI
|
||||
where that metric is unreliable.
|
||||
|
||||
Run benchmarks with the following commands:
|
||||
|
||||
```
|
||||
cargo bench --bench run_criterion
|
||||
|
||||
# run a particular criterion benchmark
|
||||
cargo bench --bench run_criterion -- <benchmark_name>
|
||||
|
||||
# run in profiling mode which outputs flamegraphs. Set profile time in seconds:
|
||||
cargo bench --bench run_criterion -- --profile-time <time>
|
||||
|
||||
# to run iai, you need valgrind installed and to install iai-callgrind-runner
|
||||
# at the same version as is in Cargo.toml:
|
||||
cargo install iai-callgrind-runner --version 0.7.3
|
||||
|
||||
cargo bench --bench run_iai
|
||||
```
|
||||
|
||||
For consistency, both runners -- `run_iai.rs` and `run_criterion.rs` -- import
|
||||
the same setup code from `setup.rs`.
|
||||
|
||||
## Setup
|
||||
|
||||
`setup.rs` contains the setup code to run benchmarks. `fn prolog_benches()` at
|
||||
the top of the file is where the benchmarks are defined.
|
||||
|
||||
Benchmarks are organized around running queries against a prolog module file.
|
||||
Before a benchmark starts, `benchmark.setup()` is called which reads the module
|
||||
file and initializes a new `scryer_prolog::machine::Machine`.
|
||||
|
||||
Each benchmark measurement is done by running a query against the machine. In
|
||||
the case of criterion each query is run many times, in the case of iai it's run
|
||||
once.
|
||||
|
||||
## Adding benchmarks
|
||||
|
||||
This design is meant to suppoort defining lots of benchmarks.
|
||||
|
||||
To add a new benchmark:
|
||||
|
||||
* Add a new file `benches/[module].pl` that contains setup prolog code. Import
|
||||
libraries, define predicates, etc.
|
||||
* Add a new section in `setup.rs::prolog_benchmarks()` that refers to to the
|
||||
file and write a query to be benchmarked.
|
||||
* If the query mutates the machine, then use `Strategy::Fresh` so the criterion
|
||||
benchmark will recreate a new machine for each benchmark run, otherwise use
|
||||
`Strategy::Reuse` which has lower overhead. (This is not used by the iai
|
||||
benchmark because it only runs once anyway.)
|
||||
|
||||
Some tips:
|
||||
|
||||
* The goal of benchmarking is to know if a library or engine change improved
|
||||
performance or not.
|
||||
* Once a benchmark is defined and named, avoid changing it's definition. If a
|
||||
benchmark needs to change to be more useful, give the new definition a new
|
||||
name instead. This will prevent charts from showing wild changes in
|
||||
performance just because the definition changed (see previous).
|
||||
* Aim for queries to execute in less than 0.5s realtime. Longer runtimes make it
|
||||
easier for humans to see big differences, but benchmarks either run 10x slower
|
||||
(iai) or execute repeatedly to attain statistical significance (criterion) and
|
||||
in both cases benchmarking queries that take longer than about 0.5s are
|
||||
cumbersome to run.
|
||||
* Consider that the library runtime actually parses the text output of the top
|
||||
level. So don't use custom outputs or it will fail to parse. Also keep the
|
||||
output small so it doesn't just benchmark the ouput parsing code.
|
||||
* DO test the output of the benchmark run, we don't want to count broken
|
||||
benchmarks.
|
||||
|
||||
## CI
|
||||
|
||||
Both benchmark harnesses are run in `.github/workflows/ci.yaml` in the `report`
|
||||
job, and the results are published as build artifacts.
|
||||
|
||||
## Todo
|
||||
|
||||
- [ ] Currently, the execution time to load a module is not benchmarked. It
|
||||
would be nice to have at least one benchmark for loading a module (probably a
|
||||
big one).
|
||||
- [ ] Write a new action that downloads the test and benchmark results
|
||||
artifacts, plots them over time, and publishes a report to github pages.
|
||||
41
benches/csv.pl
Normal file
41
benches/csv.pl
Normal file
File diff suppressed because one or more lines are too long
130
benches/edges.pl
Normal file
130
benches/edges.pl
Normal file
@@ -0,0 +1,130 @@
|
||||
:- use_module(library(clpb)).
|
||||
:- use_module(library(assoc)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(pairs)).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Contiguous United States and DC as they appear in SGB:
|
||||
http://www-cs-faculty.stanford.edu/~uno/sgb.html
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
edge(al, fl).
|
||||
edge(al, ga).
|
||||
edge(al, ms).
|
||||
edge(al, tn).
|
||||
edge(ar, la).
|
||||
edge(ar, mo).
|
||||
edge(ar, ms).
|
||||
edge(ar, ok).
|
||||
edge(ar, tn).
|
||||
edge(ar, tx).
|
||||
edge(az, ca).
|
||||
edge(az, nm).
|
||||
edge(az, nv).
|
||||
edge(az, ut).
|
||||
edge(ca, nv).
|
||||
edge(ca, or).
|
||||
edge(co, ks).
|
||||
edge(co, ne).
|
||||
edge(co, nm).
|
||||
edge(co, ok).
|
||||
edge(co, ut).
|
||||
edge(co, wy).
|
||||
edge(ct, ma).
|
||||
edge(ct, ny).
|
||||
edge(ct, ri).
|
||||
edge(dc, md).
|
||||
edge(dc, va).
|
||||
edge(de, md).
|
||||
edge(de, nj).
|
||||
edge(de, pa).
|
||||
edge(fl, ga).
|
||||
edge(ga, nc).
|
||||
edge(ga, sc).
|
||||
edge(ga, tn).
|
||||
edge(ia, il).
|
||||
edge(ia, mn).
|
||||
edge(ia, mo).
|
||||
edge(ia, ne).
|
||||
edge(ia, sd).
|
||||
edge(ia, wi).
|
||||
edge(id, mt).
|
||||
edge(id, nv).
|
||||
edge(id, or).
|
||||
edge(id, ut).
|
||||
edge(id, wa).
|
||||
edge(id, wy).
|
||||
edge(il, in).
|
||||
edge(il, ky).
|
||||
edge(il, mo).
|
||||
edge(il, wi).
|
||||
edge(in, ky).
|
||||
edge(in, mi).
|
||||
edge(in, oh).
|
||||
edge(ks, mo).
|
||||
edge(ks, ne).
|
||||
edge(ks, ok).
|
||||
edge(ky, mo).
|
||||
edge(ky, oh).
|
||||
edge(ky, tn).
|
||||
edge(ky, va).
|
||||
edge(ky, wv).
|
||||
edge(la, ms).
|
||||
edge(la, tx).
|
||||
edge(ma, nh).
|
||||
edge(ma, ny).
|
||||
edge(ma, ri).
|
||||
edge(ma, vt).
|
||||
edge(md, pa).
|
||||
edge(md, va).
|
||||
edge(md, wv).
|
||||
edge(me, nh).
|
||||
edge(mi, oh).
|
||||
edge(mi, wi).
|
||||
edge(mn, nd).
|
||||
edge(mn, sd).
|
||||
edge(mn, wi).
|
||||
edge(mo, ne).
|
||||
edge(mo, ok).
|
||||
edge(mo, tn).
|
||||
edge(ms, tn).
|
||||
edge(mt, nd).
|
||||
edge(mt, sd).
|
||||
edge(mt, wy).
|
||||
edge(nc, sc).
|
||||
edge(nc, tn).
|
||||
edge(nc, va).
|
||||
edge(nd, sd).
|
||||
edge(ne, sd).
|
||||
edge(ne, wy).
|
||||
edge(nh, vt).
|
||||
edge(nj, ny).
|
||||
edge(nj, pa).
|
||||
edge(nm, ok).
|
||||
edge(nm, tx).
|
||||
edge(nv, or).
|
||||
edge(nv, ut).
|
||||
edge(ny, pa).
|
||||
edge(ny, vt).
|
||||
edge(oh, pa).
|
||||
edge(oh, wv).
|
||||
edge(ok, tx).
|
||||
edge(or, wa).
|
||||
edge(pa, wv).
|
||||
edge(sd, wy).
|
||||
edge(tn, va).
|
||||
edge(ut, wy).
|
||||
edge(va, wv).
|
||||
|
||||
independent_set(G, *(NBs)) :-
|
||||
findall(U-V, (edge(U, V),G@<U), Edges),
|
||||
setof(U, V^(member(U-V, Edges);member(V-U, Edges)), Nodes),
|
||||
pairs_keys_values(Pairs, Nodes, _),
|
||||
list_to_assoc(Pairs, Assoc),
|
||||
maplist(not_both(Assoc), Edges, NBs).
|
||||
|
||||
not_both(Assoc, U-V, ~BU + ~BV) :-
|
||||
get_assoc(U, Assoc, BU),
|
||||
get_assoc(V, Assoc, BV).
|
||||
|
||||
independent_set_count(G, Count) :- independent_set(G, Sat), sat_count(Sat, Count).
|
||||
2
benches/numlist.pl
Normal file
2
benches/numlist.pl
Normal file
@@ -0,0 +1,2 @@
|
||||
:- use_module(library(between)).
|
||||
run_numlist(Upper, Head) :- numlist(1, Upper, L), L = [Head|_].
|
||||
46
benches/run_criterion.rs
Normal file
46
benches/run_criterion.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use pprof::criterion::{Output, PProfProfiler};
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
mod setup;
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
fn bench_criterion(c: &mut Criterion) {
|
||||
for (&name, bench) in setup::prolog_benches().iter() {
|
||||
match bench.strategy {
|
||||
setup::Strategy::Fresh => c.bench_function(name, |b| {
|
||||
b.iter_batched(|| bench.setup(), |mut r| r(), BatchSize::LargeInput)
|
||||
}),
|
||||
setup::Strategy::Reuse => c.bench_function(name, |b| b.iter(bench.setup())),
|
||||
};
|
||||
}
|
||||
}
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn config() -> Criterion {
|
||||
Criterion::default()
|
||||
.sample_size(20)
|
||||
.with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn config() -> Criterion {
|
||||
Criterion::default().sample_size(20)
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
criterion_group!(
|
||||
name = benches;
|
||||
config = config();
|
||||
targets = bench_criterion
|
||||
);
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
criterion_main!(benches);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
fn main() {}
|
||||
38
benches/run_iai.rs
Normal file
38
benches/run_iai.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
mod setup;
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
mod iai {
|
||||
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||
|
||||
use scryer_prolog::machine::parsed_results::QueryResolution;
|
||||
|
||||
use super::setup;
|
||||
|
||||
#[library_benchmark]
|
||||
#[bench::count_edges(setup::prolog_benches()["count_edges"].setup())]
|
||||
#[bench::numlist(setup::prolog_benches()["numlist"].setup())]
|
||||
#[bench::csv_codename(setup::prolog_benches()["csv_codename"].setup())]
|
||||
fn bench(mut run: impl FnMut() -> QueryResolution) -> QueryResolution {
|
||||
run()
|
||||
}
|
||||
|
||||
library_benchmark_group!(
|
||||
name = benches;
|
||||
benchmarks = bench
|
||||
);
|
||||
|
||||
main!(library_benchmark_groups = benches);
|
||||
|
||||
pub fn call_main() {
|
||||
main()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
fn main() {
|
||||
iai::call_main();
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
fn main() {}
|
||||
133
benches/setup.rs
Normal file
133
benches/setup.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use std::{collections::BTreeMap, fs, path::Path};
|
||||
|
||||
use maplit::btreemap;
|
||||
use scryer_prolog::machine::{
|
||||
parsed_results::{QueryResolution, Value},
|
||||
Machine,
|
||||
};
|
||||
|
||||
pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
|
||||
[
|
||||
(
|
||||
"count_edges", // name of the benchmark
|
||||
"benches/edges.pl", // name of the prolog module file to load. use the same file in multiple benchmarks
|
||||
"independent_set_count(ky, Count).", // query to benchmark in the context of the loaded module. consider making the query adjustable to tune the run time to ~0.1s
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Count" => Value::try_from("2869176".to_string()).unwrap() },
|
||||
),
|
||||
(
|
||||
"numlist",
|
||||
"benches/numlist.pl",
|
||||
"run_numlist(1000000, Head).",
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Head" => Value::try_from("1".to_string()).unwrap()},
|
||||
),
|
||||
(
|
||||
"csv_codename",
|
||||
"benches/csv.pl",
|
||||
"get_codename(\"0020\",Name).",
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Name" => Value::try_from("SPACE".to_string()).unwrap()},
|
||||
),
|
||||
]
|
||||
.map(|b| {
|
||||
(
|
||||
b.0,
|
||||
PrologBenchmark {
|
||||
name: b.0,
|
||||
filename: b.1,
|
||||
query: b.2,
|
||||
strategy: b.3,
|
||||
bindings: b.4,
|
||||
},
|
||||
)
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
pub enum Strategy {
|
||||
#[allow(dead_code)]
|
||||
Fresh,
|
||||
Reuse,
|
||||
}
|
||||
|
||||
pub struct PrologBenchmark {
|
||||
pub name: &'static str,
|
||||
pub filename: &'static str,
|
||||
pub query: &'static str,
|
||||
pub strategy: Strategy,
|
||||
pub bindings: BTreeMap<&'static str, Value>,
|
||||
}
|
||||
|
||||
impl PrologBenchmark {
|
||||
pub fn make_machine(&self) -> Machine {
|
||||
let program = fs::read_to_string(self.filename).unwrap();
|
||||
let module_name = Path::new(self.filename)
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap();
|
||||
let mut machine = Machine::new_lib();
|
||||
machine.load_module_string(module_name, program);
|
||||
machine
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
pub fn setup(&self) -> impl FnMut() -> QueryResolution {
|
||||
let mut machine = self.make_machine();
|
||||
let query = self.query;
|
||||
move || {
|
||||
use criterion::black_box;
|
||||
black_box(machine.run_query(black_box(query.to_string()))).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn validate_benchmarks() {
|
||||
use super::prolog_benches;
|
||||
use scryer_prolog::machine::parsed_results::{QueryMatch, QueryResolution};
|
||||
use std::{fmt::Write, fs};
|
||||
|
||||
struct BenchResult {
|
||||
pub name: &'static str,
|
||||
pub setup_inference_count: u64,
|
||||
pub query_inference_count: u64,
|
||||
}
|
||||
|
||||
let mut results: Vec<BenchResult> = vec![];
|
||||
|
||||
for (_, r) in prolog_benches() {
|
||||
let mut machine = r.make_machine();
|
||||
let setup_inference_count = machine.get_inference_count();
|
||||
|
||||
let result = machine.run_query(r.query.to_string()).unwrap();
|
||||
let query_inference_count = machine.get_inference_count() - setup_inference_count;
|
||||
|
||||
let expected = QueryResolution::Matches(vec![QueryMatch::from(r.bindings.clone())]);
|
||||
assert_eq!(result, expected, "validating benchmark {}", r.name);
|
||||
|
||||
results.push(BenchResult {
|
||||
name: r.name,
|
||||
setup_inference_count,
|
||||
query_inference_count,
|
||||
})
|
||||
}
|
||||
|
||||
let mut json: String = Default::default();
|
||||
json.push('[');
|
||||
for r in results {
|
||||
json.push('\n');
|
||||
write!(
|
||||
json,
|
||||
r#"{{"name":"{}","setup_inference_count":{},"query_inference_count":{}}},"#,
|
||||
r.name, r.setup_inference_count, r.query_inference_count
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
json.pop(); // trailing comma
|
||||
json.push_str("\n]");
|
||||
fs::write("target/benchmark_inference_counts.json", json).expect("Unable to write file");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -55,7 +55,7 @@ fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("libraries.rs");
|
||||
|
||||
let mut libraries = File::create(&dest_path).unwrap();
|
||||
let mut libraries = File::create(dest_path).unwrap();
|
||||
let lib_path = Path::new("src/lib");
|
||||
|
||||
libraries
|
||||
@@ -66,7 +66,7 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
find_prolog_files(&mut libraries, "", &lib_path);
|
||||
find_prolog_files(&mut libraries, "", lib_path);
|
||||
libraries.write_all(b"\n m\n };\n}\n").unwrap();
|
||||
|
||||
let instructions_path = Path::new(&out_dir).join("instructions.rs");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::*;
|
||||
use syn::parse::*;
|
||||
use syn::visit::*;
|
||||
use syn::*;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
@@ -11,7 +11,9 @@ struct StaticStrVisitor {
|
||||
|
||||
impl StaticStrVisitor {
|
||||
fn new() -> Self {
|
||||
Self { static_strs: IndexSet::new() }
|
||||
Self {
|
||||
static_strs: IndexSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +35,7 @@ impl Parse for ReadHeapCellExprAndArms {
|
||||
arms.push(input.parse()?);
|
||||
|
||||
while !input.is_empty() {
|
||||
if let Ok(_) = input.parse::<Token![,]>() {}
|
||||
let _ = input.parse::<Token![,]>();
|
||||
arms.push(input.parse()?);
|
||||
}
|
||||
|
||||
@@ -50,7 +52,7 @@ impl Parse for MacroFnArgs {
|
||||
}
|
||||
|
||||
while !input.is_empty() {
|
||||
if let Ok(_) = input.parse::<Token![,]>() {}
|
||||
let _ = input.parse::<Token![,]>();
|
||||
args.push(input.parse()?);
|
||||
}
|
||||
|
||||
@@ -63,22 +65,20 @@ impl<'ast> Visit<'ast> for StaticStrVisitor {
|
||||
let Macro { path, .. } = m;
|
||||
|
||||
if path.is_ident("atom") {
|
||||
if let Some(Lit::Str(string)) = m.parse_body::<Lit>().ok() {
|
||||
if let Ok(Lit::Str(string)) = m.parse_body::<Lit>() {
|
||||
self.static_strs.insert(string.value());
|
||||
}
|
||||
} else if path.is_ident("read_heap_cell") || path.is_ident("match_untyped_arena_ptr") {
|
||||
if let Some(m) = m.parse_body::<ReadHeapCellExprAndArms>().ok() {
|
||||
if let Ok(m) = m.parse_body::<ReadHeapCellExprAndArms>() {
|
||||
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);
|
||||
}
|
||||
} else if let Ok(m) = m.parse_body::<MacroFnArgs>() {
|
||||
for e in m.args {
|
||||
self.visit_expr(&e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,9 +145,8 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
|
||||
visitor.visit_file(&syntax);
|
||||
}
|
||||
|
||||
match process_filepath(instruction_rs_path) {
|
||||
Ok(syntax) => visitor.visit_file(&syntax),
|
||||
Err(_) => {}
|
||||
if let Ok(syntax) = process_filepath(instruction_rs_path) {
|
||||
visitor.visit_file(&syntax)
|
||||
}
|
||||
|
||||
let indices = (0..visitor.static_strs.len()).map(|i| (i << 3) as u64);
|
||||
@@ -159,7 +158,7 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
|
||||
quote! {
|
||||
use phf;
|
||||
|
||||
static STRINGS: [&'static str; #static_strs_len] = [
|
||||
static STRINGS: [&str; #static_strs_len] = [
|
||||
#(
|
||||
#static_strs,
|
||||
)*
|
||||
|
||||
@@ -24,11 +24,12 @@ pub(crate) trait Allocator {
|
||||
code: &mut CodeDeque,
|
||||
);
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
cell: &Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
r: RegType,
|
||||
@@ -41,51 +42,16 @@ pub(crate) trait Allocator {
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
cell: &Cell<VarReg>,
|
||||
context: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
);
|
||||
|
||||
fn reset(&mut self);
|
||||
fn reset_arg(&mut self, arg_num: usize);
|
||||
fn reset_at_head(&mut self, args: &Vec<Term>);
|
||||
fn reset_at_head(&mut self, args: &[Term]);
|
||||
fn reset_contents(&mut self);
|
||||
|
||||
fn advance_arg(&mut self);
|
||||
|
||||
/*
|
||||
fn bindings(&self) -> &AllocVarDict;
|
||||
fn bindings_mut(&mut self) -> &mut AllocVarDict;
|
||||
fn take_bindings(self) -> AllocVarDict;
|
||||
*/
|
||||
|
||||
fn max_reg_allocated(&self) -> usize;
|
||||
|
||||
// TODO: wha.. why?? grrr. it drains the VarStatus data from vs (which it owns!)
|
||||
// into self.bindings and perm_vs after all is computed (i.e. vs.populate_restricting_sets()
|
||||
// and vs.set_perm_vals(has_deep_cut) have both been called).
|
||||
/*
|
||||
fn drain_var_data<'a>(
|
||||
&mut self,
|
||||
vs: VariableFixtures,
|
||||
num_of_chunks: usize,
|
||||
) -> VariableFixtures {
|
||||
let mut perm_vs = VariableFixtures::new();
|
||||
|
||||
for (var, var_status) in vs.into_iter() {
|
||||
match var_status {
|
||||
VarStatus::Temp(chunk_num, tvd) => {
|
||||
self.bindings_mut()
|
||||
.insert(var.clone(), VarAlloc::Temp(chunk_num, 0, tvd));
|
||||
}
|
||||
VarStatus::Perm(_) => {
|
||||
self.bindings_mut().insert(var.clone(), VarAlloc::Perm(0));
|
||||
perm_vs.insert(var, var_status);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
perm_vs
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
415
src/arena.rs
415
src/arena.rs
@@ -4,25 +4,31 @@ use crate::machine::loader::LiveLoadState;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::machine::streams::*;
|
||||
use crate::raw_block::*;
|
||||
use crate::rcu::Rcu;
|
||||
use crate::rcu::RcuRef;
|
||||
use crate::read::*;
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use std::alloc;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
use std::net::TcpListener;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ptr;
|
||||
use std::sync::RwLock;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! arena_alloc {
|
||||
($e:expr, $arena:expr) => {{
|
||||
let result = $e;
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { ArenaAllocated::alloc($arena, result) }
|
||||
unsafe {
|
||||
ArenaAllocated::alloc($arena, result)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -31,24 +37,36 @@ macro_rules! float_alloc {
|
||||
($e:expr, $arena:expr) => {{
|
||||
let result = $e;
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { $arena.f64_tbl.build_with(result) }
|
||||
unsafe {
|
||||
$arena.f64_tbl.build_with(result).as_ptr()
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::Weak;
|
||||
|
||||
const F64_TABLE_INIT_SIZE: usize = 1 << 16;
|
||||
const F64_TABLE_ALIGN: usize = 8;
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
static F64_TABLE_BUF_BASE: RefCell<*const u8> = RefCell::new(ptr::null_mut());
|
||||
#[inline(always)]
|
||||
fn global_f64table() -> &'static RwLock<Weak<F64Table>> {
|
||||
#[cfg(feature = "rust_beta_channel")]
|
||||
{
|
||||
// const Weak::new will be stabilized in 1.73 which is currently in beta,
|
||||
// till then we need a OnceLock for initialization
|
||||
static GLOBAL_ATOM_TABLE: RwLock<Weak<F64Table>> = RwLock::const_new(Weak::new());
|
||||
&GLOBAL_ATOM_TABLE
|
||||
}
|
||||
#[cfg(not(feature = "rust_beta_channel"))]
|
||||
{
|
||||
use std::sync::OnceLock;
|
||||
static GLOBAL_ATOM_TABLE: OnceLock<RwLock<Weak<F64Table>>> = OnceLock::new();
|
||||
GLOBAL_ATOM_TABLE.get_or_init(|| RwLock::new(Weak::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
static mut F64_TABLE_BUF_BASE: *const u8 = ptr::null_mut();
|
||||
|
||||
impl RawBlockTraits for F64Table {
|
||||
#[inline]
|
||||
fn init_size() -> usize {
|
||||
@@ -63,69 +81,86 @@ impl RawBlockTraits for F64Table {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct F64Table {
|
||||
block: RawBlock<F64Table>,
|
||||
}
|
||||
|
||||
impl Drop for F64Table {
|
||||
fn drop(&mut self) {
|
||||
self.block.deallocate();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn set_f64_tbl_buf_base(ptr: *const u8) {
|
||||
F64_TABLE_BUF_BASE.with(|f64_table_buf_base| {
|
||||
*f64_table_buf_base.borrow_mut() = ptr;
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn get_f64_tbl_buf_base() -> *const u8 {
|
||||
F64_TABLE_BUF_BASE.with(|f64_table_buf_base| *f64_table_buf_base.borrow())
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn set_f64_tbl_buf_base(ptr: *const u8) {
|
||||
unsafe {
|
||||
F64_TABLE_BUF_BASE = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub(crate) fn get_f64_tbl_buf_base() -> *const u8 {
|
||||
unsafe { F64_TABLE_BUF_BASE }
|
||||
block: Rcu<RawBlock<F64Table>>,
|
||||
update: Mutex<()>,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn lookup_float(offset: usize) -> *mut OrderedFloat<f64> {
|
||||
let base = get_f64_tbl_buf_base() as usize;
|
||||
(base + offset) as *mut _
|
||||
pub fn lookup_float(
|
||||
offset: F64Offset,
|
||||
) -> RcuRef<RawBlock<F64Table>, UnsafeCell<OrderedFloat<f64>>> {
|
||||
let f64table = global_f64table()
|
||||
.read()
|
||||
.unwrap()
|
||||
.upgrade()
|
||||
.expect("We should only be looking up floats while there is a float table");
|
||||
|
||||
RcuRef::try_map(f64table.block.active_epoch(), |raw_block| unsafe {
|
||||
raw_block
|
||||
.base
|
||||
.add(offset.0)
|
||||
.cast_mut()
|
||||
.cast::<UnsafeCell<OrderedFloat<f64>>>()
|
||||
.as_ref()
|
||||
})
|
||||
.expect("The offset should result in a non-null pointer")
|
||||
}
|
||||
|
||||
impl F64Table {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
let table = Self { block: RawBlock::new() };
|
||||
set_f64_tbl_buf_base(table.block.base);
|
||||
table
|
||||
pub fn new() -> Arc<Self> {
|
||||
let upgraded = global_f64table().read().unwrap().upgrade();
|
||||
// don't inline upgraded, otherwise temporary will be dropped too late in case of None
|
||||
if let Some(atom_table) = upgraded {
|
||||
atom_table
|
||||
} else {
|
||||
let mut guard = global_f64table().write().unwrap();
|
||||
// try to upgrade again in case we lost the race on the write lock
|
||||
if let Some(atom_table) = guard.upgrade() {
|
||||
atom_table
|
||||
} else {
|
||||
let atom_table = Arc::new(Self {
|
||||
block: Rcu::new(RawBlock::new()),
|
||||
update: Mutex::new(()),
|
||||
});
|
||||
*guard = Arc::downgrade(&atom_table);
|
||||
atom_table
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn build_with(&mut self, value: f64) -> F64Ptr {
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn build_with(&self, value: f64) -> F64Offset {
|
||||
let update_guard = self.update.lock();
|
||||
|
||||
// we don't have an index table for lookups as AtomTable does so
|
||||
// just get the epoch after we take the upgrade lock
|
||||
let mut block_epoch = self.block.active_epoch();
|
||||
|
||||
let mut ptr;
|
||||
|
||||
loop {
|
||||
ptr = self.block.alloc(mem::size_of::<f64>());
|
||||
ptr = block_epoch.alloc(mem::size_of::<f64>());
|
||||
|
||||
if ptr.is_null() {
|
||||
self.block.grow();
|
||||
set_f64_tbl_buf_base(self.block.base);
|
||||
let new_block = block_epoch.grow_new().unwrap();
|
||||
self.block.replace(new_block);
|
||||
block_epoch = self.block.active_epoch();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ptr::write(ptr as *mut OrderedFloat<f64>, OrderedFloat(value));
|
||||
F64Ptr(ptr::NonNull::new_unchecked(ptr as *mut _))
|
||||
|
||||
let float = F64Offset(ptr as usize - block_epoch.base as usize);
|
||||
|
||||
// atometable would have to update the index table at this point
|
||||
|
||||
// expicit drop to ensure we don't accidentally drop it early
|
||||
drop(update_guard);
|
||||
|
||||
float
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,14 +169,13 @@ impl F64Table {
|
||||
pub enum ArenaHeaderTag {
|
||||
Integer = 0b10,
|
||||
Rational = 0b11,
|
||||
OssifiedOpDir = 0b0000100,
|
||||
LiveLoadState = 0b0001000,
|
||||
InactiveLoadState = 0b1011000,
|
||||
InputFileStream = 0b10000,
|
||||
OutputFileStream = 0b10100,
|
||||
NamedTcpStream = 0b011100,
|
||||
NamedTlsStream = 0b100000,
|
||||
HttpReadStream = 0b100001,
|
||||
HttpReadStream = 0b100001,
|
||||
HttpWriteStream = 0b100010,
|
||||
ReadlineStream = 0b110000,
|
||||
StaticStringStream = 0b110100,
|
||||
@@ -195,7 +229,7 @@ impl<T: ?Sized + PartialOrd> PartialOrd for TypedArenaPtr<T> {
|
||||
|
||||
impl<T: ?Sized + PartialEq> PartialEq for TypedArenaPtr<T> {
|
||||
fn eq(&self, other: &TypedArenaPtr<T>) -> bool {
|
||||
self.0 == other.0 || &**self == &**other
|
||||
self.0 == other.0 || **self == **other
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,13 +244,13 @@ impl<T: ?Sized + Ord> Ord for TypedArenaPtr<T> {
|
||||
impl<T: ?Sized + Hash> Hash for TypedArenaPtr<T> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
(&*self as &T).hash(hasher)
|
||||
(self as &T).hash(hasher)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Clone for TypedArenaPtr<T> {
|
||||
fn clone(&self) -> Self {
|
||||
TypedArenaPtr(self.0)
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,10 +278,10 @@ impl<T: fmt::Display> fmt::Display for TypedArenaPtr<T> {
|
||||
|
||||
impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> {
|
||||
// data must be allocated in the arena already.
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[inline]
|
||||
pub const fn new(data: *mut T) -> Self {
|
||||
let result = unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) };
|
||||
result
|
||||
unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -276,7 +310,9 @@ impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> {
|
||||
|
||||
#[inline]
|
||||
pub fn set_tag(&mut self, tag: ArenaHeaderTag) {
|
||||
unsafe { (*self.header_ptr_mut()).set_tag(tag); }
|
||||
unsafe {
|
||||
(*self.header_ptr_mut()).set_tag(tag);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -310,13 +346,14 @@ pub trait ArenaAllocated: Sized {
|
||||
mem::size_of::<ArenaHeader>()
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
unsafe fn alloc(arena: &mut Arena, value: Self) -> Self::PtrToAllocated {
|
||||
let size = value.size() + mem::size_of::<AllocSlab>();
|
||||
|
||||
#[cfg(target_pointer_width="32")]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let align = mem::align_of::<AllocSlab>() * 2;
|
||||
|
||||
#[cfg(target_pointer_width="64")]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let align = mem::align_of::<AllocSlab>();
|
||||
let layout = alloc::Layout::from_size_align_unchecked(size, align);
|
||||
|
||||
@@ -326,7 +363,7 @@ pub trait ArenaAllocated: Sized {
|
||||
(*slab).header = ArenaHeader::build_with(value.size() as u64, Self::tag());
|
||||
|
||||
let offset = (*slab).payload_offset();
|
||||
let result = value.copy_to_arena(offset as *mut Self);
|
||||
let result = value.copy_to_arena(offset);
|
||||
|
||||
arena.base = slab;
|
||||
|
||||
@@ -334,12 +371,18 @@ pub trait ArenaAllocated: Sized {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct F64Ptr(pub ptr::NonNull<OrderedFloat<f64>>);
|
||||
#[derive(Debug)]
|
||||
pub struct F64Ptr(RcuRef<RawBlock<F64Table>, UnsafeCell<OrderedFloat<f64>>>);
|
||||
|
||||
impl Clone for F64Ptr {
|
||||
fn clone(&self) -> Self {
|
||||
Self(RcuRef::clone(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for F64Ptr {
|
||||
fn eq(&self, other: &F64Ptr) -> bool {
|
||||
self.0 == other.0 || &**self == &**other
|
||||
RcuRef::ptr_eq(&self.0, &other.0) || self.deref() == other.deref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +390,7 @@ impl Eq for F64Ptr {}
|
||||
|
||||
impl PartialOrd for F64Ptr {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
(**self).partial_cmp(&**other)
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,13 +403,13 @@ impl Ord for F64Ptr {
|
||||
impl Hash for F64Ptr {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
(&*self as &OrderedFloat<f64>).hash(hasher)
|
||||
(self as &OrderedFloat<f64>).hash(hasher)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for F64Ptr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", *self)
|
||||
write!(f, "{}", self as &OrderedFloat<f64>)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,28 +418,26 @@ impl Deref for F64Ptr {
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.0.as_ptr() }
|
||||
unsafe { self.0.get().as_ref().unwrap() }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for F64Ptr {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.0.as_ptr() }
|
||||
unsafe { &mut *self.0.get().as_mut().unwrap() }
|
||||
}
|
||||
}
|
||||
|
||||
impl F64Ptr {
|
||||
#[inline(always)]
|
||||
pub fn from_offset(offset: usize) -> Self {
|
||||
unsafe {
|
||||
F64Ptr(ptr::NonNull::new_unchecked(lookup_float(offset)))
|
||||
}
|
||||
pub fn from_offset(offset: F64Offset) -> Self {
|
||||
Self(lookup_float(offset))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_offset(&self) -> F64Offset {
|
||||
F64Offset(self.0.as_ptr() as usize - get_f64_tbl_buf_base() as usize)
|
||||
F64Offset(self.0.get() as usize - RcuRef::get_root(&self.0).base as usize)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +457,7 @@ impl F64Offset {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(self) -> F64Ptr {
|
||||
F64Ptr::from_offset(self.0)
|
||||
F64Ptr::from_offset(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -437,7 +478,7 @@ impl Eq for F64Offset {}
|
||||
impl PartialOrd for F64Offset {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
self.as_ptr().partial_cmp(&other.as_ptr())
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,11 +515,12 @@ impl ArenaAllocated for Integer {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
TypedArenaPtr::new(dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,33 +538,12 @@ impl ArenaAllocated for Rational {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[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)
|
||||
TypedArenaPtr::new(dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,11 +561,12 @@ impl ArenaAllocated for LiveLoadState {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
TypedArenaPtr::new(dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -562,11 +584,12 @@ impl ArenaAllocated for TcpListener {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
TypedArenaPtr::new(dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -577,20 +600,21 @@ impl ArenaAllocated for HttpListener {
|
||||
|
||||
#[inline]
|
||||
fn tag() -> ArenaHeaderTag {
|
||||
ArenaHeaderTag::HttpListener
|
||||
ArenaHeaderTag::HttpListener
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<Self>()
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
}
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,20 +624,21 @@ impl ArenaAllocated for HttpResponse {
|
||||
|
||||
#[inline]
|
||||
fn tag() -> ArenaHeaderTag {
|
||||
ArenaHeaderTag::HttpResponse
|
||||
ArenaHeaderTag::HttpResponse
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<Self>()
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
}
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,11 +655,12 @@ impl ArenaAllocated for IndexPtr {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
TypedArenaPtr::new(dst)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,7 +679,10 @@ impl ArenaAllocated for IndexPtr {
|
||||
|
||||
(*slab).next = arena.base;
|
||||
|
||||
let result = value.copy_to_arena(mem::transmute::<_, *mut IndexPtr>(&(*slab).header));
|
||||
let result = value.copy_to_arena(
|
||||
&(*slab).header as *const crate::arena::ArenaHeader
|
||||
as *mut crate::machine::machine_indices::IndexPtr,
|
||||
);
|
||||
arena.base = slab;
|
||||
|
||||
result
|
||||
@@ -664,7 +693,7 @@ impl ArenaAllocated for IndexPtr {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct AllocSlab {
|
||||
next: *mut AllocSlab,
|
||||
#[cfg(target_pointer_width="32")]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
_padding: u32,
|
||||
header: ArenaHeader,
|
||||
}
|
||||
@@ -672,16 +701,20 @@ struct AllocSlab {
|
||||
#[derive(Debug)]
|
||||
pub struct Arena {
|
||||
base: *mut AllocSlab,
|
||||
pub f64_tbl: F64Table,
|
||||
pub f64_tbl: Arc<F64Table>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Arena {}
|
||||
unsafe impl Sync for Arena {}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl Arena {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Arena { base: ptr::null_mut(), f64_tbl: F64Table::new() }
|
||||
Arena {
|
||||
base: ptr::null_mut(),
|
||||
f64_tbl: F64Table::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,10 +745,10 @@ unsafe fn drop_slab_in_place(value: &mut AllocSlab) {
|
||||
#[cfg(feature = "http")]
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<HttpReadStream>>>());
|
||||
}
|
||||
ArenaHeaderTag::HttpWriteStream => {
|
||||
ArenaHeaderTag::HttpWriteStream => {
|
||||
#[cfg(feature = "http")]
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<HttpWriteStream>>>());
|
||||
}
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<HttpWriteStream>>>());
|
||||
}
|
||||
ArenaHeaderTag::ReadlineStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<ReadlineStream>>());
|
||||
}
|
||||
@@ -725,35 +758,32 @@ unsafe fn drop_slab_in_place(value: &mut AllocSlab) {
|
||||
ArenaHeaderTag::ByteStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<ByteStream>>>());
|
||||
}
|
||||
ArenaHeaderTag::OssifiedOpDir => {
|
||||
ptr::drop_in_place(value.payload_offset::<OssifiedOpDir>());
|
||||
}
|
||||
ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => {
|
||||
ptr::drop_in_place(value.payload_offset::<LiveLoadState>());
|
||||
}
|
||||
ArenaHeaderTag::Dropped => {
|
||||
}
|
||||
ArenaHeaderTag::Dropped => {}
|
||||
ArenaHeaderTag::TcpListener => {
|
||||
ptr::drop_in_place(value.payload_offset::<TcpListener>());
|
||||
}
|
||||
ArenaHeaderTag::HttpListener => {
|
||||
ArenaHeaderTag::HttpListener => {
|
||||
#[cfg(feature = "http")]
|
||||
ptr::drop_in_place(value.payload_offset::<HttpListener>());
|
||||
}
|
||||
ArenaHeaderTag::HttpResponse => {
|
||||
ptr::drop_in_place(value.payload_offset::<HttpListener>());
|
||||
}
|
||||
ArenaHeaderTag::HttpResponse => {
|
||||
#[cfg(feature = "http")]
|
||||
ptr::drop_in_place(value.payload_offset::<HttpResponse>());
|
||||
}
|
||||
ptr::drop_in_place(value.payload_offset::<HttpResponse>());
|
||||
}
|
||||
ArenaHeaderTag::StandardOutputStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<StandardOutputStream>>());
|
||||
}
|
||||
ArenaHeaderTag::StandardErrorStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<StandardErrorStream>>());
|
||||
}
|
||||
ArenaHeaderTag::NullStream | ArenaHeaderTag::IndexPtrUndefined |
|
||||
ArenaHeaderTag::IndexPtrDynamicUndefined | ArenaHeaderTag::IndexPtrDynamicIndex |
|
||||
ArenaHeaderTag::IndexPtrIndex => {
|
||||
}
|
||||
ArenaHeaderTag::NullStream
|
||||
| ArenaHeaderTag::IndexPtrUndefined
|
||||
| ArenaHeaderTag::IndexPtrDynamicUndefined
|
||||
| ArenaHeaderTag::IndexPtrDynamicIndex
|
||||
| ArenaHeaderTag::IndexPtrIndex => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,9 +821,9 @@ impl AllocSlab {
|
||||
}
|
||||
|
||||
fn payload_offset<T>(&self) -> *mut T {
|
||||
let mut ptr = (self as *const AllocSlab) as usize;
|
||||
ptr += mem::size_of::<AllocSlab>();
|
||||
ptr as *mut T
|
||||
// This looks really scary, should this method be marked as unsafe?
|
||||
// Also, this seems to cause UB.
|
||||
unsafe { (self as *const AllocSlab).add(1) as *mut T }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,27 +831,29 @@ const_assert!(mem::size_of::<OrderedFloat<f64>>() == 8);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::machine::mock_wam::*;
|
||||
use crate::machine::partial_string::*;
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
#[test]
|
||||
fn float_ptr_cast() {
|
||||
let mut wam = MockWAM::new();
|
||||
let wam = MockWAM::new();
|
||||
|
||||
let f = 0f64;
|
||||
let fp = float_alloc!(f, wam.machine_st.arena);
|
||||
let mut cell = HeapCellValue::from(fp);
|
||||
let mut cell = HeapCellValue::from(fp.clone());
|
||||
|
||||
assert_eq!(cell.get_tag(), HeapCellValueTag::F64);
|
||||
assert_eq!(cell.get_mark_bit(), false);
|
||||
assert_eq!(*fp, OrderedFloat(f));
|
||||
assert!(!cell.get_mark_bit());
|
||||
assert_eq!(fp.deref(), &OrderedFloat(f));
|
||||
|
||||
cell.set_mark_bit(true);
|
||||
|
||||
assert_eq!(cell.get_mark_bit(), true);
|
||||
assert!(cell.get_mark_bit());
|
||||
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::F64, ptr) => {
|
||||
@@ -832,14 +864,15 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
fn heap_cell_value_const_cast() {
|
||||
let mut wam = MockWAM::new();
|
||||
#[cfg(target_pointer_width="32")]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
let const_value = HeapCellValue::from(ConsPtr::build_with(
|
||||
0x0000_0431 as *const _,
|
||||
ConsPtrMaskTag::Cons,
|
||||
));
|
||||
#[cfg(target_pointer_width="64")]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
let const_value = HeapCellValue::from(ConsPtr::build_with(
|
||||
0x0000_5555_ff00_0431 as *const _,
|
||||
ConsPtrMaskTag::Cons,
|
||||
@@ -847,10 +880,13 @@ mod tests {
|
||||
|
||||
match const_value.to_untyped_arena_ptr() {
|
||||
Some(arena_ptr) => {
|
||||
assert_eq!(arena_ptr.into_bytes(), const_value.to_untyped_arena_ptr_bytes());
|
||||
assert_eq!(
|
||||
arena_ptr.into_bytes(),
|
||||
const_value.to_untyped_arena_ptr_bytes()
|
||||
);
|
||||
}
|
||||
None => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -860,23 +896,26 @@ mod tests {
|
||||
|
||||
match stream_cell.to_untyped_arena_ptr() {
|
||||
Some(arena_ptr) => {
|
||||
assert_eq!(arena_ptr.into_bytes(), stream_cell.to_untyped_arena_ptr_bytes());
|
||||
assert_eq!(
|
||||
arena_ptr.into_bytes(),
|
||||
stream_cell.to_untyped_arena_ptr_bytes()
|
||||
);
|
||||
}
|
||||
None => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on arena.rs UB")]
|
||||
fn heap_put_literal_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
// integer
|
||||
|
||||
let big_int: Integer = 2 * Integer::from(1u64 << 63);
|
||||
let big_int_ptr: TypedArenaPtr<Integer> =
|
||||
arena_alloc!(big_int, &mut wam.machine_st.arena);
|
||||
let big_int_ptr: TypedArenaPtr<Integer> = arena_alloc!(big_int, &mut wam.machine_st.arena);
|
||||
|
||||
assert!(!big_int_ptr.as_ptr().is_null());
|
||||
|
||||
@@ -886,7 +925,6 @@ mod tests {
|
||||
let untyped_arena_ptr = match cell.to_untyped_arena_ptr() {
|
||||
Some(ptr) => ptr,
|
||||
None => {
|
||||
assert!(false);
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
@@ -928,7 +966,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
None => {
|
||||
assert!(false); // we fail.
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -951,8 +989,8 @@ mod tests {
|
||||
let f_atom = atom!("f");
|
||||
let g_atom = atom!("g");
|
||||
|
||||
assert_eq!(f_atom.as_str(), "f");
|
||||
assert_eq!(g_atom.as_str(), "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);
|
||||
@@ -962,10 +1000,10 @@ mod tests {
|
||||
match f_atom_cell.to_atom() {
|
||||
Some(atom) => {
|
||||
assert_eq!(f_atom, atom);
|
||||
assert_eq!(atom.as_str(), "f");
|
||||
assert_eq!(&*atom.as_str(), "f");
|
||||
}
|
||||
None => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -973,7 +1011,7 @@ mod tests {
|
||||
(HeapCellValueTag::Atom, (atom, arity)) => {
|
||||
assert_eq!(f_atom, atom);
|
||||
assert_eq!(arity, 0);
|
||||
assert_eq!(atom.as_str(), "f");
|
||||
assert_eq!(&*atom.as_str(), "f");
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
@@ -982,31 +1020,32 @@ mod tests {
|
||||
(HeapCellValueTag::Atom, (atom, arity)) => {
|
||||
assert_eq!(g_atom, atom);
|
||||
assert_eq!(arity, 0);
|
||||
assert_eq!(atom.as_str(), "g");
|
||||
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_var_cell =
|
||||
put_partial_string(&mut wam.machine_st.heap, "ronan", &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");
|
||||
assert_eq!(&*pstr.as_str_from(0), "ronan");
|
||||
}
|
||||
None => {
|
||||
assert!(false);
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
read_heap_cell!(pstr_cell,
|
||||
(HeapCellValueTag::PStr, pstr_atom) => {
|
||||
let pstr = PartialString::from(pstr_atom);
|
||||
assert_eq!(pstr.as_str_from(0), "ronan");
|
||||
assert_eq!(&*pstr.as_str_from(0), "ronan");
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
@@ -1019,7 +1058,7 @@ mod tests {
|
||||
|
||||
match fixnum_cell.to_fixnum() {
|
||||
Some(n) => assert_eq!(n.get_num(), 3),
|
||||
None => assert!(false),
|
||||
None => unreachable!(),
|
||||
}
|
||||
|
||||
read_heap_cell!(fixnum_cell,
|
||||
@@ -1035,52 +1074,48 @@ mod tests {
|
||||
|
||||
match fixnum_b_cell.to_fixnum() {
|
||||
Some(n) => assert_eq!(n.get_num(), 1 << 54),
|
||||
None => assert!(false),
|
||||
None => unreachable!(),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(1 << 56) {
|
||||
Ok(_) => assert!(false),
|
||||
_ => assert!(true),
|
||||
if Fixnum::build_with_checked(1 << 56).is_ok() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(i64::MAX) {
|
||||
Ok(_) => assert!(false),
|
||||
_ => assert!(true),
|
||||
if Fixnum::build_with_checked(i64::MAX).is_ok() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(i64::MIN) {
|
||||
Ok(_) => assert!(false),
|
||||
_ => assert!(true),
|
||||
if Fixnum::build_with_checked(i64::MIN).is_ok() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(-1) {
|
||||
Ok(n) => assert_eq!(n.get_num(), -1),
|
||||
_ => assert!(false),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked((1 << 55) - 1) {
|
||||
Ok(n) => assert_eq!(n.get_num(), (1 << 55) - 1),
|
||||
_ => assert!(false),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(-(1 << 55)) {
|
||||
Ok(n) => assert_eq!(n.get_num(), -(1 << 55)),
|
||||
_ => assert!(false),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(-(1 << 55) - 1) {
|
||||
Ok(_n) => assert!(false),
|
||||
_ => assert!(true),
|
||||
if Fixnum::build_with_checked(-(1 << 55) - 1).is_ok() {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(-1) {
|
||||
Ok(n) => assert_eq!(-n, Fixnum::build_with(1)),
|
||||
_ => assert!(false),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// float
|
||||
|
||||
let float = 3.1415926f64;
|
||||
let float = std::f64::consts::PI;
|
||||
let float_ptr = float_alloc!(float, wam.machine_st.arena);
|
||||
let cell = HeapCellValue::from(float_ptr);
|
||||
|
||||
@@ -1115,7 +1150,7 @@ mod tests {
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::Atom, (el, _arity)) => {
|
||||
assert_eq!(el.flat_index(), empty_list_as_cell!().get_value());
|
||||
assert_eq!(el.as_str(), "[]");
|
||||
assert_eq!(&*el.as_str(), "[]");
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
|
||||
@@ -14,6 +14,8 @@ use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::machine::machine_errors::*;
|
||||
|
||||
use dashu::base::Abs;
|
||||
use dashu::base::BitTest;
|
||||
use num_order::NumOrd;
|
||||
use ordered_float::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
@@ -158,13 +160,13 @@ fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), Ari
|
||||
Literal::Float(n) => interm.push(ArithmeticTerm::Number(Number::Float(*n.as_ptr()))),
|
||||
Literal::Rational(n) => interm.push(ArithmeticTerm::Number(Number::Rational(*n))),
|
||||
Literal::Atom(name) if name == &atom!("e") => interm.push(ArithmeticTerm::Number(
|
||||
Number::Float(OrderedFloat(std::f64::consts::E))
|
||||
Number::Float(OrderedFloat(std::f64::consts::E)),
|
||||
)),
|
||||
Literal::Atom(name) if name == &atom!("pi") => interm.push(ArithmeticTerm::Number(
|
||||
Number::Float(OrderedFloat(std::f64::consts::PI))
|
||||
Number::Float(OrderedFloat(std::f64::consts::PI)),
|
||||
)),
|
||||
Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
|
||||
Number::Float(OrderedFloat(std::f64::EPSILON))
|
||||
Number::Float(OrderedFloat(std::f64::EPSILON)),
|
||||
)),
|
||||
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
|
||||
}
|
||||
@@ -195,13 +197,6 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
atom!("sin") => Ok(Instruction::Sin(a1, t)),
|
||||
atom!("tan") => Ok(Instruction::Tan(a1, t)),
|
||||
atom!("log") => Ok(Instruction::Log(a1, t)),
|
||||
atom!("asinh") => Ok(Instruction::ASinh(a1, t)),
|
||||
atom!("acosh") => Ok(Instruction::ACosh(a1, t)),
|
||||
atom!("atanh") => Ok(Instruction::ATanh(a1, t)),
|
||||
atom!("sinh") => Ok(Instruction::Sinh(a1, t)),
|
||||
atom!("cosh") => Ok(Instruction::Cosh(a1, t)),
|
||||
atom!("tanh") => Ok(Instruction::Tanh(a1, t)),
|
||||
atom!("log10") => Ok(Instruction::Log10(a1, t)),
|
||||
atom!("exp") => Ok(Instruction::Exp(a1, t)),
|
||||
atom!("sqrt") => Ok(Instruction::Sqrt(a1, t)),
|
||||
atom!("acos") => Ok(Instruction::ACos(a1, t)),
|
||||
@@ -273,7 +268,7 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
let ninterm = if a1.interm_or(0) == 0 {
|
||||
self.incr_interm()
|
||||
} else {
|
||||
self.interm.push(a1.clone());
|
||||
self.interm.push(a1);
|
||||
a1.interm_or(0)
|
||||
};
|
||||
|
||||
@@ -317,32 +312,22 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
arg: usize,
|
||||
) -> Result<ArithCont, ArithmeticError> {
|
||||
let mut code = CodeDeque::new();
|
||||
let mut iter = src.iter()?;
|
||||
|
||||
while let Some(term_ref) = iter.next() {
|
||||
for term_ref in src.iter()? {
|
||||
match term_ref? {
|
||||
ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?,
|
||||
ArithTermRef::Var(lvl, cell, name) => {
|
||||
let var_num = name.to_var_num().unwrap();
|
||||
|
||||
let r = if lvl == Level::Shallow {
|
||||
self.marker.mark_non_callable(
|
||||
var_num,
|
||||
arg,
|
||||
term_loc,
|
||||
cell,
|
||||
&mut code,
|
||||
)
|
||||
self.marker
|
||||
.mark_non_callable(var_num, arg, term_loc, cell, &mut code)
|
||||
} else if term_loc.is_last() || cell.get().norm().reg_num() == 0 {
|
||||
let r = self.marker.get_binding(var_num);
|
||||
|
||||
if r.reg_num() == 0 {
|
||||
self.marker.mark_var::<QueryInstruction>(
|
||||
var_num,
|
||||
lvl,
|
||||
cell,
|
||||
term_loc,
|
||||
&mut code,
|
||||
var_num, lvl, cell, term_loc, &mut code,
|
||||
);
|
||||
cell.get().norm()
|
||||
} else {
|
||||
@@ -367,16 +352,17 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
}
|
||||
|
||||
// integer division rounding function -- 9.1.3.1.
|
||||
pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number {
|
||||
pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Number {
|
||||
match n {
|
||||
&Number::Integer(i) => {
|
||||
if let Some(n) = i.to_i64() {
|
||||
fixnum!(Number, n, arena)
|
||||
let result = (&*i).try_into();
|
||||
if let Ok(value) = result {
|
||||
fixnum!(Number, value, arena)
|
||||
} else {
|
||||
*n
|
||||
}
|
||||
}
|
||||
&Number::Fixnum(_) => *n,
|
||||
Number::Fixnum(_) => *n,
|
||||
&Number::Float(f) => {
|
||||
let f = f.floor();
|
||||
|
||||
@@ -389,11 +375,11 @@ pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number {
|
||||
Number::Integer(arena_alloc!(Integer::from(f.0 as i64), arena))
|
||||
}
|
||||
}
|
||||
&Number::Rational(ref r) => {
|
||||
Number::Rational(ref r) => {
|
||||
let (_, floor) = (r.fract(), r.floor());
|
||||
|
||||
if let Some(floor) = floor.to_i64() {
|
||||
fixnum!(Number, floor, arena)
|
||||
if let Ok(value) = (&floor).try_into() {
|
||||
fixnum!(Number, value, arena)
|
||||
} else {
|
||||
Number::Integer(arena_alloc!(floor, arena))
|
||||
}
|
||||
@@ -412,9 +398,9 @@ impl From<Fixnum> for Integer {
|
||||
pub(crate) fn rnd_f(n: &Number) -> f64 {
|
||||
match n {
|
||||
&Number::Fixnum(n) => n.get_num() as f64,
|
||||
&Number::Integer(ref n) => n.to_f64().value(),
|
||||
Number::Integer(ref n) => n.to_f64().value(),
|
||||
&Number::Float(OrderedFloat(f)) => f,
|
||||
&Number::Rational(ref r) => r.to_f64().value(),
|
||||
Number::Rational(ref r) => r.to_f64().value(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,7 +420,7 @@ fn classify_float(f: f64) -> Result<f64, EvalError> {
|
||||
}
|
||||
}
|
||||
FpCategory::Nan => Err(EvalError::Undefined),
|
||||
_ => Ok(f)
|
||||
_ => Ok(f),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,39 +528,51 @@ 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.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::Integer(ref n2)) => n1.get_num().num_eq(&**n2),
|
||||
(Number::Integer(ref n1), &Number::Fixnum(n2)) => n1.num_eq(&n2.get_num()),
|
||||
(&Number::Fixnum(n1), Number::Rational(ref n2)) => {
|
||||
Integer::from(n1.get_num()).num_eq(&**n2)
|
||||
}
|
||||
(Number::Rational(ref n1), &Number::Fixnum(n2)) => {
|
||||
n1.num_eq(&Integer::from(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().value()).eq(n2),
|
||||
(&Number::Float(n1), &Number::Integer(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())),
|
||||
(&Number::Integer(ref n1), &Number::Rational(ref n2)) => {
|
||||
(Number::Integer(ref n1), Number::Integer(ref n2)) => n1.eq(n2),
|
||||
(Number::Integer(ref n1), Number::Float(n2)) => {
|
||||
OrderedFloat(n1.to_f64().value()).eq(n2)
|
||||
}
|
||||
(&Number::Float(n1), Number::Integer(ref n2)) => {
|
||||
n1.eq(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(Number::Integer(ref n1), Number::Rational(ref n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
&Rational::from(&**n1) == &**n2
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
&**n1 == &**n2
|
||||
n1.num_eq(&**n2)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(ref n1), &Number::Integer(ref n2)) => {
|
||||
(Number::Rational(ref n1), Number::Integer(ref n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
&**n1 == &Rational::from(&**n2)
|
||||
n1 == &Rational::from(&**n2)
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
&**n1 == &**n2
|
||||
n1.num_eq(&**n2)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(ref n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(&n2),
|
||||
(&Number::Float(n1), &Number::Rational(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())),
|
||||
(Number::Rational(ref n1), &Number::Float(n2)) => {
|
||||
OrderedFloat(n1.to_f64().value()).eq(&n2)
|
||||
}
|
||||
(&Number::Float(n1), Number::Rational(ref n2)) => {
|
||||
n1.eq(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2),
|
||||
(&Number::Rational(ref r1), &Number::Rational(ref r2)) => r1.eq(&r2),
|
||||
(Number::Rational(ref r1), Number::Rational(ref r2)) => r1.eq(r2),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -594,8 +592,8 @@ impl PartialOrd<usize> for Number {
|
||||
(n as usize).partial_cmp(rhs)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => (&**n).partial_cmp(rhs),
|
||||
Number::Rational(r) => (&**r).partial_cmp(rhs),
|
||||
Number::Integer(n) => Some((n).num_cmp(rhs)),
|
||||
Number::Rational(r) => Some((r).num_cmp(&Integer::from(*rhs))),
|
||||
Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)),
|
||||
}
|
||||
}
|
||||
@@ -614,8 +612,8 @@ impl PartialEq<usize> for Number {
|
||||
(n as usize).eq(rhs)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => (&**n).eq(rhs),
|
||||
Number::Rational(r) => (&**r).eq(rhs),
|
||||
Number::Integer(n) => (n).num_eq(rhs),
|
||||
Number::Rational(r) => (r).num_eq(&Integer::from(*rhs)),
|
||||
Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)),
|
||||
}
|
||||
}
|
||||
@@ -631,17 +629,19 @@ impl Ord for Number {
|
||||
fn cmp(&self, rhs: &Number) -> Ordering {
|
||||
match (self, rhs) {
|
||||
(&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::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()))
|
||||
(**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(n1), &Number::Integer(n2)) => (*n1).cmp(&*n2),
|
||||
(&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(n2),
|
||||
(&Number::Float(n1), &Number::Integer(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())),
|
||||
(&Number::Float(n1), Number::Integer(ref n2)) => {
|
||||
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Integer(n1), &Number::Rational(n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
@@ -649,7 +649,7 @@ impl Ord for Number {
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(n1), &Number::Integer(n2)) => {
|
||||
@@ -659,11 +659,15 @@ impl Ord for Number {
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(&n2),
|
||||
(&Number::Float(n1), &Number::Rational(n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())),
|
||||
(&Number::Rational(n1), &Number::Float(n2)) => {
|
||||
OrderedFloat(n1.to_f64().value()).cmp(&n2)
|
||||
}
|
||||
(&Number::Float(n1), &Number::Rational(n2)) => {
|
||||
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Float(f1), &Number::Float(f2)) => f1.cmp(&f2),
|
||||
(&Number::Rational(r1), &Number::Rational(r2)) => (*r1).cmp(&*r2),
|
||||
}
|
||||
@@ -704,16 +708,16 @@ impl TryFrom<HeapCellValue> for Number {
|
||||
|
||||
// Computes n ^ power. Ignores the sign of power.
|
||||
pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer {
|
||||
let mut power = Integer::from(power.abs());
|
||||
let mut power = power.abs();
|
||||
|
||||
if power == 0 {
|
||||
return Integer::from(1);
|
||||
if power.is_zero() {
|
||||
return Integer::ONE;
|
||||
}
|
||||
|
||||
let mut oddand = Integer::from(1);
|
||||
let mut oddand = Integer::ONE;
|
||||
|
||||
while power > 1 {
|
||||
if power.is_odd() {
|
||||
while power.num_gt(&1) {
|
||||
if power.bit(0) {
|
||||
oddand *= &n;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
use crate::parser::ast::MAX_ARITY;
|
||||
use crate::raw_block::*;
|
||||
use crate::rcu::{Rcu, RcuRef};
|
||||
use crate::types::*;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::Weak;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use modular_bitfield::prelude::*;
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Atom {
|
||||
@@ -33,65 +38,43 @@ impl<'a> From<&'a Atom> for Atom {
|
||||
impl From<bool> for Atom {
|
||||
#[inline]
|
||||
fn from(value: bool) -> Self {
|
||||
if value { atom!("true") } else { atom!("false") }
|
||||
if value {
|
||||
atom!("true")
|
||||
} else {
|
||||
atom!("false")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl indexmap::Equivalent<Atom> for str {
|
||||
fn equivalent(&self, key: &Atom) -> bool {
|
||||
&*key.as_str() == self
|
||||
}
|
||||
}
|
||||
|
||||
const ATOM_TABLE_INIT_SIZE: usize = 1 << 16;
|
||||
const ATOM_TABLE_ALIGN: usize = 8;
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
static ATOM_TABLE_BUF_BASE: std::cell::RefCell<*const u8> = std::cell::RefCell::new(ptr::null_mut());
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
static ATOM_TABLE_BUF_BASE: std::sync::atomic::AtomicPtr<u8> =
|
||||
std::sync::atomic::AtomicPtr::new(ptr::null_mut());
|
||||
|
||||
fn set_atom_tbl_buf_base(old_ptr: *const u8, new_ptr: *const u8) -> Result<(), *const u8> {
|
||||
#[cfg(test)]
|
||||
#[inline(always)]
|
||||
fn global_atom_table() -> &'static RwLock<Weak<AtomTable>> {
|
||||
#[cfg(feature = "rust_beta_channel")]
|
||||
{
|
||||
ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| {
|
||||
let mut borrow = atom_table_buf_base.borrow_mut();
|
||||
if *borrow != old_ptr {
|
||||
Err(*borrow)
|
||||
} else {
|
||||
*borrow = new_ptr;
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
};
|
||||
#[cfg(not(test))]
|
||||
// const Weak::new will be stabilized in 1.73 which is currently in beta,
|
||||
// till then we need a OnceLock for initialization
|
||||
static GLOBAL_ATOM_TABLE: RwLock<Weak<AtomTable>> = RwLock::const_new(Weak::new());
|
||||
&GLOBAL_ATOM_TABLE
|
||||
}
|
||||
#[cfg(not(feature = "rust_beta_channel"))]
|
||||
{
|
||||
ATOM_TABLE_BUF_BASE
|
||||
.compare_exchange(
|
||||
old_ptr.cast_mut(),
|
||||
new_ptr.cast_mut(),
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
)
|
||||
.map_err(|ptr| ptr.cast_const())
|
||||
}?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn get_atom_tbl_buf_base() -> *const u8 {
|
||||
#[cfg(test)]
|
||||
{
|
||||
ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow())
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
ATOM_TABLE_BUF_BASE.load(std::sync::atomic::Ordering::Relaxed)
|
||||
use std::sync::OnceLock;
|
||||
static GLOBAL_ATOM_TABLE: OnceLock<RwLock<Weak<AtomTable>>> = OnceLock::new();
|
||||
GLOBAL_ATOM_TABLE.get_or_init(|| RwLock::new(Weak::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Overwriting atom table base pointer")]
|
||||
fn atomtable_is_not_concurrency_safe() {
|
||||
let _table_a = AtomTable::new();
|
||||
let _table_b = AtomTable::new();
|
||||
#[inline(always)]
|
||||
fn arc_atom_table() -> Option<Arc<AtomTable>> {
|
||||
global_atom_table().read().unwrap().upgrade()
|
||||
}
|
||||
|
||||
impl RawBlockTraits for AtomTable {
|
||||
@@ -109,9 +92,11 @@ impl RawBlockTraits for AtomTable {
|
||||
#[bitfield]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct AtomHeader {
|
||||
#[allow(unused)] m: bool,
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
len: B50,
|
||||
#[allow(unused)] padding: B13,
|
||||
#[allow(unused)]
|
||||
padding: B13,
|
||||
}
|
||||
|
||||
impl AtomHeader {
|
||||
@@ -120,13 +105,6 @@ impl AtomHeader {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -142,29 +120,76 @@ macro_rules! is_char {
|
||||
};
|
||||
}
|
||||
|
||||
impl Atom {
|
||||
#[inline]
|
||||
pub fn buf(self) -> *const u8 {
|
||||
let ptr = self.as_ptr();
|
||||
pub enum AtomString<'a> {
|
||||
Static(&'a str),
|
||||
Dynamic(AtomTableRef<str>),
|
||||
}
|
||||
|
||||
if ptr.is_null() {
|
||||
return ptr::null();
|
||||
impl AtomString<'_> {
|
||||
pub fn map<F>(self, f: F) -> Self
|
||||
where
|
||||
for<'a> F: FnOnce(&'a str) -> &'a str,
|
||||
{
|
||||
match self {
|
||||
Self::Static(reference) => Self::Static(f(reference)),
|
||||
Self::Dynamic(guard) => Self::Dynamic(AtomTableRef::map(guard, f)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(ptr as usize + mem::size_of::<AtomHeader>()) as *const u8
|
||||
impl std::fmt::Debug for AtomString<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(self.deref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AtomString<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.deref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for AtomString<'_> {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Static(reference) => reference,
|
||||
Self::Dynamic(guard) => guard.deref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
impl rustyline::completion::Candidate for AtomString<'_> {
|
||||
fn display(&self) -> &str {
|
||||
self.deref()
|
||||
}
|
||||
|
||||
fn replacement(&self) -> &str {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Atom {
|
||||
#[inline(always)]
|
||||
pub fn is_static(self) -> bool {
|
||||
(self.index as usize) < STRINGS.len() << 3
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(self) -> *const u8 {
|
||||
pub fn as_ptr(self) -> Option<AtomTableRef<u8>> {
|
||||
if self.is_static() {
|
||||
ptr::null()
|
||||
None
|
||||
} else {
|
||||
(get_atom_tbl_buf_base() as usize + (self.index as usize) - (STRINGS.len() << 3)) as *const u8
|
||||
let atom_table =
|
||||
arc_atom_table().expect("We should only have an Atom while there is an AtomTable");
|
||||
unsafe {
|
||||
AtomTableRef::try_map(atom_table.buf(), |buf| {
|
||||
(buf as *const u8)
|
||||
.add((self.index as usize) - (STRINGS.len() << 3))
|
||||
.as_ref()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,13 +203,19 @@ impl Atom {
|
||||
if self.is_static() {
|
||||
STRINGS[(self.index >> 3) as usize].len()
|
||||
} else {
|
||||
unsafe { ptr::read(self.as_ptr() as *const AtomHeader).len() as _ }
|
||||
let ptr = self.as_ptr().unwrap();
|
||||
let ptr = ptr.deref() as *const u8 as *const AtomHeader;
|
||||
unsafe { ptr::read(ptr) }.len() as _
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn flat_index(self) -> u64 {
|
||||
(self.index >> 3) as u64
|
||||
self.index >> 3
|
||||
}
|
||||
|
||||
pub fn as_char(self) -> Option<char> {
|
||||
@@ -194,48 +225,49 @@ impl Atom {
|
||||
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) as usize];
|
||||
}
|
||||
|
||||
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))
|
||||
if c2.is_none() {
|
||||
c1
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn defrock_brackets(&self, atom_tbl: &mut AtomTable) -> Self {
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> AtomString<'static> {
|
||||
if self.is_static() {
|
||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||
} else if let Some(ptr) = self.as_ptr() {
|
||||
AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| {
|
||||
let header =
|
||||
// Miri seems to hit this line a lot
|
||||
unsafe { ptr::read::<AtomHeader>(ptr as *const u8 as *const AtomHeader) };
|
||||
let len = header.len() as usize;
|
||||
let buf = unsafe { (ptr as *const u8).add(mem::size_of::<AtomHeader>()) };
|
||||
|
||||
unsafe { str::from_utf8_unchecked(slice::from_raw_parts(buf, len)) }
|
||||
}))
|
||||
} else {
|
||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn defrock_brackets(&self, atom_tbl: &AtomTable) -> Self {
|
||||
let s = self.as_str();
|
||||
|
||||
let s = if s.starts_with('(') && s.ends_with(')') {
|
||||
let sub_str = if s.starts_with('(') && s.ends_with(')') {
|
||||
&s['('.len_utf8()..s.len() - ')'.len_utf8()]
|
||||
} else {
|
||||
return *self;
|
||||
};
|
||||
|
||||
atom_tbl.build_with(s)
|
||||
AtomTable::build_with(atom_tbl, sub_str)
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
let str_ptr = ptr.add(mem::size_of::<AtomHeader>());
|
||||
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr, string.len());
|
||||
}
|
||||
|
||||
impl PartialOrd for Atom {
|
||||
@@ -248,116 +280,156 @@ impl PartialOrd for Atom {
|
||||
impl Ord for Atom {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Atom) -> Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
self.as_str().cmp(&*other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AtomTable {
|
||||
pub struct InnerAtomTable {
|
||||
block: RawBlock<AtomTable>,
|
||||
pub table: IndexSet<Atom>,
|
||||
pub table: Rcu<IndexSet<Atom>>,
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn atom_table_base_pointer_mismatch(expected: *const u8, got: *const u8) -> ! {
|
||||
assert_eq!(expected, got, "Overwriting atom table base pointer, expected old value to be {expected:p}, but found {got:p}");
|
||||
unreachable!("This should only be called in a case of a mismatch as such the assert_eq should have failed!")
|
||||
#[derive(Debug)]
|
||||
pub struct AtomTable {
|
||||
inner: Rcu<InnerAtomTable>,
|
||||
// this lock is taking during resizing
|
||||
update: Mutex<()>,
|
||||
}
|
||||
|
||||
impl Drop for AtomTable {
|
||||
fn drop(&mut self) {
|
||||
if let Err(got) = set_atom_tbl_buf_base(self.block.base, ptr::null()) {
|
||||
atom_table_base_pointer_mismatch(self.block.base, got);
|
||||
}
|
||||
self.block.deallocate();
|
||||
pub type AtomTableRef<M> = RcuRef<InnerAtomTable, M>;
|
||||
|
||||
impl InnerAtomTable {
|
||||
#[inline(always)]
|
||||
fn lookup_str(self: &InnerAtomTable, string: &str) -> Option<Atom> {
|
||||
STATIC_ATOMS_MAP
|
||||
.get(string)
|
||||
.cloned()
|
||||
.or_else(|| self.table.active_epoch().get(string).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomTable {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
let mut block = RawBlock::new();
|
||||
|
||||
if let Err(got) = set_atom_tbl_buf_base(ptr::null(), block.base) {
|
||||
block.deallocate();
|
||||
atom_table_base_pointer_mismatch(ptr::null(), got);
|
||||
}
|
||||
|
||||
Self {
|
||||
block,
|
||||
table: IndexSet::new(),
|
||||
pub fn new() -> Arc<Self> {
|
||||
let upgraded = global_atom_table().read().unwrap().upgrade();
|
||||
// don't inline upgraded, otherwise temporary will be dropped too late in case of None
|
||||
if let Some(atom_table) = upgraded {
|
||||
atom_table
|
||||
} else {
|
||||
let mut guard = global_atom_table().write().unwrap();
|
||||
// try to upgrade again in case we lost the race on the write lock
|
||||
if let Some(atom_table) = guard.upgrade() {
|
||||
atom_table
|
||||
} else {
|
||||
let atom_table = Arc::new(Self {
|
||||
inner: Rcu::new(InnerAtomTable {
|
||||
block: RawBlock::new(),
|
||||
table: Rcu::new(IndexSet::new()),
|
||||
}),
|
||||
update: Mutex::new(()),
|
||||
});
|
||||
*guard = Arc::downgrade(&atom_table);
|
||||
atom_table
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn buf(&self) -> *const u8 {
|
||||
self.block.base as *const u8
|
||||
pub fn buf(&self) -> AtomTableRef<u8> {
|
||||
AtomTableRef::<InnerAtomTable>::map(self.inner.active_epoch(), |inner| {
|
||||
unsafe { inner.block.base.as_ref() }.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn top(&self) -> *const u8 {
|
||||
self.block.top
|
||||
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
|
||||
self.inner.active_epoch().table.active_epoch()
|
||||
}
|
||||
|
||||
#[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(atom_table: &AtomTable, string: &str) -> Atom {
|
||||
loop {
|
||||
let mut block_epoch = atom_table.inner.active_epoch();
|
||||
let mut table_epoch = block_epoch.table.active_epoch();
|
||||
|
||||
pub fn build_with(&mut self, string: &str) -> Atom {
|
||||
if let Some(atom) = self.lookup_str(string) {
|
||||
return atom;
|
||||
}
|
||||
if let Some(atom) = block_epoch.lookup_str(string) {
|
||||
return atom;
|
||||
}
|
||||
|
||||
// take a lock to prevent concurrent updates
|
||||
let update_guard = atom_table.update.lock().unwrap();
|
||||
|
||||
let is_same_allocation =
|
||||
RcuRef::same_epoch(&block_epoch, &atom_table.inner.active_epoch());
|
||||
let is_same_atom_list =
|
||||
RcuRef::same_epoch(&table_epoch, &block_epoch.table.active_epoch());
|
||||
|
||||
if !(is_same_allocation && is_same_atom_list) {
|
||||
// some other thread raced us between our lookup and
|
||||
// us aquring the update lock,
|
||||
// try again
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
unsafe {
|
||||
let len_ptr = loop {
|
||||
let ptr = block_epoch.block.alloc(size);
|
||||
|
||||
if ptr.is_null() {
|
||||
let old_base = self.block.base;
|
||||
self.block.grow();
|
||||
if let Err(got) = set_atom_tbl_buf_base(old_base, self.block.base) {
|
||||
atom_table_base_pointer_mismatch(old_base, got);
|
||||
}
|
||||
// garbage collection would go here
|
||||
let new_block = block_epoch.block.grow_new().unwrap();
|
||||
let new_table = Rcu::new(table_epoch.clone());
|
||||
let new_alloc = InnerAtomTable {
|
||||
block: new_block,
|
||||
table: new_table,
|
||||
};
|
||||
atom_table.inner.replace(new_alloc);
|
||||
block_epoch = atom_table.inner.active_epoch();
|
||||
table_epoch = block_epoch.table.active_epoch();
|
||||
} else {
|
||||
break;
|
||||
break ptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ptr
|
||||
};
|
||||
let ptr_base = block_epoch.block.base as usize;
|
||||
|
||||
let ptr_base = self.block.base as usize;
|
||||
write_to_ptr(string, len_ptr);
|
||||
|
||||
write_to_ptr(string, len_ptr);
|
||||
let atom = Atom {
|
||||
index: ((STRINGS.len() << 3) + len_ptr as usize - ptr_base) as u64,
|
||||
};
|
||||
|
||||
let atom = Atom {
|
||||
index: ((STRINGS.len() << 3) + len_ptr as usize - ptr_base) as u64,
|
||||
};
|
||||
let mut table = table_epoch.clone();
|
||||
table.insert(atom);
|
||||
block_epoch.table.replace(table);
|
||||
|
||||
self.table.insert(atom);
|
||||
// expicit drop to ensure we don't accidentally drop it early
|
||||
drop(update_guard);
|
||||
|
||||
atom
|
||||
return atom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for AtomTable {}
|
||||
unsafe impl Sync for AtomTable {}
|
||||
|
||||
#[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,
|
||||
#[allow(unused)]
|
||||
f: bool,
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
#[allow(unused)]
|
||||
tag: B6,
|
||||
}
|
||||
|
||||
impl AtomCell {
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
fn main() -> std::process::ExitCode {
|
||||
use std::sync::atomic::Ordering;
|
||||
use scryer_prolog::atom_table::Atom;
|
||||
use scryer_prolog::*;
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
ctrlc::set_handler(move || {
|
||||
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
|
||||
}).unwrap();
|
||||
scryer_prolog::machine::INTERRUPT.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut wam = machine::Machine::new();
|
||||
wam.run_top_level()
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
runtime.block_on(async move {
|
||||
let mut wam = machine::Machine::new(Default::default());
|
||||
wam.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 0))
|
||||
})
|
||||
}
|
||||
|
||||
431
src/codegen.rs
431
src/codegen.rs
@@ -1,15 +1,16 @@
|
||||
use crate::atom_table::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::temp_v;
|
||||
use crate::allocator::*;
|
||||
use crate::arithmetic::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::debray_allocator::*;
|
||||
use crate::forms::*;
|
||||
use crate::indexing::*;
|
||||
use crate::instructions::*;
|
||||
use crate::iterators::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::targets::*;
|
||||
use crate::temp_v;
|
||||
use crate::types::*;
|
||||
use crate::variable_records::*;
|
||||
|
||||
use crate::instr;
|
||||
use crate::machine::disjuncts::*;
|
||||
@@ -48,15 +49,21 @@ impl BranchCodeStack {
|
||||
}
|
||||
|
||||
fn code<'a>(&'a mut self, default_code: &'a mut CodeDeque) -> &'a mut CodeDeque {
|
||||
self.stack.last_mut()
|
||||
self.stack
|
||||
.last_mut()
|
||||
.and_then(|stack| stack.last_mut())
|
||||
.unwrap_or(default_code)
|
||||
}
|
||||
|
||||
fn push_missing_vars(&mut self, depth: usize, marker: &mut DebrayAllocator) -> SubsumedBranchHits {
|
||||
fn push_missing_vars(
|
||||
&mut self,
|
||||
depth: usize,
|
||||
marker: &mut DebrayAllocator,
|
||||
) -> SubsumedBranchHits {
|
||||
let mut subsumed_hits = SubsumedBranchHits::with_hasher(FxBuildHasher::default());
|
||||
let mut propagated_var_nums = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
|
||||
for idx in (self.stack.len() - depth .. self.stack.len()).rev() {
|
||||
for idx in (self.stack.len() - depth..self.stack.len()).rev() {
|
||||
let branch = &mut marker.branch_stack[idx];
|
||||
let branch_hits = &branch.hits;
|
||||
|
||||
@@ -80,9 +87,17 @@ impl BranchCodeStack {
|
||||
}
|
||||
}
|
||||
|
||||
if idx > self.stack.len() - depth {
|
||||
propagated_var_nums.insert(var_num);
|
||||
}
|
||||
|
||||
subsumed_hits.insert(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
for var_num in propagated_var_nums.drain(..) {
|
||||
marker.branch_stack[idx - 1].add_branch_occurrence(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
subsumed_hits
|
||||
@@ -91,14 +106,14 @@ impl BranchCodeStack {
|
||||
fn push_jump_instrs(&mut self, depth: usize) {
|
||||
// add 2 in each arm length to compensate for each jump
|
||||
// instruction and each branch instruction not yet added.
|
||||
let mut jump_span: usize = self.stack[self.stack.len() - depth ..]
|
||||
let mut jump_span: usize = self.stack[self.stack.len() - depth..]
|
||||
.iter()
|
||||
.map(|branch| branch.iter().map(|code| code.len() + 2).sum::<usize>())
|
||||
.sum();
|
||||
|
||||
jump_span -= depth;
|
||||
|
||||
for idx in self.stack.len() - depth .. self.stack.len() {
|
||||
for idx in self.stack.len() - depth..self.stack.len() {
|
||||
let inner_len = self.stack[idx].len();
|
||||
|
||||
for (inner_idx, code) in self.stack[idx].iter_mut().enumerate() {
|
||||
@@ -106,7 +121,7 @@ impl BranchCodeStack {
|
||||
jump_span -= code.len() + 1;
|
||||
} else {
|
||||
jump_span -= code.len() + 1;
|
||||
code.push_back(instr!("jmp_by_call", jump_span as usize));
|
||||
code.push_back(instr!("jmp_by_call", jump_span));
|
||||
|
||||
jump_span -= 1;
|
||||
}
|
||||
@@ -117,9 +132,11 @@ impl BranchCodeStack {
|
||||
fn pop_branch(&mut self, depth: usize, settings: CodeGenSettings) -> CodeDeque {
|
||||
let mut combined_code = CodeDeque::new();
|
||||
|
||||
for mut branch_arm in self.stack.drain(self.stack.len() - depth ..).rev() {
|
||||
for mut branch_arm in self.stack.drain(self.stack.len() - depth..).rev() {
|
||||
let num_branch_arms = branch_arm.len();
|
||||
branch_arm.last_mut().map(|code| code.extend(combined_code.drain(..)));
|
||||
if let Some(code) = branch_arm.last_mut() {
|
||||
code.extend(combined_code.drain(..))
|
||||
}
|
||||
|
||||
for (idx, code) in branch_arm.into_iter().enumerate() {
|
||||
combined_code.push_back(if idx == 0 {
|
||||
@@ -255,7 +272,7 @@ impl CodeGenSettings {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CodeGenerator<'a> {
|
||||
pub(crate) atom_tbl: &'a mut AtomTable,
|
||||
pub(crate) atom_tbl: &'a AtomTable,
|
||||
marker: DebrayAllocator,
|
||||
settings: CodeGenSettings,
|
||||
pub(crate) skeleton: PredicateSkeleton,
|
||||
@@ -269,14 +286,7 @@ impl DebrayAllocator {
|
||||
vr: &Cell<VarReg>,
|
||||
code: &mut CodeDeque,
|
||||
) -> RegType {
|
||||
self.mark_var::<QueryInstruction>(
|
||||
var_num,
|
||||
Level::Shallow,
|
||||
vr,
|
||||
term_loc,
|
||||
code,
|
||||
);
|
||||
|
||||
self.mark_var::<QueryInstruction>(var_num, Level::Shallow, vr, term_loc, code);
|
||||
vr.get().norm()
|
||||
}
|
||||
|
||||
@@ -295,7 +305,14 @@ impl DebrayAllocator {
|
||||
self.mark_var_in_non_callable(var_num, term_loc, vr, code);
|
||||
temp_v!(arg)
|
||||
} else {
|
||||
self.increment_running_count(var_num);
|
||||
if let VarAlloc::Perm(_, PermVarAllocation::Pending) =
|
||||
&self.var_data.records[var_num].allocation
|
||||
{
|
||||
self.mark_var_in_non_callable(var_num, term_loc, vr, code);
|
||||
} else {
|
||||
self.increment_running_count(var_num);
|
||||
}
|
||||
|
||||
RegType::Perm(p)
|
||||
}
|
||||
}
|
||||
@@ -308,8 +325,8 @@ impl DebrayAllocator {
|
||||
// decrement the arity of the PutStructure instruction by 1.
|
||||
fn trim_structure_by_last_arg(instr: &mut Instruction, last_arg: &Term) {
|
||||
match instr {
|
||||
Instruction::PutStructure(_, ref mut arity, _) |
|
||||
Instruction::GetStructure(.., ref mut arity, _) => {
|
||||
Instruction::PutStructure(_, ref mut arity, _)
|
||||
| Instruction::GetStructure(.., ref mut arity, _) => {
|
||||
if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg {
|
||||
// it is acceptable if arity == 0 is the result of
|
||||
// this decrement. call/N will have to read the index
|
||||
@@ -352,16 +369,16 @@ impl<'a, 'b> AddToFreeList<'a, QueryInstruction> for CodeGenerator<'b> {
|
||||
|
||||
fn structure_cell(term: &Term) -> Option<&Cell<RegType>> {
|
||||
match term {
|
||||
&Term::Cons(ref cell, ..) |
|
||||
&Term::Clause(ref cell, ..) |
|
||||
Term::PartialString(ref cell, ..) |
|
||||
Term::CompleteString(ref cell, ..) => Some(cell),
|
||||
&Term::Cons(ref cell, ..)
|
||||
| &Term::Clause(ref cell, ..)
|
||||
| Term::PartialString(ref cell, ..)
|
||||
| Term::CompleteString(ref cell, ..) => Some(cell),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> CodeGenerator<'b> {
|
||||
pub(crate) fn new(atom_tbl: &'b mut AtomTable, settings: CodeGenSettings) -> Self {
|
||||
pub(crate) fn new(atom_tbl: &'b AtomTable, settings: CodeGenSettings) -> Self {
|
||||
CodeGenerator {
|
||||
atom_tbl,
|
||||
marker: DebrayAllocator::new(),
|
||||
@@ -375,7 +392,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
Target: crate::targets::CompilationTarget<'a>,
|
||||
{
|
||||
if let Some(ref mut instr) = target.back_mut() {
|
||||
if Target::is_void_instr(&*instr) {
|
||||
if Target::is_void_instr(instr) {
|
||||
Target::incr_void_instr(instr);
|
||||
return;
|
||||
}
|
||||
@@ -392,7 +409,8 @@ impl<'b> CodeGenerator<'b> {
|
||||
target: &mut CodeDeque,
|
||||
) {
|
||||
if self.marker.var_data.records[var_num].num_occurrences > 1 {
|
||||
self.marker.mark_var::<Target>(var_num, Level::Deep, cell, term_loc, target);
|
||||
self.marker
|
||||
.mark_var::<Target>(var_num, Level::Deep, cell, term_loc, target);
|
||||
} else {
|
||||
Self::add_or_increment_void_instr::<Target>(target);
|
||||
}
|
||||
@@ -408,31 +426,33 @@ impl<'b> CodeGenerator<'b> {
|
||||
&Term::AnonVar => {
|
||||
Self::add_or_increment_void_instr::<Target>(target);
|
||||
}
|
||||
&Term::Cons(ref cell, ..) |
|
||||
&Term::Clause(ref cell, ..) |
|
||||
Term::PartialString(ref cell, ..) |
|
||||
Term::CompleteString(ref cell, ..) => {
|
||||
self.marker.mark_non_var::<Target>(Level::Deep, term_loc, cell, target);
|
||||
&Term::Cons(ref cell, ..)
|
||||
| &Term::Clause(ref cell, ..)
|
||||
| Term::PartialString(ref cell, ..)
|
||||
| Term::CompleteString(ref cell, ..) => {
|
||||
self.marker
|
||||
.mark_non_var::<Target>(Level::Deep, term_loc, cell, target);
|
||||
target.push_back(Target::clause_arg_to_instr(cell.get()));
|
||||
}
|
||||
&Term::Literal(_, ref constant) => {
|
||||
target.push_back(Target::constant_subterm(constant.clone()));
|
||||
Term::Literal(_, ref constant) => {
|
||||
target.push_back(Target::constant_subterm(*constant));
|
||||
}
|
||||
&Term::Var(ref cell, ref var_ptr) => {
|
||||
self.deep_var_instr::<Target>(cell, var_ptr.to_var_num().unwrap(), term_loc, target);
|
||||
Term::Var(ref cell, ref var_ptr) => {
|
||||
self.deep_var_instr::<Target>(
|
||||
cell,
|
||||
var_ptr.to_var_num().unwrap(),
|
||||
term_loc,
|
||||
target,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn compile_target<'a, Target, Iter>(
|
||||
&mut self,
|
||||
iter: Iter,
|
||||
term_loc: GenContext,
|
||||
) -> CodeDeque
|
||||
fn compile_target<'a, Target, Iter>(&mut self, iter: Iter, term_loc: GenContext) -> CodeDeque
|
||||
where
|
||||
Target: crate::targets::CompilationTarget<'a>,
|
||||
Iter: Iterator<Item = TermRef<'a>>,
|
||||
CodeGenerator<'b>: AddToFreeList<'a, Target>
|
||||
CodeGenerator<'b>: AddToFreeList<'a, Target>,
|
||||
{
|
||||
let mut target = CodeDeque::new();
|
||||
|
||||
@@ -442,14 +462,19 @@ impl<'b> CodeGenerator<'b> {
|
||||
if let GenContext::Head = term_loc {
|
||||
self.marker.advance_arg();
|
||||
} else {
|
||||
self.marker.mark_anon_var::<Target>(lvl, term_loc, &mut target);
|
||||
self.marker
|
||||
.mark_anon_var::<Target>(lvl, term_loc, &mut target);
|
||||
}
|
||||
}
|
||||
TermRef::Clause(lvl, cell, name, terms) => {
|
||||
self.marker.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
target.push_back(Target::to_structure(lvl, name, terms.len(), cell.get()));
|
||||
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get());
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_term_to_free_list(
|
||||
self,
|
||||
cell.get(),
|
||||
);
|
||||
|
||||
if let Some(instr) = target.back_mut() {
|
||||
if let Some(term) = terms.last() {
|
||||
@@ -462,38 +487,52 @@ impl<'b> CodeGenerator<'b> {
|
||||
}
|
||||
|
||||
for subterm in terms {
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_subterm_to_free_list(self, subterm);
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_subterm_to_free_list(
|
||||
self, subterm,
|
||||
);
|
||||
}
|
||||
}
|
||||
TermRef::Cons(lvl, cell, head, tail) => {
|
||||
self.marker.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
target.push_back(Target::to_list(lvl, cell.get()));
|
||||
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_term_to_free_list(self, cell.get());
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_term_to_free_list(
|
||||
self,
|
||||
cell.get(),
|
||||
);
|
||||
|
||||
self.subterm_to_instr::<Target>(head, term_loc, &mut target);
|
||||
self.subterm_to_instr::<Target>(tail, term_loc, &mut target);
|
||||
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_subterm_to_free_list(self, head);
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_subterm_to_free_list(self, tail);
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_subterm_to_free_list(
|
||||
self, head,
|
||||
);
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_subterm_to_free_list(
|
||||
self, tail,
|
||||
);
|
||||
}
|
||||
TermRef::Literal(lvl @ Level::Shallow, cell, Literal::String(ref string)) => {
|
||||
self.marker.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
target.push_back(Target::to_pstr(lvl, *string, cell.get(), false));
|
||||
}
|
||||
TermRef::Literal(lvl @ Level::Shallow, cell, constant) => {
|
||||
self.marker.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
target.push_back(Target::to_constant(lvl, *constant, cell.get()));
|
||||
}
|
||||
TermRef::PartialString(lvl, cell, string, tail) => {
|
||||
self.marker.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
let atom = self.atom_tbl.build_with(&string);
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
let atom = AtomTable::build_with(self.atom_tbl, string);
|
||||
|
||||
target.push_back(Target::to_pstr(lvl, atom, cell.get(), true));
|
||||
self.subterm_to_instr::<Target>(tail, term_loc, &mut target);
|
||||
}
|
||||
TermRef::CompleteString(lvl, cell, atom) => {
|
||||
self.marker.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
target.push_back(Target::to_pstr(lvl, atom, cell.get(), false));
|
||||
}
|
||||
TermRef::Var(lvl @ Level::Shallow, cell, var) => {
|
||||
@@ -535,10 +574,10 @@ impl<'b> CodeGenerator<'b> {
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_inlined<'a>(
|
||||
fn compile_inlined(
|
||||
&mut self,
|
||||
ct: &InlinedClauseType,
|
||||
terms: &'a Vec<Term>,
|
||||
terms: &'_ [Term],
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
) -> Result<(), CompilationError> {
|
||||
@@ -562,13 +601,13 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
compare_number_instr!(cmp, at_1, at_2)
|
||||
}
|
||||
&InlinedClauseType::IsAtom(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Char(_)) |
|
||||
&Term::Literal(_, Literal::Atom(atom!("[]"))) |
|
||||
&Term::Literal(_, Literal::Atom(..)) => {
|
||||
InlinedClauseType::IsAtom(..) => match &terms[0] {
|
||||
Term::Literal(_, Literal::Char(_))
|
||||
| Term::Literal(_, Literal::Atom(atom!("[]")))
|
||||
| Term::Literal(_, Literal::Atom(..)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -585,21 +624,21 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsAtomic(..) => match &terms[0] {
|
||||
&Term::AnonVar |
|
||||
&Term::Clause(..) |
|
||||
&Term::Cons(..) |
|
||||
&Term::PartialString(..) |
|
||||
&Term::CompleteString(..) => {
|
||||
InlinedClauseType::IsAtomic(..) => match &terms[0] {
|
||||
Term::AnonVar
|
||||
| Term::Clause(..)
|
||||
| Term::Cons(..)
|
||||
| Term::PartialString(..)
|
||||
| Term::CompleteString(..) => {
|
||||
instr!("$fail")
|
||||
}
|
||||
&Term::Literal(_, Literal::String(_)) => {
|
||||
Term::Literal(_, Literal::String(_)) => {
|
||||
instr!("$fail")
|
||||
}
|
||||
&Term::Literal(..) => {
|
||||
Term::Literal(..) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -613,15 +652,15 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("atomic", r)
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsCompound(..) => match &terms[0] {
|
||||
&Term::Clause(..) |
|
||||
&Term::Cons(..) |
|
||||
&Term::PartialString(..) |
|
||||
&Term::CompleteString(..) |
|
||||
&Term::Literal(_, Literal::String(..)) => {
|
||||
InlinedClauseType::IsCompound(..) => match &terms[0] {
|
||||
Term::Clause(..)
|
||||
| Term::Cons(..)
|
||||
| Term::PartialString(..)
|
||||
| Term::CompleteString(..)
|
||||
| Term::Literal(_, Literal::String(..)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -638,24 +677,30 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsRational(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Rational(_)) => {
|
||||
InlinedClauseType::IsRational(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::Rational(_)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
let r = self.marker.mark_non_callable(name.to_var_num().unwrap(), 1, term_loc, vr, code);
|
||||
let r = self.marker.mark_non_callable(
|
||||
name.to_var_num().unwrap(),
|
||||
1,
|
||||
term_loc,
|
||||
vr,
|
||||
code,
|
||||
);
|
||||
instr!("rational", r)
|
||||
}
|
||||
_ => {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsFloat(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Float(_)) => {
|
||||
InlinedClauseType::IsFloat(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::Float(_)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -672,14 +717,14 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsNumber(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Float(_)) |
|
||||
&Term::Literal(_, Literal::Rational(_)) |
|
||||
&Term::Literal(_, Literal::Integer(_)) |
|
||||
&Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
InlinedClauseType::IsNumber(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::Float(_))
|
||||
| Term::Literal(_, Literal::Rational(_))
|
||||
| Term::Literal(_, Literal::Integer(_))
|
||||
| Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -696,11 +741,11 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsNonVar(..) => match &terms[0] {
|
||||
&Term::AnonVar => {
|
||||
InlinedClauseType::IsNonVar(..) => match terms[0] {
|
||||
Term::AnonVar => {
|
||||
instr!("$fail")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -717,12 +762,11 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$succeed")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsInteger(..) => match &terms[0] {
|
||||
&Term::Literal(_, Literal::Integer(_)) |
|
||||
&Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
InlinedClauseType::IsInteger(..) => match &terms[0] {
|
||||
Term::Literal(_, Literal::Integer(_)) | Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -739,18 +783,18 @@ impl<'b> CodeGenerator<'b> {
|
||||
instr!("$fail")
|
||||
}
|
||||
},
|
||||
&InlinedClauseType::IsVar(..) => match &terms[0] {
|
||||
&Term::Literal(..) |
|
||||
&Term::Clause(..) |
|
||||
&Term::Cons(..) |
|
||||
&Term::PartialString(..) |
|
||||
&Term::CompleteString(..) => {
|
||||
InlinedClauseType::IsVar(..) => match terms[0] {
|
||||
Term::Literal(..)
|
||||
| Term::Clause(..)
|
||||
| Term::Cons(..)
|
||||
| Term::PartialString(..)
|
||||
| Term::CompleteString(..) => {
|
||||
instr!("$fail")
|
||||
}
|
||||
&Term::AnonVar => {
|
||||
Term::AnonVar => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
self.marker.reset_arg(1);
|
||||
|
||||
let r = self.marker.mark_non_callable(
|
||||
@@ -785,41 +829,74 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
fn compile_is_call(
|
||||
&mut self,
|
||||
terms: &Vec<Term>,
|
||||
terms: &[Term],
|
||||
code: &mut CodeDeque,
|
||||
term_loc: GenContext,
|
||||
call_policy: CallPolicy,
|
||||
) -> Result<(), CompilationError> {
|
||||
macro_rules! compile_expr {
|
||||
($self:expr, $terms:expr, $term_loc:expr, $code:expr) => ({
|
||||
($self:expr, $terms:expr, $term_loc:expr, $code:expr) => {{
|
||||
let (acode, at) = $self.compile_arith_expr($terms, 1, $term_loc, 2)?;
|
||||
$code.extend(acode.into_iter());
|
||||
at
|
||||
});
|
||||
}};
|
||||
}
|
||||
|
||||
self.marker.reset_arg(2);
|
||||
|
||||
let at = match &terms[0] {
|
||||
&Term::Var(ref vr, ref name) => {
|
||||
let at = match terms[0] {
|
||||
Term::Var(ref vr, ref name) => {
|
||||
let var_num = name.to_var_num().unwrap();
|
||||
|
||||
self.marker.mark_var::<QueryInstruction>(
|
||||
var_num,
|
||||
Level::Shallow,
|
||||
vr,
|
||||
term_loc,
|
||||
code,
|
||||
);
|
||||
if self.marker.var_data.records[var_num].num_occurrences > 1 {
|
||||
self.marker.mark_var::<QueryInstruction>(
|
||||
var_num,
|
||||
Level::Shallow,
|
||||
vr,
|
||||
term_loc,
|
||||
code,
|
||||
);
|
||||
|
||||
self.marker.mark_safe_var_unconditionally(var_num);
|
||||
self.marker.mark_safe_var_unconditionally(var_num);
|
||||
compile_expr!(self, &terms[1], term_loc, code)
|
||||
} else {
|
||||
self.marker
|
||||
.mark_anon_var::<QueryInstruction>(Level::Shallow, term_loc, code);
|
||||
|
||||
compile_expr!(self, &terms[1], term_loc, code)
|
||||
if let Term::Var(ref vr, ref var) = &terms[1] {
|
||||
let var_num = var.to_var_num().unwrap();
|
||||
|
||||
// if var is an anonymous variable, insert
|
||||
// is/2 call so that an instantiation error is
|
||||
// thrown when the predicate is run.
|
||||
if self.marker.var_data.records[var_num].num_occurrences > 1 {
|
||||
self.marker.mark_var::<QueryInstruction>(
|
||||
var_num,
|
||||
Level::Shallow,
|
||||
vr,
|
||||
term_loc,
|
||||
code,
|
||||
);
|
||||
|
||||
self.marker.mark_safe_var_unconditionally(var_num);
|
||||
|
||||
let at = ArithmeticTerm::Reg(vr.get().norm());
|
||||
self.add_call(code, instr!("$get_number", at), call_policy);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
compile_expr!(self, &terms[1], term_loc, code)
|
||||
}
|
||||
}
|
||||
&Term::Literal(_, c @ Literal::Integer(_) |
|
||||
c @ Literal::Float(_) |
|
||||
c @ Literal::Rational(_) |
|
||||
c @ Literal::Fixnum(_)) => {
|
||||
Term::Literal(
|
||||
_,
|
||||
c @ Literal::Integer(_)
|
||||
| c @ Literal::Float(_)
|
||||
| c @ Literal::Rational(_)
|
||||
| c @ Literal::Fixnum(_),
|
||||
) => {
|
||||
let v = HeapCellValue::from(c);
|
||||
code.push_back(instr!("put_constant", Level::Shallow, v, temp_v!(1)));
|
||||
|
||||
@@ -838,7 +915,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_seq<'a>(
|
||||
fn compile_seq(
|
||||
&mut self,
|
||||
clauses: &ChunkedTermVec,
|
||||
code: &mut CodeDeque,
|
||||
@@ -892,10 +969,15 @@ impl<'b> CodeGenerator<'b> {
|
||||
code.push_back(instr!("proceed"));
|
||||
}
|
||||
}
|
||||
&QueryTerm::LocalCut(var_num) => {
|
||||
&QueryTerm::LocalCut { var_num, cut_prev } => {
|
||||
let code = branch_code_stack.code(code);
|
||||
let r = self.marker.get_binding(var_num);
|
||||
code.push_back(instr!("cut", r));
|
||||
|
||||
code.push_back(if cut_prev {
|
||||
instr!("cut_prev", r)
|
||||
} else {
|
||||
instr!("cut", r)
|
||||
});
|
||||
|
||||
if self.marker.in_tail_position {
|
||||
if self.marker.var_data.allocates {
|
||||
@@ -912,15 +994,29 @@ impl<'b> CodeGenerator<'b> {
|
||||
ClauseType::BuiltIn(BuiltInClauseType::Is(..)),
|
||||
ref terms,
|
||||
call_policy,
|
||||
) => self.compile_is_call(terms, branch_code_stack.code(code), term_loc, call_policy)?,
|
||||
) => self.compile_is_call(
|
||||
terms,
|
||||
branch_code_stack.code(code),
|
||||
term_loc,
|
||||
call_policy,
|
||||
)?,
|
||||
&QueryTerm::Clause(_, ClauseType::Inlined(ref ct), ref terms, _) => {
|
||||
self.compile_inlined(ct, terms, term_loc, branch_code_stack.code(code))?
|
||||
self.compile_inlined(
|
||||
ct,
|
||||
terms,
|
||||
term_loc,
|
||||
branch_code_stack.code(code),
|
||||
)?
|
||||
}
|
||||
&QueryTerm::Fail => {
|
||||
branch_code_stack.code(code).push_back(instr!("$fail"));
|
||||
}
|
||||
term @ &QueryTerm::Clause(..) => {
|
||||
self.compile_query_line(term, term_loc, branch_code_stack.code(code));
|
||||
self.compile_query_line(
|
||||
term,
|
||||
term_loc,
|
||||
branch_code_stack.code(code),
|
||||
);
|
||||
|
||||
if self.marker.max_reg_allocated() > MAX_ARITY {
|
||||
return Err(CompilationError::ExceededMaxArity);
|
||||
@@ -948,7 +1044,8 @@ impl<'b> CodeGenerator<'b> {
|
||||
}
|
||||
ClauseItem::BranchEnd(depth) => {
|
||||
if !clause_iter.in_tail_position() {
|
||||
let subsumed_hits = branch_code_stack.push_missing_vars(depth, &mut self.marker);
|
||||
let subsumed_hits =
|
||||
branch_code_stack.push_missing_vars(depth, &mut self.marker);
|
||||
self.marker.pop_branch(depth, subsumed_hits);
|
||||
branch_code_stack.push_jump_instrs(depth);
|
||||
} else {
|
||||
@@ -974,14 +1071,21 @@ impl<'b> CodeGenerator<'b> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compile_rule(&mut self, rule: &Rule, var_data: VarData) -> Result<Code, CompilationError> {
|
||||
let Rule { head: (_, args), clauses } = rule;
|
||||
pub(crate) fn compile_rule(
|
||||
&mut self,
|
||||
rule: &Rule,
|
||||
var_data: VarData,
|
||||
) -> Result<Code, CompilationError> {
|
||||
let Rule {
|
||||
head: (_, args),
|
||||
clauses,
|
||||
} = rule;
|
||||
self.marker.var_data = var_data;
|
||||
let mut code = VecDeque::new();
|
||||
|
||||
self.marker.reset_at_head(args);
|
||||
|
||||
let iter = FactIterator::from_rule_head_clause(&args);
|
||||
let iter = FactIterator::from_rule_head_clause(args);
|
||||
let fact = self.compile_target::<FactInstruction, _>(iter, GenContext::Head);
|
||||
|
||||
if self.marker.max_reg_allocated() > MAX_ARITY {
|
||||
@@ -989,14 +1093,18 @@ impl<'b> CodeGenerator<'b> {
|
||||
}
|
||||
|
||||
self.marker.reset_free_list();
|
||||
code.extend(fact.into_iter());
|
||||
code.extend(fact);
|
||||
|
||||
self.compile_seq(clauses, &mut code)?;
|
||||
|
||||
Ok(Vec::from(code))
|
||||
}
|
||||
|
||||
pub(crate) fn compile_fact(&mut self, fact: &Fact, var_data: VarData) -> Result<Code, CompilationError> {
|
||||
pub(crate) fn compile_fact(
|
||||
&mut self,
|
||||
fact: &Fact,
|
||||
var_data: VarData,
|
||||
) -> Result<Code, CompilationError> {
|
||||
let mut code = Vec::new();
|
||||
self.marker.var_data = var_data;
|
||||
|
||||
@@ -1004,16 +1112,13 @@ impl<'b> CodeGenerator<'b> {
|
||||
self.marker.reset_at_head(args);
|
||||
|
||||
let iter = FactInstruction::iter(&fact.head);
|
||||
let compiled_fact = self.compile_target::<FactInstruction, _>(
|
||||
iter,
|
||||
GenContext::Head,
|
||||
);
|
||||
let compiled_fact = self.compile_target::<FactInstruction, _>(iter, GenContext::Head);
|
||||
|
||||
if self.marker.max_reg_allocated() > MAX_ARITY {
|
||||
return Err(CompilationError::ExceededMaxArity);
|
||||
}
|
||||
|
||||
code.extend(compiled_fact.into_iter());
|
||||
code.extend(compiled_fact);
|
||||
}
|
||||
|
||||
code.push(instr!("proceed"));
|
||||
@@ -1026,13 +1131,13 @@ impl<'b> CodeGenerator<'b> {
|
||||
let iter = QueryIterator::new(term);
|
||||
let query = self.compile_target::<QueryInstruction, _>(iter, term_loc);
|
||||
|
||||
code.extend(query.into_iter());
|
||||
code.extend(query);
|
||||
|
||||
match term {
|
||||
&QueryTerm::Clause(_, ref ct, _, call_policy) => {
|
||||
self.add_call(code, ct.to_instr(), call_policy);
|
||||
}
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1045,8 +1150,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
if let Some(args) = clause.args() {
|
||||
for (instantiated_arg_index, arg) in args.iter().enumerate() {
|
||||
match arg {
|
||||
Term::Var(..) | Term::AnonVar => {
|
||||
}
|
||||
Term::Var(..) | Term::AnonVar => {}
|
||||
_ => {
|
||||
if optimal_index != instantiated_arg_index {
|
||||
if left >= right {
|
||||
@@ -1071,7 +1175,11 @@ impl<'b> CodeGenerator<'b> {
|
||||
}
|
||||
|
||||
if left < right {
|
||||
subseqs.push(ClauseSpan { left, right, instantiated_arg_index: optimal_index });
|
||||
subseqs.push(ClauseSpan {
|
||||
left,
|
||||
right,
|
||||
instantiated_arg_index: optimal_index,
|
||||
});
|
||||
}
|
||||
|
||||
optimal_index = 0;
|
||||
@@ -1102,7 +1210,8 @@ impl<'b> CodeGenerator<'b> {
|
||||
optimal_index: usize,
|
||||
) -> Result<Code, CompilationError> {
|
||||
let mut code = VecDeque::new();
|
||||
let mut code_offsets = CodeOffsets::new(I::new(), optimal_index + 1);
|
||||
let mut code_offsets =
|
||||
CodeOffsets::new(I::new(), optimal_index + 1, self.settings.non_counted_bt);
|
||||
|
||||
let mut skip_stub_try_me_else = false;
|
||||
let clauses_len = clauses.len();
|
||||
@@ -1114,12 +1223,12 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
let clause_code = match clause {
|
||||
PredicateClause::Fact(fact, var_data) => {
|
||||
let var_data = std::mem::replace(var_data, VarData::default());
|
||||
self.compile_fact(&fact, var_data)?
|
||||
let var_data = std::mem::take(var_data);
|
||||
self.compile_fact(fact, var_data)?
|
||||
}
|
||||
PredicateClause::Rule(rule, var_data) => {
|
||||
let var_data = std::mem::replace(var_data, VarData::default());
|
||||
self.compile_rule(&rule, var_data)?
|
||||
let var_data = std::mem::take(var_data);
|
||||
self.compile_rule(rule, var_data)?
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1147,7 +1256,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
skip_stub_try_me_else = !self.settings.is_dynamic();
|
||||
}
|
||||
|
||||
let arg = clause.args().and_then(|args| args.iter().nth(optimal_index));
|
||||
let arg = clause.args().and_then(|args| args.get(optimal_index));
|
||||
|
||||
if let Some(arg) = arg {
|
||||
let index = code.len();
|
||||
@@ -1190,7 +1299,12 @@ impl<'b> CodeGenerator<'b> {
|
||||
let split_pred = Self::split_predicate(&clauses);
|
||||
let multi_seq = split_pred.len() > 1;
|
||||
|
||||
for ClauseSpan { left, right, instantiated_arg_index } in split_pred {
|
||||
for ClauseSpan {
|
||||
left,
|
||||
right,
|
||||
instantiated_arg_index,
|
||||
} in split_pred
|
||||
{
|
||||
let skel_lower_bound = self.skeleton.clauses.len();
|
||||
let code_segment = if self.settings.is_dynamic() {
|
||||
self.compile_pred_subseq::<DynamicCodeIndices>(
|
||||
@@ -1221,9 +1335,8 @@ impl<'b> CodeGenerator<'b> {
|
||||
if self.settings.is_extensible {
|
||||
let segment_is_indexed = code_segment[0].to_indexing_line().is_some();
|
||||
|
||||
for clause_index_info in self.skeleton.clauses
|
||||
.make_contiguous()[skel_lower_bound..]
|
||||
.iter_mut()
|
||||
for clause_index_info in
|
||||
self.skeleton.clauses.make_contiguous()[skel_lower_bound..].iter_mut()
|
||||
{
|
||||
clause_index_info.clause_start +=
|
||||
clause_start_offset + 2 * (segment_is_indexed as usize);
|
||||
|
||||
@@ -39,6 +39,19 @@ impl BranchOccurrences {
|
||||
subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
|
||||
debug_assert!(self.current_branch < self.num_branches);
|
||||
let num_branches = self.num_branches;
|
||||
|
||||
let entry = self
|
||||
.hits
|
||||
.entry(var_num)
|
||||
.or_insert_with(|| BitVec::repeat(false, num_branches));
|
||||
|
||||
entry.set(self.current_branch, true);
|
||||
self.subsumed_hits.insert(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -76,26 +89,23 @@ impl BranchStack {
|
||||
}
|
||||
}
|
||||
|
||||
fn safety_unneeded_in_branch(&self, safety: &VarSafetyStatus, branch: &BranchDesignator) -> bool {
|
||||
fn safety_unneeded_in_branch(
|
||||
&self,
|
||||
safety: &VarSafetyStatus,
|
||||
branch: &BranchDesignator,
|
||||
) -> bool {
|
||||
match safety {
|
||||
VarSafetyStatus::Needed => false,
|
||||
VarSafetyStatus::LocallyUnneeded(planter_branch) =>
|
||||
self.branch_subsumes(planter_branch, branch),
|
||||
VarSafetyStatus::LocallyUnneeded(planter_branch) => {
|
||||
self.branch_subsumes(planter_branch, branch)
|
||||
}
|
||||
VarSafetyStatus::GloballyUnneeded => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
|
||||
if let Some(occurrences) = self.last_mut() {
|
||||
debug_assert!(occurrences.current_branch < occurrences.num_branches);
|
||||
|
||||
let num_branches = occurrences.num_branches;
|
||||
|
||||
let entry = occurrences.hits.entry(var_num)
|
||||
.or_insert_with(|| BitVec::repeat(false, num_branches));
|
||||
|
||||
entry.set(occurrences.current_branch, true);
|
||||
occurrences.subsumed_hits.insert(var_num);
|
||||
occurrences.add_branch_occurrence(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,11 +115,15 @@ impl BranchStack {
|
||||
|
||||
pub(crate) fn current_branch_designator(&self) -> BranchDesignator {
|
||||
let branch_stack_num = self.len();
|
||||
let branch_num = self.last()
|
||||
let branch_num = self
|
||||
.last()
|
||||
.map(|occurrences| occurrences.current_branch)
|
||||
.unwrap_or(0);
|
||||
|
||||
BranchDesignator { branch_stack_num, branch_num }
|
||||
BranchDesignator {
|
||||
branch_stack_num,
|
||||
branch_num,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -121,7 +135,7 @@ impl BranchStack {
|
||||
#[inline]
|
||||
pub(crate) fn drain_branches(&mut self, depth: usize) -> std::vec::Drain<BranchOccurrences> {
|
||||
let start_idx = self.len() - depth;
|
||||
self.drain(start_idx ..)
|
||||
self.drain(start_idx..)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,20 +169,26 @@ impl DebrayAllocator {
|
||||
for var_num in subsumed_hits {
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, ref mut allocation) => {
|
||||
match allocation {
|
||||
PermVarAllocation::Done { shallow_safety, deep_safety, .. } => {
|
||||
if !self.branch_stack.safety_unneeded_in_branch(shallow_safety, &branch_designator) {
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.shallow_safety.insert(var_num);
|
||||
}
|
||||
|
||||
if !self.branch_stack.safety_unneeded_in_branch(deep_safety, &branch_designator) {
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.deep_safety.insert(var_num);
|
||||
}
|
||||
if let PermVarAllocation::Done {
|
||||
shallow_safety,
|
||||
deep_safety,
|
||||
..
|
||||
} = allocation
|
||||
{
|
||||
if !self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(shallow_safety, &branch_designator)
|
||||
{
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.shallow_safety.insert(var_num);
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
|
||||
if !self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(deep_safety, &branch_designator)
|
||||
{
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.deep_safety.insert(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,15 +202,15 @@ impl DebrayAllocator {
|
||||
pub(crate) fn pop_branch(&mut self, depth: usize, subsumed_hits: SubsumedBranchHits) {
|
||||
let removed_branches = self.branch_stack.drain_branches(depth);
|
||||
|
||||
let (deep_safety, shallow_safety) = removed_branches
|
||||
.into_iter()
|
||||
.fold((BitSet::default(), BitSet::default()),
|
||||
|(mut deep_safety, mut shallow_safety), branch_occurrences| {
|
||||
deep_safety.union_with(&branch_occurrences.deep_safety);
|
||||
shallow_safety.union_with(&branch_occurrences.shallow_safety);
|
||||
let (deep_safety, shallow_safety) = removed_branches.into_iter().fold(
|
||||
(BitSet::default(), BitSet::default()),
|
||||
|(mut deep_safety, mut shallow_safety), branch_occurrences| {
|
||||
deep_safety.union_with(&branch_occurrences.deep_safety);
|
||||
shallow_safety.union_with(&branch_occurrences.shallow_safety);
|
||||
|
||||
(deep_safety, shallow_safety)
|
||||
});
|
||||
(deep_safety, shallow_safety)
|
||||
},
|
||||
);
|
||||
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
@@ -201,7 +221,7 @@ impl DebrayAllocator {
|
||||
|
||||
(&latest_branch.deep_safety, &latest_branch.shallow_safety)
|
||||
}
|
||||
None => (&deep_safety, &shallow_safety)
|
||||
None => (&deep_safety, &shallow_safety),
|
||||
};
|
||||
|
||||
for var_num in subsumed_hits.iter().cloned() {
|
||||
@@ -221,10 +241,13 @@ impl DebrayAllocator {
|
||||
);
|
||||
|
||||
if running_count < num_occurrences {
|
||||
*allocation = PermVarAllocation::Done { shallow_safety, deep_safety };
|
||||
*allocation = PermVarAllocation::Done {
|
||||
shallow_safety,
|
||||
deep_safety,
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,9 +267,11 @@ impl DebrayAllocator {
|
||||
|
||||
fn occurs_shallowly_in_head(&self, var_num: usize, r: usize) -> bool {
|
||||
match &self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Temp { temp_var_data, term_loc: GenContext::Head, .. } => {
|
||||
temp_var_data.use_set.contains(&(GenContext::Head, r))
|
||||
}
|
||||
VarAlloc::Temp {
|
||||
temp_var_data,
|
||||
term_loc: GenContext::Head,
|
||||
..
|
||||
} => temp_var_data.use_set.contains(&(GenContext::Head, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -269,11 +294,9 @@ impl DebrayAllocator {
|
||||
let mut result = 0;
|
||||
|
||||
for reg in self.temp_lb.. {
|
||||
if !self.is_in_use(reg) {
|
||||
if !temp_var_data.no_use_set.contains(reg) {
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
if !self.is_in_use(reg) && !temp_var_data.no_use_set.contains(reg) {
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,13 +318,12 @@ impl DebrayAllocator {
|
||||
let mut result = 0;
|
||||
|
||||
for reg in self.temp_lb.. {
|
||||
if !self.is_in_use(reg) {
|
||||
if !temp_var_data.no_use_set.contains(reg) {
|
||||
if !temp_var_data.conflict_set.contains(reg) {
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !self.is_in_use(reg)
|
||||
&& !temp_var_data.no_use_set.contains(reg)
|
||||
&& !temp_var_data.conflict_set.contains(reg)
|
||||
{
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,13 +345,14 @@ impl DebrayAllocator {
|
||||
// consider its use set. T == par_k iff
|
||||
// (GenContext::Last(_), k) is in t_var.use_set.
|
||||
|
||||
match &self.var_data.records[t_var].allocation {
|
||||
VarAlloc::Temp { temp_var_data, .. } => {
|
||||
if !temp_var_data.use_set.contains(&(GenContext::Last(chunk_num), k)) {
|
||||
return Some((t_var, self.alloc_with_ca(t_var)));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let VarAlloc::Temp { temp_var_data, .. } =
|
||||
&self.var_data.records[t_var].allocation
|
||||
{
|
||||
if !temp_var_data
|
||||
.use_set
|
||||
.contains(&(GenContext::Last(chunk_num), k))
|
||||
{
|
||||
return Some((t_var, self.alloc_with_ca(t_var)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,23 +367,22 @@ impl DebrayAllocator {
|
||||
chunk_num: usize,
|
||||
code: &mut CodeDeque,
|
||||
) {
|
||||
match self.alloc_in_last_goal_hint(chunk_num) {
|
||||
Some((var_num, r)) => {
|
||||
let k = self.arg_c;
|
||||
if let Some((var_num, r)) = self.alloc_in_last_goal_hint(chunk_num) {
|
||||
let k = self.arg_c;
|
||||
|
||||
if r != k {
|
||||
let r = RegType::Temp(r);
|
||||
if r != k {
|
||||
let r = RegType::Temp(r);
|
||||
|
||||
code.push_back(Target::move_to_register(r, k));
|
||||
code.push_back(Target::move_to_register(r, k));
|
||||
|
||||
self.shallow_temp_mappings.swap_remove(&k);
|
||||
self.shallow_temp_mappings.insert(r.reg_num(), var_num);
|
||||
self.shallow_temp_mappings.swap_remove(&k);
|
||||
self.shallow_temp_mappings.insert(r.reg_num(), var_num);
|
||||
|
||||
self.var_data.records[var_num].allocation.set_register(r.reg_num());
|
||||
self.in_use.insert(r.reg_num());
|
||||
}
|
||||
self.var_data.records[var_num]
|
||||
.allocation
|
||||
.set_register(r.reg_num());
|
||||
self.in_use.insert(r.reg_num());
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -417,12 +439,9 @@ impl DebrayAllocator {
|
||||
fn in_place(&self, var_num: usize, term_loc: GenContext, r: RegType, k: usize) -> bool {
|
||||
match term_loc {
|
||||
GenContext::Head if !r.is_perm() => r.reg_num() == k,
|
||||
_ => {
|
||||
match &self.var_data.records[var_num].allocation {
|
||||
&VarAlloc::Temp { temp_reg, .. } if r.reg_num() == k =>
|
||||
temp_reg == k,
|
||||
_ => false,
|
||||
}
|
||||
_ => match &self.var_data.records[var_num].allocation {
|
||||
&VarAlloc::Temp { temp_reg, .. } if r.reg_num() == k => temp_reg == k,
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -466,11 +485,8 @@ impl DebrayAllocator {
|
||||
}
|
||||
|
||||
fn add_perm_to_free_list(&mut self, chunk_num: usize, var_num: usize) {
|
||||
match &self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(..) => {
|
||||
self.perm_free_list.push_back((chunk_num, var_num));
|
||||
}
|
||||
_ => {}
|
||||
if let VarAlloc::Perm(..) = &self.var_data.records[var_num].allocation {
|
||||
self.perm_free_list.push_back((chunk_num, var_num));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,8 +499,7 @@ impl DebrayAllocator {
|
||||
VarAlloc::Perm(p, PermVarAllocation::Pending) if *p > 0 => {
|
||||
return Some(std::mem::replace(p, 0));
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
@@ -495,13 +510,9 @@ impl DebrayAllocator {
|
||||
}
|
||||
|
||||
pub(crate) fn free_var(&mut self, chunk_num: usize, var_num: usize) {
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, allocation) => {
|
||||
*allocation = PermVarAllocation::Pending;
|
||||
self.add_perm_to_free_list(chunk_num, var_num);
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
if let VarAlloc::Perm(_, allocation) = &mut self.var_data.records[var_num].allocation {
|
||||
*allocation = PermVarAllocation::Pending;
|
||||
self.add_perm_to_free_list(chunk_num, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,7 +520,14 @@ impl DebrayAllocator {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
deep_safety,
|
||||
shallow_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
}
|
||||
@@ -524,21 +542,29 @@ impl DebrayAllocator {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
deep_safety,
|
||||
shallow_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
// GetVariable in head chunk is considered safe.
|
||||
if lvl == Level::Deep {
|
||||
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
} else if term_loc == GenContext::Head {
|
||||
*shallow_safety = VarSafetyStatus::GloballyUnneeded;
|
||||
} else {
|
||||
if let Some(temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c).cloned() {
|
||||
match &mut self.var_data.records[temp_var_num].allocation {
|
||||
VarAlloc::Temp { ref mut to_perm_var_num, .. } => {
|
||||
*to_perm_var_num = Some(var_num);
|
||||
}
|
||||
_ => unreachable!()
|
||||
} else if let Some(&temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c) {
|
||||
match &mut self.var_data.records[temp_var_num].allocation {
|
||||
VarAlloc::Temp {
|
||||
ref mut to_perm_var_num,
|
||||
..
|
||||
} => {
|
||||
*to_perm_var_num = Some(var_num);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -560,21 +586,27 @@ impl DebrayAllocator {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, PermVarAllocation::Done { ref mut shallow_safety, .. }) => {
|
||||
if !self.in_tail_position || self.branch_stack.safety_unneeded_in_branch(shallow_safety, &branch_designator) {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
ref mut shallow_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if !self.in_tail_position
|
||||
|| self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(shallow_safety, &branch_designator)
|
||||
{
|
||||
Target::argument_to_value(r, arg_c)
|
||||
} else {
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
Target::unsafe_argument_to_value(r, arg_c)
|
||||
}
|
||||
}
|
||||
VarAlloc::Temp { ref mut safety, .. } => {
|
||||
if self.branch_stack.safety_unneeded_in_branch(safety, &branch_designator) {
|
||||
Target::argument_to_value(r, arg_c)
|
||||
} else {
|
||||
*safety = VarSafetyStatus::GloballyUnneeded;
|
||||
Target::unsafe_argument_to_value(r, arg_c)
|
||||
}
|
||||
VarAlloc::Temp { .. } => {
|
||||
debug_assert!(matches!(r, RegType::Temp(_)));
|
||||
Target::argument_to_value(r, arg_c)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -590,8 +622,17 @@ impl DebrayAllocator {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, PermVarAllocation::Done { ref mut deep_safety, .. }) => {
|
||||
if self.branch_stack.safety_unneeded_in_branch(deep_safety, &branch_designator) {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
ref mut deep_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(deep_safety, &branch_designator)
|
||||
{
|
||||
Target::subterm_to_value(r)
|
||||
} else {
|
||||
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
@@ -599,7 +640,10 @@ impl DebrayAllocator {
|
||||
}
|
||||
}
|
||||
VarAlloc::Temp { ref mut safety, .. } => {
|
||||
if self.branch_stack.safety_unneeded_in_branch(safety, &branch_designator) {
|
||||
if self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(safety, &branch_designator)
|
||||
{
|
||||
Target::subterm_to_value(r)
|
||||
} else {
|
||||
*safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
@@ -626,7 +670,7 @@ impl Allocator for DebrayAllocator {
|
||||
in_use: BitSet::default(),
|
||||
temp_free_list: vec![],
|
||||
perm_free_list: VecDeque::new(),
|
||||
branch_stack: BranchStack { stack: vec![] }
|
||||
branch_stack: BranchStack { stack: vec![] },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,7 +732,7 @@ impl Allocator for DebrayAllocator {
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
cell: &Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
) {
|
||||
@@ -696,21 +740,23 @@ impl Allocator for DebrayAllocator {
|
||||
RegType::Temp(0) => {
|
||||
let o = self.alloc_reg_to_var::<Target>(var_num, lvl, term_loc, code);
|
||||
cell.set(VarReg::Norm(RegType::Temp(o)));
|
||||
|
||||
(RegType::Temp(o), true)
|
||||
}
|
||||
RegType::Perm(0) => {
|
||||
let p = self.alloc_perm_var(var_num, term_loc.chunk_num());
|
||||
cell.set(VarReg::Norm(RegType::Perm(p)));
|
||||
(RegType::Perm(p), true)
|
||||
}
|
||||
r @ RegType::Perm(_) => {
|
||||
let is_new_var = match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, allocation) => if allocation.pending() {
|
||||
*allocation = PermVarAllocation::done();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
},
|
||||
VarAlloc::Perm(_, allocation) => {
|
||||
if allocation.pending() {
|
||||
*allocation = PermVarAllocation::done();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
@@ -726,7 +772,7 @@ impl Allocator for DebrayAllocator {
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
cell: &Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
r: RegType,
|
||||
@@ -792,8 +838,21 @@ impl Allocator for DebrayAllocator {
|
||||
|
||||
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType {
|
||||
match self.get_binding(var_num) {
|
||||
RegType::Perm(0) | RegType::Temp(0) => {
|
||||
RegType::Perm(self.alloc_perm_var(var_num, chunk_num))
|
||||
RegType::Perm(0) => RegType::Perm(self.alloc_perm_var(var_num, chunk_num)),
|
||||
RegType::Temp(0) => {
|
||||
let t = self.alloc_reg_to_non_var();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Temp {
|
||||
temp_reg, safety, ..
|
||||
} => {
|
||||
*temp_reg = t;
|
||||
*safety = VarSafetyStatus::GloballyUnneeded;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
RegType::Temp(t)
|
||||
}
|
||||
r => r,
|
||||
}
|
||||
@@ -816,19 +875,21 @@ impl Allocator for DebrayAllocator {
|
||||
self.arg_c += 1;
|
||||
}
|
||||
|
||||
fn reset_at_head(&mut self, args: &Vec<Term>) {
|
||||
fn reset_at_head(&mut self, args: &[Term]) {
|
||||
self.reset_arg(args.len());
|
||||
self.arity = args.len();
|
||||
|
||||
for (idx, arg) in args.iter().enumerate() {
|
||||
if let &Term::Var(_, ref var) = arg {
|
||||
if let Term::Var(_, ref var) = arg {
|
||||
let var_num = var.to_var_num().unwrap();
|
||||
let r = self.get_binding(var_num);
|
||||
|
||||
if !r.is_perm() && r.reg_num() == 0 {
|
||||
self.in_use.insert(idx + 1);
|
||||
self.shallow_temp_mappings.insert(idx + 1, var_num);
|
||||
self.var_data.records[var_num].allocation.set_register(idx + 1);
|
||||
self.var_data.records[var_num]
|
||||
.allocation
|
||||
.set_register(idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
706
src/ffi.rs
706
src/ffi.rs
@@ -24,12 +24,14 @@ use crate::atom_table::Atom;
|
||||
use std::alloc::{alloc, Layout};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::ffi::{CString, c_void};
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error;
|
||||
use std::ffi::{c_void, CString};
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
use libffi::low::{ffi_cif, types, CodePtr, ffi_abi_FFI_DEFAULT_ABI, prep_cif, ffi_type, type_tag};
|
||||
use libloading::{Symbol, Library};
|
||||
use libffi::low::type_tag::STRUCT;
|
||||
use libffi::low::{ffi_abi_FFI_DEFAULT_ABI, ffi_cif, ffi_type, prep_cif, types, CodePtr};
|
||||
use libloading::{Library, Symbol};
|
||||
|
||||
pub struct FunctionDefinition {
|
||||
pub name: String,
|
||||
@@ -65,338 +67,394 @@ struct PointerArgs {
|
||||
|
||||
impl ForeignFunctionTable {
|
||||
pub fn merge(&mut self, other: ForeignFunctionTable) {
|
||||
self.table.extend(other.table);
|
||||
self.table.extend(other.table);
|
||||
}
|
||||
|
||||
pub fn define_struct(&mut self, name: &str, atom_fields: Vec<Atom>) {
|
||||
let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(&x)).collect();
|
||||
fields.push(std::ptr::null_mut::<ffi_type>());
|
||||
let mut struct_type: ffi_type = Default::default();
|
||||
struct_type.type_ = type_tag::STRUCT;
|
||||
struct_type.elements = fields.as_mut_ptr();
|
||||
self.structs.insert(name.to_string(), StructImpl { ffi_type: struct_type, fields, atom_fields});
|
||||
let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(x)).collect();
|
||||
fields.push(std::ptr::null_mut::<ffi_type>());
|
||||
let struct_type = ffi_type {
|
||||
type_: STRUCT,
|
||||
elements: fields.as_mut_ptr(),
|
||||
..Default::default()
|
||||
};
|
||||
self.structs.insert(
|
||||
name.to_string(),
|
||||
StructImpl {
|
||||
ffi_type: struct_type,
|
||||
fields,
|
||||
atom_fields,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type {
|
||||
unsafe {
|
||||
match source {
|
||||
atom!("sint64") => &mut types::sint64,
|
||||
atom!("sint32") => &mut types::sint32,
|
||||
atom!("sint16") => &mut types::sint16,
|
||||
atom!("sint8") => &mut types::sint8,
|
||||
atom!("uint64") => &mut types::uint64,
|
||||
atom!("uint32") => &mut types::uint32,
|
||||
atom!("uint16") => &mut types::uint16,
|
||||
atom!("uint8") => &mut types::uint8,
|
||||
atom!("bool") => &mut types::sint8,
|
||||
atom!("void") => &mut types::void,
|
||||
atom!("cstr") => &mut types::pointer,
|
||||
atom!("ptr") => &mut types::pointer,
|
||||
atom!("f32") => &mut types::float,
|
||||
atom!("f64") => &mut types::double,
|
||||
struct_name => {
|
||||
match self.structs.get_mut(struct_name.as_str()) {
|
||||
Some(ref mut struct_type) => {
|
||||
&mut struct_type.ffi_type
|
||||
},
|
||||
None => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
match source {
|
||||
atom!("sint64") => addr_of_mut!(types::sint64),
|
||||
atom!("sint32") => addr_of_mut!(types::sint32),
|
||||
atom!("sint16") => addr_of_mut!(types::sint16),
|
||||
atom!("sint8") => addr_of_mut!(types::sint8),
|
||||
atom!("uint64") => addr_of_mut!(types::uint64),
|
||||
atom!("uint32") => addr_of_mut!(types::uint32),
|
||||
atom!("uint16") => addr_of_mut!(types::uint16),
|
||||
atom!("uint8") => addr_of_mut!(types::uint8),
|
||||
atom!("bool") => addr_of_mut!(types::sint8),
|
||||
atom!("void") => addr_of_mut!(types::void),
|
||||
atom!("cstr") => addr_of_mut!(types::pointer),
|
||||
atom!("ptr") => addr_of_mut!(types::pointer),
|
||||
atom!("f32") => addr_of_mut!(types::float),
|
||||
atom!("f64") => addr_of_mut!(types::double),
|
||||
struct_name => match self.structs.get_mut(&*struct_name.as_str()) {
|
||||
Some(ref mut struct_type) => &mut struct_type.ffi_type,
|
||||
None => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load_library(&mut self, library_name: &str, functions: &Vec<FunctionDefinition>) -> Result<(), Box<dyn Error>> {
|
||||
let mut ff_table: ForeignFunctionTable = Default::default();
|
||||
unsafe {
|
||||
let library = Library::new(library_name)?;
|
||||
for function in functions {
|
||||
let symbol_name: CString = CString::new(function.name.clone())?;
|
||||
let code_ptr: Symbol<*mut c_void> = library.get(&symbol_name.into_bytes_with_nul())?;
|
||||
let mut args: Vec<_> = function.args.iter().map(|x| self.map_type_ffi(&x)).collect();
|
||||
let mut cif: ffi_cif = Default::default();
|
||||
prep_cif(
|
||||
&mut cif,
|
||||
ffi_abi_FFI_DEFAULT_ABI,
|
||||
args.len(),
|
||||
self.map_type_ffi(&function.return_value),
|
||||
args.as_mut_ptr()
|
||||
).unwrap();
|
||||
pub(crate) fn load_library(
|
||||
&mut self,
|
||||
library_name: &str,
|
||||
functions: &Vec<FunctionDefinition>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut ff_table: ForeignFunctionTable = Default::default();
|
||||
unsafe {
|
||||
let library = Library::new(library_name)?;
|
||||
for function in functions {
|
||||
let symbol_name: CString = CString::new(function.name.clone())?;
|
||||
let code_ptr: Symbol<*mut c_void> =
|
||||
library.get(&symbol_name.into_bytes_with_nul())?;
|
||||
let mut args: Vec<_> = function.args.iter().map(|x| self.map_type_ffi(x)).collect();
|
||||
let mut cif: ffi_cif = Default::default();
|
||||
prep_cif(
|
||||
&mut cif,
|
||||
ffi_abi_FFI_DEFAULT_ABI,
|
||||
args.len(),
|
||||
self.map_type_ffi(&function.return_value),
|
||||
args.as_mut_ptr(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let return_struct_name = if (*self.map_type_ffi(&function.return_value)).type_ as u32 == libffi::raw::FFI_TYPE_STRUCT {
|
||||
Some(function.return_value.as_str().to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let return_struct_name = if (*self.map_type_ffi(&function.return_value)).type_
|
||||
as u32
|
||||
== libffi::raw::FFI_TYPE_STRUCT
|
||||
{
|
||||
Some(function.return_value.as_str().to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
ff_table.table.insert(function.name.clone(), FunctionImpl {
|
||||
cif,
|
||||
args,
|
||||
code_ptr: CodePtr(code_ptr.into_raw().into_raw() as *mut _),
|
||||
return_struct_name,
|
||||
});
|
||||
}
|
||||
std::mem::forget(library);
|
||||
}
|
||||
self.merge(ff_table);
|
||||
Ok(())
|
||||
ff_table.table.insert(
|
||||
function.name.clone(),
|
||||
FunctionImpl {
|
||||
cif,
|
||||
args,
|
||||
code_ptr: CodePtr(code_ptr.into_raw().into_raw() as *mut _),
|
||||
return_struct_name,
|
||||
},
|
||||
);
|
||||
}
|
||||
std::mem::forget(library);
|
||||
}
|
||||
self.merge(ff_table);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_pointer_args(args: &mut Vec<Value>, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap<String, StructImpl>) -> Result<PointerArgs, FFIError> {
|
||||
let mut pointers = Vec::with_capacity(args.len());
|
||||
let mut _memory = Vec::new();
|
||||
for i in 0..args.len() {
|
||||
let field_type = type_args[i];
|
||||
unsafe {
|
||||
macro_rules! push_int {
|
||||
($type:ty) => {
|
||||
{
|
||||
let n: $type = <$type>::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?;
|
||||
let mut box_value = Box::new(n) as Box<dyn Any>;
|
||||
pointers.push(&mut *box_value as *mut _ as *mut c_void);
|
||||
_memory.push(box_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match (*field_type).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => push_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => push_int!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => push_int!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => push_int!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => push_int!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => push_int!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => push_int!(u64),
|
||||
libffi::raw::FFI_TYPE_SINT64 => push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_FLOAT => {
|
||||
let n: f32 = args[i].as_float()? as f32;
|
||||
let mut box_value = Box::new(n) as Box<dyn Any>;
|
||||
pointers.push(&mut *box_value as *mut _ as *mut c_void);
|
||||
_memory.push(box_value);
|
||||
},
|
||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||
let n: f64 = args[i].as_float()?;
|
||||
let mut box_value = Box::new(n) as Box<dyn Any>;
|
||||
pointers.push(&mut *box_value as *mut _ as *mut c_void);
|
||||
_memory.push(box_value);
|
||||
},
|
||||
libffi::raw::FFI_TYPE_POINTER => {
|
||||
let ptr: *mut c_void = args[i].as_ptr()?;
|
||||
pointers.push(ptr);
|
||||
},
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let (mut ptr, _size, _align) = Self::build_struct(&mut args[i], structs_table)?;
|
||||
pointers.push(&mut *ptr as *mut _ as *mut c_void);
|
||||
_memory.push(ptr);
|
||||
},
|
||||
_ => return Err(FFIError::InvalidFFIType)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(PointerArgs {
|
||||
pointers,
|
||||
_memory
|
||||
})
|
||||
fn build_pointer_args(
|
||||
args: &mut [Value],
|
||||
type_args: &[*mut ffi_type],
|
||||
structs_table: &mut HashMap<String, StructImpl>,
|
||||
) -> Result<PointerArgs, FFIError> {
|
||||
let mut pointers = Vec::with_capacity(args.len());
|
||||
let mut _memory = Vec::new();
|
||||
for i in 0..args.len() {
|
||||
let field_type = type_args[i];
|
||||
unsafe {
|
||||
macro_rules! push_int {
|
||||
($type:ty) => {{
|
||||
let n: $type = <$type>::try_from(args[i].as_int()?)
|
||||
.map_err(|_| FFIError::ValueDontFit)?;
|
||||
let mut box_value = Box::new(n) as Box<dyn Any>;
|
||||
pointers.push(&mut *box_value as *mut _ as *mut c_void);
|
||||
_memory.push(box_value);
|
||||
}};
|
||||
}
|
||||
|
||||
match (*field_type).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => push_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => push_int!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => push_int!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => push_int!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => push_int!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => push_int!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => push_int!(u64),
|
||||
libffi::raw::FFI_TYPE_SINT64 => push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_FLOAT => {
|
||||
let n: f32 = args[i].as_float()? as f32;
|
||||
let mut box_value = Box::new(n) as Box<dyn Any>;
|
||||
pointers.push(&mut *box_value as *mut _ as *mut c_void);
|
||||
_memory.push(box_value);
|
||||
}
|
||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||
let n: f64 = args[i].as_float()?;
|
||||
let mut box_value = Box::new(n) as Box<dyn Any>;
|
||||
pointers.push(&mut *box_value as *mut _ as *mut c_void);
|
||||
_memory.push(box_value);
|
||||
}
|
||||
libffi::raw::FFI_TYPE_POINTER => {
|
||||
let ptr: *mut c_void = args[i].as_ptr()?;
|
||||
pointers.push(ptr);
|
||||
}
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let (mut ptr, _size, _align) =
|
||||
Self::build_struct(&mut args[i], structs_table)?;
|
||||
pointers.push(&mut *ptr as *mut _ as *mut c_void);
|
||||
_memory.push(ptr);
|
||||
}
|
||||
_ => return Err(FFIError::InvalidFFIType),
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(PointerArgs { pointers, _memory })
|
||||
}
|
||||
|
||||
fn build_struct(arg: &mut Value, structs_table: &mut HashMap<String, StructImpl>) -> Result<(Box<dyn Any>, usize, usize), FFIError> {
|
||||
unsafe {
|
||||
match arg {
|
||||
Value::Struct(ref name, ref mut struct_args) => {
|
||||
if let Some(ref mut struct_type) = structs_table.clone().get_mut(name) {
|
||||
let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap();
|
||||
let align = struct_type.ffi_type.alignment as usize;
|
||||
let size = struct_type.ffi_type.size;
|
||||
let ptr = alloc(layout) as *mut c_void;
|
||||
let mut field_ptr = ptr;
|
||||
fn build_struct(
|
||||
arg: &mut Value,
|
||||
structs_table: &mut HashMap<String, StructImpl>,
|
||||
) -> Result<(Box<dyn Any>, usize, usize), FFIError> {
|
||||
unsafe {
|
||||
match arg {
|
||||
Value::Struct(ref name, ref mut struct_args) => {
|
||||
if let Some(ref mut struct_type) = structs_table.clone().get_mut(name) {
|
||||
let layout = Layout::from_size_align(
|
||||
struct_type.ffi_type.size,
|
||||
struct_type.ffi_type.alignment.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let align = struct_type.ffi_type.alignment as usize;
|
||||
let size = struct_type.ffi_type.size;
|
||||
let ptr = alloc(layout) as *mut c_void;
|
||||
let mut field_ptr = ptr;
|
||||
|
||||
for i in 0..(struct_type.fields.len()-1) {
|
||||
macro_rules! try_write_int {
|
||||
($type:ty) => {
|
||||
{
|
||||
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
|
||||
let n: $type = <$type>::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?;
|
||||
std::ptr::write(field_ptr as *mut $type, n);
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
|
||||
}
|
||||
}
|
||||
}
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..(struct_type.fields.len() - 1) {
|
||||
macro_rules! try_write_int {
|
||||
($type:ty) => {{
|
||||
field_ptr = field_ptr
|
||||
.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
|
||||
let n: $type = <$type>::try_from(struct_args[i].as_int()?)
|
||||
.map_err(|_| FFIError::ValueDontFit)?;
|
||||
std::ptr::write(field_ptr as *mut $type, n);
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! write {
|
||||
($type:ty, $value:expr) => {
|
||||
{
|
||||
let data: $type = $value;
|
||||
std::ptr::write(field_ptr as *mut $type, data);
|
||||
field_ptr = field_ptr.add(align);
|
||||
}
|
||||
}
|
||||
}
|
||||
macro_rules! write {
|
||||
($type:ty, $value:expr) => {{
|
||||
let data: $type = $value;
|
||||
std::ptr::write(field_ptr as *mut $type, data);
|
||||
field_ptr = field_ptr.add(align);
|
||||
}};
|
||||
}
|
||||
|
||||
let field = struct_type.fields[i];
|
||||
match (*field).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => try_write_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => try_write_int!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => try_write_int!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => try_write_int!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => try_write_int!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => try_write_int!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => try_write_int!(u64),
|
||||
libffi::raw::FFI_TYPE_SINT64 => try_write_int!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => write!(*mut c_void, struct_args[i].as_ptr()?),
|
||||
libffi::raw::FFI_TYPE_FLOAT => write!(f32, struct_args[i].as_float()? as f32),
|
||||
libffi::raw::FFI_TYPE_DOUBLE => write!(f64, struct_args[i].as_float()?),
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let (struct_ptr, struct_size, struct_align) = Self::build_struct(&mut struct_args[i], structs_table)?;
|
||||
field_ptr = field_ptr.add(field_ptr.align_offset(struct_align));
|
||||
|
||||
std::ptr::copy(& *struct_ptr as *const _ as *const c_void, field_ptr as *mut c_void, struct_size);
|
||||
field_ptr = field_ptr.add(struct_size);
|
||||
},
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok((Box::from_raw(ptr), size, align));
|
||||
} else {
|
||||
return Err(FFIError::InvalidStructName);
|
||||
}
|
||||
}
|
||||
_ => return Err(FFIError::ValueCast)
|
||||
}
|
||||
}
|
||||
let field = struct_type.fields[i];
|
||||
match (*field).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => try_write_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => try_write_int!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => try_write_int!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => try_write_int!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => try_write_int!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => try_write_int!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => try_write_int!(u64),
|
||||
libffi::raw::FFI_TYPE_SINT64 => try_write_int!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => {
|
||||
write!(*mut c_void, struct_args[i].as_ptr()?)
|
||||
}
|
||||
libffi::raw::FFI_TYPE_FLOAT => {
|
||||
write!(f32, struct_args[i].as_float()? as f32)
|
||||
}
|
||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||
write!(f64, struct_args[i].as_float()?)
|
||||
}
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let (struct_ptr, struct_size, struct_align) =
|
||||
Self::build_struct(&mut struct_args[i], structs_table)?;
|
||||
field_ptr = field_ptr.add(field_ptr.align_offset(struct_align));
|
||||
|
||||
std::ptr::copy(
|
||||
&*struct_ptr as *const _ as *const c_void,
|
||||
field_ptr,
|
||||
struct_size,
|
||||
);
|
||||
field_ptr = field_ptr.add(struct_size);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[allow(clippy::from_raw_with_void_ptr)]
|
||||
Ok((Box::from_raw(ptr), size, align))
|
||||
} else {
|
||||
Err(FFIError::InvalidStructName)
|
||||
}
|
||||
}
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec(&mut self, name: &str, mut args: Vec<Value>) -> Result<Value, FFIError> {
|
||||
let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?;
|
||||
let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs)?;
|
||||
|
||||
return unsafe {
|
||||
macro_rules! call_and_return {
|
||||
($type:ty) => {
|
||||
{
|
||||
let mut n: Box<u8> = Box::new(0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
|
||||
);
|
||||
Ok(Value::Int(i64::from(*n)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match (*function_impl.cif.rtype).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_VOID => call_and_return!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT8 => call_and_return!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => call_and_return!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => call_and_return!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => call_and_return!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => call_and_return!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => call_and_return!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => {
|
||||
let mut n: Box<u64> = Box::new(0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
|
||||
);
|
||||
Ok(Value::Int(i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?))
|
||||
},
|
||||
libffi::raw::FFI_TYPE_SINT64 => call_and_return!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => call_and_return!(*mut c_void),
|
||||
libffi::raw::FFI_TYPE_FLOAT => {
|
||||
let mut n: Box<f32> = Box::new(0.0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
|
||||
);
|
||||
Ok(Value::Float((*n).into()))
|
||||
},
|
||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||
let mut n: Box<f64> = Box::new(0.0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
|
||||
);
|
||||
Ok(Value::Float(*n))
|
||||
},
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let name = &function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?;
|
||||
let struct_type = self.structs.get(name).ok_or(FFIError::StructNotFound)?;
|
||||
let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap();
|
||||
let ptr = alloc(layout) as *mut c_void;
|
||||
let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?;
|
||||
let mut pointer_args =
|
||||
Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs)?;
|
||||
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *ptr as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
|
||||
);
|
||||
let struct_val = self.read_struct(ptr, name, struct_type);
|
||||
drop(Box::from_raw(ptr));
|
||||
struct_val
|
||||
}
|
||||
_ => unreachable!()
|
||||
}
|
||||
};
|
||||
return unsafe {
|
||||
macro_rules! call_and_return {
|
||||
($type:ty) => {{
|
||||
let mut n: Box<u8> = Box::new(0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void,
|
||||
);
|
||||
Ok(Value::Int(i64::from(*n)))
|
||||
}};
|
||||
}
|
||||
|
||||
match (*function_impl.cif.rtype).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_VOID => call_and_return!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT8 => call_and_return!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => call_and_return!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => call_and_return!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => call_and_return!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => call_and_return!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => call_and_return!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => {
|
||||
let mut n: Box<u64> = Box::new(0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
Ok(Value::Int(
|
||||
i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?,
|
||||
))
|
||||
}
|
||||
libffi::raw::FFI_TYPE_SINT64 => call_and_return!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => call_and_return!(*mut c_void),
|
||||
libffi::raw::FFI_TYPE_FLOAT => {
|
||||
let mut n: Box<f32> = Box::new(0.0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
Ok(Value::Float((*n).into()))
|
||||
}
|
||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||
let mut n: Box<f64> = Box::new(0.0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
Ok(Value::Float(*n))
|
||||
}
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let name = &function_impl
|
||||
.return_struct_name
|
||||
.clone()
|
||||
.ok_or(FFIError::StructNotFound)?;
|
||||
let struct_type = self.structs.get(name).ok_or(FFIError::StructNotFound)?;
|
||||
let layout = Layout::from_size_align(
|
||||
struct_type.ffi_type.size,
|
||||
struct_type.ffi_type.alignment.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let ptr = alloc(layout) as *mut c_void;
|
||||
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *ptr as *mut _,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
let struct_val = self.read_struct(ptr, name, struct_type);
|
||||
#[allow(clippy::from_raw_with_void_ptr)]
|
||||
drop(Box::from_raw(ptr));
|
||||
struct_val
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn read_struct(&self, ptr: *mut c_void, name: &str, struct_type: &StructImpl) -> Result<Value, FFIError> {
|
||||
unsafe {
|
||||
let mut returns = Vec::new();
|
||||
let mut field_ptr = ptr;
|
||||
fn read_struct(
|
||||
&self,
|
||||
ptr: *mut c_void,
|
||||
name: &str,
|
||||
struct_type: &StructImpl,
|
||||
) -> Result<Value, FFIError> {
|
||||
unsafe {
|
||||
let mut returns = Vec::new();
|
||||
let mut field_ptr = ptr;
|
||||
|
||||
for i in 0..(struct_type.fields.len()-1) {
|
||||
let field = struct_type.fields[i];
|
||||
for i in 0..(struct_type.fields.len() - 1) {
|
||||
let field = struct_type.fields[i];
|
||||
|
||||
macro_rules! read_and_push_int {
|
||||
($type:ty) => {
|
||||
{
|
||||
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
|
||||
let n = std::ptr::read(field_ptr as *mut $type);
|
||||
returns.push(Value::Int(i64::from(n)));
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
|
||||
}
|
||||
}
|
||||
}
|
||||
macro_rules! read_and_push_int {
|
||||
($type:ty) => {{
|
||||
field_ptr =
|
||||
field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
|
||||
let n = std::ptr::read(field_ptr as *mut $type);
|
||||
returns.push(Value::Int(i64::from(n)));
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
|
||||
}};
|
||||
}
|
||||
|
||||
match (*field).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => read_and_push_int!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => read_and_push_int!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => {
|
||||
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<u64>()));
|
||||
let n = std::ptr::read(field_ptr as *mut u64);
|
||||
returns.push(Value::Int(i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?));
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<u64>());
|
||||
},
|
||||
libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let substruct = struct_type.atom_fields[i].as_str();
|
||||
let struct_type = self.structs.get(substruct).ok_or(FFIError::StructNotFound)?;
|
||||
field_ptr = field_ptr.add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize));
|
||||
let struct_val = self.read_struct(field_ptr, substruct, struct_type);
|
||||
returns.push(struct_val?);
|
||||
field_ptr = field_ptr.add(struct_type.ffi_type.size);
|
||||
},
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::Struct(name.into(), returns))
|
||||
}
|
||||
match (*field).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => read_and_push_int!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => read_and_push_int!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => {
|
||||
field_ptr =
|
||||
field_ptr.add(field_ptr.align_offset(std::mem::align_of::<u64>()));
|
||||
let n = std::ptr::read(field_ptr as *mut u64);
|
||||
returns.push(Value::Int(
|
||||
i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?,
|
||||
));
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<u64>());
|
||||
}
|
||||
libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let substruct = struct_type.atom_fields[i].as_str();
|
||||
let struct_type = self
|
||||
.structs
|
||||
.get(&*substruct)
|
||||
.ok_or(FFIError::StructNotFound)?;
|
||||
field_ptr = field_ptr
|
||||
.add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize));
|
||||
let struct_val = self.read_struct(field_ptr, &substruct, struct_type);
|
||||
returns.push(struct_val?);
|
||||
field_ptr = field_ptr.add(struct_type.ffi_type.size);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::Struct(name.into(), returns))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,26 +468,26 @@ pub enum Value {
|
||||
|
||||
impl Value {
|
||||
fn as_int(&self) -> Result<i64, FFIError> {
|
||||
match self {
|
||||
Value::Int(n) => Ok(*n),
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
match self {
|
||||
Value::Int(n) => Ok(*n),
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_float(&self) -> Result<f64, FFIError> {
|
||||
match self {
|
||||
Value::Float(n) => Ok(*n),
|
||||
Value::Int(n) => Ok(*n as f64),
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
match self {
|
||||
Value::Float(n) => Ok(*n),
|
||||
Value::Int(n) => Ok(*n as f64),
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_ptr(&mut self) -> Result<*mut c_void, FFIError> {
|
||||
match self {
|
||||
Value::CString(ref mut cstr) => Ok(&mut *cstr as *mut _ as *mut c_void),
|
||||
Value::Int(n) => Ok(*n as *mut c_void),
|
||||
_ => Err(FFIError::ValueCast)
|
||||
}
|
||||
match self {
|
||||
Value::CString(ref mut cstr) => Ok(&mut *cstr as *mut _ as *mut c_void),
|
||||
Value::Int(n) => Ok(*n as *mut c_void),
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
187
src/forms.rs
187
src/forms.rs
@@ -7,10 +7,11 @@ use crate::machine::loader::PredicateQueue;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::parser::CompositeOpDesc;
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::parser::parser::CompositeOpDesc;
|
||||
use crate::types::*;
|
||||
|
||||
use dashu::base::Signed;
|
||||
use fxhash::FxBuildHasher;
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
@@ -58,7 +59,7 @@ impl AppendOrPrepend {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum VarComparison {
|
||||
Indistinct,
|
||||
Distinct
|
||||
Distinct,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@@ -99,11 +100,7 @@ pub enum RootIterationPolicy {
|
||||
impl RootIterationPolicy {
|
||||
#[inline(always)]
|
||||
pub fn iterable(&self) -> bool {
|
||||
if let RootIterationPolicy::Iterated = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, RootIterationPolicy::Iterated)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,13 +148,17 @@ impl DerefMut for ChunkedTermVec {
|
||||
}
|
||||
|
||||
impl ChunkedTermVec {
|
||||
#[allow(clippy::new_without_default)]
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self { chunk_vec: VecDeque::new() }
|
||||
Self {
|
||||
chunk_vec: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reserve_branch(&mut self, capacity: usize) {
|
||||
self.chunk_vec.push_back(ChunkedTerms::Branch(Vec::with_capacity(capacity)));
|
||||
self.chunk_vec
|
||||
.push_back(ChunkedTerms::Branch(Vec::with_capacity(capacity)));
|
||||
}
|
||||
|
||||
pub fn push_branch_arm(&mut self, branch: VecDeque<ChunkedTerms>) {
|
||||
@@ -173,19 +174,22 @@ impl ChunkedTermVec {
|
||||
|
||||
#[inline]
|
||||
pub fn add_chunk(&mut self) {
|
||||
self.chunk_vec.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![])));
|
||||
self.chunk_vec
|
||||
.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![])));
|
||||
}
|
||||
|
||||
pub fn push_chunk_term(&mut self, term: QueryTerm) {
|
||||
match self.chunk_vec.back_mut() {
|
||||
Some(ChunkedTerms::Branch(_)) => {
|
||||
self.chunk_vec.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term])));
|
||||
self.chunk_vec
|
||||
.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term])));
|
||||
}
|
||||
Some(ChunkedTerms::Chunk(chunk)) => {
|
||||
chunk.push_back(term);
|
||||
}
|
||||
None => {
|
||||
self.chunk_vec.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term])));
|
||||
self.chunk_vec
|
||||
.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term])));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,8 +200,8 @@ pub enum QueryTerm {
|
||||
// register, clause type, subterms, clause call policy.
|
||||
Clause(Cell<RegType>, ClauseType, Vec<Term>, CallPolicy),
|
||||
Fail,
|
||||
LocalCut(usize), // var_num
|
||||
GlobalCut(usize), // var_num
|
||||
LocalCut { var_num: usize, cut_prev: bool }, // var_num
|
||||
GlobalCut(usize), // var_num
|
||||
GetCutPoint { var_num: usize, prev_b: bool },
|
||||
GetLevel(usize), // var_num
|
||||
}
|
||||
@@ -205,7 +209,7 @@ pub enum QueryTerm {
|
||||
impl QueryTerm {
|
||||
pub(crate) fn arity(&self) -> usize {
|
||||
match self {
|
||||
&QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(),
|
||||
QueryTerm::Clause(_, _, subterms, ..) => subterms.len(),
|
||||
&QueryTerm::GetLevel(_) | &QueryTerm::GetCutPoint { .. } => 1,
|
||||
_ => 0,
|
||||
}
|
||||
@@ -266,11 +270,10 @@ impl ClauseInfo for Term {
|
||||
fn name(&self) -> Option<Atom> {
|
||||
match self {
|
||||
Term::Clause(_, name, terms) => {
|
||||
|
||||
match name {
|
||||
atom!(":-") => {
|
||||
match terms.len() {
|
||||
1 => None, // a declaration.
|
||||
1 => None, // a declaration.
|
||||
2 => terms[0].name(),
|
||||
_ => Some(*name),
|
||||
}
|
||||
@@ -285,7 +288,7 @@ impl ClauseInfo for Term {
|
||||
|
||||
fn arity(&self) -> usize {
|
||||
match self {
|
||||
Term::Clause(_, name, terms) => match name.as_str() {
|
||||
Term::Clause(_, name, terms) => match &*name.as_str() {
|
||||
":-" => match terms.len() {
|
||||
1 => 0,
|
||||
2 => terms[0].arity(),
|
||||
@@ -311,15 +314,15 @@ impl ClauseInfo for Rule {
|
||||
impl ClauseInfo for PredicateClause {
|
||||
fn name(&self) -> Option<Atom> {
|
||||
match self {
|
||||
&PredicateClause::Fact(ref term, ..) => term.head.name(),
|
||||
&PredicateClause::Rule(ref rule, ..) => rule.name(),
|
||||
PredicateClause::Fact(ref term, ..) => term.head.name(),
|
||||
PredicateClause::Rule(ref rule, ..) => rule.name(),
|
||||
}
|
||||
}
|
||||
|
||||
fn arity(&self) -> usize {
|
||||
match self {
|
||||
&PredicateClause::Fact(ref term, ..) => term.head.arity(),
|
||||
&PredicateClause::Rule(ref rule, ..) => rule.arity(),
|
||||
PredicateClause::Fact(ref term, ..) => term.head.arity(),
|
||||
PredicateClause::Rule(ref rule, ..) => rule.arity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,7 +337,7 @@ impl PredicateClause {
|
||||
pub(crate) fn args(&self) -> Option<&[Term]> {
|
||||
match self {
|
||||
PredicateClause::Fact(term, ..) => match &term.head {
|
||||
Term::Clause(_, _, args) => Some(&args),
|
||||
Term::Clause(_, _, args) => Some(args),
|
||||
_ => None,
|
||||
},
|
||||
PredicateClause::Rule(rule, ..) => {
|
||||
@@ -410,7 +413,6 @@ pub(crate) fn fixity(spec: u32) -> Fixity {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl OpDecl {
|
||||
#[inline]
|
||||
pub(crate) fn new(op_desc: OpDesc, name: Atom) -> Self {
|
||||
@@ -429,13 +431,10 @@ impl OpDecl {
|
||||
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_mut(&key) {
|
||||
Some(cell) => {
|
||||
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 => {}
|
||||
if let Some(cell) = op_dir.get_mut(&key) {
|
||||
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));
|
||||
}
|
||||
|
||||
op_dir.insert(key, self.op_desc)
|
||||
@@ -446,7 +445,7 @@ impl OpDecl {
|
||||
existing_desc: Option<CompositeOpDesc>,
|
||||
op_dir: &mut OpDir,
|
||||
) -> Result<(), SessionError> {
|
||||
let (spec, name) = (self.op_desc.get_spec(), self.name.clone());
|
||||
let (spec, name) = (self.op_desc.get_spec(), self.name);
|
||||
|
||||
if is_infix!(spec as u32) {
|
||||
if let Some(desc) = existing_desc {
|
||||
@@ -477,35 +476,28 @@ pub enum AtomOrString {
|
||||
|
||||
impl AtomOrString {
|
||||
#[inline]
|
||||
pub fn as_atom(&self, atom_tbl: &mut AtomTable) -> Atom {
|
||||
pub fn as_atom(&self, atom_tbl: &AtomTable) -> Atom {
|
||||
match self {
|
||||
&AtomOrString::Atom(atom) => {
|
||||
atom
|
||||
}
|
||||
AtomOrString::String(string) => {
|
||||
atom_tbl.build_with(&string)
|
||||
}
|
||||
&AtomOrString::Atom(atom) => atom,
|
||||
AtomOrString::String(string) => AtomTable::build_with(atom_tbl, string),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
pub fn as_str(&self) -> AtomString<'_> {
|
||||
match self {
|
||||
AtomOrString::Atom(atom) if atom == &atom!("[]") => "",
|
||||
AtomOrString::Atom(atom) if atom == &atom!("[]") => AtomString::Static(""),
|
||||
AtomOrString::Atom(atom) => atom.as_str(),
|
||||
AtomOrString::String(string) => string.as_str(),
|
||||
AtomOrString::String(string) => AtomString::Static(string.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_string(self) -> String {
|
||||
match self {
|
||||
AtomOrString::Atom(atom) => {
|
||||
atom.as_str().to_owned()
|
||||
}
|
||||
AtomOrString::String(string) => {
|
||||
string
|
||||
}
|
||||
impl From<AtomOrString> for String {
|
||||
fn from(val: AtomOrString) -> Self {
|
||||
match val {
|
||||
AtomOrString::Atom(atom) => atom.as_str().to_owned(),
|
||||
AtomOrString::String(string) => string,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -547,7 +539,7 @@ pub(crate) fn fetch_op_spec(name: Atom, arity: usize, op_dir: &OpDir) -> Option<
|
||||
}
|
||||
}),
|
||||
1 => {
|
||||
if let Some(op_desc) = op_dir.get(&(name.clone(), Fixity::Pre)) {
|
||||
if let Some(op_desc) = op_dir.get(&(name, Fixity::Pre)) {
|
||||
if op_desc.get_prec() > 0 {
|
||||
return Some(*op_desc);
|
||||
}
|
||||
@@ -561,9 +553,7 @@ pub(crate) fn fetch_op_spec(name: Atom, arity: usize, op_dir: &OpDir) -> Option<
|
||||
}
|
||||
})
|
||||
}
|
||||
0 => {
|
||||
fetch_atom_op_spec(name, None, op_dir)
|
||||
}
|
||||
0 => fetch_atom_op_spec(name, None, op_dir),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -595,10 +585,7 @@ pub 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::with_hasher(FxBuildHasher::default()),
|
||||
@@ -620,7 +607,7 @@ impl Module {
|
||||
meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()),
|
||||
extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()),
|
||||
local_extensible_predicates: LocalExtensiblePredicates::with_hasher(
|
||||
FxBuildHasher::default()
|
||||
FxBuildHasher::default(),
|
||||
),
|
||||
listing_src: ListingSource::DynamicallyGenerated,
|
||||
}
|
||||
@@ -753,11 +740,15 @@ impl ArenaFrom<Number> for HeapCellValue {
|
||||
impl Number {
|
||||
pub(crate) fn sign(&self) -> Number {
|
||||
match self {
|
||||
&Number::Float(f) if f == 0.0 => Number::Float(OrderedFloat(0f64)),
|
||||
&Number::Float(f) => Number::Float(OrderedFloat(f.signum())),
|
||||
Number::Float(f) if *f == 0.0 => Number::Float(OrderedFloat(0f64)),
|
||||
Number::Float(f) => Number::Float(OrderedFloat(f.signum())),
|
||||
_ => {
|
||||
if self.is_positive() {
|
||||
Number::Fixnum(Fixnum::build_with(1))
|
||||
if self.is_zero() {
|
||||
Number::Fixnum(Fixnum::build_with(0))
|
||||
} else {
|
||||
Number::Fixnum(Fixnum::build_with(1))
|
||||
}
|
||||
} else if self.is_negative() {
|
||||
Number::Fixnum(Fixnum::build_with(-1))
|
||||
} else {
|
||||
@@ -770,39 +761,36 @@ impl Number {
|
||||
#[inline]
|
||||
pub(crate) fn is_positive(&self) -> bool {
|
||||
match self {
|
||||
&Number::Fixnum(n) => n.get_num() > 0,
|
||||
&Number::Integer(ref n) => &**n > &Integer::from(0),
|
||||
&Number::Float(f) => f.is_sign_positive(),
|
||||
&Number::Rational(ref r) => &**r > &Rational::from(0),
|
||||
Number::Fixnum(n) => n.get_num() > 0,
|
||||
Number::Integer(ref n) => n.is_positive(),
|
||||
Number::Float(f) => f.is_sign_positive(),
|
||||
Number::Rational(ref r) => r.is_positive(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_negative(&self) -> bool {
|
||||
match self {
|
||||
&Number::Fixnum(n) => n.get_num() < 0,
|
||||
&Number::Integer(ref n) => &**n < &Integer::from(0),
|
||||
&Number::Float(OrderedFloat(f)) => f.is_sign_negative() && OrderedFloat(f) != -0f64,
|
||||
&Number::Rational(ref r) => &**r < &Rational::from(0),
|
||||
Number::Fixnum(n) => n.get_num() < 0,
|
||||
Number::Integer(ref n) => n.is_negative(),
|
||||
&Number::Float(OrderedFloat(f)) => f.is_sign_negative() && f != -0f64,
|
||||
Number::Rational(ref r) => r.is_negative(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_zero(&self) -> bool {
|
||||
match self {
|
||||
&Number::Fixnum(n) => n.get_num() == 0,
|
||||
&Number::Integer(ref n) => &**n == &Integer::from(0),
|
||||
&Number::Float(f) => f == OrderedFloat(0f64) || f == OrderedFloat(-0f64),
|
||||
&Number::Rational(ref r) => &**r == &Rational::from(0),
|
||||
Number::Fixnum(n) => n.get_num() == 0,
|
||||
Number::Integer(ref n) => n.is_zero(),
|
||||
&Number::Float(OrderedFloat(f)) => f == 0.0 || f == -0.0,
|
||||
Number::Rational(ref r) => r.is_zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_integer(&self) -> bool {
|
||||
match self {
|
||||
Number::Fixnum(_) | Number::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, Number::Fixnum(_) | Number::Integer(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -892,7 +880,7 @@ impl ClauseIndexInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub(crate) struct PredicateInfo {
|
||||
pub(crate) is_extensible: bool,
|
||||
pub(crate) is_discontiguous: bool,
|
||||
@@ -901,19 +889,6 @@ pub(crate) struct PredicateInfo {
|
||||
pub(crate) has_clauses: bool,
|
||||
}
|
||||
|
||||
impl Default for PredicateInfo {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
PredicateInfo {
|
||||
is_extensible: false,
|
||||
is_discontiguous: false,
|
||||
is_dynamic: false,
|
||||
is_multifile: false,
|
||||
has_clauses: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateInfo {
|
||||
#[inline]
|
||||
pub(crate) fn compile_incrementally(&self) -> bool {
|
||||
@@ -923,8 +898,10 @@ impl PredicateInfo {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn must_retract_local_clauses(&self, is_cross_module_clause: bool) -> bool {
|
||||
self.is_extensible && self.has_clauses && !self.is_discontiguous &&
|
||||
!(self.is_multifile && is_cross_module_clause)
|
||||
self.is_extensible
|
||||
&& self.has_clauses
|
||||
&& !self.is_discontiguous
|
||||
&& !(self.is_multifile && is_cross_module_clause)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,7 +947,7 @@ impl LocalPredicateSkeleton {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn add_retracted_dynamic_clause_info(&mut self, clause_info: ClauseIndexInfo) {
|
||||
debug_assert_eq!(self.is_dynamic, true);
|
||||
debug_assert!(self.is_dynamic);
|
||||
|
||||
if self.retracted_dynamic_clauses.is_none() {
|
||||
self.retracted_dynamic_clauses = Some(vec![]);
|
||||
@@ -1013,19 +990,17 @@ impl PredicateSkeleton {
|
||||
&mut self,
|
||||
clause_clause_loc: usize,
|
||||
) -> Option<usize> {
|
||||
let search_result = self.core.clause_clause_locs
|
||||
.make_contiguous()[0..self.core.clause_assert_margin]
|
||||
.binary_search_by(|loc| clause_clause_loc.cmp(&loc));
|
||||
let search_result = self.core.clause_clause_locs.make_contiguous()
|
||||
[0..self.core.clause_assert_margin]
|
||||
.binary_search_by(|loc| clause_clause_loc.cmp(loc));
|
||||
|
||||
match search_result {
|
||||
Ok(loc) => Some(loc),
|
||||
Err(_) => {
|
||||
self.core.clause_clause_locs
|
||||
.make_contiguous()[self.core.clause_assert_margin..]
|
||||
.binary_search_by(|loc| loc.cmp(&clause_clause_loc))
|
||||
.map(|loc| loc + self.core.clause_assert_margin)
|
||||
.ok()
|
||||
}
|
||||
Err(_) => self.core.clause_clause_locs.make_contiguous()
|
||||
[self.core.clause_assert_margin..]
|
||||
.binary_search_by(|loc| loc.cmp(&clause_clause_loc))
|
||||
.map(|loc| loc + self.core.clause_assert_margin)
|
||||
.ok(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1127
src/heap_iter.rs
1127
src/heap_iter.rs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
57
src/http.rs
57
src/http.rs
@@ -1,54 +1,23 @@
|
||||
use std::sync::{Arc, Mutex, Condvar};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use http_body_util::Full;
|
||||
use bytes::Bytes;
|
||||
use hyper::service::Service;
|
||||
use hyper::{body::Incoming as IncomingBody, Request, Response};
|
||||
use std::io::BufRead;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use warp::http;
|
||||
|
||||
pub struct HttpListener {
|
||||
pub incoming: std::sync::mpsc::Receiver<HttpRequest>
|
||||
pub incoming: std::sync::mpsc::Receiver<HttpRequest>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HttpRequest {
|
||||
pub request: Request<IncomingBody>,
|
||||
pub request_data: HttpRequestData,
|
||||
pub response: HttpResponse,
|
||||
}
|
||||
|
||||
pub type HttpResponse = Arc<(Mutex<bool>, Mutex<Option<Response<Full<Bytes>>>>, Condvar)>;
|
||||
pub type HttpResponse = Arc<(Mutex<bool>, Mutex<Option<warp::reply::Response>>, Condvar)>;
|
||||
|
||||
pub struct HttpService {
|
||||
pub tx: std::sync::mpsc::SyncSender<HttpRequest>,
|
||||
}
|
||||
|
||||
impl Service<Request<IncomingBody>> for HttpService {
|
||||
type Response = Response<Full<Bytes>>;
|
||||
type Error = hyper::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
|
||||
|
||||
fn call(&mut self, req: Request<IncomingBody>) -> Self::Future {
|
||||
// new connection!
|
||||
// we send the Request info to Prolog
|
||||
let response = Arc::new((Mutex::new(false), Mutex::new(None), Condvar::new()));
|
||||
let http_request = HttpRequest { request: req, response: Arc::clone(&response) };
|
||||
self.tx.send(http_request).unwrap();
|
||||
|
||||
// we wait for the Response info from Prolog
|
||||
{
|
||||
let (ready, _response, cvar) = &*response;
|
||||
let mut ready = ready.lock().unwrap();
|
||||
while !*ready {
|
||||
ready = cvar.wait(ready).unwrap();
|
||||
}
|
||||
}
|
||||
{
|
||||
let (_, response, _) = &*response;
|
||||
let response = response.lock().unwrap().take();
|
||||
let res = response.expect("Data race error in HTTP Server");
|
||||
Box::pin(async move {
|
||||
Ok(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
pub struct HttpRequestData {
|
||||
pub method: http::Method,
|
||||
pub headers: http::HeaderMap,
|
||||
pub path: String,
|
||||
pub query: String,
|
||||
pub body: Box<dyn BufRead + Send>,
|
||||
}
|
||||
|
||||
257
src/indexing.rs
257
src/indexing.rs
@@ -150,17 +150,20 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
let third_level_index = if self.append_or_prepend.is_append() {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(external),
|
||||
IndexedChoiceInstruction::Trust(index)
|
||||
].into()
|
||||
IndexedChoiceInstruction::Trust(index),
|
||||
]
|
||||
.into()
|
||||
} else {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(index),
|
||||
IndexedChoiceInstruction::Trust(external)
|
||||
].into()
|
||||
IndexedChoiceInstruction::Trust(external),
|
||||
]
|
||||
.into()
|
||||
};
|
||||
|
||||
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::SwitchOnConstant(ref mut constants)) => {
|
||||
@@ -188,7 +191,8 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
};
|
||||
|
||||
let indexing_code_len = self.indexing_code.len();
|
||||
self.indexing_code.push(IndexingLine::DynamicIndexedChoice(third_level_index));
|
||||
self.indexing_code
|
||||
.push(IndexingLine::DynamicIndexedChoice(third_level_index));
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref mut constants)) => {
|
||||
@@ -275,10 +279,8 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
);
|
||||
}
|
||||
None | Some(IndexingCodePtr::Fail) => {
|
||||
constants.insert(
|
||||
overlapping_constant,
|
||||
IndexingCodePtr::External(index),
|
||||
);
|
||||
constants
|
||||
.insert(overlapping_constant, IndexingCodePtr::External(index));
|
||||
}
|
||||
Some(IndexingCodePtr::DynamicExternal(o)) => {
|
||||
self.add_dynamic_indexed_choice_for_constant(
|
||||
@@ -345,16 +347,10 @@ 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);
|
||||
@@ -432,17 +428,20 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
let third_level_index = if self.append_or_prepend.is_append() {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(external),
|
||||
IndexedChoiceInstruction::Trust(index)
|
||||
].into()
|
||||
IndexedChoiceInstruction::Trust(index),
|
||||
]
|
||||
.into()
|
||||
} else {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(index),
|
||||
IndexedChoiceInstruction::Trust(external)
|
||||
].into()
|
||||
IndexedChoiceInstruction::Trust(external),
|
||||
]
|
||||
.into()
|
||||
};
|
||||
|
||||
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)) => {
|
||||
@@ -470,7 +469,8 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
};
|
||||
|
||||
let indexing_code_len = self.indexing_code.len();
|
||||
self.indexing_code.push(IndexingLine::DynamicIndexedChoice(third_level_index));
|
||||
self.indexing_code
|
||||
.push(IndexingLine::DynamicIndexedChoice(third_level_index));
|
||||
|
||||
match &mut self.indexing_code[self.offset] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
|
||||
@@ -584,13 +584,15 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
let third_level_index = if self.append_or_prepend.is_append() {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(o),
|
||||
IndexedChoiceInstruction::Trust(index)
|
||||
].into()
|
||||
IndexedChoiceInstruction::Trust(index),
|
||||
]
|
||||
.into()
|
||||
} else {
|
||||
vec![
|
||||
IndexedChoiceInstruction::Try(index),
|
||||
IndexedChoiceInstruction::Trust(o)
|
||||
].into()
|
||||
IndexedChoiceInstruction::Trust(o),
|
||||
]
|
||||
.into()
|
||||
};
|
||||
|
||||
self.indexing_code
|
||||
@@ -613,7 +615,7 @@ 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.
|
||||
new_clause_loc: usize, // the absolute location of the new clause in the code vector.
|
||||
append_or_prepend: AppendOrPrepend,
|
||||
) {
|
||||
let opt_arg_index_key = match append_or_prepend {
|
||||
@@ -636,11 +638,7 @@ pub(crate) fn merge_clause_index(
|
||||
for overlapping_constant in overlapping_constants {
|
||||
merging_ptr.offset = 0;
|
||||
|
||||
merging_ptr.index_overlapping_constant(
|
||||
*constant,
|
||||
*overlapping_constant,
|
||||
offset,
|
||||
);
|
||||
merging_ptr.index_overlapping_constant(*constant, *overlapping_constant, offset);
|
||||
}
|
||||
}
|
||||
OptArgIndexKey::Structure(_, index_loc, name, arity) => {
|
||||
@@ -667,7 +665,7 @@ pub(crate) fn merge_clause_index(
|
||||
pub(crate) fn remove_constant_indices(
|
||||
constant: Literal,
|
||||
overlapping_constants: &[Literal],
|
||||
indexing_code: &mut Vec<IndexingLine>,
|
||||
indexing_code: &mut [IndexingLine],
|
||||
offset: usize,
|
||||
) {
|
||||
let mut index = 0;
|
||||
@@ -813,7 +811,7 @@ pub(crate) fn remove_constant_indices(
|
||||
pub(crate) fn remove_structure_index(
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
indexing_code: &mut Vec<IndexingLine>,
|
||||
indexing_code: &mut [IndexingLine],
|
||||
offset: usize,
|
||||
) {
|
||||
let mut index = 0;
|
||||
@@ -845,10 +843,10 @@ pub(crate) fn remove_structure_index(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
|
||||
structures_index = index;
|
||||
|
||||
match structures.get(&(name.clone(), arity)).cloned() {
|
||||
match structures.get(&(name, arity)).cloned() {
|
||||
Some(IndexingCodePtr::DynamicExternal(_))
|
||||
| Some(IndexingCodePtr::External(_)) => {
|
||||
structures.remove(&(name.clone(), arity));
|
||||
structures.remove(&(name, arity));
|
||||
break;
|
||||
}
|
||||
Some(IndexingCodePtr::Internal(o)) => {
|
||||
@@ -879,7 +877,7 @@ pub(crate) fn remove_structure_index(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
|
||||
ref mut structures,
|
||||
)) => {
|
||||
structures.insert((name.clone(), arity), ext);
|
||||
structures.insert((name, arity), ext);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -910,7 +908,7 @@ pub(crate) fn remove_structure_index(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
|
||||
ref mut structures,
|
||||
)) => {
|
||||
structures.insert((name.clone(), arity), ext);
|
||||
structures.insert((name, arity), ext);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -950,7 +948,7 @@ pub(crate) fn remove_structure_index(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remove_list_index(indexing_code: &mut Vec<IndexingLine>, offset: usize) {
|
||||
pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usize) {
|
||||
let mut index = 0;
|
||||
|
||||
match &mut indexing_code[index] {
|
||||
@@ -1030,7 +1028,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut Vec<IndexingLine>, offset: u
|
||||
|
||||
pub(crate) fn remove_index(
|
||||
opt_arg_index_key: &OptArgIndexKey,
|
||||
indexing_code: &mut Vec<IndexingLine>,
|
||||
indexing_code: &mut [IndexingLine],
|
||||
clause_loc: usize,
|
||||
) {
|
||||
match opt_arg_index_key {
|
||||
@@ -1051,43 +1049,55 @@ pub(crate) fn remove_index(
|
||||
|
||||
#[inline]
|
||||
fn cap_choice_seq(prelude: &mut [IndexedChoiceInstruction]) {
|
||||
prelude.first_mut().map(|instr| {
|
||||
if let Some(instr) = prelude.first_mut() {
|
||||
*instr = IndexedChoiceInstruction::Try(instr.offset());
|
||||
});
|
||||
}
|
||||
|
||||
cap_choice_seq_with_trust(prelude);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) {
|
||||
prelude.last_mut().map(|instr| {
|
||||
if let IndexedChoiceInstruction::Retry(i) = instr {
|
||||
*instr = IndexedChoiceInstruction::Trust(*i);
|
||||
if let Some(instr) = prelude.last_mut() {
|
||||
match instr {
|
||||
IndexedChoiceInstruction::Retry(i) => {
|
||||
*instr = IndexedChoiceInstruction::Trust(*i);
|
||||
}
|
||||
IndexedChoiceInstruction::DefaultRetry(i) => {
|
||||
*instr = IndexedChoiceInstruction::DefaultTrust(*i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn uncap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) {
|
||||
prelude.last_mut().map(|instr| {
|
||||
if let IndexedChoiceInstruction::Trust(i) = instr {
|
||||
*instr = IndexedChoiceInstruction::Retry(*i);
|
||||
if let Some(instr) = prelude.last_mut() {
|
||||
match instr {
|
||||
IndexedChoiceInstruction::Trust(i) => {
|
||||
*instr = IndexedChoiceInstruction::Retry(*i);
|
||||
}
|
||||
IndexedChoiceInstruction::DefaultTrust(i) => {
|
||||
*instr = IndexedChoiceInstruction::DefaultRetry(*i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) {
|
||||
prelude.first_mut().map(|instr| {
|
||||
if let Some(instr) = prelude.first_mut() {
|
||||
if let IndexedChoiceInstruction::Try(i) = instr {
|
||||
*instr = IndexedChoiceInstruction::Retry(*i);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn constant_key_alternatives(
|
||||
constant: Literal,
|
||||
atom_tbl: &mut AtomTable,
|
||||
atom_tbl: &AtomTable,
|
||||
// arena: &mut Arena,
|
||||
) -> Vec<Literal> {
|
||||
let mut constants = vec![];
|
||||
@@ -1099,7 +1109,7 @@ pub(crate) fn constant_key_alternatives(
|
||||
}
|
||||
}
|
||||
Literal::Char(c) => {
|
||||
let atom = atom_tbl.build_with(&c.to_string());
|
||||
let atom = AtomTable::build_with(atom_tbl, &c.to_string());
|
||||
constants.push(Literal::Atom(atom));
|
||||
}
|
||||
/*
|
||||
@@ -1116,10 +1126,13 @@ pub(crate) fn constant_key_alternatives(
|
||||
}
|
||||
*/
|
||||
Literal::Integer(ref n) => {
|
||||
if let Some(n) = n.to_isize() {
|
||||
Fixnum::build_with_checked(n as i64).map(|n| {
|
||||
constants.push(Literal::Fixnum(n));
|
||||
}).unwrap();
|
||||
let result = (&**n).try_into();
|
||||
if let Ok(value) = result {
|
||||
Fixnum::build_with_checked(value)
|
||||
.map(|n| {
|
||||
constants.push(Literal::Fixnum(n));
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -1147,11 +1160,19 @@ pub(crate) trait Indexer {
|
||||
|
||||
fn new() -> Self;
|
||||
|
||||
fn constants(&mut self) -> &mut IndexMap<Literal, VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>;
|
||||
fn constants(
|
||||
&mut self,
|
||||
) -> &mut IndexMap<Literal, VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>;
|
||||
fn lists(&mut self) -> &mut VecDeque<Self::ThirdLevelIndex>;
|
||||
fn structures(&mut self) -> &mut IndexMap<(Atom, usize), VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>;
|
||||
fn structures(
|
||||
&mut self,
|
||||
) -> &mut IndexMap<(Atom, usize), VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>;
|
||||
|
||||
fn compute_index(is_initial_index: bool, index: usize) -> Self::ThirdLevelIndex;
|
||||
fn compute_index(
|
||||
is_initial_index: bool,
|
||||
index: usize,
|
||||
non_counted_bt: bool,
|
||||
) -> Self::ThirdLevelIndex;
|
||||
|
||||
fn second_level_index<IndexKey: Eq + Hash>(
|
||||
indices: IndexMap<IndexKey, VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>,
|
||||
@@ -1187,7 +1208,9 @@ impl Indexer for StaticCodeIndices {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn constants(&mut self) -> &mut IndexMap<Literal, VecDeque<IndexedChoiceInstruction>, FxBuildHasher> {
|
||||
fn constants(
|
||||
&mut self,
|
||||
) -> &mut IndexMap<Literal, VecDeque<IndexedChoiceInstruction>, FxBuildHasher> {
|
||||
&mut self.constants
|
||||
}
|
||||
|
||||
@@ -1197,13 +1220,21 @@ impl Indexer for StaticCodeIndices {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn structures(&mut self) -> &mut IndexMap<(Atom, usize), VecDeque<IndexedChoiceInstruction>, FxBuildHasher> {
|
||||
fn structures(
|
||||
&mut self,
|
||||
) -> &mut IndexMap<(Atom, usize), VecDeque<IndexedChoiceInstruction>, FxBuildHasher> {
|
||||
&mut self.structures
|
||||
}
|
||||
|
||||
fn compute_index(is_initial_index: bool, index: usize) -> IndexedChoiceInstruction {
|
||||
fn compute_index(
|
||||
is_initial_index: bool,
|
||||
index: usize,
|
||||
non_counted_bt: bool,
|
||||
) -> IndexedChoiceInstruction {
|
||||
if is_initial_index {
|
||||
IndexedChoiceInstruction::Try(index + 1)
|
||||
} else if non_counted_bt {
|
||||
IndexedChoiceInstruction::DefaultRetry(index + 1)
|
||||
} else {
|
||||
IndexedChoiceInstruction::Retry(index + 1)
|
||||
}
|
||||
@@ -1220,10 +1251,8 @@ impl Indexer for StaticCodeIndices {
|
||||
index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1));
|
||||
cap_choice_seq_with_trust(code.make_contiguous());
|
||||
prelude.push_back(IndexingLine::from(code));
|
||||
} else {
|
||||
code.front().map(|i| {
|
||||
index_locs.insert(key, IndexingCodePtr::External(i.offset()));
|
||||
});
|
||||
} else if let Some(i) = code.front() {
|
||||
index_locs.insert(key, IndexingCodePtr::External(i.offset()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1231,7 +1260,9 @@ impl Indexer for StaticCodeIndices {
|
||||
}
|
||||
|
||||
fn switch_on<IndexKey: Eq + Hash>(
|
||||
mut instr_fn: impl FnMut(IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher>) -> IndexingInstruction,
|
||||
mut instr_fn: impl FnMut(
|
||||
IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher>,
|
||||
) -> IndexingInstruction,
|
||||
index: &mut IndexMap<IndexKey, VecDeque<IndexedChoiceInstruction>, FxBuildHasher>,
|
||||
prelude: &mut VecDeque<IndexingLine>,
|
||||
) -> IndexingCodePtr {
|
||||
@@ -1258,7 +1289,7 @@ impl Indexer for StaticCodeIndices {
|
||||
) -> IndexingCodePtr {
|
||||
if lists.len() > 1 {
|
||||
cap_choice_seq_with_trust(lists.make_contiguous());
|
||||
let lists = mem::replace(lists, VecDeque::new());
|
||||
let lists = std::mem::take(lists);
|
||||
prelude.push_back(IndexingLine::from(lists));
|
||||
|
||||
IndexingCodePtr::Internal(1)
|
||||
@@ -1318,7 +1349,7 @@ impl Indexer for DynamicCodeIndices {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_index(_: bool, index: usize) -> usize {
|
||||
fn compute_index(_: bool, index: usize, _: bool) -> usize {
|
||||
index + 1
|
||||
}
|
||||
|
||||
@@ -1331,11 +1362,11 @@ impl Indexer for DynamicCodeIndices {
|
||||
for (key, code) in indices.into_iter() {
|
||||
if code.len() > 1 {
|
||||
index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1));
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(code.into_iter().collect()));
|
||||
} else {
|
||||
code.front().map(|i| {
|
||||
index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
|
||||
});
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(
|
||||
code.into_iter().collect(),
|
||||
));
|
||||
} else if let Some(i) = code.front() {
|
||||
index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1343,7 +1374,9 @@ impl Indexer for DynamicCodeIndices {
|
||||
}
|
||||
|
||||
fn switch_on<IndexKey: Eq + Hash>(
|
||||
mut instr_fn: impl FnMut(IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher>) -> IndexingInstruction,
|
||||
mut instr_fn: impl FnMut(
|
||||
IndexMap<IndexKey, IndexingCodePtr, FxBuildHasher>,
|
||||
) -> IndexingInstruction,
|
||||
index: &mut IndexMap<IndexKey, VecDeque<usize>, FxBuildHasher>,
|
||||
prelude: &mut VecDeque<IndexingLine>,
|
||||
) -> IndexingCodePtr {
|
||||
@@ -1369,8 +1402,10 @@ impl Indexer for DynamicCodeIndices {
|
||||
prelude: &mut VecDeque<IndexingLine>,
|
||||
) -> IndexingCodePtr {
|
||||
if lists.len() > 1 {
|
||||
let lists = mem::replace(lists, VecDeque::new());
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(lists.into_iter().collect()));
|
||||
let lists = std::mem::take(lists);
|
||||
prelude.push_back(IndexingLine::DynamicIndexedChoice(
|
||||
lists.into_iter().collect(),
|
||||
));
|
||||
IndexingCodePtr::Internal(1)
|
||||
} else {
|
||||
lists
|
||||
@@ -1400,43 +1435,45 @@ impl Indexer for DynamicCodeIndices {
|
||||
pub(crate) struct CodeOffsets<I: Indexer> {
|
||||
indices: I,
|
||||
optimal_index: usize,
|
||||
non_counted_bt: bool,
|
||||
}
|
||||
|
||||
impl<I: Indexer> CodeOffsets<I> {
|
||||
pub(crate) fn new(indices: I, optimal_index: usize) -> Self {
|
||||
pub(crate) fn new(indices: I, optimal_index: usize, non_counted_bt: bool) -> Self {
|
||||
CodeOffsets {
|
||||
indices,
|
||||
optimal_index,
|
||||
non_counted_bt,
|
||||
}
|
||||
}
|
||||
|
||||
fn index_list(&mut self, index: usize) {
|
||||
let is_initial_index = self.indices.lists().is_empty();
|
||||
let index = I::compute_index(is_initial_index, index);
|
||||
let index = I::compute_index(is_initial_index, index, self.non_counted_bt);
|
||||
self.indices.lists().push_back(index);
|
||||
}
|
||||
|
||||
fn index_constant(
|
||||
&mut self,
|
||||
atom_tbl: &mut AtomTable,
|
||||
atom_tbl: &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(VecDeque::new());
|
||||
let code = self.indices.constants().entry(constant).or_default();
|
||||
|
||||
let is_initial_index = code.is_empty();
|
||||
code.push_back(I::compute_index(is_initial_index, index));
|
||||
code.push_back(I::compute_index(
|
||||
is_initial_index,
|
||||
index,
|
||||
self.non_counted_bt,
|
||||
));
|
||||
|
||||
for constant in &overlapping_constants {
|
||||
let code = self
|
||||
.indices
|
||||
.constants()
|
||||
.entry(*constant)
|
||||
.or_insert(VecDeque::new());
|
||||
let code = self.indices.constants().entry(*constant).or_default();
|
||||
|
||||
let is_initial_index = code.is_empty();
|
||||
let index = I::compute_index(is_initial_index, index);
|
||||
let index = I::compute_index(is_initial_index, index, self.non_counted_bt);
|
||||
|
||||
code.push_back(index);
|
||||
}
|
||||
@@ -1445,16 +1482,16 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
}
|
||||
|
||||
fn index_structure(&mut self, name: Atom, arity: usize, index: usize) -> usize {
|
||||
let code = self
|
||||
.indices
|
||||
.structures()
|
||||
.entry((name.clone(), arity))
|
||||
.or_insert(VecDeque::new());
|
||||
let code = self.indices.structures().entry((name, arity)).or_default();
|
||||
|
||||
let code_len = code.len();
|
||||
let is_initial_index = code.is_empty();
|
||||
|
||||
code.push_back(I::compute_index(is_initial_index, index));
|
||||
code.push_back(I::compute_index(
|
||||
is_initial_index,
|
||||
index,
|
||||
self.non_counted_bt,
|
||||
));
|
||||
code_len
|
||||
}
|
||||
|
||||
@@ -1463,7 +1500,7 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
optimal_arg: &Term,
|
||||
index: usize,
|
||||
clause_index_info: &mut ClauseIndexInfo,
|
||||
atom_tbl: &mut AtomTable,
|
||||
atom_tbl: &AtomTable,
|
||||
) {
|
||||
match optimal_arg {
|
||||
&Term::Clause(_, atom!("."), ref terms) if terms.len() == 2 => {
|
||||
@@ -1476,7 +1513,7 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
}
|
||||
&Term::Clause(_, name, ref terms) => {
|
||||
clause_index_info.opt_arg_index_key =
|
||||
OptArgIndexKey::Structure(self.optimal_index, 0, name.clone(), terms.len());
|
||||
OptArgIndexKey::Structure(self.optimal_index, 0, name, terms.len());
|
||||
|
||||
self.index_structure(name, terms.len(), index);
|
||||
}
|
||||
@@ -1528,20 +1565,14 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
&mut prelude,
|
||||
);
|
||||
|
||||
match &mut str_loc {
|
||||
IndexingCodePtr::Internal(ref mut i) => {
|
||||
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
if let IndexingCodePtr::Internal(ref mut i) = &mut str_loc {
|
||||
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
|
||||
}
|
||||
|
||||
match &mut lst_loc {
|
||||
IndexingCodePtr::Internal(ref mut i) => {
|
||||
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
|
||||
*i += emitted_switch_on_structure as usize; // str_loc.is_internal() as usize;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
if let IndexingCodePtr::Internal(ref mut i) = &mut lst_loc {
|
||||
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
|
||||
*i += emitted_switch_on_structure as usize; // str_loc.is_internal() as usize;
|
||||
}
|
||||
|
||||
let var_offset = 1 + skip_stub_try_me_else as usize;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::collections::VecDeque;
|
||||
use std::iter::*;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum TermRef<'a> {
|
||||
AnonVar(Level),
|
||||
@@ -35,6 +36,7 @@ impl<'a> TermRef<'a> {
|
||||
}
|
||||
*/
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum TermIterState<'a> {
|
||||
AnonVar(Level),
|
||||
@@ -62,9 +64,7 @@ impl<'a> TermIterState<'a> {
|
||||
Term::PartialString(cell, string_buf, tail) => {
|
||||
TermIterState::InitialPartialString(lvl, cell, string_buf, tail)
|
||||
}
|
||||
Term::CompleteString(cell, atom) => {
|
||||
TermIterState::CompleteString(lvl, cell, *atom)
|
||||
}
|
||||
Term::CompleteString(cell, atom) => TermIterState::CompleteString(lvl, cell, *atom),
|
||||
Term::Var(cell, var_ptr) => TermIterState::Var(lvl, cell, var_ptr.clone()),
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,8 @@ pub(crate) struct QueryIterator<'a> {
|
||||
|
||||
impl<'a> QueryIterator<'a> {
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_stack.push(TermIterState::subterm_to_state(lvl, term));
|
||||
self.state_stack
|
||||
.push(TermIterState::subterm_to_state(lvl, term));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -94,19 +95,16 @@ impl<'a> QueryIterator<'a> {
|
||||
|
||||
fn from_term(term: &'a Term) -> Self {
|
||||
let state = match term {
|
||||
Term::AnonVar | Term::Cons(..) | Term::Literal(..) |
|
||||
Term::PartialString(..) | Term::CompleteString(..) => {
|
||||
Term::AnonVar
|
||||
| Term::Cons(..)
|
||||
| Term::Literal(..)
|
||||
| Term::PartialString(..)
|
||||
| Term::CompleteString(..) => {
|
||||
return QueryIterator {
|
||||
state_stack: vec![],
|
||||
}
|
||||
}
|
||||
Term::Clause(r, name, terms) => TermIterState::Clause(
|
||||
Level::Root,
|
||||
0,
|
||||
r,
|
||||
*name,
|
||||
terms,
|
||||
),
|
||||
Term::Clause(r, name, terms) => TermIterState::Clause(Level::Root, 0, r, *name, terms),
|
||||
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Root, cell, var_ptr.clone()),
|
||||
};
|
||||
|
||||
@@ -117,19 +115,22 @@ impl<'a> QueryIterator<'a> {
|
||||
|
||||
fn extend_state(&mut self, lvl: Level, term: &'a QueryTerm) {
|
||||
match term {
|
||||
&QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => {
|
||||
self.state_stack.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms));
|
||||
QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms));
|
||||
}
|
||||
&QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
|
||||
self.state_stack.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms));
|
||||
}
|
||||
_ => {
|
||||
QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(term: &'a QueryTerm) -> Self {
|
||||
let mut iter = QueryIterator { state_stack: vec![] };
|
||||
let mut iter = QueryIterator {
|
||||
state_stack: vec![],
|
||||
};
|
||||
iter.extend_state(Level::Root, term);
|
||||
iter
|
||||
}
|
||||
@@ -170,13 +171,15 @@ impl<'a> Iterator for QueryIterator<'a> {
|
||||
}
|
||||
}
|
||||
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
||||
self.state_stack.push(TermIterState::FinalCons(lvl, cell, head, tail));
|
||||
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));
|
||||
self.state_stack
|
||||
.push(TermIterState::FinalPartialString(lvl, cell, string, tail));
|
||||
self.push_subterm(lvl.child_level(), tail);
|
||||
}
|
||||
TermIterState::FinalPartialString(lvl, cell, atom, tail) => {
|
||||
@@ -213,7 +216,7 @@ impl<'a> FactIterator<'a> {
|
||||
.push_back(TermIterState::subterm_to_state(lvl, term));
|
||||
}
|
||||
|
||||
pub(crate) fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
|
||||
pub(crate) fn from_rule_head_clause(terms: &'a [Term]) -> Self {
|
||||
let state_queue = terms
|
||||
.iter()
|
||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||
@@ -248,11 +251,7 @@ impl<'a> FactIterator<'a> {
|
||||
)]
|
||||
}
|
||||
Term::CompleteString(cell, atom) => {
|
||||
vec![TermIterState::CompleteString(
|
||||
Level::Root,
|
||||
cell,
|
||||
*atom,
|
||||
)]
|
||||
vec![TermIterState::CompleteString(Level::Root, cell, *atom)]
|
||||
}
|
||||
Term::Literal(cell, constant) => {
|
||||
vec![TermIterState::Literal(Level::Root, cell, constant)]
|
||||
@@ -315,11 +314,14 @@ impl<'a> Iterator for FactIterator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn post_order_iter<'a>(term: &'a Term) -> QueryIterator<'a> {
|
||||
pub(crate) fn post_order_iter(term: &'_ Term) -> QueryIterator {
|
||||
QueryIterator::from_term(term)
|
||||
}
|
||||
|
||||
pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: RootIterationPolicy) -> FactIterator<'a> {
|
||||
pub(crate) fn breadth_first_iter(
|
||||
term: &'_ Term,
|
||||
iterable_root: RootIterationPolicy,
|
||||
) -> FactIterator {
|
||||
FactIterator::new(term, iterable_root)
|
||||
}
|
||||
|
||||
@@ -343,7 +345,7 @@ pub(crate) struct ClauseIterator<'a> {
|
||||
remaining_chunks_on_stack: usize,
|
||||
}
|
||||
|
||||
fn state_from_chunked_terms<'a>(chunk_vec: &'a VecDeque<ChunkedTerms>) -> ClauseIteratorState<'a> {
|
||||
fn state_from_chunked_terms(chunk_vec: &'_ VecDeque<ChunkedTerms>) -> ClauseIteratorState {
|
||||
if chunk_vec.len() == 1 {
|
||||
if let Some(ChunkedTerms::Branch(ref branches)) = chunk_vec.front() {
|
||||
return ClauseIteratorState::RemainingBranches(branches, 0);
|
||||
@@ -356,18 +358,14 @@ fn state_from_chunked_terms<'a>(chunk_vec: &'a VecDeque<ChunkedTerms>) -> Clause
|
||||
impl<'a> ClauseIterator<'a> {
|
||||
pub fn new(clauses: &'a ChunkedTermVec) -> Self {
|
||||
match state_from_chunked_terms(&clauses.chunk_vec) {
|
||||
state @ ClauseIteratorState::RemainingBranches(..) => {
|
||||
Self {
|
||||
state_stack: vec![state],
|
||||
remaining_chunks_on_stack: 0,
|
||||
}
|
||||
}
|
||||
state @ ClauseIteratorState::RemainingChunks(..) => {
|
||||
Self {
|
||||
state_stack: vec![state],
|
||||
remaining_chunks_on_stack: 1,
|
||||
}
|
||||
}
|
||||
state @ ClauseIteratorState::RemainingBranches(..) => Self {
|
||||
state_stack: vec![state],
|
||||
remaining_chunks_on_stack: 0,
|
||||
},
|
||||
state @ ClauseIteratorState::RemainingChunks(..) => Self {
|
||||
state_stack: vec![state],
|
||||
remaining_chunks_on_stack: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,14 +401,16 @@ impl<'a> Iterator for ClauseIterator<'a> {
|
||||
match state {
|
||||
ClauseIteratorState::RemainingChunks(chunks, focus) if focus < chunks.len() => {
|
||||
if focus + 1 < chunks.len() {
|
||||
self.state_stack.push(ClauseIteratorState::RemainingChunks(chunks, focus + 1));
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingChunks(chunks, focus + 1));
|
||||
} else {
|
||||
self.remaining_chunks_on_stack -= 1;
|
||||
}
|
||||
|
||||
match &chunks[focus] {
|
||||
ChunkedTerms::Branch(branches) => {
|
||||
self.state_stack.push(ClauseIteratorState::RemainingBranches(branches, 0));
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingBranches(branches, 0));
|
||||
}
|
||||
ChunkedTerms::Chunk(chunk) => {
|
||||
return Some(ClauseItem::Chunk(chunk));
|
||||
@@ -420,8 +420,11 @@ impl<'a> Iterator for ClauseIterator<'a> {
|
||||
ClauseIteratorState::RemainingChunks(chunks, focus) => {
|
||||
debug_assert_eq!(chunks.len(), focus);
|
||||
}
|
||||
ClauseIteratorState::RemainingBranches(branches, focus) if focus < branches.len() => {
|
||||
self.state_stack.push(ClauseIteratorState::RemainingBranches(&branches, focus + 1));
|
||||
ClauseIteratorState::RemainingBranches(branches, focus)
|
||||
if focus < branches.len() =>
|
||||
{
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingBranches(branches, focus + 1));
|
||||
let state = state_from_chunked_terms(&branches[focus]);
|
||||
|
||||
if let ClauseIteratorState::RemainingChunks(..) = &state {
|
||||
|
||||
22
src/lib.rs
22
src/lib.rs
@@ -2,6 +2,9 @@
|
||||
|
||||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate maplit;
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
@@ -17,13 +20,13 @@ pub mod codegen;
|
||||
mod debray_allocator;
|
||||
#[cfg(feature = "ffi")]
|
||||
mod ffi;
|
||||
mod variable_records;
|
||||
mod forms;
|
||||
mod heap_iter;
|
||||
pub mod heap_print;
|
||||
#[cfg(feature = "http")]
|
||||
mod http;
|
||||
mod indexing;
|
||||
mod variable_records;
|
||||
#[macro_use]
|
||||
pub mod instructions {
|
||||
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
|
||||
@@ -38,3 +41,20 @@ mod targets;
|
||||
pub mod types;
|
||||
|
||||
use instructions::instr;
|
||||
|
||||
mod rcu;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[wasm_bindgen]
|
||||
pub fn eval_code(s: &str) -> String {
|
||||
use machine::mock_wam::*;
|
||||
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let mut wam = Machine::with_test_streams();
|
||||
let bytes = wam.test_load_string(s);
|
||||
String::from_utf8_lossy(&bytes).to_string()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
:- module(atts, [op(1199, fx, attribute),
|
||||
call_residue_vars/2,
|
||||
term_attributed_variables/2]).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(terms)).
|
||||
|
||||
/* represent the list of attributes belonging to a variable,
|
||||
@@ -110,12 +110,5 @@ user:goal_expansion(Term, M:get_atts(Var, Attr)) :-
|
||||
nonvar(Term),
|
||||
Term = get_atts(Var, M, Attr).
|
||||
|
||||
:- meta_predicate call_residue_vars(0, ?).
|
||||
|
||||
call_residue_vars(Goal, Vars) :-
|
||||
'$get_attr_var_queue_delim'(B),
|
||||
call(Goal),
|
||||
'$get_attr_var_queue_beyond'(B, Vars).
|
||||
|
||||
term_attributed_variables(Term, Vars) :-
|
||||
'$term_attributed_variables'(Term, Vars).
|
||||
|
||||
@@ -124,7 +124,7 @@ call(_, _, _, _, _, _, _, _, _).
|
||||
% while others can be set with `set_prolog_flag/2`.
|
||||
%
|
||||
% The flags that Scryer Prolog support are:
|
||||
%
|
||||
%
|
||||
% * `max_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 1023. Read only.
|
||||
% * `bounded`: `true` if integer arithmethic is bounded between some min/max values. On Scryer is always set
|
||||
% to `false` since it supports unbounded integer arithmethic. Read only.
|
||||
@@ -143,6 +143,7 @@ call(_, _, _, _, _, _, _, _, _).
|
||||
% enabled) and `error` which throws an exception when a cylic term is created. Read and write.
|
||||
% * `unknown`: How undefined predicates are handled when called. Possible values are `error` (the default, an error is thrown),
|
||||
% `fail` (the call silently fails) and `warn` (the call fails and a warning about the undefined predicate is printed).
|
||||
% * `answer_write_options`: Additional write options used by the top level for writing answers.
|
||||
%
|
||||
current_prolog_flag(Flag, Value) :- Flag == max_arity, !, Value = 1023.
|
||||
current_prolog_flag(max_arity, 1023).
|
||||
@@ -160,6 +161,14 @@ current_prolog_flag(Flag, OccursCheckEnabled) :-
|
||||
Flag == occurs_check,
|
||||
!,
|
||||
'$is_sto_enabled'(OccursCheckEnabled).
|
||||
current_prolog_flag(occurs_check, OccursCheckEnabled) :-
|
||||
'$is_sto_enabled'(OccursCheckEnabled).
|
||||
current_prolog_flag(Flag, Value) :-
|
||||
Flag == answer_write_options,
|
||||
!,
|
||||
answer_write_options(Value).
|
||||
current_prolog_flag(answer_write_options, Value) :-
|
||||
answer_write_options(Value).
|
||||
current_prolog_flag(Flag, _) :-
|
||||
atom(Flag),
|
||||
throw(error(domain_error(prolog_flag, Flag), current_prolog_flag/2)). % 8.17.2.3 b
|
||||
@@ -167,10 +176,15 @@ current_prolog_flag(Flag, _) :-
|
||||
nonvar(Flag),
|
||||
throw(error(type_error(atom, Flag), current_prolog_flag/2)). % 8.17.2.3 a
|
||||
|
||||
answer_write_options(Value) :-
|
||||
( iso_ext:bb_get('$answer_write_options', Value) -> true
|
||||
; Value = []
|
||||
).
|
||||
|
||||
%% set_prolog_flag(Flag, Value).
|
||||
%
|
||||
% Sets the internal value of the flag. To see the list of flags supported by Scryer Prolog,
|
||||
% check `current_prolog_flag/2`. The flags that are read only will fail if you try to change their values
|
||||
% check `current_prolog_flag/2`. The flags that are read only will fail if you try to change their values
|
||||
set_prolog_flag(Flag, Value) :-
|
||||
(var(Flag) ; var(Value)),
|
||||
throw(error(instantiation_error, set_prolog_flag/2)). % 8.17.1.3 a, b
|
||||
@@ -207,14 +221,25 @@ set_prolog_flag(occurs_check, false) :-
|
||||
set_prolog_flag(occurs_check, error) :-
|
||||
!, '$set_sto_with_error_as_unify'.
|
||||
set_prolog_flag(double_quotes, Value) :-
|
||||
throw(error(domain_error(flag_value, double_quotes + Value),
|
||||
set_prolog_flag/2)). % 8.17.1.3 e
|
||||
flag_domain_error(double_quotes, Value).
|
||||
set_prolog_flag(answer_write_options, Options) :-
|
||||
!,
|
||||
catch(catch(builtins:parse_write_options(Options, _, set_prolog_flag/2),
|
||||
error(domain_error(_,_), _),
|
||||
throw(error(type_error(_,_), _))), % convert domain error to type error ....
|
||||
error(type_error(_,_), _), % ... to catch type and domain errors.
|
||||
flag_domain_error(answer_write_options, Options)),
|
||||
iso_ext:bb_put('$answer_write_options', Options).
|
||||
set_prolog_flag(Flag, _) :-
|
||||
atom(Flag),
|
||||
throw(error(domain_error(prolog_flag, Flag), set_prolog_flag/2)). % 8.17.1.3 d
|
||||
set_prolog_flag(Flag, _) :-
|
||||
throw(error(type_error(atom, Flag), set_prolog_flag/2)). % 8.17.1.3 c
|
||||
|
||||
flag_domain_error(Flag, Value) :-
|
||||
% domain error via 8.17.1.3 e: Value is inappropriate for Flag
|
||||
throw(error(domain_error(flag_value, Flag + Value), set_prolog_flag/2)).
|
||||
|
||||
% control operators.
|
||||
|
||||
%% fail.
|
||||
@@ -262,6 +287,9 @@ repeat :- repeat.
|
||||
%% ->(G1, G2)
|
||||
%
|
||||
% If-then and if-then-else constructs
|
||||
|
||||
:- non_counted_backtracking (->)/2.
|
||||
|
||||
G1 -> G2 :- control_entry_point((G1 -> G2)).
|
||||
|
||||
|
||||
@@ -275,6 +303,9 @@ staggered_if_then(G1, G2) :-
|
||||
%% ;(G1, G2)
|
||||
%
|
||||
% Disjunction (or)
|
||||
|
||||
:- non_counted_backtracking (;)/2.
|
||||
|
||||
G1 ; G2 :- control_entry_point((G1 ; G2)).
|
||||
|
||||
|
||||
@@ -297,6 +328,9 @@ staggered_sc(_, G) :- call(G).
|
||||
% to reason about the programs. Also restricts the ability to run the program with alternative execution strategies
|
||||
!.
|
||||
|
||||
:- non_counted_backtracking get_cp/1.
|
||||
get_cp(B) :- '$get_cp'(B).
|
||||
|
||||
:- non_counted_backtracking set_cp/1.
|
||||
|
||||
set_cp(B) :- '$set_cp'(B).
|
||||
@@ -304,6 +338,9 @@ set_cp(B) :- '$set_cp'(B).
|
||||
%% ,(G1, G2)
|
||||
%
|
||||
% Conjuction (and)
|
||||
|
||||
:- non_counted_backtracking (',')/2.
|
||||
|
||||
','(G1, G2) :- control_entry_point((G1, G2)).
|
||||
|
||||
|
||||
@@ -325,24 +362,21 @@ cont_list_goal(Conts, '$call'(builtins:dispatch_call_list(Conts))).
|
||||
|
||||
:- non_counted_backtracking dispatch_prep/3.
|
||||
|
||||
dispatch_prep(Gs, B, [Cont|Conts]) :-
|
||||
dispatch_prep(Gs, B, Conts) :-
|
||||
( callable(Gs) ->
|
||||
strip_module(Gs, M, Gs0),
|
||||
( nonvar(Gs0),
|
||||
dispatch_prep_(Gs0, B, [Cont|Conts]) ->
|
||||
dispatch_prep_(Gs0, B, Conts) ->
|
||||
true
|
||||
; Gs0 == ! ->
|
||||
Cont = '$call'(builtins:set_cp(B)),
|
||||
Conts = []
|
||||
Conts = ['$call'(builtins:set_cp(B))]
|
||||
; nonvar(Gs0),
|
||||
\+ callable(Gs0) ->
|
||||
throw(dispatch_prep_error)
|
||||
; Cont = Gs,
|
||||
Conts = []
|
||||
; Conts = [Gs]
|
||||
)
|
||||
; var(Gs) ->
|
||||
Cont = Gs,
|
||||
Conts = []
|
||||
Conts = [Gs]
|
||||
; throw(dispatch_prep_error)
|
||||
).
|
||||
|
||||
@@ -353,20 +387,32 @@ dispatch_prep_((G1, G2), B, [Cont|Conts]) :-
|
||||
dispatch_prep(G1, B, IConts1),
|
||||
cont_list_goal(IConts1, Cont),
|
||||
dispatch_prep(G2, B, Conts).
|
||||
dispatch_prep_((G1 ; G2), B, [Cont|Conts]) :-
|
||||
dispatch_prep(G1, B, IConts0),
|
||||
dispatch_prep_((G1 ; G2), B, Conts) :-
|
||||
( nonvar(G1) ->
|
||||
( G1 = (G11 -> G12) ->
|
||||
dispatch_prep(G11, B, IConts2),
|
||||
dispatch_prep(G12, B, IConts3),
|
||||
cont_list_goal(IConts2, Cont2),
|
||||
cont_list_goal(IConts3, Cont3),
|
||||
Cont0 = '$call'(builtins:staggered_if_then(Cont2, Cont3))
|
||||
; dispatch_prep(G1, B, IConts0),
|
||||
dispatch_prep(G2, B, IConts1),
|
||||
cont_list_goal(IConts0, Cont0)
|
||||
)
|
||||
; dispatch_prep(G1, B1, IConts0),
|
||||
cont_list_goal(IConts0, Cont0)
|
||||
),
|
||||
dispatch_prep(G2, B, IConts1),
|
||||
cont_list_goal(IConts0, Cont0),
|
||||
cont_list_goal(IConts1, Cont1),
|
||||
Cont = '$call'(builtins:staggered_sc(Cont0, Cont1)),
|
||||
Conts = [].
|
||||
dispatch_prep_((G1 -> G2), B, [Cont|Conts]) :-
|
||||
dispatch_prep(G1, B, IConts1),
|
||||
Conts = ['$call'(builtins:staggered_sc(Cont0, Cont1))].
|
||||
dispatch_prep_((G1 -> G2), B, Conts) :-
|
||||
dispatch_prep(G1, B1, IConts1),
|
||||
dispatch_prep(G2, B, IConts2),
|
||||
cont_list_goal(IConts1, Cont1),
|
||||
cont_list_goal(IConts2, Cont2),
|
||||
Cont = '$call'(builtins:staggered_if_then(Cont1, Cont2)),
|
||||
Conts = [].
|
||||
Conts = ['$call'(builtins:get_cp(B1)),
|
||||
'$call'(builtins:staggered_if_then(Cont1, Cont2))].
|
||||
|
||||
|
||||
:- non_counted_backtracking dispatch_call_list/1.
|
||||
@@ -540,26 +586,30 @@ parse_write_options(Options, OptionValues, Stub) :-
|
||||
|
||||
|
||||
parse_write_options_(double_quotes(DoubleQuotes), double_quotes-DoubleQuotes) :-
|
||||
( nonvar(DoubleQuotes),
|
||||
lists:member(DoubleQuotes, [true, false]),
|
||||
( var(DoubleQuotes) ->
|
||||
throw(error(instantiation_error, _))
|
||||
; lists:member(DoubleQuotes, [true, false]),
|
||||
!
|
||||
; throw(error(domain_error(write_option, double_quotes(DoubleQuotes)), _))
|
||||
).
|
||||
parse_write_options_(ignore_ops(IgnoreOps), ignore_ops-IgnoreOps) :-
|
||||
( nonvar(IgnoreOps),
|
||||
lists:member(IgnoreOps, [true, false]),
|
||||
( var(IgnoreOps) ->
|
||||
throw(error(instantiation_error, _))
|
||||
; lists:member(IgnoreOps, [true, false]),
|
||||
!
|
||||
; throw(error(domain_error(write_option, ignore_ops(IgnoreOps)), _))
|
||||
).
|
||||
parse_write_options_(quoted(Quoted), quoted-Quoted) :-
|
||||
( nonvar(Quoted),
|
||||
lists:member(Quoted, [true, false]),
|
||||
( var(Quoted) ->
|
||||
throw(error(instantiation_error, _))
|
||||
; lists:member(Quoted, [true, false]),
|
||||
!
|
||||
; throw(error(domain_error(write_option, quoted(Quoted)), _))
|
||||
).
|
||||
parse_write_options_(numbervars(NumberVars), numbervars-NumberVars) :-
|
||||
( nonvar(NumberVars),
|
||||
lists:member(NumberVars, [true, false]),
|
||||
( var(NumberVars) ->
|
||||
throw(error(instantiation_error, _))
|
||||
; lists:member(NumberVars, [true, false]),
|
||||
!
|
||||
; throw(error(domain_error(write_option, numbervars(NumberVars)), _))
|
||||
).
|
||||
@@ -567,7 +617,9 @@ parse_write_options_(variable_names(VNNames), variable_names-VNNames) :-
|
||||
must_be_var_names_list(VNNames),
|
||||
!.
|
||||
parse_write_options_(max_depth(MaxDepth), max_depth-MaxDepth) :-
|
||||
( integer(MaxDepth),
|
||||
( var(MaxDepth) ->
|
||||
throw(error(instantiation_error, _))
|
||||
; integer(MaxDepth),
|
||||
MaxDepth >= 0,
|
||||
!
|
||||
; throw(error(domain_error(write_option, max_depth(MaxDepth)), _))
|
||||
@@ -681,30 +733,9 @@ parse_read_term_options(Options, OptionValues, Stub) :-
|
||||
parse_options_list(Options, builtins:parse_read_term_options_, DefaultOptions, OptionValues, Stub).
|
||||
|
||||
|
||||
parse_read_term_options_(singletons(Vars), singletons-Vars) :-
|
||||
( ( var(Vars)
|
||||
; '$skip_max_list'(_, _, Vars, Rs),
|
||||
Rs == []
|
||||
) ->
|
||||
!
|
||||
; throw(error(domain_error(read_option, singletons(Vars)), read_term/2))
|
||||
).
|
||||
parse_read_term_options_(variables(Vars), variables-Vars) :-
|
||||
( ( var(Vars)
|
||||
; '$skip_max_list'(_, _, Vars, Rs),
|
||||
Rs == []
|
||||
) ->
|
||||
!
|
||||
; throw(error(domain_error(read_option, variables(Vars)), read_term/2))
|
||||
).
|
||||
parse_read_term_options_(variable_names(Vars), variable_names-Vars) :-
|
||||
( ( var(Vars)
|
||||
; '$skip_max_list'(_, _, Vars, Rs),
|
||||
Rs == []
|
||||
) ->
|
||||
!
|
||||
; throw(error(domain_error(read_option, variable_names(Vars)), read_term/2))
|
||||
).
|
||||
parse_read_term_options_(singletons(Vars), singletons-Vars) :- !.
|
||||
parse_read_term_options_(variables(Vars), variables-Vars) :- !.
|
||||
parse_read_term_options_(variable_names(Vars), variable_names-Vars) :- !.
|
||||
parse_read_term_options_(E,_) :-
|
||||
throw(error(domain_error(read_option, E), _)).
|
||||
|
||||
@@ -714,7 +745,7 @@ parse_read_term_options_(E,_) :-
|
||||
% Read Term from the stream Stream. It supports several options:
|
||||
% * `variables(-Vars)` unifies Vars with a list of variables in the term. Similar to do `term_variables/2` with the new term.
|
||||
% * `variable_names(-Vars)` unifies Vars with a list `Name=Var` with Name describing the variable name and Var the variable itself that appears in Term.
|
||||
% * `singletons` similar to `variable_names` but only reports variables occurring only once in Term.
|
||||
% * `singletons` similar to `variable_names` but only reports variables occurring only once in Term.
|
||||
read_term(Stream, Term, Options) :-
|
||||
parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term/3),
|
||||
'$read_term'(Stream, Term, Singletons, Variables, VariableNames).
|
||||
@@ -788,7 +819,7 @@ catch(G,C,R) :-
|
||||
|
||||
catch(G,C,R,Bb) :-
|
||||
'$install_new_block'(NBb),
|
||||
'$call_with_inference_counting'(call(G)),
|
||||
call(G),
|
||||
end_block(Bb, NBb).
|
||||
catch(G,C,R,Bb) :-
|
||||
'$reset_block'(Bb),
|
||||
@@ -1142,8 +1173,13 @@ clause(H, B) :-
|
||||
% The clause will be inserted at the beginning of the module.
|
||||
asserta(Clause0) :-
|
||||
loader:strip_subst_module(Clause0, user, Module, Clause),
|
||||
iso_ext:asserta(Module, Clause).
|
||||
asserta_(Module, Clause).
|
||||
|
||||
asserta_(Module, (Head :- Body)) :-
|
||||
!,
|
||||
'$asserta'(Module, Head, Body).
|
||||
asserta_(Module, Fact) :-
|
||||
'$asserta'(Module, Fact, true).
|
||||
|
||||
:- meta_predicate assertz(:).
|
||||
|
||||
@@ -1153,7 +1189,13 @@ asserta(Clause0) :-
|
||||
% The clase will be inserted at the end of the module.
|
||||
assertz(Clause0) :-
|
||||
loader:strip_subst_module(Clause0, user, Module, Clause),
|
||||
iso_ext:assertz(Module, Clause).
|
||||
assertz_(Module, Clause).
|
||||
|
||||
assertz_(Module, (Head :- Body)) :-
|
||||
!,
|
||||
'$assertz'(Module, Head, Body).
|
||||
assertz_(Module, Fact) :-
|
||||
'$assertz'(Module, Fact, true).
|
||||
|
||||
|
||||
:- meta_predicate retract(:).
|
||||
@@ -1172,6 +1214,9 @@ retract(Clause0) :-
|
||||
Body = true,
|
||||
retract_module_clause(Head, Body, Module)
|
||||
; Clause = (Head :- Body) ->
|
||||
( var(Module) -> Module = user
|
||||
; true
|
||||
),
|
||||
retract_module_clause(Head, Body, Module)
|
||||
).
|
||||
|
||||
@@ -1194,7 +1239,10 @@ call_retract_helper(Head, Body, P, Module) :-
|
||||
; ClauseQualifier = Module
|
||||
),
|
||||
ClauseQualifier:'$clause'(Head, Body),
|
||||
'$get_clause_p'(Head, P, Module).
|
||||
% ensure '$get_clause_p'/3 is not the last clause so it can
|
||||
% recover the choice point of '$clause' if necessary.
|
||||
'$get_clause_p'(Head, P, Module),
|
||||
true.
|
||||
|
||||
call_retract(Head, Body, Name, Arity, Module) :-
|
||||
findall(P, builtins:call_retract_helper(Head, Body, P, Module), Ps),
|
||||
@@ -1342,11 +1390,6 @@ current_predicate(Pred) :-
|
||||
; throw(error(type_error(predicate_indicator, Pred), current_predicate/1))
|
||||
).
|
||||
|
||||
'$iterate_op_db_refs'(RPriority, RSpec, ROp, _, RPriority, RSpec, ROp).
|
||||
'$iterate_op_db_refs'(RPriority, RSpec, ROp, OssifiedOpDir, Priority, Spec, Op) :-
|
||||
'$get_next_op_db_ref'(RPriority, RSpec, ROp, OssifiedOpDir, RRPriority, RRSpec, RROp),
|
||||
'$iterate_op_db_refs'(RRPriority, RRSpec, RROp, OssifiedOpDir, Priority, Spec, Op).
|
||||
|
||||
can_be_op_priority(Priority) :- var(Priority).
|
||||
can_be_op_priority(Priority) :- op_priority(Priority).
|
||||
|
||||
@@ -1362,8 +1405,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'(RPriority, RSpec, ROp, OssifiedOpDir, _, _, Op),
|
||||
'$iterate_op_db_refs'(RPriority, RSpec, ROp, OssifiedOpDir, Priority, Spec, Op)
|
||||
'$get_next_op_db_ref'(Priority, Spec, Op, ListOfOps),
|
||||
lists:member(op(Priority, Spec, Op), ListOfOps)
|
||||
).
|
||||
|
||||
list_of_op_atoms(Var) :-
|
||||
@@ -1587,7 +1630,7 @@ atom_concat(Atom_1, Atom_2, Atom_12) :-
|
||||
%% sub_atom(+Atom, ?Before, ?Length, ?After, ?SubAtom).
|
||||
%
|
||||
% Relates an atom to a subatom inside with some key properties:
|
||||
%
|
||||
%
|
||||
% * SubAtom starts at Before characters (0-based) from Atom
|
||||
% * SubAtom has Length characters
|
||||
% * After SubAtom there are After characters in Atom
|
||||
@@ -2186,6 +2229,9 @@ set_stream_position(S_or_a, Position) :-
|
||||
%% callable(X).
|
||||
%
|
||||
% True iff X is bound o an atom or a compund term.
|
||||
|
||||
:- non_counted_backtracking callable/1.
|
||||
|
||||
callable(X) :-
|
||||
( nonvar(X), functor(X, F, _), atom(F) ->
|
||||
true
|
||||
@@ -2209,4 +2255,7 @@ nl(Stream) :-
|
||||
%
|
||||
% Throws an exception of the following structure: `error(ErrorTerm, ImpDef)`.
|
||||
error(Error_term, Imp_def) :-
|
||||
throw(error(Error_term, Imp_def)).
|
||||
( var(Error_term) ->
|
||||
throw(error(instantiation_error, error/2))
|
||||
; throw(error(Error_term, Imp_def))
|
||||
).
|
||||
|
||||
@@ -20,6 +20,7 @@ read and write chars.
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(between)).
|
||||
:- use_module(library(iso_ext), [partial_string/1,partial_string/3]).
|
||||
|
||||
fabricate_var_name(VarType, VarName, N) :-
|
||||
@@ -74,9 +75,10 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
|
||||
).
|
||||
|
||||
|
||||
%% char_type(+Char, -Type).
|
||||
%% char_type(?Char, ?Type).
|
||||
%
|
||||
% Given a Char, Type is one of the categories that char fits in.
|
||||
% Type is one of the categories that Char fits in.
|
||||
% At least one of the arguments must be ground.
|
||||
% Possible categories are:
|
||||
%
|
||||
% - `alnum`
|
||||
@@ -105,8 +107,8 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
|
||||
% - `symbolic_control`
|
||||
% - `symbolic_hexadecimal`
|
||||
% - `upper`
|
||||
% - `to_lower(Lower)`
|
||||
% - `to_upper(Upper)`
|
||||
% - `lower(Lower)`
|
||||
% - `upper(Upper)`
|
||||
% - `whitespace`
|
||||
%
|
||||
% An example:
|
||||
@@ -124,25 +126,35 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
|
||||
% ; Type = octet
|
||||
% ; Type = prolog
|
||||
% ; Type = symbolic_control
|
||||
% ; Type = to_lower("a")
|
||||
% ; Type = to_upper("A")
|
||||
% ; Type = lower("a")
|
||||
% ; Type = upper("A")
|
||||
% ; false.
|
||||
% ```
|
||||
%
|
||||
% Note that uppercase and lowercase transformations use a string. This is because
|
||||
% some characters do not map 1:1 between lowercase and uppercase.
|
||||
char_type(Char, Type) :-
|
||||
must_be(character, Char),
|
||||
( ground(Type) ->
|
||||
( ctype(Type) ->
|
||||
'$char_type'(Char, Type)
|
||||
; domain_error(char_type, Type, char_type/2)
|
||||
)
|
||||
; ctype(Type),
|
||||
can_be(character, Char),
|
||||
( \+ ctype(Type) ->
|
||||
domain_error(char_type, Type, char_type/2)
|
||||
; true
|
||||
),
|
||||
( ground(Char) ->
|
||||
ctype(Type),
|
||||
'$char_type'(Char, Type)
|
||||
; ground(Type) ->
|
||||
ccode(Code),
|
||||
char_code(Char, Code),
|
||||
'$char_type'(Char, Type)
|
||||
; must_be(character, Char)
|
||||
).
|
||||
|
||||
|
||||
% 0xD800 to 0xDFFF are surrogate code points used by UTF-16.
|
||||
|
||||
ccode(Code) :- between(0, 0xD7FF, Code).
|
||||
ccode(Code) :- between(0xE000, 0x10FFFF, Code).
|
||||
|
||||
ctype(alnum).
|
||||
ctype(alpha).
|
||||
ctype(alphabetic).
|
||||
@@ -168,8 +180,8 @@ ctype(sign).
|
||||
ctype(solo).
|
||||
ctype(symbolic_control).
|
||||
ctype(symbolic_hexadecimal).
|
||||
ctype(to_lower(_)).
|
||||
ctype(to_upper(_)).
|
||||
ctype(lower(_)).
|
||||
ctype(upper(_)).
|
||||
ctype(upper).
|
||||
ctype(whitespace).
|
||||
|
||||
|
||||
187
src/lib/clpb.pl
187
src/lib/clpb.pl
@@ -1,10 +1,29 @@
|
||||
/* CLP(B): Constraint Logic Programming over Boolean Variables
|
||||
|
||||
Copyright (C): 2019-2023 Markus Triska
|
||||
All rights reserved.
|
||||
|
||||
Author: Markus Triska
|
||||
E-mail: triska@metalevel.at
|
||||
WWW: http://www.metalevel.at
|
||||
WWW: https://www.metalevel.at
|
||||
Copyright (C): 2019-2023 Markus Triska
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
@@ -17,8 +36,8 @@
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(clpb, [op(300, fy, ~),
|
||||
op(500, yfx, #),
|
||||
sat/1,
|
||||
op(500, yfx, #),
|
||||
sat/1,
|
||||
taut/2,
|
||||
labeling/1,
|
||||
sat_count/2,
|
||||
@@ -91,6 +110,46 @@ domain_error(Expectation, Term) :-
|
||||
type_error(Expectation, Term) :-
|
||||
type_error(Expectation, Term, unknown(Term)-1).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Compatibility predicates.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- meta_predicate(include(1, ?, ?)).
|
||||
|
||||
include(_, [], []).
|
||||
include(Goal, [L|Ls0], Ls) :-
|
||||
( call(Goal, L) ->
|
||||
Ls = [L|Rest]
|
||||
; Ls = Rest
|
||||
),
|
||||
include(Goal, Ls0, Rest).
|
||||
|
||||
:- meta_predicate(exclude(1, ?, ?)).
|
||||
|
||||
exclude(_, [], []).
|
||||
exclude(Goal, [L|Ls0], Ls) :-
|
||||
( call(Goal, L) ->
|
||||
Ls = Rest
|
||||
; Ls = [L|Rest]
|
||||
),
|
||||
exclude(Goal, Ls0, Rest).
|
||||
|
||||
:- meta_predicate(partition(2,?,?,?,?)).
|
||||
|
||||
partition(_, [], [], [], []).
|
||||
partition(Pred, [H|T], L, E, G) :-
|
||||
call(Pred, H, Diff),
|
||||
partition_(Diff, H, Pred, T, L, E, G).
|
||||
|
||||
partition_(<, H, Pred, T, [H|Rest], E, G) :-
|
||||
partition(Pred, T, Rest, E, G).
|
||||
partition_(=, H, Pred, T, L, [H|Rest], G) :-
|
||||
partition(Pred, T, L, Rest, G).
|
||||
partition_(>, H, Pred, T, L, E, [H|Rest]) :-
|
||||
partition(Pred, T, L, E, Rest).
|
||||
|
||||
:- meta_predicate(partition(1,?,?,?)).
|
||||
|
||||
partition(Pred, Ls0, As, Bs) :-
|
||||
include(Pred, Ls0, As),
|
||||
exclude(Pred, Ls0, Bs).
|
||||
@@ -130,18 +189,18 @@ A _Boolean expression_ is one of:
|
||||
| `1` | true |
|
||||
| _variable_ | unknown truth value |
|
||||
| _atom_ | universally quantified variable |
|
||||
| ~ _Expr_ | logical NOT |
|
||||
| _Expr_ + _Expr_ | logical OR |
|
||||
| _Expr_ * _Expr_ | logical AND |
|
||||
| _Expr_ # _Expr_ | exclusive OR |
|
||||
| _Var_ ^ _Expr_ | existential quantification |
|
||||
| _Expr_ =:= _Expr_ | equality |
|
||||
| _Expr_ =\= _Expr_ | disequality (same as #) |
|
||||
| _Expr_ =< _Expr_ | less or equal (implication) |
|
||||
| _Expr_ >= _Expr_ | greater or equal |
|
||||
| _Expr_ < _Expr_ | less than |
|
||||
| _Expr_ > _Expr_ | greater than |
|
||||
| card(Is,Exprs) | cardinality constraint (_see below_) |
|
||||
| `~` _Expr_ | logical NOT |
|
||||
| _Expr_ `+` _Expr_ | logical OR |
|
||||
| _Expr_ `*` _Expr_ | logical AND |
|
||||
| _Expr_ `#` _Expr_ | exclusive OR |
|
||||
| _Var_ `^` _Expr_ | existential quantification |
|
||||
| _Expr_ `=:=` _Expr_ | equality |
|
||||
| _Expr_ `=\=` _Expr_ | disequality (same as #) |
|
||||
| _Expr_ `=<` _Expr_ | less or equal (implication) |
|
||||
| _Expr_ `>=` _Expr_ | greater or equal |
|
||||
| _Expr_ `<` _Expr_ | less than |
|
||||
| _Expr_ `>` _Expr_ | greater than |
|
||||
| `card(Is,Exprs)` | cardinality constraint (_see below_) |
|
||||
| `+(Exprs)` | n-fold disjunction (_see below_) |
|
||||
| `*(Exprs)` | n-fold conjunction (_see below_) |
|
||||
|
||||
@@ -447,6 +506,10 @@ non_monotonic(X) :-
|
||||
; true
|
||||
).
|
||||
|
||||
:- meta_predicate(bdd_nodes(1, ?, ?)).
|
||||
:- meta_predicate(bdd_nodes_(1, ?, ?, ?)).
|
||||
:- meta_predicate(with_aux(1, ?)).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Rewriting to canonical expressions.
|
||||
Atoms are converted to variables with a special attribute.
|
||||
@@ -1188,7 +1251,7 @@ bdd_restriction_(Node, VI, Value, Res) -->
|
||||
node_id(Node, ID) },
|
||||
( { I0 =:= VI } ->
|
||||
( { Value =:= 0 } -> { Res = Low }
|
||||
; { Value =:= 1 } -> { Res = High }
|
||||
; { Res = High }
|
||||
)
|
||||
; { I0 > VI } -> { Res = Node }
|
||||
; state(G0), { get_assoc(ID, G0, Res) } -> []
|
||||
@@ -1513,8 +1576,7 @@ random_bindings(VNum, Node) -->
|
||||
%
|
||||
% ```
|
||||
% ?- sat(A#B), weighted_maximum([1,2,1], [A,B,C], Maximum).
|
||||
% A = 0, B = 1, C = 1, Maximum = 3
|
||||
% ; false.
|
||||
% A = 0, B = 1, C = 1, Maximum = 3.
|
||||
% ```
|
||||
|
||||
weighted_maximum(Ws, Vars, Max) :-
|
||||
@@ -1635,8 +1697,7 @@ attribute_goals(Var) -->
|
||||
{ bdd_nodes(BDD, Nodes),
|
||||
phrase(nodes(Nodes), Ns) },
|
||||
[clpb:'$clpb_bdd'(Ns)]
|
||||
; { prepare_global_variables(BDD),
|
||||
phrase(sat_ands(Formula), Ands0),
|
||||
; { phrase(sat_ands(Formula), Ands0),
|
||||
ands_fusion(Ands0, Ands),
|
||||
maplist(formula_anf, Ands, ANFs0),
|
||||
sort(ANFs0, ANFs1),
|
||||
@@ -1656,6 +1717,10 @@ attribute_goals(Var) -->
|
||||
booleans(RestVs)
|
||||
; boolean(Var) % the variable may have occurred only in taut/2
|
||||
).
|
||||
attribute_goals(Var) -->
|
||||
{ get_atts(Var, clpb_max(_)),
|
||||
!,
|
||||
put_atts(Var, -clpb_max(_)) }.
|
||||
attribute_goals(Var) -->
|
||||
{ get_atts(Var, clpb_bdd(BDD)),
|
||||
ground(BDD),
|
||||
@@ -1663,36 +1728,13 @@ attribute_goals(Var) -->
|
||||
|
||||
del_clpb(Var) :-
|
||||
del_attr(Var, clpb),
|
||||
del_attr(Var, clpb_hash).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
To make residual projection work with recorded constraints, the
|
||||
global counters must be adjusted so that new variables and nodes
|
||||
also get new IDs. Also, clpb_next_id/2 is used to actually create
|
||||
these counters, because creating them with b_setval/2 would make
|
||||
them [] on backtracking, which is quite unfortunate in itself.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
del_attr(Var, clpb_hash),
|
||||
del_attr(Var, clpb_atom).
|
||||
|
||||
b_setval(K, T) :- bb_b_put(K, T).
|
||||
nb_setval(K, T) :- bb_put(K, T).
|
||||
b_getval(K, T) :- bb_get(K, T).
|
||||
|
||||
prepare_global_variables(BDD) :-
|
||||
clpb_next_id('$clpb_next_var', V0),
|
||||
clpb_next_id('$clpb_next_node', N0),
|
||||
bdd_nodes(BDD, Nodes),
|
||||
foldl(max_variable_node, Nodes, V0-N0, MaxV0-MaxN0),
|
||||
MaxV is MaxV0 + 1,
|
||||
MaxN is MaxN0 + 1,
|
||||
b_setval('$clpb_next_var', MaxV),
|
||||
b_setval('$clpb_next_node', MaxN).
|
||||
|
||||
max_variable_node(Node, V0-N0, V-N) :-
|
||||
node_id(Node, N1),
|
||||
node_varindex(Node, V1),
|
||||
N is max(N0,N1),
|
||||
V is max(V0,V1).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Fuse formulas that share the same variables into single conjunctions.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
@@ -1817,7 +1859,8 @@ booleans([B|Bs]) --> boolean(B), booleans(Bs).
|
||||
|
||||
boolean(Var) -->
|
||||
{ del_clpb(Var) },
|
||||
( { get_attr(Var, clpb_omit_boolean, true) } -> []
|
||||
( { get_attr(Var, clpb_omit_boolean, true) } ->
|
||||
{ put_atts(Var, -clpb_omit_boolean(_)) }
|
||||
; [clpb:sat(Var =:= Var)]
|
||||
).
|
||||
|
||||
@@ -1919,49 +1962,3 @@ clpb_atom_var(Atom, Var) :-
|
||||
put_assoc(Atom, A0, Var, A),
|
||||
b_setval('$clpb_atoms', A)
|
||||
).
|
||||
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Compatibility predicates.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
|
||||
include(Goal, List, Is) :-
|
||||
include_(List, Goal, Is).
|
||||
|
||||
include_([], _, []).
|
||||
include_([X1|Xs1], P, Is) :-
|
||||
( call(P, X1)
|
||||
-> Is = [X1|Is1]
|
||||
; Is = Is1
|
||||
),
|
||||
include_(Xs1, P, Is1).
|
||||
|
||||
|
||||
exclude(Goal, List, Is) :-
|
||||
exclude_(List, Goal, Is).
|
||||
|
||||
exclude_([], _, []).
|
||||
exclude_([X1|Xs1], P, Is) :-
|
||||
( call(P, X1)
|
||||
-> Is = Is1
|
||||
; Is = [X1|Is1]
|
||||
),
|
||||
exclude_(Xs1, P, Is1).
|
||||
|
||||
|
||||
partition(Pred, List, Less, Equal, Greater) :-
|
||||
partition_(List, Pred, Less, Equal, Greater).
|
||||
|
||||
partition_([], _, [], [], []).
|
||||
partition_([H|T], Pred, L, E, G) :-
|
||||
call(Pred, H, Diff),
|
||||
partition_(Diff, H, Pred, T, L, E, G).
|
||||
|
||||
partition_(<, H, Pred, T, [H|Rest], E, G) :-
|
||||
partition_(T, Pred, Rest, E, G).
|
||||
partition_(=, H, Pred, T, L, [H|Rest], G) :-
|
||||
partition_(T, Pred, L, Rest, G).
|
||||
partition_(>, H, Pred, T, L, E, [H|Rest]) :-
|
||||
partition_(T, Pred, L, E, Rest).
|
||||
|
||||
572
src/lib/clpz.pl
572
src/lib/clpz.pl
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020-2023 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020-2024 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
/** Predicates for cryptographic applications.
|
||||
@@ -25,6 +25,7 @@
|
||||
crypto_password_hash/3, % +Password, -Hash, +Options
|
||||
crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options
|
||||
crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options
|
||||
ed25519_seed_keypair/2, % +Seed, -KeyPair
|
||||
ed25519_new_keypair/1, % -KeyPair
|
||||
ed25519_keypair_public_key/2, % +KeyPair, +PublicKey
|
||||
ed25519_sign/4, % +KeyPair, +Data, -Signature, +Options
|
||||
@@ -191,6 +192,11 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
|
||||
% The default encoding is `utf8`. The alternative is `octet`, to
|
||||
% treat the input as a list of raw bytes.
|
||||
%
|
||||
% - `hmac(+Key)`
|
||||
% Compute a hash-based message authentication code (HMAC) using
|
||||
% Key, a list of bytes. This option is currently supported for
|
||||
% algorithms `sha256`, `sha384` and `sha512`.
|
||||
%
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
@@ -213,9 +219,18 @@ crypto_data_hash(Data0, Hash, Options0) :-
|
||||
( hash_algorithm(A) -> true
|
||||
; domain_error(hash_algorithm, A, crypto_data_hash/3)
|
||||
),
|
||||
'$crypto_data_hash'(Data, Encoding, HashBytes, A),
|
||||
( member(HMAC, Options0), nonvar(HMAC), HMAC = hmac(Ks) ->
|
||||
must_be_bytes(Ks, crypto_data_hash/3),
|
||||
hmac_algorithm(A),
|
||||
'$crypto_hmac'(Data, Encoding, Ks, HashBytes, A)
|
||||
; '$crypto_data_hash'(Data, Encoding, HashBytes, A)
|
||||
),
|
||||
hex_bytes(Hash, HashBytes).
|
||||
|
||||
hmac_algorithm(sha256).
|
||||
hmac_algorithm(sha384).
|
||||
hmac_algorithm(sha512).
|
||||
|
||||
options_data_chars(Options, Data, Chars, Encoding) :-
|
||||
option(encoding(Encoding), Options, utf8),
|
||||
must_be(atom, Encoding),
|
||||
@@ -612,6 +627,50 @@ encoding_chars(utf8, Cs, Cs) :-
|
||||
===============================
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
%% ed25519_seed_keypair(+Seed, -Pair)
|
||||
%
|
||||
% Use Seed to deterministically generate an Ed25519 key pair Pair, a
|
||||
% list of characters. Seed must be a list of 32 bytes. It can be
|
||||
% chosen at random (using for example `crypto_n_random_bytes/2`) or
|
||||
% derived from input keying material (IKM) using for example
|
||||
% `crypto_data_hkdf/4`. The pair contains the private key and must be
|
||||
% kept absolutely secret. Pair can be used for signing. Its public
|
||||
% key can be obtained with `ed25519_keypair_public_key/2`.
|
||||
|
||||
ed25519_seed_keypair(Seed, Pair) :-
|
||||
must_be_bytes(Seed, ed25519_keypair_from_seed/2),
|
||||
length(Seed, 32),
|
||||
'$ed25519_seed_to_public_key'(Seed, Public),
|
||||
maplist(char_code, Public, PublicBytes),
|
||||
phrase(ed25519_PKCS8v2(Seed,PublicBytes), DERs),
|
||||
maplist(char_code, Pair, DERs).
|
||||
|
||||
% DER (and hence BER) encoding of an Ed25519 private key and
|
||||
% corresponding public key in PKCS#8v2 format (RFC 5958) as specified
|
||||
% in RFC 8410.
|
||||
|
||||
ed25519_PKCS8v2(Seed, PublicBytes) -->
|
||||
[0x30,81], % a SEQUENCE of 81 bytes follows
|
||||
|
||||
% the publicKey is present, hence we set version to v2
|
||||
[2,1,1], % the integer 1 denoting version 2 (awesome design!)
|
||||
|
||||
% privateKeyAlgorithm: SEQUENCE
|
||||
[0x30,5], % a SEQUENCE of 5 bytes follows
|
||||
[6,3], % an OBJECT IDENTIFIER of 3 bytes follows
|
||||
[43,101,112], % OID of Ed25519
|
||||
|
||||
% privateKey: OCTET STRING
|
||||
[4,34], % an OCTET STRING of 34 bytes follows
|
||||
[4,32], % an OCTET STRING of 32 bytes follows
|
||||
seq(Seed), % the seed is the private key
|
||||
|
||||
% publicKey: [1] IMPLICIT BIT STRING; context-specific, hence bit 7 set
|
||||
[0b10000001], % the public key follows
|
||||
[33], % a BIT STRING of length 33 follows
|
||||
[0], % 32 bytes is divisible by 8, hence 0 unused bits
|
||||
seq(PublicBytes).
|
||||
|
||||
%% ed25519_new_keypair(-Pair)
|
||||
%
|
||||
% Yields a new Ed25519 key pair Pair, a list of characters. The
|
||||
@@ -620,7 +679,8 @@ encoding_chars(utf8, Cs, Cs) :-
|
||||
% with `ed25519_keypair_public_key/2`.
|
||||
|
||||
ed25519_new_keypair(Pair) :-
|
||||
'$ed25519_new_keypair'(Pair).
|
||||
crypto_n_random_bytes(32, Bytes),
|
||||
ed25519_seed_keypair(Bytes, Pair).
|
||||
|
||||
%% ed25519_keypair_public_key(+Pair, -PublicKey)
|
||||
%
|
||||
@@ -629,8 +689,11 @@ ed25519_new_keypair(Pair) :-
|
||||
% The public key is represented as a list of characters.
|
||||
|
||||
ed25519_keypair_public_key(Pair, PublicKey) :-
|
||||
must_be_octet_chars(Pair, ed25519_keypair_public_key),
|
||||
'$ed25519_keypair_public_key'(Pair, PublicKey).
|
||||
must_be_octet_chars(Pair, ed25519_keypair_public_key/2),
|
||||
reverse(Pair, RPs),
|
||||
length(RPublicKey, 32),
|
||||
phrase((seq(RPublicKey),...), RPs),
|
||||
reverse(RPublicKey, PublicKey).
|
||||
|
||||
%% ed25519_sign(+Key, +Data, -Signature, +Options)
|
||||
%
|
||||
@@ -638,10 +701,14 @@ ed25519_keypair_public_key(Pair, PublicKey) :-
|
||||
% PKCS#8 v2 format as generated by `ed25519_new_keypair/1`. Sign Data
|
||||
% with Key, yielding Signature as a list of hexadecimal characters.
|
||||
|
||||
ed25519_sign(Key, Data0, Signature, Options) :-
|
||||
must_be_octet_chars(Key, ed25519_sign),
|
||||
ed25519_sign(KeyPair, Data0, Signature, Options) :-
|
||||
must_be_octet_chars(KeyPair, ed25519_sign/4),
|
||||
length(Prefix, 16),
|
||||
length(PrivateKeyChars, 32),
|
||||
phrase((seq(Prefix),seq(PrivateKeyChars),...), KeyPair),
|
||||
maplist(char_code, PrivateKeyChars, PrivateKey),
|
||||
options_data_chars(Options, Data0, Data, Encoding),
|
||||
'$ed25519_sign'(Key, Data, Encoding, Signature0),
|
||||
'$ed25519_sign_raw'(PrivateKey, Data, Encoding, Signature0),
|
||||
hex_bytes(Signature, Signature0).
|
||||
|
||||
%% ed25519_verify(+Key, +Data, +Signature, +Options)
|
||||
@@ -658,10 +725,10 @@ ed25519_sign(Key, Data0, Signature, Options) :-
|
||||
% which treats Data as a list of raw bytes.
|
||||
|
||||
ed25519_verify(Key, Data0, Signature0, Options) :-
|
||||
must_be_octet_chars(Key, ed25519_verify),
|
||||
must_be_octet_chars(Key, ed25519_verify/4),
|
||||
options_data_chars(Options, Data0, Data, Encoding),
|
||||
hex_bytes(Signature0, Signature),
|
||||
'$ed25519_verify'(Key, Data, Encoding, Signature).
|
||||
'$ed25519_verify_raw'(Key, Data, Encoding, Signature).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
X25519: ECDH key exchange over Curve25519
|
||||
@@ -761,9 +828,26 @@ curve_a(curve(_,_,A,_,_,_,_,_), A).
|
||||
curve_b(curve(_,_,_,B,_,_,_,_), B).
|
||||
curve_field_length(curve(_,_,_,_,_,_,FieldLength,_), FieldLength).
|
||||
|
||||
%% crypto_curve_generator(+Curve, -G)
|
||||
%
|
||||
% Yields the generator point G of Curve.
|
||||
|
||||
crypto_curve_generator(curve(_,_,_,_,G,_,_,_), G).
|
||||
|
||||
%% crypto_curve_order(+Curve, -Order)
|
||||
%
|
||||
% Yields the order of Curve.
|
||||
|
||||
crypto_curve_order(curve(_,_,_,_,_,Order,_,_), Order).
|
||||
|
||||
%% crypto_curve_scalar_mult(+Curve, +Scalar, +Point, -Result)
|
||||
%
|
||||
% Computes the point _Result = Scalar*Point_. Scalar must be an
|
||||
% integer, and Point must be a point on Curve. This operation can be
|
||||
% used to negotiate a shared secret over a public channel. Consider
|
||||
% using `curve25519_scalar_mult/3` instead for more desirable
|
||||
% security properties.
|
||||
|
||||
crypto_curve_scalar_mult(Curve, Scalar, point(X,Y), point(RX, RY)) :-
|
||||
must_be(integer, Scalar),
|
||||
must_be_on_curve(Curve, point(X,Y)),
|
||||
@@ -830,6 +914,12 @@ fitting_exponent(N, E0, E) :-
|
||||
fitting_exponent(N, E1, E)
|
||||
).
|
||||
|
||||
%% crypto_name_curve(+Name, -Curve)
|
||||
%
|
||||
% Yields a representation of the elliptic curve with name Name.
|
||||
% Currently, the only supported name is `secp256k1`, a Koblitz curve
|
||||
% regarded as secure.
|
||||
|
||||
crypto_name_curve(secp256k1,
|
||||
curve(secp256k1,
|
||||
0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
|
||||
|
||||
@@ -221,7 +221,7 @@ row([X | Y], Opt) -->
|
||||
!,
|
||||
( separator(Opt) ->
|
||||
row(Y, Opt)
|
||||
; end_token ->
|
||||
; end_token,
|
||||
{ Y = [] }).
|
||||
|
||||
|
||||
|
||||
@@ -12,9 +12,12 @@ to learn more about them.
|
||||
[op(1105, xfy, '|'),
|
||||
phrase/2,
|
||||
phrase/3,
|
||||
phrase/4,
|
||||
phrase/5,
|
||||
seq//1,
|
||||
seqq//1,
|
||||
... //0
|
||||
... //0,
|
||||
(-->)/2
|
||||
]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
@@ -26,6 +29,10 @@ to learn more about them.
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?, ?).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?, ?, ?).
|
||||
|
||||
%% phrase(+Body, ?Ls).
|
||||
%
|
||||
% True iff Body describes the list Ls. Body must be a DCG body.
|
||||
@@ -75,6 +82,34 @@ phrase(GRBody, S0, S) :-
|
||||
; call(M:GRBody1, S0, S)
|
||||
).
|
||||
|
||||
phrase(GRBody, Arg, S0, S) :-
|
||||
strip_module(GRBody, M, GRBody1),
|
||||
( var(GRBody) ->
|
||||
instantiation_error(phrase/4)
|
||||
; nonvar(GRBody1),
|
||||
GRBody1 =.. GRBodys1,
|
||||
append(GRBodys1, [Arg], GRBodys2),
|
||||
GRBody2 =.. GRBodys2,
|
||||
dcg_constr(GRBody2),
|
||||
dcg_body(GRBody2, S0, S, GRBody3) ->
|
||||
call(M:GRBody3)
|
||||
; call(M:GRBody1, Arg, S0, S)
|
||||
).
|
||||
|
||||
phrase(GRBody, Arg1, Arg2, S0, S) :-
|
||||
strip_module(GRBody, M, GRBody1),
|
||||
( var(GRBody) ->
|
||||
instantiation_error(phrase/5)
|
||||
; nonvar(GRBody1),
|
||||
GRBody1 =.. GRBodys1,
|
||||
append(GRBodys1, [Arg1,Arg2], GRBodys2),
|
||||
GRBody2 =.. GRBodys2,
|
||||
dcg_constr(GRBody2),
|
||||
dcg_body(GRBody2, S0, S, GRBody3) ->
|
||||
call(M:GRBody3)
|
||||
; call(M:GRBody1, Arg1, Arg2, S0, S)
|
||||
).
|
||||
|
||||
% The same version of the below two dcg_rule clauses, but with module scoping.
|
||||
dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :-
|
||||
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||
@@ -101,7 +136,10 @@ dcg_rule(( NonTerminal --> GRBody ), ( Head :- Body )) :-
|
||||
dcg_non_terminal(NonTerminal, S0, S, Goal) :-
|
||||
NonTerminal =.. NonTerminalUniv,
|
||||
append(NonTerminalUniv, [S0, S], GoalUniv),
|
||||
Goal =.. GoalUniv.
|
||||
( callable(NonTerminal) ->
|
||||
Goal =.. GoalUniv
|
||||
; Goal = NonTerminal % let call/N throw an error instead of throwing one here.
|
||||
).
|
||||
|
||||
dcg_terminals(Terminals, S0, S, S0 = List) :-
|
||||
append(Terminals, S, List).
|
||||
@@ -116,8 +154,6 @@ dcg_body(GRBody, S0, S, Body) :-
|
||||
dcg_body(NonTerminal, S0, S, Goal1) :-
|
||||
nonvar(NonTerminal),
|
||||
\+ dcg_constr(NonTerminal),
|
||||
NonTerminal \= ( _ -> _ ),
|
||||
NonTerminal \= ( \+ _ ),
|
||||
loader:strip_module(NonTerminal, M, NonTerminal0),
|
||||
dcg_non_terminal(NonTerminal0, S0, S, Goal0),
|
||||
( functor(NonTerminal, (:), 2) ->
|
||||
@@ -135,9 +171,13 @@ dcg_constr(( _'|'_ )). % 7.14.6 - alternative
|
||||
dcg_constr({_}). % 7.14.7
|
||||
dcg_constr(call(_)). % 7.14.8
|
||||
dcg_constr(phrase(_)). % 7.14.9
|
||||
dcg_constr(phrase(_,_)). % extension of 7.14.9
|
||||
dcg_constr(phrase(_,_,_)). % extension of 7.14.9
|
||||
dcg_constr(!). % 7.14.10
|
||||
%% dcg_constr(\+ _). % 7.14.11 - not (existence implementation dep.)
|
||||
dcg_constr((_->_)). % 7.14.12 - if-then (existence implementation dep.)
|
||||
dcg_constr(\+ G_0) :- % 7.14.11 - not (existence implementation def.)
|
||||
throw(error(representation_error(dcg_body), [culprit- (\+ G_0)])).
|
||||
dcg_constr((If->Then)) :- % 7.14.12 - if-then (existence implementation def.)
|
||||
throw(error(representation_error(dcg_body), [culprit- (If->Then)])).
|
||||
|
||||
% The principal functor of the first argument indicates
|
||||
% the construct to be expanded.
|
||||
@@ -162,8 +202,10 @@ dcg_cbody(( GREither '|' GROr ), S0, S, ( Either ; Or )) :-
|
||||
dcg_cbody({Goal}, S0, S, ( Goal, S0 = S )).
|
||||
dcg_cbody(call(Cont), S0, S, call(Cont, S0, S)).
|
||||
dcg_cbody(phrase(Body), S0, S, phrase(Body, S0, S)).
|
||||
dcg_cbody(phrase(Body, Arg), S0, S, phrase(Body, Arg, S0, S)).
|
||||
dcg_cbody(phrase(Body, Arg1, Arg2), S0, S, phrase(Body, Arg1, Arg2, S0, S)).
|
||||
dcg_cbody(!, S0, S, ( !, S0 = S )).
|
||||
dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )).
|
||||
% dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )).
|
||||
dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :-
|
||||
dcg_body(GRIf, S0, S1, If),
|
||||
dcg_body(GRThen, S1, S, Then).
|
||||
@@ -202,6 +244,8 @@ seqq([Es|Ess]) --> seq(Es), seqq(Ess).
|
||||
|
||||
error_goal(error(E, must_be/2), error(E, must_be/2)).
|
||||
error_goal(error(E, (=..)/2), error(E, (=..)/2)).
|
||||
error_goal(error(representation_error(dcg_body), Context),
|
||||
error(representation_error(dcg_body), Context)).
|
||||
error_goal(E, _) :- throw(E).
|
||||
|
||||
user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
|
||||
@@ -217,3 +261,10 @@ user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
|
||||
).
|
||||
|
||||
user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])).
|
||||
|
||||
|
||||
% (-->)/2 behaves as if it didn't exist. We export (and define) it
|
||||
% only so that clauses for (-->)/2 cannot be asserted when
|
||||
% library(dcgs) is loaded.
|
||||
|
||||
(_-->_) :- throw(error(existence_error(procedure,(-->)/2),(-->)/2)).
|
||||
|
||||
@@ -53,5 +53,4 @@ $(G_0) :-
|
||||
%
|
||||
% Generalize away Goal.
|
||||
|
||||
|
||||
*(_).
|
||||
|
||||
@@ -7,7 +7,7 @@ arguments are different terms.
|
||||
|
||||
:- use_module(library(atts)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(lists), [append/3]).
|
||||
:- use_module(library(lists), [append/3, maplist/3]).
|
||||
|
||||
:- attribute dif/1.
|
||||
|
||||
@@ -23,6 +23,34 @@ dif_set_variables([Var|Vars], X, Y) :-
|
||||
put_dif_att(Var, X, Y),
|
||||
dif_set_variables(Vars, X, Y).
|
||||
|
||||
remove_goal([], _, []).
|
||||
remove_goal([G0|G0s], Goal0, Goals) :-
|
||||
( G0 == Goal0 ->
|
||||
remove_goal(G0s, Goal0, Goals)
|
||||
; Goals = [G0|Goals1],
|
||||
remove_goal(G0s, Goal0, Goals1)
|
||||
).
|
||||
|
||||
vars_remove_goal([], _).
|
||||
vars_remove_goal([Var|Vars], Goal0) :-
|
||||
( get_atts(Var, +dif(Goals0)) ->
|
||||
remove_goal(Goals0, Goal0, Goals),
|
||||
( Goals = [] ->
|
||||
put_atts(Var, -dif(_))
|
||||
; put_atts(Var, +dif(Goals))
|
||||
)
|
||||
; true
|
||||
),
|
||||
vars_remove_goal(Vars, Goal0).
|
||||
|
||||
reinforce_goal(Goal0, Goal) :-
|
||||
Goal = (
|
||||
term_variables(Goal0, Vars),
|
||||
dif:vars_remove_goal(Vars, Goal0),
|
||||
Goal0 = (L \== R),
|
||||
dif:dif(L, R)
|
||||
).
|
||||
|
||||
append_goals([], _).
|
||||
append_goals([Var|Vars], Goals) :-
|
||||
( get_atts(Var, +dif(VarGoals)) ->
|
||||
@@ -34,9 +62,10 @@ append_goals([Var|Vars], Goals) :-
|
||||
append_goals(Vars, Goals).
|
||||
|
||||
verify_attributes(Var, Value, Goals) :-
|
||||
( get_atts(Var, +dif(Goals)) ->
|
||||
( get_atts(Var, +dif(Goals0)) ->
|
||||
term_variables(Value, ValueVars),
|
||||
append_goals(ValueVars, Goals)
|
||||
append_goals(ValueVars, Goals0),
|
||||
maplist(reinforce_goal, Goals0, Goals)
|
||||
; Goals = []
|
||||
).
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ directory_must_exist(Directory, Context) :-
|
||||
; throw(error(existence_error(directory, Directory), Context))
|
||||
).
|
||||
|
||||
%% workind_directory(Dir0, Dir).
|
||||
%% working_directory(Dir0, Dir).
|
||||
%
|
||||
% Dir0 is the current working directory, and the working directory
|
||||
% is changed to Dir.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020-2023 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020-2024 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
I place this code in the public domain. Use it in any way you want.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
@@ -302,14 +302,14 @@ format_number_chars(N0, Chars) :-
|
||||
N is N0, % evaluate compound expression
|
||||
number_chars(N, Chars).
|
||||
|
||||
n_newlines(0) --> !.
|
||||
n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N).
|
||||
n_newlines(0) --> [].
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
?- phrase(upto_what(Cs, ~), "abc~test", Rest).
|
||||
Cs = [a,b,c], Rest = [~,t,e,s,t].
|
||||
?- phrase(upto_what(Cs, ~), "abc", Rest).
|
||||
Cs = [a,b,c], Rest = [].
|
||||
?- phrase(format:upto_what(Cs, ~), "abc~test", Rest).
|
||||
Cs = "abc", Rest = "~test".
|
||||
?- phrase(format:upto_what(Cs, ~), "abc", Rest).
|
||||
Cs = "abc", Rest = [].
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
separate_digits_fractional(Arg, Sep, Num, Cs) :-
|
||||
@@ -444,9 +444,9 @@ format(Stream, Fs, Args) :-
|
||||
?- phrase(format:cells("~`at~50|", [], 0, [], []), Cs),
|
||||
phrase(format:format_cells(Cs), Ls).
|
||||
?- phrase(format:cells("~ta~t~tb~tc~21|", [], 0, [], []), Cs).
|
||||
Cs = [cell(0,21,[glue(' ',_A),chars("a"),glue(' ',_B),glue(' ',_C),chars("b"),glue(' ',_D),chars("c ...")])]
|
||||
Cs = [cell(0,21,[glue(' ',_A),chars("a"),glue(' ',_B),glue(' ',_C),chars("b"),glue(' ',_D),chars("c")])].
|
||||
?- phrase(format:cells("~ta~t~4|", [], 0, [], []), Cs).
|
||||
Cs = [cell(0,4,[glue(' ',_A),chars("a"),glue(' ',_B)])]
|
||||
Cs = [cell(0,4,[glue(' ',_A),chars("a"),glue(' ',_B)])].
|
||||
|
||||
?- phrase(format:format_cell(cell(0,1,[glue(a,_94)])), Ls).
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
*/
|
||||
|
||||
/** This library provides an starting point to build HTTP server based applications.
|
||||
It is based on [Hyper](https://hyper.rs/), which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However,
|
||||
some advanced features that Hyper provides are still not accesible.
|
||||
It is based on [Warp](https://github.com/seanmonstar/warp), which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However,
|
||||
some advanced features that Warp provides are still not accesible.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -46,7 +46,6 @@ recommeded to use the helper predicates, which are easier to understand and clea
|
||||
Some things that are still missing:
|
||||
|
||||
- Read forms in multipart format
|
||||
- HTTP Basic Auth
|
||||
- Session handling via cookies
|
||||
- HTML Templating (but you can use [Teruel](https://github.com/aarroyoc/teruel/), [Marquete](https://github.com/aarroyoc/marquete/) or [Djota](https://github.com/aarroyoc/djota) for that)
|
||||
*/
|
||||
@@ -54,14 +53,19 @@ Some things that are still missing:
|
||||
|
||||
:- module(http_server, [
|
||||
http_listen/2,
|
||||
http_listen/3,
|
||||
http_headers/2,
|
||||
http_status_code/2,
|
||||
http_body/2,
|
||||
http_redirect/2,
|
||||
http_query/3
|
||||
http_query/3,
|
||||
http_basic_auth/4
|
||||
]).
|
||||
|
||||
:- meta_predicate http_listen(?, :).
|
||||
:- meta_predicate http_listen(?, :, ?).
|
||||
|
||||
:- meta_predicate http_basic_auth(:, :, ?, ?).
|
||||
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(crypto)).
|
||||
@@ -74,25 +78,58 @@ Some things that are still missing:
|
||||
|
||||
%% http_listen(+Port, +Handlers).
|
||||
%
|
||||
% Listens for HTTP connections on port Port. Each handler on the list Handlers should be of the form: `HttpVerb(PathUnification, Predicate)`.
|
||||
% For example: `get(user/User, get_info(User))` will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable)
|
||||
% and it will call `get_info` with three arguments: an `http_request` term, an `http_response` term and User.
|
||||
% Equivalent to `http_listen(Port, Handlers, [])`.
|
||||
http_listen(Port, Module:Handlers0) :-
|
||||
must_be(integer, Port),
|
||||
must_be(list, Handlers0),
|
||||
maplist(module_qualification(Module), Handlers0, Handlers),
|
||||
http_listen_(Port, Handlers).
|
||||
http_listen_(Port, Handlers, []).
|
||||
|
||||
%% http_listen(+Port, +Handlers, +Options).
|
||||
%
|
||||
% Listens for HTTP connections on port Port. Each handler on the list Handlers should be of the form: `HttpVerb(PathUnification, Predicate)`.
|
||||
% For example: `get(user/User, get_info(User))` will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable)
|
||||
% and it will call `get_info` with three arguments: an `http_request` term, an `http_response` term and User.
|
||||
%
|
||||
% The following options are supported:
|
||||
%
|
||||
% - `tls_key(+Key)` - a TLS key for HTTPS (string)
|
||||
% - `tls_cert(+Cert)` - a TLS cert for HTTPS (string)
|
||||
% - `content_length_limit(+Limit)` - maximum length (in bytes) for the incoming bodies. By default, 32KB.
|
||||
%
|
||||
% In order to have a HTTPS server (instead of plain HTTP), both `tls_key` and `tls_cert` options must be provided.
|
||||
http_listen(Port, Module:Handlers0, Options) :-
|
||||
must_be(integer, Port),
|
||||
must_be(list, Handlers0),
|
||||
must_be(list, Options),
|
||||
maplist(module_qualification(Module), Handlers0, Handlers),
|
||||
http_listen_(Port, Handlers, Options).
|
||||
|
||||
module_qualification(M, H0, H) :-
|
||||
H0 =.. [Method, Path, Goal],
|
||||
H =.. [Method, Path, M:Goal].
|
||||
|
||||
http_listen_(Port, Handlers) :-
|
||||
http_listen_(Port, Handlers, Options) :-
|
||||
parse_options(Options, TLSKey, TLSCert, ContentLengthLimit),
|
||||
phrase(format_("0.0.0.0:~d", [Port]), Addr),
|
||||
'$http_listen'(Addr, HttpListener),!,
|
||||
'$http_listen'(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit),!,
|
||||
format("Listening at ~s\n", [Addr]),
|
||||
http_loop(HttpListener, Handlers).
|
||||
|
||||
parse_options(Options, TLSKey, TLSCert, ContentLengthLimit) :-
|
||||
member_option_default(tls_key, Options, "", TLSKey),
|
||||
member_option_default(tls_cert, Options, "", TLSCert),
|
||||
member_option_default(content_length_limit, Options, 32768, ContentLengthLimit),
|
||||
must_be(integer, ContentLengthLimit).
|
||||
|
||||
member_option_default(Key, List, _Default, Value) :-
|
||||
X =.. [Key, Value],
|
||||
member(X, List).
|
||||
member_option_default(Key, List, Default, Default) :-
|
||||
X =.. [Key, _],
|
||||
\+ member(X, List).
|
||||
|
||||
|
||||
http_loop(HttpListener, Handlers) :-
|
||||
'$http_accept'(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle),
|
||||
current_time(Time),
|
||||
@@ -114,7 +151,7 @@ http_loop(HttpListener, Handlers) :-
|
||||
)
|
||||
; (
|
||||
'$http_answer'(ResponseHandle, 404, [], ResponseStream),
|
||||
call_cleanup(format(ResponseStream, "Not Found"), close(ResponseStream)))
|
||||
call_cleanup(format(ResponseStream, "Not Found", []), close(ResponseStream)))
|
||||
),
|
||||
http_loop(HttpListener, Handlers).
|
||||
|
||||
@@ -263,25 +300,21 @@ http_query(http_request(_, _, Queries), Key, Value) :- member(Key-Value, Queries
|
||||
|
||||
parse_queries([Key-Value|Queries]) -->
|
||||
string_without("=", Key0),
|
||||
{
|
||||
phrase(url_decode(Key), Key0)
|
||||
},
|
||||
"=",
|
||||
string_without("&", Value0),
|
||||
{
|
||||
phrase(url_decode(Value), Value0)
|
||||
},
|
||||
"&",
|
||||
parse_queries(Queries).
|
||||
parse_queries(Queries),
|
||||
{
|
||||
phrase(url_decode(Key), Key0),
|
||||
phrase(url_decode(Value), Value0)
|
||||
}.
|
||||
|
||||
parse_queries([Key-Value]) -->
|
||||
string_without("=", Key0),
|
||||
{
|
||||
phrase(url_decode(Key), Key0)
|
||||
},
|
||||
"=",
|
||||
string_without(" ", Value0),
|
||||
{
|
||||
phrase(url_decode(Key), Key0),
|
||||
phrase(url_decode(Value), Value0)
|
||||
}.
|
||||
|
||||
@@ -292,9 +325,13 @@ parse_queries([]) -->
|
||||
url_decode([Char|Chars]) -->
|
||||
[Char],
|
||||
{
|
||||
Char \= '%'
|
||||
Char \= '%',
|
||||
Char \= (+)
|
||||
},
|
||||
url_decode(Chars).
|
||||
url_decode([' '|Chars]) -->
|
||||
"+",
|
||||
url_decode(Chars).
|
||||
url_decode([Char|Chars]) -->
|
||||
"%",
|
||||
[A],
|
||||
@@ -352,3 +389,49 @@ url_decode([Char|Chars]) -->
|
||||
url_decode(Chars).
|
||||
|
||||
url_decode([]) --> [].
|
||||
|
||||
%% http_basic_auth(+LoginPredicate, +Handler, +Request, -Response)
|
||||
%
|
||||
% Metapredicate that wraps an existing Handler with an HTTP Basic Auth flow.
|
||||
% Checks if a given user + password is authorized to execute that handler, returning 401
|
||||
% if it's not satisfied.
|
||||
%
|
||||
% `LoginPredicate` must be a predicate of arity 2 that takes a User and a Password.
|
||||
% `Handler` will have, in addition to the Request and Response arguments, a User argument
|
||||
% containing the User given in the authentication.
|
||||
%
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% main :-
|
||||
% http_listen(8800,[get('/', http_basic_auth(login, inside_handler("data")))]).
|
||||
%
|
||||
% login(User, Pass) :-
|
||||
% User = "aarroyoc",
|
||||
% Pass = "123456".
|
||||
%
|
||||
% inside_handler(Data, User, Request, Response) :-
|
||||
% http_body(Response, text(User)).
|
||||
% ```
|
||||
http_basic_auth(LoginPredicate, Handler, Request, Response) :-
|
||||
http_headers(Request, Headers),
|
||||
member("authorization"-AuthorizationStr, Headers),
|
||||
append("Basic ", Coded, AuthorizationStr),
|
||||
chars_base64(UserPass, Coded, []),
|
||||
append(User, [':'|Password], UserPass),
|
||||
(
|
||||
call(LoginPredicate, User, Password) ->
|
||||
call(Handler, User, Request, Response)
|
||||
; http_basic_auth_unauthorized_response(Response)
|
||||
).
|
||||
|
||||
http_basic_auth(_LoginPredicate, _Handler, Request, Response) :-
|
||||
http_headers(Request, Headers),
|
||||
\+ member("authorization"-_, Headers),
|
||||
http_basic_auth_unauthorized_response(Response).
|
||||
|
||||
http_basic_auth_unauthorized_response(Response) :-
|
||||
http_status_code(Response, 401),
|
||||
http_headers(Response, ["www-authenticate"-"Basic realm=\"Scryer Prolog\", charset=\"UTF-8\""]),
|
||||
http_body(Response, text("Unauthorized")).
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ but they're not part of the ISO Prolog standard at the moment.
|
||||
bb_put/2,
|
||||
call_cleanup/2,
|
||||
call_with_inference_limit/3,
|
||||
call_residue_vars/2,
|
||||
forall/2,
|
||||
partial_string/1,
|
||||
partial_string/3,
|
||||
@@ -18,8 +19,7 @@ but they're not part of the ISO Prolog standard at the moment.
|
||||
call_nth/2,
|
||||
countall/2,
|
||||
copy_term_nat/2,
|
||||
asserta/2,
|
||||
assertz/2]).
|
||||
copy_term/3]).
|
||||
|
||||
:- use_module(library(error), [can_be/2,
|
||||
domain_error/3,
|
||||
@@ -28,6 +28,8 @@ but they're not part of the ISO Prolog standard at the moment.
|
||||
|
||||
:- use_module(library(lists), [maplist/3]).
|
||||
|
||||
:- use_module(library('$project_atts')).
|
||||
|
||||
:- meta_predicate(forall(0, 0)).
|
||||
|
||||
%% forall(Generate, Test).
|
||||
@@ -214,26 +216,6 @@ run_cleaners_without_handling(Cp) :-
|
||||
|
||||
% call_with_inference_limit
|
||||
|
||||
:- non_counted_backtracking end_block/4.
|
||||
|
||||
end_block(_, Bb, NBb, _L) :-
|
||||
'$clean_up_block'(NBb),
|
||||
'$reset_block'(Bb).
|
||||
end_block(B, _Bb, NBb, L) :-
|
||||
'$install_inference_counter'(B, L, _),
|
||||
'$reset_block'(NBb),
|
||||
'$fail'.
|
||||
|
||||
:- non_counted_backtracking handle_ile/3.
|
||||
|
||||
handle_ile(B, inference_limit_exceeded(B), inference_limit_exceeded) :-
|
||||
!,
|
||||
'$pop_ball_stack'.
|
||||
handle_ile(B, _, _) :-
|
||||
'$remove_call_policy_check'(B),
|
||||
'$pop_from_ball_stack',
|
||||
'$unwind_stack'.
|
||||
|
||||
:- meta_predicate(call_with_inference_limit(0, ?, ?)).
|
||||
|
||||
:- non_counted_backtracking call_with_inference_limit/3.
|
||||
@@ -256,8 +238,6 @@ call_with_inference_limit(G, L, R) :-
|
||||
call_with_inference_limit(G, L, R, Bb, B),
|
||||
'$remove_call_policy_check'(B).
|
||||
|
||||
install_inference_counter(B, L, Count0) :-
|
||||
'$install_inference_counter'(B, L, Count0).
|
||||
|
||||
:- meta_predicate(call_with_inference_limit(0,?,?,?,?)).
|
||||
|
||||
@@ -265,23 +245,34 @@ install_inference_counter(B, L, Count0) :-
|
||||
|
||||
call_with_inference_limit(G, L, R, Bb, B) :-
|
||||
'$install_new_block'(NBb),
|
||||
'$install_inference_counter'(B, L, Count0),
|
||||
'$install_inference_counter'(NBb, L, Count0),
|
||||
'$call_with_inference_counting'(call(G)),
|
||||
'$inference_level'(R, B),
|
||||
'$remove_inference_counter'(B, Count1),
|
||||
'$remove_inference_counter'(NBb, Count1),
|
||||
Diff is L - (Count1 - Count0),
|
||||
end_block(B, Bb, NBb, Diff).
|
||||
( '$clean_up_block'(NBb),
|
||||
'$reset_block'(Bb)
|
||||
; '$install_inference_counter'(NBb, Diff, _),
|
||||
'$reset_block'(NBb),
|
||||
'$fail'
|
||||
).
|
||||
call_with_inference_limit(_, _, R, Bb, B) :-
|
||||
( '$inference_limit_exceeded' ->
|
||||
R = inference_limit_exceeded
|
||||
; true
|
||||
),
|
||||
'$get_current_block'(NBb),
|
||||
'$remove_inference_counter'(NBb, _),
|
||||
'$reset_block'(Bb),
|
||||
'$remove_inference_counter'(B, _),
|
||||
( '$get_ball'(Ball),
|
||||
'$remove_call_policy_check'(B),
|
||||
( '$get_ball'(_),
|
||||
'$push_ball_stack',
|
||||
'$get_cp'(Cp),
|
||||
'$set_cp_by_default'(Cp)
|
||||
; '$remove_call_policy_check'(B),
|
||||
'$fail'
|
||||
),
|
||||
handle_ile(B, Ball, R).
|
||||
'$set_cp_by_default'(Cp),
|
||||
'$pop_from_ball_stack',
|
||||
'$unwind_stack'
|
||||
; nonvar(R)
|
||||
).
|
||||
|
||||
%% partial_string(String, L, L0)
|
||||
%
|
||||
@@ -358,10 +349,10 @@ call_nth_nesting(C, ID) :-
|
||||
bb_put(ID, 0),
|
||||
bb_put(i_call_nth_counter, C).
|
||||
|
||||
%% countall(Goal, N).
|
||||
%% countall(G_0, N).
|
||||
%
|
||||
% countall(Goal, N) counts all solutions of Goal and unifies N with
|
||||
% this number of solutions. This predicate always succeeds once.
|
||||
% countall(G_0, N) is true iff N unifies with the total number of
|
||||
% answers of call(G_0).
|
||||
|
||||
:- meta_predicate(countall(0, ?)).
|
||||
|
||||
@@ -370,7 +361,7 @@ countall(Goal, N) :-
|
||||
( integer(N) ->
|
||||
( N < 0 ->
|
||||
domain_error(not_less_than_zero, N, countall/2)
|
||||
; N > 0
|
||||
; true
|
||||
)
|
||||
; true
|
||||
),
|
||||
@@ -395,21 +386,23 @@ countall(Goal, N) :-
|
||||
copy_term_nat(Source, Dest) :-
|
||||
'$copy_term_without_attr_vars'(Source, Dest).
|
||||
|
||||
%% asserta(Module, Rule_Fact).
|
||||
%% copy_term(+Term, -Copy, -Gs).
|
||||
%
|
||||
% Similar to `asserta/1` but allows specifying a Module
|
||||
asserta(Module, (Head :- Body)) :-
|
||||
!,
|
||||
'$asserta'(Module, Head, Body).
|
||||
asserta(Module, Fact) :-
|
||||
'$asserta'(Module, Fact, true).
|
||||
% Produce a deep copy of Term and unify it to Copy, without attributes.
|
||||
% Unify Gs with a list of goals that represent the attributes of Term.
|
||||
% Similar to `copy_term/2` but splitting the attributes.
|
||||
copy_term(Term, Copy, Gs) :-
|
||||
can_be(list, Gs),
|
||||
findall(Term-Rs, '$project_atts':term_residual_goals(Term,Rs), [Copy-Gs]),
|
||||
( var(Gs) ->
|
||||
Gs = []
|
||||
; true
|
||||
).
|
||||
|
||||
%% assertz(Module, Rule_Fact).
|
||||
%
|
||||
% Similar to `assertz/1` but allows specifying a Module
|
||||
assertz(Module, (Head :- Body)) :-
|
||||
!,
|
||||
'$assertz'(Module, Head, Body).
|
||||
assertz(Module, Fact) :-
|
||||
'$assertz'(Module, Fact, true).
|
||||
:- meta_predicate call_residue_vars(0, ?).
|
||||
|
||||
call_residue_vars(Goal, Vars) :-
|
||||
can_be(list, Vars),
|
||||
'$get_attr_var_queue_delim'(B),
|
||||
call(Goal),
|
||||
'$get_attr_var_queue_beyond'(B, Vars).
|
||||
|
||||
@@ -23,7 +23,9 @@ finding out the PID of the running system.
|
||||
unsetenv/1,
|
||||
shell/1,
|
||||
shell/2,
|
||||
pid/1]).
|
||||
pid/1,
|
||||
raw_argv/1,
|
||||
argv/1]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(charsio)).
|
||||
@@ -110,3 +112,34 @@ permitted('_').
|
||||
must_be_chars(Cs) :-
|
||||
must_be(list, Cs),
|
||||
maplist(must_be(character), Cs).
|
||||
|
||||
%% raw_argv(-Argv)
|
||||
%
|
||||
% True iff Argv is the list of arguments that this program was started with (usually passed via command line).
|
||||
% In contrast to `argv/1`, this version includes every argument, without any postprocessing, just as the operating
|
||||
% system reports it to the system. This includes-flags of Scryer itself, which are not needed in general.
|
||||
raw_argv(Argv) :-
|
||||
can_be(list, Argv),
|
||||
'$argv'(Argv).
|
||||
|
||||
%% argv(-Argv)
|
||||
%
|
||||
% True if Argv is the list of arguments that this program was started with (usually passed via command line).
|
||||
% In this version, only arguments specific to the program are passed. To differentiate between the system
|
||||
% arguments and the program arguments, we use `--` as a separator.
|
||||
%
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% % Call with scryer-prolog -f -- -t hello
|
||||
% ?- argv(X).
|
||||
% X = ["-t", "hello"].
|
||||
% ```
|
||||
argv(Argv) :-
|
||||
can_be(list, Argv),
|
||||
'$argv'(Argv0),
|
||||
( append(_, ["--"|Argv1], Argv0) ->
|
||||
Argv = Argv1
|
||||
;
|
||||
Argv = []
|
||||
).
|
||||
|
||||
147
src/lib/pio.pl
147
src/lib/pio.pl
@@ -9,6 +9,7 @@
|
||||
|
||||
:- module(pio, [phrase_from_file/2,
|
||||
phrase_from_file/3,
|
||||
phrase_from_stream/2,
|
||||
phrase_to_file/2,
|
||||
phrase_to_file/3,
|
||||
phrase_to_stream/2
|
||||
@@ -17,16 +18,30 @@
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(freeze)).
|
||||
:- use_module(library(iso_ext), [setup_call_cleanup/3, partial_string/3]).
|
||||
:- use_module(library(lists), [member/2, maplist/2]).
|
||||
:- use_module(library(gensym)).
|
||||
:- use_module(library(iso_ext), [
|
||||
bb_get/2, bb_put/2, setup_call_cleanup/3, partial_string/3, partial_string_tail/2
|
||||
]).
|
||||
:- use_module(library(lists), [append/3, length/2, member/2, maplist/2]).
|
||||
:- use_module(library(charsio), [get_n_chars/3]).
|
||||
|
||||
:- meta_predicate(phrase_from_file(2, ?)).
|
||||
:- meta_predicate(phrase_from_file(2, ?, ?)).
|
||||
:- meta_predicate(phrase_from_stream(2, ?)).
|
||||
:- meta_predicate(phrase_to_file(2, ?)).
|
||||
:- meta_predicate(phrase_to_file(2, ?, ?)).
|
||||
:- meta_predicate(phrase_to_stream(2, ?)).
|
||||
|
||||
|
||||
%% phrase_from_stream(+GRBody, +Stream)
|
||||
%
|
||||
% True if grammar rule body GRBody covers the contents of the stream,
|
||||
% represented as a list of characters.
|
||||
|
||||
phrase_from_stream(GRBody, Stream) :-
|
||||
stream_to_lazy_list(Stream, Ls),
|
||||
phrase(GRBody, Ls).
|
||||
|
||||
%% phrase_from_file(+GRBody, +File)
|
||||
%
|
||||
% True if grammar rule body GRBody covers the contents of File,
|
||||
@@ -48,25 +63,121 @@ phrase_from_file(NT, File, Options) :-
|
||||
member(Type, [text,binary])
|
||||
; Type = text
|
||||
),
|
||||
setup_call_cleanup(open(File, read, Stream, [reposition(true)|Options]),
|
||||
( stream_to_lazy_list(Stream, Xs),
|
||||
phrase(NT, Xs) ),
|
||||
close(Stream))
|
||||
).
|
||||
setup_call_cleanup(
|
||||
open(File, read, Stream, Options),
|
||||
phrase_from_stream(NT, Stream),
|
||||
close(Stream)
|
||||
)
|
||||
).
|
||||
|
||||
% How many chars to read from stream and buffer in each step
|
||||
chars_to_read(4096).
|
||||
|
||||
stream_to_lazy_list(Stream, Xs) :-
|
||||
stream_property(Stream, position(Pos)),
|
||||
freeze(Xs, reader_step(Stream, Pos, Xs)).
|
||||
stream_to_lazy_list(Stream, Ls) :-
|
||||
get_stream_buffer_position(Stream, Pos),
|
||||
freeze(Ls, render_step(Stream, Pos, Ls)).
|
||||
|
||||
reader_step(Stream, Pos, Xs0) :-
|
||||
set_stream_position(Stream, Pos),
|
||||
( at_end_of_stream(Stream)
|
||||
-> Xs0 = []
|
||||
; get_n_chars(Stream, 4096, Cs),
|
||||
partial_string(Cs, Xs0, Xs),
|
||||
stream_to_lazy_list(Stream, Xs)
|
||||
).
|
||||
render_step(Stream, Pos, Ls) :-
|
||||
set_stream_buffer_position(Stream, Pos),
|
||||
( buffer_at_end_of_stream(Stream) ->
|
||||
Ls = []
|
||||
; chars_to_read(CharsToRead),
|
||||
buffer_get_n_chars(Stream, CharsToRead, Chars),
|
||||
partial_string(Chars, Ls, Ls0),
|
||||
stream_to_lazy_list(Stream, Ls0)
|
||||
).
|
||||
|
||||
buffer_at_end_of_stream(Stream) :-
|
||||
stream_bufferids(Stream, _, BufferPosId, _),
|
||||
bb_get(BufferPosId, Pos),
|
||||
Pos = eof.
|
||||
|
||||
get_stream_buffer_position(Stream, Pos) :-
|
||||
stream_bufferids(Stream, _, BufferPosId, _),
|
||||
bb_get(BufferPosId, Pos).
|
||||
|
||||
set_stream_buffer_position(Stream, Pos) :-
|
||||
stream_bufferids(Stream, _, BufferPosId, _),
|
||||
bb_put(BufferPosId, Pos).
|
||||
|
||||
buffer_get_n_chars(Stream, N, Chars) :-
|
||||
stream_bufferids(Stream, BufferId, BufferPosId, BufferLenId),
|
||||
buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N),
|
||||
bb_get(BufferId, Buffer),
|
||||
bb_get(BufferPosId, BufferPos),
|
||||
( BufferPos = eof ->
|
||||
Chars = []
|
||||
; string_get_n_chars(Buffer, BufferPos, N, Chars),
|
||||
length(Chars, NChars),
|
||||
( NChars = 0 ->
|
||||
BufferPos1 = eof
|
||||
; BufferPos1 is BufferPos + NChars
|
||||
),
|
||||
bb_put(BufferPosId, BufferPos1)
|
||||
).
|
||||
|
||||
buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N) :-
|
||||
bb_get(BufferPosId, BufferPos),
|
||||
bb_get(BufferLenId, BufferLen),
|
||||
( BufferLen < BufferPos + N ->
|
||||
bb_get(BufferId, Buffer),
|
||||
(
|
||||
( var(Buffer) ->
|
||||
BufferTail = Buffer
|
||||
; partial_string_last_tail(Buffer, BufferTail)
|
||||
) ->
|
||||
( at_end_of_stream(Stream) ->
|
||||
BufferTail = [],
|
||||
bb_put(BufferId, Buffer)
|
||||
; chars_to_read(CharsToRead),
|
||||
get_n_chars(Stream, CharsToRead, Chars),
|
||||
length(Chars, NChars),
|
||||
partial_string(Chars, BufferTail, _),
|
||||
bb_put(BufferId, Buffer),
|
||||
BufferLen1 is BufferLen + NChars,
|
||||
bb_put(BufferLenId, BufferLen1),
|
||||
buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N)
|
||||
)
|
||||
; true
|
||||
)
|
||||
; true
|
||||
).
|
||||
|
||||
partial_string_last_tail(PartialString, PartialStringTail) :-
|
||||
partial_string_tail(PartialString, PartialStringTail0),
|
||||
( var(PartialStringTail0) ->
|
||||
PartialStringTail = PartialStringTail0
|
||||
; partial_string_last_tail(PartialStringTail0, PartialStringTail)
|
||||
).
|
||||
|
||||
string_get_n_chars(String, Pos, N, Chars) :-
|
||||
'$skip_max_list'(_, Pos, String, String1),
|
||||
'$skip_max_list'(N1, N, String1, _),
|
||||
length(Chars, N1),
|
||||
append(Chars, _, String1).
|
||||
|
||||
stream_bufferids(Stream, BufferId, BufferPosId, BufferLenId) :-
|
||||
( bb_get(streams_buffers, _) ->
|
||||
true
|
||||
; bb_put(streams_buffers, [])
|
||||
),
|
||||
bb_get(streams_buffers, StreamsBuffers),
|
||||
( member(
|
||||
stream_buffer(Stream, BufferId, BufferPosId, BufferLenId),
|
||||
StreamsBuffers
|
||||
) ->
|
||||
true
|
||||
; gensym(buffer, BufferId),
|
||||
gensym(buffer_pos, BufferPosId),
|
||||
gensym(buffer_len, BufferLenId),
|
||||
bb_put(
|
||||
streams_buffers,
|
||||
[stream_buffer(Stream, BufferId, BufferPosId, BufferLenId)|StreamsBuffers]
|
||||
),
|
||||
bb_put(BufferId, _),
|
||||
bb_put(BufferPosId, 0),
|
||||
bb_put(BufferLenId, 0)
|
||||
).
|
||||
|
||||
%% phrase_to_stream(+GRBody, +Stream)
|
||||
%
|
||||
|
||||
@@ -37,8 +37,10 @@
|
||||
atomic_si/1,
|
||||
list_si/1,
|
||||
character_si/1,
|
||||
term_si/1,
|
||||
chars_si/1,
|
||||
dif_si/2]).
|
||||
dif_si/2,
|
||||
when_si/2]).
|
||||
|
||||
:- use_module(library(lists)).
|
||||
|
||||
@@ -68,6 +70,11 @@ character_si(Ch) :-
|
||||
atom(Ch),
|
||||
atom_length(Ch,1).
|
||||
|
||||
term_si(Term) :-
|
||||
( ground(Term) -> acyclic_term(Term)
|
||||
; throw(error(instantiation_error, term_si/1))
|
||||
).
|
||||
|
||||
chars_si(Chs0) :-
|
||||
'$skip_max_list'(_,_, Chs0,Chs),
|
||||
( nonvar(Chs) -> Chs == [] ; true ), % fails for infinite lists too
|
||||
@@ -92,3 +99,31 @@ dif_si(X, Y) :-
|
||||
( X \= Y -> true
|
||||
; throw(error(instantiation_error,dif_si/2))
|
||||
).
|
||||
|
||||
:- meta_predicate(when_si(+, 0)).
|
||||
|
||||
%% when_si(Condition, Goal).
|
||||
%
|
||||
% Executes Goal when Condition becomes true. Throws an instantiation error if
|
||||
% it can't decide.
|
||||
when_si(Condition, Goal) :-
|
||||
% Taken from https://stackoverflow.com/a/40449516
|
||||
( when_condition_si(Condition) ->
|
||||
( Condition ->
|
||||
Goal
|
||||
; throw(error(instantiation_error,when_si/2))
|
||||
)
|
||||
; throw(error(domain_error(when_condition_si, Condition),_))
|
||||
).
|
||||
|
||||
when_condition_si(Cond) :-
|
||||
var(Cond), !, throw(error(instantiation_error,when_condition_si/2)).
|
||||
when_condition_si(ground(_)).
|
||||
when_condition_si(nonvar(_)).
|
||||
when_condition_si((A, B)) :-
|
||||
when_condition_si(A),
|
||||
when_condition_si(B).
|
||||
when_condition_si((A ; B)) :-
|
||||
when_condition_si(A),
|
||||
when_condition_si(B).
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ sleep(T) :-
|
||||
:- meta_predicate time(0).
|
||||
|
||||
:- dynamic(time_id/1).
|
||||
:- dynamic(time_state/2).
|
||||
:- dynamic(time_state/3).
|
||||
|
||||
time_next_id(N) :-
|
||||
( retract(time_id(N0)) ->
|
||||
@@ -111,9 +111,9 @@ time_next_id(N) :-
|
||||
% Reports the execution time of Goal.
|
||||
|
||||
time(Goal) :-
|
||||
'$cpu_now'(T0),
|
||||
cputime_inferences(T0, I0),
|
||||
time_next_id(ID),
|
||||
setup_call_cleanup(asserta(time_state(ID, T0)),
|
||||
setup_call_cleanup(asserta(time_state(ID, T0, I0)),
|
||||
( call_cleanup(catch(Goal, E, (report_time(ID),throw(E))),
|
||||
Det = true),
|
||||
time_true(ID),
|
||||
@@ -123,49 +123,72 @@ time(Goal) :-
|
||||
; report_time(ID),
|
||||
false
|
||||
),
|
||||
retract(time_state(ID, _))).
|
||||
retract(time_state(ID, _, _))).
|
||||
|
||||
cputime_inferences(T, I) :-
|
||||
'$cpu_now'(T),
|
||||
'$inference_count'(I).
|
||||
|
||||
time_true(ID) :-
|
||||
report_time(ID).
|
||||
time_true(ID) :-
|
||||
% on backtracking, update the stored CPU time for this ID
|
||||
retract(time_state(ID, _)),
|
||||
'$cpu_now'(T0),
|
||||
asserta(time_state(ID, T0)),
|
||||
retract(time_state(ID, _, _)),
|
||||
cputime_inferences(T0, I0),
|
||||
asserta(time_state(ID, T0, I0)),
|
||||
false.
|
||||
|
||||
report_time(ID) :-
|
||||
time_state(ID, T0),
|
||||
'$cpu_now'(T),
|
||||
time_state(ID, T0, I0),
|
||||
cputime_inferences(T, I),
|
||||
Time is T - T0,
|
||||
Inferences0 is I - I0,
|
||||
% we must subtract the number of inferences that time/1 itself takes;
|
||||
% this may have to be adapted if the implementation changes,
|
||||
% so that (for example) true/1 takes exactly 1 inference.
|
||||
( bb_get('$answer_count', 0) ->
|
||||
Inferences is Inferences0 - 60,
|
||||
Pre = " ", Post = ""
|
||||
; Pre = "", Post = " "
|
||||
; Inferences is Inferences0 - 9,
|
||||
Pre = "", Post = " "
|
||||
),
|
||||
format("~s% CPU time: ~3fs~n~s", [Pre,Time,Post]).
|
||||
phrase((Pre,"% CPU time: ", format_("~3f", [Time]), "s, ",
|
||||
format_("~U", [Inferences])," inference",s_if_necessary(Inferences),"\n",
|
||||
Post), Cs),
|
||||
format("~s", [Cs]).
|
||||
|
||||
s_if_necessary(Inferences) -->
|
||||
{ compare(C, 1, Inferences) },
|
||||
s_(C).
|
||||
|
||||
s_(=) --> "".
|
||||
s_(<) --> "s".
|
||||
s_(>) --> " (exception?)".
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
?- time((true;false)).
|
||||
%@ % CPU time: 0.006s
|
||||
%@ true
|
||||
%@ ; % CPU time: 0.001s
|
||||
%@ false.
|
||||
% CPU time: 0.000s, 1 inference
|
||||
true
|
||||
; % CPU time: 0.000s, 0 inference (exception?)
|
||||
false.
|
||||
|
||||
:- time(use_module(library(clpz))).
|
||||
%@ % CPU time: 3.711s
|
||||
%@ true.
|
||||
% CPU time: 0.343s, 409_874 inferences
|
||||
true.
|
||||
|
||||
:- time(use_module(library(lists))).
|
||||
%@ % CPU time: 0.006s
|
||||
%@ true.
|
||||
% CPU time: 0.000s, 19 inferences
|
||||
true.
|
||||
|
||||
?- time(member(X, "abc")).
|
||||
%@ % CPU time: 0.005s
|
||||
%@ X = a
|
||||
%@ ; % CPU time: 0.000s
|
||||
%@ X = b
|
||||
%@ ; % CPU time: 0.000s
|
||||
%@ X = c
|
||||
%@ ; % CPU time: 0.000s
|
||||
%@ false.
|
||||
% CPU time: 0.000s, 1 inference
|
||||
X = a
|
||||
; % CPU time: 0.000s, 3 inferences
|
||||
X = b
|
||||
; % CPU time: 0.000s, 3 inferences
|
||||
X = c.
|
||||
|
||||
?- time((repeat,false)).
|
||||
% CPU time: 2.726s, 53_330_502 inferences
|
||||
error('$interrupt_thrown',repl/0).
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
29
src/lib/wasm.pl
Normal file
29
src/lib/wasm.pl
Normal file
@@ -0,0 +1,29 @@
|
||||
/** Predicates for the WebAssembly platform
|
||||
|
||||
This module contains predicates that are only available in
|
||||
the WASM (WebAssembly) version of Scryer Prolog.
|
||||
*/
|
||||
|
||||
:- module(wasm, [js_eval/2]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
|
||||
%% js_eval(+JsCode, -Result).
|
||||
%
|
||||
% Executes a JavaScript snippet `JsCode` using the platform
|
||||
% `eval` function. `Result` takes the return value of that code.
|
||||
% Strings, booleans, numbers, null and undefined are directly mapped to Prolog.
|
||||
% Arrays, objects, bigints, symbols and functions are not mapped.
|
||||
% Instead, a `js_{type}` atom will be returned.
|
||||
%
|
||||
% Example (on a browser):
|
||||
%
|
||||
% ```
|
||||
% ?- js_eval("prompt('What is your name?')", Name).
|
||||
% % A prompt is showed, with a textbox.
|
||||
% Name = "Whatever was written on the textbox".
|
||||
% ```
|
||||
js_eval(JsCode, Result) :-
|
||||
must_be(chars, JsCode),
|
||||
can_be(chars, Result),
|
||||
'$js_eval'(JsCode, Result).
|
||||
106
src/lib/when.pl
Normal file
106
src/lib/when.pl
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
Provides the predicate `when/2`.
|
||||
*/
|
||||
|
||||
:- module(when, [when/2]).
|
||||
|
||||
:- use_module(library(atts)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(lambda)).
|
||||
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(debug)).
|
||||
|
||||
:- attribute when_list/1.
|
||||
|
||||
:- meta_predicate(when(+, 0)).
|
||||
|
||||
%% when(Condition, Goal).
|
||||
%
|
||||
% Executes Goal when Condition becomes true.
|
||||
when(Condition, Goal) :-
|
||||
( when_condition(Condition) ->
|
||||
( Condition ->
|
||||
Goal
|
||||
; term_variables(Condition, Vars),
|
||||
maplist(
|
||||
[Goal, Condition]+\Var^(
|
||||
get_atts(Var, when_list(Whens0)) ->
|
||||
Whens = [when(Condition, Goal) | Whens0],
|
||||
put_atts(Var, when_list(Whens))
|
||||
; put_atts(Var, when_list([when(Condition, Goal)]))
|
||||
),
|
||||
Vars
|
||||
)
|
||||
)
|
||||
; throw(error(domain_error(when_condition, Condition),_))
|
||||
).
|
||||
|
||||
when_condition(Cond) :-
|
||||
% Should this be delayed?
|
||||
var(Cond), !, throw(error(instantiation_error,when_condition/1)).
|
||||
when_condition(ground(_)).
|
||||
when_condition(nonvar(_)).
|
||||
when_condition((A, B)) :-
|
||||
when_condition(A),
|
||||
when_condition(B).
|
||||
when_condition((A ; B)) :-
|
||||
when_condition(A),
|
||||
when_condition(B).
|
||||
|
||||
remove_goal([], _, []).
|
||||
remove_goal([G0|G0s], Goal, Goals) :-
|
||||
( G0 == Goal ->
|
||||
remove_goal(G0s, Goal, Goals)
|
||||
; Goals = [G0|Goals1],
|
||||
remove_goal(G0s, Goal, Goals1)
|
||||
).
|
||||
|
||||
vars_remove_goal(Vars, Goal) :-
|
||||
maplist(
|
||||
Goal+\Var^(
|
||||
get_atts(Var, when_list(Whens0)) ->
|
||||
remove_goal(Whens0, Goal, Whens),
|
||||
( Whens = [] ->
|
||||
put_atts(Var, -when_list(_))
|
||||
; put_atts(Var, when_list(Whens))
|
||||
)
|
||||
; true
|
||||
),
|
||||
Vars
|
||||
).
|
||||
|
||||
reinforce_goal(Goal0, Goal) :-
|
||||
Goal = (
|
||||
term_variables(Goal0, Vars),
|
||||
when:vars_remove_goal(Vars, Goal0),
|
||||
Goal0
|
||||
).
|
||||
|
||||
verify_attributes(Var, Value, Goals) :-
|
||||
( get_atts(Var, when_list(Whens)) ->
|
||||
( var(Value) ->
|
||||
( get_atts(Value, when_list(WhensValue)) ->
|
||||
append(Whens, WhensValue, WhensNew),
|
||||
put_atts(Value, when_list(WhensNew))
|
||||
; put_atts(Value, when_list(Whens))
|
||||
),
|
||||
Goals = []
|
||||
; maplist(reinforce_goal, Whens, Goals)
|
||||
)
|
||||
; Goals = []
|
||||
).
|
||||
|
||||
gather_when_goals([], _) --> [].
|
||||
gather_when_goals([When|Whens], Var) -->
|
||||
( { term_variables(When, [V0|_]), Var == V0 } ->
|
||||
[when:When]
|
||||
; []
|
||||
),
|
||||
gather_when_goals(Whens, Var).
|
||||
|
||||
attribute_goals(Var) -->
|
||||
{ get_atts(Var, when_list(Whens)) },
|
||||
gather_when_goals(Whens, Var),
|
||||
{ put_atts(Var, -when_list(_)) }.
|
||||
388
src/loader.pl
388
src/loader.pl
@@ -35,9 +35,9 @@ write_error(Error) :-
|
||||
),
|
||||
write('.').
|
||||
|
||||
% '$print_message_and_fail'(inference_limit_exceeded(B)) :-
|
||||
% integer(B),
|
||||
% throw(inference_limit_exceeded(B)).
|
||||
|
||||
:- non_counted_backtracking '$print_message_and_fail'/1.
|
||||
|
||||
'$print_message_and_fail'(Error) :-
|
||||
write_error(Error),
|
||||
nl,
|
||||
@@ -70,6 +70,8 @@ term_expansion_list([Term|Terms], ExpandedTermsHead, ExpandedTermsTail) :-
|
||||
).
|
||||
|
||||
|
||||
:- non_counted_backtracking goal_expansion/3.
|
||||
|
||||
goal_expansion(Goal, Module, ExpandedGoal) :-
|
||||
( atom(Module),
|
||||
'$predicate_defined'(Module, goal_expansion, 2),
|
||||
@@ -84,7 +86,6 @@ goal_expansion(Goal, Module, ExpandedGoal) :-
|
||||
).
|
||||
|
||||
|
||||
|
||||
create_file_load_context(Stream, Path, Evacuable) :-
|
||||
'$push_load_context'(Stream, Path),
|
||||
'$push_load_state_payload'(Evacuable).
|
||||
@@ -111,7 +112,7 @@ success_or_warning(Goal) :-
|
||||
( call(Goal) ->
|
||||
true
|
||||
; %% initialization goals can fail without thwarting the load.
|
||||
write('Warning: initialization/1 failed for: '),
|
||||
write('% Warning: initialization/1 failed for: '),
|
||||
writeq(Goal),
|
||||
nl
|
||||
).
|
||||
@@ -134,9 +135,15 @@ file_load_init(Stream, Evacuable) :-
|
||||
run_initialization_goals.
|
||||
|
||||
file_load_cleanup(Evacuable, Error) :-
|
||||
load_context(Module),
|
||||
abolish(Module:'$initialization_goals'/1),
|
||||
unload_evacuable(Evacuable),
|
||||
'$print_message_and_fail'(Error),
|
||||
throw(Error).
|
||||
( clause('$toplevel':started, _) ->
|
||||
% let the toplevel call loader:write_error/1
|
||||
throw(Error)
|
||||
; '$print_message_and_fail'(Error)
|
||||
; throw(file_load_error)
|
||||
).
|
||||
|
||||
file_load(Stream, Path, Evacuable) :-
|
||||
create_file_load_context(Stream, Path, Evacuable),
|
||||
@@ -181,7 +188,7 @@ warn_about_singletons([], _).
|
||||
warn_about_singletons([Singleton|Singletons], LinesRead) :-
|
||||
( filter_anonymous_vars([Singleton|Singletons], VarEqs),
|
||||
VarEqs \== [] ->
|
||||
write('Warning: singleton variables '),
|
||||
write('% Warning: singleton variables '),
|
||||
print_comma_separated_list(VarEqs),
|
||||
write(' at line '),
|
||||
write(LinesRead),
|
||||
@@ -224,14 +231,17 @@ complete_partial_goal(N, HeadArg, InnerHeadArgs, SuppArgs, CompleteHeadArg) :-
|
||||
integer(N),
|
||||
N >= 0,
|
||||
HeadArg =.. [Functor | InnerHeadArgs],
|
||||
% the next two lines are equivalent to length(SuppArgs, N) but
|
||||
% avoid length/2 so that copy_term/3 (which is invoked by
|
||||
% length/2) can be bootstrapped without self-reference.
|
||||
functor(SuppArgsFunctor, '.', N),
|
||||
SuppArgsFunctor =.. [_ | SuppArgs],
|
||||
% length(SuppArgs, N),
|
||||
append(InnerHeadArgs, SuppArgs, InnerHeadArgs0),
|
||||
CompleteHeadArg =.. [Functor | InnerHeadArgs0].
|
||||
( callable(Functor) ->
|
||||
% the next two lines are equivalent to length(SuppArgs, N) but
|
||||
% avoid length/2 so that copy_term/3 (which is invoked by
|
||||
% length/2) can be bootstrapped without self-reference.
|
||||
functor(SuppArgsFunctor, '.', N),
|
||||
SuppArgsFunctor =.. [_ | SuppArgs],
|
||||
% length(SuppArgs, N),
|
||||
append(InnerHeadArgs, SuppArgs, InnerHeadArgs0),
|
||||
CompleteHeadArg =.. [Functor | InnerHeadArgs0]
|
||||
; type_error(callable, Functor, _)
|
||||
).
|
||||
|
||||
inner_meta_specs(0, HeadArg, InnerHeadArgs, InnerMetaSpecs) :-
|
||||
!,
|
||||
@@ -276,7 +286,7 @@ module_expanded_head_variables(Head, HeadVars) :-
|
||||
|
||||
print_goal_expansion_warning(Pred) :-
|
||||
nl,
|
||||
write('Warning: clause body goal expansion failed because '),
|
||||
write('% Warning: clause body goal expansion failed because '),
|
||||
writeq(Pred),
|
||||
write(' is not callable.'),
|
||||
nl.
|
||||
@@ -289,7 +299,7 @@ expand_term_goals(Terms0, Terms) :-
|
||||
( atom(Module) ->
|
||||
prolog_load_context(module, Target),
|
||||
module_expanded_head_variables(Head2, HeadVars),
|
||||
catch(expand_goal(Body0, Target, Body1, HeadVars),
|
||||
catch(expand_goal(Body0, Target, Body1, HeadVars, []),
|
||||
error(type_error(callable, Pred), _),
|
||||
( loader:print_goal_expansion_warning(Pred),
|
||||
builtins:(Body1 = Body0)
|
||||
@@ -299,7 +309,7 @@ expand_term_goals(Terms0, Terms) :-
|
||||
)
|
||||
; module_expanded_head_variables(Head1, HeadVars),
|
||||
prolog_load_context(module, Target),
|
||||
catch(expand_goal(Body0, Target, Body1, HeadVars),
|
||||
catch(expand_goal(Body0, Target, Body1, HeadVars, []),
|
||||
error(type_error(callable, Pred), _),
|
||||
( loader:print_goal_expansion_warning(Pred),
|
||||
builtins:(Body1 = Body0)
|
||||
@@ -373,6 +383,59 @@ remove_module(Module, Evacuable) :-
|
||||
).
|
||||
|
||||
|
||||
predicate_indicator(PI) :-
|
||||
( var(PI) ->
|
||||
throw(error(instantiation_error, _))
|
||||
; PI = Name/Arity,
|
||||
must_be(atom, Name),
|
||||
must_be(integer, Arity),
|
||||
Arity >= 0
|
||||
).
|
||||
|
||||
predicate_indicator_sequence(PI_Seq) :-
|
||||
( var(PI_Seq) ->
|
||||
throw(error(instantiation_error, load/1))
|
||||
; PI_Seq = (PI, PIs),
|
||||
predicate_indicator(PI),
|
||||
( predicate_indicator(PIs) ->
|
||||
true
|
||||
; predicate_indicator_sequence(PIs)
|
||||
)
|
||||
).
|
||||
|
||||
:- meta_predicate add_predicate_declaration(3, ?).
|
||||
|
||||
add_predicate_declaration(Handler, Name/Arity) :-
|
||||
predicate_indicator(Name/Arity),
|
||||
prolog_load_context(module, Module),
|
||||
call(Handler, Module, Name, Arity).
|
||||
add_predicate_declaration(Handler, Module:Name/Arity) :-
|
||||
must_be(atom, Module),
|
||||
predicate_indicator(Name/Arity),
|
||||
call(Handler, Module, Name, Arity).
|
||||
add_predicate_declaration(Handler, [PI|PIs]) :-
|
||||
'$skip_max_list'(_, _, PIs, Tail),
|
||||
( Tail == [],
|
||||
maplist(loader:predicate_indicator, PIs) ->
|
||||
maplist(loader:add_predicate_declaration(Handler), [PI|PIs])
|
||||
; throw(error(type_error(predicate_indicator_list, [PI|PIs]), load/1))
|
||||
).
|
||||
add_predicate_declaration(Handler, (PI, PIs)) :-
|
||||
( predicate_indicator_sequence((PI, PIs)) ->
|
||||
add_predicate_declaration(Handler, PI),
|
||||
add_predicate_declaration(Handler, PIs)
|
||||
; throw(error(type_error(predicate_indicator_sequence, (PI, PIs)), load/1))
|
||||
).
|
||||
|
||||
add_dynamic_predicate(Evacuable, Module, Name, Arity) :-
|
||||
'$add_dynamic_predicate'(Module, Name, Arity, Evacuable).
|
||||
|
||||
add_multifile_predicate(Evacuable, Module, Name, Arity) :-
|
||||
'$add_multifile_predicate'(Module, Name, Arity, Evacuable).
|
||||
|
||||
add_discontiguous_predicate(Evacuable, Module, Name, Arity) :-
|
||||
'$add_discontiguous_predicate'(Module, Name, Arity, Evacuable).
|
||||
|
||||
compile_declaration(use_module(Module), Evacuable) :-
|
||||
use_module(Module, [], Evacuable).
|
||||
compile_declaration(use_module(Module, Exports), Evacuable) :-
|
||||
@@ -385,39 +448,12 @@ compile_declaration(module(Module, Exports), Evacuable) :-
|
||||
'$declare_module'(Module, Exports, Evacuable)
|
||||
; type_error(atom, Module, load/1)
|
||||
).
|
||||
compile_declaration(dynamic(Module:Name/Arity), Evacuable) :-
|
||||
!,
|
||||
must_be(atom, Module),
|
||||
must_be(atom, Name),
|
||||
must_be(integer, Arity),
|
||||
'$add_dynamic_predicate'(Module, Name, Arity, Evacuable).
|
||||
compile_declaration(dynamic(Name/Arity), Evacuable) :-
|
||||
must_be(atom, Name),
|
||||
must_be(integer, Arity),
|
||||
prolog_load_context(module, Module),
|
||||
'$add_dynamic_predicate'(Module, Name, Arity, Evacuable).
|
||||
compile_declaration(multifile(Module:Name/Arity), Evacuable) :-
|
||||
!,
|
||||
must_be(atom, Module),
|
||||
must_be(atom, Name),
|
||||
must_be(integer, Arity),
|
||||
'$add_multifile_predicate'(Module, Name, Arity, Evacuable).
|
||||
compile_declaration(multifile(Name/Arity), Evacuable) :-
|
||||
must_be(atom, Name),
|
||||
must_be(integer, Arity),
|
||||
prolog_load_context(module, Module),
|
||||
'$add_multifile_predicate'(Module, Name, Arity, Evacuable).
|
||||
compile_declaration(discontiguous(Module:Name/Arity), Evacuable) :-
|
||||
!,
|
||||
must_be(atom, Module),
|
||||
must_be(atom, Name),
|
||||
must_be(integer, Arity),
|
||||
'$add_discontiguous_predicate'(Module, Name, Arity, Evacuable).
|
||||
compile_declaration(discontiguous(Name/Arity), Evacuable) :-
|
||||
must_be(atom, Name),
|
||||
must_be(integer, Arity),
|
||||
prolog_load_context(module, Module),
|
||||
'$add_discontiguous_predicate'(Module, Name, Arity, Evacuable).
|
||||
compile_declaration(dynamic(PIs), Evacuable) :-
|
||||
add_predicate_declaration(loader:add_dynamic_predicate(Evacuable), PIs).
|
||||
compile_declaration(multifile(PIs), Evacuable) :-
|
||||
add_predicate_declaration(loader:add_multifile_predicate(Evacuable), PIs).
|
||||
compile_declaration(discontiguous(PIs), Evacuable) :-
|
||||
add_predicate_declaration(loader:add_discontiguous_predicate(Evacuable), PIs).
|
||||
compile_declaration(initialization(Goal), Evacuable) :-
|
||||
prolog_load_context(module, Module),
|
||||
assertz(Module:'$initialization_goals'(Goal)).
|
||||
@@ -504,7 +540,9 @@ consult(Item) :-
|
||||
|
||||
use_module(Module) :-
|
||||
'$push_load_state_payload'(Evacuable),
|
||||
use_module(Module, [], Evacuable).
|
||||
catch('$call'(loader:use_module(Module, [], Evacuable)),
|
||||
file_load_error,
|
||||
'$call'(builtins:false)).
|
||||
|
||||
use_module(Module, Exports) :-
|
||||
'$push_load_state_payload'(Evacuable),
|
||||
@@ -586,6 +624,12 @@ use_module(Module, Exports, Evacuable) :-
|
||||
)
|
||||
).
|
||||
|
||||
consult_stream(Stream, PathFileName) :-
|
||||
'$push_load_state_payload'(Evacuable),
|
||||
file_load(Stream, PathFileName, Subevacuable),
|
||||
'$use_module'(Evacuable, Subevacuable, _).
|
||||
|
||||
:- non_counted_backtracking check_predicate_property/5.
|
||||
|
||||
check_predicate_property(meta_predicate, Module, Name, Arity, MetaPredicateTerm) :-
|
||||
'$meta_predicate_property'(Module, Name, Arity, MetaPredicateTerm).
|
||||
@@ -599,6 +643,7 @@ check_predicate_property(discontiguous, Module, Name, Arity, discontiguous) :-
|
||||
'$discontiguous_property'(Module, Name, Arity).
|
||||
|
||||
|
||||
:- non_counted_backtracking extract_predicate_property/2.
|
||||
|
||||
extract_predicate_property(Property, PropertyType) :-
|
||||
( var(Property) ->
|
||||
@@ -606,12 +651,16 @@ extract_predicate_property(Property, PropertyType) :-
|
||||
; functor(Property, PropertyType, _)
|
||||
).
|
||||
|
||||
:- non_counted_backtracking load_context/1.
|
||||
|
||||
load_context(Module) :-
|
||||
( prolog_load_context(module, Module) ->
|
||||
true
|
||||
; Module = user
|
||||
).
|
||||
|
||||
:- non_counted_backtracking predicate_property/2.
|
||||
|
||||
predicate_property(Callable, Property) :-
|
||||
( var(Callable) ->
|
||||
instantiation_error(predicate_property/2)
|
||||
@@ -648,8 +697,6 @@ strip_subst_module(Goal, M1, M2, G) :-
|
||||
; true
|
||||
).
|
||||
|
||||
:- non_counted_backtracking subgoal_expansion/3.
|
||||
|
||||
/*
|
||||
* subgoal_expansion differs from goal_expansion only in that it fails
|
||||
* unconditionally after catching an(y) exception, aborting the
|
||||
@@ -657,10 +704,14 @@ strip_subst_module(Goal, M1, M2, G) :-
|
||||
* when expand_goal is later invoked at runtime.
|
||||
*/
|
||||
|
||||
:- non_counted_backtracking subgoal_expansion_fail/1.
|
||||
|
||||
subgoal_expansion_fail(B) :-
|
||||
builtins:set_cp(B),
|
||||
fail.
|
||||
|
||||
:- non_counted_backtracking subgoal_expansion/3.
|
||||
|
||||
subgoal_expansion(Goal, Module, ExpandedGoal) :-
|
||||
'$get_cp'(B),
|
||||
( atom(Module),
|
||||
@@ -678,9 +729,9 @@ subgoal_expansion(Goal, Module, ExpandedGoal) :-
|
||||
).
|
||||
|
||||
|
||||
:- non_counted_backtracking expand_subgoal/5.
|
||||
:- non_counted_backtracking expand_subgoal/6.
|
||||
|
||||
expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars) :-
|
||||
expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars, TGs) :-
|
||||
strip_subst_module(UnexpandedGoals, M, Module, UnexpandedGoals0),
|
||||
nonvar(UnexpandedGoals0),
|
||||
complete_partial_goal(MS, UnexpandedGoals0, _, SuppArgs, UnexpandedGoals1),
|
||||
@@ -692,10 +743,10 @@ expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars) :-
|
||||
),
|
||||
strip_subst_module(UnexpandedGoals3, Module, Module1, UnexpandedGoals4),
|
||||
( inner_meta_specs(0, UnexpandedGoals4, _, MetaSpecs) ->
|
||||
expand_module_names(UnexpandedGoals4, MetaSpecs, Module1, ExpandedGoals0, HeadVars)
|
||||
expand_module_names(UnexpandedGoals4, MetaSpecs, Module1, ExpandedGoals0, HeadVars, TGs)
|
||||
; ExpandedGoals0 = UnexpandedGoals4
|
||||
),
|
||||
'$compile_inline_or_expanded_goal'(ExpandedGoals0, SuppArgs, ExpandedGoals1, Module1),
|
||||
'$compile_inline_or_expanded_goal'(ExpandedGoals0, SuppArgs, ExpandedGoals1, Module1, UnexpandedGoals0),
|
||||
expand_module_name(ExpandedGoals1, MS, Module1, ExpandedGoals).
|
||||
|
||||
|
||||
@@ -721,10 +772,10 @@ expand_module_name(ESG0, MS, M, ESG) :-
|
||||
|
||||
:- non_counted_backtracking eq_member/2.
|
||||
|
||||
eq_member(V, [L-_|Ls]) :-
|
||||
eq_member(V-M, [L-M|Ls]) :-
|
||||
V == L.
|
||||
eq_member(V, [_|Ls]) :-
|
||||
eq_member(V, Ls).
|
||||
eq_member(V-M, [_|Ls]) :-
|
||||
eq_member(V-M, Ls).
|
||||
|
||||
:- non_counted_backtracking qualified_spec/1.
|
||||
|
||||
@@ -734,11 +785,19 @@ qualified_spec(MS) :- integer(MS), MS >= 0.
|
||||
|
||||
:- non_counted_backtracking expand_meta_predicate_subgoals/5.
|
||||
|
||||
expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars) :-
|
||||
expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars, TGs) :-
|
||||
( var(SG) ->
|
||||
( qualified_spec(MS) ->
|
||||
( eq_member(SG, HeadVars) ->
|
||||
( eq_member(SG-_, HeadVars) ->
|
||||
ESG = SG
|
||||
; eq_member(SG-TG, TGs),
|
||||
% transitive goals come about from previous equalities:
|
||||
% if SG was bound by (=)/2 to a potential goal TG earlier
|
||||
% in the goal sequence, expand TG and substitute SG with it
|
||||
% in this subgoal context. the binding to SG must not be
|
||||
% changed.
|
||||
expand_subgoal(TG, MS, M, ESG, HeadVars, TGs) ->
|
||||
true
|
||||
; expand_module_name(SG, MS, M, ESG)
|
||||
)
|
||||
; ESG = SG
|
||||
@@ -747,26 +806,26 @@ expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars
|
||||
expand_module_name(SG, MS, M, ESG)
|
||||
; '$is_expanded_or_inlined'(SG) ->
|
||||
ESG = SG
|
||||
; expand_subgoal(SG, MS, M, ESG, HeadVars) ->
|
||||
; expand_subgoal(SG, MS, M, ESG, HeadVars, TGs) ->
|
||||
true
|
||||
; integer(MS),
|
||||
MS >= 0 ->
|
||||
expand_module_name(SG, MS, M, ESG)
|
||||
; SG = ESG
|
||||
),
|
||||
expand_meta_predicate_subgoals(SGs, MSs, M, ESGs, HeadVars).
|
||||
expand_meta_predicate_subgoals(SGs, MSs, M, ESGs, HeadVars, TGs).
|
||||
|
||||
expand_meta_predicate_subgoals([], _, _, [], _).
|
||||
expand_meta_predicate_subgoals([], _, _, [], _, _).
|
||||
|
||||
:- non_counted_backtracking expand_module_names/5.
|
||||
:- non_counted_backtracking expand_module_names/6.
|
||||
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) :-
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
Goals =.. [GoalFunctor | SubGoals],
|
||||
( GoalFunctor == (:),
|
||||
SubGoals = [M, SubGoal] ->
|
||||
expand_module_names(SubGoal, MetaSpecs, M, ExpandedSubGoal, HeadVars),
|
||||
expand_module_names(SubGoal, MetaSpecs, M, ExpandedSubGoal, HeadVars, TGs),
|
||||
expand_module_name(ExpandedSubGoal, 0, M, ExpandedGoals)
|
||||
; expand_meta_predicate_subgoals(SubGoals, MetaSpecs, Module, ExpandedGoalList, HeadVars),
|
||||
; expand_meta_predicate_subgoals(SubGoals, MetaSpecs, Module, ExpandedGoalList, HeadVars, TGs),
|
||||
ExpandedGoals =.. [GoalFunctor | ExpandedGoalList]
|
||||
).
|
||||
|
||||
@@ -774,26 +833,26 @@ expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) :-
|
||||
:- non_counted_backtracking expand_goal/3.
|
||||
|
||||
expand_goal(UnexpandedGoals, Module, ExpandedGoals) :-
|
||||
catch(loader:expand_goal(UnexpandedGoals, Module, ExpandedGoals, []),
|
||||
catch(loader:expand_goal(UnexpandedGoals, Module, ExpandedGoals, [], []),
|
||||
error(type_error(callable, _), _),
|
||||
UnexpandedGoals = ExpandedGoals),
|
||||
!.
|
||||
|
||||
:- non_counted_backtracking expand_goal/4.
|
||||
:- non_counted_backtracking expand_goal/5.
|
||||
|
||||
expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
( var(UnexpandedGoals) ->
|
||||
expand_module_names(call(UnexpandedGoals), [0], Module, ExpandedGoals, HeadVars)
|
||||
expand_module_names(call(UnexpandedGoals), [0], Module, ExpandedGoals, HeadVars, TGs)
|
||||
; goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1),
|
||||
( Module \== user ->
|
||||
goal_expansion(UnexpandedGoals1, user, Goals)
|
||||
; Goals = UnexpandedGoals1
|
||||
),
|
||||
( expand_goal_cases(Goals, Module, ExpandedGoals, HeadVars) ->
|
||||
( expand_goal_cases(Goals, Module, ExpandedGoals, HeadVars, TGs) ->
|
||||
true
|
||||
; predicate_property(Module:Goals, meta_predicate(MetaSpecs0)),
|
||||
MetaSpecs0 =.. [_ | MetaSpecs] ->
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars)
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars, TGs)
|
||||
; thread_goals(Goals, ExpandedGoals, (','))
|
||||
; Goals = ExpandedGoals
|
||||
)
|
||||
@@ -826,28 +885,40 @@ expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals) :-
|
||||
)
|
||||
).
|
||||
|
||||
:- non_counted_backtracking expand_goal_cases/4.
|
||||
:- non_counted_backtracking transitive_goal/3.
|
||||
|
||||
expand_goal_cases((Goal0, Goals0), Module, ExpandedGoals, HeadVars) :-
|
||||
( expand_goal(Goal0, Module, Goal1, HeadVars) ->
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars),
|
||||
transitive_goal(G, TGs0, TGs1) :-
|
||||
( G = (G1 = PotentialGoal),
|
||||
callable(PotentialGoal),
|
||||
subsumes_term(G1, PotentialGoal) ->
|
||||
TGs1 = [G1-PotentialGoal|TGs0]
|
||||
; TGs1 = TGs0
|
||||
).
|
||||
|
||||
:- non_counted_backtracking expand_goal_cases/5.
|
||||
|
||||
expand_goal_cases((Goal0, Goals0), Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
( expand_goal(Goal0, Module, Goal1, HeadVars, TGs) ->
|
||||
transitive_goal(Goal0, TGs, TGs1),
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars, TGs1),
|
||||
thread_goals(Goal1, ExpandedGoals, Goals1, (','))
|
||||
; expand_goal(Goals0, Module, Goals1, HeadVars),
|
||||
; expand_goal(Goals0, Module, Goals1, HeadVars, TGs),
|
||||
ExpandedGoals = (Goal0, Goals1)
|
||||
).
|
||||
expand_goal_cases((Goals0 -> Goals1), Module, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars),
|
||||
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars),
|
||||
expand_goal_cases((Goals0 -> Goals1), Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars, TGs),
|
||||
transitive_goal(ExpandedGoals0, TGs, TGs1),
|
||||
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars, TGs1),
|
||||
ExpandedGoals = (ExpandedGoals0 -> ExpandedGoals1).
|
||||
expand_goal_cases((Goals0 ; Goals1), Module, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars),
|
||||
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars),
|
||||
expand_goal_cases((Goals0 ; Goals1), Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars, TGs),
|
||||
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars, TGs),
|
||||
ExpandedGoals = (ExpandedGoals0 ; ExpandedGoals1).
|
||||
expand_goal_cases((\+ Goals0), Module, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars),
|
||||
expand_goal_cases((\+ Goals0), Module, ExpandedGoals, HeadVars, TGs) :-
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars, TGs),
|
||||
ExpandedGoals = (\+ Goals1).
|
||||
expand_goal_cases((Module:Goals0), _, ExpandedGoals, HeadVars) :-
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars),
|
||||
expand_goal_cases((Module:Goals0), _, ExpandedGoals, HeadVars, TGs) :-
|
||||
expand_goal(Goals0, Module, Goals1, HeadVars, TGs),
|
||||
ExpandedGoals = (Module:Goals1).
|
||||
|
||||
:- non_counted_backtracking thread_goals/3.
|
||||
@@ -890,7 +961,6 @@ thread_goals(Goals0, Goals1, Hole, Functor) :-
|
||||
% n_call_clause(N, Clauses) :-
|
||||
% length(Args, N),
|
||||
% Head =.. [call, G | Args],
|
||||
% CallNHead =.. [call, '$call'(G) | Args],
|
||||
% N1 is N + 1,
|
||||
% StripModule =.. ['$strip_module', G, M1, G1],
|
||||
% FastCall =.. ['$fast_call', G | Args],
|
||||
@@ -903,7 +973,7 @@ thread_goals(Goals0, Goals1, Hole, Functor) :-
|
||||
% PrepareCallClause,
|
||||
% expand_call_goal(G2, M1, G3),
|
||||
% strip_subst_module(G3, M1, M2, G4),
|
||||
% '$call_with_inference_counting'(ModuleCall))].
|
||||
% ModuleCall)].
|
||||
%
|
||||
% generate_call_forms :-
|
||||
% between(1, 64, N),
|
||||
@@ -930,7 +1000,7 @@ call(G0) :-
|
||||
'$strip_module'(G0, M0, G1),
|
||||
expand_call_goal(G1, M0, G2),
|
||||
strip_subst_module(G2, M0, M1, G3),
|
||||
'$call_with_inference_counting'('$module_call'(M1, G3)).
|
||||
'$module_call'(M1, G3).
|
||||
|
||||
:-non_counted_backtracking call/2.
|
||||
call(A,B) :-
|
||||
@@ -943,7 +1013,7 @@ call(A,B) :-
|
||||
'$prepare_call_clause'(E,D,B),
|
||||
expand_call_goal(E,C,F),
|
||||
strip_subst_module(F,C,G,H),
|
||||
'$call_with_inference_counting'('$module_call'(G,H)).
|
||||
'$module_call'(G,H).
|
||||
|
||||
:-non_counted_backtracking call/3.
|
||||
call(A,B,C) :-
|
||||
@@ -956,7 +1026,7 @@ call(A,B,C) :-
|
||||
'$prepare_call_clause'(F,E,B,C),
|
||||
expand_call_goal(F,D,G),
|
||||
strip_subst_module(G,D,H,I),
|
||||
'$call_with_inference_counting'('$module_call'(H,I)).
|
||||
'$module_call'(H,I).
|
||||
|
||||
:-non_counted_backtracking call/4.
|
||||
call(A,B,C,D) :-
|
||||
@@ -969,7 +1039,7 @@ call(A,B,C,D) :-
|
||||
'$prepare_call_clause'(G,F,B,C,D),
|
||||
expand_call_goal(G,E,H),
|
||||
strip_subst_module(H,E,I,J),
|
||||
'$call_with_inference_counting'('$module_call'(I,J)).
|
||||
'$module_call'(I,J).
|
||||
|
||||
:-non_counted_backtracking call/5.
|
||||
call(A,B,C,D,E) :-
|
||||
@@ -982,7 +1052,7 @@ call(A,B,C,D,E) :-
|
||||
'$prepare_call_clause'(H,G,B,C,D,E),
|
||||
expand_call_goal(H,F,I),
|
||||
strip_subst_module(I,F,J,K),
|
||||
'$call_with_inference_counting'('$module_call'(J,K)).
|
||||
'$module_call'(J,K).
|
||||
|
||||
:-non_counted_backtracking call/6.
|
||||
call(A,B,C,D,E,F) :-
|
||||
@@ -995,7 +1065,7 @@ call(A,B,C,D,E,F) :-
|
||||
'$prepare_call_clause'(I,H,B,C,D,E,F),
|
||||
expand_call_goal(I,G,J),
|
||||
strip_subst_module(J,G,K,L),
|
||||
'$call_with_inference_counting'('$module_call'(K,L)).
|
||||
'$module_call'(K,L).
|
||||
|
||||
:-non_counted_backtracking call/7.
|
||||
call(A,B,C,D,E,F,G) :-
|
||||
@@ -1008,7 +1078,7 @@ call(A,B,C,D,E,F,G) :-
|
||||
'$prepare_call_clause'(J,I,B,C,D,E,F,G),
|
||||
expand_call_goal(J,H,K),
|
||||
strip_subst_module(K,H,L,M),
|
||||
'$call_with_inference_counting'('$module_call'(L,M)).
|
||||
'$module_call'(L,M).
|
||||
|
||||
:-non_counted_backtracking call/8.
|
||||
call(A,B,C,D,E,F,G,H) :-
|
||||
@@ -1021,7 +1091,7 @@ call(A,B,C,D,E,F,G,H) :-
|
||||
'$prepare_call_clause'(K,J,B,C,D,E,F,G,H),
|
||||
expand_call_goal(K,I,L),
|
||||
strip_subst_module(L,I,M,N),
|
||||
'$call_with_inference_counting'('$module_call'(M,N)).
|
||||
'$module_call'(M,N).
|
||||
|
||||
:-non_counted_backtracking call/9.
|
||||
call(A,B,C,D,E,F,G,H,I) :-
|
||||
@@ -1034,7 +1104,7 @@ call(A,B,C,D,E,F,G,H,I) :-
|
||||
'$prepare_call_clause'(L,K,B,C,D,E,F,G,H,I),
|
||||
expand_call_goal(L,J,M),
|
||||
strip_subst_module(M,J,N,O),
|
||||
'$call_with_inference_counting'('$module_call'(N,O)).
|
||||
'$module_call'(N,O).
|
||||
|
||||
:-non_counted_backtracking call/10.
|
||||
call(A,B,C,D,E,F,G,H,I,J) :-
|
||||
@@ -1047,7 +1117,7 @@ call(A,B,C,D,E,F,G,H,I,J) :-
|
||||
'$prepare_call_clause'(M,L,B,C,D,E,F,G,H,I,J),
|
||||
expand_call_goal(M,K,N),
|
||||
strip_subst_module(N,K,O,P),
|
||||
'$call_with_inference_counting'('$module_call'(O,P)).
|
||||
'$module_call'(O,P).
|
||||
|
||||
:-non_counted_backtracking call/11.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K) :-
|
||||
@@ -1060,7 +1130,7 @@ call(A,B,C,D,E,F,G,H,I,J,K) :-
|
||||
'$prepare_call_clause'(N,M,B,C,D,E,F,G,H,I,J,K),
|
||||
expand_call_goal(N,L,O),
|
||||
strip_subst_module(O,L,P,Q),
|
||||
'$call_with_inference_counting'('$module_call'(P,Q)).
|
||||
'$module_call'(P,Q).
|
||||
|
||||
:-non_counted_backtracking call/12.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L) :-
|
||||
@@ -1073,7 +1143,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L) :-
|
||||
'$prepare_call_clause'(O,N,B,C,D,E,F,G,H,I,J,K,L),
|
||||
expand_call_goal(O,M,P),
|
||||
strip_subst_module(P,M,Q,R),
|
||||
'$call_with_inference_counting'('$module_call'(Q,R)).
|
||||
'$module_call'(Q,R).
|
||||
|
||||
:-non_counted_backtracking call/13.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M) :-
|
||||
@@ -1086,7 +1156,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M) :-
|
||||
'$prepare_call_clause'(P,O,B,C,D,E,F,G,H,I,J,K,L,M),
|
||||
expand_call_goal(P,N,Q),
|
||||
strip_subst_module(Q,N,R,S),
|
||||
'$call_with_inference_counting'('$module_call'(R,S)).
|
||||
'$module_call'(R,S).
|
||||
|
||||
:-non_counted_backtracking call/14.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :-
|
||||
@@ -1099,7 +1169,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :-
|
||||
'$prepare_call_clause'(Q,P,B,C,D,E,F,G,H,I,J,K,L,M,N),
|
||||
expand_call_goal(Q,O,R),
|
||||
strip_subst_module(R,O,S,T),
|
||||
'$call_with_inference_counting'('$module_call'(S,T)).
|
||||
'$module_call'(S,T).
|
||||
|
||||
:-non_counted_backtracking call/15.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :-
|
||||
@@ -1112,7 +1182,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :-
|
||||
'$prepare_call_clause'(R,Q,B,C,D,E,F,G,H,I,J,K,L,M,N,O),
|
||||
expand_call_goal(R,P,S),
|
||||
strip_subst_module(S,P,T,U),
|
||||
'$call_with_inference_counting'('$module_call'(T,U)).
|
||||
'$module_call'(T,U).
|
||||
|
||||
:-non_counted_backtracking call/16.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :-
|
||||
@@ -1125,7 +1195,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :-
|
||||
'$prepare_call_clause'(S,R,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P),
|
||||
expand_call_goal(S,Q,T),
|
||||
strip_subst_module(T,Q,U,V),
|
||||
'$call_with_inference_counting'('$module_call'(U,V)).
|
||||
'$module_call'(U,V).
|
||||
|
||||
:-non_counted_backtracking call/17.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :-
|
||||
@@ -1138,7 +1208,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :-
|
||||
'$prepare_call_clause'(T,S,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q),
|
||||
expand_call_goal(T,R,U),
|
||||
strip_subst_module(U,R,V,W),
|
||||
'$call_with_inference_counting'('$module_call'(V,W)).
|
||||
'$module_call'(V,W).
|
||||
|
||||
:-non_counted_backtracking call/18.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :-
|
||||
@@ -1151,7 +1221,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :-
|
||||
'$prepare_call_clause'(U,T,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R),
|
||||
expand_call_goal(U,S,V),
|
||||
strip_subst_module(V,S,W,X),
|
||||
'$call_with_inference_counting'('$module_call'(W,X)).
|
||||
'$module_call'(W,X).
|
||||
|
||||
:-non_counted_backtracking call/19.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :-
|
||||
@@ -1164,7 +1234,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :-
|
||||
'$prepare_call_clause'(V,U,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S),
|
||||
expand_call_goal(V,T,W),
|
||||
strip_subst_module(W,T,X,Y),
|
||||
'$call_with_inference_counting'('$module_call'(X,Y)).
|
||||
'$module_call'(X,Y).
|
||||
|
||||
:-non_counted_backtracking call/20.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :-
|
||||
@@ -1177,7 +1247,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :-
|
||||
'$prepare_call_clause'(W,V,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T),
|
||||
expand_call_goal(W,U,X),
|
||||
strip_subst_module(X,U,Y,Z),
|
||||
'$call_with_inference_counting'('$module_call'(Y,Z)).
|
||||
'$module_call'(Y,Z).
|
||||
|
||||
:-non_counted_backtracking call/21.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :-
|
||||
@@ -1190,7 +1260,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :-
|
||||
'$prepare_call_clause'(X,W,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U),
|
||||
expand_call_goal(X,V,Y),
|
||||
strip_subst_module(Y,V,Z,A1),
|
||||
'$call_with_inference_counting'('$module_call'(Z,A1)).
|
||||
'$module_call'(Z,A1).
|
||||
|
||||
:-non_counted_backtracking call/22.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :-
|
||||
@@ -1203,7 +1273,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :-
|
||||
'$prepare_call_clause'(Y,X,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V),
|
||||
expand_call_goal(Y,W,Z),
|
||||
strip_subst_module(Z,W,A1,B1),
|
||||
'$call_with_inference_counting'('$module_call'(A1,B1)).
|
||||
'$module_call'(A1,B1).
|
||||
|
||||
:-non_counted_backtracking call/23.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :-
|
||||
@@ -1216,7 +1286,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :-
|
||||
'$prepare_call_clause'(Z,Y,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W),
|
||||
expand_call_goal(Z,X,A1),
|
||||
strip_subst_module(A1,X,B1,C1),
|
||||
'$call_with_inference_counting'('$module_call'(B1,C1)).
|
||||
'$module_call'(B1,C1).
|
||||
|
||||
:-non_counted_backtracking call/24.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :-
|
||||
@@ -1229,7 +1299,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :-
|
||||
'$prepare_call_clause'(A1,Z,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X),
|
||||
expand_call_goal(A1,Y,B1),
|
||||
strip_subst_module(B1,Y,C1,D1),
|
||||
'$call_with_inference_counting'('$module_call'(C1,D1)).
|
||||
'$module_call'(C1,D1).
|
||||
|
||||
:-non_counted_backtracking call/25.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :-
|
||||
@@ -1242,7 +1312,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :-
|
||||
'$prepare_call_clause'(B1,A1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y),
|
||||
expand_call_goal(B1,Z,C1),
|
||||
strip_subst_module(C1,Z,D1,E1),
|
||||
'$call_with_inference_counting'('$module_call'(D1,E1)).
|
||||
'$module_call'(D1,E1).
|
||||
|
||||
:-non_counted_backtracking call/26.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :-
|
||||
@@ -1255,7 +1325,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :-
|
||||
'$prepare_call_clause'(C1,B1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z),
|
||||
expand_call_goal(C1,A1,D1),
|
||||
strip_subst_module(D1,A1,E1,F1),
|
||||
'$call_with_inference_counting'('$module_call'(E1,F1)).
|
||||
'$module_call'(E1,F1).
|
||||
|
||||
:-non_counted_backtracking call/27.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :-
|
||||
@@ -1268,7 +1338,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :-
|
||||
'$prepare_call_clause'(D1,C1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1),
|
||||
expand_call_goal(D1,B1,E1),
|
||||
strip_subst_module(E1,B1,F1,G1),
|
||||
'$call_with_inference_counting'('$module_call'(F1,G1)).
|
||||
'$module_call'(F1,G1).
|
||||
|
||||
:-non_counted_backtracking call/28.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :-
|
||||
@@ -1281,7 +1351,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :-
|
||||
'$prepare_call_clause'(E1,D1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1),
|
||||
expand_call_goal(E1,C1,F1),
|
||||
strip_subst_module(F1,C1,G1,H1),
|
||||
'$call_with_inference_counting'('$module_call'(G1,H1)).
|
||||
'$module_call'(G1,H1).
|
||||
|
||||
:-non_counted_backtracking call/29.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :-
|
||||
@@ -1294,7 +1364,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :-
|
||||
'$prepare_call_clause'(F1,E1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1),
|
||||
expand_call_goal(F1,D1,G1),
|
||||
strip_subst_module(G1,D1,H1,I1),
|
||||
'$call_with_inference_counting'('$module_call'(H1,I1)).
|
||||
'$module_call'(H1,I1).
|
||||
|
||||
:-non_counted_backtracking call/30.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :-
|
||||
@@ -1307,7 +1377,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :-
|
||||
'$prepare_call_clause'(G1,F1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1),
|
||||
expand_call_goal(G1,E1,H1),
|
||||
strip_subst_module(H1,E1,I1,J1),
|
||||
'$call_with_inference_counting'('$module_call'(I1,J1)).
|
||||
'$module_call'(I1,J1).
|
||||
|
||||
:-non_counted_backtracking call/31.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :-
|
||||
@@ -1320,7 +1390,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :-
|
||||
'$prepare_call_clause'(H1,G1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1),
|
||||
expand_call_goal(H1,F1,I1),
|
||||
strip_subst_module(I1,F1,J1,K1),
|
||||
'$call_with_inference_counting'('$module_call'(J1,K1)).
|
||||
'$module_call'(J1,K1).
|
||||
|
||||
:-non_counted_backtracking call/32.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :-
|
||||
@@ -1333,7 +1403,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :-
|
||||
'$prepare_call_clause'(I1,H1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1),
|
||||
expand_call_goal(I1,G1,J1),
|
||||
strip_subst_module(J1,G1,K1,L1),
|
||||
'$call_with_inference_counting'('$module_call'(K1,L1)).
|
||||
'$module_call'(K1,L1).
|
||||
|
||||
:-non_counted_backtracking call/33.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :-
|
||||
@@ -1346,7 +1416,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :
|
||||
'$prepare_call_clause'(J1,I1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1),
|
||||
expand_call_goal(J1,H1,K1),
|
||||
strip_subst_module(K1,H1,L1,M1),
|
||||
'$call_with_inference_counting'('$module_call'(L1,M1)).
|
||||
'$module_call'(L1,M1).
|
||||
|
||||
:-non_counted_backtracking call/34.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :-
|
||||
@@ -1359,7 +1429,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(K1,J1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1),
|
||||
expand_call_goal(K1,I1,L1),
|
||||
strip_subst_module(L1,I1,M1,N1),
|
||||
'$call_with_inference_counting'('$module_call'(M1,N1)).
|
||||
'$module_call'(M1,N1).
|
||||
|
||||
:-non_counted_backtracking call/35.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :-
|
||||
@@ -1372,7 +1442,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(L1,K1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1),
|
||||
expand_call_goal(L1,J1,M1),
|
||||
strip_subst_module(M1,J1,N1,O1),
|
||||
'$call_with_inference_counting'('$module_call'(N1,O1)).
|
||||
'$module_call'(N1,O1).
|
||||
|
||||
:-non_counted_backtracking call/36.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :-
|
||||
@@ -1385,7 +1455,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(M1,L1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1),
|
||||
expand_call_goal(M1,K1,N1),
|
||||
strip_subst_module(N1,K1,O1,P1),
|
||||
'$call_with_inference_counting'('$module_call'(O1,P1)).
|
||||
'$module_call'(O1,P1).
|
||||
|
||||
:-non_counted_backtracking call/37.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :-
|
||||
@@ -1398,7 +1468,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(N1,M1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1),
|
||||
expand_call_goal(N1,L1,O1),
|
||||
strip_subst_module(O1,L1,P1,Q1),
|
||||
'$call_with_inference_counting'('$module_call'(P1,Q1)).
|
||||
'$module_call'(P1,Q1).
|
||||
|
||||
:-non_counted_backtracking call/38.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :-
|
||||
@@ -1411,7 +1481,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(O1,N1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1),
|
||||
expand_call_goal(O1,M1,P1),
|
||||
strip_subst_module(P1,M1,Q1,R1),
|
||||
'$call_with_inference_counting'('$module_call'(Q1,R1)).
|
||||
'$module_call'(Q1,R1).
|
||||
|
||||
:-non_counted_backtracking call/39.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :-
|
||||
@@ -1424,7 +1494,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(P1,O1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1),
|
||||
expand_call_goal(P1,N1,Q1),
|
||||
strip_subst_module(Q1,N1,R1,S1),
|
||||
'$call_with_inference_counting'('$module_call'(R1,S1)).
|
||||
'$module_call'(R1,S1).
|
||||
|
||||
:-non_counted_backtracking call/40.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :-
|
||||
@@ -1437,7 +1507,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(Q1,P1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1),
|
||||
expand_call_goal(Q1,O1,R1),
|
||||
strip_subst_module(R1,O1,S1,T1),
|
||||
'$call_with_inference_counting'('$module_call'(S1,T1)).
|
||||
'$module_call'(S1,T1).
|
||||
|
||||
:-non_counted_backtracking call/41.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :-
|
||||
@@ -1450,7 +1520,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(R1,Q1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1),
|
||||
expand_call_goal(R1,P1,S1),
|
||||
strip_subst_module(S1,P1,T1,U1),
|
||||
'$call_with_inference_counting'('$module_call'(T1,U1)).
|
||||
'$module_call'(T1,U1).
|
||||
|
||||
:-non_counted_backtracking call/42.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :-
|
||||
@@ -1463,7 +1533,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(S1,R1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1),
|
||||
expand_call_goal(S1,Q1,T1),
|
||||
strip_subst_module(T1,Q1,U1,V1),
|
||||
'$call_with_inference_counting'('$module_call'(U1,V1)).
|
||||
'$module_call'(U1,V1).
|
||||
|
||||
:-non_counted_backtracking call/43.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :-
|
||||
@@ -1476,7 +1546,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(T1,S1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1),
|
||||
expand_call_goal(T1,R1,U1),
|
||||
strip_subst_module(U1,R1,V1,W1),
|
||||
'$call_with_inference_counting'('$module_call'(V1,W1)).
|
||||
'$module_call'(V1,W1).
|
||||
|
||||
:-non_counted_backtracking call/44.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :-
|
||||
@@ -1489,7 +1559,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(U1,T1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1),
|
||||
expand_call_goal(U1,S1,V1),
|
||||
strip_subst_module(V1,S1,W1,X1),
|
||||
'$call_with_inference_counting'('$module_call'(W1,X1)).
|
||||
'$module_call'(W1,X1).
|
||||
|
||||
:-non_counted_backtracking call/45.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :-
|
||||
@@ -1502,7 +1572,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(V1,U1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1),
|
||||
expand_call_goal(V1,T1,W1),
|
||||
strip_subst_module(W1,T1,X1,Y1),
|
||||
'$call_with_inference_counting'('$module_call'(X1,Y1)).
|
||||
'$module_call'(X1,Y1).
|
||||
|
||||
:-non_counted_backtracking call/46.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :-
|
||||
@@ -1515,7 +1585,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(W1,V1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1),
|
||||
expand_call_goal(W1,U1,X1),
|
||||
strip_subst_module(X1,U1,Y1,Z1),
|
||||
'$call_with_inference_counting'('$module_call'(Y1,Z1)).
|
||||
'$module_call'(Y1,Z1).
|
||||
|
||||
:-non_counted_backtracking call/47.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :-
|
||||
@@ -1528,7 +1598,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(X1,W1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1),
|
||||
expand_call_goal(X1,V1,Y1),
|
||||
strip_subst_module(Y1,V1,Z1,A2),
|
||||
'$call_with_inference_counting'('$module_call'(Z1,A2)).
|
||||
'$module_call'(Z1,A2).
|
||||
|
||||
:-non_counted_backtracking call/48.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :-
|
||||
@@ -1541,7 +1611,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(Y1,X1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1),
|
||||
expand_call_goal(Y1,W1,Z1),
|
||||
strip_subst_module(Z1,W1,A2,B2),
|
||||
'$call_with_inference_counting'('$module_call'(A2,B2)).
|
||||
'$module_call'(A2,B2).
|
||||
|
||||
:-non_counted_backtracking call/49.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :-
|
||||
@@ -1554,7 +1624,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(Z1,Y1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1),
|
||||
expand_call_goal(Z1,X1,A2),
|
||||
strip_subst_module(A2,X1,B2,C2),
|
||||
'$call_with_inference_counting'('$module_call'(B2,C2)).
|
||||
'$module_call'(B2,C2).
|
||||
|
||||
:-non_counted_backtracking call/50.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :-
|
||||
@@ -1567,7 +1637,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(A2,Z1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1),
|
||||
expand_call_goal(A2,Y1,B2),
|
||||
strip_subst_module(B2,Y1,C2,D2),
|
||||
'$call_with_inference_counting'('$module_call'(C2,D2)).
|
||||
'$module_call'(C2,D2).
|
||||
|
||||
:-non_counted_backtracking call/51.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :-
|
||||
@@ -1580,7 +1650,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(B2,A2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1),
|
||||
expand_call_goal(B2,Z1,C2),
|
||||
strip_subst_module(C2,Z1,D2,E2),
|
||||
'$call_with_inference_counting'('$module_call'(D2,E2)).
|
||||
'$module_call'(D2,E2).
|
||||
|
||||
:-non_counted_backtracking call/52.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :-
|
||||
@@ -1593,7 +1663,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(C2,B2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1),
|
||||
expand_call_goal(C2,A2,D2),
|
||||
strip_subst_module(D2,A2,E2,F2),
|
||||
'$call_with_inference_counting'('$module_call'(E2,F2)).
|
||||
'$module_call'(E2,F2).
|
||||
|
||||
:-non_counted_backtracking call/53.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :-
|
||||
@@ -1606,7 +1676,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(D2,C2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2),
|
||||
expand_call_goal(D2,B2,E2),
|
||||
strip_subst_module(E2,B2,F2,G2),
|
||||
'$call_with_inference_counting'('$module_call'(F2,G2)).
|
||||
'$module_call'(F2,G2).
|
||||
|
||||
:-non_counted_backtracking call/54.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :-
|
||||
@@ -1619,7 +1689,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(E2,D2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2),
|
||||
expand_call_goal(E2,C2,F2),
|
||||
strip_subst_module(F2,C2,G2,H2),
|
||||
'$call_with_inference_counting'('$module_call'(G2,H2)).
|
||||
'$module_call'(G2,H2).
|
||||
|
||||
:-non_counted_backtracking call/55.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :-
|
||||
@@ -1632,7 +1702,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(F2,E2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2),
|
||||
expand_call_goal(F2,D2,G2),
|
||||
strip_subst_module(G2,D2,H2,I2),
|
||||
'$call_with_inference_counting'('$module_call'(H2,I2)).
|
||||
'$module_call'(H2,I2).
|
||||
|
||||
:-non_counted_backtracking call/56.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :-
|
||||
@@ -1645,7 +1715,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(G2,F2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2),
|
||||
expand_call_goal(G2,E2,H2),
|
||||
strip_subst_module(H2,E2,I2,J2),
|
||||
'$call_with_inference_counting'('$module_call'(I2,J2)).
|
||||
'$module_call'(I2,J2).
|
||||
|
||||
:-non_counted_backtracking call/57.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :-
|
||||
@@ -1658,7 +1728,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(H2,G2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2),
|
||||
expand_call_goal(H2,F2,I2),
|
||||
strip_subst_module(I2,F2,J2,K2),
|
||||
'$call_with_inference_counting'('$module_call'(J2,K2)).
|
||||
'$module_call'(J2,K2).
|
||||
|
||||
:-non_counted_backtracking call/58.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :-
|
||||
@@ -1671,7 +1741,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(I2,H2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2),
|
||||
expand_call_goal(I2,G2,J2),
|
||||
strip_subst_module(J2,G2,K2,L2),
|
||||
'$call_with_inference_counting'('$module_call'(K2,L2)).
|
||||
'$module_call'(K2,L2).
|
||||
|
||||
:-non_counted_backtracking call/59.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :-
|
||||
@@ -1684,7 +1754,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(J2,I2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2),
|
||||
expand_call_goal(J2,H2,K2),
|
||||
strip_subst_module(K2,H2,L2,M2),
|
||||
'$call_with_inference_counting'('$module_call'(L2,M2)).
|
||||
'$module_call'(L2,M2).
|
||||
|
||||
:-non_counted_backtracking call/60.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :-
|
||||
@@ -1697,7 +1767,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(K2,J2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2),
|
||||
expand_call_goal(K2,I2,L2),
|
||||
strip_subst_module(L2,I2,M2,N2),
|
||||
'$call_with_inference_counting'('$module_call'(M2,N2)).
|
||||
'$module_call'(M2,N2).
|
||||
|
||||
:-non_counted_backtracking call/61.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :-
|
||||
@@ -1710,7 +1780,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(L2,K2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2),
|
||||
expand_call_goal(L2,J2,M2),
|
||||
strip_subst_module(M2,J2,N2,O2),
|
||||
'$call_with_inference_counting'('$module_call'(N2,O2)).
|
||||
'$module_call'(N2,O2).
|
||||
|
||||
:-non_counted_backtracking call/62.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :-
|
||||
@@ -1723,7 +1793,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(M2,L2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2),
|
||||
expand_call_goal(M2,K2,N2),
|
||||
strip_subst_module(N2,K2,O2,P2),
|
||||
'$call_with_inference_counting'('$module_call'(O2,P2)).
|
||||
'$module_call'(O2,P2).
|
||||
|
||||
:-non_counted_backtracking call/63.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :-
|
||||
@@ -1736,7 +1806,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(N2,M2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2),
|
||||
expand_call_goal(N2,L2,O2),
|
||||
strip_subst_module(O2,L2,P2,Q2),
|
||||
'$call_with_inference_counting'('$module_call'(P2,Q2)).
|
||||
'$module_call'(P2,Q2).
|
||||
|
||||
:-non_counted_backtracking call/64.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :-
|
||||
@@ -1749,7 +1819,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(O2,N2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2),
|
||||
expand_call_goal(O2,M2,P2),
|
||||
strip_subst_module(P2,M2,Q2,R2),
|
||||
'$call_with_inference_counting'('$module_call'(Q2,R2)).
|
||||
'$module_call'(Q2,R2).
|
||||
|
||||
:-non_counted_backtracking call/65.
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :-
|
||||
@@ -1762,4 +1832,4 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1
|
||||
'$prepare_call_clause'(P2,O2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2),
|
||||
expand_call_goal(P2,N2,Q2),
|
||||
strip_subst_module(Q2,N2,R2,S2),
|
||||
'$call_with_inference_counting'('$module_call'(R2,S2)).
|
||||
'$module_call'(R2,S2).
|
||||
|
||||
@@ -14,3 +14,9 @@ impl MachineArgs {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MachineArgs {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use dashu::base::Abs;
|
||||
use dashu::base::Gcd;
|
||||
use dashu::base::{Abs, Gcd, Signed, UnsignedAbs};
|
||||
use dashu::integer::fast_div::ConstDivisor;
|
||||
use dashu::integer::IBig;
|
||||
use divrem::*;
|
||||
use num_order::NumOrd;
|
||||
|
||||
use crate::arena::*;
|
||||
use crate::arithmetic::*;
|
||||
@@ -50,9 +51,7 @@ macro_rules! drop_iter_on_err {
|
||||
};
|
||||
}
|
||||
|
||||
fn zero_divisor_eval_error(
|
||||
stub_gen: impl Fn() -> FunctorStub + 'static,
|
||||
) -> MachineStubGen {
|
||||
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();
|
||||
@@ -61,9 +60,7 @@ fn zero_divisor_eval_error(
|
||||
})
|
||||
}
|
||||
|
||||
fn undefined_eval_error(
|
||||
stub_gen: impl Fn() -> FunctorStub + 'static,
|
||||
) -> MachineStubGen {
|
||||
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();
|
||||
@@ -87,18 +84,18 @@ fn numerical_type_error(
|
||||
|
||||
fn isize_gcd(n1: isize, n2: isize) -> Option<isize> {
|
||||
if n1 == 0 {
|
||||
return n2.checked_abs().map(|n| n as isize);
|
||||
return n2.checked_abs();
|
||||
}
|
||||
|
||||
if n2 == 0 {
|
||||
return n1.checked_abs().map(|n| n as isize);
|
||||
return n1.checked_abs();
|
||||
}
|
||||
|
||||
let n1 = n1.checked_abs();
|
||||
let n2 = n2.checked_abs();
|
||||
|
||||
let mut n1 = if let Some(n1) = n1 { n1 } else { return None };
|
||||
let mut n2 = if let Some(n2) = n2 { n2 } else { return None };
|
||||
let mut n1 = n1?;
|
||||
let mut n2 = n2?;
|
||||
|
||||
let mut shift = 0;
|
||||
|
||||
@@ -118,9 +115,7 @@ fn isize_gcd(n1: isize, n2: isize) -> Option<isize> {
|
||||
}
|
||||
|
||||
if n1 > n2 {
|
||||
let t = n2;
|
||||
n2 = n1;
|
||||
n1 = t;
|
||||
std::mem::swap(&mut n2, &mut n1);
|
||||
}
|
||||
|
||||
n2 -= n1;
|
||||
@@ -169,9 +164,7 @@ pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number,
|
||||
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(&*n1 + &*n2, arena))
|
||||
}
|
||||
| (Number::Rational(n2), Number::Integer(n1)) => Ok(Number::arena_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)?))
|
||||
@@ -179,9 +172,7 @@ pub(crate) fn add(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number,
|
||||
(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(&*r1 + &*r2, arena))
|
||||
}
|
||||
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::arena_from(&*r1 + &*r2, arena)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,12 +188,12 @@ pub(crate) fn neg(n: Number, arena: &mut Arena) -> Number {
|
||||
Number::Integer(n) => {
|
||||
let n_clone: Integer = (*n).clone();
|
||||
Number::arena_from(-Integer::from(n_clone), arena)
|
||||
},
|
||||
}
|
||||
Number::Float(OrderedFloat(f)) => Number::Float(OrderedFloat(-f)),
|
||||
Number::Rational(r) => {
|
||||
let r_clone: Rational = (*r).clone();
|
||||
Number::arena_from(-Rational::from(r_clone), arena)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,12 +210,12 @@ pub(crate) fn abs(n: Number, arena: &mut Arena) -> Number {
|
||||
Number::Integer(n) => {
|
||||
let n_clone: Integer = (*n).clone();
|
||||
Number::arena_from(Integer::from(n_clone.abs()), arena)
|
||||
},
|
||||
}
|
||||
Number::Float(f) => Number::Float(f.abs()),
|
||||
Number::Rational(r) => {
|
||||
let r_clone: Rational = (*r).clone();
|
||||
Number::arena_from(Rational::from(r_clone.abs()), arena)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,18 +348,18 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1_i = n1.get_num();
|
||||
|
||||
if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && &*n2 < &Integer::from(0) {
|
||||
if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && n2.is_negative() {
|
||||
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))
|
||||
Ok(Number::arena_from(binary_pow(n1, &n2), arena))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => {
|
||||
let n2_i = n2.get_num();
|
||||
|
||||
if !(&*n1 == &Integer::from(1) || &*n1 == &Integer::from(0) || &*n1 == &Integer::from(-1)) && n2_i < 0 {
|
||||
if !(n1.is_one() || n1.is_zero() || n1.num_eq(&-1)) && n2_i < 0 {
|
||||
let n = Number::Integer(n1);
|
||||
Err(numerical_type_error(ValidType::Float, n, stub_gen))
|
||||
} else {
|
||||
@@ -377,11 +368,11 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
if !(&*n1 == &Integer::from(1) || &*n1 == &Integer::from(0) || &*n1 == &Integer::from(-1)) && &*n2 < &Integer::from(0) {
|
||||
if !(n1.is_one() || n1.is_zero() || n1.num_eq(&-1)) && n2.is_negative() {
|
||||
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))
|
||||
Ok(Number::arena_from(binary_pow((*n1).clone(), &n2), arena))
|
||||
}
|
||||
}
|
||||
(n1, Number::Integer(n2)) => {
|
||||
@@ -454,14 +445,14 @@ pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
if &*n2 > &n1.get_num() {
|
||||
if (*n2).num_gt(&n1.get_num()) {
|
||||
Ok(Number::Integer(n2))
|
||||
} else {
|
||||
Ok(Number::Fixnum(n1))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => {
|
||||
if &*n1 > &n2.get_num() {
|
||||
if (*n1).num_gt(&n2.get_num()) {
|
||||
Ok(Number::Integer(n1))
|
||||
} else {
|
||||
Ok(Number::Fixnum(n2))
|
||||
@@ -498,14 +489,14 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
if &*n2 < &n1.get_num() {
|
||||
if (*n2).num_lt(&n1.get_num()) {
|
||||
Ok(Number::Integer(n2))
|
||||
} else {
|
||||
Ok(Number::Fixnum(n1))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => {
|
||||
if &*n1 < &n2.get_num() {
|
||||
if (*n1).num_lt(&n2.get_num()) {
|
||||
Ok(Number::Integer(n1))
|
||||
} else {
|
||||
Ok(Number::Fixnum(n2))
|
||||
@@ -552,7 +543,7 @@ pub fn rational_from_number(
|
||||
Number::Integer(n) => {
|
||||
let n_clone: Integer = (*n).clone();
|
||||
Ok(arena_alloc!(Rational::from(n_clone), arena))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +551,7 @@ pub(crate) fn rdiv(
|
||||
r1: TypedArenaPtr<Rational>,
|
||||
r2: TypedArenaPtr<Rational>,
|
||||
) -> Result<Rational, MachineStubGen> {
|
||||
if &*r2 == &0 {
|
||||
if r2.is_zero() {
|
||||
let stub_gen = || {
|
||||
let rdiv_atom = atom!("rdiv");
|
||||
functor_stub(rdiv_atom, 2)
|
||||
@@ -582,19 +573,17 @@ pub(crate) fn idiv(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number,
|
||||
(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 {
|
||||
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());
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
let n2 = Integer::from(n2.get_num());
|
||||
|
||||
Ok(Number::arena_from(n1 / n2, arena))
|
||||
}
|
||||
Ok(Number::arena_from(n1 / n2, arena))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
if &*n2 == &0 {
|
||||
if n2.is_zero() {
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else {
|
||||
Ok(Number::arena_from(Integer::from(n1) / &*n2, arena))
|
||||
@@ -608,13 +597,10 @@ pub(crate) fn idiv(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number,
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
if &*n2 == &0 {
|
||||
if n2.is_zero() {
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else {
|
||||
Ok(Number::arena_from(
|
||||
<(Integer, Integer)>::from(n1.div_rem_floor_ref(&*n2)).0,
|
||||
arena,
|
||||
))
|
||||
Ok(Number::arena_from(&*n1 / &*n2, arena))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(_), n2) | (Number::Integer(_), n2) => {
|
||||
@@ -658,33 +644,39 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
let n1 = Integer::from(n1_i);
|
||||
|
||||
if let Ok(n2) = usize::try_from(n2_i) {
|
||||
return Ok(Number::arena_from(n1 >> n2, arena));
|
||||
} else {
|
||||
return Ok(Number::arena_from(n1 >> usize::max_value(), arena));
|
||||
Ok(Number::arena_from(n1 >> n2, arena))
|
||||
} else {
|
||||
Ok(Number::arena_from(n1 >> usize::max_value(), arena))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
|
||||
match n2.to_usize() {
|
||||
Some(n2) => Ok(Number::arena_from(n1 >> n2, arena)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(n1 >> usize::max_value(), arena))
|
||||
},
|
||||
let result: Result<usize, _> = (&*n2).try_into();
|
||||
|
||||
match result {
|
||||
Ok(n2) => Ok(Number::arena_from(n1 >> n2, arena)),
|
||||
Err(_) => Ok(Number::arena_from(n1 >> usize::max_value(), arena)),
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 >> usize::max_value()),arena))
|
||||
},
|
||||
},
|
||||
(Number::Integer(n1), Number::Integer(n2)) => match n2.to_usize() {
|
||||
Some(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 >> usize::max_value()), arena))
|
||||
},
|
||||
_ => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 >> usize::max_value()),
|
||||
arena,
|
||||
)),
|
||||
},
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
let result: Result<usize, _> = (&*n2).try_into();
|
||||
|
||||
match result {
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
||||
Err(_) => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 >> usize::max_value()),
|
||||
arena,
|
||||
)),
|
||||
}
|
||||
}
|
||||
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
(n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
|
||||
@@ -709,32 +701,32 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
let n1 = Integer::from(n1_i);
|
||||
|
||||
if let Ok(n2) = usize::try_from(n2_i) {
|
||||
return Ok(Number::arena_from(n1 << n2, arena));
|
||||
} else {
|
||||
return Ok(Number::arena_from(n1 << usize::max_value(), arena));
|
||||
Ok(Number::arena_from(n1 << n2, arena))
|
||||
} else {
|
||||
Ok(Number::arena_from(n1 << usize::max_value(), arena))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
|
||||
match n2.to_u32() {
|
||||
Some(n2) => Ok(Number::arena_from(n1.to_u64().unwrap() << n2, arena)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(n1 << usize::max_value(), arena))
|
||||
}
|
||||
match (&*n2).try_into() as Result<usize, _> {
|
||||
Ok(n2) => Ok(Number::arena_from(n1 << n2, arena)),
|
||||
_ => Ok(Number::arena_from(n1 << usize::max_value(), arena)),
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 << usize::max_value()),arena))
|
||||
}
|
||||
_ => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 << usize::max_value()),
|
||||
arena,
|
||||
)),
|
||||
},
|
||||
(Number::Integer(n1), Number::Integer(n2)) => match n2.to_u32() {
|
||||
Some(n2) => Ok(Number::arena_from(Integer::from(n1.to_u64().unwrap() << n2), arena)),
|
||||
_ => {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 << usize::max_value()),arena))
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => match (&*n2).try_into() as Result<usize, _> {
|
||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
||||
_ => Ok(Number::arena_from(
|
||||
Integer::from(&*n1 << usize::max_value()),
|
||||
arena,
|
||||
)),
|
||||
},
|
||||
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||
@@ -833,6 +825,23 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result<Number,
|
||||
functor_stub(mod_atom, 2)
|
||||
};
|
||||
|
||||
fn ibig_rem_floor(n1: &Integer, n2: &Integer) -> Integer {
|
||||
let ring = ConstDivisor::new(n2.unsigned_abs());
|
||||
let n1 = n1.clone();
|
||||
|
||||
if n2.is_negative() {
|
||||
let unsigned_result = IBig::from(ring.reduce(n1).residue());
|
||||
|
||||
if unsigned_result.is_zero() {
|
||||
unsigned_result
|
||||
} else {
|
||||
unsigned_result + n2
|
||||
}
|
||||
} else {
|
||||
IBig::from(ring.reduce(n1).residue())
|
||||
}
|
||||
}
|
||||
|
||||
match (x, y) {
|
||||
(Number::Fixnum(n1), Number::Fixnum(n2)) => {
|
||||
let n2_i = n2.get_num();
|
||||
@@ -845,14 +854,11 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result<Number,
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
if &*n2 == &0 {
|
||||
if n2.is_zero() {
|
||||
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,
|
||||
))
|
||||
Ok(Number::arena_from(ibig_rem_floor(&n1, &n2), arena))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => {
|
||||
@@ -862,20 +868,14 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result<Number,
|
||||
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,
|
||||
))
|
||||
Ok(Number::arena_from(ibig_rem_floor(&n1, &n2), arena))
|
||||
}
|
||||
}
|
||||
(Number::Integer(x), Number::Integer(y)) => {
|
||||
if &*y == &0 {
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
if n2.is_zero() {
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else {
|
||||
Ok(Number::arena_from(
|
||||
<(Integer, Integer)>::from(x.div_rem_floor_ref(&*y)).1,
|
||||
arena,
|
||||
))
|
||||
Ok(Number::arena_from(ibig_rem_floor(&n1, &n2), arena))
|
||||
}
|
||||
}
|
||||
(Number::Integer(_), n2) | (Number::Fixnum(_), n2) => {
|
||||
@@ -903,7 +903,7 @@ pub(crate) fn remainder(x: Number, y: Number, arena: &mut Arena) -> Result<Numbe
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
if &*n2 == &0 {
|
||||
if n2.is_zero() {
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
@@ -921,7 +921,7 @@ pub(crate) fn remainder(x: Number, y: Number, arena: &mut Arena) -> Result<Numbe
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
if &*n2 == &0 {
|
||||
if n2.is_zero() {
|
||||
Err(zero_divisor_eval_error(stub_gen))
|
||||
} else {
|
||||
Ok(Number::arena_from(Integer::from(&*n1 % &*n2), arena))
|
||||
@@ -948,11 +948,8 @@ pub(crate) fn gcd(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
if let Some(result) = isize_gcd(n1_i, n2_i) {
|
||||
Ok(Number::arena_from(result, arena))
|
||||
} else {
|
||||
let value: IBig = Integer::from(n1_i).gcd(&Integer::from(n2_i)).into();
|
||||
Ok(Number::arena_from(
|
||||
value,
|
||||
arena,
|
||||
))
|
||||
let value: Integer = Integer::from(n1_i).gcd(&Integer::from(n2_i)).into();
|
||||
Ok(Number::arena_from(value, arena))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) | (Number::Integer(n2), Number::Fixnum(n1)) => {
|
||||
@@ -961,8 +958,9 @@ pub(crate) fn gcd(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
||||
Ok(Number::arena_from(Integer::from(n2_clone.gcd(&n1)), arena))
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
let n1_clone: Integer = (*n1).clone();
|
||||
Ok(Number::arena_from(Integer::from(n1_clone.gcd(&Integer::from(n2.to_isize().unwrap()))) as IBig, arena))
|
||||
let n2: isize = (&*n2).try_into().unwrap();
|
||||
let value: Integer = (&*n1).gcd(&Integer::from(n2)).into();
|
||||
Ok(Number::arena_from(value, arena))
|
||||
}
|
||||
(Number::Float(f), _) | (_, Number::Float(f)) => {
|
||||
let n = Number::Float(f);
|
||||
@@ -1031,53 +1029,6 @@ pub(crate) fn atan(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.atan())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn asinh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.asinh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn acosh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.acosh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn atanh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
let stub_gen = || {
|
||||
let is_atom = atom!("is");
|
||||
functor_stub(is_atom, 2)
|
||||
};
|
||||
|
||||
let f1 = try_numeric_result!(result_f(&n1), stub_gen)?;
|
||||
|
||||
try_numeric_result!(if f1 == 1.0 || f1 == -1.0 {
|
||||
Err(EvalError::Undefined)
|
||||
} else {
|
||||
result_f(&Number::Float(OrderedFloat(f1.atanh())))
|
||||
},
|
||||
stub_gen)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn sinh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.sinh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn cosh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.cosh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn tanh(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.tanh())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn log10(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.log(10f64))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn float_fractional_part(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
unary_float_fn_template(n1, |f| f.fract())
|
||||
@@ -1107,7 +1058,6 @@ 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);
|
||||
@@ -1160,17 +1110,18 @@ impl MachineState {
|
||||
pub fn get_number(&mut self, at: &ArithmeticTerm) -> Result<Number, MachineStub> {
|
||||
match at {
|
||||
&ArithmeticTerm::Reg(r) => {
|
||||
let value = self.store(self.deref(self[r]));
|
||||
let value = self.store(self.deref(self[r]));
|
||||
|
||||
match Number::try_from(value) {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => self.arith_eval_by_metacall(value),
|
||||
}
|
||||
}
|
||||
&ArithmeticTerm::Interm(i) => {
|
||||
Ok(mem::replace(&mut self.interms[i - 1], Number::Fixnum(Fixnum::build_with(0))))
|
||||
}
|
||||
&ArithmeticTerm::Number(n) => Ok(n),
|
||||
&ArithmeticTerm::Interm(i) => Ok(mem::replace(
|
||||
&mut self.interms[i - 1],
|
||||
Number::Fixnum(Fixnum::build_with(0)),
|
||||
)),
|
||||
ArithmeticTerm::Number(n) => Ok(*n),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1183,13 +1134,17 @@ impl MachineState {
|
||||
|
||||
match rational_from_number(n, caller, &mut self.arena) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(e_gen) => Err(e_gen(self))
|
||||
Err(e_gen) => Err(e_gen(self)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn arith_eval_by_metacall(&mut self, value: HeapCellValue) -> Result<Number, MachineStub> {
|
||||
pub(crate) fn arith_eval_by_metacall(
|
||||
&mut self,
|
||||
value: HeapCellValue,
|
||||
) -> Result<Number, MachineStub> {
|
||||
let stub_gen = || functor_stub(atom!("is"), 2);
|
||||
let mut iter = stackful_post_order_iter(&mut self.heap, &mut self.stack, value);
|
||||
let mut iter =
|
||||
stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, value);
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
if value.get_forwarding_bit() {
|
||||
@@ -1338,27 +1293,6 @@ impl MachineState {
|
||||
atom!("tan") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, tan(a1))
|
||||
))),
|
||||
atom!("cosh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, cosh(a1))
|
||||
))),
|
||||
atom!("sinh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, sinh(a1))
|
||||
))),
|
||||
atom!("tanh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, tanh(a1))
|
||||
))),
|
||||
atom!("acosh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, acosh(a1))
|
||||
))),
|
||||
atom!("asinh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, asinh(a1))
|
||||
))),
|
||||
atom!("atanh") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, atanh(a1))
|
||||
))),
|
||||
atom!("log10") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, log10(a1))
|
||||
))),
|
||||
atom!("float_fractional_part") => self.interms.push(Number::Float(OrderedFloat(
|
||||
drop_iter_on_err!(self, iter, float_fractional_part(a1))
|
||||
))),
|
||||
@@ -1489,30 +1423,16 @@ mod tests {
|
||||
use crate::machine::mock_wam::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
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),
|
||||
);
|
||||
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();
|
||||
|
||||
@@ -118,12 +118,9 @@ impl MachineState {
|
||||
and_frame[i] = self.registers[i];
|
||||
}
|
||||
|
||||
and_frame[arity + 1] =
|
||||
fixnum_as_cell!(Fixnum::build_with(self.b0 as i64));
|
||||
and_frame[arity + 2] =
|
||||
fixnum_as_cell!(Fixnum::build_with(self.num_of_args as i64));
|
||||
and_frame[arity + 3] =
|
||||
fixnum_as_cell!(Fixnum::build_with(self.attr_var_init.cp as i64));
|
||||
and_frame[arity + 1] = fixnum_as_cell!(Fixnum::build_with(self.b0 as i64));
|
||||
and_frame[arity + 2] = fixnum_as_cell!(Fixnum::build_with(self.num_of_args as i64));
|
||||
and_frame[arity + 3] = fixnum_as_cell!(Fixnum::build_with(self.attr_var_init.cp as i64));
|
||||
|
||||
self.verify_attributes();
|
||||
|
||||
@@ -136,7 +133,8 @@ impl MachineState {
|
||||
let mut seen_set = IndexSet::new();
|
||||
let mut seen_vars = vec![];
|
||||
|
||||
let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, cell);
|
||||
let mut iter =
|
||||
stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, cell);
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
read_heap_cell!(value,
|
||||
|
||||
@@ -8,7 +8,9 @@ fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec<usize>) -> b
|
||||
&Instruction::TryMeElse(offset) if offset > 0 => {
|
||||
stack.push(index + offset);
|
||||
}
|
||||
&Instruction::DefaultRetryMeElse(offset) | &Instruction::RetryMeElse(offset) if offset > 0 => {
|
||||
&Instruction::DefaultRetryMeElse(offset) | &Instruction::RetryMeElse(offset)
|
||||
if offset > 0 =>
|
||||
{
|
||||
stack.push(index + offset);
|
||||
}
|
||||
&Instruction::DynamicElse(_, _, NextOrFail::Next(offset)) if offset > 0 => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
32
src/machine/config.rs
Normal file
32
src/machine/config.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
pub struct MachineConfig {
|
||||
pub streams: StreamConfig,
|
||||
pub toplevel: &'static str,
|
||||
}
|
||||
|
||||
pub enum StreamConfig {
|
||||
Stdio,
|
||||
Memory,
|
||||
}
|
||||
|
||||
impl Default for MachineConfig {
|
||||
fn default() -> Self {
|
||||
MachineConfig {
|
||||
streams: StreamConfig::Stdio,
|
||||
toplevel: include_str!("../toplevel.pl"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineConfig {
|
||||
pub fn in_memory() -> Self {
|
||||
MachineConfig {
|
||||
streams: StreamConfig::Memory,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_toplevel(mut self, toplevel: &'static str) -> Self {
|
||||
self.toplevel = toplevel;
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ 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 push_attr_var_queue(&mut self, attr_var_loc: usize);
|
||||
fn stack(&mut self) -> &mut Stack;
|
||||
fn threshold(&self) -> usize;
|
||||
}
|
||||
@@ -73,7 +74,6 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
if h >= self.old_h {
|
||||
*self.value_at_scan() = list_loc_as_cell!(h);
|
||||
self.scan += 1;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -91,15 +91,24 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
self.target.push(hcv);
|
||||
}
|
||||
|
||||
let cdr = self.target.store(self.target.deref(heap_loc_as_cell!(addr + 1)));
|
||||
let cdr = self
|
||||
.target
|
||||
.store(self.target.deref(heap_loc_as_cell!(addr + 1)));
|
||||
|
||||
if !cdr.is_var() {
|
||||
// mark addr + 1 as a list back edge in the cdr of the list
|
||||
self.trail_list_cell(addr + 1, threshold);
|
||||
self.target[addr + 1].set_mark_bit(true);
|
||||
self.target[addr + 1].set_forwarding_bit(true);
|
||||
} else {
|
||||
let car = self.target.store(self.target.deref(heap_loc_as_cell!(addr)));
|
||||
let car = self
|
||||
.target
|
||||
.store(self.target.deref(heap_loc_as_cell!(addr)));
|
||||
|
||||
if !car.is_var() {
|
||||
// mark addr as a list back edge in the car of the list
|
||||
self.trail_list_cell(addr, threshold);
|
||||
self.target[addr].set_mark_bit(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,10 +179,11 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
|
||||
fn copy_attr_var_lists(&mut self) {
|
||||
while !self.attr_var_list_locs.is_empty() {
|
||||
let iter = mem::replace(&mut self.attr_var_list_locs, vec![]);
|
||||
let iter = std::mem::take(&mut self.attr_var_list_locs);
|
||||
|
||||
for (threshold, list_loc) in iter {
|
||||
self.target[threshold] = list_loc_as_cell!(self.target.threshold());
|
||||
self.target.push_attr_var_queue(threshold - 1);
|
||||
self.copy_attr_var_list(list_loc);
|
||||
}
|
||||
}
|
||||
@@ -188,10 +198,10 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
while let HeapCellValueTag::Lis = list_addr.get_tag() {
|
||||
let threshold = self.target.threshold();
|
||||
let heap_loc = list_addr.get_value() as usize;
|
||||
let str_loc = self.target[heap_loc].get_value() as usize;
|
||||
let str_loc = self.target[heap_loc].get_value() as usize;
|
||||
|
||||
self.target.push(heap_loc_as_cell!(threshold+2));
|
||||
self.target.push(heap_loc_as_cell!(threshold+1));
|
||||
self.target.push(heap_loc_as_cell!(threshold + 2));
|
||||
self.target.push(heap_loc_as_cell!(threshold + 1));
|
||||
|
||||
read_heap_cell!(self.target[str_loc],
|
||||
(HeapCellValueTag::Atom) => {
|
||||
@@ -259,6 +269,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
}
|
||||
|
||||
fn copy_var(&mut self, addr: HeapCellValue) {
|
||||
let index = addr.get_value() as usize;
|
||||
let rd = self.target.deref(addr);
|
||||
let ra = self.target.store(rd);
|
||||
|
||||
@@ -267,7 +278,20 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
if h >= self.old_h {
|
||||
*self.value_at_scan() = ra;
|
||||
self.scan += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, h) => {
|
||||
if h >= self.old_h && self.target[index].get_mark_bit() {
|
||||
*self.value_at_scan() = heap_loc_as_cell!(
|
||||
if ra.get_forwarding_bit() {
|
||||
h + 1
|
||||
} else {
|
||||
h
|
||||
}
|
||||
);
|
||||
|
||||
self.scan += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -352,12 +376,16 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn unwind_trail(&mut self) {
|
||||
for (r, value) in self.trail.drain(0..) {
|
||||
fn unwind_trail(mut self) {
|
||||
for (r, value) in self.trail {
|
||||
let index = r.get_value() as usize;
|
||||
|
||||
match r.get_tag() {
|
||||
RefTag::AttrVar | RefTag::HeapCell => self.target[index] = value,
|
||||
RefTag::AttrVar | RefTag::HeapCell => {
|
||||
self.target[index] = value;
|
||||
self.target[index].set_mark_bit(false);
|
||||
self.target[index].set_forwarding_bit(false);
|
||||
}
|
||||
RefTag::StackCell => self.target.stack()[index] = value,
|
||||
}
|
||||
}
|
||||
@@ -370,6 +398,7 @@ mod tests {
|
||||
use crate::machine::mock_wam::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
|
||||
fn copier_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
@@ -377,8 +406,9 @@ mod tests {
|
||||
let a_atom = atom!("a");
|
||||
let b_atom = atom!("b");
|
||||
|
||||
wam.machine_st.heap
|
||||
.extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
|
||||
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));
|
||||
@@ -401,20 +431,26 @@ mod tests {
|
||||
|
||||
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_var_cell =
|
||||
put_partial_string(&mut wam.machine_st.heap, "abc ", &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_var_cell =
|
||||
put_partial_string(&mut wam.machine_st.heap, "def", &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_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)));
|
||||
wam.machine_st
|
||||
.heap
|
||||
.push(fixnum_as_cell!(Fixnum::build_with(0i64)));
|
||||
|
||||
{
|
||||
let wam = TermCopyingMockWAM { wam: &mut wam };
|
||||
@@ -428,14 +464,20 @@ mod tests {
|
||||
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[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)));
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[12],
|
||||
fixnum_as_cell!(Fixnum::build_with(0i64))
|
||||
);
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
|
||||
428
src/machine/cycle_detection.rs
Normal file
428
src/machine/cycle_detection.rs
Normal file
@@ -0,0 +1,428 @@
|
||||
use crate::atom_table::*;
|
||||
use crate::types::*;
|
||||
|
||||
/* Use the pointer reversal technique of the Deutsch-Schorr-Waite
|
||||
* algorithm to detect cycles in Prolog terms.
|
||||
*
|
||||
* Much of the structure and nomenclature of the GC marking algorithm
|
||||
* is adapted here but there are a few significant changes:
|
||||
*
|
||||
* - Forwarded cells now form a trail of bread crumbs leading back to self.start
|
||||
* - Cells are only marked during the backward phase
|
||||
* - Visiting subterms of a visited compound does not immediately shift to the backward phase
|
||||
* - The heads of LIS structures are both marked and forwarded rather
|
||||
* than just forwarded to distinguish them from tails;
|
||||
* continue_forwarding() checks for this before entering the forward
|
||||
* phase
|
||||
*
|
||||
* Commonalities with the GC marking algorithm:
|
||||
* - The contents of forwarded cells are modified only when they are unforwarded
|
||||
* - Marked (but unforwarded!) cells immediately shift to the backward phase
|
||||
*/
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CycleDetectingIter<'a, const STOP_AT_CYCLES: bool> {
|
||||
pub(crate) heap: &'a mut [HeapCellValue],
|
||||
start: usize,
|
||||
current: usize,
|
||||
next: u64,
|
||||
cycle_found: bool,
|
||||
mark_phase: bool,
|
||||
}
|
||||
|
||||
impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self {
|
||||
heap[start].set_forwarding_bit(true);
|
||||
let next = heap[start].get_value();
|
||||
|
||||
Self {
|
||||
heap,
|
||||
start,
|
||||
current: start,
|
||||
next,
|
||||
cycle_found: false,
|
||||
mark_phase: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn cycle_found(&self) -> bool {
|
||||
self.cycle_found
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cycle_detection_active(&self) -> bool {
|
||||
STOP_AT_CYCLES && self.mark_phase && !self.cycle_found
|
||||
}
|
||||
|
||||
fn backward_and_return(&mut self) -> HeapCellValue {
|
||||
let mut current = self.heap[self.current];
|
||||
current.set_value(self.next);
|
||||
|
||||
if self.backward() {
|
||||
// set the f and m bits on the heap cell at start
|
||||
// so we invoke backward() and return None next call.
|
||||
|
||||
self.heap[self.current].set_forwarding_bit(false);
|
||||
self.heap[self.current].set_mark_bit(self.mark_phase);
|
||||
}
|
||||
|
||||
current
|
||||
}
|
||||
|
||||
fn traverse_subterm(&mut self, h: usize, arity: usize) -> Option<usize> {
|
||||
let mut last_cell_loc = h + arity - 1;
|
||||
|
||||
for idx in (h..h + arity).rev() {
|
||||
if self.heap[idx].get_forwarding_bit() {
|
||||
if self.cycle_detection_active() {
|
||||
self.cycle_found = true;
|
||||
return None;
|
||||
}
|
||||
|
||||
last_cell_loc -= 1;
|
||||
} else if self.heap[idx].get_mark_bit() == self.mark_phase {
|
||||
last_cell_loc -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Some(last_cell_loc)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn continue_forwarding(&self) -> bool {
|
||||
self.heap[self.current].get_mark_bit() != self.mark_phase
|
||||
|| self.heap[self.current].get_forwarding_bit()
|
||||
}
|
||||
|
||||
fn forward(&mut self) -> Option<HeapCellValue> {
|
||||
loop {
|
||||
if self.continue_forwarding() {
|
||||
match self.heap[self.current].get_tag() {
|
||||
tag @ HeapCellValueTag::AttrVar | tag @ HeapCellValueTag::Var => {
|
||||
let next = self.next as usize;
|
||||
|
||||
if self.heap[next].get_forwarding_bit() {
|
||||
return if self.current != next {
|
||||
if self.cycle_detection_active() {
|
||||
self.cycle_found = true;
|
||||
None
|
||||
} else {
|
||||
Some(self.backward_and_return())
|
||||
}
|
||||
} else {
|
||||
Some(self.backward_and_return())
|
||||
};
|
||||
} else if self.heap[next].get_mark_bit() == self.mark_phase {
|
||||
return Some(self.backward_and_return());
|
||||
}
|
||||
|
||||
self.heap[next].set_forwarding_bit(true);
|
||||
|
||||
let temp = self.heap[next].get_value();
|
||||
|
||||
self.heap[next].set_value(self.current as u64);
|
||||
self.current = next;
|
||||
self.next = temp;
|
||||
|
||||
if self.next < self.heap.len() as u64 {
|
||||
return Some(HeapCellValue::build_with(tag, next as u64));
|
||||
}
|
||||
}
|
||||
HeapCellValueTag::Str => {
|
||||
let h = self.next as usize;
|
||||
let cell = self.heap[h];
|
||||
let arity = cell_as_atom_cell!(self.heap[h]).get_arity();
|
||||
|
||||
let last_cell_loc = match self.traverse_subterm(h + 1, arity) {
|
||||
Some(last_cell_loc) => last_cell_loc,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
if last_cell_loc == h {
|
||||
if self.backward() {
|
||||
return None;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if self.cycle_detection_active() {
|
||||
for idx in (h + 1..last_cell_loc).rev() {
|
||||
if self.heap[idx].get_forwarding_bit() {
|
||||
self.cycle_found = true;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.heap[last_cell_loc].set_forwarding_bit(true);
|
||||
|
||||
self.next = self.heap[last_cell_loc].get_value();
|
||||
self.heap[last_cell_loc].set_value(self.current as u64);
|
||||
self.current = last_cell_loc;
|
||||
|
||||
return Some(cell);
|
||||
}
|
||||
HeapCellValueTag::Lis => {
|
||||
let mut cell = self.heap[self.current];
|
||||
cell.set_value(self.next);
|
||||
|
||||
let last_cell_loc = match self.traverse_subterm(self.next as usize, 2) {
|
||||
Some(last_cell_loc) => last_cell_loc,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
if self.cycle_detection_active() {
|
||||
for idx in (self.next as usize..last_cell_loc).rev() {
|
||||
if self.heap[idx].get_forwarding_bit() {
|
||||
self.cycle_found = true;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (last_cell_loc + 1) as u64 == self.next {
|
||||
if self.backward() {
|
||||
return None;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if last_cell_loc as u64 == self.next {
|
||||
// car cells of lists are both marked and forwarded.
|
||||
self.heap[last_cell_loc].set_mark_bit(self.mark_phase);
|
||||
}
|
||||
|
||||
self.heap[last_cell_loc].set_forwarding_bit(true);
|
||||
|
||||
self.next = self.heap[last_cell_loc].get_value();
|
||||
self.heap[last_cell_loc].set_value(self.current as u64);
|
||||
self.current = last_cell_loc;
|
||||
|
||||
return Some(cell);
|
||||
}
|
||||
HeapCellValueTag::PStrLoc => {
|
||||
let h = self.next as usize;
|
||||
let cell = self.heap[h];
|
||||
let last_cell_loc = h + 1;
|
||||
|
||||
if self.heap[last_cell_loc].get_forwarding_bit() {
|
||||
if self.cycle_detection_active() {
|
||||
self.cycle_found = true;
|
||||
return None;
|
||||
} else if self.backward() {
|
||||
return None;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
self.heap[last_cell_loc].set_forwarding_bit(true);
|
||||
|
||||
self.next = self.heap[last_cell_loc].get_value();
|
||||
self.heap[last_cell_loc].set_value(self.current as u64);
|
||||
self.current = last_cell_loc;
|
||||
|
||||
return Some(cell);
|
||||
}
|
||||
HeapCellValueTag::PStrOffset => {
|
||||
let h = self.next as usize;
|
||||
let cell = self.heap[h];
|
||||
let last_cell_loc = h + 1;
|
||||
|
||||
if self.heap[h].get_tag() == HeapCellValueTag::PStr {
|
||||
if self.heap[last_cell_loc].get_forwarding_bit() {
|
||||
if self.cycle_detection_active() {
|
||||
self.cycle_found = true;
|
||||
return None;
|
||||
} else if self.backward() {
|
||||
return None;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
self.heap[last_cell_loc].set_forwarding_bit(true);
|
||||
|
||||
self.next = self.heap[last_cell_loc].get_value();
|
||||
self.heap[last_cell_loc].set_value(self.current as u64);
|
||||
self.current = last_cell_loc;
|
||||
} else {
|
||||
debug_assert!(self.heap[h].get_tag() == HeapCellValueTag::CStr);
|
||||
|
||||
self.next = self.heap[h].get_value();
|
||||
self.heap[h].set_value(self.current as u64);
|
||||
self.current = h;
|
||||
}
|
||||
|
||||
return Some(cell);
|
||||
}
|
||||
tag @ HeapCellValueTag::Atom => {
|
||||
let cell = HeapCellValue::build_with(tag, self.next);
|
||||
let arity = AtomCell::from_bytes(cell.into_bytes()).get_arity();
|
||||
|
||||
if arity == 0 {
|
||||
return Some(self.backward_and_return());
|
||||
} else if self.backward() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
HeapCellValueTag::PStr => {
|
||||
if self.backward() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Some(self.backward_and_return());
|
||||
}
|
||||
}
|
||||
} else if self.backward() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pivot_subterm(&mut self) {
|
||||
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);
|
||||
|
||||
self.heap[self.current].set_forwarding_bit(true);
|
||||
}
|
||||
|
||||
fn continue_backward(&mut self) -> bool {
|
||||
self.heap[self.current].set_forwarding_bit(false);
|
||||
|
||||
if self.current == self.start {
|
||||
return false;
|
||||
}
|
||||
|
||||
let temp = self.heap[self.current].get_value();
|
||||
|
||||
match self.heap[temp as usize].get_tag() {
|
||||
HeapCellValueTag::Str => {
|
||||
let mut new_str_back_link = self.current;
|
||||
|
||||
for idx in (0..self.current).rev() {
|
||||
if self.heap[idx].get_tag() == HeapCellValueTag::Atom
|
||||
&& cell_as_atom_cell!(self.heap[idx]).get_arity() > 0
|
||||
{
|
||||
new_str_back_link = idx;
|
||||
break;
|
||||
}
|
||||
|
||||
if self.heap[idx].get_mark_bit() != self.mark_phase
|
||||
&& !self.heap[idx].get_forwarding_bit()
|
||||
{
|
||||
new_str_back_link = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.heap[self.current].set_mark_bit(self.mark_phase);
|
||||
self.heap[self.current].set_value(self.next);
|
||||
|
||||
let back_link_cell = self.heap[new_str_back_link];
|
||||
|
||||
self.next = back_link_cell.get_value();
|
||||
self.heap[new_str_back_link].set_value(temp);
|
||||
self.current = new_str_back_link;
|
||||
|
||||
read_heap_cell!(back_link_cell,
|
||||
(HeapCellValueTag::Atom, (_name, arity)) => {
|
||||
if arity > 0 {
|
||||
self.heap[self.current].set_mark_bit(self.mark_phase);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
);
|
||||
|
||||
self.heap[self.current].set_forwarding_bit(true);
|
||||
false
|
||||
}
|
||||
HeapCellValueTag::Lis => {
|
||||
if self.heap[self.current].get_mark_bit() == self.mark_phase {
|
||||
true
|
||||
} else {
|
||||
self.heap[self.current - 1].set_mark_bit(self.mark_phase);
|
||||
self.heap[self.current].set_mark_bit(self.mark_phase);
|
||||
|
||||
if self.heap[self.current - 1].get_forwarding_bit() {
|
||||
self.heap[self.current].set_value(self.next);
|
||||
self.next = self.current as u64 - 1;
|
||||
self.current = temp as usize;
|
||||
|
||||
true
|
||||
} else {
|
||||
self.pivot_subterm();
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.heap[self.current].set_mark_bit(self.mark_phase);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn backward(&mut self) -> bool {
|
||||
while self.continue_backward() {
|
||||
let temp = self.heap[self.current].get_value();
|
||||
|
||||
self.heap[self.current].set_value(self.next);
|
||||
self.next = self.current as u64;
|
||||
self.current = temp as usize;
|
||||
}
|
||||
|
||||
if self.current == self.start {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn invert_marker(&mut self) {
|
||||
self.cycle_found = false;
|
||||
|
||||
if self.heap[self.start].get_forwarding_bit() {
|
||||
while !self.backward() {}
|
||||
}
|
||||
|
||||
self.mark_phase = false;
|
||||
self.heap[self.start].set_forwarding_bit(true);
|
||||
|
||||
self.next = self.heap[self.start].get_value();
|
||||
self.current = self.start;
|
||||
|
||||
while self.forward().is_some() {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const STOP_AT_CYCLES: bool> Iterator for CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
type Item = HeapCellValue;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.forward()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const STOP_AT_CYCLES: bool> Drop for CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
fn drop(&mut self) {
|
||||
self.invert_marker();
|
||||
|
||||
if self.current == self.start {
|
||||
return;
|
||||
}
|
||||
|
||||
while !self.backward() {}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ impl BranchNumber {
|
||||
fn halve_delta(&self) -> BranchNumber {
|
||||
BranchNumber {
|
||||
branch_num: self.branch_num.clone(),
|
||||
delta : &self.delta / Rational::from(2),
|
||||
delta: &self.delta / Rational::from(2),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,10 @@ pub struct BranchInfo {
|
||||
|
||||
impl BranchInfo {
|
||||
fn new(branch_num: BranchNumber) -> Self {
|
||||
Self { branch_num, chunks: vec![] }
|
||||
Self {
|
||||
branch_num,
|
||||
chunks: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,11 +152,14 @@ enum TraversalState {
|
||||
// where it leaves off.
|
||||
BuildFinalDisjunct(usize),
|
||||
Fail,
|
||||
GetCutPoint{ var_num: usize, prev_b: bool },
|
||||
GetCutPoint { var_num: usize, prev_b: bool },
|
||||
Cut { var_num: usize, is_global: bool },
|
||||
CutPrev(usize),
|
||||
ResetCallPolicy(CallPolicy),
|
||||
Term(Term),
|
||||
RemoveBranchNum, // pop the current_branch_num and from the root set.
|
||||
OverrideGlobalCutVar(usize),
|
||||
ResetGlobalCutVarOverride(Option<usize>),
|
||||
RemoveBranchNum, // pop the current_branch_num and from the root set.
|
||||
AddBranchNum(BranchNumber), // set current_branch_num, add it to the root set
|
||||
RepBranchNum(BranchNumber), // replace current_branch_num and the latest in the root set
|
||||
}
|
||||
@@ -168,6 +174,7 @@ pub struct VariableClassifier {
|
||||
var_num: usize,
|
||||
root_set: RootSet,
|
||||
global_cut_var_num: Option<usize>,
|
||||
global_cut_var_num_override: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -179,22 +186,22 @@ pub struct VarData {
|
||||
|
||||
impl VarData {
|
||||
fn emit_initial_get_level(&mut self, build_stack: &mut ChunkedTermVec) {
|
||||
let global_cut_var_num =
|
||||
if let &Some(global_cut_var_num) = &self.global_cut_var_num {
|
||||
match &self.records[global_cut_var_num].allocation {
|
||||
VarAlloc::Perm(..) => Some(global_cut_var_num),
|
||||
VarAlloc::Temp { term_loc, .. } if term_loc.chunk_num() > 0 => {
|
||||
Some(global_cut_var_num)
|
||||
}
|
||||
_ => None
|
||||
let global_cut_var_num = if let &Some(global_cut_var_num) = &self.global_cut_var_num {
|
||||
match &self.records[global_cut_var_num].allocation {
|
||||
VarAlloc::Perm(..) => Some(global_cut_var_num),
|
||||
VarAlloc::Temp { term_loc, .. } if term_loc.chunk_num() > 0 => {
|
||||
Some(global_cut_var_num)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(global_cut_var_num) = global_cut_var_num {
|
||||
let term = QueryTerm::GetLevel(global_cut_var_num);
|
||||
self.records[global_cut_var_num].allocation = VarAlloc::Perm(0, PermVarAllocation::Pending);
|
||||
self.records[global_cut_var_num].allocation =
|
||||
VarAlloc::Perm(0, PermVarAllocation::Pending);
|
||||
|
||||
match build_stack.front_mut() {
|
||||
Some(ChunkedTerms::Branch(_)) => {
|
||||
@@ -219,7 +226,7 @@ fn merge_branch_seq(branches: impl Iterator<Item = BranchInfo>) -> BranchInfo {
|
||||
|
||||
for mut branch in branches {
|
||||
branch_info.branch_num = branch.branch_num;
|
||||
branch_info.chunks.extend(branch.chunks.drain(..));
|
||||
branch_info.chunks.append(&mut branch.chunks);
|
||||
}
|
||||
|
||||
branch_info.branch_num.delta = branch_info.branch_num.delta * Integer::from(2);
|
||||
@@ -229,7 +236,7 @@ fn merge_branch_seq(branches: impl Iterator<Item = BranchInfo>) -> BranchInfo {
|
||||
}
|
||||
|
||||
fn flatten_into_disjunct(build_stack: &mut ChunkedTermVec, preceding_len: usize) {
|
||||
let branch_vec = build_stack.drain(preceding_len + 1 ..).collect();
|
||||
let branch_vec = build_stack.drain(preceding_len + 1..).collect();
|
||||
|
||||
if let ChunkedTerms::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] {
|
||||
disjuncts.push(branch_vec);
|
||||
@@ -249,16 +256,20 @@ impl VariableClassifier {
|
||||
root_set: RootSet::new(),
|
||||
var_num: 0,
|
||||
global_cut_var_num: None,
|
||||
global_cut_var_num_override: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn classify_fact(mut self, term: Term) -> Result<ClassifyFactResult, CompilationError> {
|
||||
self.classify_head_variables(&term)?;
|
||||
Ok((term, self.branch_map.separate_and_classify_variables(
|
||||
self.var_num,
|
||||
self.global_cut_var_num,
|
||||
self.current_chunk_num,
|
||||
)))
|
||||
Ok((
|
||||
term,
|
||||
self.branch_map.separate_and_classify_variables(
|
||||
self.var_num,
|
||||
self.global_cut_var_num,
|
||||
self.current_chunk_num,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn classify_rule<'a, LS: LoadState<'a>>(
|
||||
@@ -287,7 +298,7 @@ impl VariableClassifier {
|
||||
|
||||
fn merge_branches(&mut self) {
|
||||
for branches in self.branch_map.values_mut() {
|
||||
let mut old_branches = std::mem::replace(branches, vec![]);
|
||||
let mut old_branches = std::mem::take(branches);
|
||||
|
||||
while let Some(last_branch_num) = old_branches.last().map(|bi| &bi.branch_num) {
|
||||
let mut old_branches_len = old_branches.len();
|
||||
@@ -298,7 +309,7 @@ impl VariableClassifier {
|
||||
}
|
||||
}
|
||||
|
||||
let iter = old_branches.drain(old_branches_len - 1 ..);
|
||||
let iter = old_branches.drain(old_branches_len - 1..);
|
||||
branches.push(merge_branch_seq(iter));
|
||||
}
|
||||
|
||||
@@ -346,10 +357,11 @@ impl VariableClassifier {
|
||||
}
|
||||
|
||||
fn probe_body_var(&mut self, var_info: VarInfo) {
|
||||
let term_loc = self.current_chunk_type.to_gen_context(self.current_chunk_num);
|
||||
let term_loc = self
|
||||
.current_chunk_type
|
||||
.to_gen_context(self.current_chunk_num);
|
||||
|
||||
let branch_info_v = self.branch_map.entry(var_info.var_ptr.clone())
|
||||
.or_insert_with(|| vec![]);
|
||||
let branch_info_v = self.branch_map.entry(var_info.var_ptr.clone()).or_default();
|
||||
|
||||
let needs_new_branch = if let Some(last_bi) = branch_info_v.last() {
|
||||
!self.root_set.contains(&last_bi.branch_num)
|
||||
@@ -396,60 +408,58 @@ impl VariableClassifier {
|
||||
|
||||
fn classify_head_variables(&mut self, term: &Term) -> Result<(), CompilationError> {
|
||||
match term {
|
||||
Term::Clause(..) | Term::Literal(_, Literal::Atom(_)) => {
|
||||
}
|
||||
Term::Clause(..) | Term::Literal(_, Literal::Atom(_)) => {}
|
||||
_ => return Err(CompilationError::InvalidRuleHead),
|
||||
}
|
||||
|
||||
let mut classify_info = ClassifyInfo { arg_c: 1, arity: term.arity() };
|
||||
let mut classify_info = ClassifyInfo {
|
||||
arg_c: 1,
|
||||
arity: term.arity(),
|
||||
};
|
||||
|
||||
match term {
|
||||
Term::Clause(_, _, terms) => {
|
||||
for term in terms.into_iter() {
|
||||
for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) {
|
||||
if let TermRef::Var(lvl, _, var_ptr) = term_ref {
|
||||
// a body term, so we need the child level here.
|
||||
let lvl = lvl.child_level();
|
||||
if let Term::Clause(_, _, terms) = term {
|
||||
for term in terms.iter() {
|
||||
for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) {
|
||||
if let TermRef::Var(lvl, _, var_ptr) = term_ref {
|
||||
// a body term, so we need the child level here.
|
||||
let lvl = lvl.child_level();
|
||||
|
||||
// the body of the if let here is an inlined
|
||||
// "probe_head_var". note the difference between it
|
||||
// and "probe_body_var".
|
||||
let branch_info_v = self.branch_map.entry(var_ptr.clone())
|
||||
.or_insert_with(|| vec![]);
|
||||
// the body of the if let here is an inlined
|
||||
// "probe_head_var". note the difference between it
|
||||
// and "probe_body_var".
|
||||
let branch_info_v = self.branch_map.entry(var_ptr.clone()).or_default();
|
||||
|
||||
let needs_new_branch = branch_info_v.is_empty();
|
||||
let needs_new_branch = branch_info_v.is_empty();
|
||||
|
||||
if needs_new_branch {
|
||||
branch_info_v.push(BranchInfo::new(self.current_branch_num.clone()));
|
||||
}
|
||||
|
||||
let branch_info = branch_info_v.last_mut().unwrap();
|
||||
let needs_new_chunk = branch_info.chunks.is_empty();
|
||||
|
||||
if needs_new_chunk {
|
||||
branch_info.chunks.push(ChunkInfo {
|
||||
chunk_num: self.current_chunk_num,
|
||||
term_loc: GenContext::Head,
|
||||
vars: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
let chunk_info = branch_info.chunks.last_mut().unwrap();
|
||||
let var_info = VarInfo {
|
||||
var_ptr,
|
||||
classify_info,
|
||||
chunk_type: self.current_chunk_type,
|
||||
lvl,
|
||||
};
|
||||
|
||||
chunk_info.vars.push(var_info);
|
||||
if needs_new_branch {
|
||||
branch_info_v.push(BranchInfo::new(self.current_branch_num.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
classify_info.arg_c += 1;
|
||||
let branch_info = branch_info_v.last_mut().unwrap();
|
||||
let needs_new_chunk = branch_info.chunks.is_empty();
|
||||
|
||||
if needs_new_chunk {
|
||||
branch_info.chunks.push(ChunkInfo {
|
||||
chunk_num: self.current_chunk_num,
|
||||
term_loc: GenContext::Head,
|
||||
vars: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
let chunk_info = branch_info.chunks.last_mut().unwrap();
|
||||
let var_info = VarInfo {
|
||||
var_ptr,
|
||||
classify_info,
|
||||
chunk_type: self.current_chunk_type,
|
||||
lvl,
|
||||
};
|
||||
|
||||
chunk_info.vars.push(var_info);
|
||||
}
|
||||
}
|
||||
|
||||
classify_info.arg_c += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -502,6 +512,12 @@ impl VariableClassifier {
|
||||
self.probe_in_situ_var(var_num);
|
||||
build_stack.push_chunk_term(QueryTerm::GetCutPoint { var_num, prev_b });
|
||||
}
|
||||
TraversalState::OverrideGlobalCutVar(var_num) => {
|
||||
self.global_cut_var_num_override = Some(var_num);
|
||||
}
|
||||
TraversalState::ResetGlobalCutVarOverride(old_override) => {
|
||||
self.global_cut_var_num_override = old_override;
|
||||
}
|
||||
TraversalState::Cut { var_num, is_global } => {
|
||||
if self.try_set_chunk_at_inlined_boundary() {
|
||||
build_stack.add_chunk();
|
||||
@@ -509,13 +525,26 @@ impl VariableClassifier {
|
||||
|
||||
self.probe_in_situ_var(var_num);
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
if is_global {
|
||||
QueryTerm::GlobalCut(var_num)
|
||||
} else {
|
||||
QueryTerm::LocalCut(var_num)
|
||||
build_stack.push_chunk_term(if is_global {
|
||||
QueryTerm::GlobalCut(var_num)
|
||||
} else {
|
||||
QueryTerm::LocalCut {
|
||||
var_num,
|
||||
cut_prev: false,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
TraversalState::CutPrev(var_num) => {
|
||||
if self.try_set_chunk_at_inlined_boundary() {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
self.probe_in_situ_var(var_num);
|
||||
|
||||
build_stack.push_chunk_term(QueryTerm::LocalCut {
|
||||
var_num,
|
||||
cut_prev: true,
|
||||
});
|
||||
}
|
||||
TraversalState::Fail => {
|
||||
build_stack.push_chunk_term(QueryTerm::Fail);
|
||||
@@ -539,22 +568,28 @@ impl VariableClassifier {
|
||||
classifier.probe_body_term(arg_c + 1, terms.len(), term);
|
||||
}
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
clause_to_query_term(
|
||||
loader,
|
||||
name,
|
||||
terms,
|
||||
classifier.call_policy,
|
||||
),
|
||||
);
|
||||
build_stack.push_chunk_term(clause_to_query_term(
|
||||
loader,
|
||||
name,
|
||||
terms,
|
||||
classifier.call_policy,
|
||||
));
|
||||
};
|
||||
|
||||
match term {
|
||||
Term::Clause(_, name @ (atom!("->") | atom!(";") | atom!(",")), mut terms) if terms.len() == 3 => {
|
||||
Term::Clause(
|
||||
_,
|
||||
name @ (atom!("->") | atom!(";") | atom!(",")),
|
||||
mut terms,
|
||||
) if terms.len() == 3 => {
|
||||
if let Some(last_arg) = terms.last() {
|
||||
if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg {
|
||||
terms.pop();
|
||||
state_stack.push(TraversalState::Term(Term::Clause(Cell::default(), name, terms)));
|
||||
state_stack.push(TraversalState::Term(Term::Clause(
|
||||
Cell::default(),
|
||||
name,
|
||||
terms,
|
||||
)));
|
||||
} else {
|
||||
add_chunk(self, name, terms);
|
||||
}
|
||||
@@ -583,7 +618,7 @@ impl VariableClassifier {
|
||||
|
||||
let mut branch_numbers = vec![first_branch_num];
|
||||
|
||||
for idx in 1 .. branches.len() {
|
||||
for idx in 1..branches.len() {
|
||||
let succ_branch_number = branch_numbers[idx - 1].incr_by_delta();
|
||||
|
||||
branch_numbers.push(if idx + 1 < branches.len() {
|
||||
@@ -610,8 +645,11 @@ impl VariableClassifier {
|
||||
state_stack.push(TraversalState::AddBranchNum(branch_num));
|
||||
}
|
||||
|
||||
if let TraversalState::BuildDisjunct(build_stack_len) = state_stack[final_disjunct_loc] {
|
||||
state_stack[final_disjunct_loc] = TraversalState::BuildFinalDisjunct(build_stack_len);
|
||||
if let TraversalState::BuildDisjunct(build_stack_len) =
|
||||
state_stack[final_disjunct_loc]
|
||||
{
|
||||
state_stack[final_disjunct_loc] =
|
||||
TraversalState::BuildFinalDisjunct(build_stack_len);
|
||||
}
|
||||
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
@@ -621,18 +659,34 @@ impl VariableClassifier {
|
||||
let then_term = terms.pop().unwrap();
|
||||
let if_term = terms.pop().unwrap();
|
||||
|
||||
let prev_b = if matches!(state_stack.last(), Some(TraversalState::RemoveBranchNum)) {
|
||||
// check if the second-to-last element is a regular BuildDisjunct, as we don't
|
||||
// want to add GetPrevLevel in case of a TrustMe.
|
||||
matches!(state_stack.iter().rev().nth(1), Some(TraversalState::BuildDisjunct(..)))
|
||||
let prev_b = if matches!(
|
||||
state_stack.last(),
|
||||
Some(TraversalState::RemoveBranchNum)
|
||||
) {
|
||||
// check if the second-to-last element
|
||||
// is a regular BuildDisjunct, as we
|
||||
// don't want to add GetPrevLevel in
|
||||
// case of a TrustMe.
|
||||
match state_stack.iter().rev().nth(1) {
|
||||
Some(&TraversalState::BuildDisjunct(preceding_len)) => {
|
||||
preceding_len + 1 == build_stack.len()
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
state_stack.push(TraversalState::Term(then_term));
|
||||
state_stack.push(TraversalState::Cut { var_num: self.var_num, is_global: false });
|
||||
state_stack.push(TraversalState::Cut {
|
||||
var_num: self.var_num,
|
||||
is_global: false,
|
||||
});
|
||||
state_stack.push(TraversalState::Term(if_term));
|
||||
state_stack.push(TraversalState::GetCutPoint { var_num: self.var_num, prev_b });
|
||||
state_stack.push(TraversalState::GetCutPoint {
|
||||
var_num: self.var_num,
|
||||
prev_b,
|
||||
});
|
||||
|
||||
self.var_num += 1;
|
||||
}
|
||||
@@ -643,12 +697,23 @@ impl VariableClassifier {
|
||||
build_stack.reserve_branch(2);
|
||||
|
||||
state_stack.push(TraversalState::BuildFinalDisjunct(build_stack_len));
|
||||
state_stack.push(TraversalState::Term(Term::Clause(Cell::default(), atom!("$succeed"), vec![])));
|
||||
state_stack.push(TraversalState::Term(Term::Clause(
|
||||
Cell::default(),
|
||||
atom!("$succeed"),
|
||||
vec![],
|
||||
)));
|
||||
state_stack.push(TraversalState::BuildDisjunct(build_stack_len));
|
||||
state_stack.push(TraversalState::Fail);
|
||||
state_stack.push(TraversalState::Cut { var_num: self.var_num, is_global: false });
|
||||
state_stack.push(TraversalState::CutPrev(self.var_num));
|
||||
state_stack.push(TraversalState::ResetGlobalCutVarOverride(
|
||||
self.global_cut_var_num_override,
|
||||
));
|
||||
state_stack.push(TraversalState::Term(not_term));
|
||||
state_stack.push(TraversalState::GetCutPoint { var_num: self.var_num, prev_b: true });
|
||||
state_stack.push(TraversalState::OverrideGlobalCutVar(self.var_num));
|
||||
state_stack.push(TraversalState::GetCutPoint {
|
||||
var_num: self.var_num,
|
||||
prev_b: false,
|
||||
});
|
||||
|
||||
self.current_chunk_type = ChunkType::Mid;
|
||||
self.current_chunk_num += 1;
|
||||
@@ -668,15 +733,13 @@ impl VariableClassifier {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
qualified_clause_to_query_term(
|
||||
loader,
|
||||
module_name,
|
||||
predicate_name,
|
||||
vec![],
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
build_stack.push_chunk_term(qualified_clause_to_query_term(
|
||||
loader,
|
||||
module_name,
|
||||
predicate_name,
|
||||
vec![],
|
||||
self.call_policy,
|
||||
));
|
||||
}
|
||||
(
|
||||
Term::Literal(_, Literal::Atom(module_name)),
|
||||
@@ -690,15 +753,13 @@ impl VariableClassifier {
|
||||
self.probe_body_term(arg_c + 1, terms.len(), term);
|
||||
}
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
qualified_clause_to_query_term(
|
||||
loader,
|
||||
module_name,
|
||||
name,
|
||||
terms,
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
build_stack.push_chunk_term(qualified_clause_to_query_term(
|
||||
loader,
|
||||
module_name,
|
||||
name,
|
||||
terms,
|
||||
self.call_policy,
|
||||
));
|
||||
}
|
||||
(module_name, predicate_name) => {
|
||||
if update_chunk_data(self, atom!("call"), 2) {
|
||||
@@ -711,18 +772,18 @@ impl VariableClassifier {
|
||||
terms.push(module_name);
|
||||
terms.push(predicate_name);
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
clause_to_query_term(
|
||||
loader,
|
||||
atom!("call"),
|
||||
vec![Term::Clause(Cell::default(), atom!(":"), terms)],
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
build_stack.push_chunk_term(clause_to_query_term(
|
||||
loader,
|
||||
atom!("call"),
|
||||
vec![Term::Clause(Cell::default(), atom!(":"), terms)],
|
||||
self.call_policy,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Term::Clause(_, atom!("$call_with_inference_counting"), mut terms) if terms.len() == 1 => {
|
||||
Term::Clause(_, atom!("$call_with_inference_counting"), mut terms)
|
||||
if terms.len() == 1 =>
|
||||
{
|
||||
state_stack.push(TraversalState::ResetCallPolicy(self.call_policy));
|
||||
state_stack.push(TraversalState::Term(terms.pop().unwrap()));
|
||||
|
||||
@@ -738,41 +799,43 @@ impl VariableClassifier {
|
||||
|
||||
self.probe_body_term(1, 1, &var);
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
clause_to_query_term(
|
||||
loader,
|
||||
atom!("call"),
|
||||
vec![var],
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
build_stack.push_chunk_term(clause_to_query_term(
|
||||
loader,
|
||||
atom!("call"),
|
||||
vec![var],
|
||||
self.call_policy,
|
||||
));
|
||||
}
|
||||
Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => {
|
||||
if self.global_cut_var_num.is_none() {
|
||||
self.global_cut_var_num = Some(self.var_num);
|
||||
self.var_num += 1;
|
||||
}
|
||||
let (var_num, is_global) =
|
||||
if let Some(var_num) = self.global_cut_var_num_override {
|
||||
(var_num, false)
|
||||
} else if let Some(var_num) = self.global_cut_var_num {
|
||||
(var_num, true)
|
||||
} else {
|
||||
let var_num = self.var_num;
|
||||
|
||||
self.probe_in_situ_var(self.global_cut_var_num.unwrap());
|
||||
self.global_cut_var_num = Some(var_num);
|
||||
self.var_num += 1;
|
||||
|
||||
state_stack.push(TraversalState::Cut {
|
||||
var_num: self.global_cut_var_num.unwrap(),
|
||||
is_global: true,
|
||||
});
|
||||
(var_num, true)
|
||||
};
|
||||
|
||||
self.probe_in_situ_var(var_num);
|
||||
|
||||
state_stack.push(TraversalState::Cut { var_num, is_global });
|
||||
}
|
||||
Term::Literal(_, Literal::Atom(name)) => {
|
||||
if update_chunk_data(self, name, 0) {
|
||||
build_stack.add_chunk();
|
||||
}
|
||||
|
||||
build_stack.push_chunk_term(
|
||||
clause_to_query_term(
|
||||
loader,
|
||||
name,
|
||||
vec![],
|
||||
self.call_policy,
|
||||
),
|
||||
);
|
||||
build_stack.push_chunk_term(clause_to_query_term(
|
||||
loader,
|
||||
name,
|
||||
vec![],
|
||||
self.call_policy,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
return Err(CompilationError::InadmissibleQueryTerm);
|
||||
@@ -800,12 +863,11 @@ impl BranchMap {
|
||||
};
|
||||
|
||||
for (var, branches) in self.iter_mut() {
|
||||
let (mut var_num, var_num_incr) =
|
||||
if let Var::InSitu(var_num) = *var.borrow() {
|
||||
(var_num, false)
|
||||
} else {
|
||||
(var_data.records.len(), true)
|
||||
};
|
||||
let (mut var_num, var_num_incr) = if let Var::InSitu(var_num) = *var.borrow() {
|
||||
(var_num, false)
|
||||
} else {
|
||||
(var_data.records.len(), true)
|
||||
};
|
||||
|
||||
for branch in branches.iter_mut() {
|
||||
if var_num_incr {
|
||||
@@ -813,7 +875,8 @@ impl BranchMap {
|
||||
var_data.records.push(VariableRecord::default());
|
||||
}
|
||||
|
||||
if branch.chunks.len() <= 1 { // true iff var is a temporary variable.
|
||||
if branch.chunks.len() <= 1 {
|
||||
// true iff var is a temporary variable.
|
||||
debug_assert_eq!(branch.chunks.len(), 1);
|
||||
|
||||
let chunk = &mut branch.chunks[0];
|
||||
@@ -822,7 +885,9 @@ impl BranchMap {
|
||||
for var_info in chunk.vars.iter_mut() {
|
||||
if var_info.lvl == Level::Shallow {
|
||||
let term_loc = var_info.chunk_type.to_gen_context(chunk.chunk_num);
|
||||
temp_var_data.use_set.insert((term_loc, var_info.classify_info.arg_c));
|
||||
temp_var_data
|
||||
.use_set
|
||||
.insert((term_loc, var_info.classify_info.arg_c));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1557
src/machine/gc.rs
1557
src/machine/gc.rs
File diff suppressed because it is too large
Load Diff
@@ -130,11 +130,7 @@ pub fn print_heap_terms<'a, I: Iterator<Item = &'a HeapCellValue>>(heap: I, h: u
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn put_complete_string(
|
||||
heap: &mut Heap,
|
||||
s: &str,
|
||||
atom_tbl: &mut AtomTable,
|
||||
) -> HeapCellValue {
|
||||
pub(crate) fn put_complete_string(heap: &mut Heap, s: &str, atom_tbl: &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.
|
||||
@@ -157,11 +153,7 @@ pub(crate) fn put_complete_string(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn put_partial_string(
|
||||
heap: &mut Heap,
|
||||
s: &str,
|
||||
atom_tbl: &mut AtomTable,
|
||||
) -> HeapCellValue {
|
||||
pub(crate) fn put_partial_string(heap: &mut Heap, s: &str, atom_tbl: &AtomTable) -> HeapCellValue {
|
||||
match allocate_pstr(heap, s, atom_tbl) {
|
||||
Some(h) => {
|
||||
pstr_loc_as_cell!(h)
|
||||
@@ -173,15 +165,11 @@ pub(crate) fn put_partial_string(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn allocate_pstr(
|
||||
heap: &mut Heap,
|
||||
mut src: &str,
|
||||
atom_tbl: &mut AtomTable,
|
||||
) -> Option<usize> {
|
||||
pub(crate) fn allocate_pstr(heap: &mut Heap, mut src: &str, atom_tbl: &AtomTable) -> Option<usize> {
|
||||
let orig_h = heap.len();
|
||||
|
||||
loop {
|
||||
if src == "" {
|
||||
if src.is_empty() {
|
||||
return if orig_h == heap.len() {
|
||||
None
|
||||
} else {
|
||||
@@ -211,7 +199,7 @@ pub(crate) fn allocate_pstr(
|
||||
|
||||
heap.push(string_as_pstr_cell!(pstr));
|
||||
|
||||
if rest_src != "" {
|
||||
if !rest_src.is_empty() {
|
||||
heap.push(pstr_loc_as_cell!(h + 2));
|
||||
src = rest_src;
|
||||
} else {
|
||||
@@ -258,7 +246,10 @@ pub(crate) fn to_local_code_ptr(heap: &Heap, addr: HeapCellValue) -> Option<usiz
|
||||
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(),
|
||||
Ok(Number::Integer(n)) => {
|
||||
let value: usize = (&*n).try_into().unwrap();
|
||||
Some(value)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
13106
src/machine/lib_integration_test_commands.txt
Normal file
13106
src/machine/lib_integration_test_commands.txt
Normal file
File diff suppressed because one or more lines are too long
612
src/machine/lib_machine.rs
Normal file
612
src/machine/lib_machine.rs
Normal file
@@ -0,0 +1,612 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::atom_table;
|
||||
use crate::heap_print::{HCPrinter, HCValueOutputter, PrinterOutputter};
|
||||
use crate::machine::machine_indices::VarKey;
|
||||
use crate::machine::mock_wam::CompositeOpDir;
|
||||
use crate::machine::{BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS};
|
||||
use crate::parser::ast::{Var, VarPtr};
|
||||
use crate::parser::parser::{Parser, Tokens};
|
||||
use crate::read::write_term_to_heap;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use super::{
|
||||
streams::Stream, Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine, MachineConfig,
|
||||
QueryResolution, QueryResolutionLine, QueryResult, Value,
|
||||
};
|
||||
|
||||
impl Machine {
|
||||
pub fn new_lib() -> Self {
|
||||
Machine::new(MachineConfig::in_memory())
|
||||
}
|
||||
|
||||
pub fn load_module_string(&mut self, module_name: &str, program: String) {
|
||||
let stream = Stream::from_owned_string(program, &mut self.machine_st.arena);
|
||||
self.load_file(module_name, stream);
|
||||
}
|
||||
|
||||
pub fn consult_module_string(&mut self, module_name: &str, program: String) {
|
||||
let stream = Stream::from_owned_string(program, &mut self.machine_st.arena);
|
||||
self.machine_st.registers[1] = stream_as_cell!(stream);
|
||||
self.machine_st.registers[2] = atom_as_cell!(&atom_table::AtomTable::build_with(
|
||||
&self.machine_st.atom_tbl,
|
||||
module_name
|
||||
));
|
||||
|
||||
self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2));
|
||||
}
|
||||
|
||||
fn allocate_stub_choice_point(&mut self) {
|
||||
// NOTE: create a choice point to terminate the dispatch_loop
|
||||
// if an exception is thrown.
|
||||
|
||||
let stub_b = self.machine_st.stack.allocate_or_frame(0);
|
||||
let or_frame = self.machine_st.stack.index_or_frame_mut(stub_b);
|
||||
|
||||
or_frame.prelude.num_cells = 0;
|
||||
or_frame.prelude.e = 0;
|
||||
or_frame.prelude.cp = 0;
|
||||
or_frame.prelude.b = 0;
|
||||
or_frame.prelude.bp = BREAK_FROM_DISPATCH_LOOP_LOC;
|
||||
or_frame.prelude.boip = 0;
|
||||
or_frame.prelude.biip = 0;
|
||||
or_frame.prelude.tr = 0;
|
||||
or_frame.prelude.h = 0;
|
||||
or_frame.prelude.b0 = 0;
|
||||
or_frame.prelude.attr_var_queue_len = 0;
|
||||
|
||||
self.machine_st.b = stub_b;
|
||||
self.machine_st.hb = self.machine_st.heap.len();
|
||||
self.machine_st.block = stub_b;
|
||||
}
|
||||
|
||||
pub fn run_query(&mut self, query: String) -> QueryResult {
|
||||
// println!("Query: {}", query);
|
||||
// Parse the query so we can analyze and then call the term
|
||||
let mut parser = Parser::new(
|
||||
Stream::from_owned_string(query, &mut self.machine_st.arena),
|
||||
&mut self.machine_st,
|
||||
);
|
||||
let op_dir = CompositeOpDir::new(&self.indices.op_dir, None);
|
||||
let term = parser
|
||||
.read_term(&op_dir, Tokens::Default)
|
||||
.expect("Failed to parse query");
|
||||
|
||||
self.allocate_stub_choice_point();
|
||||
|
||||
// Write parsed term to heap
|
||||
let term_write_result =
|
||||
write_term_to_heap(&term, &mut self.machine_st.heap, &self.machine_st.atom_tbl)
|
||||
.expect("couldn't write term to heap");
|
||||
|
||||
let var_names: IndexMap<_, _> = term_write_result
|
||||
.var_dict
|
||||
.iter()
|
||||
.map(|(var_key, cell)| match var_key {
|
||||
// NOTE: not the intention behind Var::InSitu here but
|
||||
// we can hijack it to store anonymous variables
|
||||
// without creating problems.
|
||||
VarKey::AnonVar(h) => (*cell, VarPtr::from(Var::InSitu(*h))),
|
||||
VarKey::VarPtr(var_ptr) => (*cell, var_ptr.clone()),
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Write term to heap
|
||||
self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc];
|
||||
|
||||
self.machine_st.cp = LIB_QUERY_SUCCESS; // BREAK_FROM_DISPATCH_LOOP_LOC;
|
||||
let call_index_p = self
|
||||
.indices
|
||||
.code_dir
|
||||
.get(&(atom!("call"), 1))
|
||||
.expect("couldn't get code index")
|
||||
.local()
|
||||
.unwrap();
|
||||
|
||||
self.machine_st.execute_at_index(1, call_index_p);
|
||||
|
||||
let stub_b = self.machine_st.b;
|
||||
|
||||
let mut matches: Vec<QueryResolutionLine> = Vec::new();
|
||||
// Call the term
|
||||
loop {
|
||||
self.dispatch_loop();
|
||||
|
||||
//println!("b: {}", self.machine_st.b);
|
||||
//println!("stub_b: {}", stub_b);
|
||||
//println!("fail: {}", self.machine_st.fail);
|
||||
|
||||
if !self.machine_st.ball.stub.is_empty() {
|
||||
// NOTE: this means an exception was thrown, at which
|
||||
// point we backtracked to the stub choice point.
|
||||
// this should halt the search for solutions as it
|
||||
// does in the Scryer top-level. the exception term is
|
||||
// contained in self.machine_st.ball.
|
||||
let error_string = self
|
||||
.machine_st
|
||||
.ball
|
||||
.stub
|
||||
.iter()
|
||||
.filter(|h| {
|
||||
matches!(
|
||||
h.get_tag(),
|
||||
HeapCellValueTag::Atom | HeapCellValueTag::Fixnum
|
||||
)
|
||||
})
|
||||
.map(|h| match h.get_tag() {
|
||||
HeapCellValueTag::Atom => {
|
||||
let (name, _) = cell_as_atom_cell!(h).get_name_and_arity();
|
||||
name.as_str().to_string()
|
||||
}
|
||||
HeapCellValueTag::Fixnum => h.get_value().clone().to_string(),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ");
|
||||
|
||||
return Err(error_string);
|
||||
}
|
||||
|
||||
/*
|
||||
if self.machine_st.fail {
|
||||
// NOTE: only print results on success
|
||||
self.machine_st.fail = false;
|
||||
println!("fail!");
|
||||
matches.push(QueryResolutionLine::False);
|
||||
break;
|
||||
};
|
||||
*/
|
||||
|
||||
if self.machine_st.p == LIB_QUERY_SUCCESS {
|
||||
if term_write_result.var_dict.is_empty() {
|
||||
matches.push(QueryResolutionLine::True);
|
||||
break;
|
||||
}
|
||||
} else if self.machine_st.p == BREAK_FROM_DISPATCH_LOOP_LOC {
|
||||
// NOTE: only print results on success
|
||||
// self.machine_st.fail = false;
|
||||
// println!("b == stub_b");
|
||||
matches.push(QueryResolutionLine::False);
|
||||
break;
|
||||
}
|
||||
|
||||
let mut bindings: BTreeMap<String, Value> = BTreeMap::new();
|
||||
|
||||
for (var_key, term_to_be_printed) in &term_write_result.var_dict {
|
||||
if var_key.to_string().starts_with('_') {
|
||||
continue;
|
||||
}
|
||||
let mut printer = HCPrinter::new(
|
||||
&mut self.machine_st.heap,
|
||||
Arc::clone(&self.machine_st.atom_tbl),
|
||||
&mut self.machine_st.stack,
|
||||
&self.indices.op_dir,
|
||||
PrinterOutputter::new(),
|
||||
*term_to_be_printed,
|
||||
);
|
||||
|
||||
printer.ignore_ops = false;
|
||||
printer.numbervars = true;
|
||||
printer.quoted = true;
|
||||
printer.max_depth = 1000; // NOTE: set this to 0 for unbounded depth
|
||||
printer.double_quotes = true;
|
||||
printer.var_names = var_names.clone();
|
||||
|
||||
let outputter = printer.print();
|
||||
|
||||
let output: String = outputter.result();
|
||||
// println!("Result: {} = {}", var_key.to_string(), output);
|
||||
|
||||
if var_key.to_string() != output {
|
||||
bindings.insert(
|
||||
var_key.to_string(),
|
||||
Value::try_from(output).expect("Couldn't convert Houtput to Value"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
matches.push(QueryResolutionLine::Match(bindings));
|
||||
|
||||
// NOTE: there are outstanding choicepoints, backtrack
|
||||
// through them for further solutions. if
|
||||
// self.machine_st.b == stub_b we've backtracked to the stub
|
||||
// choice point, so we should break.
|
||||
self.machine_st.backtrack();
|
||||
|
||||
if self.machine_st.b <= stub_b {
|
||||
// NOTE: out of choicepoints to backtrack through, no
|
||||
// more solutions to gather.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: deallocate stub choice point
|
||||
if self.machine_st.b == stub_b {
|
||||
self.trust_me();
|
||||
}
|
||||
|
||||
Ok(QueryResolution::from(matches))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use super::*;
|
||||
use crate::machine::{QueryMatch, QueryResolution, Value};
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
fn programatic_query() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from(r#"triple("a",P,"b")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(
|
||||
output,
|
||||
Ok(QueryResolution::Matches(vec![
|
||||
QueryMatch::from(btreemap! {
|
||||
"P" => Value::from("p1"),
|
||||
}),
|
||||
QueryMatch::from(btreemap! {
|
||||
"P" => Value::from("p2"),
|
||||
}),
|
||||
]))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
machine.run_query(String::from(r#"triple("a","p1","b")."#)),
|
||||
Ok(QueryResolution::True)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
machine.run_query(String::from(r#"triple("x","y","z")."#)),
|
||||
Ok(QueryResolution::False)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
fn failing_query() {
|
||||
let mut machine = Machine::new_lib();
|
||||
let query = String::from(r#"triple("a",P,"b")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(
|
||||
output,
|
||||
Err(String::from(
|
||||
"error existence_error procedure / triple 3 / triple 3"
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn complex_results() {
|
||||
let mut machine = Machine::new_lib();
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
:- discontiguous(subject_class/2).
|
||||
:- discontiguous(constructor/2).
|
||||
|
||||
subject_class("Todo", c).
|
||||
constructor(c, '[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]').
|
||||
|
||||
subject_class("Recipe", xyz).
|
||||
constructor(xyz, '[{action: "addLink", source: "this", predicate: "recipe://title", target: "literal://string:Meta%20Muffins"}]').
|
||||
"#.to_string());
|
||||
|
||||
let result = machine.run_query(String::from(
|
||||
"subject_class(\"Todo\", C), constructor(C, Actions).",
|
||||
));
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"C" => Value::from("c"),
|
||||
"Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"todo://state\", target: \"todo://ready\"}]"),
|
||||
}
|
||||
),]))
|
||||
);
|
||||
|
||||
let result = machine.run_query(String::from(
|
||||
"subject_class(\"Recipe\", C), constructor(C, Actions).",
|
||||
));
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"C" => Value::from("xyz"),
|
||||
"Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"recipe://title\", target: \"literal://string:Meta%20Muffins\"}]"),
|
||||
}
|
||||
),]))
|
||||
);
|
||||
|
||||
let result = machine.run_query(String::from("subject_class(Class, _)."));
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![
|
||||
QueryMatch::from(btreemap! {
|
||||
"Class" => Value::from("Todo")
|
||||
}),
|
||||
QueryMatch::from(btreemap! {
|
||||
"Class" => Value::from("Recipe")
|
||||
}),
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
fn empty_predicate() {
|
||||
let mut machine = Machine::new_lib();
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
:- discontiguous(subject_class/2).
|
||||
"#
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let result = machine.run_query(String::from("subject_class(X, _)."));
|
||||
assert_eq!(result, Ok(QueryResolution::False));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
fn list_results() {
|
||||
let mut machine = Machine::new_lib();
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
list([1,2,3]).
|
||||
"#
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
let result = machine.run_query(String::from("list(X)."));
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"X" => Value::List(
|
||||
Vec::from([
|
||||
Value::Float(OrderedFloat::from(1.0)),
|
||||
Value::Float(OrderedFloat::from(2.0)),
|
||||
Value::Float(OrderedFloat::from(3.0))
|
||||
])
|
||||
)
|
||||
}
|
||||
),]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
fn consult() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from(r#"triple("a",P,"b")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(
|
||||
output,
|
||||
Ok(QueryResolution::Matches(vec![
|
||||
QueryMatch::from(btreemap! {
|
||||
"P" => Value::from("p1"),
|
||||
}),
|
||||
QueryMatch::from(btreemap! {
|
||||
"P" => Value::from("p2"),
|
||||
}),
|
||||
]))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
machine.run_query(String::from(r#"triple("a","p1","b")."#)),
|
||||
Ok(QueryResolution::True)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
machine.run_query(String::from(r#"triple("x","y","z")."#)),
|
||||
Ok(QueryResolution::False)
|
||||
);
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "new", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
machine.run_query(String::from(r#"triple("a","p1","b")."#)),
|
||||
Ok(QueryResolution::False)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
machine.run_query(String::from(r#"triple("a","new","b")."#)),
|
||||
Ok(QueryResolution::True)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
fn integration_test() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
// File with test commands, i.e. program code to consult and queries to run
|
||||
let code = include_str!("./lib_integration_test_commands.txt");
|
||||
|
||||
// Split the code into blocks
|
||||
let blocks = code.split("=====");
|
||||
|
||||
let mut i = 0;
|
||||
let mut last_result: Option<_> = None;
|
||||
// Iterate over the blocks
|
||||
for block in blocks {
|
||||
// Trim the block to remove any leading or trailing whitespace
|
||||
let block = block.trim();
|
||||
|
||||
// Skip empty blocks
|
||||
if block.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the block is a query
|
||||
if let Some(query) = block.strip_prefix("query") {
|
||||
// Parse and execute the query
|
||||
let result = machine.run_query(query.to_string());
|
||||
assert!(result.is_ok());
|
||||
|
||||
last_result = Some(result);
|
||||
} else if let Some(code) = block.strip_prefix("consult") {
|
||||
// Load the code into the machine
|
||||
machine.consult_module_string("facts", code.to_string());
|
||||
} else if let Some(result) = block.strip_prefix("result") {
|
||||
i += 1;
|
||||
if let Some(Ok(ref last_result)) = last_result {
|
||||
println!(
|
||||
"\n\n=====Result No. {}=======\n{}\n===============",
|
||||
i,
|
||||
last_result.to_string().trim()
|
||||
);
|
||||
assert_eq!(last_result.to_string().trim(), result.to_string().trim(),)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
fn findall() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query =
|
||||
String::from(r#"findall([Predicate, Target], triple(_,Predicate,Target), Result)."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(
|
||||
output,
|
||||
Ok(QueryResolution::Matches(vec![QueryMatch::from(
|
||||
btreemap! {
|
||||
"Result" => Value::List(
|
||||
Vec::from([
|
||||
Value::List([Value::from("p1"), Value::from("b")].into()),
|
||||
Value::List([Value::from("p2"), Value::from("b")].into()),
|
||||
])
|
||||
),
|
||||
}
|
||||
),]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_return_partial_matches() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
:- discontiguous(property_resolve/2).
|
||||
subject_class("Todo", c).
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from(r#"property_resolve(C, "isLiked"), subject_class("Todo", C)."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::False));
|
||||
|
||||
let query = String::from(r#"subject_class("Todo", C), property_resolve(C, "isLiked")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::False));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dont_return_partial_matches_without_discountiguous() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
a("true for a").
|
||||
b("true for b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from(r#"a("true for a")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::True));
|
||||
|
||||
let query = String::from(r#"a("true for a"), b("true for b")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::True));
|
||||
|
||||
let query = String::from(r#"a("true for b"), b("true for b")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::False));
|
||||
|
||||
let query = String::from(r#"a("true for a"), b("true for a")."#);
|
||||
let output = machine.run_query(query);
|
||||
assert_eq!(output, Ok(QueryResolution::False));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_existent_predicate_should_not_cause_panic_when_other_predicates_are_defined() {
|
||||
let mut machine = Machine::new_lib();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = String::from("non_existent_predicate(\"a\",\"p1\",\"b\").");
|
||||
|
||||
let result = machine.run_query(query);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Err(String::from("error existence_error procedure / non_existent_predicate 3 / non_existent_predicate 3"))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use crate::parser::ast::*;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
use ref_thread_local::RefThreadLocal;
|
||||
pub use ref_thread_local::RefThreadLocal;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
@@ -63,21 +63,27 @@ fn add_op_decl_as_module_export<'a, LS: LoadState<'a>>(
|
||||
|
||||
match op_decl.insert_into_op_dir(wam_op_dir) {
|
||||
Some(op_desc) => {
|
||||
payload.retraction_info.push_record(RetractionRecord::ReplacedUserOp(
|
||||
*op_decl,
|
||||
op_desc,
|
||||
));
|
||||
payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::ReplacedUserOp(*op_decl, op_desc));
|
||||
|
||||
payload.module_op_exports.push((*op_decl, Some(op_desc)));
|
||||
}
|
||||
None => {
|
||||
payload.retraction_info.push_record(RetractionRecord::AddedUserOp(*op_decl));
|
||||
payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::AddedUserOp(*op_decl));
|
||||
payload.module_op_exports.push((*op_decl, None));
|
||||
}
|
||||
}
|
||||
|
||||
let compilation_target = payload.compilation_target;
|
||||
add_op_decl(&mut payload.retraction_info, &compilation_target, module_op_dir, op_decl);
|
||||
add_op_decl(
|
||||
&mut payload.retraction_info,
|
||||
&compilation_target,
|
||||
module_op_dir,
|
||||
op_decl,
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn add_op_decl(
|
||||
@@ -89,10 +95,7 @@ pub(super) fn add_op_decl(
|
||||
match op_decl.insert_into_op_dir(op_dir) {
|
||||
Some(op_desc) => match &compilation_target {
|
||||
CompilationTarget::User => {
|
||||
retraction_info.push_record(RetractionRecord::ReplacedUserOp(
|
||||
*op_decl,
|
||||
op_desc,
|
||||
));
|
||||
retraction_info.push_record(RetractionRecord::ReplacedUserOp(*op_decl, op_desc));
|
||||
}
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
retraction_info.push_record(RetractionRecord::ReplacedModuleOp(
|
||||
@@ -107,10 +110,8 @@ pub(super) fn add_op_decl(
|
||||
retraction_info.push_record(RetractionRecord::AddedUserOp(*op_decl));
|
||||
}
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
retraction_info.push_record(RetractionRecord::AddedModuleOp(
|
||||
*module_name,
|
||||
*op_decl,
|
||||
));
|
||||
retraction_info
|
||||
.push_record(RetractionRecord::AddedModuleOp(*module_name, *op_decl));
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -136,10 +137,9 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>(
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() {
|
||||
let arena = &mut LS::machine_st(payload).arena;
|
||||
|
||||
let target_code_index = code_dir
|
||||
let target_code_index = *code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::default(arena))
|
||||
.clone();
|
||||
.or_insert_with(|| CodeIndex::default(arena));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
@@ -160,7 +160,12 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>(
|
||||
}
|
||||
}
|
||||
ModuleExport::OpDecl(ref op_decl) => {
|
||||
add_op_decl(&mut payload.retraction_info, compilation_target, op_dir, op_decl);
|
||||
add_op_decl(
|
||||
&mut payload.retraction_info,
|
||||
compilation_target,
|
||||
op_dir,
|
||||
op_decl,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,16 +188,15 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
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 arena = &mut LS::machine_st(payload).arena;
|
||||
|
||||
let target_code_index = code_dir
|
||||
let target_code_index = *code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::default(arena))
|
||||
.clone();
|
||||
.or_insert_with(|| CodeIndex::default(arena));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
@@ -203,18 +207,13 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
);
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
imported_module.module_decl.name.clone(),
|
||||
imported_module.module_decl.name,
|
||||
(*name, *arity),
|
||||
));
|
||||
}
|
||||
}
|
||||
ModuleExport::OpDecl(ref op_decl) => {
|
||||
add_op_decl_as_module_export::<LS>(
|
||||
payload,
|
||||
op_dir,
|
||||
wam_op_dir,
|
||||
op_decl,
|
||||
);
|
||||
add_op_decl_as_module_export::<LS>(payload, op_dir, wam_op_dir, op_decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,16 +238,20 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>(
|
||||
let key = (*name, *arity);
|
||||
|
||||
if let Some(meta_specs) = imported_module.meta_predicates.get(&key) {
|
||||
wam_prelude.indices.meta_predicates.insert(key.clone(), meta_specs.clone());
|
||||
wam_prelude
|
||||
.indices
|
||||
.meta_predicates
|
||||
.insert(key, meta_specs.clone());
|
||||
}
|
||||
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key) {
|
||||
let arena = &mut LS::machine_st(payload).arena;
|
||||
|
||||
let target_code_index = wam_prelude.indices.code_dir
|
||||
.entry(key.clone())
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena))
|
||||
.clone();
|
||||
let target_code_index = *wam_prelude
|
||||
.indices
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
@@ -259,7 +262,7 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>(
|
||||
);
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
imported_module.module_decl.name.clone(),
|
||||
imported_module.module_decl.name,
|
||||
(*name, *arity),
|
||||
));
|
||||
}
|
||||
@@ -305,10 +308,9 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key) {
|
||||
let arena = &mut LS::machine_st(payload).arena;
|
||||
|
||||
let target_code_index = code_dir
|
||||
let target_code_index = *code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena))
|
||||
.clone();
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
@@ -319,18 +321,13 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
);
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
imported_module.module_decl.name.clone(),
|
||||
imported_module.module_decl.name,
|
||||
(*name, *arity),
|
||||
));
|
||||
}
|
||||
}
|
||||
ModuleExport::OpDecl(ref op_decl) => {
|
||||
add_op_decl_as_module_export::<LS>(
|
||||
payload,
|
||||
op_dir,
|
||||
wam_op_dir,
|
||||
op_decl,
|
||||
);
|
||||
add_op_decl_as_module_export::<LS>(payload, op_dir, wam_op_dir, op_decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,10 +375,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
mut clause_target_poses: Vec<Option<usize>>,
|
||||
is_dynamic: bool,
|
||||
) {
|
||||
let old_compilation_target = mem::replace(
|
||||
&mut self.payload.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 {
|
||||
@@ -424,7 +419,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
payload_compilation_target,
|
||||
clause_clause_compilation_target,
|
||||
key,
|
||||
mem::replace(&mut skeleton.clause_clause_locs, VecDeque::new()),
|
||||
std::mem::take(&mut skeleton.clause_clause_locs),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -437,7 +432,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
};
|
||||
|
||||
self.retract_local_clauses_impl(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(
|
||||
@@ -471,27 +466,53 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
None => return,
|
||||
};
|
||||
|
||||
for (key, code_index) in removed_module.code_dir.iter_mut() {
|
||||
match removed_module
|
||||
.local_extensible_predicates
|
||||
.get(&(CompilationTarget::User, *key))
|
||||
{
|
||||
Some(skeleton) if skeleton.is_multifile => continue,
|
||||
_ => {}
|
||||
let mut skipped_local_predicates = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
|
||||
for ((local_compilation_target, key), skeleton) in
|
||||
removed_module.local_extensible_predicates.iter()
|
||||
{
|
||||
skipped_local_predicates.insert(key);
|
||||
|
||||
if skeleton.is_multifile {
|
||||
continue;
|
||||
}
|
||||
|
||||
let old_index_ptr = code_index.replace(IndexPtr::undefined());
|
||||
if let Some(code_index) = removed_module.code_dir.get_mut(key) {
|
||||
if let Some(global_skeleton) = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(local_compilation_target, key)
|
||||
{
|
||||
let old_index_ptr = code_index.replace(if global_skeleton.core.is_dynamic {
|
||||
IndexPtr::dynamic_undefined()
|
||||
} else {
|
||||
IndexPtr::undefined()
|
||||
});
|
||||
|
||||
self.payload.retraction_info
|
||||
.push_record(RetractionRecord::ReplacedModulePredicate(
|
||||
module_name,
|
||||
*key,
|
||||
old_index_ptr,
|
||||
));
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedModulePredicate(module_name, *key, old_index_ptr),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (key, code_index) in removed_module.code_dir.iter_mut() {
|
||||
if skipped_local_predicates.contains(key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !code_index.is_undefined() && !code_index.is_dynamic_undefined() {
|
||||
let old_index_ptr = code_index.replace(IndexPtr::undefined());
|
||||
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedModulePredicate(module_name, *key, old_index_ptr),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (key, skeleton) in removed_module.extensible_predicates.drain(..) {
|
||||
self.payload.retraction_info
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::RemovedSkeleton(
|
||||
CompilationTarget::Module(module_name),
|
||||
key,
|
||||
@@ -499,7 +520,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
));
|
||||
}
|
||||
|
||||
self.wam_prelude.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: Atom) {
|
||||
@@ -523,15 +547,17 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
(Some(module_code_index), Some(target_code_index))
|
||||
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, old_index_ptr));
|
||||
let old_index_ptr =
|
||||
target_code_index.replace(IndexPtr::undefined());
|
||||
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, fixity(op_decl.op_desc.get_spec() as u32)));
|
||||
let op_dir_value_opt = op_dir
|
||||
.remove(&(op_decl.name, fixity(op_decl.op_desc.get_spec() as u32)));
|
||||
|
||||
if let Some(op_desc) = op_dir_value_opt {
|
||||
retraction_info.push_record(op_retractor(*op_decl, op_desc));
|
||||
@@ -552,9 +578,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
RetractionRecord::ReplacedUserOp,
|
||||
);
|
||||
}
|
||||
CompilationTarget::Module(target_module_name)
|
||||
if target_module_name != module_name =>
|
||||
{
|
||||
CompilationTarget::Module(target_module_name) if target_module_name != module_name => {
|
||||
let predicate_retractor = |key, index_ptr| {
|
||||
RetractionRecord::ReplacedModulePredicate(module_name, key, index_ptr)
|
||||
};
|
||||
@@ -563,7 +587,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
RetractionRecord::ReplacedModuleOp(module_name, op_decl, op_desc)
|
||||
};
|
||||
|
||||
if let Some(module) = self.wam_prelude.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,
|
||||
@@ -579,7 +608,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
CompilationTarget::Module(_) => {}
|
||||
};
|
||||
|
||||
self.wam_prelude.indices.modules.insert(module_name, removed_module);
|
||||
self.wam_prelude
|
||||
.indices
|
||||
.modules
|
||||
.insert(module_name, removed_module);
|
||||
}
|
||||
|
||||
fn get_or_insert_local_code_index(
|
||||
@@ -588,26 +620,22 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
key: PredicateKey,
|
||||
) -> CodeIndex {
|
||||
match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => module
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(
|
||||
Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| {
|
||||
CodeIndex::new(
|
||||
IndexPtr::undefined(),
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
))
|
||||
.clone(),
|
||||
)
|
||||
}),
|
||||
None => {
|
||||
self.add_dynamically_generated_module(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(
|
||||
Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| {
|
||||
CodeIndex::new(
|
||||
IndexPtr::undefined(),
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
))
|
||||
.clone(),
|
||||
)
|
||||
}),
|
||||
None => {
|
||||
unreachable!()
|
||||
}
|
||||
@@ -624,13 +652,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let arena = &mut LS::machine_st(&mut self.payload).arena;
|
||||
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self
|
||||
CompilationTarget::User => *self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena))
|
||||
.clone(),
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)),
|
||||
CompilationTarget::Module(module_name) => {
|
||||
self.get_or_insert_local_code_index(module_name, key)
|
||||
}
|
||||
@@ -645,13 +672,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let arena = &mut LS::machine_st(&mut self.payload).arena;
|
||||
|
||||
if module_name == atom!("user") {
|
||||
return self
|
||||
return *self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena))
|
||||
.clone();
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
} else {
|
||||
self.get_or_insert_local_code_index(module_name, key)
|
||||
}
|
||||
@@ -678,7 +704,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
CompilationTarget::Module(module_name) => {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
module.extensible_predicates.insert(key.clone(), skeleton);
|
||||
module.extensible_predicates.insert(key, skeleton);
|
||||
|
||||
let record = RetractionRecord::AddedExtensiblePredicate(
|
||||
CompilationTarget::Module(module_name),
|
||||
@@ -731,11 +757,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
match payload_compilation_target {
|
||||
CompilationTarget::User => {
|
||||
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);
|
||||
}
|
||||
None => {}
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&filename)
|
||||
{
|
||||
op_decl.insert_into_op_dir(&mut module.op_dir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -771,10 +796,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
ClauseType::Named(arity, name, _) => {
|
||||
let payload_compilation_target = self.payload.compilation_target;
|
||||
|
||||
let idx = self.get_or_insert_code_index(
|
||||
(name, arity),
|
||||
payload_compilation_target,
|
||||
);
|
||||
let idx = self.get_or_insert_code_index((name, arity), payload_compilation_target);
|
||||
|
||||
ClauseType::Named(arity, name, idx)
|
||||
}
|
||||
@@ -802,13 +824,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
|
||||
pub(super) fn get_meta_specs(&self, name: Atom, arity: usize) -> Option<&Vec<MetaSpec>> {
|
||||
self.wam_prelude
|
||||
.indices
|
||||
.get_meta_predicate_spec(
|
||||
name,
|
||||
arity,
|
||||
&self.payload.compilation_target,
|
||||
)
|
||||
self.wam_prelude.indices.get_meta_predicate_spec(
|
||||
name,
|
||||
arity,
|
||||
&self.payload.compilation_target,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn add_meta_predicate_record(
|
||||
@@ -829,65 +849,58 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.insert(key, meta_specs)
|
||||
{
|
||||
Some(old_meta_specs) => {
|
||||
self.payload.retraction_info
|
||||
.push_record(RetractionRecord::ReplacedMetaPredicate(
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedMetaPredicate(
|
||||
module_name,
|
||||
key.0,
|
||||
old_meta_specs,
|
||||
));
|
||||
),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
self.payload.retraction_info
|
||||
.push_record(RetractionRecord::AddedMetaPredicate(
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::AddedMetaPredicate(module_name, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => match self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
Some(ref mut module) => match module.meta_predicates.insert(key, meta_specs) {
|
||||
Some(old_meta_specs) => {
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedMetaPredicate(
|
||||
module_name,
|
||||
key,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
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.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedMetaPredicate(
|
||||
module_name,
|
||||
key.0,
|
||||
old_meta_specs,
|
||||
),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::AddedMetaPredicate(module_name, key),
|
||||
);
|
||||
}
|
||||
}
|
||||
key.0,
|
||||
old_meta_specs,
|
||||
),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
self.add_dynamically_generated_module(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.payload.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);
|
||||
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
module.meta_predicates.insert(key, meta_specs);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::AddedMetaPredicate(module_name, key));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_dynamically_generated_module(&mut self, module_name: Atom) {
|
||||
let module_decl = ModuleDecl {
|
||||
name: module_name.clone(),
|
||||
name: module_name,
|
||||
exports: vec![],
|
||||
};
|
||||
|
||||
@@ -901,10 +914,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
&mut module.meta_predicates,
|
||||
);
|
||||
|
||||
self.payload.retraction_info
|
||||
.push_record(RetractionRecord::AddedModule(module_name.clone()));
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::AddedModule(module_name));
|
||||
|
||||
self.wam_prelude.indices.modules.insert(module_name.clone(), module);
|
||||
self.wam_prelude.indices.modules.insert(module_name, module);
|
||||
}
|
||||
|
||||
fn import_builtins_in_module(
|
||||
@@ -943,59 +957,67 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
self.remove_module_exports(module_name);
|
||||
self.remove_replaced_in_situ_module(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());
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
|
||||
let old_module_decl = mem::replace(&mut module.module_decl, module_decl.clone());
|
||||
|
||||
let local_extensible_predicates = mem::replace(
|
||||
&mut module.local_extensible_predicates,
|
||||
LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()),
|
||||
let local_extensible_predicates = mem::replace(
|
||||
&mut module.local_extensible_predicates,
|
||||
LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()),
|
||||
);
|
||||
|
||||
for ((compilation_target, key), skeleton) in local_extensible_predicates.iter() {
|
||||
self.retract_local_clauses_impl(
|
||||
*compilation_target,
|
||||
*key,
|
||||
&skeleton.clause_clause_locs,
|
||||
);
|
||||
|
||||
for ((compilation_target, key), skeleton) in local_extensible_predicates.iter() {
|
||||
self.retract_local_clauses_impl(
|
||||
*compilation_target,
|
||||
*key,
|
||||
let is_dynamic = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(compilation_target, key)
|
||||
.map(|skeleton| skeleton.core.is_dynamic)
|
||||
.unwrap_or(false);
|
||||
|
||||
if is_dynamic {
|
||||
let clause_clause_compilation_target = match compilation_target {
|
||||
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
|
||||
module => *module,
|
||||
};
|
||||
|
||||
self.retract_local_clause_clauses(
|
||||
clause_clause_compilation_target,
|
||||
&skeleton.clause_clause_locs,
|
||||
);
|
||||
|
||||
let is_dynamic = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(compilation_target, key)
|
||||
.map(|skeleton| skeleton.core.is_dynamic)
|
||||
.unwrap_or(false);
|
||||
|
||||
if is_dynamic {
|
||||
let clause_clause_compilation_target = match compilation_target {
|
||||
CompilationTarget::User => {
|
||||
CompilationTarget::Module(atom!("builtins"))
|
||||
}
|
||||
module => module.clone(),
|
||||
};
|
||||
|
||||
self.retract_local_clause_clauses(
|
||||
clause_clause_compilation_target,
|
||||
&skeleton.clause_clause_locs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.payload.retraction_info
|
||||
.push_record(RetractionRecord::ReplacedModule(
|
||||
old_module_decl,
|
||||
listing_src.clone(),
|
||||
local_extensible_predicates,
|
||||
));
|
||||
}
|
||||
None => {}
|
||||
|
||||
self.payload
|
||||
.retraction_info
|
||||
.push_record(RetractionRecord::ReplacedModule(
|
||||
old_module_decl,
|
||||
listing_src.clone(),
|
||||
local_extensible_predicates,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_module(&mut self, module_decl: ModuleDecl, listing_src: ListingSource) {
|
||||
self.reset_in_situ_module(module_decl.clone(), &listing_src);
|
||||
pub(crate) fn add_module(
|
||||
&mut self,
|
||||
module_decl: ModuleDecl,
|
||||
listing_src: ListingSource,
|
||||
) -> Result<(), SessionError> {
|
||||
let module_name = module_decl.name;
|
||||
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get(&module_name) {
|
||||
if let ListingSource::DynamicallyGenerated = module.listing_src {
|
||||
} else {
|
||||
LS::err_on_builtin_module_overwrite(module_name)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.reset_in_situ_module(module_decl.clone(), &listing_src);
|
||||
|
||||
let mut module = match self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
Some(mut module) => {
|
||||
module.listing_src = listing_src;
|
||||
@@ -1033,6 +1055,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
|
||||
self.wam_prelude.indices.modules.insert(module_name, module);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn import_module(&mut self, module_name: Atom) -> Result<(), SessionError> {
|
||||
@@ -1051,7 +1075,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
)?;
|
||||
}
|
||||
CompilationTarget::Module(ref defining_module_name) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(defining_module_name) {
|
||||
match self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.modules
|
||||
.get_mut(defining_module_name)
|
||||
{
|
||||
Some(ref mut target_module) => {
|
||||
import_module_exports_into_module::<LS>(
|
||||
&mut self.payload,
|
||||
@@ -1091,17 +1120,20 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let payload_compilation_target = self.payload.compilation_target;
|
||||
|
||||
let result = match &payload_compilation_target {
|
||||
CompilationTarget::User => {
|
||||
import_qualified_module_exports::<LS>(
|
||||
&mut self.payload,
|
||||
&payload_compilation_target,
|
||||
&module,
|
||||
&exports,
|
||||
&mut self.wam_prelude,
|
||||
)
|
||||
}
|
||||
CompilationTarget::User => import_qualified_module_exports::<LS>(
|
||||
&mut self.payload,
|
||||
&payload_compilation_target,
|
||||
&module,
|
||||
&exports,
|
||||
&mut self.wam_prelude,
|
||||
),
|
||||
CompilationTarget::Module(ref defining_module_name) => {
|
||||
match self.wam_prelude.indices.modules.get_mut(defining_module_name) {
|
||||
match self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.modules
|
||||
.get_mut(defining_module_name)
|
||||
{
|
||||
Some(ref mut target_module) => {
|
||||
import_qualified_module_exports_into_module::<LS>(
|
||||
&mut self.payload,
|
||||
@@ -1113,9 +1145,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
&mut self.wam_prelude.indices.op_dir,
|
||||
)
|
||||
}
|
||||
None => {
|
||||
Err(SessionError::ModuleCannotImportSelf(module_name))
|
||||
}
|
||||
None => Err(SessionError::ModuleCannotImportSelf(module_name)),
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1123,29 +1153,38 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
self.wam_prelude.indices.modules.insert(module_name, module);
|
||||
result
|
||||
} else {
|
||||
Err(SessionError::ExistenceError(ExistenceError::Module(module_name)))
|
||||
Err(SessionError::ExistenceError(ExistenceError::Module(
|
||||
module_name,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn use_module(&mut self, module_src: ModuleSource) -> Result<(), SessionError> {
|
||||
let (stream, listing_src) = match module_src {
|
||||
ModuleSource::File(filename) => {
|
||||
let mut path_buf = PathBuf::from(filename.as_str());
|
||||
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, file, &mut LS::machine_st(&mut self.payload).arena),
|
||||
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()) {
|
||||
ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) {
|
||||
Some(code) => {
|
||||
if let Some(ref module) = self.wam_prelude.indices.modules.get(&library) {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get(&library) {
|
||||
if let ListingSource::DynamicallyGenerated = &module.listing_src {
|
||||
(
|
||||
Stream::from_static_string(*code, &mut LS::machine_st(&mut self.payload).arena),
|
||||
Stream::from_static_string(
|
||||
code,
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
),
|
||||
ListingSource::User,
|
||||
)
|
||||
} else {
|
||||
@@ -1153,7 +1192,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
} else {
|
||||
(
|
||||
Stream::from_static_string(*code, &mut LS::machine_st(&mut self.payload).arena),
|
||||
Stream::from_static_string(
|
||||
code,
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
),
|
||||
ListingSource::User,
|
||||
)
|
||||
}
|
||||
@@ -1172,9 +1214,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
);
|
||||
|
||||
let subloader: Loader<'_, BootstrappingLoadState> = Loader {
|
||||
payload: BootstrappingLoadState(
|
||||
LoadStatePayload::new(self.wam_prelude.code.len(), term_stream)
|
||||
),
|
||||
payload: BootstrappingLoadState(LoadStatePayload::new(
|
||||
self.wam_prelude.code.len(),
|
||||
term_stream,
|
||||
)),
|
||||
wam_prelude: MachinePreludeView {
|
||||
indices: self.wam_prelude.indices,
|
||||
code: self.wam_prelude.code,
|
||||
@@ -1201,22 +1244,29 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
) -> Result<(), SessionError> {
|
||||
let (stream, listing_src) = match module_src {
|
||||
ModuleSource::File(filename) => {
|
||||
let mut path_buf = PathBuf::from(filename.as_str());
|
||||
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, file, &mut LS::machine_st(&mut self.payload).arena),
|
||||
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()) {
|
||||
ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) {
|
||||
Some(code) => {
|
||||
if self.wam_prelude.indices.modules.contains_key(&library) {
|
||||
return self.import_qualified_module(library, exports);
|
||||
} else {
|
||||
(
|
||||
Stream::from_static_string(*code, &mut LS::machine_st(&mut self.payload).arena),
|
||||
Stream::from_static_string(
|
||||
code,
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
),
|
||||
ListingSource::User,
|
||||
)
|
||||
}
|
||||
@@ -1235,9 +1285,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
);
|
||||
|
||||
let subloader: Loader<'_, BootstrappingLoadState> = Loader {
|
||||
payload: BootstrappingLoadState(
|
||||
LoadStatePayload::new(self.wam_prelude.code.len(), term_stream),
|
||||
),
|
||||
payload: BootstrappingLoadState(LoadStatePayload::new(
|
||||
self.wam_prelude.code.len(),
|
||||
term_stream,
|
||||
)),
|
||||
wam_prelude: MachinePreludeView {
|
||||
indices: self.wam_prelude.indices,
|
||||
code: self.wam_prelude.code,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -44,7 +44,8 @@ pub(crate) enum ValidType {
|
||||
InCharacter,
|
||||
Integer,
|
||||
List,
|
||||
#[allow(unused)] Number,
|
||||
#[allow(unused)]
|
||||
Number,
|
||||
Pair,
|
||||
// PredicateIndicator,
|
||||
// Variable
|
||||
@@ -76,6 +77,12 @@ impl ValidType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum ResourceError {
|
||||
FiniteMemory(HeapCellValue),
|
||||
OutOfFiles,
|
||||
}
|
||||
|
||||
pub(crate) trait TypeError {
|
||||
fn type_error(self, machine_st: &mut MachineState, valid_type: ValidType) -> MachineError;
|
||||
}
|
||||
@@ -154,6 +161,30 @@ pub(crate) trait PermissionError {
|
||||
) -> MachineError;
|
||||
}
|
||||
|
||||
impl PermissionError for Atom {
|
||||
fn permission_error(
|
||||
self,
|
||||
_machine_st: &mut MachineState,
|
||||
index_atom: Atom,
|
||||
perm: Permission,
|
||||
) -> MachineError {
|
||||
let stub = functor!(
|
||||
atom!("permission_error"),
|
||||
[
|
||||
atom(perm.as_atom()),
|
||||
atom(index_atom),
|
||||
cell(atom_as_cell!(self))
|
||||
]
|
||||
);
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
location: None,
|
||||
from: ErrorProvenance::Received,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PermissionError for HeapCellValue {
|
||||
fn permission_error(
|
||||
self,
|
||||
@@ -254,9 +285,11 @@ 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))]
|
||||
[
|
||||
atom_as_cell!(atom!("/"), 2),
|
||||
atom_as_cell!(name),
|
||||
fixnum_as_cell!(Fixnum::build_with(arity as i64)),
|
||||
]
|
||||
}
|
||||
|
||||
impl MachineState {
|
||||
@@ -281,11 +314,18 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn resource_error(&mut self, value: HeapCellValue) -> MachineError {
|
||||
let stub = functor!(
|
||||
atom!("resource_error"),
|
||||
[atom(atom!("finite_memory")), cell(value)]
|
||||
);
|
||||
pub(super) fn resource_error(&mut self, err: ResourceError) -> MachineError {
|
||||
let stub = match err {
|
||||
ResourceError::FiniteMemory(size_requested) => {
|
||||
functor!(
|
||||
atom!("resource_error"),
|
||||
[atom(atom!("finite_memory")), cell(size_requested)]
|
||||
)
|
||||
}
|
||||
ResourceError::OutOfFiles => {
|
||||
functor!(atom!("resource_error"), [atom(atom!("file_descriptors"))])
|
||||
}
|
||||
};
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
@@ -302,26 +342,6 @@ impl MachineState {
|
||||
culprit.type_error(self, valid_type)
|
||||
}
|
||||
|
||||
pub(super) fn module_resolution_error(
|
||||
&mut self,
|
||||
mod_name: Atom,
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
) -> MachineError {
|
||||
let h = self.heap.len();
|
||||
|
||||
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!(atom!("evaluation_error"), [str(h, 0)], [ind_stub]);
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
location: None,
|
||||
from: ErrorProvenance::Constructed,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn existence_error(&mut self, err: ExistenceError) -> MachineError {
|
||||
match err {
|
||||
ExistenceError::Module(name) => {
|
||||
@@ -336,6 +356,28 @@ impl MachineState {
|
||||
from: ErrorProvenance::Received,
|
||||
}
|
||||
}
|
||||
ExistenceError::QualifiedProcedure {
|
||||
module_name,
|
||||
name,
|
||||
arity,
|
||||
} => {
|
||||
let h = self.heap.len();
|
||||
|
||||
let ind_stub = functor!(atom!("/"), [atom(name), fixnum(arity)]);
|
||||
let res_stub = functor!(atom!(":"), [atom(module_name), str(h + 3, 0)], [ind_stub]);
|
||||
|
||||
let stub = functor!(
|
||||
atom!("existence_error"),
|
||||
[atom(atom!("procedure")), str(h, 0)],
|
||||
[res_stub]
|
||||
);
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
location: None,
|
||||
from: ErrorProvenance::Constructed,
|
||||
}
|
||||
}
|
||||
ExistenceError::Procedure(name, arity) => {
|
||||
let culprit = functor!(atom!("/"), [atom(name), fixnum(arity)]);
|
||||
|
||||
@@ -439,26 +481,34 @@ impl MachineState {
|
||||
|
||||
pub(super) fn session_error(&mut self, err: SessionError) -> MachineError {
|
||||
match err {
|
||||
SessionError::CannotOverwriteBuiltIn(key) => {
|
||||
// SessionError::CannotOverwriteImport(pred_atom) => {
|
||||
self.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("static_procedure"),
|
||||
functor_stub(key.0, key.1).into_iter().collect::<MachineStub>(),
|
||||
)
|
||||
SessionError::CannotOverwriteBuiltIn(key) => self.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("static_procedure"),
|
||||
functor_stub(key.0, key.1)
|
||||
.into_iter()
|
||||
.collect::<MachineStub>(),
|
||||
),
|
||||
SessionError::CannotOverwriteStaticProcedure(key) => self.permission_error(
|
||||
Permission::Modify,
|
||||
atom!("static_procedure"),
|
||||
functor_stub(key.0, key.1)
|
||||
.into_iter()
|
||||
.collect::<MachineStub>(),
|
||||
),
|
||||
SessionError::CannotOverwriteBuiltInModule(module) => {
|
||||
self.permission_error(Permission::Modify, atom!("static_module"), module)
|
||||
}
|
||||
SessionError::ExistenceError(err) => self.existence_error(err),
|
||||
// SessionError::InvalidFileName(filename) => {
|
||||
// Self::existence_error(h, ExistenceError::Module(filename))
|
||||
// }
|
||||
SessionError::ModuleDoesNotContainExport(..) => {
|
||||
let error_atom = atom!("module_does_not_contain_claimed_export");
|
||||
SessionError::ModuleDoesNotContainExport(module_name, key) => {
|
||||
let functor_stub = functor_stub(key.0, key.1);
|
||||
|
||||
self.permission_error(
|
||||
Permission::Access,
|
||||
atom!("private_procedure"),
|
||||
functor!(error_atom),
|
||||
)
|
||||
let stub = functor!(
|
||||
atom!("module_does_not_contain_claimed_export"),
|
||||
[atom(module_name), str(self.heap.len() + 4, 0)],
|
||||
[functor_stub]
|
||||
);
|
||||
|
||||
self.permission_error(Permission::Access, atom!("private_procedure"), stub)
|
||||
}
|
||||
SessionError::ModuleCannotImportSelf(module_name) => {
|
||||
let error_atom = atom!("module_cannot_import_self");
|
||||
@@ -471,7 +521,11 @@ impl MachineState {
|
||||
}
|
||||
SessionError::NamelessEntry => {
|
||||
let error_atom = atom!("nameless_procedure");
|
||||
self.permission_error(Permission::Create, atom!("static_procedure"), functor!(error_atom))
|
||||
self.permission_error(
|
||||
Permission::Create,
|
||||
atom!("static_procedure"),
|
||||
functor!(error_atom),
|
||||
)
|
||||
}
|
||||
SessionError::OpIsInfixAndPostFix(op) => {
|
||||
self.permission_error(Permission::Create, atom!("operator"), functor!(op))
|
||||
@@ -541,21 +595,21 @@ impl MachineState {
|
||||
|
||||
#[cfg(feature = "ffi")]
|
||||
pub(super) fn ffi_error(&mut self, err: FFIError) -> MachineError {
|
||||
let error_atom = match err {
|
||||
FFIError::ValueCast => atom!("value_cast"),
|
||||
FFIError::ValueDontFit => atom!("value_dont_fit"),
|
||||
FFIError::InvalidFFIType => atom!("invalid_ffi_type"),
|
||||
FFIError::InvalidStructName => atom!("invalid_struct_name"),
|
||||
FFIError::FunctionNotFound => atom!("function_not_found"),
|
||||
FFIError::StructNotFound => atom!("struct_not_found"),
|
||||
};
|
||||
let stub = functor!(atom!("ffi_error"),[atom(error_atom)]);
|
||||
let error_atom = match err {
|
||||
FFIError::ValueCast => atom!("value_cast"),
|
||||
FFIError::ValueDontFit => atom!("value_dont_fit"),
|
||||
FFIError::InvalidFFIType => atom!("invalid_ffi_type"),
|
||||
FFIError::InvalidStructName => atom!("invalid_struct_name"),
|
||||
FFIError::FunctionNotFound => atom!("function_not_found"),
|
||||
FFIError::StructNotFound => atom!("struct_not_found"),
|
||||
};
|
||||
let stub = functor!(atom!("ffi_error"), [atom(error_atom)]);
|
||||
|
||||
MachineError {
|
||||
stub,
|
||||
location: None,
|
||||
from: ErrorProvenance::Constructed,
|
||||
}
|
||||
MachineError {
|
||||
stub,
|
||||
location: None,
|
||||
from: ErrorProvenance::Constructed,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn error_form(&mut self, err: MachineError, src: FunctorStub) -> MachineStub {
|
||||
@@ -599,7 +653,7 @@ impl MachineState {
|
||||
self.ball.boundary = 0;
|
||||
self.ball.stub.truncate(0);
|
||||
|
||||
self.heap.extend(err.into_iter());
|
||||
self.heap.extend(err);
|
||||
|
||||
self.registers[1] = if err_len == 1 {
|
||||
heap_loc_as_cell!(h)
|
||||
@@ -663,56 +717,58 @@ impl From<ParserError> for CompilationError {
|
||||
impl CompilationError {
|
||||
pub(crate) fn line_and_col_num(&self) -> Option<(usize, usize)> {
|
||||
match self {
|
||||
&CompilationError::ParserError(ref err) => err.line_and_col_num(),
|
||||
CompilationError::ParserError(err) => err.line_and_col_num(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_functor(&self) -> MachineStub {
|
||||
match self {
|
||||
&CompilationError::Arithmetic(..) => {
|
||||
CompilationError::Arithmetic(..) => {
|
||||
functor!(atom!("arithmetic_error"))
|
||||
}
|
||||
&CompilationError::CannotParseCyclicTerm => {
|
||||
CompilationError::CannotParseCyclicTerm => {
|
||||
functor!(atom!("cannot_parse_cyclic_term"))
|
||||
}
|
||||
&CompilationError::ExceededMaxArity => {
|
||||
CompilationError::ExceededMaxArity => {
|
||||
functor!(atom!("exceeded_max_arity"))
|
||||
}
|
||||
&CompilationError::ExpectedRel => {
|
||||
CompilationError::ExpectedRel => {
|
||||
functor!(atom!("expected_relation"))
|
||||
}
|
||||
&CompilationError::InadmissibleFact => { // TODO: type_error(callable, _).
|
||||
CompilationError::InadmissibleFact => {
|
||||
// TODO: type_error(callable, _).
|
||||
functor!(atom!("inadmissible_fact"))
|
||||
}
|
||||
&CompilationError::InadmissibleQueryTerm => { // TODO: type_error(callable, _).
|
||||
CompilationError::InadmissibleQueryTerm => {
|
||||
// TODO: type_error(callable, _).
|
||||
functor!(atom!("inadmissible_query_term"))
|
||||
}
|
||||
&CompilationError::InconsistentEntry => {
|
||||
CompilationError::InconsistentEntry => {
|
||||
functor!(atom!("inconsistent_entry"))
|
||||
}
|
||||
&CompilationError::InvalidMetaPredicateDecl => {
|
||||
CompilationError::InvalidMetaPredicateDecl => {
|
||||
functor!(atom!("invalid_meta_predicate_decl"))
|
||||
}
|
||||
&CompilationError::InvalidModuleDecl => {
|
||||
CompilationError::InvalidModuleDecl => {
|
||||
functor!(atom!("invalid_module_declaration"))
|
||||
}
|
||||
&CompilationError::InvalidModuleExport => {
|
||||
CompilationError::InvalidModuleExport => {
|
||||
functor!(atom!("invalid_module_export"))
|
||||
}
|
||||
&CompilationError::InvalidModuleResolution(ref module_name) => {
|
||||
CompilationError::InvalidModuleResolution(ref module_name) => {
|
||||
functor!(atom!("no_such_module"), [atom(module_name)])
|
||||
}
|
||||
&CompilationError::InvalidRuleHead => {
|
||||
CompilationError::InvalidRuleHead => {
|
||||
functor!(atom!("invalid_head_of_rule")) // TODO: type_error(callable, _).
|
||||
}
|
||||
&CompilationError::InvalidUseModuleDecl => {
|
||||
CompilationError::InvalidUseModuleDecl => {
|
||||
functor!(atom!("invalid_use_module_declaration"))
|
||||
}
|
||||
&CompilationError::ParserError(ref err) => {
|
||||
CompilationError::ParserError(ref err) => {
|
||||
functor!(err.as_atom())
|
||||
}
|
||||
&CompilationError::UnreadableTerm => {
|
||||
CompilationError::UnreadableTerm => {
|
||||
functor!(atom!("unreadable_term"))
|
||||
}
|
||||
}
|
||||
@@ -820,10 +876,10 @@ pub enum CycleSearchResult {
|
||||
Cyclic(usize),
|
||||
EmptyList,
|
||||
NotList(usize, HeapCellValue), // the list length until the second argument in the heap
|
||||
PartialList(usize, Ref), // the list length (up to max), and an offset into the heap.
|
||||
ProperList(usize), // the list length.
|
||||
PartialList(usize, Ref), // the list length (up to max), and an offset into the heap.
|
||||
ProperList(usize), // the list length.
|
||||
PStrLocation(usize, usize, usize), // list length (up to max), the heap address of the PStr, the offset
|
||||
UntouchedList(usize, usize), // list length (up to max), the address of an uniterated Addr::Lis(address).
|
||||
UntouchedList(usize, usize), // list length (up to max), the address of an uniterated Addr::Lis(address).
|
||||
UntouchedCStr(Atom, usize),
|
||||
}
|
||||
|
||||
@@ -838,7 +894,7 @@ impl MachineState {
|
||||
match BrentAlgState::detect_cycles(&self.heap, list) {
|
||||
CycleSearchResult::PartialList(..) => {
|
||||
let err = self.instantiation_error();
|
||||
return Err(self.error_form(err, stub_gen()))
|
||||
return Err(self.error_form(err, stub_gen()));
|
||||
}
|
||||
CycleSearchResult::NotList(..) | CycleSearchResult::Cyclic(_) => {
|
||||
let err = self.type_error(ValidType::List, list);
|
||||
@@ -942,6 +998,11 @@ pub enum ExistenceError {
|
||||
Module(Atom),
|
||||
ModuleSource(ModuleSource),
|
||||
Procedure(Atom, usize),
|
||||
QualifiedProcedure {
|
||||
module_name: Atom,
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
},
|
||||
SourceSink(HeapCellValue),
|
||||
Stream(HeapCellValue),
|
||||
}
|
||||
@@ -950,6 +1011,8 @@ pub enum ExistenceError {
|
||||
pub enum SessionError {
|
||||
CompilationError(CompilationError),
|
||||
CannotOverwriteBuiltIn(PredicateKey),
|
||||
CannotOverwriteBuiltInModule(Atom),
|
||||
CannotOverwriteStaticProcedure(PredicateKey),
|
||||
ExistenceError(ExistenceError),
|
||||
ModuleDoesNotContainExport(Atom, PredicateKey),
|
||||
ModuleCannotImportSelf(Atom),
|
||||
@@ -959,19 +1022,6 @@ pub enum SessionError {
|
||||
QueryCannotBeDefinedAsFact,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum EvalSession {
|
||||
// EntrySuccess,
|
||||
Error(SessionError),
|
||||
}
|
||||
|
||||
impl From<SessionError> for EvalSession {
|
||||
#[inline]
|
||||
fn from(err: SessionError) -> Self {
|
||||
EvalSession::Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for SessionError {
|
||||
#[inline]
|
||||
fn from(err: std::io::Error) -> SessionError {
|
||||
@@ -992,10 +1042,3 @@ impl From<CompilationError> for SessionError {
|
||||
SessionError::CompilationError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParserError> for EvalSession {
|
||||
#[inline]
|
||||
fn from(err: ParserError) -> Self {
|
||||
EvalSession::from(SessionError::from(err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@ use crate::parser::ast::*;
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::machine::ClauseType;
|
||||
use crate::machine::loader::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::streams::Stream;
|
||||
use crate::machine::ClauseType;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use modular_bitfield::{BitfieldSpecifier, bitfield};
|
||||
use modular_bitfield::specifiers::*;
|
||||
use scryer_modular_bitfield::specifiers::*;
|
||||
use scryer_modular_bitfield::{bitfield, BitfieldSpecifier};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeSet;
|
||||
@@ -21,14 +21,6 @@ use crate::types::*;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) struct OrderedOpDirKey(pub(crate) Atom, pub(crate) Fixity);
|
||||
|
||||
pub(crate) type OssifiedOpDir = IndexMap<(Atom, Fixity), (usize, Specifier)>;
|
||||
|
||||
#[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 enum TermOrderCategory {
|
||||
@@ -86,7 +78,8 @@ pub enum IndexPtrTag {
|
||||
#[derive(Debug, Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct IndexPtr {
|
||||
pub p: B56,
|
||||
#[allow(unused)] m: bool,
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
pub tag: IndexPtrTag,
|
||||
}
|
||||
|
||||
@@ -125,28 +118,22 @@ impl IndexPtr {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_undefined(&self) -> bool {
|
||||
match self.tag() {
|
||||
IndexPtrTag::Undefined => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.tag(), IndexPtrTag::Undefined)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_dynamic_undefined(&self) -> bool {
|
||||
match self.tag() {
|
||||
IndexPtrTag::DynamicUndefined => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.tag(), IndexPtrTag::DynamicUndefined)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Ord, Hash, PartialOrd, Eq, PartialEq)]
|
||||
pub struct CodeIndex(TypedArenaPtr<IndexPtr>);
|
||||
|
||||
#[cfg(target_pointer_width="32")]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const_assert!(std::mem::align_of::<CodeIndex>() == 4);
|
||||
|
||||
#[cfg(target_pointer_width="64")]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const_assert!(std::mem::align_of::<CodeIndex>() == 8);
|
||||
|
||||
impl Deref for CodeIndex {
|
||||
@@ -238,6 +225,7 @@ pub enum VarKey {
|
||||
}
|
||||
|
||||
impl VarKey {
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
#[inline]
|
||||
pub(crate) fn to_string(&self) -> String {
|
||||
match self {
|
||||
@@ -248,11 +236,7 @@ impl VarKey {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_anon(&self) -> bool {
|
||||
if let VarKey::AnonVar(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, VarKey::AnonVar(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +277,8 @@ impl IndexStore {
|
||||
let (name, arity) = key;
|
||||
|
||||
if !ClauseType::is_inbuilt(name, arity) {
|
||||
self.modules.get(&(atom!("builtins")))
|
||||
self.modules
|
||||
.get(&(atom!("builtins")))
|
||||
.map(|module| module.code_dir.contains_key(&(name, arity)))
|
||||
.unwrap_or(false)
|
||||
} else {
|
||||
@@ -435,20 +420,16 @@ impl IndexStore {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self.meta_predicates.get(&(name, arity)),
|
||||
CompilationTarget::Module(ref module_name) => match self.modules.get(module_name) {
|
||||
Some(ref module) => module
|
||||
Some(module) => module
|
||||
.meta_predicates
|
||||
.get(&(name.clone(), arity))
|
||||
.get(&(name, arity))
|
||||
.or_else(|| self.meta_predicates.get(&(name, arity))),
|
||||
None => self.meta_predicates.get(&(name, arity)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_dynamic_predicate(
|
||||
&self,
|
||||
module_name: Atom,
|
||||
key: PredicateKey,
|
||||
) -> bool {
|
||||
pub(crate) fn is_dynamic_predicate(&self, module_name: Atom, key: PredicateKey) -> bool {
|
||||
match module_name {
|
||||
atom!("user") => self
|
||||
.extensible_predicates
|
||||
@@ -456,7 +437,7 @@ impl IndexStore {
|
||||
.map(|skeleton| skeleton.core.is_dynamic)
|
||||
.unwrap_or(false),
|
||||
_ => match self.modules.get(&module_name) {
|
||||
Some(ref module) => module
|
||||
Some(module) => module
|
||||
.extensible_predicates
|
||||
.get(&key)
|
||||
.map(|skeleton| skeleton.core.is_dynamic)
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::*;
|
||||
use crate::heap_print::*;
|
||||
use crate::machine::Machine;
|
||||
use crate::machine::attributed_variables::*;
|
||||
use crate::machine::copier::*;
|
||||
use crate::machine::heap::*;
|
||||
@@ -11,6 +10,7 @@ use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::machine::stack::*;
|
||||
use crate::machine::streams::*;
|
||||
use crate::machine::Machine;
|
||||
use crate::parser::ast::*;
|
||||
use crate::read::TermWriteResult;
|
||||
use crate::types::*;
|
||||
@@ -22,6 +22,7 @@ use indexmap::IndexMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) type Registers = [HeapCellValue; MAX_ARITY + 1];
|
||||
|
||||
@@ -57,14 +58,14 @@ pub enum OnEOF {
|
||||
}
|
||||
|
||||
pub struct MachineState {
|
||||
pub atom_tbl: AtomTable,
|
||||
pub atom_tbl: Arc<AtomTable>,
|
||||
pub arena: Arena,
|
||||
pub(super) pdl: Vec<HeapCellValue>,
|
||||
pub(super) s: HeapPtr,
|
||||
pub(super) s_offset: usize,
|
||||
pub(super) p: usize,
|
||||
pub(super) oip: u32, // first internal code ptr
|
||||
pub(super) iip : u32, // second internal code ptr
|
||||
pub(super) iip: u32, // second internal code ptr
|
||||
pub(super) b: usize,
|
||||
pub(super) b0: usize,
|
||||
pub(super) e: usize,
|
||||
@@ -79,7 +80,7 @@ pub struct MachineState {
|
||||
pub(super) trail: Vec<TrailEntry>,
|
||||
pub(super) tr: usize,
|
||||
pub(super) hb: usize,
|
||||
pub(super) block: usize, // an offset into the OR stack.
|
||||
pub(super) block: usize, // an offset into the OR stack.
|
||||
pub(super) scc_block: usize, // an offset into the OR stack for setup_call_cleanup/3.
|
||||
pub(super) ball: Ball,
|
||||
pub(super) ball_stack: Vec<Ball>, // save current ball before jumping via, e.g., verify_attr interrupt.
|
||||
@@ -95,7 +96,6 @@ pub struct MachineState {
|
||||
pub(crate) unify_fn: fn(&mut MachineState),
|
||||
pub(crate) bind_fn: fn(&mut MachineState, Ref, HeapCellValue),
|
||||
pub(crate) run_cleaners_fn: fn(&mut Machine) -> bool,
|
||||
pub(crate) increment_call_count_fn: fn(&mut MachineState) -> CallResult,
|
||||
}
|
||||
|
||||
impl fmt::Debug for MachineState {
|
||||
@@ -203,12 +203,12 @@ pub fn pstr_loc_and_offset(heap: &[HeapCellValue], index: usize) -> (usize, Fixn
|
||||
fn push_var_eq_functors<'a>(
|
||||
heap: &mut Heap,
|
||||
iter: impl Iterator<Item = (&'a VarKey, &'a HeapCellValue)>,
|
||||
atom_tbl: &mut AtomTable,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Vec<HeapCellValue> {
|
||||
let mut list_of_var_eqs = vec![];
|
||||
|
||||
for (var, binding) in iter {
|
||||
let var_atom = atom_tbl.build_with(&var.to_string());
|
||||
let var_atom = AtomTable::build_with(atom_tbl, &var.to_string());
|
||||
let h = heap.len();
|
||||
|
||||
heap.push(atom_as_cell!(atom!("="), 2));
|
||||
@@ -243,9 +243,11 @@ impl Ball {
|
||||
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()
|
||||
self.stub
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|heap_value| heap_value - diff)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,6 +289,11 @@ impl<'a> CopierTarget for CopyTerm<'a> {
|
||||
self.state.heap.push(hcv);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
|
||||
self.state.attr_var_init.attr_var_queue.push(attr_var_loc);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn store(&self, value: HeapCellValue) -> HeapCellValue {
|
||||
self.state.store(value)
|
||||
@@ -305,6 +312,7 @@ impl<'a> CopierTarget for CopyTerm<'a> {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct CopyBallTerm<'a> {
|
||||
attr_var_queue: &'a mut Vec<usize>,
|
||||
stack: &'a mut Stack,
|
||||
heap: &'a mut Heap,
|
||||
heap_boundary: usize,
|
||||
@@ -312,10 +320,16 @@ pub(super) struct CopyBallTerm<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CopyBallTerm<'a> {
|
||||
pub(super) fn new(stack: &'a mut Stack, heap: &'a mut Heap, stub: &'a mut Heap) -> Self {
|
||||
pub(super) fn new(
|
||||
attr_var_queue: &'a mut Vec<usize>,
|
||||
stack: &'a mut Stack,
|
||||
heap: &'a mut Heap,
|
||||
stub: &'a mut Heap,
|
||||
) -> Self {
|
||||
let hb = heap.len();
|
||||
|
||||
CopyBallTerm {
|
||||
attr_var_queue,
|
||||
stack,
|
||||
heap,
|
||||
heap_boundary: hb,
|
||||
@@ -357,6 +371,11 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
|
||||
self.stub.push(value);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
|
||||
self.attr_var_queue.push(attr_var_loc);
|
||||
}
|
||||
|
||||
fn store(&self, value: HeapCellValue) -> HeapCellValue {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
|
||||
@@ -409,28 +428,33 @@ impl MachineState {
|
||||
self.fail = false;
|
||||
}
|
||||
|
||||
pub(crate) fn increment_call_count(&mut self) -> CallResult {
|
||||
if self.cwil.inference_limit_exceeded || self.ball.stub.len() > 0 {
|
||||
return Ok(());
|
||||
pub(crate) fn increment_call_count(&mut self) -> bool {
|
||||
if self.cwil.inference_limit_exceeded || !self.ball.stub.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if let Some(&(ref limit, bp)) = self.cwil.limits.last() {
|
||||
if self.cwil.count == *limit {
|
||||
self.cwil.inference_limit_exceeded = true;
|
||||
self.cwil.global_count += 1;
|
||||
|
||||
return Err(
|
||||
functor!(atom!("inference_limit_exceeded"), [fixnum(bp)])
|
||||
);
|
||||
if let Some(&(ref limit, block)) = self.cwil.limits.last() {
|
||||
if self.cwil.local_count == *limit {
|
||||
self.cwil.inference_limit_exceeded = true;
|
||||
self.block = block;
|
||||
self.unwind_stack();
|
||||
|
||||
return false;
|
||||
} else {
|
||||
self.cwil.count += 1;
|
||||
self.cwil.local_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(super) fn try_char_list(&mut self, addrs: Vec<HeapCellValue>) -> Result<String, MachineError> {
|
||||
pub(super) fn try_char_list(
|
||||
&mut self,
|
||||
addrs: Vec<HeapCellValue>,
|
||||
) -> Result<String, MachineError> {
|
||||
let mut chars = String::new();
|
||||
|
||||
for addr in addrs {
|
||||
@@ -515,18 +539,25 @@ impl MachineState {
|
||||
mut var_list: Vec<(VarKey, HeapCellValue, usize)>,
|
||||
singleton_var_list: Vec<HeapCellValue>,
|
||||
) -> CallResult {
|
||||
var_list.sort_by(|(_,_,idx_1),(_,_,idx_2)| idx_1.cmp(idx_2));
|
||||
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().filter_map(|(var_name, var,_)| if var_name.is_anon() { None } else { Some((var_name,var)) }),
|
||||
&mut self.atom_tbl,
|
||||
var_list.iter().filter_map(|(var_name, var, _)| {
|
||||
if var_name.is_anon() {
|
||||
None
|
||||
} else {
|
||||
Some((var_name, var))
|
||||
}
|
||||
}),
|
||||
&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())
|
||||
);
|
||||
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);
|
||||
|
||||
@@ -535,9 +566,10 @@ impl MachineState {
|
||||
}
|
||||
|
||||
let vars_addr = self.registers[4];
|
||||
let vars_offset = heap_loc_as_cell!(
|
||||
iter_to_heap_list(&mut self.heap, var_list.into_iter().map(|(_,cell,_)| cell))
|
||||
);
|
||||
let vars_offset = heap_loc_as_cell!(iter_to_heap_list(
|
||||
&mut self.heap,
|
||||
var_list.into_iter().map(|(_, cell, _)| cell)
|
||||
));
|
||||
|
||||
unify_fn!(*self, vars_offset, vars_addr);
|
||||
|
||||
@@ -546,9 +578,10 @@ impl MachineState {
|
||||
}
|
||||
|
||||
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())
|
||||
);
|
||||
let var_names_offset = heap_loc_as_cell!(iter_to_heap_list(
|
||||
&mut self.heap,
|
||||
list_of_var_eqs.into_iter()
|
||||
));
|
||||
|
||||
Ok(unify_fn!(*self, var_names_offset, var_names_addr))
|
||||
}
|
||||
@@ -575,7 +608,9 @@ impl MachineState {
|
||||
|
||||
let mut singleton_var_set: IndexMap<Ref, bool> = IndexMap::new();
|
||||
|
||||
for cell in stackful_preorder_iter(&mut self.heap, &mut self.stack, heap_loc) {
|
||||
for cell in
|
||||
stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, heap_loc)
|
||||
{
|
||||
let cell = unmark_cell_bits!(cell);
|
||||
|
||||
if let Some(var) = cell.as_var() {
|
||||
@@ -589,18 +624,21 @@ impl MachineState {
|
||||
|
||||
let singleton_var_list = push_var_eq_functors(
|
||||
&mut self.heap,
|
||||
term_write_result.var_dict.iter().filter(|(var_name, binding)| {
|
||||
if var_name.is_anon() {
|
||||
return false;
|
||||
}
|
||||
term_write_result
|
||||
.var_dict
|
||||
.iter()
|
||||
.filter(|(var_name, binding)| {
|
||||
if var_name.is_anon() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(r) = binding.as_var() {
|
||||
*singleton_var_set.get(&r).unwrap_or(&false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
&mut self.atom_tbl,
|
||||
if let Some(r) = binding.as_var() {
|
||||
*singleton_var_set.get(&r).unwrap_or(&false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
&self.atom_tbl,
|
||||
);
|
||||
|
||||
for var in term_write_result.var_dict.values_mut() {
|
||||
@@ -620,18 +658,14 @@ impl MachineState {
|
||||
self.write_read_term_options(var_list, singleton_var_list)
|
||||
}
|
||||
|
||||
pub fn read_term_from_user_input_eof_handler(&mut self, stream: Stream) -> Result<OnEOF, MachineStub> {
|
||||
self.eof_action(
|
||||
self.registers[2],
|
||||
stream,
|
||||
atom!("read_term"),
|
||||
3,
|
||||
)?;
|
||||
pub fn read_term_from_user_input_eof_handler(
|
||||
&mut self,
|
||||
stream: Stream,
|
||||
) -> Result<OnEOF, MachineStub> {
|
||||
self.eof_action(self.registers[2], stream, atom!("read_term"), 3)?;
|
||||
|
||||
if stream.options().eof_action() == EOFAction::Reset {
|
||||
if self.fail == false {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
if stream.options().eof_action() == EOFAction::Reset && !self.fail {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
|
||||
Ok(OnEOF::Return)
|
||||
@@ -639,13 +673,15 @@ impl MachineState {
|
||||
|
||||
// Safety: the atom_tbl lives for the lifetime of the machine, as does the helper, so the ptr
|
||||
// will always be valid.
|
||||
pub fn read_term_from_user_input(&mut self, stream: Stream, indices: &mut IndexStore) -> CallResult {
|
||||
let atoms_ptr = (&self.atom_tbl.table) as *const indexmap::IndexSet<Atom>;
|
||||
|
||||
pub fn read_term_from_user_input(
|
||||
&mut self,
|
||||
stream: Stream,
|
||||
indices: &mut IndexStore,
|
||||
) -> CallResult {
|
||||
if let Stream::Readline(ptr) = stream {
|
||||
unsafe {
|
||||
let readline = ptr.as_ptr().as_mut().unwrap();
|
||||
readline.set_atoms_for_completion(atoms_ptr);
|
||||
readline.set_atoms_for_completion(&self.atom_tbl);
|
||||
return self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
@@ -654,6 +690,14 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
if let Stream::Byte(_) = stream {
|
||||
return self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
);
|
||||
}
|
||||
|
||||
unreachable!("Stream must be a Stream::Readline(_)")
|
||||
}
|
||||
|
||||
@@ -663,17 +707,10 @@ impl MachineState {
|
||||
stream.set_past_end_of_stream(true);
|
||||
return Ok(OnEOF::Return);
|
||||
} else if stream.past_end_of_stream() {
|
||||
self.eof_action(
|
||||
self.registers[2],
|
||||
stream,
|
||||
atom!("read_term"),
|
||||
3,
|
||||
)?;
|
||||
self.eof_action(self.registers[2], stream, atom!("read_term"), 3)?;
|
||||
|
||||
if stream.options().eof_action() == EOFAction::Reset {
|
||||
if self.fail == false {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
if stream.options().eof_action() == EOFAction::Reset && !self.fail {
|
||||
return Ok(OnEOF::Continue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,11 +732,7 @@ impl MachineState {
|
||||
)?;
|
||||
|
||||
if stream.past_end_of_stream() {
|
||||
if EOFAction::Reset != stream.options().eof_action() {
|
||||
return Ok(());
|
||||
} else if self.fail {
|
||||
return Ok(());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
loop {
|
||||
@@ -709,7 +742,9 @@ impl MachineState {
|
||||
match &err {
|
||||
CompilationError::ParserError(e) if e.is_unexpected_eof() => {
|
||||
match eof_handler(self, stream)? {
|
||||
OnEOF::Return => return self.write_read_term_options(vec![], vec![]),
|
||||
OnEOF::Return => {
|
||||
return self.write_read_term_options(vec![], vec![])
|
||||
}
|
||||
OnEOF::Continue => continue,
|
||||
}
|
||||
}
|
||||
@@ -762,14 +797,14 @@ impl MachineState {
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, _arity)) => {
|
||||
debug_assert_eq!(_arity, 0);
|
||||
var_names.insert(var, VarPtr::from(name.as_str()));
|
||||
var_names.insert(var, VarPtr::from(&*name.as_str()));
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
debug_assert_eq!(arity, 0);
|
||||
var_names.insert(var, VarPtr::from(name.as_str()));
|
||||
var_names.insert(var, VarPtr::from(&*name.as_str()));
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
@@ -850,7 +885,7 @@ impl MachineState {
|
||||
|
||||
let mut printer = HCPrinter::new(
|
||||
&mut self.heap,
|
||||
&mut self.atom_tbl,
|
||||
Arc::clone(&self.atom_tbl),
|
||||
&mut self.stack,
|
||||
op_dir,
|
||||
PrinterOutputter::new(),
|
||||
@@ -872,8 +907,10 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
Ok(Number::Integer(n)) => {
|
||||
if let Some(n) = n.to_usize() {
|
||||
printer.max_depth = n;
|
||||
let result = (&*n).try_into();
|
||||
|
||||
if let Ok(value) = result {
|
||||
printer.max_depth = value;
|
||||
} else {
|
||||
self.fail = true;
|
||||
return Ok(None);
|
||||
@@ -896,7 +933,11 @@ impl MachineState {
|
||||
Ok(Some(printer))
|
||||
}
|
||||
|
||||
pub(super) fn read_predicate_key(&self, name: HeapCellValue, arity: HeapCellValue) -> (Atom, usize) {
|
||||
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)));
|
||||
|
||||
@@ -920,50 +961,71 @@ impl MachineState {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn cut_prev_body(&mut self, value: HeapCellValue) {
|
||||
let b = self.b;
|
||||
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::CutPoint, b0) => {
|
||||
let b0 = b0.get_num() as usize;
|
||||
let b0 = self.stack.index_or_frame(b0).prelude.b;
|
||||
|
||||
if b > b0 {
|
||||
self.b = b0;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CWIL {
|
||||
count: Integer,
|
||||
local_count: Integer,
|
||||
pub(crate) global_count: Integer,
|
||||
limits: Vec<(Integer, usize)>,
|
||||
inference_limit_exceeded: bool,
|
||||
pub(crate) inference_limit_exceeded: bool,
|
||||
}
|
||||
|
||||
impl CWIL {
|
||||
pub(crate) fn new() -> Self {
|
||||
CWIL {
|
||||
count: Integer::from(0),
|
||||
local_count: Integer::from(0),
|
||||
global_count: Integer::from(0),
|
||||
limits: vec![],
|
||||
inference_limit_exceeded: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_limit(&mut self, limit: usize, b: usize) -> &Integer {
|
||||
let mut limit = Integer::from(limit);
|
||||
limit += &self.count;
|
||||
pub(crate) fn add_limit(&mut self, mut limit: Integer, block: usize) -> &Integer {
|
||||
limit += &self.local_count;
|
||||
|
||||
match self.limits.last() {
|
||||
Some((ref inner_limit, _)) if *inner_limit <= limit => {}
|
||||
_ => self.limits.push((limit, b)),
|
||||
};
|
||||
_ => self.limits.push((limit, block)),
|
||||
}
|
||||
|
||||
&self.count
|
||||
&self.local_count
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn remove_limit(&mut self, b: usize) -> &Integer {
|
||||
if let Some((_, bp)) = self.limits.last() {
|
||||
if bp == &b {
|
||||
pub(crate) fn remove_limit(&mut self, block: usize) -> &Integer {
|
||||
if let Some((_, bl)) = self.limits.last() {
|
||||
if bl == &block {
|
||||
self.limits.pop();
|
||||
}
|
||||
}
|
||||
|
||||
&self.count
|
||||
&self.local_count
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.count = Integer::from(0);
|
||||
self.local_count = Integer::from(0);
|
||||
self.limits.clear();
|
||||
self.inference_limit_exceeded = false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::types::*;
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::*;
|
||||
use crate::machine::attributed_variables::*;
|
||||
@@ -14,6 +13,7 @@ use crate::machine::stack::*;
|
||||
use crate::machine::unify::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::types::*;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
@@ -50,7 +50,7 @@ impl MachineState {
|
||||
ball: Ball::new(),
|
||||
ball_stack: vec![],
|
||||
lifted_heap: Heap::new(),
|
||||
interms: vec![Number::default();256],
|
||||
interms: vec![Number::default(); 256],
|
||||
cont_pts: Vec::with_capacity(256),
|
||||
cwil: CWIL::new(),
|
||||
flags: MachineFlags::default(),
|
||||
@@ -59,8 +59,7 @@ impl MachineState {
|
||||
dynamic_mode: FirstOrNext::First,
|
||||
unify_fn: MachineState::unify,
|
||||
bind_fn: MachineState::bind,
|
||||
run_cleaners_fn: |_| { false },
|
||||
increment_call_count_fn: |_| { Ok(()) },
|
||||
run_cleaners_fn: |_| false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +148,7 @@ impl MachineState {
|
||||
TrailRef::BlackboardEntry(key_atom) => {
|
||||
self.trail.push(TrailEntry::build_with(
|
||||
TrailEntryTag::TrailedBlackboardEntry,
|
||||
key_atom.index as u64,
|
||||
key_atom.index,
|
||||
));
|
||||
|
||||
self.tr += 1;
|
||||
@@ -157,12 +156,11 @@ impl MachineState {
|
||||
TrailRef::BlackboardOffset(key_atom, value_cell) => {
|
||||
self.trail.push(TrailEntry::build_with(
|
||||
TrailEntryTag::TrailedBlackboardOffset,
|
||||
key_atom.index as u64,
|
||||
key_atom.index,
|
||||
));
|
||||
|
||||
self.trail.push(TrailEntry::from_bytes(
|
||||
value_cell.into_bytes(),
|
||||
));
|
||||
self.trail
|
||||
.push(TrailEntry::from_bytes(value_cell.into_bytes()));
|
||||
|
||||
self.tr += 2;
|
||||
}
|
||||
@@ -248,9 +246,7 @@ impl MachineState {
|
||||
r: Ref,
|
||||
value: HeapCellValue,
|
||||
) {
|
||||
let mut unifier = CompositeUnifierForOccursCheckWithError::from(
|
||||
DefaultUnifier::from(self),
|
||||
);
|
||||
let mut unifier = CompositeUnifierForOccursCheckWithError::from(DefaultUnifier::from(self));
|
||||
|
||||
unifier.bind(r, value);
|
||||
}
|
||||
@@ -297,12 +293,12 @@ impl MachineState {
|
||||
|
||||
pub fn unify_big_int(&mut self, n1: TypedArenaPtr<Integer>, value: HeapCellValue) {
|
||||
let mut unifier = DefaultUnifier::from(self);
|
||||
unifier.unify_big_num(n1, value);
|
||||
unifier.unify_big_integer(n1, value);
|
||||
}
|
||||
|
||||
pub fn unify_rational(&mut self, n1: TypedArenaPtr<Rational>, value: HeapCellValue) {
|
||||
let mut unifier = DefaultUnifier::from(self);
|
||||
unifier.unify_big_num(n1, value);
|
||||
unifier.unify_big_rational(n1, value);
|
||||
}
|
||||
|
||||
pub fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) {
|
||||
@@ -316,9 +312,7 @@ impl MachineState {
|
||||
}
|
||||
|
||||
pub(super) fn unify_with_occurs_check_with_error(&mut self) {
|
||||
let mut unifier = CompositeUnifierForOccursCheckWithError::from(
|
||||
DefaultUnifier::from(self),
|
||||
);
|
||||
let mut unifier = CompositeUnifierForOccursCheckWithError::from(DefaultUnifier::from(self));
|
||||
|
||||
unifier.unify_internal();
|
||||
}
|
||||
@@ -340,7 +334,12 @@ impl MachineState {
|
||||
self.ball.boundary = self.heap.len();
|
||||
|
||||
copy_term(
|
||||
CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut self.ball.stub),
|
||||
CopyBallTerm::new(
|
||||
&mut self.attr_var_init.attr_var_queue,
|
||||
&mut self.stack,
|
||||
&mut self.heap,
|
||||
&mut self.ball.stub,
|
||||
),
|
||||
addr,
|
||||
AttrVarPolicy::DeepCopy,
|
||||
);
|
||||
@@ -380,8 +379,7 @@ impl MachineState {
|
||||
}
|
||||
)
|
||||
}
|
||||
&mut HeapPtr::PStrChar(h, ref mut n) |
|
||||
&mut HeapPtr::PStrLocation(h, ref mut n) => {
|
||||
&mut HeapPtr::PStrChar(h, ref mut n) | &mut HeapPtr::PStrLocation(h, ref mut n) => {
|
||||
read_heap_cell!(self.heap[h],
|
||||
(HeapCellValueTag::PStr, pstr_atom) => {
|
||||
let pstr = PartialString::from(pstr_atom);
|
||||
@@ -438,8 +436,7 @@ impl MachineState {
|
||||
pub fn compare_term_test(&mut self, var_comparison: VarComparison) -> Option<Ordering> {
|
||||
let mut tabu_list = IndexSet::new();
|
||||
|
||||
while !self.pdl.is_empty() {
|
||||
let s1 = self.pdl.pop().unwrap();
|
||||
while let Some(s1) = self.pdl.pop() {
|
||||
let s1 = self.deref(s1);
|
||||
|
||||
let s2 = self.pdl.pop().unwrap();
|
||||
@@ -509,7 +506,7 @@ impl MachineState {
|
||||
} else {
|
||||
self.pdl.clear();
|
||||
return Some(
|
||||
n1.chars().next().cmp(&Some(c2))
|
||||
n1.as_str().chars().next().cmp(&Some(c2))
|
||||
.then(Ordering::Greater)
|
||||
);
|
||||
}
|
||||
@@ -539,7 +536,7 @@ impl MachineState {
|
||||
} else {
|
||||
self.pdl.clear();
|
||||
return Some(
|
||||
Some(c1).cmp(&n2.chars().next())
|
||||
Some(c1).cmp(&n2.as_str().chars().next())
|
||||
.then(Ordering::Less)
|
||||
);
|
||||
}
|
||||
@@ -562,7 +559,7 @@ impl MachineState {
|
||||
} else {
|
||||
self.pdl.clear();
|
||||
return Some(
|
||||
Some(c1).cmp(&n2.chars().next())
|
||||
Some(c1).cmp(&n2.as_str().chars().next())
|
||||
.then(Ordering::Less)
|
||||
);
|
||||
}
|
||||
@@ -592,7 +589,7 @@ impl MachineState {
|
||||
} else {
|
||||
self.pdl.clear();
|
||||
return Some(
|
||||
n1.chars().next().cmp(&Some(c2))
|
||||
n1.as_str().chars().next().cmp(&Some(c2))
|
||||
.then(Ordering::Greater)
|
||||
);
|
||||
}
|
||||
@@ -639,7 +636,7 @@ impl MachineState {
|
||||
// iter2 is continuable, so it
|
||||
// has a tail in the heap at
|
||||
// focus+1.
|
||||
pdl.push(iter2.heap[focus+1]);
|
||||
pdl.push(iter2.heap[focus + 1]);
|
||||
|
||||
return None;
|
||||
}
|
||||
@@ -902,8 +899,12 @@ impl MachineState {
|
||||
|
||||
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() => {
|
||||
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];
|
||||
|
||||
read_heap_cell!(focus_addr,
|
||||
@@ -950,7 +951,10 @@ impl MachineState {
|
||||
|
||||
return;
|
||||
}
|
||||
Some(PStrPrefixCmpResult { prefix_len: inner_prefix_len, .. }) => {
|
||||
Some(PStrPrefixCmpResult {
|
||||
prefix_len: inner_prefix_len,
|
||||
..
|
||||
}) => {
|
||||
prefix_len = inner_prefix_len;
|
||||
}
|
||||
None => {
|
||||
@@ -1002,9 +1006,9 @@ impl MachineState {
|
||||
self.s_offset = 0;
|
||||
self.mode = MachineMode::Read;
|
||||
|
||||
put_partial_string(&mut self.heap, pstr, &mut self.atom_tbl)
|
||||
put_partial_string(&mut self.heap, pstr, &self.atom_tbl)
|
||||
} else {
|
||||
put_complete_string(&mut self.heap, pstr, &mut self.atom_tbl)
|
||||
put_complete_string(&mut self.heap, pstr, &self.atom_tbl)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1096,7 +1100,7 @@ impl MachineState {
|
||||
(name, 0, 0)
|
||||
}
|
||||
(HeapCellValueTag::Char, c) => {
|
||||
(self.atom_tbl.build_with(&c.to_string()), 0, 0)
|
||||
(AtomTable::build_with(&self.atom_tbl, &c.to_string()), 0, 0)
|
||||
}
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
|
||||
let stub = functor_stub(atom!("call"), arity + 1);
|
||||
@@ -1130,26 +1134,23 @@ impl MachineState {
|
||||
|
||||
#[inline]
|
||||
pub fn is_cyclic_term(&mut self, value: HeapCellValue) -> bool {
|
||||
if value.is_constant() {
|
||||
let value = self.store(self.deref(value));
|
||||
|
||||
if value.is_stack_var() || value.is_constant() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value);
|
||||
let h = self.heap.len();
|
||||
self.heap.push(value);
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
if value.get_forwarding_bit() {
|
||||
let value = unmark_cell_bits!(heap_bound_store(
|
||||
iter.heap,
|
||||
heap_bound_deref(iter.heap, value),
|
||||
));
|
||||
let cycle_found = {
|
||||
let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h);
|
||||
for _ in iter.by_ref() {}
|
||||
iter.cycle_found()
|
||||
};
|
||||
|
||||
if value.is_compound(iter.heap) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
self.heap.pop();
|
||||
cycle_found
|
||||
}
|
||||
|
||||
// arg(+N, +Term, ?Arg)
|
||||
@@ -1181,7 +1182,7 @@ impl MachineState {
|
||||
|
||||
let n = match n {
|
||||
Number::Fixnum(n) => n.get_num() as usize,
|
||||
Number::Integer(n) if *n >= 0 && *n <= std::usize::MAX => n.to_usize().unwrap(),
|
||||
Number::Integer(n) if usize::try_from(&*n).is_ok() => (&*n).try_into().unwrap(),
|
||||
_ => {
|
||||
self.fail = true;
|
||||
return Ok(());
|
||||
@@ -1324,7 +1325,7 @@ impl MachineState {
|
||||
|
||||
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));
|
||||
self.heap.push(heap_loc_as_cell!(h + 1));
|
||||
|
||||
list_loc_as_cell!(h)
|
||||
} else {
|
||||
@@ -1378,7 +1379,7 @@ impl MachineState {
|
||||
|
||||
let mut type_error = |arity| {
|
||||
let err = self.type_error(ValidType::Integer, arity);
|
||||
return Err(self.error_form(err, stub_gen()));
|
||||
Err(self.error_form(err, stub_gen()))
|
||||
};
|
||||
|
||||
let arity = match Number::try_from(arity) {
|
||||
@@ -1398,9 +1399,15 @@ impl MachineState {
|
||||
let err = self.domain_error(DomainErrorType::NotLessThanZero, n);
|
||||
return Err(self.error_form(err, stub_gen()));
|
||||
}
|
||||
Ok(Number::Rational(n)) => n.numerator().to_i64().unwrap(),
|
||||
Ok(Number::Rational(n)) => {
|
||||
let value: i64 = n.numerator().try_into().unwrap();
|
||||
value
|
||||
},
|
||||
Ok(Number::Fixnum(n)) => n.get_num(),
|
||||
Ok(Number::Integer(n)) => n.to_i64().unwrap(),
|
||||
Ok(Number::Integer(n)) => {
|
||||
let value: i64 = (&*n).try_into().unwrap();
|
||||
value
|
||||
},
|
||||
Err(_) => {
|
||||
return type_error(arity);
|
||||
}
|
||||
@@ -1435,7 +1442,7 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Char, c) => {
|
||||
let c = self.atom_tbl.build_with(&c.to_string());
|
||||
let c = AtomTable::build_with(&self.atom_tbl, &c.to_string());
|
||||
|
||||
self.try_functor_fabricate_struct(
|
||||
c,
|
||||
@@ -1443,10 +1450,15 @@ impl MachineState {
|
||||
a1.as_var().unwrap(),
|
||||
);
|
||||
}
|
||||
(HeapCellValueTag::Cons | HeapCellValueTag::Fixnum |
|
||||
HeapCellValueTag::F64) if arity != 0 => {
|
||||
let err = self.type_error(ValidType::Atom, store_name);
|
||||
return Err(self.error_form(err, stub_gen())); // 8.5.1.3 e)
|
||||
}
|
||||
_ => {
|
||||
let err = self.type_error(ValidType::Atomic, store_name);
|
||||
return Err(self.error_form(err, stub_gen()));
|
||||
} // 8.5.1.3 c)
|
||||
return Err(self.error_form(err, stub_gen())); // 8.5.1.3 c)
|
||||
}
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
@@ -1569,10 +1581,9 @@ impl MachineState {
|
||||
) -> Result<Vec<HeapCellValue>, MachineStub> {
|
||||
let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, h);
|
||||
|
||||
while let Some(iteratee) = heap_pstr_iter.next() {
|
||||
for iteratee in heap_pstr_iter.by_ref() {
|
||||
match iteratee {
|
||||
PStrIteratee::Char(_, c) =>
|
||||
chars.push(char_as_cell!(c)),
|
||||
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)));
|
||||
@@ -1614,47 +1625,12 @@ impl MachineState {
|
||||
|
||||
// returns true on failure.
|
||||
pub fn ground_test(&mut self) -> bool {
|
||||
use fxhash::FxBuildHasher;
|
||||
let iter = eager_stackful_preorder_iter(&mut self.heap, self.registers[1]);
|
||||
|
||||
if self.registers[1].is_constant() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let value = self.store(self.deref(self.registers[1]));
|
||||
|
||||
if value.is_stack_var() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let mut visited = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
let mut iter = stackful_preorder_iter(&mut self.heap, &mut self.stack, value);
|
||||
let mut stack_len = 0;
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
let mut value = unmark_cell_bits!(value);
|
||||
|
||||
if value.is_var() {
|
||||
value = heap_bound_store(
|
||||
iter.heap,
|
||||
heap_bound_deref(iter.heap, value),
|
||||
);
|
||||
|
||||
if value.is_var() {
|
||||
return true;
|
||||
}
|
||||
for term in iter {
|
||||
if term.is_var() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if value.is_compound(iter.heap) {
|
||||
if visited.contains(&value) {
|
||||
for _ in stack_len .. iter.stack_len() {
|
||||
iter.pop_stack();
|
||||
}
|
||||
} else {
|
||||
visited.insert(value);
|
||||
}
|
||||
}
|
||||
|
||||
stack_len = iter.stack_len();
|
||||
}
|
||||
|
||||
false
|
||||
@@ -1676,15 +1652,16 @@ impl MachineState {
|
||||
let addr = self.store(self.deref(addr));
|
||||
|
||||
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);
|
||||
Ok(Number::Fixnum(n)) => {
|
||||
if let Ok(b) = u8::try_from(n.get_num()) {
|
||||
bytes.push(b)
|
||||
}
|
||||
}
|
||||
Ok(Number::Integer(n)) => {
|
||||
let b: u8 = (&*n).try_into().unwrap();
|
||||
|
||||
bytes.push(b);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,16 @@ pub use crate::arena::*;
|
||||
pub use crate::atom_table::*;
|
||||
use crate::heap_print::*;
|
||||
pub use crate::machine::heap::*;
|
||||
pub use crate::machine::*;
|
||||
pub use crate::machine::machine_state::*;
|
||||
pub use crate::machine::stack::*;
|
||||
pub use crate::machine::streams::*;
|
||||
pub use crate::macros::*;
|
||||
pub use crate::machine::*;
|
||||
pub use crate::parser::ast::*;
|
||||
use crate::read::*;
|
||||
pub use crate::types::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::machine::copier::CopierTarget;
|
||||
|
||||
@@ -61,7 +62,7 @@ impl MockWAM {
|
||||
|
||||
let mut printer = HCPrinter::new(
|
||||
&mut self.machine_st.heap,
|
||||
&mut self.machine_st.atom_tbl,
|
||||
Arc::clone(&self.machine_st.atom_tbl),
|
||||
&mut self.machine_st.stack,
|
||||
&self.op_dir,
|
||||
PrinterOutputter::new(),
|
||||
@@ -71,11 +72,9 @@ impl MockWAM {
|
||||
printer.var_names = term_write_result
|
||||
.var_dict
|
||||
.into_iter()
|
||||
.map(|(var, cell)| {
|
||||
match var {
|
||||
VarKey::VarPtr(var) => (cell, var.clone()),
|
||||
VarKey::AnonVar(_) => (cell, VarPtr::from(var.to_string()))
|
||||
}
|
||||
.map(|(var, cell)| match var {
|
||||
VarKey::VarPtr(var) => (cell, var.clone()),
|
||||
VarKey::AnonVar(_) => (cell, VarPtr::from(var.to_string())),
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -83,6 +82,12 @@ impl MockWAM {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MockWAM {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub struct TermCopyingMockWAM<'a> {
|
||||
pub wam: &'a mut MockWAM,
|
||||
@@ -110,14 +115,14 @@ impl<'a> Deref for TermCopyingMockWAM<'a> {
|
||||
type Target = MockWAM;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.wam
|
||||
self.wam
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<'a> DerefMut for TermCopyingMockWAM<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.wam
|
||||
self.wam
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +159,14 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
|
||||
self.wam.machine_st.heap.push(val);
|
||||
}
|
||||
|
||||
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
|
||||
self.wam
|
||||
.machine_st
|
||||
.attr_var_init
|
||||
.attr_var_queue
|
||||
.push(attr_var_loc);
|
||||
}
|
||||
|
||||
fn stack(&mut self) -> &mut Stack {
|
||||
&mut self.wam.machine_st.stack
|
||||
}
|
||||
@@ -166,9 +179,8 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
|
||||
#[cfg(test)]
|
||||
pub fn all_cells_marked_and_unforwarded(heap: &[HeapCellValue]) {
|
||||
for (idx, cell) in heap.iter().enumerate() {
|
||||
assert_eq!(
|
||||
assert!(
|
||||
cell.get_mark_bit(),
|
||||
true,
|
||||
"cell {:?} at index {} is not marked",
|
||||
cell,
|
||||
idx
|
||||
@@ -222,117 +234,23 @@ pub(crate) fn parse_and_write_parsed_term_to_heap(
|
||||
|
||||
impl Machine {
|
||||
pub fn with_test_streams() -> Self {
|
||||
use ref_thread_local::RefThreadLocal;
|
||||
|
||||
let mut machine_st = MachineState::new();
|
||||
|
||||
let user_input = Stream::Null(StreamOptions::default());
|
||||
let user_output = Stream::from_owned_string("".to_owned(), &mut machine_st.arena);
|
||||
let user_error = Stream::stderr(&mut machine_st.arena);
|
||||
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut wam = Machine {
|
||||
machine_st,
|
||||
indices: IndexStore::new(),
|
||||
code: Code::new(),
|
||||
user_input,
|
||||
user_output,
|
||||
user_error,
|
||||
load_contexts: vec![],
|
||||
runtime,
|
||||
#[cfg(feature = "ffi")]
|
||||
foreign_function_table: Default::default(),
|
||||
};
|
||||
|
||||
let mut lib_path = current_dir();
|
||||
|
||||
lib_path.pop();
|
||||
lib_path.push("lib");
|
||||
|
||||
wam.add_impls_to_indices();
|
||||
|
||||
bootstrapping_compile(
|
||||
Stream::from_static_string(
|
||||
LIBRARIES.borrow()["ops_and_meta_predicates"],
|
||||
&mut wam.machine_st.arena,
|
||||
),
|
||||
&mut wam,
|
||||
ListingSource::from_file_and_path(
|
||||
atom!("ops_and_meta_predicates.pl"),
|
||||
lib_path.clone(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
bootstrapping_compile(
|
||||
Stream::from_static_string(
|
||||
LIBRARIES.borrow()["builtins"],
|
||||
&mut wam.machine_st.arena,
|
||||
),
|
||||
&mut wam,
|
||||
ListingSource::from_file_and_path(atom!("builtins.pl"), lib_path.clone()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let Some(ref mut builtins) = wam.indices.modules.get_mut(&atom!("builtins")) {
|
||||
load_module(
|
||||
&mut wam.machine_st,
|
||||
&mut wam.indices.code_dir,
|
||||
&mut wam.indices.op_dir,
|
||||
&mut wam.indices.meta_predicates,
|
||||
&CompilationTarget::User,
|
||||
builtins,
|
||||
);
|
||||
|
||||
import_builtin_impls(&wam.indices.code_dir, builtins);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
lib_path.pop(); // remove the "lib" at the end
|
||||
|
||||
bootstrapping_compile(
|
||||
Stream::from_static_string(include_str!("../loader.pl"), &mut wam.machine_st.arena),
|
||||
&mut wam,
|
||||
ListingSource::from_file_and_path(atom!("loader.pl"), lib_path.clone()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
wam.configure_modules();
|
||||
|
||||
if let Some(loader) = wam.indices.modules.get(&atom!("loader")) {
|
||||
load_module(
|
||||
&mut wam.machine_st,
|
||||
&mut wam.indices.code_dir,
|
||||
&mut wam.indices.op_dir,
|
||||
&mut wam.indices.meta_predicates,
|
||||
&CompilationTarget::User,
|
||||
loader,
|
||||
);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
wam.load_special_forms();
|
||||
wam.load_top_level();
|
||||
wam.configure_streams();
|
||||
|
||||
wam
|
||||
Machine::new(MachineConfig::in_memory())
|
||||
}
|
||||
|
||||
pub fn test_load_file(&mut self, file: &str) -> Vec<u8> {
|
||||
use std::io::Read;
|
||||
|
||||
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);
|
||||
self.load_file(file, stream);
|
||||
self.user_output.bytes().map(|b| b.unwrap()).collect()
|
||||
}
|
||||
|
||||
pub fn test_load_string(&mut self, code: &str) -> Vec<u8> {
|
||||
let stream = Stream::from_owned_string(code.to_owned(), &mut self.machine_st.arena);
|
||||
|
||||
self.load_file("<stdin>", stream);
|
||||
self.user_output.bytes().map(|b| b.unwrap()).collect()
|
||||
}
|
||||
}
|
||||
@@ -342,30 +260,16 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
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),
|
||||
);
|
||||
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();
|
||||
@@ -578,26 +482,15 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
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),
|
||||
);
|
||||
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();
|
||||
@@ -688,20 +581,12 @@ mod tests {
|
||||
wam.heap.push(heap_loc_as_cell!(1));
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
heap_loc_as_cell!(0),
|
||||
heap_loc_as_cell!(0)
|
||||
),
|
||||
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"))
|
||||
),
|
||||
compare_term_test!(wam, heap_loc_as_cell!(0), atom_as_cell!(atom!("a"))),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
|
||||
@@ -724,29 +609,17 @@ mod tests {
|
||||
wam.heap.push(empty_list_as_cell!());
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
heap_loc_as_cell!(7),
|
||||
heap_loc_as_cell!(7)
|
||||
),
|
||||
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)
|
||||
),
|
||||
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)
|
||||
),
|
||||
compare_term_test!(wam, empty_list_as_cell!(), heap_loc_as_cell!(7)),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
|
||||
@@ -769,40 +642,24 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
empty_list_as_cell!(),
|
||||
atom_as_cell!(atom!("atom"))
|
||||
),
|
||||
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!()
|
||||
),
|
||||
compare_term_test!(wam, atom_as_cell!(atom!("atom")), empty_list_as_cell!()),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
|
||||
let one_p_one = HeapCellValue::from(float_alloc!(1.1, &mut wam.arena));
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
one_p_one,
|
||||
fixnum_as_cell!(Fixnum::build_with(1))
|
||||
),
|
||||
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
|
||||
),
|
||||
compare_term_test!(wam, fixnum_as_cell!(Fixnum::build_with(1)), one_p_one),
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
}
|
||||
@@ -821,7 +678,8 @@ mod tests {
|
||||
all_cells_unmarked(&wam.heap);
|
||||
wam.heap.clear();
|
||||
|
||||
wam.heap.extend(functor!(atom!("f"), [atom(atom!("a")), atom(atom!("b"))]));
|
||||
wam.heap
|
||||
.extend(functor!(atom!("f"), [atom(atom!("a")), atom(atom!("b"))]));
|
||||
|
||||
assert!(!wam.is_cyclic_term(str_loc_as_cell!(0)));
|
||||
|
||||
|
||||
@@ -5,18 +5,22 @@ pub mod code_walker;
|
||||
#[macro_use]
|
||||
pub mod loader;
|
||||
pub mod compile;
|
||||
pub mod config;
|
||||
pub mod copier;
|
||||
pub mod cycle_detection;
|
||||
pub mod disjuncts;
|
||||
pub mod dispatch;
|
||||
pub mod gc;
|
||||
pub mod heap;
|
||||
pub mod lib_machine;
|
||||
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 parsed_results;
|
||||
pub mod partial_string;
|
||||
pub mod disjuncts;
|
||||
pub mod preprocessor;
|
||||
pub mod stack;
|
||||
pub mod streams;
|
||||
@@ -27,9 +31,9 @@ pub mod unify;
|
||||
use crate::arena::*;
|
||||
use crate::arithmetic::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
#[cfg(feature = "ffi")]
|
||||
use crate::ffi::ForeignFunctionTable;
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::machine::args::*;
|
||||
use crate::machine::compile::*;
|
||||
@@ -49,11 +53,16 @@ use indexmap::IndexMap;
|
||||
use lazy_static::lazy_static;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
use std::cmp::Ordering;
|
||||
use std::env;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use self::config::MachineConfig;
|
||||
use self::parsed_results::*;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
|
||||
@@ -68,9 +77,9 @@ pub struct Machine {
|
||||
pub(super) user_output: Stream,
|
||||
pub(super) user_error: Stream,
|
||||
pub(super) load_contexts: Vec<LoadContext>,
|
||||
pub(super) runtime: Runtime,
|
||||
#[cfg(feature = "ffi")]
|
||||
pub(super) foreign_function_table: ForeignFunctionTable,
|
||||
pub(super) rng: StdRng,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -109,6 +118,7 @@ include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
|
||||
pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0;
|
||||
pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1;
|
||||
pub static VERIFY_ATTR_INTERRUPT_LOC: usize = 2;
|
||||
pub static LIB_QUERY_SUCCESS: usize = 3;
|
||||
|
||||
pub struct MachinePreludeView<'a> {
|
||||
pub indices: &'a mut IndexStore,
|
||||
@@ -162,8 +172,11 @@ pub(crate) fn import_builtin_impls(code_dir: &CodeDir, builtins: &mut Module) {
|
||||
|
||||
for key in keys {
|
||||
let idx = code_dir.get(&key).unwrap();
|
||||
builtins.code_dir.insert(key, idx.clone());
|
||||
builtins.module_decl.exports.push(ModuleExport::PredicateKey(key));
|
||||
builtins.code_dir.insert(key, *idx);
|
||||
builtins
|
||||
.module_decl
|
||||
.exports
|
||||
.push(ModuleExport::PredicateKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,10 +207,19 @@ impl Machine {
|
||||
code: &mut self.code,
|
||||
load_contexts: &mut self.load_contexts,
|
||||
},
|
||||
&mut self.machine_st
|
||||
&mut self.machine_st,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_inference_count(&mut self) -> u64 {
|
||||
self.machine_st
|
||||
.cwil
|
||||
.global_count
|
||||
.clone()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -206,9 +228,13 @@ impl Machine {
|
||||
self.machine_st.throw_exception(err);
|
||||
}
|
||||
|
||||
fn run_module_predicate(&mut self, module_name: Atom, key: PredicateKey) -> std::process::ExitCode {
|
||||
pub fn run_module_predicate(
|
||||
&mut self,
|
||||
module_name: Atom,
|
||||
key: PredicateKey,
|
||||
) -> std::process::ExitCode {
|
||||
if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(ref code_index) = module.code_dir.get(&key) {
|
||||
if let Some(code_index) = module.code_dir.get(&key) {
|
||||
let p = code_index.local().unwrap();
|
||||
|
||||
self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC;
|
||||
@@ -223,23 +249,19 @@ impl Machine {
|
||||
|
||||
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.machine_st.registers[2] =
|
||||
atom_as_cell!(AtomTable::build_with(&self.machine_st.atom_tbl, path));
|
||||
|
||||
self.run_module_predicate(atom!("loader"), (atom!("file_load"), 2));
|
||||
}
|
||||
|
||||
fn load_top_level(&mut self) {
|
||||
fn load_top_level(&mut self, program: &'static str) {
|
||||
let mut path_buf = current_dir();
|
||||
|
||||
path_buf.push("src/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,
|
||||
);
|
||||
let toplevel_stream = Stream::from_static_string(program, &mut self.machine_st.arena);
|
||||
|
||||
self.load_file(path, toplevel_stream);
|
||||
|
||||
@@ -285,26 +307,21 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_top_level(&mut self) -> std::process::ExitCode {
|
||||
let mut arg_pstrs = vec![];
|
||||
pub fn set_user_input(&mut self, input: String) {
|
||||
self.user_input = Stream::from_owned_string(input, &mut self.machine_st.arena);
|
||||
}
|
||||
|
||||
for arg in env::args() {
|
||||
arg_pstrs.push(put_complete_string(
|
||||
&mut self.machine_st.heap,
|
||||
&arg,
|
||||
&mut self.machine_st.atom_tbl,
|
||||
));
|
||||
}
|
||||
|
||||
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(atom!("$toplevel"), (atom!("$repl"), 1))
|
||||
pub fn get_user_output(&self) -> String {
|
||||
let output_bytes: Vec<_> = self.user_output.bytes().map(|b| b.unwrap()).collect();
|
||||
String::from_utf8(output_bytes).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn configure_modules(&mut self) {
|
||||
fn update_call_n_indices(loader: &Module, target_code_dir: &mut CodeDir, arena: &mut Arena) {
|
||||
fn update_call_n_indices(
|
||||
loader: &Module,
|
||||
target_code_dir: &mut CodeDir,
|
||||
arena: &mut Arena,
|
||||
) {
|
||||
for arity in 1..66 {
|
||||
let key = (atom!("call"), arity);
|
||||
|
||||
@@ -349,10 +366,18 @@ impl Machine {
|
||||
}
|
||||
|
||||
for (_, target_module) in self.indices.modules.iter_mut() {
|
||||
update_call_n_indices(&loader, &mut target_module.code_dir, &mut self.machine_st.arena);
|
||||
update_call_n_indices(
|
||||
&loader,
|
||||
&mut target_module.code_dir,
|
||||
&mut self.machine_st.arena,
|
||||
);
|
||||
}
|
||||
|
||||
update_call_n_indices(&loader, &mut self.indices.code_dir, &mut self.machine_st.arena);
|
||||
update_call_n_indices(
|
||||
&loader,
|
||||
&mut self.indices.code_dir,
|
||||
&mut self.machine_st.arena,
|
||||
);
|
||||
|
||||
self.indices.modules.insert(atom!("loader"), loader);
|
||||
} else {
|
||||
@@ -361,12 +386,13 @@ impl Machine {
|
||||
}
|
||||
|
||||
pub(crate) fn add_impls_to_indices(&mut self) {
|
||||
let impls_offset = self.code.len() + 3;
|
||||
let impls_offset = self.code.len() + 4;
|
||||
|
||||
self.code.extend(vec![
|
||||
Instruction::BreakFromDispatchLoop,
|
||||
Instruction::InstallVerifyAttr,
|
||||
Instruction::VerifyAttrInterrupt,
|
||||
Instruction::VerifyAttrInterrupt(0),
|
||||
Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS
|
||||
Instruction::ExecuteTermGreaterThan,
|
||||
Instruction::ExecuteTermLessThan,
|
||||
Instruction::ExecuteTermGreaterThanOrEqual,
|
||||
@@ -405,36 +431,40 @@ impl Machine {
|
||||
Instruction::ExecuteIsRational(temp_v!(1)),
|
||||
Instruction::ExecuteIsFloat(temp_v!(1)),
|
||||
Instruction::ExecuteIsNonVar(temp_v!(1)),
|
||||
Instruction::ExecuteIsVar(temp_v!(1))
|
||||
].into_iter());
|
||||
Instruction::ExecuteIsVar(temp_v!(1)),
|
||||
]);
|
||||
|
||||
for (p, instr) in self.code[impls_offset ..].iter().enumerate() {
|
||||
for (p, instr) in self.code[impls_offset..].iter().enumerate() {
|
||||
let key = instr.to_name_and_arity();
|
||||
self.indices.code_dir.insert(
|
||||
key,
|
||||
CodeIndex::new(IndexPtr::index(p + impls_offset), &mut self.machine_st.arena),
|
||||
CodeIndex::new(
|
||||
IndexPtr::index(p + impls_offset),
|
||||
&mut self.machine_st.arena,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new(config: MachineConfig) -> Self {
|
||||
use ref_thread_local::RefThreadLocal;
|
||||
|
||||
let args = MachineArgs::new();
|
||||
let mut machine_st = MachineState::new();
|
||||
|
||||
let user_input = Stream::stdin(&mut machine_st.arena, args.add_history);
|
||||
let user_output = Stream::stdout(&mut machine_st.arena);
|
||||
let user_error = Stream::stderr(&mut machine_st.arena);
|
||||
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
let runtime = tokio::runtime::Runtime::new()
|
||||
.unwrap();
|
||||
#[cfg(target_os = "wasi")]
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
let (user_input, user_output, user_error) = match config.streams {
|
||||
config::StreamConfig::Stdio => (
|
||||
Stream::stdin(&mut machine_st.arena, args.add_history),
|
||||
Stream::stdout(&mut machine_st.arena),
|
||||
Stream::stderr(&mut machine_st.arena),
|
||||
),
|
||||
config::StreamConfig::Memory => (
|
||||
Stream::Null(StreamOptions::default()),
|
||||
Stream::from_owned_string("".to_owned(), &mut machine_st.arena),
|
||||
Stream::stderr(&mut machine_st.arena),
|
||||
),
|
||||
};
|
||||
|
||||
let mut wam = Machine {
|
||||
machine_st,
|
||||
@@ -444,9 +474,9 @@ impl Machine {
|
||||
user_output,
|
||||
user_error,
|
||||
load_contexts: vec![],
|
||||
runtime,
|
||||
#[cfg(feature = "ffi")]
|
||||
foreign_function_table: Default::default(),
|
||||
foreign_function_table: Default::default(),
|
||||
rng: StdRng::from_entropy(),
|
||||
};
|
||||
|
||||
let mut lib_path = current_dir();
|
||||
@@ -470,10 +500,7 @@ impl Machine {
|
||||
.unwrap();
|
||||
|
||||
bootstrapping_compile(
|
||||
Stream::from_static_string(
|
||||
LIBRARIES.borrow()["builtins"],
|
||||
&mut wam.machine_st.arena,
|
||||
),
|
||||
Stream::from_static_string(LIBRARIES.borrow()["builtins"], &mut wam.machine_st.arena),
|
||||
&mut wam,
|
||||
ListingSource::from_file_and_path(atom!("builtins.pl"), lib_path.clone()),
|
||||
)
|
||||
@@ -519,14 +546,16 @@ impl Machine {
|
||||
}
|
||||
|
||||
wam.load_special_forms();
|
||||
wam.load_top_level();
|
||||
wam.load_top_level(config.toplevel);
|
||||
wam.configure_streams();
|
||||
|
||||
wam
|
||||
}
|
||||
|
||||
pub(crate) fn configure_streams(&mut self) {
|
||||
self.user_input.options_mut().set_alias_to_atom_opt(Some(atom!("user_input")));
|
||||
self.user_input
|
||||
.options_mut()
|
||||
.set_alias_to_atom_opt(Some(atom!("user_input")));
|
||||
|
||||
self.indices
|
||||
.stream_aliases
|
||||
@@ -534,7 +563,9 @@ impl Machine {
|
||||
|
||||
self.indices.streams.insert(self.user_input);
|
||||
|
||||
self.user_output.options_mut().set_alias_to_atom_opt(Some(atom!("user_output")));
|
||||
self.user_output
|
||||
.options_mut()
|
||||
.set_alias_to_atom_opt(Some(atom!("user_output")));
|
||||
|
||||
self.indices
|
||||
.stream_aliases
|
||||
@@ -564,9 +595,16 @@ impl Machine {
|
||||
|
||||
loop {
|
||||
let indexing_code_ptr = match &indexing_lines[oip] {
|
||||
&IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(arg, v, c, l, s)) => {
|
||||
&IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
|
||||
arg,
|
||||
v,
|
||||
c,
|
||||
l,
|
||||
s,
|
||||
)) => {
|
||||
cell = self.deref_register(arg);
|
||||
self.machine_st.select_switch_on_term_index(cell, v, c, l, s)
|
||||
self.machine_st
|
||||
.select_switch_on_term_index(cell, v, c, l, s)
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => {
|
||||
let lit = self.machine_st.constant_to_literal(cell);
|
||||
@@ -676,7 +714,12 @@ impl Machine {
|
||||
}
|
||||
);
|
||||
}
|
||||
&Instruction::GetPartialString(Level::Shallow, string, RegType::Temp(t), has_tail) => {
|
||||
&Instruction::GetPartialString(
|
||||
Level::Shallow,
|
||||
string,
|
||||
RegType::Temp(t),
|
||||
has_tail,
|
||||
) => {
|
||||
let cell = self.deref_register(t);
|
||||
|
||||
read_heap_cell!(cell,
|
||||
@@ -707,17 +750,17 @@ impl Machine {
|
||||
}
|
||||
);
|
||||
}
|
||||
Instruction::GetConstant(..) |
|
||||
Instruction::GetList(..) |
|
||||
Instruction::GetStructure(..) |
|
||||
Instruction::GetPartialString(..) |
|
||||
&Instruction::UnifyVoid(..) |
|
||||
&Instruction::UnifyConstant(..) |
|
||||
&Instruction::GetVariable(..) |
|
||||
&Instruction::GetValue(..) |
|
||||
&Instruction::UnifyVariable(..) |
|
||||
&Instruction::UnifyValue(..) |
|
||||
&Instruction::UnifyLocalValue(..) => {
|
||||
Instruction::GetConstant(..)
|
||||
| Instruction::GetList(..)
|
||||
| Instruction::GetStructure(..)
|
||||
| Instruction::GetPartialString(..)
|
||||
| &Instruction::UnifyVoid(..)
|
||||
| &Instruction::UnifyConstant(..)
|
||||
| &Instruction::GetVariable(..)
|
||||
| &Instruction::GetValue(..)
|
||||
| &Instruction::UnifyVariable(..)
|
||||
| &Instruction::UnifyValue(..)
|
||||
| &Instruction::UnifyLocalValue(..) => {
|
||||
offset += 1;
|
||||
}
|
||||
_ => {
|
||||
@@ -732,9 +775,10 @@ impl Machine {
|
||||
fn next_applicable_clause(&mut self, mut offset: usize) -> Option<usize> {
|
||||
while !self.next_clause_applicable(self.machine_st.p + offset + 1) {
|
||||
match &self.code[self.machine_st.p + offset] {
|
||||
&Instruction::DefaultRetryMeElse(o) | &Instruction::RetryMeElse(o) |
|
||||
&Instruction::DynamicElse(.., NextOrFail::Next(o)) |
|
||||
&Instruction::DynamicInternalElse(.., NextOrFail::Next(o)) => offset += o,
|
||||
&Instruction::DefaultRetryMeElse(o)
|
||||
| &Instruction::RetryMeElse(o)
|
||||
| &Instruction::DynamicElse(.., NextOrFail::Next(o))
|
||||
| &Instruction::DynamicInternalElse(.., NextOrFail::Next(o)) => offset += o,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
@@ -753,14 +797,16 @@ impl Machine {
|
||||
match &indexing_lines[self.machine_st.oip as usize] {
|
||||
IndexingLine::IndexedChoice(indexed_choice) => {
|
||||
match &indexed_choice[(self.machine_st.iip + inner_offset) as usize] {
|
||||
&IndexedChoiceInstruction::Retry(o) => {
|
||||
&IndexedChoiceInstruction::Retry(o)
|
||||
| &IndexedChoiceInstruction::DefaultRetry(o) => {
|
||||
if self.next_clause_applicable(self.machine_st.p + o) {
|
||||
return Some(inner_offset);
|
||||
}
|
||||
|
||||
inner_offset += 1;
|
||||
}
|
||||
&IndexedChoiceInstruction::Trust(o) => {
|
||||
&IndexedChoiceInstruction::Trust(o)
|
||||
| &IndexedChoiceInstruction::DefaultTrust(o) => {
|
||||
return if self.next_clause_applicable(self.machine_st.p + o) {
|
||||
Some(inner_offset)
|
||||
} else {
|
||||
@@ -813,12 +859,13 @@ impl Machine {
|
||||
or_frame.prelude.tr = self.machine_st.tr;
|
||||
or_frame.prelude.h = self.machine_st.heap.len();
|
||||
or_frame.prelude.b0 = self.machine_st.b0;
|
||||
or_frame.prelude.attr_var_queue_len = self.machine_st.attr_var_init.attr_var_queue.len();
|
||||
or_frame.prelude.attr_var_queue_len =
|
||||
self.machine_st.attr_var_init.attr_var_queue.len();
|
||||
|
||||
self.machine_st.b = b;
|
||||
|
||||
for i in 0..n {
|
||||
or_frame[i] = self.machine_st.registers[i+1];
|
||||
or_frame[i] = self.machine_st.registers[i + 1];
|
||||
}
|
||||
|
||||
self.machine_st.hb = self.machine_st.heap.len();
|
||||
@@ -844,18 +891,19 @@ impl Machine {
|
||||
or_frame.prelude.tr = self.machine_st.tr;
|
||||
or_frame.prelude.h = self.machine_st.heap.len();
|
||||
or_frame.prelude.b0 = self.machine_st.b0;
|
||||
or_frame.prelude.attr_var_queue_len = self.machine_st.attr_var_init.attr_var_queue.len();
|
||||
or_frame.prelude.attr_var_queue_len =
|
||||
self.machine_st.attr_var_init.attr_var_queue.len();
|
||||
|
||||
self.machine_st.b = b;
|
||||
|
||||
for i in 0..n {
|
||||
or_frame[i] = self.machine_st.registers[i+1];
|
||||
or_frame[i] = self.machine_st.registers[i + 1];
|
||||
}
|
||||
|
||||
self.machine_st.hb = self.machine_st.heap.len();
|
||||
|
||||
self.machine_st.oip = 0;
|
||||
self.machine_st.iip = 0;
|
||||
// self.machine_st.oip = 0;
|
||||
// self.machine_st.iip = 0;
|
||||
}
|
||||
|
||||
self.machine_st.p += offset;
|
||||
@@ -912,7 +960,7 @@ impl Machine {
|
||||
let curr_tr = self.machine_st.tr;
|
||||
|
||||
for i in 0..n {
|
||||
self.machine_st.registers[i+1] = or_frame[i];
|
||||
self.machine_st.registers[i + 1] = or_frame[i];
|
||||
}
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
@@ -939,8 +987,22 @@ impl Machine {
|
||||
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
|
||||
self.machine_st.oip = 0;
|
||||
self.machine_st.iip = 0;
|
||||
// these registers don't need to be reset here and MUST
|
||||
// NOT be (nor in indexed_try! trust_epilogue is an
|
||||
// exception, see next paragraph)! oip could be reset
|
||||
// without any adverse effects but iip is needed by
|
||||
// get_clause_p to find the last executed clause/2 clause.
|
||||
|
||||
// trust_epilogue must reset these for the sake of
|
||||
// subsequent predicates beginning with
|
||||
// switch_to_term. get_clause_p copes by checking
|
||||
// self.machine_st.b > self.machine.e: if true, it is safe
|
||||
// to use self.machine_st.iip; if false, use the choice
|
||||
// point left at the top of the stack by '$clause'
|
||||
// (specifically its biip value).
|
||||
|
||||
// self.machine_st.oip = 0;
|
||||
// self.machine_st.iip = 0;
|
||||
} else {
|
||||
self.trust_epilogue(offset);
|
||||
}
|
||||
@@ -956,7 +1018,7 @@ impl Machine {
|
||||
let curr_tr = self.machine_st.tr;
|
||||
|
||||
for i in 0..n {
|
||||
self.machine_st.registers[i+1] = or_frame[i];
|
||||
self.machine_st.registers[i + 1] = or_frame[i];
|
||||
}
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
@@ -983,7 +1045,7 @@ impl Machine {
|
||||
self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len);
|
||||
|
||||
self.machine_st.hb = target_h;
|
||||
self.machine_st.p = self.machine_st.p + offset;
|
||||
self.machine_st.p += offset;
|
||||
|
||||
self.machine_st.stack.truncate(b);
|
||||
self.machine_st.heap.truncate(target_h);
|
||||
@@ -999,7 +1061,7 @@ impl Machine {
|
||||
let n = or_frame.prelude.num_cells;
|
||||
|
||||
for i in 0..n {
|
||||
self.machine_st.registers[i+1] = or_frame[i];
|
||||
self.machine_st.registers[i + 1] = or_frame[i];
|
||||
}
|
||||
|
||||
let old_tr = or_frame.prelude.tr;
|
||||
@@ -1038,15 +1100,17 @@ impl Machine {
|
||||
#[inline(always)]
|
||||
fn undefined_procedure(&mut self, name: Atom, arity: usize) -> CallResult {
|
||||
match self.machine_st.flags.unknown {
|
||||
Unknown::Error => {
|
||||
Err(self.machine_st.throw_undefined_error(name, arity))
|
||||
}
|
||||
Unknown::Error => Err(self.machine_st.throw_undefined_error(name, arity)),
|
||||
Unknown::Fail => {
|
||||
self.machine_st.fail = true;
|
||||
Ok(())
|
||||
}
|
||||
Unknown::Warn => {
|
||||
println!("warning: predicate {}/{} is undefined", name.as_str(), arity);
|
||||
println!(
|
||||
"% Warning: predicate {}/{} is undefined",
|
||||
name.as_str(),
|
||||
arity
|
||||
);
|
||||
self.machine_st.fail = true;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1091,9 +1155,7 @@ impl Machine {
|
||||
self.machine_st.dynamic_mode = FirstOrNext::First;
|
||||
self.machine_st.execute_at_index(arity, compiled_tl_index);
|
||||
}
|
||||
IndexPtrTag::Index => {
|
||||
self.machine_st.execute_at_index(arity, compiled_tl_index)
|
||||
}
|
||||
IndexPtrTag::Index => self.machine_st.execute_at_index(arity, compiled_tl_index),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1109,19 +1171,23 @@ impl Machine {
|
||||
} else {
|
||||
Err(self.machine_st.throw_undefined_error(name, arity))
|
||||
}
|
||||
} else {
|
||||
if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_call(name, arity, idx.get())
|
||||
} else {
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_call(name, arity, idx.get())
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
let err = self.machine_st.module_resolution_error(module_name, name, arity);
|
||||
|
||||
Err(self.machine_st.error_form(err, stub))
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
let err = self
|
||||
.machine_st
|
||||
.existence_error(ExistenceError::QualifiedProcedure {
|
||||
module_name,
|
||||
name,
|
||||
arity,
|
||||
});
|
||||
|
||||
Err(self.machine_st.error_form(err, stub))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1135,19 +1201,23 @@ impl Machine {
|
||||
} else {
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else {
|
||||
if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_execute(name, arity, idx.get())
|
||||
} else {
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else if let Some(module) = self.indices.modules.get(&module_name) {
|
||||
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_execute(name, arity, idx.get())
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
let err = self.machine_st.module_resolution_error(module_name, name, arity);
|
||||
|
||||
Err(self.machine_st.error_form(err, stub))
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
} else {
|
||||
let stub = functor_stub(name, arity);
|
||||
let err = self
|
||||
.machine_st
|
||||
.existence_error(ExistenceError::QualifiedProcedure {
|
||||
module_name,
|
||||
name,
|
||||
arity,
|
||||
});
|
||||
|
||||
Err(self.machine_st.error_form(err, stub))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1178,12 +1248,16 @@ impl Machine {
|
||||
let r_c_wo_h_atom = atom!("run_cleaners_without_handling");
|
||||
let iso_ext = atom!("iso_ext");
|
||||
|
||||
RCWH = self.indices.get_predicate_code_index(r_c_w_h_atom, 0, iso_ext)
|
||||
.and_then(|item| item.local())
|
||||
.unwrap();
|
||||
RCWOH = self.indices.get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext)
|
||||
.and_then(|item| item.local())
|
||||
.unwrap();
|
||||
RCWH = self
|
||||
.indices
|
||||
.get_predicate_code_index(r_c_w_h_atom, 0, iso_ext)
|
||||
.and_then(|item| item.local())
|
||||
.unwrap();
|
||||
RCWOH = self
|
||||
.indices
|
||||
.get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext)
|
||||
.and_then(|item| item.local())
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
(RCWH, RCWOH)
|
||||
@@ -1194,9 +1268,8 @@ impl Machine {
|
||||
let (idx, arity) = if self.machine_st.effective_block() > prev_block {
|
||||
(r_c_w_h, 0)
|
||||
} else {
|
||||
self.machine_st.registers[1] = fixnum_as_cell!(
|
||||
Fixnum::build_with(b_cutoff as i64)
|
||||
);
|
||||
self.machine_st.registers[1] =
|
||||
fixnum_as_cell!(Fixnum::build_with(b_cutoff as i64));
|
||||
|
||||
(r_c_wo_h, 1)
|
||||
};
|
||||
@@ -1263,9 +1336,8 @@ impl Machine {
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
TrailEntryTag::TrailedAttachedValue => {
|
||||
}
|
||||
TrailEntryTag::TrailedAttachedValue => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
353
src/machine/parsed_results.rs
Normal file
353
src/machine/parsed_results.rs
Normal file
@@ -0,0 +1,353 @@
|
||||
use crate::atom_table::*;
|
||||
use dashu::*;
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub type QueryResult = Result<QueryResolution, String>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum QueryResolution {
|
||||
True,
|
||||
False,
|
||||
Matches(Vec<QueryMatch>),
|
||||
}
|
||||
|
||||
pub fn prolog_value_to_json_string(value: Value) -> String {
|
||||
match value {
|
||||
Value::Integer(i) => format!("{}", i),
|
||||
Value::Float(f) => format!("{}", f),
|
||||
Value::Rational(r) => format!("{}", r),
|
||||
Value::Atom(a) => format!("{}", a.as_str()),
|
||||
Value::String(s) => {
|
||||
if let Err(_e) = serde_json::from_str::<serde_json::Value>(s.as_str()) {
|
||||
//treat as string literal
|
||||
//escape double quotes
|
||||
format!(
|
||||
"\"{}\"",
|
||||
s.replace('\"', "\\\"")
|
||||
.replace('\n', "\\n")
|
||||
.replace('\t', "\\t")
|
||||
.replace('\r', "\\r")
|
||||
)
|
||||
} else {
|
||||
//return valid json string
|
||||
s
|
||||
}
|
||||
}
|
||||
Value::List(l) => {
|
||||
let mut string_result = "[".to_string();
|
||||
for (i, v) in l.iter().enumerate() {
|
||||
if i > 0 {
|
||||
string_result.push(',');
|
||||
}
|
||||
string_result.push_str(&prolog_value_to_json_string(v.clone()));
|
||||
}
|
||||
string_result.push(']');
|
||||
string_result
|
||||
}
|
||||
Value::Structure(s, l) => {
|
||||
let mut string_result = format!("\"{}\":[", s.as_str());
|
||||
for (i, v) in l.iter().enumerate() {
|
||||
if i > 0 {
|
||||
string_result.push(',');
|
||||
}
|
||||
string_result.push_str(&prolog_value_to_json_string(v.clone()));
|
||||
}
|
||||
string_result.push(']');
|
||||
string_result
|
||||
}
|
||||
_ => "null".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn prolog_match_to_json_string(query_match: &QueryMatch) -> String {
|
||||
let mut string_result = "{".to_string();
|
||||
for (i, (k, v)) in query_match.bindings.iter().enumerate() {
|
||||
if i > 0 {
|
||||
string_result.push(',');
|
||||
}
|
||||
string_result.push_str(&format!(
|
||||
"\"{}\":{}",
|
||||
k,
|
||||
prolog_value_to_json_string(v.clone())
|
||||
));
|
||||
}
|
||||
string_result.push('}');
|
||||
string_result
|
||||
}
|
||||
|
||||
impl ToString for QueryResolution {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
QueryResolution::True => "true".to_string(),
|
||||
QueryResolution::False => "false".to_string(),
|
||||
QueryResolution::Matches(matches) => {
|
||||
let matches_json: Vec<String> =
|
||||
matches.iter().map(prolog_match_to_json_string).collect();
|
||||
format!("[{}]", matches_json.join(","))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct QueryMatch {
|
||||
pub bindings: BTreeMap<String, Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum QueryResolutionLine {
|
||||
True,
|
||||
False,
|
||||
Match(BTreeMap<String, Value>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Value {
|
||||
Integer(Integer),
|
||||
Rational(Rational),
|
||||
Float(OrderedFloat<f64>),
|
||||
Atom(Atom),
|
||||
String(String),
|
||||
List(Vec<Value>),
|
||||
Structure(Atom, Vec<Value>),
|
||||
Var,
|
||||
}
|
||||
|
||||
impl From<BTreeMap<&str, Value>> for QueryMatch {
|
||||
fn from(bindings: BTreeMap<&str, Value>) -> Self {
|
||||
QueryMatch {
|
||||
bindings: bindings
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_string(), v))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BTreeMap<String, Value>> for QueryMatch {
|
||||
fn from(bindings: BTreeMap<String, Value>) -> Self {
|
||||
QueryMatch { bindings }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<QueryResolutionLine>> for QueryResolution {
|
||||
fn from(query_result_lines: Vec<QueryResolutionLine>) -> Self {
|
||||
// If there is only one line, and it is true or false, return that.
|
||||
if query_result_lines.len() == 1 {
|
||||
match query_result_lines[0].clone() {
|
||||
QueryResolutionLine::True => return QueryResolution::True,
|
||||
QueryResolutionLine::False => return QueryResolution::False,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is only one line, and it is an empty match, return false.
|
||||
if query_result_lines.len() == 1 {
|
||||
if let QueryResolutionLine::Match(m) = query_result_lines[0].clone() {
|
||||
if m.is_empty() {
|
||||
return QueryResolution::False;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is at least one line with true and no matches, return true.
|
||||
if query_result_lines
|
||||
.iter()
|
||||
.any(|l| l == &QueryResolutionLine::True)
|
||||
&& !query_result_lines
|
||||
.iter()
|
||||
.any(|l| matches!(l, QueryResolutionLine::Match(_)))
|
||||
{
|
||||
return QueryResolution::True;
|
||||
}
|
||||
|
||||
// If there is at least one match, return all matches.
|
||||
let all_matches = query_result_lines
|
||||
.into_iter()
|
||||
.filter(|l| matches!(l, QueryResolutionLine::Match(_)))
|
||||
.map(|l| match l {
|
||||
QueryResolutionLine::Match(m) => QueryMatch::from(m),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !all_matches.is_empty() {
|
||||
return QueryResolution::Matches(all_matches);
|
||||
}
|
||||
|
||||
QueryResolution::False
|
||||
}
|
||||
}
|
||||
|
||||
fn split_response_string(input: &str) -> Vec<String> {
|
||||
let mut level_bracket = 0;
|
||||
let mut level_parenthesis = 0;
|
||||
let mut in_double_quotes = false;
|
||||
let mut in_single_quotes = false;
|
||||
let mut start = 0;
|
||||
let mut result = Vec::new();
|
||||
|
||||
for (i, c) in input.chars().enumerate() {
|
||||
match c {
|
||||
'[' => level_bracket += 1,
|
||||
']' => level_bracket -= 1,
|
||||
'(' => level_parenthesis += 1,
|
||||
')' => level_parenthesis -= 1,
|
||||
'"' => in_double_quotes = !in_double_quotes,
|
||||
'\'' => in_single_quotes = !in_single_quotes,
|
||||
',' if level_bracket == 0
|
||||
&& level_parenthesis == 0
|
||||
&& !in_double_quotes
|
||||
&& !in_single_quotes =>
|
||||
{
|
||||
result.push(input[start..i].trim().to_string());
|
||||
start = i + 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
result.push(input[start..].trim().to_string());
|
||||
result
|
||||
}
|
||||
|
||||
fn split_key_value_pairs(input: &str) -> Vec<(String, String)> {
|
||||
let items = split_response_string(input);
|
||||
let mut result = Vec::new();
|
||||
|
||||
for item in items {
|
||||
let parts: Vec<&str> = item.splitn(2, '=').collect();
|
||||
if parts.len() == 2 {
|
||||
let key = parts[0].trim().to_string();
|
||||
let value = parts[1].trim().to_string();
|
||||
result.push((key, value));
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn parse_prolog_response(input: &str) -> HashMap<String, String> {
|
||||
let mut map: HashMap<String, String> = HashMap::new();
|
||||
// Use regex to match strings including commas inside them
|
||||
for result in split_key_value_pairs(input) {
|
||||
let key = result.0;
|
||||
let value = result.1;
|
||||
// cut off at given characters/strings:
|
||||
let value = value.split('\n').next().unwrap().to_string();
|
||||
let value = value.split(' ').next().unwrap().to_string();
|
||||
let value = value.split('\t').next().unwrap().to_string();
|
||||
let value = value.split("error").next().unwrap().to_string();
|
||||
map.insert(key, value);
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
impl TryFrom<String> for QueryResolutionLine {
|
||||
type Error = ();
|
||||
fn try_from(string: String) -> Result<Self, Self::Error> {
|
||||
match string.as_str() {
|
||||
"true" => Ok(QueryResolutionLine::True),
|
||||
"false" => Ok(QueryResolutionLine::False),
|
||||
_ => Ok(QueryResolutionLine::Match(
|
||||
parse_prolog_response(&string)
|
||||
.iter()
|
||||
.map(|(k, v)| -> Result<(String, Value), ()> {
|
||||
let key = k.to_string();
|
||||
let value = v.to_string();
|
||||
Ok((key, Value::try_from(value)?))
|
||||
})
|
||||
.filter_map(Result::ok)
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn split_nested_list(input: &str) -> Vec<String> {
|
||||
let mut level = 0;
|
||||
let mut start = 0;
|
||||
let mut result = Vec::new();
|
||||
|
||||
for (i, c) in input.chars().enumerate() {
|
||||
match c {
|
||||
'[' => level += 1,
|
||||
']' => level -= 1,
|
||||
',' if level == 0 => {
|
||||
result.push(input[start..i].trim().to_string());
|
||||
start = i + 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
result.push(input[start..].trim().to_string());
|
||||
result
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Value {
|
||||
type Error = ();
|
||||
fn try_from(string: String) -> Result<Self, Self::Error> {
|
||||
let trimmed = string.trim();
|
||||
|
||||
if let Ok(float_value) = string.parse::<f64>() {
|
||||
Ok(Value::Float(OrderedFloat(float_value)))
|
||||
} else if let Ok(int_value) = string.parse::<i128>() {
|
||||
Ok(Value::Integer(int_value.into()))
|
||||
} else if trimmed.starts_with('\'') && trimmed.ends_with('\'')
|
||||
|| trimmed.starts_with('"') && trimmed.ends_with('"')
|
||||
{
|
||||
Ok(Value::String(trimmed[1..trimmed.len() - 1].into()))
|
||||
} else if trimmed.starts_with('[') && trimmed.ends_with(']') {
|
||||
let split = split_nested_list(&trimmed[1..trimmed.len() - 1]);
|
||||
|
||||
let values = split
|
||||
.into_iter()
|
||||
.map(Value::try_from)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(Value::List(values))
|
||||
} else if trimmed.starts_with('{') && trimmed.ends_with('}') {
|
||||
let iter = trimmed[1..trimmed.len() - 1].split(',');
|
||||
let mut values = vec![];
|
||||
|
||||
for value in iter {
|
||||
let items: Vec<_> = value.split(':').collect();
|
||||
if items.len() == 2 {
|
||||
let _key = items[0].to_string();
|
||||
let value = items[1].to_string();
|
||||
values.push(Value::try_from(value)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Structure(atom!("{}"), values))
|
||||
} else if trimmed.starts_with("<<") && trimmed.ends_with(">>") {
|
||||
let iter = trimmed[2..trimmed.len() - 2].split(',');
|
||||
let mut values = vec![];
|
||||
|
||||
for value in iter {
|
||||
let items: Vec<_> = value.split(':').collect();
|
||||
if items.len() == 2 {
|
||||
let _key = items[0].to_string();
|
||||
let value = items[1].to_string();
|
||||
values.push(Value::try_from(value)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Structure(atom!("<<>>"), values))
|
||||
} else if !trimmed.contains(',') && !trimmed.contains('\'') && !trimmed.contains('"') {
|
||||
Ok(Value::String(trimmed.into()))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Value {
|
||||
fn from(str: &str) -> Self {
|
||||
Value::String(str.to_string())
|
||||
}
|
||||
}
|
||||
@@ -34,20 +34,19 @@ impl From<Atom> for PartialString {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Atom> for PartialString {
|
||||
impl From<PartialString> for Atom {
|
||||
#[inline]
|
||||
fn into(self: Self) -> Atom {
|
||||
self.0
|
||||
fn from(val: PartialString) -> Self {
|
||||
val.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialString {
|
||||
#[inline]
|
||||
pub(super) fn new<'a>(src: &'a str, atom_tbl: &mut AtomTable) -> Option<(Self, &'a str)> {
|
||||
pub(super) fn new<'a>(src: &'a str, atom_tbl: &AtomTable) -> Option<(Self, &'a str)> {
|
||||
let terminator_idx = scan_for_terminator(src.chars());
|
||||
let pstr = PartialString(atom_tbl.build_with(&src[.. terminator_idx]));
|
||||
|
||||
Some(if terminator_idx < src.as_bytes().len() {
|
||||
let pstr = PartialString(AtomTable::build_with(atom_tbl, &src[..terminator_idx]));
|
||||
Some(if terminator_idx < src.as_bytes().len() {
|
||||
(pstr, &src[terminator_idx + 1..])
|
||||
} else {
|
||||
(pstr, "")
|
||||
@@ -55,8 +54,8 @@ impl PartialString {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn as_str_from(&self, n: usize) -> &str {
|
||||
&self.0.as_str()[n..]
|
||||
pub(crate) fn as_str_from(&self, n: usize) -> AtomString {
|
||||
self.0.as_str().map(|str| &str[n..])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +68,7 @@ pub struct HeapPStrIter<'a> {
|
||||
stepper: fn(&mut HeapPStrIter<'a>) -> Option<PStrIteratee>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PStrPrefixCmpResult {
|
||||
pub focus: usize,
|
||||
pub offset: usize,
|
||||
@@ -124,11 +123,15 @@ impl<'a> HeapPStrIter<'a> {
|
||||
|
||||
let mut final_result = None;
|
||||
|
||||
while let Some(PStrIterStep { iteratee, next_hare }) = self.step(self.brent_st.hare) {
|
||||
while let Some(PStrIterStep {
|
||||
iteratee,
|
||||
next_hare,
|
||||
}) = self.step(self.brent_st.hare)
|
||||
{
|
||||
self.brent_st.hare = next_hare;
|
||||
self.focus = self.heap[iteratee.focus()];
|
||||
|
||||
result.focus = iteratee.focus();
|
||||
result.focus = iteratee.focus();
|
||||
result.offset = iteratee.offset();
|
||||
|
||||
match iteratee {
|
||||
@@ -151,13 +154,13 @@ impl<'a> HeapPStrIter<'a> {
|
||||
let s = &s[result.prefix_len..];
|
||||
|
||||
if s.len() >= t.len() {
|
||||
if s.starts_with(t) {
|
||||
if s.starts_with(&*t) {
|
||||
result.prefix_len += t.len();
|
||||
result.offset += t.len();
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else if t.starts_with(&s) {
|
||||
} else if t.starts_with(s) {
|
||||
result.prefix_len += s.len();
|
||||
result.offset += s.len();
|
||||
|
||||
@@ -190,7 +193,7 @@ impl<'a> HeapPStrIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
final_result
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn walk_hare_to_cycle_end(&mut self) {
|
||||
@@ -202,7 +205,7 @@ impl<'a> HeapPStrIter<'a> {
|
||||
self.brent_st.hare = self.orig_focus;
|
||||
self.brent_st.tortoise = self.orig_focus;
|
||||
|
||||
for _ in 0 .. self.brent_st.lam {
|
||||
for _ in 0..self.brent_st.lam {
|
||||
self.brent_st.hare = self.step(self.brent_st.hare).unwrap().next_hare;
|
||||
}
|
||||
|
||||
@@ -215,17 +218,18 @@ impl<'a> HeapPStrIter<'a> {
|
||||
self.brent_st.hare = orig_hare;
|
||||
}
|
||||
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
pub fn to_string(&mut self) -> String {
|
||||
let mut buf = String::with_capacity(32);
|
||||
|
||||
while let Some(iteratee) = self.next() {
|
||||
for iteratee in self.by_ref() {
|
||||
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 += &*pstr.as_str_from(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,43 +242,43 @@ impl<'a> HeapPStrIter<'a> {
|
||||
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) => {
|
||||
let value = self.heap[h];
|
||||
let value = heap_bound_store(
|
||||
self.heap,
|
||||
heap_bound_deref(self.heap, value),
|
||||
);
|
||||
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) => {
|
||||
let value = self.heap[h];
|
||||
let value = heap_bound_store(
|
||||
self.heap,
|
||||
heap_bound_deref(self.heap, value),
|
||||
);
|
||||
|
||||
return read_heap_cell!(value,
|
||||
(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;
|
||||
}
|
||||
return read_heap_cell!(value,
|
||||
(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;
|
||||
}
|
||||
);
|
||||
focus = self.heap[h];
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,14 +335,10 @@ impl<'a> HeapPStrIter<'a> {
|
||||
heap_bound_deref(self.heap, self.heap[h]),
|
||||
);
|
||||
|
||||
return if let Some(c) = value.as_char() {
|
||||
Some(PStrIterStep {
|
||||
iteratee: PStrIteratee::Char(curr_hare, c),
|
||||
next_hare: h+1,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return value.as_char().map(|c| PStrIterStep {
|
||||
iteratee: PStrIteratee::Char(curr_hare, c),
|
||||
next_hare: h+1,
|
||||
});
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.heap[s])
|
||||
@@ -350,14 +350,10 @@ impl<'a> HeapPStrIter<'a> {
|
||||
heap_bound_deref(self.heap, self.heap[s+1]),
|
||||
);
|
||||
|
||||
if let Some(c) = value.as_char() {
|
||||
Some(PStrIterStep {
|
||||
iteratee: PStrIteratee::Char(curr_hare, c),
|
||||
next_hare: s+2,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
value.as_char().map(|c| PStrIterStep {
|
||||
iteratee: PStrIteratee::Char(curr_hare, c),
|
||||
next_hare: s+2,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -381,13 +377,15 @@ impl<'a> HeapPStrIter<'a> {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
let PStrIterStep {
|
||||
iteratee,
|
||||
next_hare,
|
||||
} = match self.step(self.brent_st.hare) {
|
||||
Some(results) => results,
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
self.focus = self.heap[iteratee.focus()];
|
||||
|
||||
@@ -400,10 +398,7 @@ impl<'a> HeapPStrIter<'a> {
|
||||
|
||||
match self.brent_st.step(next_hare) {
|
||||
Some(cycle_result) => {
|
||||
debug_assert!(match cycle_result {
|
||||
CycleSearchResult::Cyclic(..) => true,
|
||||
_ => false,
|
||||
});
|
||||
debug_assert!(matches!(cycle_result, CycleSearchResult::Cyclic(..)));
|
||||
|
||||
self.walk_hare_to_cycle_end();
|
||||
self.stepper = HeapPStrIter::post_cycle_discovery_stepper;
|
||||
@@ -421,13 +416,15 @@ impl<'a> HeapPStrIter<'a> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let PStrIterStep { iteratee, next_hare } =
|
||||
match self.step(self.brent_st.hare) {
|
||||
Some(results) => results,
|
||||
None => {
|
||||
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;
|
||||
@@ -515,11 +512,8 @@ impl<'a> Iterator for PStrCharsIter<'a> {
|
||||
|
||||
match pstr.as_str_from(n).chars().next() {
|
||||
Some(c) => {
|
||||
self.item = Some(PStrIteratee::PStrSegment(
|
||||
f1,
|
||||
pstr_atom,
|
||||
n + c.len_utf8(),
|
||||
));
|
||||
self.item =
|
||||
Some(PStrIteratee::PStrSegment(f1, pstr_atom, n + c.len_utf8()));
|
||||
|
||||
return Some(c);
|
||||
}
|
||||
@@ -546,11 +540,7 @@ pub enum PStrCmpResult {
|
||||
impl PStrCmpResult {
|
||||
#[inline]
|
||||
pub fn is_second_iter(&self) -> bool {
|
||||
if let PStrCmpResult::SecondIterContinuable(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, PStrCmpResult::SecondIterContinuable(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,8 +586,8 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
return PStrCmpResult::Ordered(c1.cmp(&c2));
|
||||
}
|
||||
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -619,15 +609,15 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
if n1 < pstr_atom.len() {
|
||||
step_2.iteratee = PStrIteratee::PStrSegment(f2, pstr_atom, n1);
|
||||
|
||||
let c1_result = cycle_detection_step(i1, i2, &step_1);
|
||||
let c1_result = cycle_detection_step(i1, i2, step_1);
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
|
||||
if !c1_result {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -637,7 +627,7 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let c2_result = cycle_detection_step(i2, i1, &step_2);
|
||||
let c2_result = cycle_detection_step(i2, i1, step_2);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
|
||||
if !c2_result {
|
||||
@@ -658,15 +648,15 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
if n1 < pstr_atom.len() {
|
||||
step_1.iteratee = PStrIteratee::PStrSegment(f1, pstr_atom, n1);
|
||||
|
||||
let c2_result = cycle_detection_step(i2, i1, &step_2);
|
||||
let c2_result = cycle_detection_step(i2, i1, step_2);
|
||||
r2 = step(i2, step_2.next_hare);
|
||||
|
||||
if !c2_result {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -676,7 +666,7 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let c1_result = cycle_detection_step(i1, i2, &step_1);
|
||||
let c1_result = cycle_detection_step(i1, i2, step_1);
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
|
||||
if !c1_result {
|
||||
@@ -684,11 +674,13 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
}
|
||||
}
|
||||
}
|
||||
(PStrIteratee::PStrSegment(f1, pstr1_atom, n1),
|
||||
PStrIteratee::PStrSegment(f2, pstr2_atom, n2)) => {
|
||||
(
|
||||
PStrIteratee::PStrSegment(f1, pstr1_atom, n1),
|
||||
PStrIteratee::PStrSegment(f2, pstr2_atom, n2),
|
||||
) => {
|
||||
if pstr1_atom == pstr2_atom && n1 == n2 {
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -707,9 +699,9 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
let str2 = pstr2.as_str_from(n2);
|
||||
|
||||
match str1.len().cmp(&str2.len()) {
|
||||
Ordering::Equal if str1 == str2 => {
|
||||
cycle_detection_step(i1, i2, &step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, &step_2);
|
||||
Ordering::Equal if *str1 == *str2 => {
|
||||
cycle_detection_step(i1, i2, step_1);
|
||||
let both_cyclic = cycle_detection_step(i2, i1, step_2);
|
||||
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
@@ -718,18 +710,20 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Ordering::Less if str2.starts_with(str1) => {
|
||||
step_2.iteratee = PStrIteratee::PStrSegment(f2, pstr2_atom, n2 + str1.len());
|
||||
let c1_result = cycle_detection_step(i1, i2, &step_1);
|
||||
Ordering::Less if str2.starts_with(&*str1) => {
|
||||
step_2.iteratee =
|
||||
PStrIteratee::PStrSegment(f2, pstr2_atom, n2 + str1.len());
|
||||
let c1_result = cycle_detection_step(i1, i2, step_1);
|
||||
r1 = step(i1, i1.brent_st.hare);
|
||||
|
||||
if !c1_result {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Ordering::Greater if str1.starts_with(str2) => {
|
||||
step_1.iteratee = PStrIteratee::PStrSegment(f1, pstr1_atom, n1 + str2.len());
|
||||
let c2_result = cycle_detection_step(i2, i1, &step_2);
|
||||
Ordering::Greater if str1.starts_with(&*str2) => {
|
||||
step_1.iteratee =
|
||||
PStrIteratee::PStrSegment(f1, pstr1_atom, n1 + str2.len());
|
||||
let c2_result = cycle_detection_step(i2, i1, step_2);
|
||||
r2 = step(i2, i2.brent_st.hare);
|
||||
|
||||
if !c2_result {
|
||||
@@ -737,7 +731,7 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return PStrCmpResult::Ordered(str1.cmp(str2));
|
||||
return PStrCmpResult::Ordered(str1.cmp(&*str2));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -770,20 +764,34 @@ pub fn compare_pstr_prefixes<'a>(
|
||||
if i1.focus == empty_list_as_cell!() {
|
||||
PStrCmpResult::Ordered(Ordering::Less)
|
||||
} else {
|
||||
PStrCmpResult::SecondIterContinuable(r2.unwrap().iteratee)
|
||||
let r2_step = r2.unwrap();
|
||||
|
||||
// advance i2 to the next character so the same character
|
||||
// isn't repeated
|
||||
if matches!(r2_step.iteratee, PStrIteratee::Char(..)) {
|
||||
cycle_detection_step(i2, i1, &r2_step);
|
||||
}
|
||||
|
||||
PStrCmpResult::SecondIterContinuable(r2_step.iteratee)
|
||||
}
|
||||
} else if r2_at_end {
|
||||
if i2.focus == empty_list_as_cell!() {
|
||||
PStrCmpResult::Ordered(Ordering::Greater)
|
||||
} else {
|
||||
PStrCmpResult::FirstIterContinuable(r1.unwrap().iteratee)
|
||||
let r1_step = r1.unwrap();
|
||||
|
||||
// advance i1 to the next character so the same character
|
||||
// isn't repeated
|
||||
if matches!(r1_step.iteratee, PStrIteratee::Char(..)) {
|
||||
cycle_detection_step(i1, i2, &r1_step);
|
||||
}
|
||||
|
||||
PStrCmpResult::FirstIterContinuable(r1_step.iteratee)
|
||||
}
|
||||
} else if i1.is_continuable() && i2.is_continuable() {
|
||||
PStrCmpResult::Ordered(Ordering::Equal)
|
||||
} else {
|
||||
if i1.is_continuable() && i2.is_continuable() {
|
||||
PStrCmpResult::Ordered(Ordering::Equal)
|
||||
} else {
|
||||
PStrCmpResult::Unordered
|
||||
}
|
||||
PStrCmpResult::Unordered
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,14 +801,12 @@ mod test {
|
||||
use crate::machine::mock_wam::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
||||
fn pstr_iter_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
let pstr_var_cell = put_partial_string(
|
||||
&mut wam.machine_st.heap,
|
||||
"abc ",
|
||||
&mut wam.machine_st.atom_tbl,
|
||||
);
|
||||
let pstr_var_cell =
|
||||
put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl);
|
||||
|
||||
let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
|
||||
|
||||
@@ -819,11 +825,8 @@ mod test {
|
||||
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_var_cell =
|
||||
put_partial_string(&mut wam.machine_st.heap, "def", &wam.machine_st.atom_tbl);
|
||||
|
||||
let pstr_second_cell = wam.machine_st.heap[pstr_second_var_cell.get_value() as usize];
|
||||
|
||||
@@ -836,7 +839,11 @@ mod test {
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(PStrIteratee::PStrSegment(2, cell_as_atom!(pstr_second_cell), 0))
|
||||
Some(PStrIteratee::PStrSegment(
|
||||
2,
|
||||
cell_as_atom!(pstr_second_cell),
|
||||
0
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(iter.next(), None);
|
||||
@@ -855,7 +862,11 @@ mod test {
|
||||
);
|
||||
assert_eq!(
|
||||
iter.next(),
|
||||
Some(PStrIteratee::PStrSegment(2, cell_as_atom!(pstr_second_cell), 0))
|
||||
Some(PStrIteratee::PStrSegment(
|
||||
2,
|
||||
cell_as_atom!(pstr_second_cell),
|
||||
0
|
||||
))
|
||||
);
|
||||
|
||||
assert_eq!(iter.next(), None);
|
||||
@@ -863,15 +874,19 @@ mod test {
|
||||
}
|
||||
|
||||
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_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)));
|
||||
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() {}
|
||||
for _ in iter.by_ref() {}
|
||||
|
||||
assert!(!iter.at_string_terminator());
|
||||
}
|
||||
@@ -892,31 +907,25 @@ mod test {
|
||||
// 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,
|
||||
);
|
||||
put_partial_string(&mut wam.machine_st.heap, "ab", &wam.machine_st.atom_tbl);
|
||||
|
||||
wam.machine_st.heap.pop();
|
||||
|
||||
wam.machine_st.heap.push(pstr_loc_as_cell!(second_h+2));
|
||||
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,
|
||||
);
|
||||
put_partial_string(&mut wam.machine_st.heap, "c ", &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_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_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)));
|
||||
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);
|
||||
@@ -929,11 +938,7 @@ mod test {
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
put_partial_string(
|
||||
&mut wam.machine_st.heap,
|
||||
"abc ",
|
||||
&mut wam.machine_st.atom_tbl,
|
||||
);
|
||||
put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl);
|
||||
|
||||
let pstr_cell = wam.machine_st.heap[0];
|
||||
|
||||
@@ -963,11 +968,8 @@ mod test {
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
let cstr_var_cell = put_complete_string(
|
||||
&mut wam.machine_st.heap,
|
||||
"abc",
|
||||
&mut wam.machine_st.atom_tbl,
|
||||
);
|
||||
let cstr_var_cell =
|
||||
put_complete_string(&mut wam.machine_st.heap, "abc", &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));
|
||||
@@ -982,30 +984,18 @@ mod test {
|
||||
|
||||
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[2], char_as_cell!('a'),);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[4],
|
||||
char_as_cell!('b'),
|
||||
);
|
||||
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[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,
|
||||
);
|
||||
let cstr_var_cell =
|
||||
put_complete_string(&mut wam.machine_st.heap, "abc", &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
|
||||
@@ -1020,37 +1010,22 @@ mod test {
|
||||
|
||||
unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
|
||||
|
||||
assert_eq!(wam.machine_st.fail, false);
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[2],
|
||||
char_as_cell!('a'),
|
||||
);
|
||||
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[4], char_as_cell!('b'),);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[6],
|
||||
char_as_cell!('c'),
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[6], char_as_cell!('c'),);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[7],
|
||||
empty_list_as_cell!(),
|
||||
);
|
||||
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,
|
||||
);
|
||||
let cstr_var_cell =
|
||||
put_complete_string(&mut wam.machine_st.heap, "d", &wam.machine_st.atom_tbl);
|
||||
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(2));
|
||||
wam.machine_st.heap.push(char_as_cell!('d'));
|
||||
@@ -1058,17 +1033,14 @@ mod test {
|
||||
|
||||
unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
|
||||
|
||||
assert_eq!(wam.machine_st.fail, false);
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// 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,
|
||||
);
|
||||
let cstr_var_cell =
|
||||
put_complete_string(&mut wam.machine_st.heap, "abc", &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));
|
||||
@@ -1083,32 +1055,19 @@ mod test {
|
||||
|
||||
unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
|
||||
|
||||
assert_eq!(wam.machine_st.fail, false);
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[2],
|
||||
char_as_cell!('a'),
|
||||
);
|
||||
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[4], char_as_cell!('b'),);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[6],
|
||||
char_as_cell!('c'),
|
||||
);
|
||||
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,
|
||||
);
|
||||
put_complete_string(&mut wam.machine_st.heap, "abcdef", &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));
|
||||
@@ -1117,13 +1076,16 @@ mod test {
|
||||
|
||||
print_heap_terms(wam.machine_st.heap.iter(), 0);
|
||||
|
||||
assert_eq!(wam.machine_st.fail, false);
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
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)));
|
||||
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.
|
||||
|
||||
@@ -1132,7 +1094,9 @@ mod test {
|
||||
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)));
|
||||
wam.machine_st
|
||||
.heap
|
||||
.push(fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
|
||||
{
|
||||
let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 2);
|
||||
@@ -1142,9 +1106,119 @@ mod test {
|
||||
Some(PStrIteratee::PStrSegment(2, atom!("abc"), 1))
|
||||
);
|
||||
|
||||
// assert!(iter.next().is_none());
|
||||
|
||||
while let Some(_) = iter.next() {}
|
||||
for _ in iter {}
|
||||
}
|
||||
|
||||
// #2293, test1.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("a ")));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(1));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test2.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a")));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test3.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("a b")));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(1));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test4.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a ")));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test5.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a bc")));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(char_as_cell!(' '));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(6));
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test6.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("abc")));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(1));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(char_as_cell!('b'));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
|
||||
// #2293, test7.
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("abcde")));
|
||||
wam.machine_st.heap.push(char_as_cell!('a'));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(3));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(5));
|
||||
wam.machine_st.heap.push(char_as_cell!('c'));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(7));
|
||||
wam.machine_st.heap.push(heap_loc_as_cell!(7));
|
||||
wam.machine_st.heap.push(list_loc_as_cell!(9));
|
||||
wam.machine_st.heap.push(char_as_cell!('e'));
|
||||
wam.machine_st.heap.push(empty_list_as_cell!());
|
||||
|
||||
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
|
||||
|
||||
assert!(!wam.machine_st.fail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,7 @@ use indexmap::IndexSet;
|
||||
use std::cell::Cell;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub(crate) fn to_op_decl(
|
||||
prec: u16,
|
||||
spec: Atom,
|
||||
name: Atom,
|
||||
) -> Result<OpDecl, CompilationError> {
|
||||
pub(crate) fn to_op_decl(prec: u16, spec: Atom, name: Atom) -> Result<OpDecl, CompilationError> {
|
||||
match spec {
|
||||
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)),
|
||||
@@ -29,19 +25,16 @@ pub(crate) fn to_op_decl(
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_op_decl(
|
||||
mut terms: Vec<Term>,
|
||||
atom_tbl: &mut AtomTable,
|
||||
) -> Result<OpDecl, CompilationError> {
|
||||
fn setup_op_decl(mut terms: Vec<Term>, atom_tbl: &AtomTable) -> Result<OpDecl, CompilationError> {
|
||||
let name = match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Atom(name)) => name,
|
||||
Term::Literal(_, Literal::Char(c)) => atom_tbl.build_with(&c.to_string()),
|
||||
Term::Literal(_, Literal::Char(c)) => AtomTable::build_with(atom_tbl, &c.to_string()),
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
};
|
||||
|
||||
let spec = match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Atom(name)) => name,
|
||||
Term::Literal(_, Literal::Char(c)) => atom_tbl.build_with(&c.to_string()),
|
||||
Term::Literal(_, Literal::Char(c)) => AtomTable::build_with(atom_tbl, &c.to_string()),
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
};
|
||||
|
||||
@@ -65,15 +58,17 @@ fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, Compilatio
|
||||
let name = terms.pop().unwrap();
|
||||
|
||||
let arity = match arity {
|
||||
Term::Literal(_, Literal::Integer(n)) => n.to_usize(),
|
||||
Term::Literal(_, Literal::Integer(n)) => (&*n).try_into().ok(),
|
||||
Term::Literal(_, Literal::Fixnum(n)) => usize::try_from(n.get_num()).ok(),
|
||||
_ => None,
|
||||
}.ok_or(CompilationError::InvalidModuleExport)?;
|
||||
}
|
||||
.ok_or(CompilationError::InvalidModuleExport)?;
|
||||
|
||||
let name = match name {
|
||||
Term::Literal(_, Literal::Atom(name)) => Some(name),
|
||||
_ => None,
|
||||
}.ok_or(CompilationError::InvalidModuleExport)?;
|
||||
}
|
||||
.ok_or(CompilationError::InvalidModuleExport)?;
|
||||
|
||||
if *slash == atom!("/") {
|
||||
Ok((name, arity))
|
||||
@@ -87,7 +82,7 @@ fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, Compilatio
|
||||
|
||||
fn setup_module_export(
|
||||
mut term: Term,
|
||||
atom_tbl: &mut AtomTable,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Result<ModuleExport, CompilationError> {
|
||||
setup_predicate_indicator(&mut term)
|
||||
.map(ModuleExport::PredicateKey)
|
||||
@@ -105,7 +100,7 @@ fn setup_module_export(
|
||||
}
|
||||
|
||||
pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term {
|
||||
let head_term = Term::Clause(Cell::default(), atom!(""), vars.iter().cloned().collect());
|
||||
let head_term = Term::Clause(Cell::default(), atom!(""), vars.to_vec());
|
||||
let rule = vec![head_term, body_term];
|
||||
|
||||
Term::Clause(Cell::default(), atom!(":-"), rule)
|
||||
@@ -113,7 +108,7 @@ pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term {
|
||||
|
||||
pub(super) fn setup_module_export_list(
|
||||
mut export_list: Term,
|
||||
atom_tbl: &mut AtomTable,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Result<Vec<ModuleExport>, CompilationError> {
|
||||
let mut exports = vec![];
|
||||
|
||||
@@ -133,7 +128,7 @@ pub(super) fn setup_module_export_list(
|
||||
|
||||
fn setup_module_decl(
|
||||
mut terms: Vec<Term>,
|
||||
atom_tbl: &mut AtomTable,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Result<ModuleDecl, CompilationError> {
|
||||
let export_list = terms.pop().unwrap();
|
||||
let name = terms.pop().unwrap();
|
||||
@@ -141,7 +136,8 @@ fn setup_module_decl(
|
||||
let name = match name {
|
||||
Term::Literal(_, Literal::Atom(name)) => Some(name),
|
||||
_ => None,
|
||||
}.ok_or(CompilationError::InvalidModuleDecl)?;
|
||||
}
|
||||
.ok_or(CompilationError::InvalidModuleDecl)?;
|
||||
|
||||
let exports = setup_module_export_list(export_list, atom_tbl)?;
|
||||
|
||||
@@ -150,9 +146,7 @@ fn setup_module_decl(
|
||||
|
||||
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 =>
|
||||
{
|
||||
Term::Clause(_, name, mut terms) if name == atom!("library") && terms.len() == 1 => {
|
||||
match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Atom(name)) => Ok(ModuleSource::Library(name)),
|
||||
_ => Err(CompilationError::InvalidModuleDecl),
|
||||
@@ -167,13 +161,11 @@ type UseModuleExport = (ModuleSource, IndexSet<ModuleExport>);
|
||||
|
||||
fn setup_qualified_import(
|
||||
mut terms: Vec<Term>,
|
||||
atom_tbl: &mut AtomTable,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Result<UseModuleExport, CompilationError> {
|
||||
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 =>
|
||||
{
|
||||
Term::Clause(_, name, mut terms) if name == atom!("library") && terms.len() == 1 => {
|
||||
match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Atom(name)) => Ok(ModuleSource::Library(name)),
|
||||
_ => Err(CompilationError::InvalidModuleDecl),
|
||||
@@ -246,7 +238,7 @@ fn setup_meta_predicate<'a, LS: LoadState<'a>>(
|
||||
) -> Result<(Atom, Vec<MetaSpec>), CompilationError> {
|
||||
let mut meta_specs = vec![];
|
||||
|
||||
for meta_spec in terms.into_iter() {
|
||||
for meta_spec in terms.iter_mut() {
|
||||
match meta_spec {
|
||||
Term::Literal(_, Literal::Atom(meta_spec)) => {
|
||||
let meta_spec = match meta_spec {
|
||||
@@ -381,19 +373,28 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>(
|
||||
}
|
||||
|
||||
fn tag_with_module_name(module_name: Atom, term: Term) -> Term {
|
||||
Term::Clause(Cell::default(), atom!(":"), vec![
|
||||
Term::Literal(Cell::default(), Literal::Atom(module_name)),
|
||||
term
|
||||
])
|
||||
Term::Clause(
|
||||
Cell::default(),
|
||||
atom!(":"),
|
||||
vec![
|
||||
Term::Literal(Cell::default(), Literal::Atom(module_name)),
|
||||
term,
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
let process_term: fn(Atom, Term) -> Term;
|
||||
|
||||
let (module_name, key, term) = match term {
|
||||
Term::Clause(cell, atom!(":"), mut terms) if terms.len() == 2 => {
|
||||
if let Some((module_name, name)) = get_qualified_name(&terms[0], &terms[1]) {
|
||||
if let Some((module_name, name)) = get_qualified_name(&terms[0], &terms[1])
|
||||
{
|
||||
process_term = tag_with_module_name;
|
||||
(module_name, (name, terms[1].arity() + supp_args), terms.pop().unwrap())
|
||||
(
|
||||
module_name,
|
||||
(name, terms[1].arity() + supp_args),
|
||||
terms.pop().unwrap(),
|
||||
)
|
||||
} else {
|
||||
arg_terms.push(Term::Clause(cell, atom!(":"), terms));
|
||||
continue;
|
||||
@@ -408,10 +409,8 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>(
|
||||
let term = match term {
|
||||
Term::Clause(cell, name, mut terms) => {
|
||||
if let Some(Term::Literal(_, Literal::CodeIndex(_))) = terms.last() {
|
||||
arg_terms.push(process_term(
|
||||
module_name,
|
||||
Term::Clause(cell, name, terms),
|
||||
));
|
||||
arg_terms
|
||||
.push(process_term(module_name, Term::Clause(cell, name, terms)));
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -424,11 +423,14 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>(
|
||||
Term::Literal(cell, Literal::Atom(name)) => {
|
||||
let idx = loader.get_or_insert_qualified_code_index(module_name, key);
|
||||
|
||||
process_term(module_name, Term::Clause(
|
||||
cell,
|
||||
name,
|
||||
vec![Term::Literal(Cell::default(), Literal::CodeIndex(idx))],
|
||||
))
|
||||
process_term(
|
||||
module_name,
|
||||
Term::Clause(
|
||||
cell,
|
||||
name,
|
||||
vec![Term::Literal(Cell::default(), Literal::CodeIndex(idx))],
|
||||
),
|
||||
)
|
||||
}
|
||||
term => term,
|
||||
};
|
||||
@@ -462,12 +464,7 @@ pub(super) fn clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
if let ClauseType::Named(arity, name, idx) = ct {
|
||||
if let Some(meta_specs) = loader.get_meta_specs(name, arity).cloned() {
|
||||
let module_name = loader.payload.compilation_target.module_name();
|
||||
let terms = build_meta_predicate_clause(
|
||||
loader,
|
||||
module_name,
|
||||
terms,
|
||||
meta_specs,
|
||||
);
|
||||
let terms = build_meta_predicate_clause(loader, module_name, terms, meta_specs);
|
||||
|
||||
return QueryTerm::Clause(
|
||||
Cell::default(),
|
||||
@@ -501,12 +498,7 @@ pub(super) fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
|
||||
if let ClauseType::Named(arity, name, idx) = ct {
|
||||
if let Some(meta_specs) = loader.get_meta_specs(name, arity).cloned() {
|
||||
let terms = build_meta_predicate_clause(
|
||||
loader,
|
||||
module_name,
|
||||
terms,
|
||||
meta_specs,
|
||||
);
|
||||
let terms = build_meta_predicate_clause(loader, module_name, terms, meta_specs);
|
||||
|
||||
return QueryTerm::Clause(
|
||||
Cell::default(),
|
||||
@@ -529,17 +521,13 @@ pub(crate) struct Preprocessor {
|
||||
|
||||
impl Preprocessor {
|
||||
pub(super) fn new(settings: CodeGenSettings) -> Self {
|
||||
Preprocessor {
|
||||
settings,
|
||||
}
|
||||
Preprocessor { settings }
|
||||
}
|
||||
|
||||
fn setup_fact(&mut self, term: Term) -> Result<(Fact, VarData), CompilationError> {
|
||||
match term {
|
||||
Term::Clause(..) | Term::Literal(_, Literal::Atom(..)) => {
|
||||
let classifier = VariableClassifier::new(
|
||||
self.settings.default_call_policy(),
|
||||
);
|
||||
let classifier = VariableClassifier::new(self.settings.default_call_policy());
|
||||
|
||||
let (head, var_data) = classifier.classify_fact(term)?;
|
||||
Ok((Fact { head }, var_data))
|
||||
@@ -554,40 +542,29 @@ impl Preprocessor {
|
||||
head: Term,
|
||||
body: Term,
|
||||
) -> Result<(Rule, VarData), CompilationError> {
|
||||
let classifier = VariableClassifier::new(
|
||||
self.settings.default_call_policy(),
|
||||
);
|
||||
let classifier = VariableClassifier::new(self.settings.default_call_policy());
|
||||
|
||||
let (head, clauses, var_data) = classifier.classify_rule(loader, head, body)?;
|
||||
|
||||
match head {
|
||||
Term::Clause(_, name, terms) => Ok((Rule {
|
||||
head: (name, terms),
|
||||
clauses,
|
||||
}, var_data)),
|
||||
Term::Literal(_, Literal::Atom(name)) => Ok((Rule {
|
||||
head: (name, vec![]),
|
||||
clauses,
|
||||
}, var_data)),
|
||||
Term::Clause(_, name, terms) => Ok((
|
||||
Rule {
|
||||
head: (name, terms),
|
||||
clauses,
|
||||
},
|
||||
var_data,
|
||||
)),
|
||||
Term::Literal(_, Literal::Atom(name)) => Ok((
|
||||
Rule {
|
||||
head: (name, vec![]),
|
||||
clauses,
|
||||
},
|
||||
var_data,
|
||||
)),
|
||||
_ => Err(CompilationError::InvalidRuleHead),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn try_term_to_query<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
terms: Vec<Term>,
|
||||
cut_context: CutContext,
|
||||
) -> Result<TopLevel, CompilationError> {
|
||||
Ok(TopLevel::Query(self.setup_query(
|
||||
loader,
|
||||
terms,
|
||||
cut_context,
|
||||
)?))
|
||||
}
|
||||
*/
|
||||
|
||||
pub(super) fn try_term_to_tl<'a, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
@@ -615,20 +592,4 @@ impl Preprocessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn try_terms_to_tls<'a, I: IntoIterator<Item = Term>, LS: LoadState<'a>>(
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
terms: I,
|
||||
) -> Result<VecDeque<TopLevel>, CompilationError> {
|
||||
let mut results = VecDeque::new();
|
||||
|
||||
for term in terms.into_iter() {
|
||||
results.push_back(self.try_term_to_tl(loader, term)?);
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
:- module('$project_atts', [copy_term/3]).
|
||||
:- module('$project_atts', []).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(error), [can_be/2]).
|
||||
@@ -100,14 +100,6 @@ gather_residual_goals([V|Vs]) -->
|
||||
|
||||
delete_all_attributes_from_var(V) :- '$delete_all_attributes_from_var'(V).
|
||||
|
||||
copy_term(Term, Copy, Gs) :-
|
||||
can_be(list, Gs),
|
||||
findall(Term-Rs, term_residual_goals(Term,Rs), [Copy-Gs]),
|
||||
( var(Gs) ->
|
||||
Gs = []
|
||||
; true
|
||||
).
|
||||
|
||||
term_residual_goals(Term,Rs) :-
|
||||
'$term_attributed_variables'(Term, Vs),
|
||||
phrase(gather_residual_goals(Vs), Rs),
|
||||
|
||||
@@ -30,12 +30,6 @@ pub struct Stack {
|
||||
_marker: PhantomData<HeapCellValue>,
|
||||
}
|
||||
|
||||
impl Drop for Stack {
|
||||
fn drop(&mut self) {
|
||||
self.buf.deallocate();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AndFramePrelude {
|
||||
pub(crate) num_cells: usize,
|
||||
@@ -62,7 +56,7 @@ impl Index<usize> for AndFrame {
|
||||
let index_offset = (index - 1) * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = mem::transmute::<&AndFrame, *const u8>(self);
|
||||
let ptr = self as *const crate::machine::stack::AndFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
|
||||
&*(ptr as *const HeapCellValue)
|
||||
@@ -76,7 +70,7 @@ impl IndexMut<usize> for AndFrame {
|
||||
let index_offset = (index - 1) * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = mem::transmute::<&mut AndFrame, *const u8>(self);
|
||||
let ptr = self as *mut crate::machine::stack::AndFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
|
||||
&mut *(ptr as *mut HeapCellValue)
|
||||
@@ -135,7 +129,7 @@ impl Index<usize> for OrFrame {
|
||||
let index_offset = index * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = mem::transmute::<&OrFrame, *const u8>(self);
|
||||
let ptr = self as *const crate::machine::stack::OrFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
|
||||
&*(ptr as *const HeapCellValue)
|
||||
@@ -150,7 +144,7 @@ impl IndexMut<usize> for OrFrame {
|
||||
let index_offset = index * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = mem::transmute::<&mut OrFrame, *const u8>(self);
|
||||
let ptr = self as *mut crate::machine::stack::OrFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
|
||||
&mut *(ptr as *mut HeapCellValue)
|
||||
@@ -189,13 +183,13 @@ impl Stack {
|
||||
let frame_size = AndFrame::size_of(num_cells);
|
||||
|
||||
unsafe {
|
||||
let e = self.buf.ptr as usize - self.buf.base as usize;
|
||||
let e = (*self.buf.ptr.get_mut()) 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 {
|
||||
ptr::write(
|
||||
(new_ptr as usize + offset) as *mut HeapCellValue,
|
||||
new_ptr.add(offset) as *mut HeapCellValue,
|
||||
stack_loc_as_cell!(AndFrame, e, idx + 1),
|
||||
);
|
||||
|
||||
@@ -209,11 +203,15 @@ impl Stack {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn top(&self) -> usize {
|
||||
unsafe { (*self.buf.ptr.get()) as usize - self.buf.base as usize }
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> usize {
|
||||
let frame_size = OrFrame::size_of(num_cells);
|
||||
|
||||
unsafe {
|
||||
let b = self.buf.ptr as usize - self.buf.base as usize;
|
||||
let b = (*self.buf.ptr.get_mut()) as usize - self.buf.base as usize;
|
||||
let new_ptr = self.alloc(frame_size);
|
||||
let mut offset = prelude_size::<OrFramePrelude>();
|
||||
|
||||
@@ -244,7 +242,8 @@ impl Stack {
|
||||
#[inline(always)]
|
||||
pub(crate) fn index_and_frame_mut(&mut self, e: usize) -> &mut AndFrame {
|
||||
unsafe {
|
||||
let ptr = self.buf.base as usize + e;
|
||||
// This is doing alignment wrong
|
||||
let ptr = self.buf.base.add(e);
|
||||
&mut *(ptr as *mut AndFrame)
|
||||
}
|
||||
}
|
||||
@@ -269,8 +268,8 @@ impl Stack {
|
||||
pub(crate) fn truncate(&mut self, b: usize) {
|
||||
let base = self.buf.base as usize + b;
|
||||
|
||||
if base < self.buf.ptr as usize {
|
||||
self.buf.ptr = base as *mut _;
|
||||
if base < (*self.buf.ptr.get_mut()) as usize {
|
||||
*self.buf.ptr.get_mut() = base as *mut _;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,6 +281,7 @@ mod tests {
|
||||
use crate::machine::mock_wam::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn stack_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
@@ -290,7 +290,7 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
e,
|
||||
0// 10 * mem::size_of::<HeapCellValue>() + prelude_size::<AndFrame>()
|
||||
0 // 10 * mem::size_of::<HeapCellValue>() + prelude_size::<AndFrame>()
|
||||
);
|
||||
|
||||
assert_eq!(and_frame.prelude.num_cells, 10);
|
||||
@@ -315,7 +315,10 @@ mod tests {
|
||||
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));
|
||||
assert_eq!(
|
||||
and_frame[idx + 1],
|
||||
stack_loc_as_cell!(AndFrame, next_e, idx + 1)
|
||||
);
|
||||
}
|
||||
|
||||
let and_frame = wam.machine_st.stack.index_and_frame(e);
|
||||
|
||||
@@ -4,32 +4,38 @@ use crate::parser::ast::*;
|
||||
use crate::parser::char_reader::*;
|
||||
use crate::read::*;
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
use crate::http::HttpResponse;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::types::*;
|
||||
#[cfg(feature = "http")]
|
||||
use crate::http::HttpResponse;
|
||||
|
||||
pub use modular_bitfield::prelude::*;
|
||||
pub use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::hash::{Hash};
|
||||
use std::hash::Hash;
|
||||
use std::io;
|
||||
use std::io::{BufRead, Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
|
||||
#[cfg(feature = "http")]
|
||||
use std::io::BufRead;
|
||||
use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
|
||||
use std::mem;
|
||||
use std::net::{TcpStream, Shutdown};
|
||||
use std::net::{Shutdown, TcpStream};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::PathBuf;
|
||||
use std::ptr;
|
||||
|
||||
#[cfg(feature = "tls")]
|
||||
use native_tls::TlsStream;
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
use warp::hyper;
|
||||
|
||||
#[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[bits = 1]
|
||||
pub enum StreamType {
|
||||
@@ -154,8 +160,10 @@ impl StreamLayout<CharReader<InputFileStream>> {
|
||||
fn position(&mut self) -> Option<u64> {
|
||||
// stream is the internal CharReader. subtract
|
||||
// its pending buffer length from position.
|
||||
self.get_mut().file.seek(SeekFrom::Current(0))
|
||||
.map(|pos| pos - self.stream.rem_buf_len() as u64)
|
||||
self.get_mut()
|
||||
.file
|
||||
.stream_position()
|
||||
.map(|pos| pos - self.stream.rem_buf_len() as u64)
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
@@ -195,7 +203,7 @@ 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)
|
||||
self.stream.get_ref()[pos..].chars().next().map(Ok)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -205,7 +213,9 @@ impl CharRead for StaticStringStream {
|
||||
|
||||
#[inline(always)]
|
||||
fn put_back_char(&mut self, c: char) {
|
||||
self.stream.seek(SeekFrom::Current(- (c.len_utf8() as i64))).unwrap();
|
||||
self.stream
|
||||
.seek(SeekFrom::Current(-(c.len_utf8() as i64)))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,15 +296,15 @@ impl Read for HttpReadStream {
|
||||
#[cfg(feature = "http")]
|
||||
pub struct HttpWriteStream {
|
||||
status_code: u16,
|
||||
headers: hyper::HeaderMap,
|
||||
headers: mem::ManuallyDrop<hyper::HeaderMap>,
|
||||
response: TypedArenaPtr<HttpResponse>,
|
||||
buffer: Vec<u8>,
|
||||
buffer: mem::ManuallyDrop<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "http")]
|
||||
impl Debug for HttpWriteStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Http Write Stream")
|
||||
write!(f, "Http Write Stream")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,28 +312,34 @@ impl Debug for HttpWriteStream {
|
||||
impl Write for HttpWriteStream {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.buffer.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
self.buffer.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
let (ready, response, cvar) = &**self.response;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut ready = ready.lock().unwrap();
|
||||
{
|
||||
let mut response = response.lock().unwrap();
|
||||
|
||||
let bytes = bytes::Bytes::copy_from_slice(&self.buffer);
|
||||
let mut response_ = hyper::Response::builder()
|
||||
.status(self.status_code);
|
||||
*response_.headers_mut().unwrap() = self.headers.clone();
|
||||
*response = Some(response_.body(http_body_util::Full::new(bytes)).unwrap());
|
||||
}
|
||||
*ready = true;
|
||||
cvar.notify_one();
|
||||
#[cfg(feature = "http")]
|
||||
impl HttpWriteStream {
|
||||
fn drop(&mut self) {
|
||||
let headers = unsafe { mem::ManuallyDrop::take(&mut self.headers) };
|
||||
let buffer = unsafe { mem::ManuallyDrop::take(&mut self.buffer) };
|
||||
|
||||
Ok(())
|
||||
let (ready, response, cvar) = &**self.response;
|
||||
|
||||
let mut ready = ready.lock().unwrap();
|
||||
{
|
||||
let mut response = response.lock().unwrap();
|
||||
|
||||
let mut response_ = warp::http::Response::builder().status(self.status_code);
|
||||
*response_.headers_mut().unwrap() = headers;
|
||||
*response = Some(response_.body(warp::hyper::Body::from(buffer)).unwrap());
|
||||
}
|
||||
*ready = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,7 +388,7 @@ impl StreamOptions {
|
||||
#[inline]
|
||||
pub fn get_alias(self) -> Option<Atom> {
|
||||
if self.has_alias() {
|
||||
Some(Atom::from((self.alias() as u64) << 3))
|
||||
Some(Atom::from(self.alias() << 3))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -449,9 +465,11 @@ macro_rules! arena_allocated_impl_for_stream {
|
||||
mem::size_of::<StreamLayout<$stream_type>>()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
// Miri seems to hit this a lot
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
}
|
||||
@@ -510,7 +528,9 @@ impl Stream {
|
||||
#[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())))),
|
||||
StreamLayout::new(CharReader::new(ByteStream(Cursor::new(
|
||||
string.into_bytes()
|
||||
)))),
|
||||
arena
|
||||
))
|
||||
}
|
||||
@@ -546,7 +566,7 @@ impl Stream {
|
||||
#[cfg(feature = "http")]
|
||||
ArenaHeaderTag::HttpReadStream => Stream::HttpRead(TypedArenaPtr::new(ptr as *mut _)),
|
||||
#[cfg(feature = "http")]
|
||||
ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::ReadlineStream => Stream::Readline(TypedArenaPtr::new(ptr as *mut _)),
|
||||
ArenaHeaderTag::StaticStringStream => {
|
||||
Stream::StaticString(TypedArenaPtr::new(ptr as *mut _))
|
||||
@@ -566,29 +586,17 @@ impl Stream {
|
||||
|
||||
#[inline]
|
||||
pub fn is_stderr(&self) -> bool {
|
||||
if let Stream::StandardError(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Stream::StandardError(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_stdout(&self) -> bool {
|
||||
if let Stream::StandardOutput(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Stream::StandardOutput(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_stdin(&self) -> bool {
|
||||
if let Stream::Readline(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Stream::Readline(_))
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *const ArenaHeader {
|
||||
@@ -603,7 +611,7 @@ impl Stream {
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(ptr) => ptr.header_ptr(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(ptr) => ptr.header_ptr(),
|
||||
Stream::HttpWrite(ptr) => ptr.header_ptr(),
|
||||
Stream::Null(_) => ptr::null(),
|
||||
Stream::Readline(ptr) => ptr.header_ptr(),
|
||||
Stream::StandardOutput(ptr) => ptr.header_ptr(),
|
||||
@@ -728,14 +736,14 @@ impl CharRead for Stream {
|
||||
Stream::StaticString(src) => (*src).peek_char(),
|
||||
Stream::Byte(cursor) => (*cursor).peek_char(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(_) => Some(Err(std::io::Error::new(
|
||||
Stream::HttpWrite(_) => Some(Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
))),
|
||||
Stream::OutputFile(_) |
|
||||
Stream::StandardError(_) |
|
||||
Stream::StandardOutput(_) |
|
||||
Stream::Null(_) => Some(Err(std::io::Error::new(
|
||||
Stream::OutputFile(_)
|
||||
| Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::Null(_) => Some(Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
))),
|
||||
@@ -754,14 +762,14 @@ impl CharRead for Stream {
|
||||
Stream::StaticString(src) => (*src).read_char(),
|
||||
Stream::Byte(cursor) => (*cursor).read_char(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(_) => Some(Err(std::io::Error::new(
|
||||
Stream::HttpWrite(_) => Some(Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
))),
|
||||
Stream::OutputFile(_) |
|
||||
Stream::StandardError(_) |
|
||||
Stream::StandardOutput(_) |
|
||||
Stream::Null(_) => Some(Err(std::io::Error::new(
|
||||
Stream::OutputFile(_)
|
||||
| Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::Null(_) => Some(Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
))),
|
||||
@@ -780,11 +788,11 @@ impl CharRead for Stream {
|
||||
Stream::StaticString(src) => src.put_back_char(c),
|
||||
Stream::Byte(cursor) => cursor.put_back_char(c),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(_) => {}
|
||||
Stream::OutputFile(_) |
|
||||
Stream::StandardError(_) |
|
||||
Stream::StandardOutput(_) |
|
||||
Stream::Null(_) => {}
|
||||
Stream::HttpWrite(_) => {}
|
||||
Stream::OutputFile(_)
|
||||
| Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::Null(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -800,11 +808,11 @@ impl CharRead for Stream {
|
||||
Stream::StaticString(ref mut src) => src.consume(nread),
|
||||
Stream::Byte(ref mut cursor) => cursor.consume(nread),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(_) => {}
|
||||
Stream::OutputFile(_) |
|
||||
Stream::StandardError(_) |
|
||||
Stream::StandardOutput(_) |
|
||||
Stream::Null(_) => {}
|
||||
Stream::HttpWrite(_) => {}
|
||||
Stream::OutputFile(_)
|
||||
| Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::Null(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -812,7 +820,7 @@ impl CharRead for Stream {
|
||||
impl Read for Stream {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let bytes_read = match self {
|
||||
match self {
|
||||
Stream::InputFile(file) => (*file).read(buf),
|
||||
Stream::NamedTcp(tcp_stream) => (*tcp_stream).read(buf),
|
||||
#[cfg(feature = "tls")]
|
||||
@@ -823,20 +831,18 @@ impl Read for Stream {
|
||||
Stream::StaticString(src) => (*src).read(buf),
|
||||
Stream::Byte(cursor) => (*cursor).read(buf),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(_) => Err(std::io::Error::new(
|
||||
Stream::HttpWrite(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
)),
|
||||
Stream::OutputFile(_)
|
||||
| Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::Null(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
)),
|
||||
};
|
||||
|
||||
bytes_read
|
||||
| Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::Null(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::ReadFromOutputStream,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,16 +857,16 @@ impl Write for Stream {
|
||||
Stream::StandardOutput(stream) => stream.write(buf),
|
||||
Stream::StandardError(stream) => stream.write(buf),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(ref mut stream) => stream.get_mut().write(buf),
|
||||
Stream::HttpWrite(ref mut stream) => stream.get_mut().write(buf),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::WriteToInputStream,
|
||||
)),
|
||||
Stream::StaticString(_) |
|
||||
Stream::Readline(_) |
|
||||
Stream::InputFile(..) |
|
||||
Stream::Null(_) => Err(std::io::Error::new(
|
||||
Stream::StaticString(_)
|
||||
| Stream::Readline(_)
|
||||
| Stream::InputFile(..)
|
||||
| Stream::Null(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::WriteToInputStream,
|
||||
)),
|
||||
@@ -877,16 +883,16 @@ impl Write for Stream {
|
||||
Stream::StandardError(stream) => stream.stream.flush(),
|
||||
Stream::StandardOutput(stream) => stream.stream.flush(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(ref mut stream) => stream.stream.get_mut().flush(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(ref mut stream) => stream.stream.get_mut().flush(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::FlushToInputStream,
|
||||
)),
|
||||
Stream::StaticString(_) |
|
||||
Stream::Readline(_) |
|
||||
Stream::InputFile(_) |
|
||||
Stream::Null(_) => Err(std::io::Error::new(
|
||||
Stream::StaticString(_)
|
||||
| Stream::Readline(_)
|
||||
| Stream::InputFile(_)
|
||||
| Stream::Null(_) => Err(std::io::Error::new(
|
||||
ErrorKind::PermissionDenied,
|
||||
StreamError::FlushToInputStream,
|
||||
)),
|
||||
@@ -898,8 +904,10 @@ impl Write for Stream {
|
||||
enum StreamError {
|
||||
PeekByteFailed,
|
||||
PeekByteFromNonPeekableStream,
|
||||
#[allow(unused)] PeekCharFailed,
|
||||
#[allow(unused)] PeekCharFromNonPeekableStream,
|
||||
#[allow(unused)]
|
||||
PeekCharFailed,
|
||||
#[allow(unused)]
|
||||
PeekCharFromNonPeekableStream,
|
||||
ReadFromOutputStream,
|
||||
WriteToInputStream,
|
||||
FlushToInputStream,
|
||||
@@ -958,19 +966,19 @@ impl PartialEq for Stream {
|
||||
|
||||
impl Eq for Stream {}
|
||||
|
||||
fn cursor_position<T>(past_end_of_stream: &mut bool, cursor: &Cursor<T>, cursor_len: u64) -> AtEndOfStream {
|
||||
let position = cursor.position();
|
||||
|
||||
let at_end_of_stream = match position.cmp(&cursor_len) {
|
||||
fn cursor_position<T>(
|
||||
past_end_of_stream: &mut bool,
|
||||
cursor: &Cursor<T>,
|
||||
cursor_len: u64,
|
||||
) -> AtEndOfStream {
|
||||
match cursor.position().cmp(&cursor_len) {
|
||||
Ordering::Equal => AtEndOfStream::At,
|
||||
Ordering::Greater => {
|
||||
*past_end_of_stream = true;
|
||||
AtEndOfStream::Past
|
||||
}
|
||||
Ordering::Less => AtEndOfStream::Not,
|
||||
};
|
||||
|
||||
at_end_of_stream
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream {
|
||||
@@ -984,17 +992,10 @@ impl Stream {
|
||||
Stream::StaticString(string_stream_layout) => {
|
||||
Some(string_stream_layout.stream.stream.position())
|
||||
}
|
||||
Stream::InputFile(file_stream) => {
|
||||
file_stream.position()
|
||||
}
|
||||
Stream::InputFile(file_stream) => file_stream.position(),
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(..) => {
|
||||
Some(0)
|
||||
}
|
||||
Stream::NamedTcp(..)
|
||||
| Stream::Readline(..) => {
|
||||
Some(0)
|
||||
}
|
||||
Stream::NamedTls(..) => Some(0),
|
||||
Stream::NamedTcp(..) | Stream::Readline(..) => Some(0),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@@ -1003,22 +1004,23 @@ impl Stream {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_position(&mut self, position: u64) {
|
||||
match self {
|
||||
Stream::InputFile(stream_layout) => {
|
||||
let StreamLayout {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
if let Stream::InputFile(stream_layout) = self {
|
||||
let StreamLayout {
|
||||
past_end_of_stream,
|
||||
stream,
|
||||
..
|
||||
} = &mut **stream_layout;
|
||||
|
||||
stream.get_mut().file.seek(SeekFrom::Start(position)).unwrap();
|
||||
stream.reset_buffer(); // flush the internal buffer.
|
||||
stream
|
||||
.get_mut()
|
||||
.file
|
||||
.seek(SeekFrom::Start(position))
|
||||
.unwrap();
|
||||
stream.reset_buffer(); // flush the internal buffer.
|
||||
|
||||
if let Ok(metadata) = stream.get_ref().file.metadata() {
|
||||
*past_end_of_stream = position > metadata.len();
|
||||
}
|
||||
if let Ok(metadata) = stream.get_ref().file.metadata() {
|
||||
*past_end_of_stream = position > metadata.len();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1127,9 +1129,7 @@ impl Stream {
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
AtEndOfStream::Not
|
||||
}
|
||||
_ => AtEndOfStream::Not,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1153,14 +1153,16 @@ impl Stream {
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(..) => atom!("read_append"),
|
||||
Stream::Byte(_)
|
||||
| Stream::Readline(_)
|
||||
| Stream::StaticString(_)
|
||||
| Stream::InputFile(..) => atom!("read"),
|
||||
| Stream::Readline(_)
|
||||
| Stream::StaticString(_)
|
||||
| Stream::InputFile(..) => atom!("read"),
|
||||
Stream::NamedTcp(..) => atom!("read_append"),
|
||||
Stream::OutputFile(file) if file.is_append => atom!("append"),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(_) => atom!("write"),
|
||||
Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) => atom!("write"),
|
||||
Stream::OutputFile(_) | Stream::StandardError(_) | Stream::StandardOutput(_) => {
|
||||
atom!("write")
|
||||
}
|
||||
Stream::Null(_) => atom!(""),
|
||||
}
|
||||
}
|
||||
@@ -1182,11 +1184,7 @@ impl Stream {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_tcp_stream(
|
||||
address: Atom,
|
||||
tcp_stream: TcpStream,
|
||||
arena: &mut Arena,
|
||||
) -> 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();
|
||||
|
||||
@@ -1234,20 +1232,20 @@ impl Stream {
|
||||
#[cfg(feature = "http")]
|
||||
#[inline]
|
||||
pub(crate) fn from_http_sender(
|
||||
response: TypedArenaPtr<HttpResponse>,
|
||||
status_code: u16,
|
||||
headers: hyper::HeaderMap,
|
||||
arena: &mut Arena,
|
||||
response: TypedArenaPtr<HttpResponse>,
|
||||
status_code: u16,
|
||||
headers: hyper::HeaderMap,
|
||||
arena: &mut Arena,
|
||||
) -> Self {
|
||||
Stream::HttpWrite(arena_alloc!(
|
||||
StreamLayout::new(CharReader::new(HttpWriteStream {
|
||||
response,
|
||||
status_code,
|
||||
headers,
|
||||
buffer: Vec::new(),
|
||||
})),
|
||||
arena
|
||||
))
|
||||
Stream::HttpWrite(arena_alloc!(
|
||||
StreamLayout::new(CharReader::new(HttpWriteStream {
|
||||
response,
|
||||
status_code,
|
||||
headers: mem::ManuallyDrop::new(headers),
|
||||
buffer: mem::ManuallyDrop::new(Vec::new()),
|
||||
})),
|
||||
arena
|
||||
))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1282,11 +1280,9 @@ impl Stream {
|
||||
match stream {
|
||||
Stream::NamedTcp(ref mut tcp_stream) => {
|
||||
tcp_stream.inner_mut().tcp_stream.shutdown(Shutdown::Both)
|
||||
},
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(ref mut tls_stream) => {
|
||||
tls_stream.inner_mut().tls_stream.shutdown()
|
||||
}
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(ref mut tls_stream) => tls_stream.inner_mut().tls_stream.shutdown(),
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(ref mut http_stream) => {
|
||||
unsafe {
|
||||
@@ -1297,14 +1293,15 @@ impl Stream {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(ref mut http_stream) => {
|
||||
Stream::HttpWrite(ref mut http_stream) => {
|
||||
http_stream.inner_mut().drop();
|
||||
unsafe {
|
||||
http_stream.set_tag(ArenaHeaderTag::Dropped);
|
||||
std::ptr::drop_in_place(&mut http_stream.inner_mut().buffer as *mut _);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Stream::InputFile(mut file_stream) => {
|
||||
// close the stream by dropping the inner File.
|
||||
unsafe {
|
||||
@@ -1323,17 +1320,13 @@ impl Stream {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(())
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_null_stream(&self) -> bool {
|
||||
if let Stream::Null(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Stream::Null(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1344,10 +1337,10 @@ impl Stream {
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(..) => true,
|
||||
Stream::NamedTcp(..)
|
||||
| Stream::Byte(_)
|
||||
| Stream::Readline(_)
|
||||
| Stream::StaticString(_)
|
||||
| Stream::InputFile(..) => true,
|
||||
| Stream::Byte(_)
|
||||
| Stream::Readline(_)
|
||||
| Stream::StaticString(_)
|
||||
| Stream::InputFile(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -1360,10 +1353,10 @@ impl Stream {
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpWrite(..) => true,
|
||||
Stream::StandardError(_)
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::NamedTcp(..)
|
||||
| Stream::Byte(_)
|
||||
| Stream::OutputFile(..) => true,
|
||||
| Stream::StandardOutput(_)
|
||||
| Stream::NamedTcp(..)
|
||||
| Stream::Byte(_)
|
||||
| Stream::OutputFile(..) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -1374,24 +1367,25 @@ impl Stream {
|
||||
self.set_lines_read(0);
|
||||
self.set_past_end_of_stream(false);
|
||||
|
||||
loop {
|
||||
match self {
|
||||
Stream::Byte(ref mut cursor) => {
|
||||
cursor.stream.get_mut().0.set_position(0);
|
||||
return true;
|
||||
}
|
||||
Stream::InputFile(ref mut file_stream) => {
|
||||
file_stream.stream.get_mut().file.seek(SeekFrom::Start(0)).unwrap();
|
||||
return true;
|
||||
}
|
||||
Stream::Readline(ref mut readline_stream) => {
|
||||
readline_stream.reset();
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
match self {
|
||||
Stream::Byte(ref mut cursor) => {
|
||||
cursor.stream.get_mut().0.set_position(0);
|
||||
true
|
||||
}
|
||||
Stream::InputFile(ref mut file_stream) => {
|
||||
file_stream
|
||||
.stream
|
||||
.get_mut()
|
||||
.file
|
||||
.seek(SeekFrom::Start(0))
|
||||
.unwrap();
|
||||
true
|
||||
}
|
||||
Stream::Readline(ref mut readline_stream) => {
|
||||
readline_stream.reset();
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1410,17 +1404,13 @@ impl Stream {
|
||||
_ => Err(std::io::Error::new(ErrorKind::UnexpectedEof, "end of file")),
|
||||
}
|
||||
}
|
||||
Stream::InputFile(ref mut file) => {
|
||||
match file.peek_byte() {
|
||||
Some(result) => {
|
||||
Ok(result?)
|
||||
}
|
||||
_ => Err(std::io::Error::new(
|
||||
ErrorKind::UnexpectedEof,
|
||||
StreamError::PeekByteFailed,
|
||||
)),
|
||||
}
|
||||
}
|
||||
Stream::InputFile(ref mut file) => match file.peek_byte() {
|
||||
Some(result) => Ok(result?),
|
||||
_ => Err(std::io::Error::new(
|
||||
ErrorKind::UnexpectedEof,
|
||||
StreamError::PeekByteFailed,
|
||||
)),
|
||||
},
|
||||
Stream::Readline(ref mut stream) => stream.stream.peek_byte(),
|
||||
Stream::NamedTcp(ref mut stream) => {
|
||||
let mut b = [0u8; 1];
|
||||
@@ -1466,12 +1456,13 @@ impl MachineState {
|
||||
stream.set_past_end_of_stream(true);
|
||||
}
|
||||
|
||||
Ok(self.fail = stream.past_end_of_stream())
|
||||
self.fail = stream.past_end_of_stream();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_stream_options(
|
||||
pub(crate) fn get_stream_options(
|
||||
&mut self,
|
||||
alias: HeapCellValue,
|
||||
eof_action: HeapCellValue,
|
||||
@@ -1663,7 +1654,10 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn open_parsing_stream(&mut self, mut stream: Stream) -> Result<Stream, ParserError> {
|
||||
pub(crate) fn open_parsing_stream(
|
||||
&mut self,
|
||||
mut stream: Stream,
|
||||
) -> Result<Stream, ParserError> {
|
||||
match stream.peek_char() {
|
||||
None => Ok(stream), // empty stream is handled gracefully by Lexer::eof
|
||||
Some(Err(e)) => Err(ParserError::IO(e)),
|
||||
@@ -1687,7 +1681,7 @@ impl MachineState {
|
||||
arity: usize,
|
||||
) -> MachineStub {
|
||||
let stub = functor_stub(caller, arity);
|
||||
let err = self.permission_error(
|
||||
let err = self.permission_error(
|
||||
perm,
|
||||
err_atom,
|
||||
if let Some(alias) = stream.options().get_alias() {
|
||||
@@ -1723,7 +1717,7 @@ impl MachineState {
|
||||
stub_arity: usize,
|
||||
) -> MachineStub {
|
||||
let stub = functor_stub(stub_name, stub_arity);
|
||||
let err = self.permission_error(Permission::Open, atom!("source_sink"), culprit);
|
||||
let err = self.permission_error(Permission::Open, atom!("source_sink"), culprit);
|
||||
|
||||
self.error_form(err, stub)
|
||||
}
|
||||
@@ -1761,9 +1755,9 @@ impl MachineState {
|
||||
caller: Atom,
|
||||
arity: usize,
|
||||
) -> CallResult {
|
||||
let opt_err = if input.is_some() && !stream.is_input_stream() {
|
||||
Some(atom!("stream")) // 8.14.2.3 g)
|
||||
} else if input.is_none() && !stream.is_output_stream() {
|
||||
let opt_err = if input.is_some() && !stream.is_input_stream()
|
||||
|| input.is_none() && !stream.is_output_stream()
|
||||
{
|
||||
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)
|
||||
@@ -1798,7 +1792,7 @@ impl MachineState {
|
||||
) -> 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)]);
|
||||
let err = self.domain_error(DomainErrorType::SourceSink, self.registers[1]);
|
||||
|
||||
return Err(self.error_form(err, stub));
|
||||
}
|
||||
@@ -1810,9 +1804,7 @@ impl MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
let mode = MachineState::deref(self, self[temp_v!(2)]);
|
||||
let mode = cell_as_atom!(self.store(mode));
|
||||
|
||||
let mode = cell_as_atom!(self.store(MachineState::deref(self, self.registers[2])));
|
||||
let mut open_options = OpenOptions::new();
|
||||
|
||||
let (is_input_file, in_append_mode) = match mode {
|
||||
@@ -1847,38 +1839,55 @@ impl MachineState {
|
||||
}
|
||||
};
|
||||
|
||||
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 mut path = PathBuf::from(&*file_spec.as_str());
|
||||
|
||||
let err = self.existence_error(
|
||||
ExistenceError::SourceSink(self[temp_v!(1)]),
|
||||
);
|
||||
loop {
|
||||
let file = match open_options.open(&path) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
match err.kind() {
|
||||
ErrorKind::NotFound => {
|
||||
// 8.11.5.3j)
|
||||
let stub = functor_stub(atom!("open"), 4);
|
||||
|
||||
return Err(self.error_form(err, stub));
|
||||
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.registers[1],
|
||||
atom!("open"),
|
||||
4,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
// assume the OS is out of file descriptors.
|
||||
let stub = functor_stub(atom!("open"), 4);
|
||||
let err = self.resource_error(ResourceError::OutOfFiles);
|
||||
|
||||
return Err(self.error_form(err, stub));
|
||||
}
|
||||
}
|
||||
ErrorKind::PermissionDenied => {
|
||||
// 8.11.5.3k)
|
||||
return Err(self.open_permission_error(self.registers[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));
|
||||
if path.extension().is_none() {
|
||||
if let Ok(metadata) = file.metadata() {
|
||||
if metadata.is_dir() {
|
||||
path.set_extension("pl");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
})
|
||||
return 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)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
use crate::forms::*;
|
||||
use crate::machine::*;
|
||||
use crate::machine::load_state::*;
|
||||
use crate::machine::loader::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::parser::*;
|
||||
use crate::read::devour_whitespace;
|
||||
@@ -45,7 +45,10 @@ impl<'a> BootstrappingTermStream<'a> {
|
||||
listing_src: ListingSource,
|
||||
) -> Self {
|
||||
let parser = Parser::new(stream, machine_st);
|
||||
Self { parser, listing_src }
|
||||
Self {
|
||||
parser,
|
||||
listing_src,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +104,7 @@ impl<TS> LoadStatePayload<TS> {
|
||||
non_counted_bt_preds: IndexSet::with_hasher(FxBuildHasher::default()),
|
||||
predicates: predicate_queue![],
|
||||
clause_clauses: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,20 +125,18 @@ impl TermStream for LiveTermStream {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InlineTermStream {
|
||||
}
|
||||
pub struct InlineTermStream {}
|
||||
|
||||
impl TermStream for InlineTermStream {
|
||||
fn next(&mut self, _: &CompositeOpDir) -> Result<Term, CompilationError> {
|
||||
Err(CompilationError::from(ParserError::unexpected_eof()))
|
||||
Err(CompilationError::from(ParserError::unexpected_eof()))
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<bool, CompilationError> {
|
||||
Ok(true)
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn listing_src(&self) -> &ListingSource {
|
||||
&ListingSource::User
|
||||
&ListingSource::User
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::arena::*;
|
||||
use crate::forms::*;
|
||||
use crate::heap_iter::stackful_preorder_iter;
|
||||
use crate::machine::*;
|
||||
use crate::heap_iter::{stackful_preorder_iter, NonListElider};
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::partial_string::*;
|
||||
use crate::machine::*;
|
||||
use crate::types::*;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
@@ -12,6 +12,7 @@ use std::ops::{Deref, DerefMut};
|
||||
use derive_deref::*;
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
use num_order::NumOrd;
|
||||
|
||||
pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
fn unify_structure(&mut self, s1: usize, value: HeapCellValue) {
|
||||
@@ -172,6 +173,98 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
let mut pstr_iter1 = HeapPStrIter::new(&machine_st.heap, s1);
|
||||
let mut pstr_iter2 = HeapPStrIter::new(&machine_st.heap, s1 + 1);
|
||||
|
||||
fn unify_sequence(
|
||||
machine_st: &mut MachineState,
|
||||
iter: PStrIteratee,
|
||||
source_cell: HeapCellValue,
|
||||
) -> bool {
|
||||
match iter {
|
||||
PStrIteratee::Char(focus, _) => {
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(source_cell);
|
||||
}
|
||||
PStrIteratee::PStrSegment(focus, _, n) => {
|
||||
read_heap_cell!(machine_st.heap[focus],
|
||||
(HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => {
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
let target_cell = match machine_st.heap[focus].get_tag() {
|
||||
HeapCellValueTag::CStr => {
|
||||
atom_as_cstr_cell!(pstr_atom)
|
||||
}
|
||||
HeapCellValueTag::PStr => {
|
||||
pstr_loc_as_cell!(focus)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
machine_st.pdl.push(target_cell);
|
||||
machine_st.pdl.push(source_cell);
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(focus));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(source_cell);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
(HeapCellValueTag::PStrOffset, pstr_loc) => {
|
||||
let n0 = cell_as_fixnum!(machine_st.heap[focus+1])
|
||||
.get_num() as usize;
|
||||
|
||||
if pstr_loc < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == n0 {
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(focus));
|
||||
machine_st.pdl.push(source_cell);
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(pstr_loc));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(source_cell);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(source_cell);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) {
|
||||
PStrCmpResult::Ordered(Ordering::Equal) => {}
|
||||
PStrCmpResult::Ordered(Ordering::Less) => {
|
||||
@@ -190,8 +283,8 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
machine_st.pdl.push(pstr_iter1.focus);
|
||||
}
|
||||
}
|
||||
continuable @ PStrCmpResult::FirstIterContinuable(iteratee) |
|
||||
continuable @ PStrCmpResult::SecondIterContinuable(iteratee) => {
|
||||
continuable @ PStrCmpResult::FirstIterContinuable(iteratee)
|
||||
| continuable @ PStrCmpResult::SecondIterContinuable(iteratee) => {
|
||||
if continuable.is_second_iter() {
|
||||
std::mem::swap(&mut pstr_iter1, &mut pstr_iter2);
|
||||
}
|
||||
@@ -203,7 +296,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
let mut focus = pstr_iter2.focus;
|
||||
|
||||
'outer: loop {
|
||||
'outer: {
|
||||
while let Some(c) = chars_iter.peek() {
|
||||
read_heap_cell!(focus,
|
||||
(HeapCellValueTag::Lis, l) => {
|
||||
@@ -228,89 +321,13 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => {
|
||||
unify_sequence(machine_st, chars_iter.item.unwrap(), focus);
|
||||
return;
|
||||
}
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
|
||||
match chars_iter.item.unwrap() {
|
||||
PStrIteratee::Char(focus, _) => {
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
}
|
||||
PStrIteratee::PStrSegment(focus, _, n) => {
|
||||
read_heap_cell!(machine_st.heap[focus],
|
||||
(HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => {
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
let target_cell = match machine_st.heap[focus].get_tag() {
|
||||
HeapCellValueTag::CStr => {
|
||||
atom_as_cstr_cell!(pstr_atom)
|
||||
}
|
||||
HeapCellValueTag::PStr => {
|
||||
pstr_loc_as_cell!(focus)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
machine_st.pdl.push(target_cell);
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(focus));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
(HeapCellValueTag::PStrOffset, pstr_loc) => {
|
||||
let n0 = cell_as_fixnum!(machine_st.heap[focus+1])
|
||||
.get_num() as usize;
|
||||
|
||||
if pstr_loc < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
if n == n0 {
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(focus));
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
} else {
|
||||
let h_len = machine_st.heap.len();
|
||||
|
||||
machine_st.heap.push(pstr_offset_as_cell!(pstr_loc));
|
||||
machine_st.heap.push(fixnum_as_cell!(
|
||||
Fixnum::build_with(n as i64)
|
||||
));
|
||||
|
||||
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
);
|
||||
|
||||
if focus < machine_st.heap.len() - 2 {
|
||||
machine_st.heap.pop();
|
||||
machine_st.heap.pop();
|
||||
}
|
||||
|
||||
machine_st.pdl.push(machine_st.heap[focus]);
|
||||
machine_st.pdl.push(heap_loc_as_cell!(h));
|
||||
|
||||
return;
|
||||
}
|
||||
if unify_sequence(machine_st, chars_iter.item.unwrap(), heap_loc_as_cell!(h)) {
|
||||
return;
|
||||
}
|
||||
|
||||
break 'outer;
|
||||
@@ -328,8 +345,6 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
machine_st.pdl.push(focus);
|
||||
machine_st.pdl.push(chars_iter.iter.focus);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
PStrCmpResult::Unordered => {
|
||||
@@ -426,8 +441,8 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
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 => {}
|
||||
Number::Integer(n2) if (*n2).num_eq(&n1.get_num()) => {}
|
||||
Number::Rational(n2) if (*n2).num_eq(&Integer::from(n1.get_num())) => {}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
@@ -439,10 +454,8 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
}
|
||||
|
||||
fn unify_big_num<N>(&mut self, n1: TypedArenaPtr<N>, value: HeapCellValue)
|
||||
where N: PartialEq<Rational>
|
||||
+ PartialEq<Integer>
|
||||
+ PartialEq<i64>
|
||||
+ ArenaAllocated
|
||||
where
|
||||
N: PartialEq<Rational> + PartialEq<Integer> + PartialEq<i64> + ArenaAllocated,
|
||||
{
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, typed_arena_ptr_as_cell!(n1));
|
||||
@@ -464,6 +477,48 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_big_integer(&mut self, n1: TypedArenaPtr<Integer>, value: HeapCellValue) {
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, typed_arena_ptr_as_cell!(n1));
|
||||
return;
|
||||
}
|
||||
|
||||
match Number::try_from(value) {
|
||||
Ok(n2) => match n2 {
|
||||
Number::Fixnum(n2) if (*n1).num_eq(&n2.get_num()) => {}
|
||||
Number::Integer(n2) if (*n1).num_eq(&*n2) => {}
|
||||
Number::Rational(n2) if (*n2).num_eq(&*n1) => {}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_big_rational(&mut self, n1: TypedArenaPtr<Rational>, value: HeapCellValue) {
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, typed_arena_ptr_as_cell!(n1));
|
||||
return;
|
||||
}
|
||||
|
||||
match Number::try_from(value) {
|
||||
Ok(n2) => match n2 {
|
||||
Number::Fixnum(n2) if (*n1).num_eq(&Integer::from(n2.get_num())) => {}
|
||||
Number::Integer(n2) if (*n1).num_eq(&*n2) => {}
|
||||
Number::Rational(n2) if n1 == n2 => {}
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_f64(&mut self, f1: F64Ptr, value: HeapCellValue) {
|
||||
if let Some(r) = value.as_var() {
|
||||
Self::bind(self, r, HeapCellValue::from(f1));
|
||||
@@ -489,10 +544,10 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
|
||||
match_untyped_arena_ptr!(ptr,
|
||||
(ArenaHeaderTag::Integer, int_ptr) => {
|
||||
Self::unify_big_num(self, int_ptr, value);
|
||||
Self::unify_big_integer(self, int_ptr, value);
|
||||
}
|
||||
(ArenaHeaderTag::Rational, rat_ptr) => {
|
||||
Self::unify_big_num(self, rat_ptr, value);
|
||||
Self::unify_big_rational(self, rat_ptr, value);
|
||||
}
|
||||
(ArenaHeaderTag::Stream, stream) => {
|
||||
read_heap_cell!(value,
|
||||
@@ -568,10 +623,8 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, l1) => {
|
||||
if d2.is_ref() {
|
||||
if tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
if d2.is_ref() && tabu_list.contains(&(d1, d2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Self::unify_list(self, l1, d2);
|
||||
@@ -650,6 +703,9 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
|
||||
(HeapCellValueTag::Cons, ptr_1) => {
|
||||
Self::unify_constant(self, ptr_1, d2);
|
||||
}
|
||||
(HeapCellValueTag::CutPoint, n1) => {
|
||||
Self::unify_fixnum(self, n1, d2);
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
@@ -676,7 +732,11 @@ fn bind_with_occurs_check<U: Unifier>(unifier: &mut U, r: Ref, value: HeapCellVa
|
||||
if !value.is_constant() {
|
||||
let machine_st: &mut MachineState = unifier.deref_mut();
|
||||
|
||||
for cell in stackful_preorder_iter(&mut machine_st.heap, &mut machine_st.stack, value) {
|
||||
for cell in stackful_preorder_iter::<NonListElider>(
|
||||
&mut machine_st.heap,
|
||||
&mut machine_st.stack,
|
||||
value,
|
||||
) {
|
||||
let cell = unmark_cell_bits!(cell);
|
||||
|
||||
if let Some(inner_r) = cell.as_var() {
|
||||
@@ -694,7 +754,7 @@ fn bind_with_occurs_check<U: Unifier>(unifier: &mut U, r: Ref, value: HeapCellVa
|
||||
U::bind(unifier, r, value);
|
||||
}
|
||||
|
||||
return occurs_triggered;
|
||||
occurs_triggered
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
|
||||
105
src/macros.rs
105
src/macros.rs
@@ -57,13 +57,6 @@ macro_rules! atom_as_cell {
|
||||
};
|
||||
}
|
||||
|
||||
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))
|
||||
@@ -88,7 +81,7 @@ macro_rules! cell_as_atom_cell {
|
||||
macro_rules! cell_as_f64_ptr {
|
||||
($cell:expr) => {{
|
||||
let offset = $cell.get_value() as usize;
|
||||
F64Ptr::from_offset(offset)
|
||||
F64Ptr::from_offset(F64Offset::new(offset))
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -236,12 +229,14 @@ macro_rules! cell_as_stream {
|
||||
}
|
||||
|
||||
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());
|
||||
($cell:expr) => {
|
||||
unsafe {
|
||||
let ptr = cell_as_untyped_arena_ptr!($cell);
|
||||
let ptr = std::mem::transmute::<_, *mut LiveLoadState>(ptr.payload_offset());
|
||||
|
||||
TypedArenaPtr::new(ptr)
|
||||
}};
|
||||
TypedArenaPtr::new(ptr)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! match_untyped_arena_ptr_pat_body {
|
||||
@@ -258,13 +253,15 @@ macro_rules! match_untyped_arena_ptr_pat_body {
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, OssifiedOpDir, $n:ident, $code:expr) => {{
|
||||
let payload_ptr = unsafe { std::mem::transmute::<_, *mut OssifiedOpDir>($ptr.payload_offset()) };
|
||||
let payload_ptr =
|
||||
unsafe { std::mem::transmute::<_, *mut OssifiedOpDir>($ptr.payload_offset()) };
|
||||
let $n = TypedArenaPtr::new(payload_ptr);
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, LiveLoadState, $n:ident, $code:expr) => {{
|
||||
let payload_ptr = unsafe { std::mem::transmute::<_, *mut LiveLoadState>($ptr.payload_offset()) };
|
||||
let payload_ptr =
|
||||
unsafe { std::mem::transmute::<_, *mut LiveLoadState>($ptr.payload_offset()) };
|
||||
let $n = TypedArenaPtr::new(payload_ptr);
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
@@ -275,21 +272,24 @@ macro_rules! match_untyped_arena_ptr_pat_body {
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, TcpListener, $listener:ident, $code:expr) => {{
|
||||
let payload_ptr = unsafe { std::mem::transmute::<_, *mut TcpListener>($ptr.payload_offset()) };
|
||||
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, HttpListener, $listener:ident, $code:expr) => {{
|
||||
let payload_ptr = unsafe { std::mem::transmute::<_, *mut HttpListener>($ptr.payload_offset()) };
|
||||
let payload_ptr =
|
||||
unsafe { std::mem::transmute::<_, *mut HttpListener>($ptr.payload_offset()) };
|
||||
#[allow(unused_mut)]
|
||||
let mut $listener = TypedArenaPtr::new(payload_ptr);
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
($ptr:ident, HttpResponse, $listener:ident, $code:expr) => {{
|
||||
let payload_ptr = unsafe { std::mem::transmute::<_, *mut HttpResponse>($ptr.payload_offset()) };
|
||||
let payload_ptr =
|
||||
unsafe { std::mem::transmute::<_, *mut HttpResponse>($ptr.payload_offset()) };
|
||||
#[allow(unused_mut)]
|
||||
let mut $listener = TypedArenaPtr::new(payload_ptr);
|
||||
#[allow(unused_braces)]
|
||||
@@ -297,7 +297,8 @@ macro_rules! match_untyped_arena_ptr_pat_body {
|
||||
}};
|
||||
($ptr:ident, IndexPtr, $ip:ident, $code:expr) => {{
|
||||
#[allow(unused_mut)]
|
||||
let mut $ip = TypedArenaPtr::new(unsafe { std::mem::transmute::<_, *mut IndexPtr>($ptr.get_ptr()) });
|
||||
let mut $ip =
|
||||
TypedArenaPtr::new(unsafe { std::mem::transmute::<_, *mut IndexPtr>($ptr.get_ptr()) });
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
}};
|
||||
@@ -315,7 +316,7 @@ macro_rules! match_untyped_arena_ptr_pat {
|
||||
| ArenaHeaderTag::NamedTcpStream
|
||||
| ArenaHeaderTag::NamedTlsStream
|
||||
| ArenaHeaderTag::HttpReadStream
|
||||
| ArenaHeaderTag::HttpWriteStream
|
||||
| ArenaHeaderTag::HttpWriteStream
|
||||
| ArenaHeaderTag::ReadlineStream
|
||||
| ArenaHeaderTag::StaticStringStream
|
||||
| ArenaHeaderTag::ByteStream
|
||||
@@ -323,10 +324,10 @@ macro_rules! match_untyped_arena_ptr_pat {
|
||||
| ArenaHeaderTag::StandardErrorStream
|
||||
};
|
||||
(IndexPtr) => {
|
||||
ArenaHeaderTag::IndexPtrUndefined |
|
||||
ArenaHeaderTag::IndexPtrDynamicUndefined |
|
||||
ArenaHeaderTag::IndexPtrDynamicIndex |
|
||||
ArenaHeaderTag::IndexPtrIndex
|
||||
ArenaHeaderTag::IndexPtrUndefined
|
||||
| ArenaHeaderTag::IndexPtrDynamicUndefined
|
||||
| ArenaHeaderTag::IndexPtrDynamicIndex
|
||||
| ArenaHeaderTag::IndexPtrIndex
|
||||
};
|
||||
($tag:ident) => {
|
||||
ArenaHeaderTag::$tag
|
||||
@@ -347,71 +348,71 @@ macro_rules! match_untyped_arena_ptr {
|
||||
}
|
||||
|
||||
macro_rules! read_heap_cell_pat_body {
|
||||
($cell:ident, Cons, $n:ident, $code:expr) => ({
|
||||
($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) => ({
|
||||
}};
|
||||
($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) => ({
|
||||
}};
|
||||
($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) => ({
|
||||
}};
|
||||
($cell:ident, PStr, $atom:ident, $code:expr) => {{
|
||||
let $atom = cell_as_atom!($cell);
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
});
|
||||
($cell:ident, CStr, $atom:ident, $code:expr) => ({
|
||||
}};
|
||||
($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) => ({
|
||||
}};
|
||||
($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) => ({
|
||||
}};
|
||||
($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) => ({
|
||||
}};
|
||||
($cell:ident, Fixnum, $value:ident, $code:expr) => {{
|
||||
let $value = Fixnum::from_bytes($cell.into_bytes());
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
});
|
||||
($cell:ident, CutPoint, $value:ident, $code:expr) => ({
|
||||
}};
|
||||
($cell:ident, CutPoint, $value:ident, $code:expr) => {{
|
||||
let $value = Fixnum::from_bytes($cell.into_bytes());
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
});
|
||||
($cell:ident, Fixnum | CutPoint, $value:ident, $code:expr) => ({
|
||||
}};
|
||||
($cell:ident, Fixnum | CutPoint, $value:ident, $code:expr) => {{
|
||||
let $value = Fixnum::from_bytes($cell.into_bytes());
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
});
|
||||
($cell:ident, CutPoint | Fixnum, $value:ident, $code:expr) => ({
|
||||
}};
|
||||
($cell:ident, CutPoint | Fixnum, $value:ident, $code:expr) => {{
|
||||
let $value = Fixnum::from_bytes($cell.into_bytes());
|
||||
#[allow(unused_braces)]
|
||||
$code
|
||||
});
|
||||
($cell:ident, Char, $value:ident, $code:expr) => ({
|
||||
}};
|
||||
($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) => ({
|
||||
}};
|
||||
($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 {
|
||||
@@ -596,7 +597,9 @@ macro_rules! index_store {
|
||||
IndexStore {
|
||||
code_dir: $code_dir,
|
||||
extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()),
|
||||
local_extensible_predicates: LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()),
|
||||
local_extensible_predicates: LocalExtensiblePredicates::with_hasher(
|
||||
FxBuildHasher::default(),
|
||||
),
|
||||
global_variables: GlobalVarDir::with_hasher(FxBuildHasher::default()),
|
||||
goal_expansion_indices: GoalExpansionIndices::with_hasher(FxBuildHasher::default()),
|
||||
meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()),
|
||||
|
||||
@@ -10,14 +10,15 @@ use std::hash::{Hash, Hasher};
|
||||
use std::io::{Error as IOError, ErrorKind};
|
||||
use std::ops::{Deref, Neg};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::vec::Vec;
|
||||
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexMap;
|
||||
use modular_bitfield::error::OutOfBounds;
|
||||
use modular_bitfield::prelude::*;
|
||||
use scryer_modular_bitfield::error::OutOfBounds;
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
pub type Specifier = u32;
|
||||
|
||||
@@ -33,6 +34,7 @@ pub const FY: u32 = 0x0080;
|
||||
pub const DELIMITER: u32 = 0x0100;
|
||||
pub const TERM: u32 = 0x1000;
|
||||
pub const LTERM: u32 = 0x3000;
|
||||
pub const BTERM: u32 = 0x11000;
|
||||
|
||||
pub const NEGATIVE_SIGN: u32 = 0x0200;
|
||||
|
||||
@@ -245,11 +247,7 @@ impl GenContext {
|
||||
|
||||
#[inline]
|
||||
pub fn is_last(self) -> bool {
|
||||
if let GenContext::Last(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, GenContext::Last(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +256,8 @@ impl GenContext {
|
||||
pub struct OpDesc {
|
||||
prec: B11,
|
||||
spec: B8,
|
||||
#[allow(unused)] padding: B13,
|
||||
#[allow(unused)]
|
||||
padding: B13,
|
||||
}
|
||||
|
||||
impl OpDesc {
|
||||
@@ -300,24 +299,16 @@ impl OpDesc {
|
||||
// name and fixity -> operator type and precedence.
|
||||
pub type OpDir = IndexMap<(Atom, Fixity), OpDesc, FxBuildHasher>;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct MachineFlags {
|
||||
pub double_quotes: DoubleQuotes,
|
||||
pub unknown: Unknown,
|
||||
}
|
||||
|
||||
impl Default for MachineFlags {
|
||||
fn default() -> Self {
|
||||
MachineFlags {
|
||||
double_quotes: DoubleQuotes::default(),
|
||||
unknown: Unknown::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub enum DoubleQuotes {
|
||||
Atom,
|
||||
#[default]
|
||||
Chars,
|
||||
Codes,
|
||||
}
|
||||
@@ -336,14 +327,9 @@ impl DoubleQuotes {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DoubleQuotes {
|
||||
fn default() -> Self {
|
||||
DoubleQuotes::Chars
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub enum Unknown {
|
||||
#[default]
|
||||
Error,
|
||||
Fail,
|
||||
Warn,
|
||||
@@ -363,13 +349,6 @@ impl Unknown {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Unknown {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Unknown::Error
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_op_dir() -> OpDir {
|
||||
let mut op_dir = OpDir::with_hasher(FxBuildHasher::default());
|
||||
|
||||
@@ -417,13 +396,13 @@ pub enum ParserError {
|
||||
impl ParserError {
|
||||
pub fn line_and_col_num(&self) -> Option<(usize, usize)> {
|
||||
match self {
|
||||
&ParserError::BackQuotedString(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::UnexpectedChar(_, line_num, col_num) |
|
||||
&ParserError::Utf8Error(line_num, col_num) => Some((line_num, col_num)),
|
||||
&ParserError::BackQuotedString(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::UnexpectedChar(_, line_num, col_num)
|
||||
| &ParserError::Utf8Error(line_num, col_num) => Some((line_num, col_num)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -432,8 +411,15 @@ impl ParserError {
|
||||
match self {
|
||||
ParserError::BackQuotedString(..) => atom!("back_quoted_string"),
|
||||
ParserError::IncompleteReduction(..) => atom!("incomplete_reduction"),
|
||||
ParserError::InvalidSingleQuotedCharacter(..) => atom!("invalid_single_quoted_character"),
|
||||
ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => atom!("unexpected_end_of_file"),
|
||||
ParserError::InvalidSingleQuotedCharacter(..) => {
|
||||
atom!("invalid_single_quoted_character")
|
||||
}
|
||||
ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => {
|
||||
atom!("unexpected_end_of_file")
|
||||
}
|
||||
ParserError::IO(e) if e.kind() == ErrorKind::InvalidData => {
|
||||
atom!("invalid_data")
|
||||
}
|
||||
ParserError::IO(_) => atom!("input_output_error"),
|
||||
ParserError::LexicalError(_) => atom!("lexical_error"),
|
||||
ParserError::MissingQuote(..) => atom!("missing_quote"),
|
||||
@@ -498,7 +484,7 @@ impl<'a, 'b> CompositeOpDir<'a, 'b> {
|
||||
|
||||
#[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 {
|
||||
let entry = if let Some(primary_op_dir) = &self.primary_op_dir {
|
||||
primary_op_dir.get(&(name, fixity))
|
||||
} else {
|
||||
None
|
||||
@@ -522,9 +508,12 @@ pub enum Fixity {
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Fixnum {
|
||||
num: B56,
|
||||
#[allow(unused)] f: bool,
|
||||
#[allow(unused)] m: bool,
|
||||
#[allow(unused)] tag: B6,
|
||||
#[allow(unused)]
|
||||
f: bool,
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
#[allow(unused)]
|
||||
tag: B6,
|
||||
}
|
||||
|
||||
impl Fixnum {
|
||||
@@ -548,7 +537,7 @@ impl Fixnum {
|
||||
|
||||
#[inline]
|
||||
pub fn get_tag(&self) -> HeapCellValueTag {
|
||||
use modular_bitfield::Specifier;
|
||||
use scryer_modular_bitfield::Specifier;
|
||||
HeapCellValueTag::from_bytes(self.tag()).unwrap()
|
||||
}
|
||||
|
||||
@@ -557,7 +546,7 @@ impl Fixnum {
|
||||
const UPPER_BOUND: i64 = (1 << 55) - 1;
|
||||
const LOWER_BOUND: i64 = -(1 << 55);
|
||||
|
||||
if LOWER_BOUND <= num && num <= UPPER_BOUND {
|
||||
if (LOWER_BOUND..=UPPER_BOUND).contains(&num) {
|
||||
Ok(Fixnum::new()
|
||||
.with_m(false)
|
||||
.with_f(false)
|
||||
@@ -572,7 +561,7 @@ impl Fixnum {
|
||||
pub fn get_num(self) -> i64 {
|
||||
let n = self.num() as i64;
|
||||
let (n, overflowed) = (n << 8).overflowing_shr(8);
|
||||
debug_assert_eq!(overflowed, false);
|
||||
debug_assert!(!overflowed);
|
||||
n
|
||||
}
|
||||
}
|
||||
@@ -623,7 +612,7 @@ impl fmt::Display for Literal {
|
||||
}
|
||||
|
||||
impl Literal {
|
||||
pub fn to_atom(&self, atom_tbl: &mut AtomTable) -> Option<Atom> {
|
||||
pub fn to_atom(&self, atom_tbl: &Arc<AtomTable>) -> Option<Atom> {
|
||||
match self {
|
||||
Literal::Atom(atom) => Some(atom.defrock_brackets(atom_tbl)),
|
||||
_ => None,
|
||||
@@ -631,7 +620,6 @@ impl Literal {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct VarPtr(Rc<RefCell<Var>>);
|
||||
|
||||
@@ -721,11 +709,12 @@ impl Var {
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
Var::Named(value) => Some(&value),
|
||||
Var::Named(value) => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
#[inline(always)]
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
@@ -791,21 +780,17 @@ impl Term {
|
||||
|
||||
#[inline]
|
||||
pub fn source_arity(terms: &[Term]) -> usize {
|
||||
if let Some(last_arg) = terms.last() {
|
||||
if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg {
|
||||
return terms.len() - 1;
|
||||
}
|
||||
if let Some(Term::Literal(_, Literal::CodeIndex(_))) = terms.last() {
|
||||
return terms.len() - 1;
|
||||
}
|
||||
|
||||
terms.len()
|
||||
}
|
||||
|
||||
fn unfold_by_str_once(term: &mut Term, s: Atom) -> Option<(Term, Term)> {
|
||||
pub(crate) fn unfold_by_str_once(term: &mut Term, s: Atom) -> Option<(Term, Term)> {
|
||||
if let Term::Clause(_, ref name, ref mut subterms) = term {
|
||||
if let Some(last_arg) = subterms.last() {
|
||||
if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg {
|
||||
subterms.pop();
|
||||
}
|
||||
if let Some(Term::Literal(_, Literal::CodeIndex(_))) = subterms.last() {
|
||||
subterms.pop();
|
||||
}
|
||||
|
||||
if name == &s && subterms.len() == 2 {
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::str;
|
||||
|
||||
pub struct CharReader<R> {
|
||||
inner: R,
|
||||
buf: SmallVec<[u8;32]>,
|
||||
buf: SmallVec<[u8; 32]>,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ pub trait CharRead {
|
||||
self.consume(c.len_utf8());
|
||||
Some(Ok(c))
|
||||
}
|
||||
result => result
|
||||
result => result,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,15 +131,9 @@ impl<R: Read> CharReader<R> {
|
||||
|
||||
pub fn peek_byte(&mut self) -> Option<io::Result<u8>> {
|
||||
match self.refresh_buffer() {
|
||||
Ok(_buf) => {}
|
||||
Err(e) => return Some(Err(e)),
|
||||
Ok(_buf) => _buf.first().cloned().map(Ok),
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
|
||||
return if let Some(b) = self.buf.get(0).cloned() {
|
||||
Some(Ok(b))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +144,35 @@ impl<R: Read> CharRead for CharReader<R> {
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
|
||||
let bad_bytes_error = |buf: &[u8]| {
|
||||
// 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.
|
||||
|
||||
io::Error::new(io::ErrorKind::InvalidData, BadUtf8Error { bytes: badbytes })
|
||||
};
|
||||
|
||||
loop {
|
||||
let buf = &self.buf[self.pos..];
|
||||
|
||||
@@ -161,79 +184,55 @@ impl<R: Read> CharRead for CharReader<R> {
|
||||
|
||||
return Some(Ok(c));
|
||||
}
|
||||
Err(e) => {
|
||||
e
|
||||
}
|
||||
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.
|
||||
return Some(Err(bad_bytes_error(buf)));
|
||||
} else if self.pos >= self.buf.len() {
|
||||
return None;
|
||||
} else if self.buf.len() - self.pos >= 4 && self.pos < e.valid_up_to() {
|
||||
return match str::from_utf8(&self.buf[self.pos..e.valid_up_to()]) {
|
||||
Ok(s) => {
|
||||
let mut chars = s.chars();
|
||||
let c = chars.next().unwrap();
|
||||
|
||||
// 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;
|
||||
Some(Ok(c))
|
||||
}
|
||||
Err(e) => {
|
||||
let badbytes = self.buf[self.pos..e.valid_up_to()].to_vec();
|
||||
|
||||
split_point += 1;
|
||||
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];
|
||||
}
|
||||
|
||||
// Raise the error. If we still have data in
|
||||
// the buffer, it will be returned on the next
|
||||
// loop.
|
||||
self.buf.truncate(buf_len - self.pos);
|
||||
|
||||
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[self.pos .. e.valid_up_to()]) {
|
||||
Ok(s) => {
|
||||
let mut chars = s.chars();
|
||||
let c = chars.next().unwrap();
|
||||
let buf_len = self.buf.len();
|
||||
self.pos = 0;
|
||||
|
||||
Some(Ok(c))
|
||||
}
|
||||
Err(e) => {
|
||||
let badbytes = self.buf[self.pos .. e.valid_up_to()].to_vec();
|
||||
if buf_len >= 4 {
|
||||
continue;
|
||||
}
|
||||
|
||||
Some(Err(io::Error::new(io::ErrorKind::InvalidData,
|
||||
BadUtf8Error { bytes: badbytes })))
|
||||
}
|
||||
};
|
||||
} else {
|
||||
let buf_len = self.buf.len();
|
||||
let mut word = [0u8; 4];
|
||||
let word_slice = &mut word[buf_len..4];
|
||||
|
||||
for (c, idx) in (self.pos..buf_len).enumerate() {
|
||||
self.buf[c] = self.buf[idx];
|
||||
match self.inner.read(word_slice) {
|
||||
Err(e) => return Some(Err(e)),
|
||||
Ok(0) => return Some(Err(bad_bytes_error(&self.buf))),
|
||||
Ok(nread) => {
|
||||
self.buf.extend_from_slice(&word_slice[0..nread]);
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -250,7 +249,7 @@ impl<R: Read> CharRead for CharReader<R> {
|
||||
let c_len = c.len_utf8();
|
||||
let mut shifted_slice = [0u8; 32];
|
||||
|
||||
shifted_slice[0..src_len].copy_from_slice(&self.buf[self.pos .. self.buf.len()]);
|
||||
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]);
|
||||
@@ -311,7 +310,10 @@ impl<R: Read> Read for CharReader<R> {
|
||||
}
|
||||
|
||||
if !buf.is_empty() {
|
||||
Err(io::Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
|
||||
Err(io::Error::new(
|
||||
ErrorKind::UnexpectedEof,
|
||||
"failed to fill whole buffer",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
@@ -364,18 +366,21 @@ where
|
||||
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()))
|
||||
.field(
|
||||
"buf",
|
||||
&format_args!("{}/{}", self.buf.capacity() - self.pos, self.buf.len()),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parser::char_reader::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn plain_string() {
|
||||
let mut read_string = CharReader::new(Cursor::new("a string"));
|
||||
|
||||
@@ -388,6 +393,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn greek_string() {
|
||||
let mut read_string = CharReader::new(Cursor::new("λέξη"));
|
||||
|
||||
@@ -400,6 +406,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn russian_string() {
|
||||
let mut read_string = CharReader::new(Cursor::new("слово"));
|
||||
|
||||
@@ -412,6 +419,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn greek_lorem_ipsum() {
|
||||
let lorem_ipsum = "Λορεμ ιπσθμ δολορ σιτ αμετ, οφφενδιτ
|
||||
εφφιcιενδι σιτ ει, ηαρθμ λεγερε qθαερενδθμ ιθσ νε. Ηασ νο εροσ
|
||||
@@ -483,6 +491,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn armenian_lorem_ipsum() {
|
||||
let lorem_ipsum = "լոռեմ իպսում դոլոռ սիթ ամեթ, նովում գռաեծո
|
||||
սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում
|
||||
@@ -556,6 +565,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
||||
fn russian_lorem_ipsum() {
|
||||
let lorem_ipsum = "Лорем ипсум долор сит амет, атяуи дицам еи
|
||||
сит, ид сеа фацилис елаборарет. Меа еу яуас алияуид, те яуи
|
||||
|
||||
@@ -48,11 +48,7 @@ pub enum Token {
|
||||
impl Token {
|
||||
#[inline]
|
||||
pub(super) fn is_end(&self) -> bool {
|
||||
if let Token::End = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Token::End)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,14 +82,14 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
pub fn lookahead_char(&mut self) -> Result<char, ParserError> {
|
||||
match self.reader.peek_char() {
|
||||
Some(Ok(c)) => Ok(c),
|
||||
_ => Err(ParserError::unexpected_eof())
|
||||
_ => Err(ParserError::unexpected_eof()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_char(&mut self) -> Result<char, ParserError> {
|
||||
match self.reader.read_char() {
|
||||
Some(Ok(c)) => Ok(c),
|
||||
_ => Err(ParserError::unexpected_eof())
|
||||
_ => Err(ParserError::unexpected_eof()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,13 +164,15 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
|
||||
match comment_loop() {
|
||||
Err(e) if e.is_unexpected_eof() => {
|
||||
return Err(ParserError::IncompleteReduction(self.line_num, self.col_num));
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.line_num,
|
||||
self.col_num,
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
Ok(_) => {
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
|
||||
if prolog_char!(c) {
|
||||
@@ -362,7 +360,10 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
if hexadecimal_digit_char!(c) {
|
||||
self.escape_sequence_to_char(|c| hexadecimal_digit_char!(c), 16)
|
||||
} else {
|
||||
Err(ParserError::IncompleteReduction(self.line_num, self.col_num))
|
||||
Err(ParserError::IncompleteReduction(
|
||||
self.line_num,
|
||||
self.col_num,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,7 +396,10 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Err(ParserError::IncompleteReduction(self.line_num, self.col_num))
|
||||
Err(ParserError::IncompleteReduction(
|
||||
self.line_num,
|
||||
self.col_num,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,9 +467,12 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
Integer::from_str_radix(&token, 16)
|
||||
.map(|n| Token::Literal(Literal::Integer(
|
||||
arena_alloc!(n, &mut self.machine_st.arena)
|
||||
)))
|
||||
.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 {
|
||||
@@ -495,9 +502,12 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
Integer::from_str_radix(&token, 8)
|
||||
.map(|n| Token::Literal(Literal::Integer(
|
||||
arena_alloc!(n, &mut self.machine_st.arena)
|
||||
)))
|
||||
.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 {
|
||||
@@ -527,9 +537,12 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
Integer::from_str_radix(&token, 2)
|
||||
.map(|n| Token::Literal(Literal::Integer(
|
||||
arena_alloc!(n, &mut self.machine_st.arena)
|
||||
)))
|
||||
.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 {
|
||||
@@ -587,10 +600,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if cut_char!(c) {
|
||||
self.skip_char(c);
|
||||
token.push(c);
|
||||
} else if semicolon_char!(c) {
|
||||
} else if cut_char!(c) || semicolon_char!(c) {
|
||||
self.skip_char(c);
|
||||
token.push(c);
|
||||
} else if single_quote_char!(c) {
|
||||
@@ -620,16 +630,20 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
if token.as_str() == "[]" {
|
||||
Ok(Token::Literal(Literal::Atom(atom!("[]"))))
|
||||
} else {
|
||||
Ok(Token::Literal(Literal::Atom(
|
||||
self.machine_st.atom_tbl.build_with(&token),
|
||||
)))
|
||||
Ok(Token::Literal(Literal::Atom(AtomTable::build_with(
|
||||
&self.machine_st.atom_tbl,
|
||||
&token,
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
fn vacate_with_float(&mut self, mut token: String) -> Result<Token, ParserError> {
|
||||
self.return_char(token.pop().unwrap());
|
||||
let n = parse_lossy::<f64, _>(token.as_bytes())?;
|
||||
Ok(Token::Literal(Literal::from(float_alloc!(n, self.machine_st.arena))))
|
||||
Ok(Token::Literal(Literal::from(float_alloc!(
|
||||
n,
|
||||
self.machine_st.arena
|
||||
))))
|
||||
}
|
||||
|
||||
fn skip_underscore_in_number(&mut self) -> Result<char, ParserError> {
|
||||
@@ -669,13 +683,17 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
if self.reader.peek_char().is_none() {
|
||||
self.return_char('.');
|
||||
|
||||
i64::from_str_radix(&token, 10)
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(n, &mut self.machine_st.arena)))
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
})
|
||||
@@ -696,12 +714,12 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
token.push(c);
|
||||
|
||||
let c = match self.lookahead_char() {
|
||||
Err(_) => return Ok(self.vacate_with_float(token)?),
|
||||
Err(_) => return self.vacate_with_float(token),
|
||||
Ok(c) => c,
|
||||
};
|
||||
|
||||
if !sign_char!(c) && !decimal_digit_char!(c) {
|
||||
return Ok(self.vacate_with_float(token)?);
|
||||
return self.vacate_with_float(token);
|
||||
}
|
||||
|
||||
if sign_char!(c) {
|
||||
@@ -711,14 +729,14 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
let c = match self.lookahead_char() {
|
||||
Err(_) => {
|
||||
self.return_char(token.pop().unwrap());
|
||||
return Ok(self.vacate_with_float(token)?);
|
||||
return 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 self.vacate_with_float(token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,172 +758,192 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
}
|
||||
|
||||
let n = parse_lossy::<f64, _>(token.as_bytes())?;
|
||||
Ok(Token::Literal(Literal::from(
|
||||
float_alloc!(n, self.machine_st.arena)
|
||||
)))
|
||||
Ok(Token::Literal(Literal::from(float_alloc!(
|
||||
n,
|
||||
self.machine_st.arena
|
||||
))))
|
||||
} else {
|
||||
return Ok(self.vacate_with_float(token)?);
|
||||
return self.vacate_with_float(token);
|
||||
}
|
||||
} else {
|
||||
let n = parse_lossy::<f64, _>(token.as_bytes())?;
|
||||
Ok(Token::Literal(Literal::from(
|
||||
float_alloc!(n, self.machine_st.arena)
|
||||
)))
|
||||
Ok(Token::Literal(Literal::from(float_alloc!(
|
||||
n,
|
||||
self.machine_st.arena
|
||||
))))
|
||||
}
|
||||
} else {
|
||||
self.return_char('.');
|
||||
|
||||
i64::from_str_radix(&token, 10)
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(n, &mut self.machine_st.arena)))
|
||||
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(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.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 {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if c == 'o' {
|
||||
self.octal_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.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 {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if c == 'b' {
|
||||
self.binary_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.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 {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if single_quote_char!(c) {
|
||||
self.skip_char(c);
|
||||
let c = self.lookahead_char()?;
|
||||
|
||||
if backslash_char!(c) {
|
||||
self.skip_char(c);
|
||||
let c = self.lookahead_char()?;
|
||||
|
||||
if new_line_char!(c) {
|
||||
self.skip_char(c);
|
||||
self.return_char('\'');
|
||||
|
||||
return Ok(Token::Literal(Literal::Fixnum(Fixnum::build_with(0))));
|
||||
} else {
|
||||
self.return_char('\\');
|
||||
}
|
||||
}
|
||||
|
||||
self.get_single_quoted_char()
|
||||
.map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64))))
|
||||
.or_else(|err| {
|
||||
match err {
|
||||
ParserError::UnexpectedChar('\'', ..) => {}
|
||||
err => return Err(err),
|
||||
}
|
||||
|
||||
self.return_char(c);
|
||||
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| {
|
||||
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
|
||||
})
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.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 {
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.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(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
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::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if c == 'o' {
|
||||
self.octal_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
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::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if c == 'b' {
|
||||
self.binary_constant(c).or_else(|e| {
|
||||
if let ParserError::ParseBigInt(..) = e {
|
||||
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::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
})
|
||||
} else if single_quote_char!(c) {
|
||||
self.skip_char(c);
|
||||
let c = self.lookahead_char()?;
|
||||
|
||||
if backslash_char!(c) {
|
||||
self.skip_char(c);
|
||||
let c = self.lookahead_char()?;
|
||||
|
||||
if new_line_char!(c) {
|
||||
self.skip_char(c);
|
||||
self.return_char('\'');
|
||||
|
||||
return Ok(Token::Literal(Literal::Fixnum(Fixnum::build_with(0))));
|
||||
} else {
|
||||
self.return_char('\\');
|
||||
}
|
||||
}
|
||||
|
||||
self.get_single_quoted_char()
|
||||
.map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64))))
|
||||
.or_else(|err| {
|
||||
match err {
|
||||
ParserError::UnexpectedChar('\'', ..) => {
|
||||
}
|
||||
err => return Err(err),
|
||||
}
|
||||
|
||||
self.return_char(c);
|
||||
|
||||
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::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| {
|
||||
ParserError::ParseBigInt(self.line_num, self.col_num)
|
||||
})
|
||||
})
|
||||
token
|
||||
.parse::<i64>()
|
||||
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
|
||||
.or_else(|_| {
|
||||
token
|
||||
.parse::<Integer>()
|
||||
.map(|n| {
|
||||
Token::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
} else {
|
||||
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::Literal(Literal::Integer(arena_alloc!(
|
||||
n,
|
||||
&mut self.machine_st.arena
|
||||
)))
|
||||
})
|
||||
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
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::Literal(Literal::Integer(arena_alloc!(n, &mut self.machine_st.arena)))
|
||||
})
|
||||
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
})
|
||||
}
|
||||
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,6 +952,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
c: Option<char>,
|
||||
layout_info: &mut LayoutInfo,
|
||||
) -> Result<(), ParserError> {
|
||||
#[allow(clippy::redundant_guards)]
|
||||
match c {
|
||||
Some(c) if layout_char!(c) => {
|
||||
self.skip_char(c);
|
||||
@@ -940,11 +979,12 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
|
||||
pub fn scan_for_layout(&mut self) -> Result<bool, ParserError> {
|
||||
match self.lookahead_char() {
|
||||
Err(e) => {
|
||||
Err(e)
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
Ok(c) => {
|
||||
let mut layout_info = LayoutInfo { inserted: false, more: true };
|
||||
let mut layout_info = LayoutInfo {
|
||||
inserted: false,
|
||||
more: true,
|
||||
};
|
||||
let mut cr = Some(c);
|
||||
|
||||
loop {
|
||||
@@ -1044,7 +1084,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
|
||||
if c == '"' {
|
||||
let s = self.char_code_list_token(c)?;
|
||||
let atom = self.machine_st.atom_tbl.build_with(&s);
|
||||
let atom = AtomTable::build_with(&self.machine_st.atom_tbl, &s);
|
||||
|
||||
return if let DoubleQuotes::Atom = self.machine_st.flags.double_quotes {
|
||||
Ok(Token::Literal(Literal::Atom(atom)))
|
||||
@@ -1054,7 +1094,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
|
||||
}
|
||||
|
||||
if c == '\u{0}' {
|
||||
return Err(ParserError::unexpected_eof())
|
||||
return Err(ParserError::unexpected_eof());
|
||||
}
|
||||
|
||||
self.name_token(c)
|
||||
|
||||
@@ -7,13 +7,14 @@ macro_rules! char_class {
|
||||
#[macro_export]
|
||||
macro_rules! alpha_char {
|
||||
($c: expr) => {
|
||||
(!$c.is_numeric() &&
|
||||
!$c.is_whitespace() &&
|
||||
!$c.is_control() &&
|
||||
!$crate::graphic_token_char!($c) &&
|
||||
!$crate::layout_char!($c) &&
|
||||
!$crate::meta_char!($c) &&
|
||||
!$crate::solo_char!($c)) || $c == '_'
|
||||
(!$c.is_numeric()
|
||||
&& !$c.is_whitespace()
|
||||
&& !$c.is_control()
|
||||
&& !$crate::graphic_token_char!($c)
|
||||
&& !$crate::layout_char!($c)
|
||||
&& !$crate::meta_char!($c)
|
||||
&& !$crate::solo_char!($c))
|
||||
|| $c == '_'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -76,7 +77,7 @@ macro_rules! cut_char {
|
||||
#[macro_export]
|
||||
macro_rules! decimal_digit_char {
|
||||
($c: expr) => {
|
||||
('0'..='9').contains(&$c)
|
||||
$c.is_ascii_digit()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -124,14 +125,14 @@ macro_rules! graphic_token_char {
|
||||
#[macro_export]
|
||||
macro_rules! hexadecimal_digit_char {
|
||||
($c: expr) => {
|
||||
('0'..='9').contains(&$c) || ('A'..='F').contains(&$c) || ('a'..='f').contains(&$c)
|
||||
$c.is_ascii_digit() || ('A'..='F').contains(&$c) || ('a'..='f').contains(&$c)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! layout_char {
|
||||
($c: expr) => {
|
||||
$crate::char_class!($c, [' ', '\n', '\t', '\u{0B}', '\u{0C}'])
|
||||
$crate::char_class!($c, [' ', '\r', '\n', '\t', '\u{0B}', '\u{0C}'])
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,4 +11,5 @@ pub mod ast;
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub mod lexer;
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod parser;
|
||||
|
||||
@@ -7,7 +7,6 @@ use crate::parser::ast::*;
|
||||
use crate::parser::char_reader::*;
|
||||
use crate::parser::lexer::*;
|
||||
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::mem;
|
||||
use std::ops::Neg;
|
||||
@@ -59,6 +58,7 @@ struct TokenDesc {
|
||||
tt: TokenType,
|
||||
priority: usize,
|
||||
spec: u32,
|
||||
unfold_bounds: usize,
|
||||
}
|
||||
|
||||
pub(crate) fn as_partial_string(
|
||||
@@ -97,7 +97,12 @@ pub(crate) fn as_partial_string(
|
||||
string.push(*c);
|
||||
}
|
||||
_ => {
|
||||
return Err(Term::Cons(Cell::default(), Box::new(head), orig_tail));
|
||||
tail = Term::Cons(
|
||||
Cell::default(),
|
||||
Box::new((**prev).clone()),
|
||||
Box::new((**succ).clone()),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +113,7 @@ pub(crate) fn as_partial_string(
|
||||
tail_ref = tail;
|
||||
}
|
||||
Term::CompleteString(_, cstr) => {
|
||||
string += cstr.as_str();
|
||||
string += &*cstr.as_str();
|
||||
tail = Term::Literal(Cell::default(), Literal::Atom(atom!("[]")));
|
||||
break;
|
||||
}
|
||||
@@ -120,26 +125,17 @@ pub(crate) fn as_partial_string(
|
||||
}
|
||||
|
||||
match &tail {
|
||||
Term::AnonVar | Term::Var(..) => {
|
||||
Ok((string, Some(Box::new(tail))))
|
||||
}
|
||||
Term::Literal(_, Literal::Atom(atom!("[]"))) => {
|
||||
Ok((string, None))
|
||||
}
|
||||
Term::AnonVar | Term::Var(..) => Ok((string, Some(Box::new(tail)))),
|
||||
Term::Literal(_, Literal::Atom(atom!("[]"))) => Ok((string, None)),
|
||||
Term::Literal(_, Literal::String(tail)) => {
|
||||
string += tail.as_str();
|
||||
string += &*tail.as_str();
|
||||
Ok((string, None))
|
||||
}
|
||||
_ => {
|
||||
Ok((string, Some(Box::new(tail))))
|
||||
}
|
||||
_ => Ok((string, Some(Box::new(tail)))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_op_desc(
|
||||
name: Atom,
|
||||
op_dir: &CompositeOpDir,
|
||||
) -> Option<CompositeOpDesc> {
|
||||
pub fn get_op_desc(name: Atom, op_dir: &CompositeOpDir) -> Option<CompositeOpDesc> {
|
||||
let mut op_desc = CompositeOpDesc {
|
||||
pre: 0,
|
||||
inf: 0,
|
||||
@@ -295,17 +291,17 @@ fn read_tokens<R: CharRead>(lexer: &mut Lexer<R>) -> Result<Vec<Token>, ParserEr
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
fn atomize_term(atom_tbl: &mut AtomTable, term: &Term) -> Option<Atom> {
|
||||
fn atomize_term(atom_tbl: &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> {
|
||||
fn atomize_constant(atom_tbl: &AtomTable, c: Literal) -> Option<Atom> {
|
||||
match c {
|
||||
Literal::Atom(ref name) => Some(*name),
|
||||
Literal::Char(c) => Some(atom_tbl.build_with(&c.to_string())),
|
||||
Literal::Char(c) => Some(AtomTable::build_with(atom_tbl, &c.to_string())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -381,6 +377,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
tt: TokenType::Term,
|
||||
priority: td.priority,
|
||||
spec,
|
||||
unfold_bounds: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -402,6 +399,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
tt: TokenType::Term,
|
||||
priority: td.priority,
|
||||
spec,
|
||||
unfold_bounds: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -409,17 +407,21 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
}
|
||||
|
||||
fn promote_atom_op(&mut self, atom: Atom, priority: usize, assoc: u32) {
|
||||
self.terms.push(Term::Literal(Cell::default(), Literal::Atom(atom)));
|
||||
self.terms
|
||||
.push(Term::Literal(Cell::default(), Literal::Atom(atom)));
|
||||
self.stack.push(TokenDesc {
|
||||
tt: TokenType::Term,
|
||||
priority,
|
||||
spec: assoc,
|
||||
unfold_bounds: 0,
|
||||
});
|
||||
}
|
||||
|
||||
fn shift(&mut self, token: Token, priority: usize, spec: Specifier) {
|
||||
let tt = match token {
|
||||
Token::Literal(Literal::String(s)) if self.lexer.machine_st.flags.double_quotes.is_codes() => {
|
||||
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.as_str().chars().rev() {
|
||||
@@ -436,7 +438,9 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
self.terms.push(list);
|
||||
TokenType::Term
|
||||
}
|
||||
Token::Literal(Literal::String(s)) if self.lexer.machine_st.flags.double_quotes.is_chars() => {
|
||||
Token::Literal(Literal::String(s))
|
||||
if self.lexer.machine_st.flags.double_quotes.is_chars() =>
|
||||
{
|
||||
self.terms.push(Term::CompleteString(Cell::default(), s));
|
||||
TokenType::Term
|
||||
}
|
||||
@@ -465,7 +469,12 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
Token::End => TokenType::End,
|
||||
};
|
||||
|
||||
self.stack.push(TokenDesc { tt, priority, spec });
|
||||
self.stack.push(TokenDesc {
|
||||
tt,
|
||||
priority,
|
||||
spec,
|
||||
unfold_bounds: 0,
|
||||
});
|
||||
}
|
||||
|
||||
fn reduce_op(&mut self, priority: usize) {
|
||||
@@ -473,10 +482,9 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
if let Some(desc1) = self.stack.pop() {
|
||||
if let Some(desc2) = self.stack.pop() {
|
||||
if let Some(desc3) = self.stack.pop() {
|
||||
if is_xfx!(desc2.spec) && affirm_xfx(priority, desc2, desc3, desc1) {
|
||||
self.push_binary_op(desc2, LTERM);
|
||||
continue;
|
||||
} else if is_yfx!(desc2.spec) && affirm_yfx(priority, desc2, desc3, desc1) {
|
||||
if is_xfx!(desc2.spec) && affirm_xfx(priority, desc2, desc3, desc1)
|
||||
|| is_yfx!(desc2.spec) && affirm_yfx(priority, desc2, desc3, desc1)
|
||||
{
|
||||
self.push_binary_op(desc2, LTERM);
|
||||
continue;
|
||||
} else if is_xfy!(desc2.spec) && affirm_xfy(priority, desc2, desc3, desc1) {
|
||||
@@ -556,10 +564,12 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
if self.stack.len() > 2 * arity {
|
||||
let idx = self.stack.len() - 2 * arity - 1;
|
||||
|
||||
if is_infix!(self.stack[idx].spec) && idx > 0 {
|
||||
if !is_op!(self.stack[idx - 1].spec) && !self.stack[idx - 1].tt.is_sep() {
|
||||
return false;
|
||||
}
|
||||
if is_infix!(self.stack[idx].spec)
|
||||
&& idx > 0
|
||||
&& !is_op!(self.stack[idx - 1].spec)
|
||||
&& !self.stack[idx - 1].tt.is_sep()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
@@ -572,51 +582,57 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
let stack_len = self.stack.len() - 2 * arity - 1;
|
||||
let idx = self.terms.len() - arity;
|
||||
|
||||
if TokenType::Term == self.stack[stack_len].tt {
|
||||
if atomize_term(&mut self.lexer.machine_st.atom_tbl, &self.terms[idx - 1]).is_some() {
|
||||
self.stack.truncate(stack_len + 1);
|
||||
if TokenType::Term == self.stack[stack_len].tt
|
||||
&& atomize_term(&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..).collect();
|
||||
let mut subterms: Vec<_> = self.terms.drain(idx..).collect();
|
||||
|
||||
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 == atom!(".") && subterms.len() == 2 {
|
||||
let tail = subterms.pop().unwrap();
|
||||
let head = subterms.pop().unwrap();
|
||||
if let Some(name) = self
|
||||
.terms
|
||||
.pop()
|
||||
.and_then(|t| atomize_term(&self.lexer.machine_st.atom_tbl, &t))
|
||||
{
|
||||
// reduce the '.' functor to a cons cell if it applies.
|
||||
if name == atom!(".") && subterms.len() == 2 {
|
||||
let tail = subterms.pop().unwrap();
|
||||
let head = subterms.pop().unwrap();
|
||||
|
||||
self.terms.push(
|
||||
match as_partial_string(head, tail) {
|
||||
Ok((string_buf, Some(tail))) => {
|
||||
Term::PartialString(Cell::default(), string_buf, tail)
|
||||
}
|
||||
Ok((string_buf, None)) => {
|
||||
let atom = self.lexer.machine_st.atom_tbl.build_with(&string_buf);
|
||||
Term::CompleteString(Cell::default(), atom)
|
||||
}
|
||||
Err(term) => term,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
self.terms.push(Term::Clause(Cell::default(), name, subterms));
|
||||
}
|
||||
|
||||
if let Some(&mut TokenDesc {
|
||||
ref mut priority,
|
||||
ref mut spec,
|
||||
ref mut tt,
|
||||
}) = self.stack.last_mut()
|
||||
{
|
||||
*tt = TokenType::Term;
|
||||
*priority = 0;
|
||||
*spec = TERM;
|
||||
}
|
||||
|
||||
return true;
|
||||
self.terms.push(match as_partial_string(head, tail) {
|
||||
Ok((string_buf, Some(tail))) => {
|
||||
Term::PartialString(Cell::default(), string_buf, tail)
|
||||
}
|
||||
Ok((string_buf, None)) => {
|
||||
let atom =
|
||||
AtomTable::build_with(&self.lexer.machine_st.atom_tbl, &string_buf);
|
||||
Term::CompleteString(Cell::default(), atom)
|
||||
}
|
||||
Err(term) => term,
|
||||
});
|
||||
} else {
|
||||
self.terms
|
||||
.push(Term::Clause(Cell::default(), name, subterms));
|
||||
}
|
||||
|
||||
if let Some(&mut TokenDesc {
|
||||
ref mut tt,
|
||||
ref mut priority,
|
||||
ref mut spec,
|
||||
ref mut unfold_bounds,
|
||||
}) = self.stack.last_mut()
|
||||
{
|
||||
if *spec == BTERM {
|
||||
return false;
|
||||
}
|
||||
|
||||
*tt = TokenType::Term;
|
||||
*priority = 0;
|
||||
*spec = TERM;
|
||||
*unfold_bounds = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,22 +644,38 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
}
|
||||
|
||||
fn expand_comma_compacted_terms(&mut self, index: usize) -> usize {
|
||||
if let Some(term) = self.terms.pop() {
|
||||
let op_desc = self.stack[index - 1];
|
||||
if let Some(mut term) = self.terms.pop() {
|
||||
let mut op_desc = self.stack[index - 1];
|
||||
|
||||
if 0 < op_desc.priority && op_desc.priority < self.stack[index].priority {
|
||||
/* '|' is a head-tail separator here, not
|
||||
* an operator, so expand the
|
||||
* terms it compacted out again. */
|
||||
match (term.name(), term.arity()) {
|
||||
(Some(name), 2) if name == atom!(",") => {
|
||||
let terms = unfold_by_str(term, name); // notice: name == "," here.
|
||||
let arity = terms.len() - 1;
|
||||
if let (Some(atom!(",")), 2) = (term.name(), term.arity()) {
|
||||
let terms = if op_desc.unfold_bounds == 0 {
|
||||
unfold_by_str(term, atom!(","))
|
||||
} else {
|
||||
let mut terms = vec![];
|
||||
|
||||
self.terms.extend(terms.into_iter());
|
||||
return arity;
|
||||
}
|
||||
_ => {}
|
||||
while let Some((fst, snd)) = unfold_by_str_once(&mut term, atom!(",")) {
|
||||
terms.push(fst);
|
||||
term = snd;
|
||||
|
||||
op_desc.unfold_bounds -= 2;
|
||||
|
||||
if op_desc.unfold_bounds == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
terms.push(term);
|
||||
terms
|
||||
};
|
||||
|
||||
let arity = terms.len() - 1;
|
||||
|
||||
self.terms.extend(terms);
|
||||
return arity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,18 +698,15 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
if desc.tt == TokenType::HeadTailSeparator {
|
||||
if arity == 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
return None;
|
||||
} else if desc.tt == TokenType::OpenList {
|
||||
return Some(arity);
|
||||
} else if desc.tt != TokenType::Comma {
|
||||
return None;
|
||||
} else if desc.tt == TokenType::HeadTailSeparator {
|
||||
if arity == 1 {
|
||||
continue;
|
||||
}
|
||||
return None;
|
||||
} else if desc.tt == TokenType::OpenList {
|
||||
return Some(arity);
|
||||
} else if desc.tt != TokenType::Comma {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,7 +724,8 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
td.tt = TokenType::Term;
|
||||
td.priority = 0;
|
||||
|
||||
self.terms.push(Term::Literal(Cell::default(), Literal::Atom(atom!("[]"))));
|
||||
self.terms
|
||||
.push(Term::Literal(Cell::default(), Literal::Atom(atom!("[]"))));
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
@@ -736,8 +766,8 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
if arity > self.terms.len() {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num
|
||||
))
|
||||
self.lexer.col_num,
|
||||
));
|
||||
}
|
||||
|
||||
let idx = self.terms.len() - arity;
|
||||
@@ -752,21 +782,20 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
tt: TokenType::Term,
|
||||
priority: 0,
|
||||
spec: TERM,
|
||||
unfold_bounds: 0,
|
||||
});
|
||||
|
||||
self.terms.push(match list {
|
||||
Term::Cons(_, head, tail) => {
|
||||
match as_partial_string(*head, *tail) {
|
||||
Ok((string_buf, Some(tail))) => {
|
||||
Term::PartialString(Cell::default(), string_buf, tail)
|
||||
}
|
||||
Ok((string_buf, None)) => {
|
||||
let atom = self.lexer.machine_st.atom_tbl.build_with(&string_buf);
|
||||
Term::CompleteString(Cell::default(), atom)
|
||||
}
|
||||
Err(term) => term,
|
||||
Term::Cons(_, head, tail) => match as_partial_string(*head, *tail) {
|
||||
Ok((string_buf, Some(tail))) => {
|
||||
Term::PartialString(Cell::default(), string_buf, tail)
|
||||
}
|
||||
}
|
||||
Ok((string_buf, None)) => {
|
||||
let atom = AtomTable::build_with(&self.lexer.machine_st.atom_tbl, &string_buf);
|
||||
Term::CompleteString(Cell::default(), atom)
|
||||
}
|
||||
Err(term) => term,
|
||||
},
|
||||
term => term,
|
||||
});
|
||||
|
||||
@@ -784,10 +813,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
td.priority = 0;
|
||||
td.spec = TERM;
|
||||
|
||||
let term = Term::Literal(
|
||||
Cell::default(),
|
||||
Literal::Atom(atom!("{}")),
|
||||
);
|
||||
let term = Term::Literal(Cell::default(), Literal::Atom(atom!("{}")));
|
||||
|
||||
self.terms.push(term);
|
||||
return Ok(true);
|
||||
@@ -818,11 +844,8 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
}
|
||||
};
|
||||
|
||||
self.terms.push(Term::Clause(
|
||||
Cell::default(),
|
||||
atom!("{}"),
|
||||
vec![term],
|
||||
));
|
||||
self.terms
|
||||
.push(Term::Clause(Cell::default(), atom!("{}"), vec![term]));
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
@@ -862,7 +885,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
.push(Term::Literal(Cell::default(), Literal::Atom(atom)));
|
||||
}
|
||||
|
||||
self.stack[idx].spec = TERM;
|
||||
self.stack[idx].spec = BTERM;
|
||||
self.stack[idx].tt = TokenType::Term;
|
||||
self.stack[idx].priority = 0;
|
||||
|
||||
@@ -933,7 +956,8 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
if let Some(term) = self.terms.last().cloned() {
|
||||
match term {
|
||||
Term::Literal(_, Literal::Atom(name))
|
||||
if name == atom!("-") && (is_prefix!(desc.spec) || is_negate!(desc.spec)) =>
|
||||
if name == atom!("-")
|
||||
&& (is_prefix!(desc.spec) || is_negate!(desc.spec)) =>
|
||||
{
|
||||
self.stack.pop();
|
||||
self.terms.pop();
|
||||
@@ -983,7 +1007,9 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
|n, arena| Literal::from(float_alloc!(n, arena)),
|
||||
),
|
||||
Token::Literal(c) => {
|
||||
if let Some(name) = atomize_constant(&mut self.lexer.machine_st.atom_tbl, c) {
|
||||
let atomized = atomize_constant(&self.lexer.machine_st.atom_tbl, c);
|
||||
|
||||
if let Some(name) = atomized {
|
||||
if !self.shift_op(name, op_dir)? {
|
||||
self.shift(Token::Literal(c), 0, TERM);
|
||||
}
|
||||
@@ -995,13 +1021,11 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
Token::Open => self.shift(Token::Open, 1300, DELIMITER),
|
||||
Token::OpenCT => self.shift(Token::OpenCT, 1300, DELIMITER),
|
||||
Token::Close => {
|
||||
if !self.reduce_term() {
|
||||
if !self.reduce_brackets() {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
}
|
||||
if !self.reduce_term() && !self.reduce_brackets() {
|
||||
return Err(ParserError::IncompleteReduction(
|
||||
self.lexer.line_num,
|
||||
self.lexer.col_num,
|
||||
));
|
||||
}
|
||||
}
|
||||
Token::OpenList => self.shift(Token::OpenList, 1300, DELIMITER),
|
||||
@@ -1026,13 +1050,20 @@ impl<'a, R: CharRead> 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 bar_atom = atom!("|");
|
||||
|
||||
let (priority, spec) = get_op_desc(bar_atom, op_dir)
|
||||
let (priority, spec) = get_op_desc(atom!("|"), op_dir)
|
||||
.map(|CompositeOpDesc { inf, spec, .. }| (inf, spec))
|
||||
.unwrap_or((1000, DELIMITER));
|
||||
|
||||
let old_stack_len = self.stack.len();
|
||||
|
||||
self.reduce_op(priority);
|
||||
|
||||
let new_stack_len = self.stack.len();
|
||||
|
||||
if let Some(term_desc) = self.stack.last_mut() {
|
||||
term_desc.unfold_bounds = old_stack_len - new_stack_len;
|
||||
}
|
||||
|
||||
self.shift(Token::HeadTailSeparator, priority, spec);
|
||||
}
|
||||
Token::Comma => {
|
||||
@@ -1069,7 +1100,11 @@ impl<'a, R: CharRead> Parser<'a, R> {
|
||||
}
|
||||
|
||||
// on success, returns the parsed term and the number of lines read.
|
||||
pub fn read_term(&mut self, op_dir: &CompositeOpDir, tokens: Tokens) -> Result<Term, ParserError> {
|
||||
pub fn read_term(
|
||||
&mut self,
|
||||
op_dir: &CompositeOpDir,
|
||||
tokens: Tokens,
|
||||
) -> Result<Term, ParserError> {
|
||||
self.tokens = match tokens {
|
||||
Tokens::Default => read_tokens(&mut self.lexer)?,
|
||||
Tokens::Provided(tokens) => tokens,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use std::alloc;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ptr;
|
||||
|
||||
pub trait RawBlockTraits {
|
||||
@@ -12,7 +13,7 @@ pub trait RawBlockTraits {
|
||||
pub struct RawBlock<T: RawBlockTraits> {
|
||||
pub base: *const u8,
|
||||
pub top: *const u8,
|
||||
pub ptr: *mut u8,
|
||||
pub ptr: UnsafeCell<*mut u8>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
@@ -22,11 +23,12 @@ impl<T: RawBlockTraits> RawBlock<T> {
|
||||
RawBlock {
|
||||
base: ptr::null(),
|
||||
top: ptr::null(),
|
||||
ptr: ptr::null_mut(),
|
||||
ptr: UnsafeCell::new(ptr::null_mut()),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
let mut block = Self::empty_block();
|
||||
|
||||
@@ -41,8 +43,8 @@ impl<T: RawBlockTraits> RawBlock<T> {
|
||||
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 _;
|
||||
self.top = self.base.add(cap);
|
||||
*self.ptr.get_mut() = self.base as *mut _;
|
||||
}
|
||||
|
||||
pub unsafe fn grow(&mut self) {
|
||||
@@ -54,7 +56,25 @@ impl<T: RawBlockTraits> RawBlock<T> {
|
||||
|
||||
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 _;
|
||||
*self.ptr.get_mut() = (self.base as usize + size) as *mut _;
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn grow_new(&self) -> Option<Self> {
|
||||
if self.base.is_null() {
|
||||
Some(Self::new())
|
||||
} else {
|
||||
let mut new_block = Self::empty_block();
|
||||
new_block.init_at_size(self.size() * 2);
|
||||
if new_block.base.is_null() {
|
||||
// allocation failed
|
||||
None
|
||||
} else {
|
||||
let allocated = (*self.ptr.get()) as usize - self.base as usize;
|
||||
self.base.copy_to(new_block.base.cast_mut(), allocated);
|
||||
*new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut();
|
||||
Some(new_block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,35 +84,39 @@ impl<T: RawBlockTraits> RawBlock<T> {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn free_space(&self) -> usize {
|
||||
unsafe fn free_space(&self) -> usize {
|
||||
debug_assert!(
|
||||
self.ptr as *const _ >= self.base,
|
||||
*self.ptr.get() as *const _ >= self.base,
|
||||
"self.ptr = {:?} < {:?} = self.base",
|
||||
self.ptr,
|
||||
*self.ptr.get(),
|
||||
self.base
|
||||
);
|
||||
|
||||
self.top as usize - self.ptr as usize
|
||||
self.top as usize - (*self.ptr.get()) as usize
|
||||
}
|
||||
|
||||
pub unsafe fn alloc(&mut self, size: usize) -> *mut u8 {
|
||||
pub unsafe fn alloc(&self, size: usize) -> *mut u8 {
|
||||
if self.free_space() >= size {
|
||||
let ptr = self.ptr;
|
||||
self.ptr = (self.ptr as usize + size) as *mut _;
|
||||
let ptr = *self.ptr.get();
|
||||
*self.ptr.get() = ptr.add(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);
|
||||
impl<T: RawBlockTraits> Drop for RawBlock<T> {
|
||||
fn drop(&mut self) {
|
||||
if !self.base.is_null() {
|
||||
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();
|
||||
*self.ptr.get_mut() = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
220
src/rcu.rs
Normal file
220
src/rcu.rs
Normal file
@@ -0,0 +1,220 @@
|
||||
use std::{
|
||||
cell::OnceCell,
|
||||
fmt::Debug,
|
||||
mem::ManuallyDrop,
|
||||
ops::Deref,
|
||||
ptr::NonNull,
|
||||
sync::{
|
||||
atomic::{AtomicPtr, AtomicU8},
|
||||
Arc, RwLock, Weak,
|
||||
},
|
||||
};
|
||||
|
||||
// the epoch counters of all threads that have ever accessed an Rcu
|
||||
// threads that have finished will have a dangling Weak reference and can be cleand up
|
||||
// having this be shared between all Rcu's is a tradeof,
|
||||
// writes will be slower as more epoch counters need to be waited for
|
||||
// reads should be faster as a thread only needs to register itself once on the first read
|
||||
//
|
||||
static EPOCH_COUNTERS: RwLock<Vec<Weak<AtomicU8>>> = RwLock::new(Vec::new());
|
||||
|
||||
thread_local! {
|
||||
// odd value means the current thread is about to access the active_epoch of an Rcu
|
||||
// a thread has a single epoch counter for all Rcu it accesses,
|
||||
// as a thread can only access one Rcu at a time
|
||||
static THREAD_EPOCH_COUNTER: OnceCell<Arc<AtomicU8>> = const { OnceCell::new() };
|
||||
}
|
||||
|
||||
pub struct Rcu<T> {
|
||||
active_value: AtomicPtr<T>,
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug> std::fmt::Debug for Rcu<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let active_epoch = self.active_epoch();
|
||||
f.debug_struct("Rcu")
|
||||
.field("active_value", &active_epoch)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Rcu<T> {
|
||||
pub fn new(initial_value: T) -> Self {
|
||||
Rcu {
|
||||
active_value: AtomicPtr::new(Arc::into_raw(Arc::new(initial_value)).cast_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn active_epoch(&self) -> RcuRef<T, T> {
|
||||
THREAD_EPOCH_COUNTER.with(|epoch_counter| {
|
||||
let epoch_counter = epoch_counter.get_or_init(|| {
|
||||
let epoch_counter = Arc::new(AtomicU8::new(0));
|
||||
// register the current threads epoch counter on init
|
||||
EPOCH_COUNTERS
|
||||
.write()
|
||||
.unwrap()
|
||||
.push(Arc::downgrade(&epoch_counter));
|
||||
epoch_counter
|
||||
});
|
||||
|
||||
let old = epoch_counter.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
|
||||
assert!(old % 2 == 0, "Old Epoch counter value should be even!");
|
||||
});
|
||||
|
||||
let arc_ptr = self.active_value.load(std::sync::atomic::Ordering::Acquire);
|
||||
|
||||
let arc = unsafe {
|
||||
// Safety:
|
||||
// - the ptr was created in Rcu::new or Rcu::replace with Arc::into_raw
|
||||
// - the Rcu is responsible for of the arc's strong refrences
|
||||
// - the Rcu is alive as this function takes a reference to the Rcu
|
||||
// - replace will wait with decrementing the old values strong count until our epoich counter is even again
|
||||
Arc::increment_strong_count(arc_ptr);
|
||||
// Safety:
|
||||
// - the ptr was created in Rcu::new or Rcu::replace with Arc::into_raw
|
||||
// - we have just ensured an additional strong count by incrementing the count
|
||||
Arc::from_raw(arc_ptr)
|
||||
};
|
||||
|
||||
THREAD_EPOCH_COUNTER.with(|epoch_counter| {
|
||||
let old = epoch_counter
|
||||
.get().expect("we initialized the OnceCell when we incremented the epoch counter the fist time")
|
||||
.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
|
||||
assert!(old % 2 != 0, "Old Epoch counter value should be odd!");
|
||||
});
|
||||
|
||||
RcuRef {
|
||||
data: arc.deref().into(),
|
||||
arc,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* replace the Rcu'S content with a new value
|
||||
*
|
||||
* This does not syncronize write and last to update the active_value pointer wins,
|
||||
* all writes that do not win will be lost, though not leaked.
|
||||
* This will block untill the old value can be reclaimed,
|
||||
* i.e. all threads whitnest to be in the read critical sections
|
||||
* have been witnest to have left the critical section at least once
|
||||
*/
|
||||
pub fn replace(&self, new_value: T) {
|
||||
let arc_ptr = self.active_value.swap(
|
||||
Arc::into_raw(Arc::new(new_value)).cast_mut(),
|
||||
std::sync::atomic::Ordering::AcqRel,
|
||||
);
|
||||
|
||||
// maually drop as we need to ensure not to drop the arc while
|
||||
// we have not witnest all threads to be or have been outside the read critical section
|
||||
// i.e. even epoch counter or different odd epoch counter
|
||||
// Safety:
|
||||
// - the ptr was created in Rcu::new or Rcu::replace with Arc::into_raw
|
||||
// - the Rcu itself holds one strong count
|
||||
let arc = unsafe { ManuallyDrop::new(Arc::from_raw(arc_ptr)) };
|
||||
|
||||
let epochs = EPOCH_COUNTERS.read().unwrap().clone();
|
||||
let mut epochs = epochs
|
||||
.into_iter()
|
||||
.flat_map(|elem| {
|
||||
let arc = elem.upgrade()?;
|
||||
let init_val = arc.load(std::sync::atomic::Ordering::Acquire);
|
||||
if init_val % 2 == 0 {
|
||||
// already even can be ignored
|
||||
return None;
|
||||
}
|
||||
// odd initial value thread is in read critical section
|
||||
// need to wait for the value to change before we can drop the arc
|
||||
Some((init_val, elem))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
while !epochs.is_empty() {
|
||||
epochs.retain(|elem| {
|
||||
let Some(arc) = elem.1.upgrade() else {
|
||||
// as the thread is dead it can't have a ref to old arc
|
||||
return false;
|
||||
};
|
||||
// the epoch counter has not changed so the thread is still in the same instance of the critical section
|
||||
// any different value is ok as
|
||||
// - even values indicate the thread is outside the critical section
|
||||
// - a diffrent odd value indicates the thread has left the critical section and can subsequently only read the new active_value
|
||||
arc.load(std::sync::atomic::Ordering::Acquire) == elem.0
|
||||
})
|
||||
}
|
||||
|
||||
// Safety:
|
||||
// - we have not dropped the arc another way
|
||||
// - we witnessed all threads either with an even epoch count or with a new odd count
|
||||
// as such they must have left the critical section at some point
|
||||
ManuallyDrop::into_inner(arc);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RcuRef<T, M>
|
||||
where
|
||||
T: ?Sized,
|
||||
M: ?Sized,
|
||||
{
|
||||
arc: Arc<T>,
|
||||
data: NonNull<M>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized, M: ?Sized + Debug> Debug for RcuRef<T, M> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RcuRef")
|
||||
.field("data", &self.deref())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// use assoiated functions rather than methods so that we don't overlap
|
||||
// with functions of the Deref Target type
|
||||
impl<T: ?Sized, M: ?Sized> RcuRef<T, M> {
|
||||
pub fn map<N: ?Sized, F: for<'a> FnOnce(&'a M) -> &'a N>(referece: Self, f: F) -> RcuRef<T, N> {
|
||||
RcuRef {
|
||||
arc: referece.arc,
|
||||
data: f(unsafe { referece.data.as_ref() }).into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_map<N: ?Sized, F: for<'a> FnOnce(&'a M) -> Option<&'a N>>(
|
||||
referece: Self,
|
||||
f: F,
|
||||
) -> Option<RcuRef<T, N>> {
|
||||
let val = f(unsafe { referece.data.as_ref() })?;
|
||||
Some(RcuRef {
|
||||
arc: Arc::clone(&referece.arc),
|
||||
data: val.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn same_epoch<M2>(this: &Self, other: &RcuRef<T, M2>) -> bool {
|
||||
Arc::ptr_eq(&this.arc, &other.arc)
|
||||
}
|
||||
|
||||
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
|
||||
this.data == other.data
|
||||
}
|
||||
|
||||
pub fn clone(this: &Self) -> Self {
|
||||
Self {
|
||||
arc: Arc::clone(&this.arc),
|
||||
data: this.data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_root(this: &Self) -> &T {
|
||||
&this.arc
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, M: ?Sized> Deref for RcuRef<T, M> {
|
||||
type Target = M;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// Safety: The pointer points into the arc we are holding
|
||||
// while we are alive so is the target
|
||||
// as the content is in an Rcu no mutable acess is given out
|
||||
unsafe { self.data.as_ref() }
|
||||
}
|
||||
}
|
||||
155
src/read.rs
155
src/read.rs
@@ -16,8 +16,6 @@ use crate::types::*;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
use rustyline::error::ReadlineError;
|
||||
#[cfg(feature = "repl")]
|
||||
@@ -26,19 +24,20 @@ use rustyline::history::DefaultHistory;
|
||||
use rustyline::{Config, Editor};
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::io::{Cursor, Error, ErrorKind, Read};
|
||||
use std::io::{Cursor, Read};
|
||||
#[cfg(feature = "repl")]
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::sync::Arc;
|
||||
|
||||
type SubtermDeque = VecDeque<(usize, usize)>;
|
||||
|
||||
pub(crate) fn devour_whitespace<'a, R: CharRead>(parser: &mut Parser<'a, R>) -> Result<bool, ParserError> {
|
||||
pub(crate) fn devour_whitespace<R: CharRead>(
|
||||
parser: &mut Parser<'_, R>,
|
||||
) -> Result<bool, ParserError> {
|
||||
match parser.lexer.scan_for_layout() {
|
||||
Err(e) if e.is_unexpected_eof() => {
|
||||
Ok(true)
|
||||
}
|
||||
Err(e) if e.is_unexpected_eof() => Ok(true),
|
||||
Err(e) => Err(e),
|
||||
Ok(_) => {
|
||||
Ok(false)
|
||||
}
|
||||
Ok(_) => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +59,6 @@ pub(crate) fn error_after_read_term<R>(
|
||||
CompilationError::from(err)
|
||||
}
|
||||
|
||||
|
||||
impl MachineState {
|
||||
pub(crate) fn read(
|
||||
&mut self,
|
||||
@@ -74,19 +72,21 @@ impl MachineState {
|
||||
|
||||
parser.add_lines_read(prior_num_lines_read);
|
||||
|
||||
let term = parser.read_term(&op_dir, Tokens::Default)
|
||||
let term = parser
|
||||
.read_term(&op_dir, Tokens::Default)
|
||||
.map_err(|err| error_after_read_term(err, prior_num_lines_read, &parser))?; // CompilationError::from
|
||||
|
||||
(term, parser.lines_read() - prior_num_lines_read)
|
||||
};
|
||||
|
||||
inner.add_lines_read(num_lines_read);
|
||||
write_term_to_heap(&term, &mut self.heap, &mut self.atom_tbl)
|
||||
write_term_to_heap(&term, &mut self.heap, &self.atom_tbl)
|
||||
}
|
||||
}
|
||||
|
||||
static mut PROMPT: bool = false;
|
||||
const HISTORY_FILE: &'static str = ".scryer_history";
|
||||
#[cfg(feature = "repl")]
|
||||
const HISTORY_FILE: &str = ".scryer_history";
|
||||
|
||||
pub(crate) fn set_prompt(value: bool) {
|
||||
unsafe {
|
||||
@@ -94,6 +94,7 @@ pub(crate) fn set_prompt(value: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
#[inline]
|
||||
fn get_prompt() -> &'static str {
|
||||
unsafe {
|
||||
@@ -110,55 +111,54 @@ pub struct ReadlineStream {
|
||||
#[cfg(feature = "repl")]
|
||||
rl: Editor<Helper, DefaultHistory>,
|
||||
pending_input: CharReader<Cursor<String>>,
|
||||
#[allow(dead_code)]
|
||||
add_history: bool,
|
||||
}
|
||||
|
||||
impl ReadlineStream {
|
||||
#[cfg(feature = "repl")]
|
||||
#[inline]
|
||||
pub fn new(pending_input: &str, add_history: bool) -> Self {
|
||||
let config = Config::builder()
|
||||
.check_cursor_position(true)
|
||||
.build();
|
||||
#[cfg(feature = "repl")]
|
||||
{
|
||||
let config = Config::builder().check_cursor_position(true).build();
|
||||
|
||||
let helper = Helper::new();
|
||||
let helper = Helper::new();
|
||||
|
||||
let mut rl = Editor::with_config(config).unwrap();
|
||||
rl.set_helper(Some(helper));
|
||||
let mut rl = Editor::with_config(config).unwrap();
|
||||
rl.set_helper(Some(helper));
|
||||
|
||||
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");
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
ReadlineStream {
|
||||
rl,
|
||||
pending_input: CharReader::new(Cursor::new(pending_input.to_owned())),
|
||||
add_history,
|
||||
}
|
||||
}
|
||||
|
||||
ReadlineStream {
|
||||
rl,
|
||||
pending_input: CharReader::new(Cursor::new(pending_input.to_owned())),
|
||||
add_history: add_history,
|
||||
#[cfg(not(feature = "repl"))]
|
||||
{
|
||||
ReadlineStream {
|
||||
pending_input: CharReader::new(Cursor::new(pending_input.to_owned())),
|
||||
add_history,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "repl"))]
|
||||
#[inline]
|
||||
pub fn new(pending_input: &str, add_history: bool) -> Self {
|
||||
ReadlineStream {
|
||||
pending_input: CharReader::new(Cursor::new(pending_input.to_owned())),
|
||||
add_history: add_history,
|
||||
#[allow(unused_variables)]
|
||||
pub fn set_atoms_for_completion(&mut self, atoms: &Arc<AtomTable>) {
|
||||
#[cfg(feature = "repl")]
|
||||
{
|
||||
let helper = self.rl.helper_mut().unwrap();
|
||||
helper.atoms = Arc::downgrade(atoms);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
pub fn set_atoms_for_completion(&mut self, atoms: *const IndexSet<Atom>) {
|
||||
let helper = self.rl.helper_mut().unwrap();
|
||||
helper.atoms = atoms;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "repl"))]
|
||||
pub fn set_atoms_for_completion(&mut self, atoms: *const IndexSet<Atom>) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reset(&mut self) {
|
||||
self.pending_input.reset_buffer();
|
||||
@@ -180,12 +180,14 @@ impl ReadlineStream {
|
||||
|
||||
unsafe {
|
||||
if PROMPT {
|
||||
self.rl.add_history_entry(self.pending_input.get_ref().get_ref()).unwrap();
|
||||
self.rl
|
||||
.add_history_entry(self.pending_input.get_ref().get_ref())
|
||||
.unwrap();
|
||||
self.save_history();
|
||||
PROMPT = false;
|
||||
}
|
||||
|
||||
if self.pending_input.get_ref().get_ref().chars().last() != Some('\n') {
|
||||
if !self.pending_input.get_ref().get_ref().ends_with('\n') {
|
||||
*self.pending_input.get_mut().get_mut() += "\n";
|
||||
}
|
||||
}
|
||||
@@ -211,17 +213,17 @@ impl ReadlineStream {
|
||||
path.push(HISTORY_FILE);
|
||||
if path.exists() {
|
||||
if self.rl.append_history(&path).is_err() {
|
||||
println!("Warning: couldn't append history (existing file)");
|
||||
println!("% Warning: couldn't append history (existing file)");
|
||||
}
|
||||
} else if self.rl.save_history(&path).is_err() {
|
||||
println!("Warning: couldn't save history (new file)");
|
||||
println!("% Warning: couldn't save history (new file)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(not(feature = "repl"))]
|
||||
fn save_history(&mut self) {
|
||||
}
|
||||
fn save_history(&mut self) {}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn peek_byte(&mut self) -> std::io::Result<u8> {
|
||||
@@ -253,7 +255,7 @@ impl Read for ReadlineStream {
|
||||
self.call_readline()?;
|
||||
self.pending_input.read(buf)
|
||||
}
|
||||
result => result
|
||||
result => result,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,16 +268,14 @@ impl CharRead for ReadlineStream {
|
||||
Some(Ok(c)) => {
|
||||
return Some(Ok(c));
|
||||
}
|
||||
_ => {
|
||||
match self.call_readline() {
|
||||
Err(e) => {
|
||||
return Some(Err(e));
|
||||
}
|
||||
_ => {
|
||||
set_prompt(false);
|
||||
}
|
||||
_ => match self.call_readline() {
|
||||
Err(e) => {
|
||||
return Some(Err(e));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
set_prompt(false);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,10 +292,10 @@ impl CharRead for ReadlineStream {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn write_term_to_heap<'a, 'b>(
|
||||
term: &'a Term,
|
||||
heap: &'b mut Heap,
|
||||
atom_tbl: &mut AtomTable,
|
||||
pub(crate) fn write_term_to_heap(
|
||||
term: &Term,
|
||||
heap: &mut Heap,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Result<TermWriteResult, CompilationError> {
|
||||
let term_writer = TermWriter::new(heap, atom_tbl);
|
||||
term_writer.write_term_to_heap(term)
|
||||
@@ -304,7 +304,7 @@ pub(crate) fn write_term_to_heap<'a, 'b>(
|
||||
#[derive(Debug)]
|
||||
struct TermWriter<'a, 'b> {
|
||||
heap: &'a mut Heap,
|
||||
atom_tbl: &'b mut AtomTable,
|
||||
atom_tbl: &'b AtomTable,
|
||||
queue: SubtermDeque,
|
||||
var_dict: HeapVarDict,
|
||||
}
|
||||
@@ -317,7 +317,7 @@ pub struct TermWriteResult {
|
||||
|
||||
impl<'a, 'b> TermWriter<'a, 'b> {
|
||||
#[inline]
|
||||
fn new(heap: &'a mut Heap, atom_tbl: &'b mut AtomTable) -> Self {
|
||||
fn new(heap: &'a mut Heap, atom_tbl: &'b AtomTable) -> Self {
|
||||
TermWriter {
|
||||
heap,
|
||||
atom_tbl,
|
||||
@@ -347,17 +347,18 @@ impl<'a, 'b> TermWriter<'a, 'b> {
|
||||
match term {
|
||||
&TermRef::Cons(..) => list_loc_as_cell!(h),
|
||||
&TermRef::AnonVar(_) | &TermRef::Var(..) => heap_loc_as_cell!(h),
|
||||
&TermRef::CompleteString(_, _, ref src) =>
|
||||
TermRef::CompleteString(_, _, src) => {
|
||||
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(_,_,_,subterms) if subterms.len() == 0 => heap_loc_as_cell!(h),
|
||||
&TermRef::Clause(_, _, _, subterms) if subterms.is_empty() => heap_loc_as_cell!(h),
|
||||
&TermRef::Clause(..) => str_loc_as_cell!(h),
|
||||
}
|
||||
}
|
||||
@@ -389,7 +390,7 @@ impl<'a, 'b> TermWriter<'a, 'b> {
|
||||
return Err(CompilationError::ExceededMaxArity);
|
||||
}
|
||||
|
||||
self.heap.push(if subterms.len() == 0 {
|
||||
self.heap.push(if subterms.is_empty() {
|
||||
heap_loc_as_cell!(heap_loc + 1)
|
||||
} else {
|
||||
str_loc_as_cell!(heap_loc + 1)
|
||||
@@ -427,7 +428,8 @@ impl<'a, 'b> TermWriter<'a, 'b> {
|
||||
}
|
||||
&TermRef::AnonVar(_) => {
|
||||
if let Some((arity, site_h)) = self.queue.pop_front() {
|
||||
self.var_dict.insert(VarKey::AnonVar(h), heap_loc_as_cell!(site_h));
|
||||
self.var_dict
|
||||
.insert(VarKey::AnonVar(h), heap_loc_as_cell!(site_h));
|
||||
|
||||
if arity > 1 {
|
||||
self.queue.push_front((arity - 1, site_h + 1));
|
||||
@@ -436,10 +438,11 @@ impl<'a, 'b> TermWriter<'a, 'b> {
|
||||
|
||||
continue;
|
||||
}
|
||||
&TermRef::CompleteString(_, _, ref src) => {
|
||||
put_complete_string(self.heap, src.as_str(), self.atom_tbl);
|
||||
TermRef::CompleteString(_, _, src) => {
|
||||
let src = src.as_str().to_owned();
|
||||
put_complete_string(self.heap, &src, self.atom_tbl);
|
||||
}
|
||||
&TermRef::PartialString(lvl, _, ref src, _) => {
|
||||
&TermRef::PartialString(lvl, _, src, _) => {
|
||||
if let Level::Root = lvl {
|
||||
// Var tags can't refer directly to partial strings,
|
||||
// so a PStrLoc cell must be pushed.
|
||||
@@ -455,7 +458,7 @@ impl<'a, 'b> TermWriter<'a, 'b> {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
&TermRef::Var(.., ref var) => {
|
||||
TermRef::Var(.., var) => {
|
||||
if let Some((arity, site_h)) = self.queue.pop_front() {
|
||||
let var_key = VarKey::VarPtr(var.clone());
|
||||
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
use indexmap::IndexSet;
|
||||
use rustyline::completion::{Completer, Candidate};
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
|
||||
use rustyline::hint::Hinter;
|
||||
use rustyline::validate::Validator;
|
||||
use rustyline::highlight::{MatchingBracketHighlighter, Highlighter};
|
||||
use rustyline::{Helper as RlHelper, Result, Context};
|
||||
use rustyline::{Context, Helper as RlHelper, Result};
|
||||
|
||||
use crate::atom_table::{Atom, STATIC_ATOMS_MAP};
|
||||
use std::sync::Weak;
|
||||
|
||||
use crate::atom_table::{AtomString, AtomTable, STATIC_ATOMS_MAP};
|
||||
|
||||
// TODO: Maybe add validation to the helper
|
||||
pub struct Helper {
|
||||
highligher: MatchingBracketHighlighter,
|
||||
pub atoms: *const IndexSet<Atom>,
|
||||
pub atoms: Weak<AtomTable>,
|
||||
}
|
||||
|
||||
impl Helper {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
highligher: MatchingBracketHighlighter::new(),
|
||||
atoms: std::ptr::null(),
|
||||
atoms: Weak::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +44,7 @@ fn get_prefix(line: &str, pos: usize) -> Option<usize> {
|
||||
}
|
||||
|
||||
if i == pos {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,39 +55,33 @@ fn get_prefix(line: &str, pos: usize) -> Option<usize> {
|
||||
start_of_atom
|
||||
}
|
||||
|
||||
pub struct StrPtr(*const str);
|
||||
|
||||
impl Candidate for StrPtr {
|
||||
fn display(&self) -> &str {
|
||||
unsafe {
|
||||
self.0.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn replacement(&self) -> &str {
|
||||
unsafe {
|
||||
self.0.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for Helper {
|
||||
type Candidate = StrPtr;
|
||||
type Candidate = AtomString<'static>;
|
||||
|
||||
fn complete(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> Result<(usize, Vec<Self::Candidate>)> {
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
_ctx: &Context<'_>,
|
||||
) -> Result<(usize, Vec<Self::Candidate>)> {
|
||||
let start_of_prefix = get_prefix(line, pos);
|
||||
if let Some(idx) = start_of_prefix {
|
||||
let sub_str = line.get(idx..pos).unwrap();
|
||||
Ok((idx, unsafe {
|
||||
let mut matching = (*self.atoms).iter()
|
||||
.chain(STATIC_ATOMS_MAP.values())
|
||||
.filter(|a| a.as_str().starts_with(sub_str))
|
||||
.map(|s| StrPtr(s.as_str()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
matching.sort_unstable_by(|a, b| Ord::cmp(&(*a.0).len(), &(*b.0).len()));
|
||||
matching
|
||||
}))
|
||||
let atom_table = self.atoms.upgrade().unwrap();
|
||||
|
||||
let index_set = atom_table.active_table();
|
||||
|
||||
let mut matching = index_set
|
||||
.iter()
|
||||
.chain(STATIC_ATOMS_MAP.values())
|
||||
.map(|a| a.as_str())
|
||||
.filter(|a| a.starts_with(sub_str))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
matching.sort_unstable_by(|a, b| Ord::cmp(&a.len(), &b.len()));
|
||||
|
||||
Ok((idx, matching))
|
||||
} else {
|
||||
Ok((0, vec![]))
|
||||
}
|
||||
|
||||
@@ -64,10 +64,7 @@ impl<'a> CompilationTarget<'a> for FactInstruction {
|
||||
}
|
||||
|
||||
fn is_void_instr(instr: &Instruction) -> bool {
|
||||
match instr {
|
||||
&Instruction::UnifyVoid(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(instr, &Instruction::UnifyVoid(_))
|
||||
}
|
||||
|
||||
fn to_pstr(lvl: Level, string: Atom, r: RegType, has_tail: bool) -> Instruction {
|
||||
@@ -75,9 +72,8 @@ impl<'a> CompilationTarget<'a> for FactInstruction {
|
||||
}
|
||||
|
||||
fn incr_void_instr(instr: &mut Instruction) {
|
||||
match instr {
|
||||
&mut Instruction::UnifyVoid(ref mut incr) => *incr += 1,
|
||||
_ => {}
|
||||
if let &mut Instruction::UnifyVoid(ref mut incr) = instr {
|
||||
*incr += 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,16 +142,12 @@ impl<'a> CompilationTarget<'a> for QueryInstruction {
|
||||
}
|
||||
|
||||
fn is_void_instr(instr: &Instruction) -> bool {
|
||||
match instr {
|
||||
&Instruction::SetVoid(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(instr, &Instruction::SetVoid(_))
|
||||
}
|
||||
|
||||
fn incr_void_instr(instr: &mut Instruction) {
|
||||
match instr {
|
||||
&mut Instruction::SetVoid(ref mut incr) => *incr += 1,
|
||||
_ => {}
|
||||
if let &mut Instruction::SetVoid(ref mut incr) = instr {
|
||||
*incr += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
306
src/tests/acyclic_term.pl
Normal file
306
src/tests/acyclic_term.pl
Normal file
@@ -0,0 +1,306 @@
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(format)).
|
||||
|
||||
term1(A) :-
|
||||
B=[C|D],
|
||||
A=[D|C],
|
||||
B=[C|B].
|
||||
|
||||
term2(A) :-
|
||||
A=[B|C],
|
||||
D=[C|C],
|
||||
D=[B|D].
|
||||
|
||||
term3(A) :-
|
||||
A=[_B|C],
|
||||
D=[C|_E],
|
||||
A=[C|D].
|
||||
|
||||
term4(A) :-
|
||||
A=[B|C],
|
||||
C=[C|B].
|
||||
|
||||
term5(A) :-
|
||||
A=[_B|C],
|
||||
D=[_E|C],
|
||||
A=[C|D].
|
||||
|
||||
term6(A) :-
|
||||
A=[B|B],
|
||||
B=[C|C].
|
||||
|
||||
term7(A) :-
|
||||
B=[C|D],
|
||||
A=[D|C],
|
||||
B=[B|D].
|
||||
|
||||
term8(A) :-
|
||||
B=C,
|
||||
B=[C|_D],
|
||||
A=[C|_E].
|
||||
|
||||
test("acyclic_term_1", (
|
||||
L = [_Y,[M,B],B|M], acyclic_term(L)
|
||||
)).
|
||||
|
||||
test("acyclic_term_2", (
|
||||
L = [_Y,[M,_B,L]|M], \+ acyclic_term(L)
|
||||
)).
|
||||
|
||||
test("acyclic_term_3", (
|
||||
L = [_Y,[M,B,L,B]|M], \+ acyclic_term(L)
|
||||
)).
|
||||
|
||||
test("acyclic_term_4", (
|
||||
L = [_Y,[L,_A,_B]|_M], \+ acyclic_term(L)
|
||||
)).
|
||||
|
||||
test("acyclic_term_5", (
|
||||
L = [_Y,[M,_A,_B]|M], acyclic_term(L)
|
||||
)).
|
||||
|
||||
test("acyclic_term_6", (
|
||||
L = [_Y,[L,_A,_B]|_M], \+ acyclic_term(L)
|
||||
)).
|
||||
|
||||
test("acyclic_term_7", (
|
||||
L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_8", (
|
||||
L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(L)
|
||||
)).
|
||||
|
||||
test("acyclic_term_9", (
|
||||
L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term_10", (
|
||||
L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(Y)
|
||||
)).
|
||||
|
||||
test("acyclic_term_11", (
|
||||
L = [A], A = [T], T = [_|X], X = Y, Y = L, \+ acyclic_term(X)
|
||||
)).
|
||||
|
||||
test("acyclic_term_12", (
|
||||
A = [1|2], X = A, T=a(X, A), acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_13", (
|
||||
A = [A|2], X = A, T=a(X, A), \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_14", (
|
||||
A = [T|2], X = A, T=a(X, A), \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_15", (
|
||||
T = [_A|T], \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_16", (
|
||||
T = [T|_L], \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_17", (
|
||||
A = [1|A], X = A, T=a(X, A), \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_18", (
|
||||
T = [_A| [[[[L|T]|[]]]]], acyclic_term(L)
|
||||
)).
|
||||
|
||||
test("acyclic_term_19", (
|
||||
T = [A| [[[[_L|T]|[]]]]], acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term_20", (
|
||||
T = [_A| [[[[_L|T]|[]]]]], \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_21", (
|
||||
A = [_C|_B], X = A, T=a(t(X,A), A), acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_22", (
|
||||
X = [a | Rest], Rest = [_Y | Rest], \+ acyclic_term(X)
|
||||
)).
|
||||
|
||||
test("acyclic_term_23", (
|
||||
_X = [a | Rest], Rest = [_Y | Rest], \+ acyclic_term(Rest)
|
||||
)).
|
||||
|
||||
test("acyclic_term_24", (
|
||||
T = [[_A, T]], G = [1|T], \+ acyclic_term(G)
|
||||
)).
|
||||
|
||||
test("acyclic_term_25", (
|
||||
T = [[_A, T]], \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_26", (
|
||||
T = [[_, _], T], \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_27", (
|
||||
T = [[T, _], 1], \+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_28", (
|
||||
T = str(A,A), acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_29", (
|
||||
T = str(A,A,A), acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term_30", (
|
||||
A = s(B, d(Y)), Y = B, acyclic_term(A),
|
||||
acyclic_term(B), acyclic_term(Y)
|
||||
)).
|
||||
|
||||
test("acyclic_term_31", (
|
||||
A=str(B,B,B), C=str(A,_D,B), acyclic_term(C),
|
||||
acyclic_term(A), acyclic_term(B)
|
||||
)).
|
||||
|
||||
test("acyclic_term_32", (
|
||||
A=B*C,A=[]*B*D*B, \+ acyclic_term(A),
|
||||
\+ acyclic_term(B), \+ acyclic_term(C),
|
||||
acyclic_term(D)
|
||||
)).
|
||||
|
||||
test("acyclic_term_33", (
|
||||
A=B*C,A=B*[]*B, \+ acyclic_term(A),
|
||||
\+ acyclic_term(B), \+ acyclic_term(C)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2111_1", (
|
||||
term1(A), \+ acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2111_2", (
|
||||
term2(A), \+ acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2111_3", (
|
||||
term3(A), \+ acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2111_4", (
|
||||
term4(A), \+ acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2111_5", (
|
||||
term5(A), \+ acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2111_6", (
|
||||
term6(A), acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2113", (
|
||||
A=[]*B,B=[]*B, \+ acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2114", (
|
||||
A=B*B, acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2116", (
|
||||
A=B*B,B=[]*[], acyclic_term(A)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2117", (
|
||||
A=[]*A,B=[]*A, \+ acyclic_term(B)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2121", (
|
||||
A=B*B, C=A*B, acyclic_term(C),
|
||||
acyclic_term(A), acyclic_term(B)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2122", (
|
||||
A=B*C,A=[]*B*B, \+ acyclic_term(A),
|
||||
\+ acyclic_term(B), \+ acyclic_term(C)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2123", (
|
||||
A=B*B,C=A*B,B=[]*[], acyclic_term(A),
|
||||
acyclic_term(B), acyclic_term(C)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2124", (
|
||||
A=B*C,B=[]*C,C=[]*B, \+ acyclic_term(A),
|
||||
\+ acyclic_term(B), \+ acyclic_term(C)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2125", (
|
||||
A=B*[],
|
||||
D=B*[],
|
||||
A=B,
|
||||
\+ acyclic_term(D),
|
||||
\+ acyclic_term(A),
|
||||
\+ acyclic_term(B)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2130_1", (
|
||||
term7(T),
|
||||
\+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2130_2", (
|
||||
term8(T),
|
||||
\+ acyclic_term(T)
|
||||
)).
|
||||
|
||||
test("acyclic_term#2131", (
|
||||
A=[B],C=[B],C=[A],
|
||||
\+ acyclic_term(C)
|
||||
)).
|
||||
|
||||
main :-
|
||||
findall(test(Name, Goal), test(Name, Goal), Tests),
|
||||
run_tests(Tests, Failed),
|
||||
show_failed(Failed),
|
||||
halt.
|
||||
|
||||
main_quiet :-
|
||||
findall(test(Name, Goal), test(Name, Goal), Tests),
|
||||
run_tests_quiet(Tests, Failed),
|
||||
( Failed = [] ->
|
||||
format("All tests passed", [])
|
||||
; format("Some tests failed", [])
|
||||
),
|
||||
halt.
|
||||
|
||||
run_tests([], []).
|
||||
run_tests([test(Name, Goal)|Tests], Failed) :-
|
||||
format("Running test \"~s\"~n", [Name]),
|
||||
( call(Goal) ->
|
||||
Failed = Failed1
|
||||
; format("Failed test \"~s\"~n", [Name]),
|
||||
Failed = [Name|Failed1]
|
||||
),
|
||||
run_tests(Tests, Failed1).
|
||||
|
||||
run_tests_quiet([], []).
|
||||
run_tests_quiet([test(Name, Goal)|Tests], Failed) :-
|
||||
( call(Goal) ->
|
||||
Failed = Failed1
|
||||
; Failed = [Name|Failed1]
|
||||
),
|
||||
run_tests_quiet(Tests, Failed1).
|
||||
|
||||
show_failed(Failed) :-
|
||||
phrase(portray_failed(Failed), F),
|
||||
format("~s", [F]).
|
||||
|
||||
portray_failed_([]) --> [].
|
||||
portray_failed_([F|Fs]) -->
|
||||
"\"", F, "\"", "\n", portray_failed_(Fs).
|
||||
|
||||
portray_failed([]) --> [].
|
||||
portray_failed([F|Fs]) -->
|
||||
"\n", "Failed tests:", "\n", portray_failed_([F|Fs]).
|
||||
277
src/tests/dif.pl
Normal file
277
src/tests/dif.pl
Normal file
@@ -0,0 +1,277 @@
|
||||
/**/
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(debug)).
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(dif)).
|
||||
|
||||
% Tests from https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dif
|
||||
|
||||
test("dif#1",(
|
||||
call_residual_goals(dif(1,2), Res),
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#2",(
|
||||
\+ (dif(1,Y), Y = 1)
|
||||
)).
|
||||
|
||||
test("dif#3",(
|
||||
call_residual_goals((dif(1,Y), Y=2), Res),
|
||||
Y == 2,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#4",(
|
||||
\+ (dif(X,-Y), X= -Y)
|
||||
)).
|
||||
|
||||
test("dif#5",(
|
||||
\+ (dif(X,Y), X=Y)
|
||||
)).
|
||||
|
||||
test("dif#6",(
|
||||
\+ (dif(X,Y), X=Y, X=1)
|
||||
)).
|
||||
|
||||
test("dif#7",(
|
||||
\+ (dif(-X,-Y), X=Y)
|
||||
)).
|
||||
|
||||
test("dif#8",(
|
||||
\+ (dif(-X,-Y), X=Y, X=1)
|
||||
)).
|
||||
|
||||
% I don't understand exactly what is expected for dif#9 and dif#10
|
||||
|
||||
test("dif#11",(
|
||||
call_residual_goals((X=Y, dif(X-Y,1-2)), Res),
|
||||
X == Y,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#12",(
|
||||
call_residual_goals((dif(X-Y,1-2), X=Y), Res),
|
||||
X == Y,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#13",(
|
||||
call_residual_goals((X=Y, Y=1, dif(X-Y,1-2)), Res),
|
||||
X == 1,
|
||||
Y == 1,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#14",(
|
||||
call_residual_goals((dif(X-Y,1-2), X=Y, Y=1), Res),
|
||||
X == 1,
|
||||
Y == 1,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#15",(
|
||||
call_residual_goals((dif(X-Y,1-2), X=Y, X=2), Res),
|
||||
X == 2,
|
||||
Y == 2,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#16",(
|
||||
call_residual_goals((dif(A-C,B-D), C-D=z-z, A-B=1-2), Res),
|
||||
A == 1,
|
||||
B == 2,
|
||||
C == z,
|
||||
D == z,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#17",(
|
||||
call_residual_goals((A-B=1-2, C-D=z-z, dif(A-C,B-D)), Res),
|
||||
A == 1,
|
||||
B == 2,
|
||||
C == z,
|
||||
D == z,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#18",(
|
||||
call_residual_goals((dif(A,[C|B]), A=[[]|_], A=[B]), Res),
|
||||
A == [[]],
|
||||
B == [],
|
||||
Res = [dif:dif([[]], [C])]
|
||||
)).
|
||||
|
||||
test("dif#19",(
|
||||
call_residual_goals((dif([E],[/]), E=1), Res),
|
||||
E == 1,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#20",(
|
||||
call_residual_goals((dif([a],B), B=[_|_], B=[b]), Res),
|
||||
B == [b],
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#21",(
|
||||
call_residual_goals((dif([],A), A = [_]), Res),
|
||||
A = [_],
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#22",(
|
||||
call_residual_goals((A = [_], dif([],A)), Res),
|
||||
A = [_],
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#t1",(
|
||||
set_prolog_flag(occurs_check, false),
|
||||
\+ \+ -X=X
|
||||
)).
|
||||
|
||||
test("dif#t2",(
|
||||
set_prolog_flag(occurs_check, false),
|
||||
\+ (-X=X, -Y=Y, X\=Y)
|
||||
)).
|
||||
|
||||
test("dif#t3",(
|
||||
set_prolog_flag(occurs_check, false),
|
||||
call_residual_goals((-X=X, dif(X,1)), Res),
|
||||
X == -X,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#t4",(
|
||||
set_prolog_flag(occurs_check, false),
|
||||
\+ (-X=X, -Y=Y, dif(X,Y))
|
||||
)).
|
||||
|
||||
test("dif#t5",(
|
||||
set_prolog_flag(occurs_check, false),
|
||||
\+ (dif(X,Y), -X=X, -Y=Y)
|
||||
)).
|
||||
|
||||
test("dif#t6",(
|
||||
set_prolog_flag(occurs_check, false),
|
||||
\+ (A=[[]|A],dif(A,B),B=[[]|A])
|
||||
)).
|
||||
|
||||
test("dif#t7",(
|
||||
set_prolog_flag(occurs_check, false),
|
||||
\+ (dif(-X,X),-Y=Y,X=Y)
|
||||
)).
|
||||
|
||||
test("dif#o1",(
|
||||
set_prolog_flag(occurs_check, true),
|
||||
\+ (-X = X)
|
||||
)).
|
||||
|
||||
test("dif#o2",(
|
||||
set_prolog_flag(occurs_check, true),
|
||||
call_residual_goals((dif(-X,X)), Res),
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#o3",(
|
||||
set_prolog_flag(occurs_check, true),
|
||||
call_residual_goals((dif(-X,Y), X=Y), Res),
|
||||
X == Y,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
test("dif#12 but with multiple variables in the residuals",(
|
||||
call_residual_goals((dif(X-Y-_, 1-2-3), X = Y), Res),
|
||||
X == Y,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
% https://github.com/mthom/scryer-prolog/issues/1956
|
||||
test("scryer-prolog#1956",(
|
||||
call_residue_vars((dif(a-a,X-_),X=b), Res),
|
||||
X == b,
|
||||
Res = []
|
||||
)).
|
||||
|
||||
% https://github.com/mthom/scryer-prolog/issues/2056
|
||||
test("scryer-prolog#2056",(
|
||||
set_prolog_flag(occurs_check, false),
|
||||
C=[D|E],
|
||||
D=[C],
|
||||
A=[A],
|
||||
dif(A,[D]),
|
||||
|
||||
\+ E=[]
|
||||
)).
|
||||
|
||||
% https://github.com/mthom/scryer-prolog/issues/2175
|
||||
test("scryer-prolog#2175",(
|
||||
dif(A,B),
|
||||
A=_C*[],
|
||||
A=[]*D*B,D=[]
|
||||
)).
|
||||
|
||||
main :-
|
||||
findall(test(Name, Goal), test(Name, Goal), Tests),
|
||||
run_tests(Tests, Failed),
|
||||
show_failed(Failed),
|
||||
halt.
|
||||
|
||||
main_quiet :-
|
||||
findall(test(Name, Goal), test(Name, Goal), Tests),
|
||||
run_tests_quiet(Tests, Failed),
|
||||
( Failed = [] ->
|
||||
format("All tests passed", [])
|
||||
; format("Some tests failed", [])
|
||||
),
|
||||
halt.
|
||||
|
||||
portray_failed_([]) --> [].
|
||||
portray_failed_([F|Fs]) -->
|
||||
"\"", F, "\"", "\n", portray_failed_(Fs).
|
||||
|
||||
portray_failed([]) --> [].
|
||||
portray_failed([F|Fs]) -->
|
||||
"\n", "Failed tests:", "\n", portray_failed_([F|Fs]).
|
||||
|
||||
show_failed(Failed) :-
|
||||
phrase(portray_failed(Failed), F),
|
||||
format("~s", [F]).
|
||||
|
||||
run_tests([], []).
|
||||
run_tests([test(Name, Goal)|Tests], Failed) :-
|
||||
format("Running test \"~s\"~n", [Name]),
|
||||
( call(Goal) ->
|
||||
Failed = Failed1
|
||||
; format("Failed test \"~s\"~n", [Name]),
|
||||
Failed = [Name|Failed1]
|
||||
),
|
||||
run_tests(Tests, Failed1).
|
||||
|
||||
run_tests_quiet([], []).
|
||||
run_tests_quiet([test(Name, Goal)|Tests], Failed) :-
|
||||
( call(Goal) ->
|
||||
Failed = Failed1
|
||||
; Failed = [Name|Failed1]
|
||||
),
|
||||
run_tests_quiet(Tests, Failed1).
|
||||
|
||||
assert_p(A, B) :-
|
||||
phrase(portray_clause_(A), Portrayed),
|
||||
phrase((B, ".\n"), Portrayed).
|
||||
|
||||
call_residual_goals(Goal, ResidualGoals) :-
|
||||
call_residue_vars(Goal, Vars),
|
||||
variables_residual_goals(Vars, ResidualGoals).
|
||||
|
||||
variables_residual_goals(Vars, Goals) :-
|
||||
phrase(variables_residual_goals(Vars), Goals).
|
||||
|
||||
variables_residual_goals([]) --> [].
|
||||
variables_residual_goals([Var|Vars]) -->
|
||||
dif:attribute_goals(Var),
|
||||
variables_residual_goals(Vars).
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user