Compare commits
662 Commits
v0.9.4
...
exian-v0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
896b90e57d | ||
|
|
e7ac3ae0f7 | ||
|
|
60595f990f | ||
|
|
042631cb4c | ||
|
|
32b6458deb | ||
|
|
12db664bd9 | ||
|
|
e9ff1348b1 | ||
|
|
fc6e007ad1 | ||
|
|
7637a0f7d4 | ||
|
|
ba7b54e8f8 | ||
|
|
012e3de3a1 | ||
|
|
72cdba82f5 | ||
|
|
24f431e2ca | ||
|
|
8afcc7742b | ||
|
|
e9834ffa3e | ||
|
|
28ec457159 | ||
|
|
1ece558a71 | ||
|
|
be18eda696 | ||
|
|
fdbaf6ac9d | ||
|
|
5c341ce371 | ||
|
|
5f58e1cfd7 | ||
|
|
dc05526c71 | ||
|
|
ffea37d899 | ||
|
|
368c9fd9dd | ||
|
|
c067bfa832 | ||
|
|
aa3d8b8fba | ||
|
|
14ce052bf2 | ||
|
|
252361fc20 | ||
|
|
78c08b87b6 | ||
|
|
eb91b3c1fb | ||
|
|
a140720c6f | ||
|
|
6f3f66c407 | ||
|
|
1f7a60dec9 | ||
|
|
ce9b41815d | ||
|
|
80bf276e09 | ||
|
|
8bc8171ec7 | ||
|
|
3b5b768f4a | ||
|
|
176858ad42 | ||
|
|
eba681786d | ||
|
|
f4297e365f | ||
|
|
cd777294b9 | ||
|
|
3e929511bb | ||
|
|
d1356db7e3 | ||
|
|
7b2bf73ba1 | ||
|
|
edd6d44bd6 | ||
|
|
4b5e4a2745 | ||
|
|
3a4dfc46da | ||
|
|
d8346b1651 | ||
|
|
20d52c093a | ||
|
|
35d34231f2 | ||
|
|
90e6e84312 | ||
|
|
cd501beb0b | ||
|
|
39d02a0caf | ||
|
|
e168b31963 | ||
|
|
eed0a5babb | ||
|
|
ad29f0f180 | ||
|
|
b14bda310f | ||
|
|
39cf60bc05 | ||
|
|
02b8a6010c | ||
|
|
b2cfc8b6f2 | ||
|
|
c5389dbbf2 | ||
|
|
633b86ddc4 | ||
|
|
348c5029b3 | ||
|
|
7ffd93c0d7 | ||
|
|
6338a40eed | ||
|
|
3aa272e44d | ||
|
|
52f3ddd20d | ||
|
|
f61c069c2b | ||
|
|
f1171e2fb2 | ||
|
|
e38ee14580 | ||
|
|
93cc4d4898 | ||
|
|
87f5295bd0 | ||
|
|
97607d2bcd | ||
|
|
216c251c48 | ||
|
|
8af0a19130 | ||
|
|
9485ae2ca5 | ||
|
|
18c451e11c | ||
|
|
a4c071a44b | ||
|
|
c0fb3fd429 | ||
|
|
9fb7d15f0a | ||
|
|
a8fa8993e1 | ||
|
|
af3792f79a | ||
|
|
d83827b21f | ||
|
|
ee39be4361 | ||
|
|
bc4689ca4a | ||
|
|
86dc22a850 | ||
|
|
9cc170f2ad | ||
|
|
61f8bc008c | ||
|
|
948cbd8f40 | ||
|
|
e1a52a4dde | ||
|
|
7b960a7d63 | ||
|
|
c5fd4c1d8a | ||
|
|
7dc6d6e4e0 | ||
|
|
1d643d8385 | ||
|
|
badd6304c7 | ||
|
|
a8da2b6a99 | ||
|
|
a43bac8fd8 | ||
|
|
72d5574ff0 | ||
|
|
a2e19bd483 | ||
|
|
d907f86c8d | ||
|
|
fadd3a0839 | ||
|
|
45041be336 | ||
|
|
b2d639b159 | ||
|
|
3a6b92d227 | ||
|
|
409159d68d | ||
|
|
143f32be23 | ||
|
|
d0a6dc9df2 | ||
|
|
62e43a3ab0 | ||
|
|
eb82d1b6d4 | ||
|
|
70d65bcea8 | ||
|
|
25edde2eb4 | ||
|
|
ab675e071a | ||
|
|
dc08a4ab11 | ||
|
|
1120bcde29 | ||
|
|
dc495f10f8 | ||
|
|
d2ffd4f4bf | ||
|
|
87dbad2294 | ||
|
|
ca52c65902 | ||
|
|
7f233da10c | ||
|
|
ff546a0f9a | ||
|
|
746d4fd106 | ||
|
|
97e2e5d7f0 | ||
|
|
1ee4f7a55f | ||
|
|
43dd1587fb | ||
|
|
8971809c83 | ||
|
|
e321f29b9c | ||
|
|
129c80bf16 | ||
|
|
183761eba4 | ||
|
|
6c7833b9c7 | ||
|
|
82e989f631 | ||
|
|
2b052bf8dd | ||
|
|
003e9d4610 | ||
|
|
d79ece2497 | ||
|
|
ae0baf4893 | ||
|
|
1114700ce6 | ||
|
|
dea43a0244 | ||
|
|
0c2c6124eb | ||
|
|
9cccd509e3 | ||
|
|
36bdab84ba | ||
|
|
cf51338a77 | ||
|
|
f738b42e49 | ||
|
|
f45426e8ab | ||
|
|
3d2439c92b | ||
|
|
6205f2f1f1 | ||
|
|
760e1d2aac | ||
|
|
e1246f0c83 | ||
|
|
bd1f8bb37e | ||
|
|
620459ea1e | ||
|
|
d2502b0520 | ||
|
|
34eab2e73a | ||
|
|
db3f2717bc | ||
|
|
7227e1d97c | ||
|
|
9d8906da30 | ||
|
|
f524062e5b | ||
|
|
eb29390a79 | ||
|
|
f91aedcc2f | ||
|
|
4f04d2dfca | ||
|
|
de89a78cbb | ||
|
|
e7600884fb | ||
|
|
3365cffa96 | ||
|
|
df3f1236f7 | ||
|
|
8766851919 | ||
|
|
014d1b0095 | ||
|
|
c885d1a7e7 | ||
|
|
7593f88d5a | ||
|
|
6885074006 | ||
|
|
73cc872b50 | ||
|
|
fae5e13bd5 | ||
|
|
6d7c217227 | ||
|
|
166ec02973 | ||
|
|
1d26f98688 | ||
|
|
58b9a6ad69 | ||
|
|
a639fec153 | ||
|
|
495bcd73f2 | ||
|
|
ae3019d923 | ||
|
|
72631b3e0b | ||
|
|
8cc74b2af7 | ||
|
|
7199e9a2db | ||
|
|
edf21494d9 | ||
|
|
b364c9073a | ||
|
|
9b293cf1b6 | ||
|
|
65dda019a2 | ||
|
|
4aa2a45736 | ||
|
|
4efceda8c5 | ||
|
|
5dc8983eec | ||
|
|
49b30e9f81 | ||
|
|
b78ccf8f28 | ||
|
|
2dc66d33dd | ||
|
|
e303e5a805 | ||
|
|
762b63e1f4 | ||
|
|
2844b5a958 | ||
|
|
1332611f83 | ||
|
|
725def07cd | ||
|
|
cbdd0fbf15 | ||
|
|
87ca083cfa | ||
|
|
d01557f3c1 | ||
|
|
8b009448f4 | ||
|
|
709dc041eb | ||
|
|
8763a42c98 | ||
|
|
d6b6eda77d | ||
|
|
4e0493474e | ||
|
|
a9847eef65 | ||
|
|
cb9b988905 | ||
|
|
073ec4c1d0 | ||
|
|
d51cbd67e0 | ||
|
|
e9bb41f0d6 | ||
|
|
b12d1ece17 | ||
|
|
706ffe7ad9 | ||
|
|
258244caf2 | ||
|
|
39ea2d5899 | ||
|
|
41b90c6e46 | ||
|
|
6bcbb91e08 | ||
|
|
e1e52338d6 | ||
|
|
995a39419a | ||
|
|
4644ea2404 | ||
|
|
f3cd6326a3 | ||
|
|
bf31d3f9a9 | ||
|
|
4c46f0e54d | ||
|
|
e563fd8e4f | ||
|
|
1297a24469 | ||
|
|
907e64e185 | ||
|
|
46f259bf7d | ||
|
|
634ccf22aa | ||
|
|
16b3bea48e | ||
|
|
e20363b462 | ||
|
|
549a26dd03 | ||
|
|
8981ab72d1 | ||
|
|
98dae9aecc | ||
|
|
22080f3787 | ||
|
|
9e1e99f961 | ||
|
|
eef7b06919 | ||
|
|
2ad870c740 | ||
|
|
3dee07f648 | ||
|
|
9daf016d63 | ||
|
|
fadcfb966e | ||
|
|
57f76169fd | ||
|
|
ae4d12a123 | ||
|
|
3c85ef2724 | ||
|
|
15f1320d05 | ||
|
|
7b357ba84d | ||
|
|
c0cd371056 | ||
|
|
79be0c0625 | ||
|
|
c0f72704ec | ||
|
|
f7bbdfe73a | ||
|
|
1ef681bd21 | ||
|
|
34ac85bb6d | ||
|
|
a69303dc93 | ||
|
|
778068129f | ||
|
|
ce56b5ed6b | ||
|
|
8e01eea440 | ||
|
|
1cf5de8f5c | ||
|
|
686f20db74 | ||
|
|
17377064cf | ||
|
|
f49a0ae79b | ||
|
|
4b480a7eab | ||
|
|
4a6f86591c | ||
|
|
37ac5bb3c1 | ||
|
|
9456517bed | ||
|
|
8f514ce419 | ||
|
|
e40125ff3a | ||
|
|
c790bbfce9 | ||
|
|
b767e4de25 | ||
|
|
ca173873af | ||
|
|
036c59e5fa | ||
|
|
7809edca19 | ||
|
|
a42a88a2c9 | ||
|
|
9dab070c11 | ||
|
|
ec28872a9a | ||
|
|
eb96a0c957 | ||
|
|
90f4716cc3 | ||
|
|
3a6289324e | ||
|
|
f496abbbef | ||
|
|
f1793744bc | ||
|
|
bd4b305649 | ||
|
|
638a765937 | ||
|
|
78fec46517 | ||
|
|
00a44921db | ||
|
|
d79bc199b3 | ||
|
|
4da0f90097 | ||
|
|
d18ef52e67 | ||
|
|
925921e9ab | ||
|
|
b98076b39e | ||
|
|
3090cb0084 | ||
|
|
971521791c | ||
|
|
2c8041250d | ||
|
|
1b22c89211 | ||
|
|
4e207fc34a | ||
|
|
8558bd2214 | ||
|
|
ba9fed3eae | ||
|
|
024c498bee | ||
|
|
8c348a7160 | ||
|
|
1f82dafcb6 | ||
|
|
d2dfb7ccda | ||
|
|
20525e5013 | ||
|
|
14c8fc3e34 | ||
|
|
3d60094330 | ||
|
|
ae27e78d80 | ||
|
|
3826ef38cd | ||
|
|
7b6f4439bb | ||
|
|
464cfd9213 | ||
|
|
9dbf592a58 | ||
|
|
e273b007a2 | ||
|
|
6df4eaf809 | ||
|
|
165d47c7b5 | ||
|
|
a45a1f6197 | ||
|
|
16187390cd | ||
|
|
57bf1ec39b | ||
|
|
7ac84d3565 | ||
|
|
32d91449b5 | ||
|
|
3799974b82 | ||
|
|
9da0a06afa | ||
|
|
b8526e0510 | ||
|
|
2d4fcf9714 | ||
|
|
4f3843742c | ||
|
|
a0e3283d64 | ||
|
|
c9a0f3332e | ||
|
|
aeacccbe4c | ||
|
|
5d468d3e19 | ||
|
|
080a4fed03 | ||
|
|
3b89c1124d | ||
|
|
236ed93c6c | ||
|
|
cea8e6b1c9 | ||
|
|
93e804eeaa | ||
|
|
b32273fc79 | ||
|
|
4fc4152eac | ||
|
|
914318b14e | ||
|
|
3fc5709c50 | ||
|
|
28926486e0 | ||
|
|
ad211d5e05 | ||
|
|
5386c183d2 | ||
|
|
0a2457943e | ||
|
|
baae1dca15 | ||
|
|
4e032c8a28 | ||
|
|
7a6620b52d | ||
|
|
dd6533e76c | ||
|
|
d262159399 | ||
|
|
c77ea48fbd | ||
|
|
35c1880937 | ||
|
|
e5cca9d10a | ||
|
|
f45b0bcfe8 | ||
|
|
8966e175f1 | ||
|
|
2a218f34b9 | ||
|
|
d8213e29c5 | ||
|
|
7f2ce57ba7 | ||
|
|
949d316773 | ||
|
|
0cf46d3ec4 | ||
|
|
e68ac8347f | ||
|
|
d4bf52e82c | ||
|
|
22538a05be | ||
|
|
6d80c843e6 | ||
|
|
b8ccebbf24 | ||
|
|
7108e87e92 | ||
|
|
fdc35f8b40 | ||
|
|
2fe7b55343 | ||
|
|
f509d3d66f | ||
|
|
079a69396a | ||
|
|
c548b14f50 | ||
|
|
590d0daa24 | ||
|
|
7a5a5bb795 | ||
|
|
5a869e8b48 | ||
|
|
28678e708b | ||
|
|
f69c58c6b6 | ||
|
|
0cde8f9a43 | ||
|
|
20735ea34c | ||
|
|
f3a92c6551 | ||
|
|
e2d1a2b6bc | ||
|
|
d3361c16d8 | ||
|
|
9420c7e41e | ||
|
|
a7598184bf | ||
|
|
bc0f614bf9 | ||
|
|
b1e66f686b | ||
|
|
b74c8139d1 | ||
|
|
75d499bf8a | ||
|
|
f1100271e2 | ||
|
|
143430dd9c | ||
|
|
00e6e323f8 | ||
|
|
b76bdd75e4 | ||
|
|
1ed4fe6555 | ||
|
|
894405149b | ||
|
|
eff094cc29 | ||
|
|
6cedbc33c0 | ||
|
|
2856cc4371 | ||
|
|
a683d3e363 | ||
|
|
2257503951 | ||
|
|
531d482ba7 | ||
|
|
28c386422b | ||
|
|
edaba024b2 | ||
|
|
333d6740ae | ||
|
|
56cb27f71c | ||
|
|
d57f871a12 | ||
|
|
afc7b74bbd | ||
|
|
d287095e61 | ||
|
|
a599a11169 | ||
|
|
b7943837a0 | ||
|
|
e01a4cdc85 | ||
|
|
555dff5bad | ||
|
|
75ac3fffae | ||
|
|
8dc80b815f | ||
|
|
6175e334a5 | ||
|
|
7e22c12a8d | ||
|
|
b5030be34f | ||
|
|
c59f54252c | ||
|
|
a9a77779e3 | ||
|
|
5f035ec6ad | ||
|
|
9eef380764 | ||
|
|
ce6a0a1541 | ||
|
|
b919a39b96 | ||
|
|
84fc639f31 | ||
|
|
a15b2789e2 | ||
|
|
2e910de485 | ||
|
|
74e3d32ca9 | ||
|
|
500a6cdaf1 | ||
|
|
17293a5753 | ||
|
|
a75266c3ce | ||
|
|
a92eac919f | ||
|
|
2ff50f06bc | ||
|
|
e219e1c483 | ||
|
|
eef9f74a68 | ||
|
|
eae1f53846 | ||
|
|
48a42ae140 | ||
|
|
44facf07ab | ||
|
|
9265d66f58 | ||
|
|
3d3baeed82 | ||
|
|
e21c772181 | ||
|
|
2f82c78bc0 | ||
|
|
ec6286ffab | ||
|
|
bb5adba4ef | ||
|
|
33d8abfed8 | ||
|
|
d336cbc86a | ||
|
|
79fbd9e90f | ||
|
|
cab61738ad | ||
|
|
e6cc408ff2 | ||
|
|
dc8348b843 | ||
|
|
cb040dafc1 | ||
|
|
c13fc2d1b6 | ||
|
|
1375f448a0 | ||
|
|
0433706db6 | ||
|
|
e74fd11b0d | ||
|
|
4480e7c066 | ||
|
|
71dec62ce2 | ||
|
|
658e39aae6 | ||
|
|
29fc55cb28 | ||
|
|
8e7dc9dba8 | ||
|
|
abe1f37167 | ||
|
|
8cde3f8ce3 | ||
|
|
dba9c9a0a7 | ||
|
|
ea02b54e7f | ||
|
|
0427d9ef11 | ||
|
|
c1571bf063 | ||
|
|
cef45de74a | ||
|
|
d7bf7445f1 | ||
|
|
8e5ddebddb | ||
|
|
ff03432624 | ||
|
|
7fd409140c | ||
|
|
9f6bbe949c | ||
|
|
e78914219e | ||
|
|
620077d8c1 | ||
|
|
556b7c733f | ||
|
|
e577a5430b | ||
|
|
e8ef9bc33a | ||
|
|
7b18b1d47a | ||
|
|
c39ea4818a | ||
|
|
90d418a181 | ||
|
|
9785a04b3a | ||
|
|
6dd0ec0b98 | ||
|
|
e9f03c89a6 | ||
|
|
8f41c1aa7e | ||
|
|
442636c131 | ||
|
|
190929e4ac | ||
|
|
2f47343cb5 | ||
|
|
3bc1885606 | ||
|
|
0552530b41 | ||
|
|
951c88d5f3 | ||
|
|
383eb5bdd9 | ||
|
|
3fa6b69a79 | ||
|
|
0bc3d67298 | ||
|
|
a898d7a667 | ||
|
|
238f1bd639 | ||
|
|
4b0f9558b3 | ||
|
|
c922ed8b0a | ||
|
|
3af24a8067 | ||
|
|
fdd6579230 | ||
|
|
16734ec7e4 | ||
|
|
d32f3ee2f3 | ||
|
|
ada9ba98cc | ||
|
|
a885e87ef7 | ||
|
|
e369c15a27 | ||
|
|
afa7af3de6 | ||
|
|
d3071988b2 | ||
|
|
08aed04dd2 | ||
|
|
7af93f1532 | ||
|
|
2b73107631 | ||
|
|
61c258f49e | ||
|
|
6f0c7f42c6 | ||
|
|
7b09d447d6 | ||
|
|
9f4d31a825 | ||
|
|
806e980cca | ||
|
|
12a61cdff8 | ||
|
|
808ea9fc79 | ||
|
|
e1d830d680 | ||
|
|
6be671d7d9 | ||
|
|
5c39e2a5a1 | ||
|
|
e24396d78a | ||
|
|
264e3e9dd6 | ||
|
|
3f25ee3749 | ||
|
|
c24a51b633 | ||
|
|
8925133f40 | ||
|
|
2faab30af9 | ||
|
|
c1b88cee99 | ||
|
|
8736526b35 | ||
|
|
7afc08af47 | ||
|
|
12aaae5fc0 | ||
|
|
e0ec5e75b4 | ||
|
|
7092e278d7 | ||
|
|
82a7d7efbd | ||
|
|
37a906cf37 | ||
|
|
bacfed8983 | ||
|
|
ed1b25cb30 | ||
|
|
bc262db6d7 | ||
|
|
86a2c5e2ed | ||
|
|
e77c97e0d1 | ||
|
|
48f3a7b87e | ||
|
|
e5aaa9d277 | ||
|
|
938401dbc6 | ||
|
|
620bf385d1 | ||
|
|
cc51b8150d | ||
|
|
775b14c537 | ||
|
|
7cfe8ee55c | ||
|
|
058b9c5e51 | ||
|
|
deed6fb984 | ||
|
|
2d04c0dcf8 | ||
|
|
5a0abd713e | ||
|
|
df33da111e | ||
|
|
17a816eff4 | ||
|
|
880e22d164 | ||
|
|
c16d2d4147 | ||
|
|
59af900aaf | ||
|
|
add03cb4ee | ||
|
|
409f56ea37 | ||
|
|
2814d79b56 | ||
|
|
4009b40029 | ||
|
|
260c52adec | ||
|
|
b1f057b67d | ||
|
|
177bc16255 | ||
|
|
4ce4cd1bc1 | ||
|
|
9e8d8aa19e | ||
|
|
4d3ceb387e | ||
|
|
5038b716eb | ||
|
|
e310f9683e | ||
|
|
c29b76143c | ||
|
|
3ad4b05a97 | ||
|
|
0da313eb84 | ||
|
|
6b50ec328c | ||
|
|
1eb6a883f6 | ||
|
|
e7fd408548 | ||
|
|
eaa95293e4 | ||
|
|
8ac663d703 | ||
|
|
902712695c | ||
|
|
5d03be4b09 | ||
|
|
689632b51c | ||
|
|
1b19ae81d5 | ||
|
|
a0d790445d | ||
|
|
213ee5ca45 | ||
|
|
74720d4d2e | ||
|
|
39bd520542 | ||
|
|
496e4e9f56 | ||
|
|
89c1ea4232 | ||
|
|
a2a50586aa | ||
|
|
0284a2092d | ||
|
|
c414f75329 | ||
|
|
943dce566b | ||
|
|
b32498b37c | ||
|
|
ea041a40f9 | ||
|
|
1d66f91a41 | ||
|
|
fee7ba58b0 | ||
|
|
8e53d12776 | ||
|
|
08a9f7c068 | ||
|
|
33793193cc | ||
|
|
285f11ccdc | ||
|
|
d87400afa0 | ||
|
|
fcb41542c3 | ||
|
|
87b4c9d736 | ||
|
|
6386e70584 | ||
|
|
722975d77e | ||
|
|
8943962704 | ||
|
|
ed57ef3d01 | ||
|
|
9e7d41025a | ||
|
|
d2f236d116 | ||
|
|
1ff995fedb | ||
|
|
1b16c6c73f | ||
|
|
ceac9c9095 | ||
|
|
0005dcaacb | ||
|
|
467a82fe46 | ||
|
|
ce40f8f10c | ||
|
|
d2d451b182 | ||
|
|
52fa51853e | ||
|
|
6575b1b573 | ||
|
|
cba81b4dc7 | ||
|
|
91253917b4 | ||
|
|
821358c062 | ||
|
|
3de0a4e2ad | ||
|
|
b8a3067744 | ||
|
|
7509cc1a07 | ||
|
|
dd2548453b | ||
|
|
a93585080c | ||
|
|
5f30c9599a | ||
|
|
4b3edfd370 | ||
|
|
d6ac03552d | ||
|
|
a72632ae85 | ||
|
|
2d18e0d719 | ||
|
|
45a8a7bb31 | ||
|
|
1a9d10d298 | ||
|
|
80a0cab6ab | ||
|
|
96720457d0 | ||
|
|
f4d2839dc7 | ||
|
|
070f1e8dfa | ||
|
|
42a0d686a5 | ||
|
|
79bc2d9c68 | ||
|
|
f3eea6302d | ||
|
|
bcfa0219e2 | ||
|
|
5e1effc69a | ||
|
|
be23c74b17 | ||
|
|
8fe1c206d3 | ||
|
|
a6157a926d | ||
|
|
9837187183 | ||
|
|
c2657cce37 | ||
|
|
7d6109feca | ||
|
|
cf14b222f2 | ||
|
|
238343f389 | ||
|
|
77e90ca725 | ||
|
|
f734c273ca | ||
|
|
e23e5203de | ||
|
|
a2dd189241 | ||
|
|
0ed4e41000 | ||
|
|
352919862f | ||
|
|
aab6567750 | ||
|
|
ea9e24f214 | ||
|
|
40fc4f9392 | ||
|
|
795a7c690d | ||
|
|
2fdbb94c79 | ||
|
|
767d90d5e7 | ||
|
|
bc04790fd5 | ||
|
|
58f7c94e74 | ||
|
|
8af29c200d | ||
|
|
1db6bff890 | ||
|
|
0292cb73d3 | ||
|
|
7b33eafbb0 | ||
|
|
ee60c463c8 | ||
|
|
e1a6b5eb03 | ||
|
|
4ebdd97ad3 | ||
|
|
6f7f979c05 | ||
|
|
ca8e280b4c | ||
|
|
b26e8b0e34 | ||
|
|
df41eb1870 | ||
|
|
90c602f867 | ||
|
|
6b8a679a51 | ||
|
|
8b16a0e1f6 | ||
|
|
ec251b254c | ||
|
|
e0c8163211 | ||
|
|
01f757c940 | ||
|
|
42f4827854 | ||
|
|
f2d3779fa9 |
0
.dockerignore
Executable file → Normal file
0
.dockerignore
Executable file → Normal file
4
.github/actions/setup-rust/action.yml
vendored
4
.github/actions/setup-rust/action.yml
vendored
@@ -33,7 +33,7 @@ runs:
|
||||
echo "CC=clang" >> $GITHUB_ENV
|
||||
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
@@ -46,7 +46,7 @@ runs:
|
||||
# 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
|
||||
- uses: pyTooling/Actions/with-post-step@v4.1.0
|
||||
with:
|
||||
main: bash ./.github/actions/setup-rust/cleanup.sh
|
||||
post: bash ./.github/actions/setup-rust/cleanup.sh
|
||||
|
||||
100
.github/workflows/ci.yml
vendored
100
.github/workflows/ci.yml
vendored
@@ -2,7 +2,9 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches:
|
||||
- master
|
||||
- rebis-dev
|
||||
tags:
|
||||
- "v**"
|
||||
pull_request:
|
||||
@@ -17,7 +19,7 @@ jobs:
|
||||
style:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
@@ -30,37 +32,54 @@ jobs:
|
||||
run: cargo fmt --check
|
||||
- name: Check clippy
|
||||
run: cargo clippy --no-deps --all-targets
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
read-msrv:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
msrv: ${{ steps.read-declared-msrv.outputs.msrv }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- id: read-declared-msrv
|
||||
name: Read msrv from Cargo.toml rust_version field
|
||||
run: echo "msrv=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name = "scryer-prolog") | ."rust_version"')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
|
||||
build-test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [read-msrv]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# 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 }
|
||||
- { os: macos-latest, rust-version: stable, target: 'x86_64-apple-darwin', 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: nightly, target: 'wasm32-unknown-unknown', publish: true, args: '--no-default-features' , test-args: '--no-run --no-default-features', use_swap: true }
|
||||
# Cargo.toml rust-version
|
||||
- { os: ubuntu-22.04, rust-version: "${{ needs.read-msrv.outputs.msrv }}" , 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'}
|
||||
- { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu', miri: true, components: "miri"}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actionhippie/swap-space@v1
|
||||
if: matrix.use_swap
|
||||
with:
|
||||
size: 10G
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
rust-version: ${{ matrix.rust-version }}
|
||||
targets: ${{ matrix.target }}
|
||||
cache-context: ${{ matrix.os }}
|
||||
components: ${{ matrix.components }}
|
||||
|
||||
# Build and test.
|
||||
- name: Build library
|
||||
@@ -68,6 +87,10 @@ jobs:
|
||||
- name: Test
|
||||
run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
|
||||
|
||||
- name: Check miri
|
||||
if: matrix.miri
|
||||
run: cargo miri test
|
||||
|
||||
# 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.
|
||||
@@ -76,25 +99,36 @@ jobs:
|
||||
run: |
|
||||
cargo rustc --target ${{ matrix.target }} ${{ matrix.args }} --verbose --bin scryer-prolog --release
|
||||
echo "$PWD/target/release" >> $GITHUB_PATH
|
||||
|
||||
- name: Install cargo-deb for creating debian packages
|
||||
if: ${{ matrix.publish && contains(matrix.target, 'linux') }}
|
||||
run: cargo install cargo-deb --force --locked
|
||||
|
||||
- name: Build release debian packages
|
||||
if: ${{ matrix.publish && contains(matrix.target, 'linux') }}
|
||||
run: cargo deb --target ${{ matrix.target }}
|
||||
|
||||
- name: Publish release binary artifact
|
||||
if: matrix.publish
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: target/${{ matrix.target }}/release/scryer-prolog*
|
||||
path: |
|
||||
target/${{ matrix.target }}/release/scryer-prolog*
|
||||
target/${{ matrix.target }}/debian/scryer-prolog*.deb
|
||||
name: scryer-prolog_${{ matrix.os }}_${{ matrix.target }}
|
||||
|
||||
logtalk-test:
|
||||
# if: false # uncomment to disable job
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-test]
|
||||
steps:
|
||||
# Download prebuilt ubuntu binary from build-test job, setup logtalk
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: scryer-prolog_ubuntu-20.04_x86_64-unknown-linux-gnu
|
||||
name: scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu
|
||||
- run: |
|
||||
chmod +x scryer-prolog
|
||||
echo "$PWD" >> "$GITHUB_PATH"
|
||||
chmod +x release/scryer-prolog
|
||||
echo "$PWD/release" >> "$GITHUB_PATH"
|
||||
- name: Install Logtalk
|
||||
uses: logtalk-actions/setup-logtalk@master
|
||||
with:
|
||||
@@ -113,12 +147,12 @@ jobs:
|
||||
|| echo "::warning ::logtalk compliance suite failed"
|
||||
# -u "https://github.com/LogtalkDotOrg/logtalk3/tree/$LOGTALK_GIT_HASH/tests/prolog/" \
|
||||
- name: Publish Logtalk test logs
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logtalk-test-logs
|
||||
path: '${{ env.LOGTALKUSER }}/tests/prolog/logtalk_tester_logs'
|
||||
- name: Publish Logtalk test results artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logtalk-test-results
|
||||
path: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml'
|
||||
@@ -133,24 +167,26 @@ jobs:
|
||||
report:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Rust
|
||||
uses: ./.github/actions/setup-rust
|
||||
with:
|
||||
rust-version: stable
|
||||
targets: x86_64-unknown-linux-gnu
|
||||
cache-context: report
|
||||
- run: |
|
||||
- name: Install CLI tools
|
||||
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
|
||||
version=`yq -ptoml -oj -r '.package[] | select(.name == "iai-callgrind") | .version' Cargo.lock`
|
||||
echo installing iai-callgrind "$version"
|
||||
cargo install iai-callgrind-runner --force --version "$version"
|
||||
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
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cargo-test-results
|
||||
path: cargo_test_results.xml
|
||||
@@ -169,7 +205,7 @@ jobs:
|
||||
- run: cargo bench --bench run_criterion -- --profile-time 60
|
||||
|
||||
- name: Publish benchmark results
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: benchmark-results
|
||||
path: |
|
||||
@@ -183,20 +219,20 @@ jobs:
|
||||
needs: [build-test]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
- name: Zip binaries for release
|
||||
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_ubuntu-22.04.zip ./scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu/scryer-prolog
|
||||
zip scryer-prolog_macos-latest.zip ./scryer-prolog_macos-latest_x86_64-apple-darwin/scryer-prolog
|
||||
zip scryer-prolog_ubuntu-22.04_i686.zip ./scryer-prolog_ubuntu-22.04_i686-unknown-linux-gnu/release/scryer-prolog ./scryer-prolog_ubuntu-22.04_i686-unknown-linux-gnu/debian/scryer-prolog*.deb
|
||||
zip scryer-prolog_ubuntu-22.04_x86_64.zip ./scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu/release/scryer-prolog ./scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu/debian/scryer-prolog*.deb
|
||||
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
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
scryer-prolog_macos-11.zip
|
||||
scryer-prolog_ubuntu-20.04.zip
|
||||
scryer-prolog_ubuntu-22.04.zip
|
||||
scryer-prolog_macos-latest.zip
|
||||
scryer-prolog_ubuntu-22.04_i686.zip
|
||||
scryer-prolog_ubuntu-22.04_x86_64.zip
|
||||
scryer-prolog_windows-latest.zip
|
||||
scryer-prolog_wasm32.zip
|
||||
|
||||
16
.github/workflows/docker-publish.yml
vendored
16
.github/workflows/docker-publish.yml
vendored
@@ -14,17 +14,17 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Workaround: https://github.com/docker/build-push-action/issues/461
|
||||
- name: Setup Docker buildx
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
uses: docker/setup-buildx-action@v2.2.1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Login against Docker registry
|
||||
- name: Log into registry
|
||||
# https://github.com/docker/login-action
|
||||
uses: docker/login-action@v2.1.0
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
# https://github.com/docker/metadata-action
|
||||
uses: docker/metadata-action@v4.1.1
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/scryer-prolog
|
||||
tags: |
|
||||
@@ -47,7 +47,13 @@ jobs:
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
# https://github.com/docker/build-push-action
|
||||
uses: docker/build-push-action@v3.2.0
|
||||
uses: docker/build-push-action@v6
|
||||
# v4 adds SLSA Provenance attestation which is
|
||||
# - unsupported by AWS Lambda
|
||||
# - limited support by Google Cloud Run
|
||||
# > If deploying a multi-architecture image, the manifest list must include linux/amd64.
|
||||
# see https://github.com/docker/build-push-action/releases/tag/v4.0.0
|
||||
# we might want to disable this if its a problem for someone
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
src/static_atoms.rs
|
||||
target/
|
||||
|
||||
.direnv/
|
||||
|
||||
|
||||
|
||||
2601
Cargo.lock
generated
2601
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
135
Cargo.toml
135
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "scryer-prolog"
|
||||
version = "0.9.4"
|
||||
version = "0.10.0"
|
||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "A modern Prolog implementation written mostly in Rust."
|
||||
@@ -10,7 +10,8 @@ license = "BSD-3-Clause"
|
||||
keywords = ["prolog", "prolog-interpreter", "prolog-system"]
|
||||
categories = ["command-line-utilities"]
|
||||
build = "build/main.rs"
|
||||
rust-version = "1.70"
|
||||
# Remember to check CI
|
||||
rust-version = "1.85"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
@@ -22,74 +23,88 @@ repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"]
|
||||
hostname = ["dep:hostname"]
|
||||
tls = ["dep:native-tls"]
|
||||
http = ["dep:warp", "dep:reqwest"]
|
||||
rust_beta_channel = []
|
||||
crypto-full = []
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "deny", check-cfg = [
|
||||
'cfg(rust_version, values("1.87.0"))',
|
||||
] }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
indexmap = "1.0.2"
|
||||
proc-macro2 = "1.0.36"
|
||||
quote = "1.0.15"
|
||||
strum = "0.23"
|
||||
strum_macros = "0.23"
|
||||
syn = { version = "2.0.32", features = ['full', 'visit', 'extra-traits'] }
|
||||
indexmap = "2.3.0"
|
||||
proc-macro2 = "1.0.86"
|
||||
quote = "1.0.36"
|
||||
strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
syn = { version = "2.0.72", features = ['full', 'visit', 'extra-traits'] }
|
||||
to-syn-value = "0.1.1"
|
||||
to-syn-value_derive = "0.1.1"
|
||||
version_check = "0.9.5"
|
||||
walkdir = "2"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.12.3"
|
||||
bit-set = "0.5.3"
|
||||
arcu = { version = "0.1.2", features = ["thread_local_counter"] }
|
||||
base64 = "0.22.1"
|
||||
bit-set = "0.8.0"
|
||||
bitvec = "1"
|
||||
blake2 = "0.8.1"
|
||||
blake2 = "0.10.6"
|
||||
bytes = "1"
|
||||
chrono = "0.4.11"
|
||||
chrono = "0.4.38"
|
||||
cpu-time = "1.0.0"
|
||||
crrl = "0.6.0"
|
||||
dashu = "0.4.0"
|
||||
derive_deref = "1.1.1"
|
||||
crrl = "0.9.0"
|
||||
dashu = { version = "0.4.2", features = ["rand"] }
|
||||
derive_more = "0.99.18"
|
||||
dirs-next = "2.0.0"
|
||||
divrem = "0.1.0"
|
||||
divrem = "1.0.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"
|
||||
libloading = "0.7"
|
||||
git-version = "0.3.9"
|
||||
indexmap = "2.3.0"
|
||||
lazy_static = "1.5.0"
|
||||
lexical = "7.0.4"
|
||||
libc = "0.2.155"
|
||||
libloading = "0.8"
|
||||
scryer-modular-bitfield = "0.11.4"
|
||||
num-order = { version = "1.2.0" }
|
||||
ordered-float = "2.6.0"
|
||||
phf = { version = "0.9", features = ["macros"] }
|
||||
ordered-float = "5.0.0"
|
||||
phf = { version = "0.11", features = ["macros"] }
|
||||
puruspe = "0.4.1"
|
||||
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"
|
||||
roxmltree = "0.11.0"
|
||||
ryu = "1.0.9"
|
||||
select = "0.6.0"
|
||||
sha3 = "0.8.2"
|
||||
smallvec = "1.8.0"
|
||||
ring = { version = "0.17.8", features = ["wasm32_unknown_unknown_js"] }
|
||||
ripemd = "0.1.3"
|
||||
roxmltree = "0.20.0"
|
||||
ryu = "1.0.18"
|
||||
sha3 = "0.10.8"
|
||||
smallvec = "1.13.2"
|
||||
static_assertions = "1.1.0"
|
||||
|
||||
serde_json = "1.0.95"
|
||||
serde = "1.0.159"
|
||||
scraper = { version = "0.23.1", default-features = false, features = [
|
||||
"errors",
|
||||
] }
|
||||
ego-tree = "0.10.0"
|
||||
|
||||
|
||||
serde_json = "1.0.122"
|
||||
serde = "1.0.204"
|
||||
parking_lot = "0.12.4"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
crossterm = { version = "0.20.0", optional = true }
|
||||
ctrlc = { version = "3.2.2", optional = true }
|
||||
hostname = { version = "0.3.1", optional = true }
|
||||
libffi = { version = "3.2.0", optional = true }
|
||||
native-tls = { version = "0.2.4", 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 }
|
||||
crossterm = { version = "0.28.1", optional = true }
|
||||
ctrlc = { version = "3.4.4", optional = true }
|
||||
hostname = { version = "0.4.0", optional = true }
|
||||
libffi = { version = "4.0.0", optional = true }
|
||||
native-tls = { version = "0.2.12", optional = true }
|
||||
# the version requirement of reqwest is kept low for compatibility with old deno versions
|
||||
# that pin reqwest to 0.11.20
|
||||
reqwest = { version = "0.11.0", optional = true }
|
||||
rustyline = { version = "14.0.0", optional = true }
|
||||
tokio = { version = "1.39.2", features = ["full"] }
|
||||
warp = { version = "0.3.7", features = ["tls"], optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
getrandom = { version = "0.2.10", features = ["js"] }
|
||||
tokio = { version = "1.28.2", features = [
|
||||
getrandom = { version = "0.2.15", features = ["js"] }
|
||||
tokio = { version = "1.39.2", features = [
|
||||
"sync",
|
||||
"macros",
|
||||
"io-util",
|
||||
@@ -99,9 +114,9 @@ tokio = { version = "1.28.2", features = [
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
|
||||
console_error_panic_hook = "0.1"
|
||||
wasm-bindgen = "0.2.87"
|
||||
wasm-bindgen = "0.2.92"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
serde-wasm-bindgen = "0.5"
|
||||
serde-wasm-bindgen = "0.6"
|
||||
web-sys = { version = "0.3", features = [
|
||||
"Document",
|
||||
"Window",
|
||||
@@ -109,17 +124,17 @@ web-sys = { version = "0.3", features = [
|
||||
"Performance",
|
||||
] }
|
||||
js-sys = "0.3"
|
||||
ouroboros = "0.18"
|
||||
|
||||
[dev-dependencies]
|
||||
current_platform = "0.2.0"
|
||||
maplit = "1.0.2"
|
||||
predicates-core = "1.0.2"
|
||||
serial_test = "2.0.0"
|
||||
serial_test = "3.1.1"
|
||||
|
||||
[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"
|
||||
iai-callgrind = "0.12.1"
|
||||
trycmd = "0.15.6"
|
||||
|
||||
[target.'cfg(not(any(target_os = "windows", all(target_arch = "wasm32", target_os = "unknown"))))'.dev-dependencies]
|
||||
pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] }
|
||||
@@ -131,6 +146,18 @@ opt-level = 3
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = 3
|
||||
debug = 2
|
||||
|
||||
[profile.wasm-dev]
|
||||
inherits = "dev"
|
||||
opt-level = 1
|
||||
lto = "off"
|
||||
|
||||
[profile.wasm-release]
|
||||
inherits = "release"
|
||||
lto = "off"
|
||||
panic = "abort"
|
||||
codegen-units = 256
|
||||
|
||||
[[bench]]
|
||||
name = "run_criterion"
|
||||
|
||||
17
Dockerfile
Executable file → Normal file
17
Dockerfile
Executable file → Normal file
@@ -1,18 +1,19 @@
|
||||
# See https://github.com/LukeMathWalker/cargo-chef
|
||||
ARG RUST_VERSION=1-buster
|
||||
FROM rust:${RUST_VERSION} as planner
|
||||
ARG DEBIAN_RELEASE=bookworm
|
||||
ARG RUST_VERSION=1-${DEBIAN_RELEASE}
|
||||
FROM rust:${RUST_VERSION} AS planner
|
||||
WORKDIR /scryer-prolog
|
||||
RUN cargo install cargo-chef
|
||||
COPY . .
|
||||
RUN cargo chef prepare --recipe-path recipe.json
|
||||
|
||||
FROM rust:${RUST_VERSION} as cacher
|
||||
FROM rust:${RUST_VERSION} AS cacher
|
||||
WORKDIR /scryer-prolog
|
||||
RUN cargo install cargo-chef
|
||||
COPY --from=planner /scryer-prolog/recipe.json recipe.json
|
||||
RUN cargo chef cook --release --recipe-path recipe.json
|
||||
|
||||
FROM rust:${RUST_VERSION} as builder
|
||||
FROM rust:${RUST_VERSION} AS builder
|
||||
WORKDIR /scryer-prolog
|
||||
COPY . .
|
||||
# Copy over the cached dependencies
|
||||
@@ -20,9 +21,11 @@ COPY --from=cacher /scryer-prolog/target target
|
||||
COPY --from=cacher $CARGO_HOME $CARGO_HOME
|
||||
RUN cargo build --release --bin scryer-prolog
|
||||
|
||||
# Newer versions of Debian (i.e. bookworm) contain libssl3 instead of libssl1.1
|
||||
# which we depend on.
|
||||
FROM debian:bullseye-slim
|
||||
FROM debian:${DEBIAN_RELEASE}-slim
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends openssl \
|
||||
&& apt-get clean -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
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)
|
||||
|
||||
11
INDEX.dj
11
INDEX.dj
@@ -55,11 +55,14 @@ the builtin Prolog modules and libraries in Scryer, check the documentation site
|
||||
|
||||
## Downloads
|
||||
|
||||
The latest version of Scryer Prolog is *0.9.3*. And it's already useful for lots of tasks.
|
||||
The latest version of Scryer Prolog is *0.9.4*. And it's already useful for lots of tasks.
|
||||
|
||||
| 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) |
|
||||
| Windows (64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_windows-latest_x86_64-pc-windows-msvc.zip) |
|
||||
| macOS (Intel) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_macos-11_x86_64-apple-darwin.zip) |
|
||||
| macOS (ARM) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog-macos-arm.zip) |
|
||||
| Linux (Ubuntu 20.04, 64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_ubuntu-20.04_x86_64-unknown-linux-gnu.zip) |
|
||||
| Linux (Ubuntu 22.04, 64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu.zip) |
|
||||
| Linux (Ubuntu 22.04, 32 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_ubuntu-22.04_i686-unknown-linux-gnu.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.
|
||||
|
||||
|
||||
230
README.md
230
README.md
@@ -1,4 +1,3 @@
|
||||
|
||||
# Scryer Prolog
|
||||
|
||||
Scryer Prolog aims to become to ISO Prolog what GHC is to Haskell: an open
|
||||
@@ -73,7 +72,7 @@ Extend Scryer Prolog to include the following, among other features:
|
||||
(`atom`, `var`, etc) with if/else ladders. (_in progress_)
|
||||
- [ ] Inlining all built-ins and system call instructions.
|
||||
- [x] Greatly reducing the number of instructions used to compile disjunctives.
|
||||
- [ ] Storing short atoms to heap cells without writing them to the atom table.
|
||||
- [x] Storing short atoms to heap cells without writing them to the atom table.
|
||||
- [ ] A compacting garbage collector satisfying the five properties of
|
||||
"[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_)
|
||||
- [ ] Mode declarations.
|
||||
@@ -113,12 +112,12 @@ strings.
|
||||
Precompiled binaries for several platforms are available for download
|
||||
at:
|
||||
|
||||
**https://github.com/mthom/scryer-prolog/releases/tag/v0.9.3**
|
||||
**https://github.com/mthom/scryer-prolog/releases/latest**
|
||||
|
||||
### Native Compilation
|
||||
|
||||
First, install the latest stable version of
|
||||
[Rust](https://www.rust-lang.org/en-US/install.html) using your
|
||||
[Rust](https://www.rust-lang.org/tools/install) using your
|
||||
preferred method. Scryer tends to use features from newer Rust
|
||||
releases, whereas Rust packages in Linux distributions, Macports,
|
||||
etc. tend to lag behind. [rustup](http://rustup.rs) will keep your
|
||||
@@ -126,9 +125,13 @@ Rust updated to the latest stable release; any existing Rust
|
||||
distribution should be uninstalled from your system before rustup is
|
||||
used.
|
||||
|
||||
Currently the only way to install the latest version of Scryer is to
|
||||
clone directly from this git repository, and compile the system. This
|
||||
can be done as follows:
|
||||
|
||||
> [!NOTE]
|
||||
> The minimum rust toolchain version required can be found in the [Cargo.toml](https://github.com/mthom/scryer-prolog/blob/master/Cargo.toml#L13)
|
||||
under the `package.rust-version` key.
|
||||
> The accuracy of this value is validated in CI
|
||||
|
||||
### From a local git checkout
|
||||
|
||||
```
|
||||
$> git clone https://github.com/mthom/scryer-prolog
|
||||
@@ -142,6 +145,32 @@ faster executable.
|
||||
After compilation, the executable `scryer-prolog` is available in the
|
||||
directory `target/release` and can be invoked to run the system.
|
||||
|
||||
### Via `cargo install`
|
||||
|
||||
#### From git
|
||||
|
||||
```
|
||||
cargo install --locked --git https://github.com/mthom/scryer-prolog.git
|
||||
```
|
||||
|
||||
Afterwards the `scryer-prolog` binary will be in the `$HOME/.cargo/bin` directory which is usually added to your PATH
|
||||
during the installation of the rust toolchain.
|
||||
|
||||
#### From Crates.io [](https://crates.io/crates/scryer-prolog) 
|
||||
|
||||
> [!NOTE]
|
||||
> The lates crates.io release can be significantly behind the version available in the git repository
|
||||
> The crates.io badge in this sections title is a link to the crates.io page.
|
||||
> The msrv badge in the section title referece to the minimum rust toolchain version required to compile the latest crates.io release
|
||||
|
||||
`scryer-prolog` is also release on crates.io and can be installed with
|
||||
|
||||
```
|
||||
cargo install --locked scryer-prolog
|
||||
```
|
||||
|
||||
### Caveats for Windows
|
||||
|
||||
On Windows, Scryer Prolog is easier to build inside a [MSYS2](https://www.msys2.org/)
|
||||
environment as some crates may require native C compilation. However,
|
||||
the resulting binary does not need MSYS2 to run. When executing Scryer in a shell, it is recommended to use a more advanced shell than mintty (the default MSYS2 shell). The [Windows Terminal](https://github.com/microsoft/terminal) works correctly.
|
||||
@@ -153,8 +182,6 @@ 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.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.
|
||||
@@ -170,57 +197,91 @@ Then a `pkg` directory will be created, containing everything you need for a web
|
||||
```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>`);
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Scryer Prolog - Sudoku Solver Example</title>
|
||||
<script type="module">
|
||||
import initScryer, { MachineBuilder } from "./pkg/scryer_prolog.js";
|
||||
|
||||
// Initialize Scryer Prolog with WASM
|
||||
const wasm = await fetch("./pkg/scryer_prolog_bg.wasm");
|
||||
const module = await WebAssembly.compile(await wasm.arrayBuffer());
|
||||
await initScryer(module);
|
||||
|
||||
// Set up the Prolog machine
|
||||
const machine = new MachineBuilder().build();
|
||||
|
||||
// Knowledge base: Sudoku rules and problem definition
|
||||
const kb = `
|
||||
:- 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 = [A,B,C,D,E,F,G,H,I],
|
||||
blocks(A, B, C),
|
||||
blocks(D, E, F),
|
||||
blocks(G, H, I).
|
||||
|
||||
blocks([], [], []).
|
||||
blocks([A,B,C|T1], [D,E,F|T2], [G,H,I|T3]) :-
|
||||
all_distinct([A,B,C,D,E,F,G,H,I]),
|
||||
blocks(T1, T2, T3).
|
||||
|
||||
problem(1, [[_,_,_,_,_,_,_,_,_],
|
||||
[_,_,_,_,_,3,_,8,5],
|
||||
[_,_,1,_,2,_,_,_,_],
|
||||
[_,_,_,5,_,7,_,_,_],
|
||||
[_,_,4,_,_,_,1,_,_],
|
||||
[_,9,_,_,_,_,_,_,_],
|
||||
[5,_,_,_,_,_,_,7,3],
|
||||
[_,_,2,_,1,_,_,_,_],
|
||||
[_,_,_,_,4,_,_,_,9]]).
|
||||
`;
|
||||
|
||||
machine.consultModuleString("user", kb);
|
||||
|
||||
// Run the query
|
||||
const query = "problem(1, Rows), sudoku(Rows), maplist(portray_clause, Rows).";
|
||||
const answers = machine.runQuery(query);
|
||||
|
||||
const formattedSolutions = [];
|
||||
|
||||
// Format the answers
|
||||
for (const solution of answers) {
|
||||
const rows = solution.bindings["Rows"].list;
|
||||
|
||||
const grid = rows.map(row =>
|
||||
row.list.map(cell => cell.integer)
|
||||
);
|
||||
|
||||
const formatted = grid.map(row => `[${row.join(", ")}]`).join("\n");
|
||||
formattedSolutions.push(formatted);
|
||||
}
|
||||
run();
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
|
||||
// Output results
|
||||
const solutionDiv = document.querySelector("#soduku-solution");
|
||||
for (const solution of formattedSolutions) {
|
||||
const newPre = document.createElement("pre");
|
||||
newPre.textContent = solution;
|
||||
solutionDiv.appendChild(newPre);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>Sudoku solver returns:</p>
|
||||
<div id="soduku-solution">
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
```
|
||||
|
||||
@@ -228,6 +289,12 @@ Then you can serve it with your favorite http server like `python -m http.server
|
||||
|
||||
### Docker Install
|
||||
|
||||
Pre-built [Docker images are available on Docker Hub](https://hub.docker.com/r/mjt128/scryer-prolog/tags).
|
||||
The `latest` tag reflects the state on `master`, which might be unstable.
|
||||
There are also tags for Scryer releases 0.9.2 and up.
|
||||
Note though, that the base images are not kept up to date at the moment,
|
||||
so be wary of security vulnerabilities (see [#2646](https://github.com/mthom/scryer-prolog/issues/2646)).
|
||||
|
||||
First, install [Docker](https://docs.docker.com/get-docker/) on Linux,
|
||||
Windows, or Mac.
|
||||
|
||||
@@ -462,7 +529,7 @@ is unified with a term that contains that variable as a proper
|
||||
subterm. For efficiency, the *occurs check* is omitted by default
|
||||
in Scryer Prolog and many other Prolog systems.
|
||||
|
||||
In Scryer Prolog, performing unifications which succeed only if the
|
||||
In Scryer Prolog, unifications which succeed only if the
|
||||
*occurs check* is omitted yield *cyclic terms*, also called
|
||||
*rational trees*. For example:
|
||||
|
||||
@@ -694,9 +761,13 @@ The modules that ship with Scryer Prolog are also called
|
||||
public key signatures and signature verification with Ed25519,
|
||||
ECDH key exchange over Curve25519 (X25519), authenticated symmetric
|
||||
encryption with ChaCha20-Poly1305, and reasoning about elliptic curves.
|
||||
* [`process`](src/lib/process.pl)
|
||||
Create and manage parallel processes.
|
||||
* [`uuid`](src/lib/uuid.pl) UUIDv4 generation and hex representation
|
||||
* [`tls`](src/lib/tls.pl)
|
||||
Predicates for negotiating TLS connections explicitly.
|
||||
* [`numerics/special_functions`](src/lib/numerics/special_functions.pl)
|
||||
Predicates for erf, gamma, beta, and related special functions.
|
||||
* [`ugraphs`](src/lib/ugraphs.pl) Graph manipulation library
|
||||
* [`simplex`](src/lib/simplex.pl) Providing `assignment/2`,
|
||||
`transportation/4` and other predicates for solving linear
|
||||
@@ -797,21 +868,42 @@ ideally suited for use in corporations and government agencies
|
||||
that are subject to strict regulations pertaining to interoperability,
|
||||
standards compliance and warranty.
|
||||
|
||||
Successful existing applications of Scryer Prolog include the
|
||||
[DocLog](https://github.com/aarroyoc/doclog) system which
|
||||
generates Scryer's own documentation and homepage, [Symbolic
|
||||
Analysis of Grants](https://www.brz.gv.at/en/BRZ-Tech-Blog/Tech-Blog-7-Symbolic-Analysis-of-Grants.html)
|
||||
by the Austrian Federal Computing Center, and parts of the
|
||||
[precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog)
|
||||
package for the analysis of dose-escalation trials in the
|
||||
safety-critical and highly regulated domain of oncology
|
||||
trial design, described in [*An Executable Specification of
|
||||
Oncology Dose-Escalation Protocols with Prolog*](https://arxiv.org/abs/2402.08334).
|
||||
Successful existing applications of Scryer Prolog include:
|
||||
|
||||
- [DocLog](https://github.com/aarroyoc/doclog) which generates
|
||||
Scryer's own documentation and homepage
|
||||
- [Grants4Companies](https://arxiv.org/abs/2406.15293): reasoning
|
||||
about business grants in the Austrian public administration
|
||||
- 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, described in [*An Executable Specification of
|
||||
Oncology Dose-Escalation Protocols with Prolog*](https://arxiv.org/abs/2402.08334)
|
||||
and culminating in [**DEDUCTION**](https://codeberg.org/dcnorris/DEDUCTION)
|
||||
- semantic reasoning and queries in [AD4M](https://github.com/coasys/ad4m),
|
||||
an agent-centric distributed application meta-ontology.
|
||||
|
||||
Scryer Prolog is also very well suited for teaching and learning
|
||||
Prolog, and for testing syntactic conformance and hence portability of
|
||||
existing Prolog programs.
|
||||
|
||||
## Scryer Prolog Meetups
|
||||
|
||||
Scryer Prolog Meetups are an excellent opportunity to present and get
|
||||
to know the latest developments in Scryer Prolog and its
|
||||
applications, to exchange ideas about current plans and
|
||||
future directions, and to discuss projects and visions
|
||||
in person.
|
||||
|
||||
- [Scryer Prolog Meetup 2023](https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2023/)
|
||||
in Düsseldorf, Germany. Its [announcement](https://github.com/mthom/scryer-prolog/discussions/1813)
|
||||
and [discussion](https://github.com/mthom/scryer-prolog/discussions/2160).
|
||||
- [Scryer Prolog Meetup 2024](https://www.digitalaustria.gv.at/wissenswertes/events/scryerprologmeetup2024)
|
||||
in Vienna, Austria. Its [announcement and discussion](https://github.com/mthom/scryer-prolog/discussions/2377).
|
||||
- **Save the date:** The [Scryer Prolog Meetup 2025](https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2025/)
|
||||
will take place on Nov. 13th and 14th 2025 in Düsseldorf, Germany.
|
||||
Its [announcement](https://github.com/mthom/scryer-prolog/discussions/2948).
|
||||
|
||||
## Support and discussions
|
||||
|
||||
If Scryer Prolog crashes or yields unexpected errors, consider filing
|
||||
|
||||
@@ -5,7 +5,7 @@ mod setup;
|
||||
mod iai {
|
||||
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||
|
||||
use scryer_prolog::machine::parsed_results::QueryResolution;
|
||||
use scryer_prolog::LeafAnswer;
|
||||
|
||||
use super::setup;
|
||||
|
||||
@@ -13,7 +13,7 @@ mod iai {
|
||||
#[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 {
|
||||
fn bench(mut run: impl FnMut() -> Vec<LeafAnswer>) -> Vec<LeafAnswer> {
|
||||
run()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use std::{collections::BTreeMap, fs, path::Path};
|
||||
|
||||
use maplit::btreemap;
|
||||
use scryer_prolog::machine::{
|
||||
parsed_results::{QueryResolution, Value},
|
||||
Machine,
|
||||
};
|
||||
use scryer_prolog::{LeafAnswer, Machine, MachineBuilder, Term};
|
||||
|
||||
pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
|
||||
[
|
||||
@@ -13,21 +10,21 @@ pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
|
||||
"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() },
|
||||
btreemap! { "Count" => Term::integer(2869176) },
|
||||
),
|
||||
(
|
||||
"numlist",
|
||||
"benches/numlist.pl",
|
||||
"run_numlist(1000000, Head).",
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Head" => Value::try_from("1".to_string()).unwrap()},
|
||||
btreemap! { "Head" => Term::integer(1) },
|
||||
),
|
||||
(
|
||||
"csv_codename",
|
||||
"benches/csv.pl",
|
||||
"get_codename(\"0020\",Name).",
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Name" => Value::try_from("SPACE".to_string()).unwrap()},
|
||||
btreemap! { "Name" => Term::string("SPACE") },
|
||||
),
|
||||
]
|
||||
.map(|b| {
|
||||
@@ -51,12 +48,13 @@ pub enum Strategy {
|
||||
Reuse,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct PrologBenchmark {
|
||||
pub name: &'static str,
|
||||
pub filename: &'static str,
|
||||
pub query: &'static str,
|
||||
pub strategy: Strategy,
|
||||
pub bindings: BTreeMap<&'static str, Value>,
|
||||
pub bindings: BTreeMap<&'static str, Term>,
|
||||
}
|
||||
|
||||
impl PrologBenchmark {
|
||||
@@ -66,28 +64,34 @@ impl PrologBenchmark {
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap();
|
||||
let mut machine = Machine::new_lib();
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
machine.load_module_string(module_name, program);
|
||||
machine
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
pub fn setup(&self) -> impl FnMut() -> QueryResolution {
|
||||
pub fn setup(&self) -> impl FnMut() -> Vec<LeafAnswer> {
|
||||
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()
|
||||
black_box(
|
||||
machine
|
||||
.run_query(black_box(query))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
#[test]
|
||||
fn validate_benchmarks() {
|
||||
use super::prolog_benches;
|
||||
use scryer_prolog::machine::parsed_results::{QueryMatch, QueryResolution};
|
||||
use scryer_prolog::LeafAnswer;
|
||||
use std::{fmt::Write, fs};
|
||||
|
||||
struct BenchResult {
|
||||
@@ -102,10 +106,13 @@ mod test {
|
||||
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 result: Vec<_> = machine
|
||||
.run_query(r.query)
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
let query_inference_count = machine.get_inference_count() - setup_inference_count;
|
||||
|
||||
let expected = QueryResolution::Matches(vec![QueryMatch::from(r.bindings.clone())]);
|
||||
let expected = [LeafAnswer::from_bindings(r.bindings.clone())];
|
||||
assert_eq!(result, expected, "validating benchmark {}", r.name);
|
||||
|
||||
results.push(BenchResult {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,43 +5,48 @@ use instructions_template::generate_instructions_rs;
|
||||
use static_string_indexing::index_static_strings;
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::path::MAIN_SEPARATOR_STR;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
fn find_prolog_files(libraries: &mut File, prefix: &str, current_dir: &Path) {
|
||||
fn find_prolog_files(path_prefix: &str, current_dir: &Path) -> Vec<(String, PathBuf)> {
|
||||
let mut libraries = vec![];
|
||||
|
||||
let entries = match current_dir.read_dir() {
|
||||
Ok(entries) => entries,
|
||||
Err(_) => return,
|
||||
Err(_) => return libraries,
|
||||
};
|
||||
|
||||
for entry in entries.filter_map(Result::ok).map(|e| e.path()) {
|
||||
if entry.is_dir() {
|
||||
if let Some(file_name) = entry.file_name() {
|
||||
let new_prefix = prefix.to_owned() + file_name.to_str().unwrap() + "/";
|
||||
find_prolog_files(libraries, &new_prefix, &entry);
|
||||
let file_name = file_name.to_str().unwrap();
|
||||
let new_path_prefix = format!("{path_prefix}{file_name}/");
|
||||
let new_libs = find_prolog_files(&new_path_prefix, &entry);
|
||||
libraries.extend(new_libs);
|
||||
}
|
||||
} else if entry.is_file() {
|
||||
let ext = std::ffi::OsStr::new("pl");
|
||||
if entry.extension() == Some(ext) {
|
||||
let contain = String::from_utf8(fs::read(&entry).unwrap()).unwrap();
|
||||
let name = entry.file_stem().unwrap().to_str().unwrap();
|
||||
let lib_name = format!("{path_prefix}{name}");
|
||||
|
||||
let line = format!(
|
||||
" m.insert(\"{}\",\n{:?});\n",
|
||||
prefix.to_owned() + name,
|
||||
contain
|
||||
);
|
||||
|
||||
libraries.write_all(line.as_bytes()).unwrap();
|
||||
libraries.push((lib_name, entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libraries
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if version_check::is_min_version("1.87.0").unwrap_or(false) {
|
||||
println!(r#"cargo:rustc-cfg=rust_version="1.87.0""#);
|
||||
}
|
||||
|
||||
let has_rustfmt = Command::new("rustfmt")
|
||||
.arg("--version")
|
||||
.stdin(Stdio::inherit())
|
||||
@@ -56,18 +61,22 @@ fn main() {
|
||||
let dest_path = Path::new(&out_dir).join("libraries.rs");
|
||||
|
||||
let mut libraries = File::create(dest_path).unwrap();
|
||||
let lib_path = Path::new("src/lib");
|
||||
let lib_path = Path::new("src").join("lib");
|
||||
|
||||
libraries
|
||||
.write_all(
|
||||
b"ref_thread_local::ref_thread_local! {
|
||||
pub(crate) static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
|
||||
let mut m = IndexMap::new();\n",
|
||||
let constants = find_prolog_files("", &lib_path);
|
||||
|
||||
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||
|
||||
writeln!(libraries, "{{").unwrap();
|
||||
for (name, lib_path) in constants {
|
||||
let path = format!("{}{}", MAIN_SEPARATOR_STR, lib_path.display());
|
||||
writeln!(
|
||||
libraries,
|
||||
"m.insert(\"{name}\", include_str!(concat!(env!(\"CARGO_MANIFEST_DIR\"), {path:?})));"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
find_prolog_files(&mut libraries, "", lib_path);
|
||||
libraries.write_all(b"\n m\n };\n}\n").unwrap();
|
||||
}
|
||||
writeln!(libraries, "}}").unwrap();
|
||||
|
||||
let instructions_path = Path::new(&out_dir).join("instructions.rs");
|
||||
let mut instructions_file = File::create(&instructions_path).unwrap();
|
||||
|
||||
@@ -84,6 +84,18 @@ impl<'ast> Visit<'ast> for StaticStrVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
const INLINED_ATOM_MAX_LEN: usize = 6;
|
||||
|
||||
fn static_string_index(string: &str, index: usize) -> u64 {
|
||||
if !string.is_empty() && string.len() <= INLINED_ATOM_MAX_LEN && !string.contains('\u{0}') {
|
||||
let mut string_buf: [u8; 8] = [0u8; 8];
|
||||
string_buf[..string.len()].copy_from_slice(string.as_bytes());
|
||||
(u64::from_le_bytes(string_buf) << 1) | 1
|
||||
} else {
|
||||
(index << 1) as u64
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStream {
|
||||
use quote::*;
|
||||
|
||||
@@ -114,14 +126,19 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
|
||||
match file.read_to_string(&mut src) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
panic!("error reading file: {:?}", e);
|
||||
panic!("error reading file: {e:?}");
|
||||
}
|
||||
}
|
||||
|
||||
let syntax = match syn::parse_file(&src) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
panic!("parse error: {} in file {:?}", e, path);
|
||||
println!("cargo::warning=parse error: {e} in file {path:?}");
|
||||
syn::File {
|
||||
shebang: None,
|
||||
attrs: vec![],
|
||||
items: vec![],
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(syntax)
|
||||
@@ -149,28 +166,44 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
|
||||
visitor.visit_file(&syntax)
|
||||
}
|
||||
|
||||
let indices = (0..visitor.static_strs.len()).map(|i| (i << 3) as u64);
|
||||
let indices_iter = indices.clone();
|
||||
let mut static_str_keys = vec![];
|
||||
let mut static_strs = vec![];
|
||||
let mut static_str_indices = vec![];
|
||||
|
||||
let static_strs_len = visitor.static_strs.len();
|
||||
let static_strs: &Vec<_> = &visitor.static_strs.into_iter().collect();
|
||||
let indices: Vec<u64> = visitor
|
||||
.static_strs
|
||||
.iter()
|
||||
.map(|string| {
|
||||
let index = static_string_index(string, static_strs.len());
|
||||
|
||||
static_str_keys.push(string);
|
||||
|
||||
if index & 1 == 1 {
|
||||
index
|
||||
} else {
|
||||
static_str_indices.push(index);
|
||||
static_strs.push(string);
|
||||
index
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let static_strs_len = static_strs.len();
|
||||
|
||||
quote! {
|
||||
use phf;
|
||||
|
||||
static STRINGS: [&str; #static_strs_len] = [
|
||||
#(
|
||||
#static_strs,
|
||||
)*
|
||||
];
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! atom {
|
||||
#((#static_strs) => { Atom { index: #indices_iter } };)*
|
||||
#((#static_str_keys) => { Atom { index: #indices } };)*
|
||||
($name:literal) => {compile_error!(concat!("unknown static atom ", $name))};
|
||||
}
|
||||
|
||||
pub static STATIC_ATOMS_MAP: phf::Map<&'static str, Atom> = phf::phf_map! {
|
||||
#(#static_strs => { Atom { index: #indices } },)*
|
||||
#(#static_strs => { Atom { index: #static_str_indices } },)*
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
14
doclog.config.pl
Normal file
14
doclog.config.pl
Normal file
@@ -0,0 +1,14 @@
|
||||
project_name("Scryer Prolog").
|
||||
readme_file("INDEX.dj").
|
||||
source_lib_folder("src/lib").
|
||||
websource("https://github.com/mthom/scryer-prolog/tree/master/src/lib").
|
||||
omit(["ops_and_meta_predicates.pl", "tabling"]).
|
||||
learn_pages_source_folder("learn").
|
||||
learn_pages_categories(["First steps", "Tutorials"]).
|
||||
learn_pages([
|
||||
page("Let's play Brisca", "Tutorials", "lets-play-brisca.dj"),
|
||||
page("Test page", "First steps", "test-page.dj")
|
||||
]).
|
||||
copy_file("logo/scryer.png", "scryer.png").
|
||||
copy_file("learn/Spanish_deck_Fournier.jpg", "learn/Spanish_deck_Fournier.jpg").
|
||||
copy_file("learn/brisca-interactive.png", "learn/brisca-interactive.png").
|
||||
82
flake.lock
generated
Normal file
82
flake.lock
generated
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1744868846,
|
||||
"narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745289264,
|
||||
"narHash": "sha256-7nt+UJ7qaIUe2J7BdnEEph9n2eKEwxUwKS/QIr091uA=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "3b7171858c20d5293360042936058fb0c4cb93a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
85
flake.nix
Normal file
85
flake.nix
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
description = "A modern Prolog implementation written mostly in Rust";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, flake-utils, rust-overlay, ... }:
|
||||
let
|
||||
meta = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).package;
|
||||
inherit (meta) name version;
|
||||
overlays = [
|
||||
(import rust-overlay)
|
||||
(self: super: {
|
||||
rustToolchainDev = super.rust-bin.stable.latest.default.override {
|
||||
extensions = [ "rust-src" "rust-analyzer" ];
|
||||
};
|
||||
rustToolchainDevWasm = super.rust-bin.stable.latest.default.override {
|
||||
extensions = [ "rust-src" "rust-analyzer" ];
|
||||
targets = [ "wasm32-unknown-unknown" ];
|
||||
};
|
||||
rustToolchainNightly = super.rust-bin.selectLatestNightlyWith (toolchain:
|
||||
toolchain.default.override {
|
||||
extensions = [ "rust-src" "rust-analyzer" "miri" ];
|
||||
}
|
||||
);
|
||||
})
|
||||
];
|
||||
in flake-utils.lib.eachDefaultSystem(system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system overlays; };
|
||||
nativeBuildInputs = with pkgs; [ pkg-config ];
|
||||
buildInputs = with pkgs; [ openssl ] ++
|
||||
lib.optionals pkgs.stdenv.isDarwin [
|
||||
pkgs.darwin.apple_sdk.frameworks.SystemConfiguration
|
||||
];
|
||||
in
|
||||
{
|
||||
devShells = {
|
||||
default = pkgs.mkShell.override { stdenv = pkgs.clangMultiStdenv; } {
|
||||
nativeBuildInputs = nativeBuildInputs;
|
||||
buildInputs = buildInputs ++ (with pkgs; [
|
||||
rustToolchainDev
|
||||
]);
|
||||
};
|
||||
wasm-js = pkgs.mkShell.override { stdenv = pkgs.clangMultiStdenv; } {
|
||||
nativeBuildInputs = nativeBuildInputs;
|
||||
buildInputs = buildInputs ++ (with pkgs; [
|
||||
wasm-pack
|
||||
rustToolchainDevWasm
|
||||
]);
|
||||
TARGET_CC = "${pkgs.clangMultiStdenv.cc}/bin/clang";
|
||||
hardeningDisable = [ "all" ];
|
||||
};
|
||||
# For use with Miri and stuff like it
|
||||
nightly = pkgs.mkShell {
|
||||
nativeBuildInputs = nativeBuildInputs;
|
||||
buildInputs = buildInputs ++ (with pkgs; [
|
||||
rustToolchainNightly
|
||||
]);
|
||||
};
|
||||
};
|
||||
|
||||
packages = rec {
|
||||
default = scryer-prolog;
|
||||
scryer-prolog = pkgs.rustPlatform.buildRustPackage {
|
||||
pname = name;
|
||||
inherit version;
|
||||
src = ./.;
|
||||
nativeBuildInputs = nativeBuildInputs;
|
||||
buildInputs = buildInputs;
|
||||
cargoLock = {
|
||||
lockFile = ./Cargo.lock;
|
||||
};
|
||||
release = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
BIN
learn/Spanish_deck_Fournier.jpg
Normal file
BIN
learn/Spanish_deck_Fournier.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 629 KiB |
BIN
learn/brisca-interactive.png
Normal file
BIN
learn/brisca-interactive.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
457
learn/lets-play-brisca.dj
Normal file
457
learn/lets-play-brisca.dj
Normal file
@@ -0,0 +1,457 @@
|
||||
# Let's play Brisca
|
||||
|
||||
In this article, we'll see how to use modern Prolog to model the game of [Brisca](https://en.wikipedia.org/wiki/Brisca). First things first, what is Brisca?
|
||||
|
||||
## Rules of the game
|
||||
|
||||
Brisca is a traditional card game from Spain, although similar games both in name and in rules are spread across the Mediterranean. It's also similar to Tute. It is played using the traditional Spanish deck, usually with 40 cards in 4 suites (oros, espadas, bastos, copas). Here you have the famous Castillian pattern by Heraclio Fournier, the one I play in my family with my grandparents.
|
||||
|
||||

|
||||
|
||||
How do you play it? Basically, players play in rounds. Every round each player on the table (usually four), selects one of his own three cards to leave one in the middle, and they do that in order. There are some rules, which I will explain later, and one person wins the round, taking all the cards with him. Players take new cards from the stock. Some cards get you points, some others don't. After there are no more cards in player's hands, we count the points, the players with more points, wins!
|
||||
|
||||
The rules for knowing which players takes the round are the following:
|
||||
- Every game there's a trump suite, which was selected as a random card from the stock at the same time when the players got their three initial cards.
|
||||
- The first player in a round (the winner of the previous round) can choose whatever card he wants.
|
||||
- The rest of the players, in order to win the round, they need to improve the card of the first player. They can put a higher card of the same suite as the first player.
|
||||
- Or they can put a card from the trump suite, which is always better than a card from a non-trump suite.
|
||||
- But between cards in the trump suite, there's still an order.
|
||||
- However players are not required to put cards that improve the game if they don't wish to do that, they can just lose the round to discard themselves a bad card.
|
||||
- The order is the following: As (ace, number 1) -> 3 -> Rey (king, number 12) -> Caballo (horse, but usually it's a knight, number 11) -> Sota (a page, number 10) -> 7 -> 6 -> 5 -> 4 -> 2.
|
||||
|
||||
## Modeling cards
|
||||
|
||||
First, we need to decide a representation of our cards. Coming from another languages we can think that a good representation might be a class or a struct, with two fields, one for the number and the other for the suite, but Prolog doesn't have objects. We can use a list with two elements. But lists are better when we're dealing with variable length data. We could also use a compound term. This is the right choice if our fields are fixed.
|
||||
|
||||
A compound term is defined by an atom, followed by the data itself enclosed by parenthesis and separated by comma. Like this: `card(oros, 4)`. Yes, very similar to predicates. In fact the only difference is how we use them, because they're the same. If we pass a compund term in the first level of a query, or inside a call/N, Prolog will treat it as code instead of data. This is one the the examples of Prolog being a homoiconic language.
|
||||
|
||||
We can go further, Prolog is very flexible and we can define custom operators easily if we want. Those are also compound terms, but with a different syntax. There's an operator already defined that is very useful for us: the dash. We can just join two pieces of data with a dash, and they'll be together in the same structure. This is usually called "pair".
|
||||
|
||||
So, using pairs we can model cards like this: `oros-4`, `espadas-7`.
|
||||
|
||||
We can code a predicate that defines valid cards:
|
||||
|
||||
```
|
||||
card(Suite-Number) :-
|
||||
member(Suite, [oros, espadas, bastos, copas]),
|
||||
member(Number, [rey, caballo, sota, 7, 6, 5, 4, 3, 2, as]).
|
||||
```
|
||||
|
||||
## Counting points
|
||||
|
||||
We are going to implement a counting predicate. The suite doesn't matter, only the number. First, we define a `card_score/2` predicate which relates a card to a score and viceversa.
|
||||
|
||||
```
|
||||
card_score(_-as, 11).
|
||||
card_score(_-3, 10).
|
||||
card_score(_-rey, 4).
|
||||
card_score(_-caballo, 3).
|
||||
card_score(_-sota, 2).
|
||||
card_score(_-X, 0) :- member(X, [7, 6, 5, 4, 2]).
|
||||
```
|
||||
|
||||
Now, if we want to know how many points gives an specific card, we can ask:
|
||||
|
||||
```
|
||||
?- card_score(oros-rey, X).
|
||||
X = 4
|
||||
; false.
|
||||
```
|
||||
|
||||
(Remember, to load a file named brisca.pl in Scryer Prolog, we can run `scryer-prolog brisca.pl` in your favourite shell.)
|
||||
|
||||
But we can also ask what cards can give you that value:
|
||||
|
||||
```
|
||||
?- card(X), card_score(X, 11).
|
||||
X = oros-as
|
||||
; X = espadas-as
|
||||
; X = bastos-as
|
||||
; X = copas-as.
|
||||
```
|
||||
|
||||
You have probably noticed that I included the card predicate in the toplevel (which kind of acts like a type restriction), not in the `card_score/2`. However, does it make sense for card_score to work on something that is not a also a card? Both approaches are valid if applied correctly. Some people prefer to set domain restrictions like this externally, as it allows you to write more concise code and it usually performs better. However, having a card predicate in `card_score/2` is more correct since it's not possible to use that predicate in something that is not a card. We can choose either option thanks to Prolog being a dynamic language.
|
||||
|
||||
In my case, my final version is going to be a predicate which includes the card restriction and uses a wrapper predicate to have the short and concise code:
|
||||
|
||||
```
|
||||
card_score(X, N) :-
|
||||
card(X),
|
||||
card_score_(X, N).
|
||||
card_score_(_-as, 11).
|
||||
card_score_(_-3, 10).
|
||||
card_score_(_-rey, 4).
|
||||
card_score_(_-caballo, 3).
|
||||
card_score_(_-sota, 2).
|
||||
card_score_(_-X, 0) :- member(X, [7, 6, 5, 4, 2]).
|
||||
```
|
||||
|
||||
Now we just need to work with a list of cards instead of a single card. This is a place where we can introduce DCGs. DCGs are the short name for [Definite Clause Grammars](https://www.metalevel.at/prolog/dcg), which is a shorthand notation used to describe sequences. We can use them to parse, generate, complete and check sequences in the form of lists.
|
||||
|
||||
We can define a base case. If there are no more elements in the sequence, the score should be zero. If there's a Card, we can calculate its score and sum it together with the rest:
|
||||
|
||||
```
|
||||
cards_score_(0) --> [].
|
||||
cards_score_(X) -->
|
||||
[Card],
|
||||
{ card_score(Card, X0), #X #= #X0 + #X1 },
|
||||
cards_score_(X1).
|
||||
```
|
||||
|
||||
In DCGs, to match an item of the sequence, we use brackets. We use braces to introduce normal Prolog code. Calling other DCGs (in this case, the same, as it's a recursive one), it's just calling it again. Notice in this code that we are doing the addition of X0 and X1 when we still don't know the value of X1. This would be an error in the traditional arithmethic system of Prolog, but it's valid with clpz. clpz allows us to have a more declarative arithmethic, at least with integers.
|
||||
|
||||
Now we can try this code using `phrase/2` which is needed to jump to a DCG.
|
||||
|
||||
```
|
||||
?- phrase(cards_score_(X), [oros-as, oros-rey, oros-7]).
|
||||
X = 15
|
||||
; false.
|
||||
```
|
||||
|
||||
It seems to work. However there's a small problem. if we try to do the reverse, generating a sequence of cards that give you a specific amount of points, we'll find trouble. The program won't end. This is basically because be default we are not fair enumerating. This means that it will go deep into the recursion, trying to create an infinite length sequence. We need a way so that it does iterative deepening (starts with sequences of length 1, then length 2, ...). Luckily, if we pass our list first through the `length/2` predicate, it will do exactly that. This is usually done at the outside.
|
||||
|
||||
```
|
||||
cards_score(Cards, Score) :-
|
||||
phrase(cards_score_(Score), Cards).
|
||||
|
||||
cards_score_(0) --> [].
|
||||
cards_score_(X) -->
|
||||
[Card],
|
||||
{ card_score(Card, X0), #X #= #X0 + #X1 },
|
||||
cards_score_(X1).
|
||||
```
|
||||
|
||||
So now:
|
||||
|
||||
```
|
||||
?- length(X, _), cards_score(X, 15).
|
||||
X = [oros-rey,oros-as]
|
||||
; X = [oros-rey,espadas-as]
|
||||
; X = [oros-rey,bastos-as]
|
||||
; X = [oros-rey,copas-as]
|
||||
; X = [oros-as,oros-rey]
|
||||
; X = [oros-as,espadas-rey]
|
||||
; X = [oros-as,bastos-rey]
|
||||
; ...
|
||||
```
|
||||
|
||||
```
|
||||
?- cards_score(X, Y).
|
||||
X = [], Y = 0
|
||||
; X = [oros-rey], Y = 4
|
||||
; X = [oros-caballo], Y = 3
|
||||
; X = [oros-sota], Y = 2
|
||||
; X = [oros-7], Y = 0
|
||||
; X = [oros-6], Y = 0
|
||||
; X = [oros-5], Y = 0
|
||||
; X = [oros-4], Y = 0
|
||||
; X = [oros-3], Y = 10
|
||||
; X = [oros-2], Y = 0
|
||||
; ...
|
||||
```
|
||||
|
||||
## Winner of a round
|
||||
|
||||
Now, let's try to model the winner of a round in the game. First, we can model the pseudo-numerical order of the cards in a suite. We can start with a simple idea, if we have a list with the right order, we can take an element and see if the other card is in the higher section of the rest of the list.
|
||||
|
||||
```
|
||||
card_higher_n(N0, N1) :-
|
||||
Order = [as, 3, rey, caballo, sota, 7, 6, 5, 4, 2],
|
||||
append(Highers, [N0|_], Order),
|
||||
member(N1, Highers).
|
||||
```
|
||||
|
||||
This works well, and it can show all of the 'Y is higher than X' relationships:
|
||||
|
||||
```
|
||||
?- card_higher_n(X, Y).
|
||||
X = 3, Y = as
|
||||
; X = rey, Y = as
|
||||
; X = rey, Y = 3
|
||||
; X = caballo, Y = as
|
||||
; X = caballo, Y = 3
|
||||
; ...
|
||||
```
|
||||
|
||||
For some people this might be enough. However, since we're in Scryer Prolog, we can also use reificated predicates. Using this technique, we can show not just the 'Y is higher than X' but also, the complete set of solutions of 'Y is NOT higher than X'. In order to do that we need to add a third argument to our predicate, which will be true or false (the predicate is true for the given X and Y or it's false). And we need to change `member/2` to a reified version of it: `memberd_t/3`.
|
||||
|
||||
```
|
||||
card_higher_n(N0, N1, T) :-
|
||||
Order = [as, 3, rey, caballo, sota, 7, 6, 5, 4, 2],
|
||||
append(Highers, [N0|_], Order),
|
||||
memberd_t(N1, Highers, T).
|
||||
```
|
||||
|
||||
Now, we get for every combination a T truth value and also, in the case of false queries, we get the constraints that need to be held to get that result:
|
||||
|
||||
```
|
||||
?- card_higher_n(X, Y, T).
|
||||
X = as, T = false
|
||||
; X = 3, Y = as, T = true
|
||||
; X = 3, T = false, dif:dif(as,Y)
|
||||
; X = rey, Y = as, T = true
|
||||
; X = rey, Y = 3, T = true
|
||||
; X = rey, T = false, dif:dif(3,Y), dif:dif(as,Y)
|
||||
; X = caballo, Y = as, T = true
|
||||
; X = caballo, Y = 3, T = true
|
||||
; X = caballo, Y = rey, T = true
|
||||
; X = caballo, T = false, dif:dif(3,Y), dif:dif(as,Y), dif:dif(rey,Y)
|
||||
```
|
||||
|
||||
For example, if X = 3 and Y = as, then Y is higher than X. But if Y is different from as, then it's false.
|
||||
|
||||
Using reified predicates can be wise if we want to raise ourselves to a more declarative way of working with Prolog, but it's newer and not applicable form some problems. In this case, it's fine, so we'll use them.
|
||||
|
||||
Now, to model the relatioships of whole cards, we need to take into account the suites too. We're going to compare the suites, if they're the same, we just apply the pseudonumerical order. If they're different we check if the suite of the supposedly higher card is the trump suite. We can continue to use reified predicates from `library(reif)` like `if_/3`, which takes a reified predicate as a condition and `=/3` which does reified unification. We also expose our T truth argument in this predicate:
|
||||
|
||||
```
|
||||
card_higher(Trump, Card, Higher, T) :-
|
||||
card(Card),
|
||||
card(Higher),
|
||||
Card = S0-N0,
|
||||
Higher = S1-N1,
|
||||
if_(S0 = S1,
|
||||
card_higher_n(N0, N1, T),
|
||||
=(S1, Trump, T)
|
||||
).
|
||||
```
|
||||
|
||||
The code allows us to generate every card combination with every trump value and their truth value:
|
||||
|
||||
```
|
||||
?- card_higher(Trump, X, Y, T).
|
||||
X = oros-rey, Y = oros-rey, T = false
|
||||
; X = oros-rey, Y = oros-caballo, T = false
|
||||
; X = oros-rey, Y = oros-sota, T = false
|
||||
; X = oros-rey, Y = oros-7, T = false
|
||||
; X = oros-rey, Y = oros-6, T = false
|
||||
; X = oros-rey, Y = oros-5, T = false
|
||||
; X = oros-rey, Y = oros-4, T = false
|
||||
; X = oros-rey, Y = oros-3, T = true
|
||||
; X = oros-rey, Y = oros-2, T = false
|
||||
; X = oros-rey, Y = oros-as, T = true
|
||||
; Trump = espadas, X = oros-rey, Y = espadas-rey, T = true
|
||||
; X = oros-rey, Y = espadas-rey, T = false, dif:dif(espadas,Trump)
|
||||
; Trump = espadas, X = oros-rey, Y = espadas-caballo, T = true
|
||||
; X = oros-rey, Y = espadas-caballo, T = false, dif:dif(espadas,Trump)
|
||||
; Trump = espadas, X = oros-rey, Y = espadas-sota, T = true
|
||||
; X = oros-rey, Y = espadas-sota, T = false, dif:dif(espadas,Trump)
|
||||
```
|
||||
|
||||
For example a espadas-caballo is higher than oros-rey if Trump = espadas, otherwise, it isn't.
|
||||
|
||||
Now, let's check for the winner. For every card we need to check if the current one is higher value than the current highest. It's very easy, we just need to to do a fold! Just like in functional programming, we have a fold _predicate_. Actually, it's a fold-left, but for this case we need the right order, so we will need to reverse the list first. The, for our foldl, we're going to use a lambda predicate from `library(lambda)`.
|
||||
|
||||
This library, allows us to write unnamed predicates that are very useful in metapredicates like `foldl/4`. The syntax might be a bit confusing if you're coming from other languages as it's a bit unique. You can find the complete description of the syntax in the [documentation page](https://www.scryer.pl/lambda.html).
|
||||
|
||||
The code looks like this:
|
||||
|
||||
```
|
||||
round_winner(Cards, Trump, WinnerCard) :-
|
||||
reverse(Cards, [FirstCard|RestCards]),
|
||||
foldl(Trump+\X^Y^Z^if_(card_higher(Trump, X, Y), Y = Z, X = Z), RestCards, FirstCard, WinnerCard).
|
||||
```
|
||||
|
||||
```
|
||||
?- length(Cards, 4), round_winner(Cards, Trump, Winner).
|
||||
Cards = [oros-rey,oros-rey,oros-rey,oros-rey], Winner = oros-rey
|
||||
; Cards = [oros-caballo,oros-rey,oros-rey,oros-rey], Winner = oros-rey
|
||||
; Cards = [oros-sota,oros-rey,oros-rey,oros-rey], Winner = oros-rey
|
||||
; Cards = [oros-7,oros-rey,oros-rey,oros-rey], Winner = oros-rey
|
||||
; Cards = [oros-6,oros-rey,oros-rey,oros-rey], Winner = oros-rey
|
||||
; Cards = [oros-5,oros-rey,oros-rey,oros-rey], Winner = oros-rey
|
||||
; Cards = [oros-4,oros-rey,oros-rey,oros-rey], Winner = oros-rey
|
||||
; Cards = [oros-3,oros-rey,oros-rey,oros-rey], Winner = oros-3
|
||||
; Cards = [oros-2,oros-rey,oros-rey,oros-rey], Winner = oros-rey
|
||||
; Cards = [oros-as,oros-rey,oros-rey,oros-rey], Winner = oros-as
|
||||
; Cards = [espadas-rey,oros-rey,oros-rey,oros-rey], Trump = oros, Winner = oros-rey
|
||||
; Cards = [espadas-rey,oros-rey,oros-rey,oros-rey], Winner = espadas-rey, dif:dif(oros,Trump)
|
||||
```
|
||||
|
||||
# A complete game
|
||||
|
||||
Now we have the basic pieces of the game. But we don't have a complete game. A game starts with someone dealing the first three cards to each player, a trump suite is selected, then we start the rounds: players put one card each, we choose a winner, the winner takes the cards and players get a new card from the stock. This all seems very procedural, but we're using Prolog. What can we do about it?
|
||||
|
||||
Let's ask ourselves what is a procedure. It's a sequence. And we have already seen a way to describe sequences in Prolog! The DCGs.
|
||||
|
||||
The basic idea however is having _explicit_ states. The predicates that we're going to write will take an state (a view of the world at a certain point) and will give us the next state.
|
||||
|
||||
Let's define the state first. In a game of Brisca we have players. Each player has the three cards he can choose to put (less if we're running out of cards) and the cards he has got from winning rounds. Aditionally we have a stock, a trump suite and the order to play, which is usually from the player who won the last round and going to the right. We could store the data in a list, with different compound terms:
|
||||
|
||||
```
|
||||
[players([player(Name, PlayableCards, WonCards), player(Name, PlayableCards, WonCards), ...]), stock(Cards), trump(Trump)]
|
||||
```
|
||||
|
||||
In order to express states using DCGs, we can use [semicontext notation](https://www.metalevel.at/prolog/dcg#semicontext):
|
||||
|
||||
```
|
||||
state(S), [S] --> [S].
|
||||
state(S0, S), [S] --> [S0].
|
||||
```
|
||||
|
||||
So, for example our predicate to reset the game, and leave all the different cards in the stock would like this:
|
||||
|
||||
```
|
||||
reset -->
|
||||
state(_, [stock(Cards)]),
|
||||
{
|
||||
setof(Card, card(Card), Cards)
|
||||
}.
|
||||
```
|
||||
|
||||
To shuffle them:
|
||||
|
||||
```
|
||||
shuffle_cards -->
|
||||
state(S0, S),
|
||||
{
|
||||
select(stock(Cards), S0, S1),
|
||||
shuffle(Cards, ShuffledCards),
|
||||
S = [stock(ShuffledCards)|S1]
|
||||
}.
|
||||
```
|
||||
|
||||
Where `shuffle/2` is a predicate that implements the Fisher-Yates algorithm in Prolog:
|
||||
|
||||
```
|
||||
shuffle([], []).
|
||||
shuffle(Xs0, [Y|Ys]) :-
|
||||
length(Xs0, N),
|
||||
random_integer(0, N, R),
|
||||
nth0(R, Xs0, Y, Xs),
|
||||
shuffle(Xs, Ys).
|
||||
```
|
||||
|
||||
We can also code more specific state accessors, which could be useful to know which part of the state each predicate can modify:
|
||||
|
||||
```
|
||||
players(P0, P), [S] -->
|
||||
[S0],
|
||||
{ select(players(P0), S0, S1), S = [players(P)|S1] }.
|
||||
```
|
||||
|
||||
The whole state management topic in DCGs could be the whole topic for another article.
|
||||
|
||||
The final DCG code for an interactive version of Brisca could be as follows:
|
||||
|
||||
```
|
||||
brisca :-
|
||||
phrase(brisca, [_], [_]).
|
||||
|
||||
brisca -->
|
||||
reset,
|
||||
shuffle_cards,
|
||||
set_trump,
|
||||
create_players([aarroyoc, xijinping, donalddtrump, vonderleyen]),
|
||||
play_rounds,
|
||||
show_scores.
|
||||
|
||||
reset -->
|
||||
state(_, [stock(Cards)]),
|
||||
{
|
||||
setof(Card, card(Card), Cards)
|
||||
}.
|
||||
shuffle_cards -->
|
||||
state(S0, S),
|
||||
{
|
||||
select(stock(Cards), S0, S1),
|
||||
shuffle(Cards, ShuffledCards),
|
||||
S = [stock(ShuffledCards)|S1]
|
||||
}.
|
||||
set_trump -->
|
||||
state(S0, S),
|
||||
{
|
||||
member(stock(Cards), S0),
|
||||
length(Cards, N),
|
||||
nth1(N, Cards, LastCard),
|
||||
LastCard = Trump-_,
|
||||
S = [trump(Trump)|S0]
|
||||
}.
|
||||
|
||||
create_players(Names) -->
|
||||
state(S0, S),
|
||||
{
|
||||
same_length(Players, Names),
|
||||
maplist(\N^X^(X=player(N, [], [])), Names, Players),
|
||||
S = [players(Players)|S0]
|
||||
},
|
||||
deal_one_card_per_player,
|
||||
deal_one_card_per_player,
|
||||
deal_one_card_per_player.
|
||||
|
||||
deal_one_card_per_player -->
|
||||
state(S0, S),
|
||||
{
|
||||
select(players(Players), S0, S1),
|
||||
select(stock(Cards), S1, S2),
|
||||
deal_one_card_per_player(Players, Players1, Cards, Cards1),
|
||||
S = [players(Players1), stock(Cards1)|S2]
|
||||
}.
|
||||
|
||||
deal_one_card_per_player([], [], Cs, Cs).
|
||||
deal_one_card_per_player(Ps, Ps, [], []).
|
||||
deal_one_card_per_player([P|Ps], [P1|Ps1], [C|Cs], Cs1) :-
|
||||
P = player(N, A0, B0),
|
||||
P1 = player(N, [C|A0], B0),
|
||||
deal_one_card_per_player(Ps, Ps1, Cs, Cs1).
|
||||
|
||||
play_rounds -->
|
||||
players(P, P),
|
||||
{ P = [player(_, X, _)|_], length(X, 0) }.
|
||||
play_rounds -->
|
||||
players(P, P),
|
||||
{ P = [player(_, X, _)|_], length(X, N), N > 0 },
|
||||
play_round,
|
||||
play_rounds.
|
||||
|
||||
play_round -->
|
||||
state(S),
|
||||
players(P0, P2),
|
||||
{
|
||||
member(trump(Trump), S),
|
||||
format("Brisca round~nTrump is: ~a~n~n", [Trump])
|
||||
},
|
||||
play_players(P0, Cards),
|
||||
{
|
||||
round_winner(Cards, Trump, WinnerCard),
|
||||
maplist(remove_card, P0, Cards, P1),
|
||||
nth0(N, Cards, WinnerCard),
|
||||
nth0(N, P1, WinnerPlayer0),
|
||||
append(PBefore, [WinnerPlayer0|PAfter], P1),
|
||||
WinnerPlayer0 = player(WinnerName, C, W0),
|
||||
format("Winner card is ~w from ~w~n", [WinnerCard, WinnerName]),
|
||||
append(W0, Cards, W1),
|
||||
append([player(WinnerName, C, W1)|PAfter], PBefore, P2)
|
||||
},
|
||||
deal_one_card_per_player.
|
||||
|
||||
remove_card(P0, C, P) :-
|
||||
P0 = player(N, C0, W),
|
||||
select(C, C0, C1),
|
||||
P = player(N, C1, W).
|
||||
|
||||
play_players([], []) --> [].
|
||||
play_players([P|Ps], [C|Cs]) -->
|
||||
{
|
||||
P = player(Name, SelectableCards, _),
|
||||
format("It's ~a's turn!~n", [Name]),
|
||||
format("Selectable cards: ~w~n", [SelectableCards]),
|
||||
read(C),
|
||||
member(C, SelectableCards)
|
||||
},
|
||||
play_players(Ps, Cs).
|
||||
play_players(Ps, Cs) --> play_players(Ps, Cs).
|
||||
|
||||
show_scores -->
|
||||
players(P, P),
|
||||
{
|
||||
maplist(\X^(
|
||||
X=player(N,_,Z),
|
||||
cards_score(Z, Y),
|
||||
format("Score of ~w is ~d~n", [N, Y])), P)
|
||||
}.
|
||||
```
|
||||
|
||||
Now, we can play! Of course, if you substitute interactive choosing (via `read/1`) with another method, you could simulate games, try AI strategies, etc
|
||||
|
||||

|
||||
3
learn/test-page.dj
Normal file
3
learn/test-page.dj
Normal file
@@ -0,0 +1,3 @@
|
||||
# Test page
|
||||
|
||||
This is a page about Scryer Prolog
|
||||
@@ -14,7 +14,7 @@ pub(crate) trait Allocator {
|
||||
lvl: Level,
|
||||
context: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
);
|
||||
) -> RegType;
|
||||
|
||||
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
|
||||
919
src/arena.rs
919
src/arena.rs
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::allocator::*;
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
@@ -5,6 +7,7 @@ use crate::debray_allocator::*;
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::iterators::*;
|
||||
use crate::offset_table::*;
|
||||
use crate::targets::QueryInstruction;
|
||||
use crate::types::*;
|
||||
|
||||
@@ -16,7 +19,7 @@ use crate::machine::machine_errors::*;
|
||||
use dashu::base::Abs;
|
||||
use dashu::base::BitTest;
|
||||
use num_order::NumOrd;
|
||||
use ordered_float::*;
|
||||
use ordered_float::{Float, OrderedFloat};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::{max, min, Ordering};
|
||||
@@ -86,7 +89,7 @@ impl<'a> ArithInstructionIterator<'a> {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ArithTermRef<'a> {
|
||||
Literal(&'a Literal),
|
||||
Literal(Literal),
|
||||
Op(Atom, usize), // name, arity.
|
||||
Var(Level, &'a Cell<VarReg>, VarPtr),
|
||||
}
|
||||
@@ -115,7 +118,7 @@ impl<'a> Iterator for ArithInstructionIterator<'a> {
|
||||
self.push_subterm(lvl.child_level(), &subterms[child_num]);
|
||||
}
|
||||
}
|
||||
TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))),
|
||||
TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(*c))),
|
||||
TermIterState::Var(lvl, cell, var_ptr) => {
|
||||
return Some(Ok(ArithTermRef::Var(lvl, cell, var_ptr)));
|
||||
}
|
||||
@@ -157,7 +160,9 @@ fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), Ari
|
||||
match c {
|
||||
Literal::Fixnum(n) => interm.push(ArithmeticTerm::Number(Number::Fixnum(*n))),
|
||||
Literal::Integer(n) => interm.push(ArithmeticTerm::Number(Number::Integer(*n))),
|
||||
Literal::Float(n) => interm.push(ArithmeticTerm::Number(Number::Float(*n.as_ptr()))),
|
||||
&Literal::F64(_offset, n) => {
|
||||
interm.push(ArithmeticTerm::Number(Number::Float(n)));
|
||||
}
|
||||
Literal::Rational(n) => interm.push(ArithmeticTerm::Number(Number::Rational(*n))),
|
||||
Literal::Atom(name) if name == &atom!("e") => interm.push(ArithmeticTerm::Number(
|
||||
Number::Float(OrderedFloat(std::f64::consts::E)),
|
||||
@@ -166,7 +171,7 @@ fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), Ari
|
||||
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(f64::EPSILON)),
|
||||
)),
|
||||
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
|
||||
}
|
||||
@@ -315,7 +320,7 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
|
||||
for term_ref in src.iter()? {
|
||||
match term_ref? {
|
||||
ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?,
|
||||
ArithTermRef::Literal(c) => push_literal(&mut self.interm, &c)?,
|
||||
ArithTermRef::Var(lvl, cell, name) => {
|
||||
let var_num = name.to_var_num().unwrap();
|
||||
|
||||
@@ -352,36 +357,43 @@ impl<'a> ArithmeticEvaluator<'a> {
|
||||
}
|
||||
|
||||
// integer division rounding function -- 9.1.3.1.
|
||||
pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Number {
|
||||
pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result<Number, EvalError> {
|
||||
match n {
|
||||
&Number::Integer(i) => {
|
||||
let result = (&*i).try_into();
|
||||
if let Ok(value) = result {
|
||||
fixnum!(Number, value, arena)
|
||||
if let Ok(value) = Fixnum::build_with_checked(&*i) {
|
||||
Ok(Number::Fixnum(value))
|
||||
} else {
|
||||
*n
|
||||
Ok(*n)
|
||||
}
|
||||
}
|
||||
Number::Fixnum(_) => *n,
|
||||
Number::Fixnum(_) => Ok(*n),
|
||||
&Number::Float(f) => {
|
||||
let f = f.floor();
|
||||
|
||||
const I64_MIN_TO_F: OrderedFloat<f64> = OrderedFloat(i64::MIN as f64);
|
||||
const I64_MAX_TO_F: OrderedFloat<f64> = OrderedFloat(i64::MAX as f64);
|
||||
const FIXNUM_MIN_TO_F: OrderedFloat<f64> = OrderedFloat(Fixnum::MIN as f64);
|
||||
const FIXNUM_MAX_TO_F: OrderedFloat<f64> = OrderedFloat(Fixnum::MAX as f64);
|
||||
|
||||
if I64_MIN_TO_F <= f && f <= I64_MAX_TO_F {
|
||||
fixnum!(Number, f.into_inner() as i64, arena)
|
||||
if (FIXNUM_MIN_TO_F..=FIXNUM_MAX_TO_F).contains(&f) {
|
||||
Ok(Number::Fixnum(
|
||||
// Safety: We checked that the value is in range
|
||||
unsafe { Fixnum::build_with_unchecked(f.into_inner() as i64) },
|
||||
))
|
||||
} else {
|
||||
Number::Integer(arena_alloc!(Integer::from(f.0 as i64), arena))
|
||||
Ok(Number::Integer(arena_alloc!(
|
||||
Integer::try_from(classify_float(f.0)?).unwrap_or_else(|_| {
|
||||
unreachable!();
|
||||
}),
|
||||
arena
|
||||
)))
|
||||
}
|
||||
}
|
||||
Number::Rational(ref r) => {
|
||||
let (_, floor) = (r.fract(), r.floor());
|
||||
let floor = r.floor();
|
||||
|
||||
if let Ok(value) = (&floor).try_into() {
|
||||
fixnum!(Number, value, arena)
|
||||
if let Ok(value) = Fixnum::build_with_checked(&floor) {
|
||||
Ok(Number::Fixnum(value))
|
||||
} else {
|
||||
Number::Integer(arena_alloc!(floor, arena))
|
||||
Ok(Number::Integer(arena_alloc!(floor, arena)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,26 +557,8 @@ impl PartialEq for Number {
|
||||
(&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.num_eq(&**n2)
|
||||
}
|
||||
}
|
||||
(Number::Rational(ref n1), Number::Integer(ref n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
n1 == &Rational::from(&**n2)
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
n1.num_eq(&**n2)
|
||||
}
|
||||
}
|
||||
(Number::Integer(ref n1), Number::Rational(ref n2)) => n1.num_eq(&**n2),
|
||||
(Number::Rational(ref n1), Number::Integer(ref n2)) => n1.num_eq(&**n2),
|
||||
(Number::Rational(ref n1), &Number::Float(n2)) => {
|
||||
OrderedFloat(n1.to_f64().value()).eq(&n2)
|
||||
}
|
||||
@@ -643,24 +637,10 @@ impl Ord for Number {
|
||||
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Integer(n1), &Number::Rational(n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
Rational::from(&**n1).cmp(n2)
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
(&Number::Rational(n1), &Number::Integer(n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
(&**n1).cmp(&Rational::from(&**n2))
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(*n1).num_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)
|
||||
@@ -674,11 +654,11 @@ impl Ord for Number {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<HeapCellValue> for Number {
|
||||
impl TryFrom<(HeapCellValue, &'_ F64Table)> for Number {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: HeapCellValue) -> Result<Number, Self::Error> {
|
||||
fn try_from((value, f64_tbl): (HeapCellValue, &F64Table)) -> Result<Number, Self::Error> {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Cons, c) => {
|
||||
match_untyped_arena_ptr!(c,
|
||||
@@ -693,8 +673,9 @@ impl TryFrom<HeapCellValue> for Number {
|
||||
}
|
||||
)
|
||||
}
|
||||
(HeapCellValueTag::F64, n) => {
|
||||
Ok(Number::Float(*n))
|
||||
(HeapCellValueTag::F64Offset, offset) => {
|
||||
let n = f64_tbl.get_entry(offset);
|
||||
Ok(Number::Float(n))
|
||||
}
|
||||
(HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => {
|
||||
Ok(Number::Fixnum(n))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::parser::ast::MAX_ARITY;
|
||||
use crate::raw_block::*;
|
||||
use crate::rcu::{Rcu, RcuRef};
|
||||
use crate::types::*;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
@@ -8,26 +9,138 @@ 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 arcu::atomic::Arcu;
|
||||
use arcu::epoch_counters::GlobalEpochCounterPool;
|
||||
use arcu::rcu_ref::RcuRef;
|
||||
use arcu::Rcu;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct AtomCell {
|
||||
name: B48,
|
||||
arity: B8,
|
||||
#[allow(unused)]
|
||||
f: bool,
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
#[allow(unused)]
|
||||
is_inlined: bool,
|
||||
#[allow(unused)]
|
||||
tag: B5,
|
||||
}
|
||||
|
||||
const INLINED_ATOM_MAX_LEN: usize = 6;
|
||||
|
||||
const_assert!(INLINED_ATOM_MAX_LEN < mem::size_of::<AtomCell>());
|
||||
const_assert!(mem::size_of::<AtomCell>() == 8);
|
||||
|
||||
const_assert!(INLINED_ATOM_MAX_LEN < mem::size_of::<Atom>());
|
||||
const_assert!(mem::size_of::<Atom>() == 8);
|
||||
|
||||
impl AtomCell {
|
||||
#[inline]
|
||||
fn new_static(index: u64) -> Self {
|
||||
// upper 23 bits of index must be 0
|
||||
debug_assert_eq!(index & !((1 << 49) - 1), 0);
|
||||
AtomCell::new()
|
||||
.with_name(index)
|
||||
.with_arity(0u8)
|
||||
.with_m(false)
|
||||
.with_f(false)
|
||||
.with_is_inlined(false)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_inlined(string: &str, arity: u8) -> Self {
|
||||
debug_assert!(string.len() <= INLINED_ATOM_MAX_LEN);
|
||||
|
||||
let mut string_buf: [u8; 8] = [0u8; 8];
|
||||
string_buf[..string.len()].copy_from_slice(string.as_bytes());
|
||||
let encoding = u64::from_le_bytes(string_buf);
|
||||
|
||||
AtomCell::new()
|
||||
.with_name(encoding)
|
||||
.with_arity(arity)
|
||||
.with_m(false)
|
||||
.with_f(false)
|
||||
.with_is_inlined(true)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_char_inlined(c: char) -> Self {
|
||||
if c == '\u{0}' {
|
||||
return Self::new_static(NULL_ATOM.flat_index());
|
||||
}
|
||||
|
||||
let mut char_buf = [0u8; 8];
|
||||
c.encode_utf8(&mut char_buf);
|
||||
|
||||
let encoding = u64::from_le_bytes(char_buf);
|
||||
|
||||
AtomCell::new()
|
||||
.with_name(encoding)
|
||||
.with_arity(0u8)
|
||||
.with_m(false)
|
||||
.with_f(false)
|
||||
.with_is_inlined(true)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build_with(atom_index: u64, arity: u8) -> Self {
|
||||
debug_assert!((arity as usize) <= MAX_ARITY);
|
||||
|
||||
AtomCell::new()
|
||||
.with_name(atom_index >> 1)
|
||||
.with_arity(arity)
|
||||
.with_f(false)
|
||||
.with_m(false)
|
||||
.with_is_inlined(atom_index & 1 == 1)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name(self) -> Atom {
|
||||
Atom {
|
||||
index: (self.name() << 1) | self.is_inlined() as u64,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_arity(self) -> usize {
|
||||
self.arity() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name_and_arity(self) -> (Atom, usize) {
|
||||
(self.get_name(), self.get_arity())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Atom {
|
||||
pub index: u64,
|
||||
}
|
||||
|
||||
const_assert!(mem::size_of::<Atom>() == 8);
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/static_atoms.rs"));
|
||||
|
||||
// populate these in STRINGS so they can be used from build_functor
|
||||
const _: Atom = atom!(".");
|
||||
const _: Atom = atom!("[]");
|
||||
const NULL_ATOM: Atom = atom!("\0");
|
||||
|
||||
impl<'a> From<&'a Atom> for Atom {
|
||||
#[inline]
|
||||
fn from(atom: &'a Atom) -> Self {
|
||||
@@ -35,41 +148,31 @@ 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl indexmap::Equivalent<Atom> for str {
|
||||
fn equivalent(&self, key: &Atom) -> bool {
|
||||
&*key.as_str() == self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Atom {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str().deref() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for Atom {
|
||||
fn eq(&self, &other: &&str) -> bool {
|
||||
self.as_str().deref() == other
|
||||
}
|
||||
}
|
||||
|
||||
const ATOM_TABLE_INIT_SIZE: usize = 1 << 16;
|
||||
const ATOM_TABLE_ALIGN: usize = 8;
|
||||
|
||||
#[inline(always)]
|
||||
fn global_atom_table() -> &'static RwLock<Weak<AtomTable>> {
|
||||
#[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<AtomTable>> = 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<AtomTable>>> = OnceLock::new();
|
||||
GLOBAL_ATOM_TABLE.get_or_init(|| RwLock::new(Weak::new()))
|
||||
}
|
||||
static GLOBAL_ATOM_TABLE: RwLock<Weak<AtomTable>> = RwLock::new(Weak::new());
|
||||
&GLOBAL_ATOM_TABLE
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -99,6 +202,12 @@ struct AtomHeader {
|
||||
padding: B13,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct AtomData {
|
||||
header: AtomHeader,
|
||||
data: str,
|
||||
}
|
||||
|
||||
impl AtomHeader {
|
||||
fn build_with(len: u64) -> Self {
|
||||
AtomHeader::new().with_len(len).with_m(false)
|
||||
@@ -109,32 +218,23 @@ impl Hash for Atom {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.as_str().hash(hasher)
|
||||
// hasher.write_usize(self.index)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_char {
|
||||
($s:expr) => {
|
||||
!$s.is_empty() && $s.chars().nth(1).is_none()
|
||||
};
|
||||
}
|
||||
|
||||
pub enum AtomString<'a> {
|
||||
Static(&'a str),
|
||||
Inlined([u8; 8]),
|
||||
Dynamic(AtomTableRef<str>),
|
||||
}
|
||||
|
||||
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)),
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn inlined_to_str(bytes: &[u8; 8]) -> &str {
|
||||
let slice_len = bytes
|
||||
.iter()
|
||||
.position(|&b| b == 0u8)
|
||||
.unwrap_or(INLINED_ATOM_MAX_LEN);
|
||||
|
||||
unsafe { str::from_utf8_unchecked(&bytes[..slice_len]) }
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AtomString<'_> {
|
||||
@@ -154,6 +254,7 @@ impl std::ops::Deref for AtomString<'_> {
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Static(reference) => reference,
|
||||
Self::Inlined(inlined) => inlined_to_str(inlined),
|
||||
Self::Dynamic(guard) => guard.deref(),
|
||||
}
|
||||
}
|
||||
@@ -171,25 +272,48 @@ impl rustyline::completion::Candidate for AtomString<'_> {
|
||||
}
|
||||
|
||||
impl Atom {
|
||||
#[inline(always)]
|
||||
pub fn is_static(self) -> bool {
|
||||
(self.index as usize) < STRINGS.len() << 3
|
||||
#[inline]
|
||||
fn new_inlined(string: &str) -> Self {
|
||||
AtomCell::new_inlined(string, 0).get_name()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(self) -> Option<AtomTableRef<u8>> {
|
||||
fn is_static(self) -> bool {
|
||||
if self.is_inlined() {
|
||||
true
|
||||
} else {
|
||||
(self.flat_index() as usize) < STRINGS.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn flat_index(self) -> u64 {
|
||||
self.index >> 1
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_inlined(self) -> bool {
|
||||
self.index & 1 == 1
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_ptr(self) -> Option<AtomTableRef<AtomData>> {
|
||||
if self.is_static() {
|
||||
None
|
||||
} else {
|
||||
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()
|
||||
})
|
||||
}
|
||||
|
||||
AtomTableRef::try_map(atom_table.inner.read(), |buf| unsafe {
|
||||
let ptr = buf
|
||||
.block
|
||||
.base
|
||||
.add(self.flat_index() as usize - STRINGS.len());
|
||||
// TODO use std::ptr::from_raw_parts instead when feature ptr_metadata is stable rust-lang/rust#81513
|
||||
let atom_data = &*(std::ptr::slice_from_raw_parts(ptr, 0) as *const AtomData);
|
||||
let len = atom_data.header.len();
|
||||
Some(&*(std::ptr::slice_from_raw_parts(ptr, len as usize) as *const AtomData))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,12 +324,14 @@ impl Atom {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(self) -> usize {
|
||||
if self.is_static() {
|
||||
STRINGS[(self.index >> 3) as usize].len()
|
||||
if let Some(s) = self.inlined_str() {
|
||||
s.len()
|
||||
} else if self.is_static() {
|
||||
let index = self.flat_index();
|
||||
STRINGS[index as usize].len()
|
||||
} else {
|
||||
let ptr = self.as_ptr().unwrap();
|
||||
let ptr = ptr.deref() as *const u8 as *const AtomHeader;
|
||||
unsafe { ptr::read(ptr) }.len() as _
|
||||
let len: u64 = self.as_ptr().unwrap().header.len();
|
||||
len as usize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,11 +339,6 @@ impl Atom {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn flat_index(self) -> u64 {
|
||||
self.index >> 3
|
||||
}
|
||||
|
||||
pub fn as_char(self) -> Option<char> {
|
||||
let s = self.as_str();
|
||||
let mut it = s.chars();
|
||||
@@ -233,21 +354,25 @@ impl Atom {
|
||||
}
|
||||
|
||||
#[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)) }
|
||||
}))
|
||||
fn inlined_str<'a>(&self) -> Option<AtomString<'a>> {
|
||||
if self.is_inlined() {
|
||||
Some(AtomString::Inlined(self.flat_index().to_le_bytes()))
|
||||
} else {
|
||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> AtomString<'static> {
|
||||
if let Some(s) = self.inlined_str() {
|
||||
s
|
||||
} else if self.is_static() {
|
||||
let index = self.flat_index() as usize;
|
||||
AtomString::Static(STRINGS[index])
|
||||
} else if let Some(ptr) = self.as_ptr() {
|
||||
AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| &ptr.data))
|
||||
} else {
|
||||
AtomString::Static(STRINGS[(self.index >> 1) as usize])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,17 +412,17 @@ impl Ord for Atom {
|
||||
#[derive(Debug)]
|
||||
pub struct InnerAtomTable {
|
||||
block: RawBlock<AtomTable>,
|
||||
pub table: Rcu<IndexSet<Atom>>,
|
||||
pub table: Arcu<IndexSet<Atom>, GlobalEpochCounterPool>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AtomTable {
|
||||
inner: Rcu<InnerAtomTable>,
|
||||
inner: Arcu<InnerAtomTable, GlobalEpochCounterPool>,
|
||||
// this lock is taking during resizing
|
||||
update: Mutex<()>,
|
||||
}
|
||||
|
||||
pub type AtomTableRef<M> = RcuRef<InnerAtomTable, M>;
|
||||
pub type AtomTableRef<M> = arcu::rcu_ref::RcuRef<InnerAtomTable, M>;
|
||||
|
||||
impl InnerAtomTable {
|
||||
#[inline(always)]
|
||||
@@ -305,7 +430,7 @@ impl InnerAtomTable {
|
||||
STATIC_ATOMS_MAP
|
||||
.get(string)
|
||||
.cloned()
|
||||
.or_else(|| self.table.active_epoch().get(string).cloned())
|
||||
.or_else(|| self.table.read().get(string).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,10 +448,13 @@ impl AtomTable {
|
||||
atom_table
|
||||
} else {
|
||||
let atom_table = Arc::new(Self {
|
||||
inner: Rcu::new(InnerAtomTable {
|
||||
block: RawBlock::new(),
|
||||
table: Rcu::new(IndexSet::new()),
|
||||
}),
|
||||
inner: Arcu::new(
|
||||
InnerAtomTable {
|
||||
block: RawBlock::new(),
|
||||
table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool),
|
||||
},
|
||||
GlobalEpochCounterPool,
|
||||
),
|
||||
update: Mutex::new(()),
|
||||
});
|
||||
*guard = Arc::downgrade(&atom_table);
|
||||
@@ -335,21 +463,18 @@ impl AtomTable {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn buf(&self) -> AtomTableRef<u8> {
|
||||
AtomTableRef::<InnerAtomTable>::map(self.inner.active_epoch(), |inner| {
|
||||
unsafe { inner.block.base.as_ref() }.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
|
||||
self.inner.active_epoch().table.active_epoch()
|
||||
self.inner.read().table.read()
|
||||
}
|
||||
|
||||
pub fn build_with(atom_table: &AtomTable, string: &str) -> Atom {
|
||||
if !string.is_empty() && string.len() <= INLINED_ATOM_MAX_LEN && !string.contains('\u{0}') {
|
||||
return Atom::new_inlined(string);
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut block_epoch = atom_table.inner.active_epoch();
|
||||
let mut table_epoch = block_epoch.table.active_epoch();
|
||||
let mut block_epoch = atom_table.inner.read();
|
||||
let mut table_epoch = block_epoch.table.read();
|
||||
|
||||
if let Some(atom) = block_epoch.lookup_str(string) {
|
||||
return atom;
|
||||
@@ -358,10 +483,8 @@ impl AtomTable {
|
||||
// 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());
|
||||
let is_same_allocation = RcuRef::same_epoch(&block_epoch, &atom_table.inner.read());
|
||||
let is_same_atom_list = RcuRef::same_epoch(&table_epoch, &block_epoch.table.read());
|
||||
|
||||
if !(is_same_allocation && is_same_atom_list) {
|
||||
// some other thread raced us between our lookup and
|
||||
@@ -371,8 +494,7 @@ impl AtomTable {
|
||||
}
|
||||
|
||||
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 size = size.next_multiple_of(AtomTable::align());
|
||||
|
||||
unsafe {
|
||||
let len_ptr = loop {
|
||||
@@ -381,26 +503,31 @@ impl AtomTable {
|
||||
if ptr.is_null() {
|
||||
// garbage collection would go here
|
||||
let new_block = block_epoch.block.grow_new().unwrap();
|
||||
let new_table = Rcu::new(table_epoch.clone());
|
||||
let new_table = Arcu::new(table_epoch.clone(), GlobalEpochCounterPool);
|
||||
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();
|
||||
block_epoch = atom_table.inner.read();
|
||||
table_epoch = block_epoch.table.read();
|
||||
} else {
|
||||
break ptr;
|
||||
}
|
||||
};
|
||||
|
||||
let ptr_base = block_epoch.block.base as usize;
|
||||
let ptr_base = block_epoch.block.base.addr();
|
||||
|
||||
write_to_ptr(string, len_ptr);
|
||||
|
||||
let atom = Atom {
|
||||
index: ((STRINGS.len() << 3) + len_ptr as usize - ptr_base) as u64,
|
||||
};
|
||||
let atom = AtomCell::new()
|
||||
.with_name((STRINGS.len() + len_ptr.addr() - ptr_base) as u64)
|
||||
.with_arity(0)
|
||||
.with_f(false)
|
||||
.with_m(false)
|
||||
.with_is_inlined(false)
|
||||
.with_tag(HeapCellValueTag::Atom as u8)
|
||||
.get_name();
|
||||
|
||||
let mut table = table_epoch.clone();
|
||||
table.insert(atom);
|
||||
@@ -417,57 +544,3 @@ impl AtomTable {
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
impl AtomCell {
|
||||
#[inline]
|
||||
pub fn build_with(name: u64, arity: u16, tag: HeapCellValueTag) -> Self {
|
||||
if arity > 0 {
|
||||
debug_assert!(arity as usize <= MAX_ARITY);
|
||||
|
||||
AtomCell::new()
|
||||
.with_name(name)
|
||||
.with_arity(arity)
|
||||
.with_f(false)
|
||||
.with_tag(tag as u8)
|
||||
} else {
|
||||
AtomCell::new()
|
||||
.with_name(name)
|
||||
.with_f(false)
|
||||
.with_tag(tag as u8)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_index(self) -> usize {
|
||||
self.name() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name(self) -> Atom {
|
||||
Atom::from((self.get_index() as u64) << 3)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_arity(self) -> usize {
|
||||
self.arity() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name_and_arity(self) -> (Atom, usize) {
|
||||
(Atom::from((self.get_index() as u64) << 3), self.get_arity())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,7 @@
|
||||
fn main() -> std::process::ExitCode {
|
||||
use scryer_prolog::atom_table::Atom;
|
||||
use scryer_prolog::*;
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
ctrlc::set_handler(move || {
|
||||
scryer_prolog::machine::INTERRUPT.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
return std::process::ExitCode::SUCCESS;
|
||||
|
||||
#[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))
|
||||
})
|
||||
return scryer_prolog::run_binary();
|
||||
}
|
||||
|
||||
174
src/codegen.rs
174
src/codegen.rs
@@ -8,11 +8,9 @@ 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::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
|
||||
@@ -271,8 +269,7 @@ impl CodeGenSettings {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct CodeGenerator<'a> {
|
||||
pub(crate) atom_tbl: &'a AtomTable,
|
||||
pub(crate) struct CodeGenerator {
|
||||
marker: DebrayAllocator,
|
||||
settings: CodeGenSettings,
|
||||
pub(crate) skeleton: PredicateSkeleton,
|
||||
@@ -321,33 +318,12 @@ impl DebrayAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
// if the final argument of the structure is a Literal::Index,
|
||||
// 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, _) => {
|
||||
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
|
||||
// constant for '$call_inline' to succeed. to find it,
|
||||
// it must know the heap location of the index.
|
||||
// self.store must stop before reading the atom into a
|
||||
// register.
|
||||
|
||||
*arity -= 1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
trait AddToFreeList<'a, Target: CompilationTarget<'a>> {
|
||||
fn add_term_to_free_list(&mut self, r: RegType);
|
||||
fn add_subterm_to_free_list(&mut self, term: &Term);
|
||||
}
|
||||
|
||||
impl<'a, 'b> AddToFreeList<'a, FactInstruction> for CodeGenerator<'b> {
|
||||
impl<'a> AddToFreeList<'a, FactInstruction> for CodeGenerator {
|
||||
fn add_term_to_free_list(&mut self, r: RegType) {
|
||||
self.marker.add_reg_to_free_list(r);
|
||||
}
|
||||
@@ -355,7 +331,7 @@ impl<'a, 'b> AddToFreeList<'a, FactInstruction> for CodeGenerator<'b> {
|
||||
fn add_subterm_to_free_list(&mut self, _term: &Term) {}
|
||||
}
|
||||
|
||||
impl<'a, 'b> AddToFreeList<'a, QueryInstruction> for CodeGenerator<'b> {
|
||||
impl<'a> AddToFreeList<'a, QueryInstruction> for CodeGenerator {
|
||||
#[inline(always)]
|
||||
fn add_term_to_free_list(&mut self, _r: RegType) {}
|
||||
|
||||
@@ -377,10 +353,9 @@ fn structure_cell(term: &Term) -> Option<&Cell<RegType>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> CodeGenerator<'b> {
|
||||
pub(crate) fn new(atom_tbl: &'b AtomTable, settings: CodeGenSettings) -> Self {
|
||||
impl CodeGenerator {
|
||||
pub(crate) fn new(settings: CodeGenSettings) -> Self {
|
||||
CodeGenerator {
|
||||
atom_tbl,
|
||||
marker: DebrayAllocator::new(),
|
||||
settings,
|
||||
skeleton: PredicateSkeleton::new(),
|
||||
@@ -448,99 +423,99 @@ impl<'b> CodeGenerator<'b> {
|
||||
};
|
||||
}
|
||||
|
||||
fn compile_target<'a, Target, Iter>(&mut self, iter: Iter, term_loc: GenContext) -> CodeDeque
|
||||
fn compile_target<'a, Target, Iter>(&mut self, iter: Iter, context: GenContext) -> CodeDeque
|
||||
where
|
||||
Target: crate::targets::CompilationTarget<'a>,
|
||||
Iter: Iterator<Item = TermRef<'a>>,
|
||||
CodeGenerator<'b>: AddToFreeList<'a, Target>,
|
||||
CodeGenerator: AddToFreeList<'a, Target>,
|
||||
{
|
||||
let mut target = CodeDeque::new();
|
||||
|
||||
for term in iter {
|
||||
match term {
|
||||
TermRef::AnonVar(lvl @ Level::Shallow) => {
|
||||
if let GenContext::Head = term_loc {
|
||||
if let GenContext::Head = context {
|
||||
self.marker.advance_arg();
|
||||
} else {
|
||||
self.marker
|
||||
.mark_anon_var::<Target>(lvl, term_loc, &mut target);
|
||||
.mark_anon_var::<Target>(lvl, context, &mut target);
|
||||
}
|
||||
}
|
||||
TermRef::Clause(lvl, cell, name, terms) => {
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
target.push_back(Target::to_structure(lvl, name, terms.len(), cell.get()));
|
||||
let terms_range =
|
||||
if let Some(subterm @ Term::Literal(_, Literal::CodeIndexOffset(_))) =
|
||||
terms.last()
|
||||
{
|
||||
self.subterm_to_instr::<Target>(subterm, context, &mut target);
|
||||
0..terms.len() - 1
|
||||
} else {
|
||||
0..terms.len()
|
||||
};
|
||||
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_term_to_free_list(
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, context, cell, &mut target);
|
||||
target.push_back(Target::to_structure(lvl, name, terms_range.end, cell.get()));
|
||||
|
||||
<CodeGenerator 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() {
|
||||
trim_structure_by_last_arg(instr, term);
|
||||
}
|
||||
for subterm in &terms[terms_range.clone()] {
|
||||
self.subterm_to_instr::<Target>(subterm, context, &mut target);
|
||||
}
|
||||
|
||||
for subterm in terms {
|
||||
self.subterm_to_instr::<Target>(subterm, term_loc, &mut target);
|
||||
}
|
||||
|
||||
for subterm in terms {
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_subterm_to_free_list(
|
||||
for subterm in &terms[terms_range] {
|
||||
<CodeGenerator 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);
|
||||
.mark_non_var::<Target>(lvl, context, cell, &mut target);
|
||||
target.push_back(Target::to_list(lvl, cell.get()));
|
||||
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_term_to_free_list(
|
||||
<CodeGenerator 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);
|
||||
self.subterm_to_instr::<Target>(head, context, &mut target);
|
||||
self.subterm_to_instr::<Target>(tail, context, &mut target);
|
||||
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_subterm_to_free_list(
|
||||
<CodeGenerator as AddToFreeList<'a, Target>>::add_subterm_to_free_list(
|
||||
self, head,
|
||||
);
|
||||
<CodeGenerator<'b> as AddToFreeList<'a, Target>>::add_subterm_to_free_list(
|
||||
<CodeGenerator 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);
|
||||
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);
|
||||
.mark_non_var::<Target>(lvl, context, 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 = AtomTable::build_with(self.atom_tbl, string);
|
||||
.mark_non_var::<Target>(lvl, context, cell, &mut target);
|
||||
|
||||
target.push_back(Target::to_pstr(lvl, atom, cell.get(), true));
|
||||
self.subterm_to_instr::<Target>(tail, term_loc, &mut target);
|
||||
target.push_back(Target::to_pstr(lvl, string.clone(), cell.get()));
|
||||
self.subterm_to_instr::<Target>(tail, context, &mut target);
|
||||
}
|
||||
TermRef::CompleteString(lvl, cell, atom) => {
|
||||
TermRef::CompleteString(lvl, cell, string) => {
|
||||
self.marker
|
||||
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
|
||||
target.push_back(Target::to_pstr(lvl, atom, cell.get(), false));
|
||||
.mark_non_var::<Target>(lvl, context, cell, &mut target);
|
||||
|
||||
target.push_back(Target::to_pstr(lvl, string.clone(), cell.get()));
|
||||
target.push_back(Target::constant_subterm(Literal::Atom(atom!("[]"))));
|
||||
}
|
||||
TermRef::Var(lvl @ Level::Shallow, cell, var) => {
|
||||
self.marker.mark_var::<Target>(
|
||||
var.to_var_num().unwrap(),
|
||||
lvl,
|
||||
cell,
|
||||
term_loc,
|
||||
context,
|
||||
&mut target,
|
||||
);
|
||||
}
|
||||
@@ -559,14 +534,14 @@ impl<'b> CodeGenerator<'b> {
|
||||
match call_policy {
|
||||
CallPolicy::Default => {
|
||||
if self.marker.in_tail_position {
|
||||
code.push_back(call_instr.to_execute().to_default());
|
||||
code.push_back(call_instr.into_execute().into_default());
|
||||
} else {
|
||||
code.push_back(call_instr.to_default())
|
||||
code.push_back(call_instr.into_default())
|
||||
}
|
||||
}
|
||||
CallPolicy::Counted => {
|
||||
if self.marker.in_tail_position {
|
||||
code.push_back(call_instr.to_execute());
|
||||
code.push_back(call_instr.into_execute());
|
||||
} else {
|
||||
code.push_back(call_instr)
|
||||
}
|
||||
@@ -602,9 +577,7 @@ 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(..)) => {
|
||||
Term::Literal(_, Literal::Atom(..)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
Term::Var(ref vr, ref name) => {
|
||||
@@ -632,9 +605,6 @@ impl<'b> CodeGenerator<'b> {
|
||||
| Term::CompleteString(..) => {
|
||||
instr!("$fail")
|
||||
}
|
||||
Term::Literal(_, Literal::String(_)) => {
|
||||
instr!("$fail")
|
||||
}
|
||||
Term::Literal(..) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
@@ -656,8 +626,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
Term::Clause(..)
|
||||
| Term::Cons(..)
|
||||
| Term::PartialString(..)
|
||||
| Term::CompleteString(..)
|
||||
| Term::Literal(_, Literal::String(..)) => {
|
||||
| Term::CompleteString(..) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
Term::Var(ref vr, ref name) => {
|
||||
@@ -697,7 +666,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
}
|
||||
},
|
||||
InlinedClauseType::IsFloat(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::Float(_)) => {
|
||||
Term::Literal(_, Literal::F64(..)) => {
|
||||
instr!("$succeed")
|
||||
}
|
||||
Term::Var(ref vr, ref name) => {
|
||||
@@ -718,7 +687,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
}
|
||||
},
|
||||
InlinedClauseType::IsNumber(..) => match terms[0] {
|
||||
Term::Literal(_, Literal::Float(_))
|
||||
Term::Literal(_, Literal::F64(..))
|
||||
| Term::Literal(_, Literal::Rational(_))
|
||||
| Term::Literal(_, Literal::Integer(_))
|
||||
| Term::Literal(_, Literal::Fixnum(_)) => {
|
||||
@@ -812,7 +781,6 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
// inlined predicates are never counted, so this overrides nothing.
|
||||
self.add_call(code, call_instr, CallPolicy::Counted);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -893,7 +861,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
Term::Literal(
|
||||
_,
|
||||
c @ Literal::Integer(_)
|
||||
| c @ Literal::Float(_)
|
||||
| c @ Literal::F64(..)
|
||||
| c @ Literal::Rational(_)
|
||||
| c @ Literal::Fixnum(_),
|
||||
) => {
|
||||
@@ -911,7 +879,6 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
let at = at.unwrap_or(interm!(1));
|
||||
self.add_call(code, instr!("is", temp_v!(1), at), call_policy);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -926,9 +893,9 @@ impl<'b> CodeGenerator<'b> {
|
||||
|
||||
while let Some(clause_item) = clause_iter.next() {
|
||||
match clause_item {
|
||||
ClauseItem::Chunk(chunk) => {
|
||||
for (idx, term) in chunk.iter().enumerate() {
|
||||
let term_loc = if idx + 1 < chunk.len() {
|
||||
ClauseItem::Chunk { terms } => {
|
||||
for (idx, term) in terms.iter().enumerate() {
|
||||
let term_loc = if idx + 1 < terms.len() {
|
||||
GenContext::Mid(chunk_num)
|
||||
} else {
|
||||
self.marker.in_tail_position = clause_iter.in_tail_position();
|
||||
@@ -1149,27 +1116,24 @@ impl<'b> CodeGenerator<'b> {
|
||||
'outer: for (right, clause) in clauses.iter().enumerate() {
|
||||
if let Some(args) = clause.args() {
|
||||
for (instantiated_arg_index, arg) in args.iter().enumerate() {
|
||||
match arg {
|
||||
Term::Var(..) | Term::AnonVar => {}
|
||||
_ => {
|
||||
if optimal_index != instantiated_arg_index {
|
||||
if left >= right {
|
||||
optimal_index = instantiated_arg_index;
|
||||
continue 'outer;
|
||||
}
|
||||
|
||||
subseqs.push(ClauseSpan {
|
||||
left,
|
||||
right,
|
||||
instantiated_arg_index: optimal_index,
|
||||
});
|
||||
|
||||
if !matches!(arg, Term::Var(..) | Term::AnonVar) {
|
||||
if optimal_index != instantiated_arg_index {
|
||||
if left >= right {
|
||||
optimal_index = instantiated_arg_index;
|
||||
left = right;
|
||||
continue 'outer;
|
||||
}
|
||||
|
||||
continue 'outer;
|
||||
subseqs.push(ClauseSpan {
|
||||
left,
|
||||
right,
|
||||
instantiated_arg_index: optimal_index,
|
||||
});
|
||||
|
||||
optimal_index = instantiated_arg_index;
|
||||
left = right;
|
||||
}
|
||||
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1262,7 +1226,7 @@ impl<'b> CodeGenerator<'b> {
|
||||
let index = code.len();
|
||||
|
||||
if clauses_len > 1 || self.settings.is_extensible {
|
||||
code_offsets.index_term(arg, index, &mut clause_index_info, self.atom_tbl);
|
||||
code_offsets.index_term(arg, index, &mut clause_index_info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::allocator::*;
|
||||
use crate::codegen::SubsumedBranchHits;
|
||||
use crate::forms::Level;
|
||||
use crate::forms::{GenContext, Level};
|
||||
use crate::instructions::*;
|
||||
use crate::machine::disjuncts::VarData;
|
||||
use crate::parser::ast::*;
|
||||
@@ -133,7 +133,10 @@ impl BranchStack {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn drain_branches(&mut self, depth: usize) -> std::vec::Drain<BranchOccurrences> {
|
||||
pub(crate) fn drain_branches(
|
||||
&mut self,
|
||||
depth: usize,
|
||||
) -> std::vec::Drain<'_, BranchOccurrences> {
|
||||
let start_idx = self.len() - depth;
|
||||
self.drain(start_idx..)
|
||||
}
|
||||
@@ -251,7 +254,7 @@ impl DebrayAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
if self.branch_stack.len() > 0 {
|
||||
if !self.branch_stack.is_empty() {
|
||||
for var_num in subsumed_hits {
|
||||
self.branch_stack.add_branch_occurrence(var_num);
|
||||
}
|
||||
@@ -534,7 +537,9 @@ impl DebrayAllocator {
|
||||
VarAlloc::Temp { safety, .. } => {
|
||||
*safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,7 +684,7 @@ impl Allocator for DebrayAllocator {
|
||||
lvl: Level,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
) {
|
||||
) -> RegType {
|
||||
let r = RegType::Temp(self.alloc_reg_to_non_var());
|
||||
|
||||
match lvl {
|
||||
@@ -696,6 +701,8 @@ impl Allocator for DebrayAllocator {
|
||||
code.push_back(Target::argument_to_variable(r, k));
|
||||
}
|
||||
};
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
|
||||
|
||||
1214
src/ffi.rs
1214
src/ffi.rs
File diff suppressed because it is too large
Load Diff
129
src/forms.rs
129
src/forms.rs
@@ -1,8 +1,8 @@
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::functor_macro::*;
|
||||
use crate::instructions::*;
|
||||
use crate::machine::disjuncts::VarData;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::loader::PredicateQueue;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
@@ -24,22 +24,8 @@ use std::fmt;
|
||||
use std::ops::{AddAssign, Deref, DerefMut};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{is_infix, is_postfix};
|
||||
|
||||
pub type PredicateKey = (Atom, usize); // name, arity.
|
||||
|
||||
/*
|
||||
// vars of predicate, toplevel offset. Vec<Term> is always a vector
|
||||
// of vars (we get their adjoining cells this way).
|
||||
pub type JumpStub = Vec<Term>;
|
||||
*/
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TopLevel {
|
||||
Fact(Fact, VarData), // Term, line_num, col_num
|
||||
Rule(Rule, VarData), // Rule, line_num, col_num
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AppendOrPrepend {
|
||||
Append,
|
||||
@@ -84,6 +70,28 @@ pub enum CallPolicy {
|
||||
Counted,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum GenContext {
|
||||
Head,
|
||||
Mid(usize),
|
||||
Last(usize), // Mid & Last: chunk_num
|
||||
}
|
||||
|
||||
impl GenContext {
|
||||
#[inline]
|
||||
pub fn chunk_num(&self) -> usize {
|
||||
match self {
|
||||
GenContext::Head => 0,
|
||||
&GenContext::Mid(cn) | &GenContext::Last(cn) => cn,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_last(self) -> bool {
|
||||
matches!(self, GenContext::Last(_))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ChunkType {
|
||||
Head,
|
||||
@@ -123,7 +131,7 @@ impl ChunkType {
|
||||
#[derive(Debug)]
|
||||
pub enum ChunkedTerms {
|
||||
Branch(Vec<VecDeque<ChunkedTerms>>),
|
||||
Chunk(VecDeque<QueryTerm>),
|
||||
Chunk { terms: VecDeque<QueryTerm> },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -161,35 +169,32 @@ impl ChunkedTermVec {
|
||||
.push_back(ChunkedTerms::Branch(Vec::with_capacity(capacity)));
|
||||
}
|
||||
|
||||
pub fn push_branch_arm(&mut self, branch: VecDeque<ChunkedTerms>) {
|
||||
match self.chunk_vec.back_mut().unwrap() {
|
||||
ChunkedTerms::Branch(branches) => {
|
||||
branches.push(branch);
|
||||
}
|
||||
ChunkedTerms::Chunk(_) => {
|
||||
self.chunk_vec.push_back(ChunkedTerms::Branch(vec![branch]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add_chunk(&mut self) {
|
||||
self.chunk_vec
|
||||
.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![])));
|
||||
let chunk = ChunkedTerms::Chunk {
|
||||
terms: VecDeque::from(vec![]),
|
||||
};
|
||||
self.chunk_vec.push_back(chunk);
|
||||
}
|
||||
|
||||
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])));
|
||||
let chunk = ChunkedTerms::Chunk {
|
||||
terms: VecDeque::from(vec![term]),
|
||||
};
|
||||
|
||||
self.chunk_vec.push_back(chunk);
|
||||
}
|
||||
Some(ChunkedTerms::Chunk(chunk)) => {
|
||||
chunk.push_back(term);
|
||||
Some(ChunkedTerms::Chunk { terms, .. }) => {
|
||||
terms.push_back(term);
|
||||
}
|
||||
None => {
|
||||
self.chunk_vec
|
||||
.push_back(ChunkedTerms::Chunk(VecDeque::from(vec![term])));
|
||||
let chunk = ChunkedTerms::Chunk {
|
||||
terms: VecDeque::from(vec![term]),
|
||||
};
|
||||
|
||||
self.chunk_vec.push_back(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,9 +371,9 @@ pub enum ModuleSource {
|
||||
|
||||
impl ModuleSource {
|
||||
pub(crate) fn as_functor_stub(&self) -> MachineStub {
|
||||
match self {
|
||||
match *self {
|
||||
ModuleSource::Library(name) => {
|
||||
functor!(atom!("library"), [atom(name)])
|
||||
functor!(atom!("library"), [atom_as_cell(name)])
|
||||
}
|
||||
ModuleSource::File(name) => {
|
||||
functor!(name)
|
||||
@@ -403,16 +408,6 @@ pub struct OpDecl {
|
||||
pub(crate) name: Atom,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn fixity(spec: u32) -> Fixity {
|
||||
match spec {
|
||||
XFY | XFX | YFX => Fixity::In,
|
||||
XF | YF => Fixity::Post,
|
||||
FX | FY => Fixity::Pre,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
impl OpDecl {
|
||||
#[inline]
|
||||
pub(crate) fn new(op_desc: OpDesc, name: Atom) -> Self {
|
||||
@@ -429,7 +424,7 @@ 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));
|
||||
let key = (self.name, self.op_desc.get_spec().fixity());
|
||||
|
||||
if let Some(cell) = op_dir.get_mut(&key) {
|
||||
let (old_prec, old_spec) = cell.get();
|
||||
@@ -447,7 +442,7 @@ impl OpDecl {
|
||||
) -> Result<(), SessionError> {
|
||||
let (spec, name) = (self.op_desc.get_spec(), self.name);
|
||||
|
||||
if is_infix!(spec as u32) {
|
||||
if spec.is_infix() {
|
||||
if let Some(desc) = existing_desc {
|
||||
if desc.post > 0 {
|
||||
return Err(SessionError::OpIsInfixAndPostFix(name));
|
||||
@@ -455,7 +450,7 @@ impl OpDecl {
|
||||
}
|
||||
}
|
||||
|
||||
if is_postfix!(spec as u32) {
|
||||
if spec.is_postfix() {
|
||||
if let Some(desc) = existing_desc {
|
||||
if desc.inf > 0 {
|
||||
return Err(SessionError::OpIsInfixAndPostFix(name));
|
||||
@@ -631,9 +626,9 @@ impl Default for Number {
|
||||
impl fmt::Display for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Number::Float(fl) => write!(f, "{}", fl),
|
||||
Number::Integer(n) => write!(f, "{}", n),
|
||||
Number::Rational(r) => write!(f, "{}", r),
|
||||
Number::Float(fl) => write!(f, "{fl}"),
|
||||
Number::Integer(n) => write!(f, "{n}"),
|
||||
Number::Rational(r) => write!(f, "{r}"),
|
||||
Number::Fixnum(n) => write!(f, "{}", n.get_num()),
|
||||
}
|
||||
}
|
||||
@@ -702,17 +697,18 @@ impl ArenaFrom<isize> for Number {
|
||||
impl ArenaFrom<u32> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: u32, _arena: &mut Arena) -> Number {
|
||||
Number::Fixnum(Fixnum::build_with(value as i64))
|
||||
Number::Fixnum(Fixnum::build_with(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<i32> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: i32, _arena: &mut Arena) -> Number {
|
||||
Number::Fixnum(Fixnum::build_with(value as i64))
|
||||
Number::Fixnum(Fixnum::build_with(value))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl ArenaFrom<Number> for Literal {
|
||||
#[inline]
|
||||
fn arena_from(value: Number, arena: &mut Arena) -> Literal {
|
||||
@@ -724,6 +720,21 @@ impl ArenaFrom<Number> for Literal {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl ArenaFrom<u64> for HeapCellValue {
|
||||
#[inline]
|
||||
fn arena_from(value: u64, arena: &mut Arena) -> HeapCellValue {
|
||||
HeapCellValue::from(fixnum!(Literal, value as i64, arena))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<usize> for HeapCellValue {
|
||||
#[inline]
|
||||
fn arena_from(value: usize, arena: &mut Arena) -> HeapCellValue {
|
||||
HeapCellValue::arena_from(value as u64, arena)
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<Number> for HeapCellValue {
|
||||
#[inline]
|
||||
@@ -731,7 +742,7 @@ impl ArenaFrom<Number> for HeapCellValue {
|
||||
match value {
|
||||
Number::Fixnum(n) => fixnum_as_cell!(n),
|
||||
Number::Integer(n) => typed_arena_ptr_as_cell!(n),
|
||||
Number::Float(OrderedFloat(n)) => HeapCellValue::from(float_alloc!(n, arena)),
|
||||
Number::Float(n) => HeapCellValue::from(arena.f64_tbl.build_with(n)),
|
||||
Number::Rational(n) => typed_arena_ptr_as_cell!(n),
|
||||
}
|
||||
}
|
||||
@@ -794,10 +805,10 @@ impl Number {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum OptArgIndexKey {
|
||||
Literal(usize, usize, Literal, Vec<Literal>), // index, IndexingCode location, opt arg, alternatives
|
||||
List(usize, usize), // index, IndexingCode location
|
||||
Literal(usize, usize, Literal, Option<Literal>), // index, IndexingCode location, opt arg, alternatives
|
||||
List(usize, usize), // index, IndexingCode location
|
||||
None,
|
||||
Structure(usize, usize, Atom, usize), // index, IndexingCode location, name, arity
|
||||
}
|
||||
|
||||
798
src/functor_macro.rs
Normal file
798
src/functor_macro.rs
Normal file
@@ -0,0 +1,798 @@
|
||||
//! A macro to construct functor terms ready to be written to the WAM
|
||||
//! heap.
|
||||
|
||||
use crate::atom_table::*;
|
||||
use crate::instructions::IndexingCodePtr;
|
||||
use crate::machine::heap::Heap;
|
||||
use crate::parser::ast::Fixnum;
|
||||
use crate::types::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum FunctorElement {
|
||||
AbsoluteCell(HeapCellValue),
|
||||
Cell(HeapCellValue),
|
||||
InnerFunctor(u64, Vec<FunctorElement>),
|
||||
String(u64, String),
|
||||
}
|
||||
|
||||
// helper macros
|
||||
macro_rules! count {
|
||||
() => (0);
|
||||
( $x:tt $($xs:tt)* ) => (1 + count!($($xs)*));
|
||||
}
|
||||
|
||||
// core macros
|
||||
|
||||
/*
|
||||
* functor! is more declarative now, with fewer effects and more
|
||||
* work done at compile time using const functions. With these
|
||||
* advantages come new quirks: expressions must generally be wrapped
|
||||
* in round parentheses for rustc to parse them. See the tests module
|
||||
* below for examples, especially those involving atom!
|
||||
* subexpressions.
|
||||
*/
|
||||
|
||||
macro_rules! functor {
|
||||
($name:expr) => ({
|
||||
vec![FunctorElement::Cell(atom_as_cell!($name))]
|
||||
});
|
||||
($name:expr, [$($dt:ident($($value:tt),*)),+]) => ({
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[FunctorElement::Cell(atom_as_cell!($name, count!($($dt) *)))],
|
||||
1,
|
||||
[])
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! inner_functor {
|
||||
($name:expr, $res_len:expr, [$($dt:ident($($value:tt),*)),+]) => ({
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[FunctorElement::Cell(atom_as_cell!($name, count!($($dt) *)))],
|
||||
1 + $res_len,
|
||||
[])
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! build_functor {
|
||||
([], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({
|
||||
vec![$($res,)* $($subfunctor),*]
|
||||
});
|
||||
([indexing_code_ptr($e:expr) $(, $dt:ident($($value:tt),*))*],
|
||||
[$($res:expr),*],
|
||||
$res_len:expr,
|
||||
[$($subfunctor:expr),*]) => ({
|
||||
let (inner_functor, cell_size) = indexing_code_ptr($e);
|
||||
let referent = if cell_size == 1 {
|
||||
heap_loc_as_cell!(1u64 + count!($($dt)*) + $res_len)
|
||||
} else {
|
||||
str_loc_as_cell!(1u64 + count!($($dt)*) + $res_len)
|
||||
};
|
||||
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::Cell(referent)],
|
||||
1 + cell_size + $res_len,
|
||||
[$($subfunctor, )* FunctorElement::InnerFunctor(cell_size, inner_functor)])
|
||||
});
|
||||
([fixnum($e:expr) $(, $dt:ident($($value:tt),*))*],
|
||||
[$($res:expr),*],
|
||||
$res_len:expr,
|
||||
[$($subfunctor:expr),*]) => ({
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::Cell(fixnum_as_cell!(/*FIXME this is not safe*/ unsafe{Fixnum::build_with_unchecked($e as i64)}))],
|
||||
1 + $res_len,
|
||||
[$($subfunctor),*])
|
||||
});
|
||||
([cell($e:expr) $(, $dt:ident($($value:tt),*))*],
|
||||
[$($res:expr),*],
|
||||
$res_len:expr,
|
||||
[$($subfunctor:expr),*]) => ({
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::AbsoluteCell($e)],
|
||||
1 + $res_len,
|
||||
[$($subfunctor),*])
|
||||
});
|
||||
([literal($e:expr) $(, $dt:ident($($value:tt),*))*],
|
||||
[$($res:expr),*],
|
||||
$res_len:expr,
|
||||
[$($subfunctor:expr),*]) => ({
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::AbsoluteCell(HeapCellValue::from($e))],
|
||||
1 + $res_len,
|
||||
[$($subfunctor),*])
|
||||
});
|
||||
([number($n:expr, $arena:expr) $(, $dt:ident($($value:tt),*))*],
|
||||
[$($res:expr),*],
|
||||
$res_len:expr,
|
||||
[$($subfunctor:expr),*]) => ({
|
||||
let number_cell = HeapCellValue::arena_from($n, $arena);
|
||||
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::Cell(number_cell)],
|
||||
1 + $res_len,
|
||||
[$($subfunctor),*])
|
||||
});
|
||||
([list([]) $(, $dt:ident($($value:tt),*))*],
|
||||
[$($res:expr),*],
|
||||
$res_len:expr,
|
||||
[$($subfunctor:expr),*]) => ({
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::Cell(empty_list_as_cell!())],
|
||||
1 + $res_len,
|
||||
[$($subfunctor),*])
|
||||
});
|
||||
([list([$id:ident($($id_value:tt),*) $(, $in_dt:ident($($in_value:tt),*))*]) $(, $dt:ident($($value:tt),*))*],
|
||||
[$($res:expr),*],
|
||||
$res_len:expr,
|
||||
[$($subfunctor:expr),*]) => ({
|
||||
build_functor!([functor((atom!(".")), [$id($($id_value),*), list([$($in_dt($($in_value),*)),*])])
|
||||
$(, $dt($($value),*))*],
|
||||
[$($res),*],
|
||||
$res_len,
|
||||
[$($subfunctor),*])
|
||||
});
|
||||
([string($s:expr) $(, $dt:ident($($value:tt),*))*], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({
|
||||
#[allow(unused_parens)]
|
||||
let string = $s;
|
||||
let pstr_len = cell_index!(Heap::compute_pstr_size(&string)) as u64;
|
||||
let result_len = 1 + count!($($dt)*) + $res_len;
|
||||
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::Cell(pstr_loc_as_cell!(heap_index!(result_len as usize) as u64))],
|
||||
1 + $res_len + pstr_len,
|
||||
[$($subfunctor, )* FunctorElement::String(pstr_len, string)])
|
||||
});
|
||||
([atom_as_cell($n:expr) $(, $dt:ident($($value:tt),*))*], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::Cell(atom_as_cell!($n))],
|
||||
1 + $res_len,
|
||||
[$($subfunctor),*])
|
||||
});
|
||||
([functor($stub:expr) $(, $dt:ident($($value:tt),*))*], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({
|
||||
let result_len = 1u64 + count!($($dt)*) + $res_len;
|
||||
let inner_functor_size = cell_index!(Heap::compute_functor_byte_size(&$stub)) as u64;
|
||||
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::Cell(str_loc_as_cell!(result_len))],
|
||||
1 + $res_len + inner_functor_size,
|
||||
[$($subfunctor, )*
|
||||
FunctorElement::InnerFunctor(inner_functor_size, $stub)])
|
||||
});
|
||||
([$id:ident($n:expr) $(, $dt:ident($($value:tt),*))*], [$($res:expr),*], $res_len:expr, [$($subfunctor:expr),*]) => ({
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::Cell($id!($n))],
|
||||
1 + $res_len,
|
||||
[$($subfunctor),*])
|
||||
});
|
||||
([functor($name:expr, [$($in_dt:ident($($in_value:tt),*)),+]) $(, $dt:ident($($value:tt),*))*],
|
||||
[$($res:expr),*],
|
||||
$res_len:expr,
|
||||
[$($subfunctor:expr),*]) => ({
|
||||
let result_len = 1u64 + count!($($dt)*) + $res_len;
|
||||
let inner_functor = inner_functor!($name, 0, [$($in_dt($($in_value),*)),*]);
|
||||
let inner_functor_size = cell_index!(Heap::compute_functor_byte_size(&inner_functor)) as u64;
|
||||
|
||||
build_functor!([$($dt($($value),*)),*],
|
||||
[$($res, )* FunctorElement::Cell(str_loc_as_cell!(result_len))],
|
||||
1 + $res_len + inner_functor_size,
|
||||
[$($subfunctor, )*
|
||||
FunctorElement::InnerFunctor(inner_functor_size, inner_functor)])
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn indexing_code_ptr(code_ptr: IndexingCodePtr) -> (Vec<FunctorElement>, u64) {
|
||||
match code_ptr {
|
||||
IndexingCodePtr::DynamicExternal(o) => {
|
||||
(functor!(atom!("dynamic_external"), [fixnum(o)]), 2)
|
||||
}
|
||||
IndexingCodePtr::External(o) => (functor!(atom!("external"), [fixnum(o)]), 2),
|
||||
IndexingCodePtr::Internal(o) => (functor!(atom!("internal"), [fixnum(o)]), 2),
|
||||
IndexingCodePtr::Fail => (vec![FunctorElement::Cell(atom_as_cell!(atom!("fail")))], 1),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn variadic_functor(
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
iter: impl Iterator<Item = Vec<FunctorElement>>,
|
||||
) -> Vec<FunctorElement> {
|
||||
let mut arg_vec = vec![
|
||||
FunctorElement::Cell(atom_as_cell!(name, arity)),
|
||||
FunctorElement::Cell(list_loc_as_cell!(2)),
|
||||
];
|
||||
|
||||
let key_value_pairs: Vec<_> = iter.collect();
|
||||
let num_items = key_value_pairs.len();
|
||||
let mut functor_offset = 0;
|
||||
|
||||
for (idx, kv_func) in key_value_pairs.iter().enumerate() {
|
||||
let functor_size = cell_index!(Heap::compute_functor_byte_size(kv_func));
|
||||
|
||||
arg_vec.push(FunctorElement::Cell(str_loc_as_cell!(
|
||||
2 + num_items * 2 + functor_offset
|
||||
)));
|
||||
arg_vec.push(FunctorElement::Cell(list_loc_as_cell!(4 + 2 * idx)));
|
||||
|
||||
functor_offset += functor_size;
|
||||
}
|
||||
|
||||
arg_vec.pop();
|
||||
arg_vec.push(FunctorElement::Cell(empty_list_as_cell!()));
|
||||
|
||||
arg_vec.extend(key_value_pairs.into_iter().map(|kv_func| {
|
||||
let inner_functor_size = cell_index!(Heap::compute_functor_byte_size(&kv_func));
|
||||
FunctorElement::InnerFunctor(inner_functor_size as u64, kv_func)
|
||||
}));
|
||||
|
||||
arg_vec
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(unused_parens)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use indexmap::indexmap;
|
||||
use std::string::String;
|
||||
use FunctorElement::*;
|
||||
|
||||
#[test]
|
||||
fn basic_terms() {
|
||||
let functor = functor!(
|
||||
atom!("first"),
|
||||
[atom_as_cell((atom!("a"))), char_as_cell('c')]
|
||||
);
|
||||
|
||||
assert_eq!(functor.len(), 3);
|
||||
|
||||
assert_eq!(functor[0], Cell(atom_as_cell!(atom!("first"), 2)));
|
||||
assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a"))));
|
||||
assert_eq!(functor[2], Cell(char_as_cell!('c')));
|
||||
|
||||
let functor = functor!(
|
||||
atom!("second"),
|
||||
[
|
||||
atom_as_cell((atom!("a"))),
|
||||
functor((atom!("b")), [fixnum(1), fixnum(2)]),
|
||||
char_as_cell('c')
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(functor.len(), 5);
|
||||
|
||||
assert_eq!(functor[0], Cell(atom_as_cell!(atom!("second"), 3)));
|
||||
assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a"))));
|
||||
assert_eq!(functor[2], Cell(str_loc_as_cell!(4)));
|
||||
assert_eq!(functor[3], Cell(char_as_cell!('c')));
|
||||
assert_eq!(
|
||||
functor[4],
|
||||
InnerFunctor(3, functor!(atom!("b"), [fixnum(1), fixnum(2)]))
|
||||
);
|
||||
|
||||
let functor = functor!(
|
||||
atom!("third"),
|
||||
[
|
||||
atom_as_cell((atom!("a"))),
|
||||
functor((atom!("b")), [fixnum(1), fixnum(2)]),
|
||||
functor((atom!("c")), [fixnum(1), fixnum(2)]),
|
||||
char_as_cell('c')
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(functor.len(), 7);
|
||||
|
||||
assert_eq!(functor[0], Cell(atom_as_cell!(atom!("third"), 4)));
|
||||
assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a"))));
|
||||
assert_eq!(functor[2], Cell(str_loc_as_cell!(5)));
|
||||
assert_eq!(functor[3], Cell(str_loc_as_cell!(8)));
|
||||
assert_eq!(functor[4], Cell(char_as_cell!('c')));
|
||||
assert_eq!(
|
||||
functor[5],
|
||||
InnerFunctor(3, functor!(atom!("b"), [fixnum(1), fixnum(2)]))
|
||||
);
|
||||
assert_eq!(
|
||||
functor[6],
|
||||
InnerFunctor(3, functor!(atom!("c"), [fixnum(1), fixnum(2)]))
|
||||
);
|
||||
|
||||
let functor = functor!(
|
||||
atom!("fourth"),
|
||||
[
|
||||
atom_as_cell((atom!("a"))),
|
||||
functor((atom!("b")), [fixnum(1), fixnum(2)]),
|
||||
functor((atom!("c")), [fixnum(1)]),
|
||||
functor((atom!("d")), [fixnum(453), fixnum(2)]),
|
||||
char_as_cell('c')
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(functor.len(), 9);
|
||||
|
||||
assert_eq!(functor[0], Cell(atom_as_cell!(atom!("fourth"), 5)));
|
||||
assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a"))));
|
||||
assert_eq!(functor[2], Cell(str_loc_as_cell!(6)));
|
||||
assert_eq!(functor[3], Cell(str_loc_as_cell!(9)));
|
||||
assert_eq!(functor[4], Cell(str_loc_as_cell!(11)));
|
||||
assert_eq!(functor[5], Cell(char_as_cell!('c')));
|
||||
assert_eq!(
|
||||
functor[6],
|
||||
InnerFunctor(3, functor!(atom!("b"), [fixnum(1), fixnum(2)]))
|
||||
);
|
||||
assert_eq!(
|
||||
functor[7],
|
||||
InnerFunctor(2, functor!(atom!("c"), [fixnum(1)]))
|
||||
);
|
||||
assert_eq!(
|
||||
functor[8],
|
||||
InnerFunctor(3, functor!(atom!("d"), [fixnum(453), fixnum(2)]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_terms_in_heap() {
|
||||
let functor = functor!(
|
||||
atom!("first"),
|
||||
[atom_as_cell((atom!("a"))), char_as_cell('b')]
|
||||
);
|
||||
|
||||
assert_eq!(functor.len(), 3);
|
||||
|
||||
let mut heap = Heap::new();
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
let loc = functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(loc, str_loc_as_cell!(0));
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("first"), 2));
|
||||
assert_eq!(heap[1], atom_as_cell!(atom!("a")));
|
||||
assert_eq!(heap[2], char_as_cell!('b'));
|
||||
|
||||
heap.truncate(2);
|
||||
|
||||
let functor = functor!(
|
||||
atom!("second"),
|
||||
[
|
||||
atom_as_cell((atom!("a"))),
|
||||
functor((atom!("b")), [fixnum(1), fixnum(2)]),
|
||||
functor((atom!("c")), [fixnum(1), fixnum(2)]),
|
||||
char_as_cell('b')
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(functor.len(), 7);
|
||||
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
let loc = functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(loc, str_loc_as_cell!(2));
|
||||
|
||||
assert_eq!(heap[2], atom_as_cell!(atom!("second"), 4));
|
||||
assert_eq!(heap[3], atom_as_cell!(atom!("a")));
|
||||
assert_eq!(heap[4], str_loc_as_cell!(7));
|
||||
assert_eq!(heap[5], str_loc_as_cell!(10));
|
||||
assert_eq!(heap[6], char_as_cell!('b'));
|
||||
assert_eq!(heap[7], atom_as_cell!(atom!("b"), 2));
|
||||
assert_eq!(heap[8], fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
assert_eq!(heap[9], fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
assert_eq!(heap[10], atom_as_cell!(atom!("c"), 2));
|
||||
assert_eq!(heap[11], fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
assert_eq!(heap[12], fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_functors() {
|
||||
let functor = functor!(
|
||||
atom!("first"),
|
||||
[
|
||||
atom_as_cell((atom!("a"))),
|
||||
functor(
|
||||
(atom!("d")),
|
||||
[
|
||||
fixnum(1),
|
||||
functor(
|
||||
(atom!("b")),
|
||||
[atom_as_cell((atom!("c"))), char_as_cell('c')]
|
||||
)
|
||||
]
|
||||
),
|
||||
functor((atom!("e")), [fixnum(453), fixnum(2)]),
|
||||
char_as_cell('b')
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(functor.len(), 7);
|
||||
|
||||
assert_eq!(functor[0], Cell(atom_as_cell!(atom!("first"), 4)));
|
||||
assert_eq!(functor[1], Cell(atom_as_cell!(atom!("a"))));
|
||||
assert_eq!(functor[2], Cell(str_loc_as_cell!(5)));
|
||||
assert_eq!(functor[3], Cell(str_loc_as_cell!(11)));
|
||||
assert_eq!(functor[4], Cell(char_as_cell!('b')));
|
||||
assert_eq!(
|
||||
functor[5],
|
||||
InnerFunctor(
|
||||
6,
|
||||
vec![
|
||||
Cell(atom_as_cell!(atom!("d"), 2)),
|
||||
Cell(fixnum_as_cell!(Fixnum::build_with(1))),
|
||||
Cell(str_loc_as_cell!(3)),
|
||||
InnerFunctor(
|
||||
3,
|
||||
functor!(atom!("b"), [atom_as_cell((atom!("c"))), char_as_cell('c')])
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
functor[6],
|
||||
InnerFunctor(3, functor!(atom!("e"), [fixnum(453), fixnum(2)]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_functors_in_heap() {
|
||||
let functor = functor!(
|
||||
atom!("first"),
|
||||
[
|
||||
atom_as_cell((atom!("a"))),
|
||||
functor(
|
||||
(atom!("second")),
|
||||
[
|
||||
fixnum(1),
|
||||
functor(
|
||||
(atom!("third")),
|
||||
[atom_as_cell((atom!("b"))), char_as_cell('c')]
|
||||
)
|
||||
]
|
||||
),
|
||||
functor((atom!("fourth")), [fixnum(453), fixnum(2)]),
|
||||
char_as_cell('b')
|
||||
]
|
||||
);
|
||||
|
||||
let mut heap = Heap::new();
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
let loc = functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(loc, str_loc_as_cell!(0));
|
||||
|
||||
assert_eq!(heap.cell_len(), 14);
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("first"), 4));
|
||||
assert_eq!(heap[1], atom_as_cell!(atom!("a")));
|
||||
assert_eq!(heap[2], str_loc_as_cell!(5));
|
||||
assert_eq!(heap[3], str_loc_as_cell!(11));
|
||||
assert_eq!(heap[4], char_as_cell!('b'));
|
||||
assert_eq!(heap[5], atom_as_cell!(atom!("second"), 2));
|
||||
assert_eq!(heap[6], fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
assert_eq!(heap[7], str_loc_as_cell!(8));
|
||||
assert_eq!(heap[8], atom_as_cell!(atom!("third"), 2));
|
||||
assert_eq!(heap[9], atom_as_cell!(atom!("b")));
|
||||
assert_eq!(heap[10], char_as_cell!('c'));
|
||||
assert_eq!(heap[11], atom_as_cell!(atom!("fourth"), 2));
|
||||
assert_eq!(heap[12], fixnum_as_cell!(Fixnum::build_with(453)));
|
||||
assert_eq!(heap[13], fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn functors_with_strings_in_heap() {
|
||||
let functor = functor!(atom!("first"), [string((String::from("a string")))]);
|
||||
|
||||
assert_eq!(functor.len(), 3);
|
||||
|
||||
let mut heap = Heap::new();
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
let loc = functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(loc, str_loc_as_cell!(0));
|
||||
assert_eq!(heap.cell_len(), 5);
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("first"), 1));
|
||||
assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(2)));
|
||||
assert_eq!(
|
||||
heap.slice_to_str(heap_index!(2), "a string".len()),
|
||||
"a string"
|
||||
);
|
||||
assert_eq!(heap[4], empty_list_as_cell!());
|
||||
|
||||
heap.truncate(0);
|
||||
|
||||
let functor = functor!(
|
||||
atom!("second"),
|
||||
[string((String::from("a stuttered\0 string")))]
|
||||
);
|
||||
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(heap.cell_len(), 10);
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("second"), 1));
|
||||
assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(2)));
|
||||
assert_eq!(
|
||||
heap.slice_to_str(heap_index!(2), "a stuttered".len()),
|
||||
"a stuttered"
|
||||
);
|
||||
assert_eq!(heap[4], list_loc_as_cell!(5));
|
||||
assert_eq!(heap[5], char_as_cell!('\u{0}'));
|
||||
assert_eq!(heap[6], pstr_loc_as_cell!(heap_index!(7)));
|
||||
assert_eq!(
|
||||
heap.slice_to_str(heap_index!(7), " string".len()),
|
||||
" string"
|
||||
);
|
||||
assert_eq!(heap[9], empty_list_as_cell!());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn functors_with_lists_in_heap() {
|
||||
let functor = functor!(
|
||||
atom!("first"),
|
||||
[list([fixnum(1), atom_as_cell((atom!("a"))), fixnum(2)])]
|
||||
);
|
||||
|
||||
assert_eq!(functor.len(), 3);
|
||||
|
||||
let mut heap = Heap::new();
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
|
||||
functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(heap.cell_len(), 11);
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("first"), 1));
|
||||
assert_eq!(heap[1], str_loc_as_cell!(2));
|
||||
assert_eq!(heap[2], atom_as_cell!(atom!("."), 2));
|
||||
assert_eq!(heap[3], fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
assert_eq!(heap[4], str_loc_as_cell!(5));
|
||||
assert_eq!(heap[5], atom_as_cell!(atom!("."), 2));
|
||||
assert_eq!(heap[6], atom_as_cell!(atom!("a")));
|
||||
assert_eq!(heap[7], str_loc_as_cell!(8));
|
||||
assert_eq!(heap[8], atom_as_cell!(atom!("."), 2));
|
||||
assert_eq!(heap[9], fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
assert_eq!(heap[10], empty_list_as_cell!());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inlined_atoms() {
|
||||
let atom_table = AtomTable::new();
|
||||
let inlined = AtomTable::build_with(&atom_table, "inline");
|
||||
|
||||
assert!(inlined.is_inlined());
|
||||
assert_eq!(&*inlined.as_str(), "inline");
|
||||
|
||||
let non_inlined = AtomTable::build_with(&atom_table, "longer non-inlined atom");
|
||||
|
||||
assert!(!non_inlined.is_inlined());
|
||||
assert_eq!(&*non_inlined.as_str(), "longer non-inlined atom");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn functors_with_indexing_code_ptr() {
|
||||
let code_ptr = IndexingCodePtr::Internal(0);
|
||||
let functor = functor!(
|
||||
atom!("first"),
|
||||
[
|
||||
string((String::from("a string"))),
|
||||
indexing_code_ptr(code_ptr)
|
||||
]
|
||||
);
|
||||
|
||||
let mut heap = Heap::new();
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
|
||||
functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(heap.cell_len(), 8);
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("first"), 2));
|
||||
assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(3)));
|
||||
assert_eq!(heap[2], str_loc_as_cell!(6));
|
||||
assert_eq!(
|
||||
heap.slice_to_str(heap_index!(3), "a string".len()),
|
||||
"a string"
|
||||
);
|
||||
assert_eq!(heap[5], empty_list_as_cell!());
|
||||
assert_eq!(heap[6], atom_as_cell!(atom!("internal"), 1));
|
||||
assert_eq!(heap[7], fixnum_as_cell!(Fixnum::build_with(0)));
|
||||
|
||||
heap.truncate(0);
|
||||
|
||||
let functor = functor!(
|
||||
atom!("second"),
|
||||
[
|
||||
string((String::from("a string"))),
|
||||
functor(
|
||||
(atom!("third")),
|
||||
[
|
||||
atom_as_cell((atom!("a"))),
|
||||
string((String::from("another string"))),
|
||||
indexing_code_ptr(code_ptr)
|
||||
]
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(heap.cell_len(), 15);
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("second"), 2));
|
||||
assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(3)));
|
||||
assert_eq!(heap[2], str_loc_as_cell!(6));
|
||||
assert_eq!(
|
||||
heap.slice_to_str(heap_index!(3), "a string".len()),
|
||||
"a string"
|
||||
);
|
||||
assert_eq!(heap[5], empty_list_as_cell!());
|
||||
assert_eq!(heap[6], atom_as_cell!(atom!("third"), 3));
|
||||
assert_eq!(heap[7], atom_as_cell!(atom!("a")));
|
||||
assert_eq!(heap[8], pstr_loc_as_cell!(heap_index!(10)));
|
||||
assert_eq!(heap[9], str_loc_as_cell!(13));
|
||||
assert_eq!(
|
||||
heap.slice_to_str(heap_index!(10), "another string".len()),
|
||||
"another string"
|
||||
);
|
||||
assert_eq!(heap[12], empty_list_as_cell!());
|
||||
assert_eq!(heap[13], atom_as_cell!(atom!("internal"), 1));
|
||||
assert_eq!(heap[14], fixnum_as_cell!(Fixnum::build_with(0)));
|
||||
|
||||
let functor = functor!(
|
||||
atom!("fourth"),
|
||||
[
|
||||
string((String::from("a string"))),
|
||||
functor(
|
||||
(atom!("a")),
|
||||
[
|
||||
functor(
|
||||
(atom!("fifth")),
|
||||
[
|
||||
fixnum(5),
|
||||
string((String::from("another string"))),
|
||||
indexing_code_ptr(code_ptr)
|
||||
]
|
||||
),
|
||||
string((String::from("and another")))
|
||||
]
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
heap.truncate(0);
|
||||
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(heap.cell_len(), 21);
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("fourth"), 2));
|
||||
assert_eq!(heap[1], pstr_loc_as_cell!(heap_index!(3)));
|
||||
assert_eq!(heap[2], str_loc_as_cell!(6));
|
||||
assert_eq!(
|
||||
heap.slice_to_str(heap_index!(3), "a string".len()),
|
||||
"a string"
|
||||
);
|
||||
assert_eq!(heap[5], empty_list_as_cell!());
|
||||
assert_eq!(heap[6], atom_as_cell!(atom!("a"), 2));
|
||||
assert_eq!(heap[7], str_loc_as_cell!(9));
|
||||
assert_eq!(heap[8], pstr_loc_as_cell!(heap_index!(18))); // <-- wrong!
|
||||
assert_eq!(heap[9], atom_as_cell!(atom!("fifth"), 3));
|
||||
assert_eq!(heap[10], fixnum_as_cell!(Fixnum::build_with(5)));
|
||||
assert_eq!(heap[11], pstr_loc_as_cell!(heap_index!(13)));
|
||||
assert_eq!(heap[12], str_loc_as_cell!(16));
|
||||
assert_eq!(
|
||||
heap.slice_to_str(heap_index!(13), "another string".len()),
|
||||
"another string"
|
||||
);
|
||||
assert_eq!(heap[15], empty_list_as_cell!());
|
||||
assert_eq!(heap[16], atom_as_cell!(atom!("internal"), 1));
|
||||
assert_eq!(heap[17], fixnum_as_cell!(Fixnum::build_with(0)));
|
||||
assert_eq!(
|
||||
heap.slice_to_str(heap_index!(18), "and another".len()),
|
||||
"and another"
|
||||
);
|
||||
assert_eq!(heap[20], empty_list_as_cell!());
|
||||
|
||||
let constants = indexmap![
|
||||
atom_as_cell!(atom!("a")) => IndexingCodePtr::External(2),
|
||||
atom_as_cell!(atom!("d")) => IndexingCodePtr::External(7),
|
||||
];
|
||||
|
||||
let functor = variadic_functor(
|
||||
atom!("switch_on_constants"),
|
||||
1,
|
||||
constants
|
||||
.iter()
|
||||
.map(|(c, ptr)| functor!(atom!(":"), [cell((*c)), indexing_code_ptr((*ptr))])),
|
||||
);
|
||||
|
||||
heap.truncate(0);
|
||||
|
||||
let mut functor_writer = Heap::functor_writer(functor);
|
||||
functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("switch_on_constants"), 1));
|
||||
assert_eq!(heap[1], list_loc_as_cell!(2));
|
||||
assert_eq!(heap[2], str_loc_as_cell!(6));
|
||||
assert_eq!(heap[3], list_loc_as_cell!(4));
|
||||
assert_eq!(heap[4], str_loc_as_cell!(11));
|
||||
assert_eq!(heap[5], empty_list_as_cell!());
|
||||
assert_eq!(heap[6], atom_as_cell!(atom!(":"), 2));
|
||||
assert_eq!(heap[7], atom_as_cell!(atom!("a")));
|
||||
assert_eq!(heap[8], str_loc_as_cell!(9));
|
||||
assert_eq!(heap[9], atom_as_cell!(atom!("external"), 1));
|
||||
assert_eq!(heap[10], fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
assert_eq!(heap[11], atom_as_cell!(atom!(":"), 2));
|
||||
assert_eq!(heap[12], atom_as_cell!(atom!("d")));
|
||||
assert_eq!(heap[13], str_loc_as_cell!(14));
|
||||
assert_eq!(heap[14], atom_as_cell!(atom!("external"), 1));
|
||||
assert_eq!(heap[15], fixnum_as_cell!(Fixnum::build_with(7)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undefined_procedure_functor() {
|
||||
// existence_error
|
||||
let culprit = functor!(atom!("/"), [atom_as_cell((atom!("a"))), fixnum(1)]);
|
||||
|
||||
let stub = functor!(
|
||||
atom!("existence_error"),
|
||||
[
|
||||
atom_as_cell((atom!("procedure"))),
|
||||
functor((culprit.clone()))
|
||||
]
|
||||
);
|
||||
|
||||
println!("{stub:?}");
|
||||
|
||||
// now the error form
|
||||
let lineless_error_form = functor!(atom!("error"), [functor(stub), functor(culprit)]);
|
||||
|
||||
println!("{lineless_error_form:?}");
|
||||
|
||||
let mut heap = Heap::new();
|
||||
let mut functor_writer = Heap::functor_writer(lineless_error_form);
|
||||
|
||||
functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("error"), 2));
|
||||
assert_eq!(heap[1], str_loc_as_cell!(3));
|
||||
assert_eq!(heap[2], str_loc_as_cell!(9));
|
||||
assert_eq!(heap[3], atom_as_cell!(atom!("existence_error"), 2));
|
||||
assert_eq!(heap[4], atom_as_cell!(atom!("procedure")));
|
||||
assert_eq!(heap[5], str_loc_as_cell!(6)); // is str_loc_as_cell!(3)
|
||||
assert_eq!(heap[6], atom_as_cell!(atom!("/"), 2));
|
||||
assert_eq!(heap[7], atom_as_cell!(atom!("a")));
|
||||
assert_eq!(heap[8], fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
assert_eq!(heap[9], atom_as_cell!(atom!("/"), 2));
|
||||
assert_eq!(heap[10], atom_as_cell!(atom!("a")));
|
||||
assert_eq!(heap[11], fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn argless_functor() {
|
||||
let name = functor!(atom!("[]"));
|
||||
|
||||
assert_eq!(name.len(), 1);
|
||||
|
||||
let mut heap = Heap::new();
|
||||
let mut functor_writer = Heap::functor_writer(name);
|
||||
let loc = functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(loc, heap_loc_as_cell!(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn predefined_subfunctors() {
|
||||
let stub = functor!(atom!("sub"), [atom_as_cell((atom!("[]")))]);
|
||||
let name = functor!(atom!("super"), [functor(stub)]);
|
||||
|
||||
let mut heap = Heap::new();
|
||||
let mut functor_writer = Heap::functor_writer(name);
|
||||
|
||||
functor_writer(&mut heap).unwrap();
|
||||
|
||||
assert_eq!(heap.cell_len(), 4);
|
||||
|
||||
assert_eq!(heap[0], atom_as_cell!(atom!("super"), 1));
|
||||
assert_eq!(heap[1], str_loc_as_cell!(2));
|
||||
assert_eq!(heap[2], atom_as_cell!(atom!("sub"), 1));
|
||||
assert_eq!(heap[3], empty_list_as_cell!());
|
||||
}
|
||||
}
|
||||
1263
src/heap_iter.rs
1263
src/heap_iter.rs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
use std::io::BufRead;
|
||||
use bytes::{buf::Reader, Bytes};
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use warp::http;
|
||||
@@ -19,5 +19,5 @@ pub struct HttpRequestData {
|
||||
pub headers: http::HeaderMap,
|
||||
pub path: String,
|
||||
pub query: String,
|
||||
pub body: Box<dyn BufRead + Send>,
|
||||
pub body: Reader<Bytes>,
|
||||
}
|
||||
|
||||
138
src/indexing.rs
138
src/indexing.rs
@@ -3,6 +3,7 @@ use crate::parser::ast::*;
|
||||
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::types::HeapCellValue;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexMap;
|
||||
@@ -112,15 +113,15 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
|
||||
match constant_key {
|
||||
Some(OptArgIndexKey::Literal(_, _, constant, _)) => {
|
||||
constants.insert(*constant, constant_ptr);
|
||||
constants.insert(HeapCellValue::from(*constant), constant_ptr);
|
||||
}
|
||||
_ if constant_ptr.is_external() => {
|
||||
// this must be a defunct clause, because it's been deleted
|
||||
// from the skeleton.
|
||||
debug_assert!(constant_key.is_none());
|
||||
}
|
||||
_ => {
|
||||
if let IndexingCodePtr::DynamicExternal(_) = constant_ptr {
|
||||
// this must be a defunct clause, because it's been deleted
|
||||
// from the skeleton.
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +145,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
fn add_static_indexed_choice_for_constant(
|
||||
&mut self,
|
||||
external: usize,
|
||||
constant: Literal,
|
||||
constant: HeapCellValue,
|
||||
index: usize,
|
||||
) {
|
||||
let third_level_index = if self.append_or_prepend.is_append() {
|
||||
@@ -181,7 +182,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
fn add_dynamic_indexed_choice_for_constant(
|
||||
&mut self,
|
||||
external: usize,
|
||||
constant: Literal,
|
||||
constant: HeapCellValue,
|
||||
index: usize,
|
||||
) {
|
||||
let third_level_index = if self.append_or_prepend.is_append() {
|
||||
@@ -235,8 +236,8 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
|
||||
fn index_overlapping_constant(
|
||||
&mut self,
|
||||
orig_constant: Literal,
|
||||
overlapping_constant: Literal,
|
||||
orig_constant: HeapCellValue,
|
||||
overlapping_constant: HeapCellValue,
|
||||
index: usize,
|
||||
) {
|
||||
loop {
|
||||
@@ -316,7 +317,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn index_constant(&mut self, constant: Literal, index: usize) {
|
||||
fn index_constant(&mut self, constant: HeapCellValue, index: usize) {
|
||||
loop {
|
||||
let indexing_code_len = self.indexing_code.len();
|
||||
|
||||
@@ -633,12 +634,15 @@ pub(crate) fn merge_clause_index(
|
||||
match &opt_arg_index_key {
|
||||
OptArgIndexKey::Literal(_, index_loc, constant, ref overlapping_constants) => {
|
||||
let offset = new_clause_loc - index_loc + 1;
|
||||
merging_ptr.index_constant(*constant, offset);
|
||||
merging_ptr.index_constant(HeapCellValue::from(*constant), offset);
|
||||
|
||||
for overlapping_constant in overlapping_constants {
|
||||
if let Some(overlapping_constant) = overlapping_constants {
|
||||
merging_ptr.offset = 0;
|
||||
|
||||
merging_ptr.index_overlapping_constant(*constant, *overlapping_constant, offset);
|
||||
merging_ptr.index_overlapping_constant(
|
||||
HeapCellValue::from(*constant),
|
||||
HeapCellValue::from(*overlapping_constant),
|
||||
offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
OptArgIndexKey::Structure(_, index_loc, name, arity) => {
|
||||
@@ -664,7 +668,7 @@ pub(crate) fn merge_clause_index(
|
||||
|
||||
pub(crate) fn remove_constant_indices(
|
||||
constant: Literal,
|
||||
overlapping_constants: &[Literal],
|
||||
overlapping_constants: Option<Literal>,
|
||||
indexing_code: &mut [IndexingLine],
|
||||
offset: usize,
|
||||
) {
|
||||
@@ -693,7 +697,7 @@ pub(crate) fn remove_constant_indices(
|
||||
|
||||
let mut constants_index = 0;
|
||||
|
||||
for constant in iter {
|
||||
for constant in iter.map(|l| HeapCellValue::from(*l)) {
|
||||
loop {
|
||||
match &mut indexing_code[index] {
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
|
||||
@@ -701,13 +705,11 @@ pub(crate) fn remove_constant_indices(
|
||||
)) => {
|
||||
constants_index = index;
|
||||
|
||||
let constant = *constant;
|
||||
|
||||
match constants.get(&constant).cloned() {
|
||||
Some(IndexingCodePtr::DynamicExternal(_))
|
||||
| Some(IndexingCodePtr::External(_))
|
||||
| Some(IndexingCodePtr::Fail) => {
|
||||
constants.remove(&constant);
|
||||
constants.swap_remove(&constant);
|
||||
break;
|
||||
}
|
||||
Some(IndexingCodePtr::Internal(o)) => {
|
||||
@@ -740,7 +742,7 @@ pub(crate) fn remove_constant_indices(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
|
||||
ref mut constants,
|
||||
)) => {
|
||||
constants.insert(*constant, ext);
|
||||
constants.insert(constant, ext);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -773,7 +775,7 @@ pub(crate) fn remove_constant_indices(
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
|
||||
ref mut constants,
|
||||
)) => {
|
||||
constants.insert(*constant, ext);
|
||||
constants.insert(constant, ext);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -846,7 +848,7 @@ pub(crate) fn remove_structure_index(
|
||||
match structures.get(&(name, arity)).cloned() {
|
||||
Some(IndexingCodePtr::DynamicExternal(_))
|
||||
| Some(IndexingCodePtr::External(_)) => {
|
||||
structures.remove(&(name, arity));
|
||||
structures.swap_remove(&(name, arity));
|
||||
break;
|
||||
}
|
||||
Some(IndexingCodePtr::Internal(o)) => {
|
||||
@@ -1033,7 +1035,7 @@ pub(crate) fn remove_index(
|
||||
) {
|
||||
match opt_arg_index_key {
|
||||
OptArgIndexKey::Literal(_, _, constant, ref overlapping_constants) => {
|
||||
remove_constant_indices(*constant, overlapping_constants, indexing_code, clause_loc);
|
||||
remove_constant_indices(*constant, *overlapping_constants, indexing_code, clause_loc);
|
||||
}
|
||||
OptArgIndexKey::Structure(_, _, name, arity) => {
|
||||
remove_structure_index(*name, *arity, indexing_code, clause_loc);
|
||||
@@ -1095,62 +1097,26 @@ fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn constant_key_alternatives(
|
||||
constant: Literal,
|
||||
atom_tbl: &AtomTable,
|
||||
// arena: &mut Arena,
|
||||
) -> Vec<Literal> {
|
||||
let mut constants = vec![];
|
||||
pub(crate) fn constant_key_alternatives(constant: Literal) -> Option<Literal> {
|
||||
let n = match &constant {
|
||||
Literal::Rational(n) if n.denominator().is_one() => n.numerator(),
|
||||
Literal::Integer(n) => n,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
match constant {
|
||||
Literal::Atom(ref name) => {
|
||||
if let Some(c) = name.as_char() {
|
||||
constants.push(Literal::Char(c));
|
||||
}
|
||||
}
|
||||
Literal::Char(c) => {
|
||||
let atom = AtomTable::build_with(atom_tbl, &c.to_string());
|
||||
constants.push(Literal::Atom(atom));
|
||||
}
|
||||
/*
|
||||
Literal::Fixnum(ref n) => {
|
||||
constants.push(Literal::Integer(arena_alloc!(n, arena))); //Rc::new(Integer::from(*n))));
|
||||
|
||||
/*
|
||||
if *n >= 0 {
|
||||
if let Ok(n) = usize::try_from(*n) {
|
||||
constants.push(Literal::Usize(n));
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
*/
|
||||
Literal::Integer(ref n) => {
|
||||
let result = (&**n).try_into();
|
||||
if let Ok(value) = result {
|
||||
Fixnum::build_with_checked(value)
|
||||
.map(|n| {
|
||||
constants.push(Literal::Fixnum(n));
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
constants
|
||||
Fixnum::build_with_checked(n).map(Literal::Fixnum).ok()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct StaticCodeIndices {
|
||||
constants: IndexMap<Literal, VecDeque<IndexedChoiceInstruction>, FxBuildHasher>,
|
||||
constants: IndexMap<HeapCellValue, VecDeque<IndexedChoiceInstruction>, FxBuildHasher>,
|
||||
lists: VecDeque<IndexedChoiceInstruction>,
|
||||
structures: IndexMap<(Atom, usize), VecDeque<IndexedChoiceInstruction>, FxBuildHasher>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DynamicCodeIndices {
|
||||
constants: IndexMap<Literal, VecDeque<usize>, FxBuildHasher>,
|
||||
constants: IndexMap<HeapCellValue, VecDeque<usize>, FxBuildHasher>,
|
||||
lists: VecDeque<usize>,
|
||||
structures: IndexMap<(Atom, usize), VecDeque<usize>, FxBuildHasher>,
|
||||
}
|
||||
@@ -1162,7 +1128,7 @@ pub(crate) trait Indexer {
|
||||
|
||||
fn constants(
|
||||
&mut self,
|
||||
) -> &mut IndexMap<Literal, VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>;
|
||||
) -> &mut IndexMap<HeapCellValue, VecDeque<Self::ThirdLevelIndex>, FxBuildHasher>;
|
||||
fn lists(&mut self) -> &mut VecDeque<Self::ThirdLevelIndex>;
|
||||
fn structures(
|
||||
&mut self,
|
||||
@@ -1210,7 +1176,7 @@ impl Indexer for StaticCodeIndices {
|
||||
#[inline]
|
||||
fn constants(
|
||||
&mut self,
|
||||
) -> &mut IndexMap<Literal, VecDeque<IndexedChoiceInstruction>, FxBuildHasher> {
|
||||
) -> &mut IndexMap<HeapCellValue, VecDeque<IndexedChoiceInstruction>, FxBuildHasher> {
|
||||
&mut self.constants
|
||||
}
|
||||
|
||||
@@ -1334,7 +1300,7 @@ impl Indexer for DynamicCodeIndices {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn constants(&mut self) -> &mut IndexMap<Literal, VecDeque<usize>, FxBuildHasher> {
|
||||
fn constants(&mut self) -> &mut IndexMap<HeapCellValue, VecDeque<usize>, FxBuildHasher> {
|
||||
&mut self.constants
|
||||
}
|
||||
|
||||
@@ -1453,14 +1419,13 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
self.indices.lists().push_back(index);
|
||||
}
|
||||
|
||||
fn index_constant(
|
||||
&mut self,
|
||||
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_default();
|
||||
fn index_constant(&mut self, constant: Literal, index: usize) -> Option<Literal> {
|
||||
let overlapping_constant_opt = constant_key_alternatives(constant);
|
||||
let code = self
|
||||
.indices
|
||||
.constants()
|
||||
.entry(HeapCellValue::from(constant))
|
||||
.or_default();
|
||||
|
||||
let is_initial_index = code.is_empty();
|
||||
code.push_back(I::compute_index(
|
||||
@@ -1469,8 +1434,8 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
self.non_counted_bt,
|
||||
));
|
||||
|
||||
for constant in &overlapping_constants {
|
||||
let code = self.indices.constants().entry(*constant).or_default();
|
||||
if let Some(constant) = overlapping_constant_opt.map(HeapCellValue::from) {
|
||||
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, self.non_counted_bt);
|
||||
@@ -1478,7 +1443,7 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
code.push_back(index);
|
||||
}
|
||||
|
||||
overlapping_constants
|
||||
overlapping_constant_opt
|
||||
}
|
||||
|
||||
fn index_structure(&mut self, name: Atom, arity: usize, index: usize) -> usize {
|
||||
@@ -1500,14 +1465,13 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
optimal_arg: &Term,
|
||||
index: usize,
|
||||
clause_index_info: &mut ClauseIndexInfo,
|
||||
atom_tbl: &AtomTable,
|
||||
) {
|
||||
match optimal_arg {
|
||||
&Term::Clause(_, atom!("."), ref terms) if terms.len() == 2 => {
|
||||
clause_index_info.opt_arg_index_key = OptArgIndexKey::List(self.optimal_index, 0);
|
||||
self.index_list(index);
|
||||
}
|
||||
&Term::Cons(..) | &Term::Literal(_, Literal::String(_)) | &Term::PartialString(..) => {
|
||||
&Term::Cons(..) | &Term::PartialString(..) | &Term::CompleteString(..) => {
|
||||
clause_index_info.opt_arg_index_key = OptArgIndexKey::List(self.optimal_index, 0);
|
||||
self.index_list(index);
|
||||
}
|
||||
@@ -1518,7 +1482,7 @@ impl<I: Indexer> CodeOffsets<I> {
|
||||
self.index_structure(name, terms.len(), index);
|
||||
}
|
||||
&Term::Literal(_, constant) => {
|
||||
let overlapping_constants = self.index_constant(atom_tbl, constant, index);
|
||||
let overlapping_constants = self.index_constant(constant, index);
|
||||
|
||||
clause_index_info.opt_arg_index_key =
|
||||
OptArgIndexKey::Literal(self.optimal_index, 0, constant, overlapping_constants);
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::parser::ast::*;
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::iter::*;
|
||||
use std::rc::Rc;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
@@ -15,27 +16,11 @@ pub(crate) enum TermRef<'a> {
|
||||
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||
Literal(Level, &'a Cell<RegType>, &'a Literal),
|
||||
Clause(Level, &'a Cell<RegType>, Atom, &'a Vec<Term>),
|
||||
PartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
|
||||
CompleteString(Level, &'a Cell<RegType>, Atom),
|
||||
PartialString(Level, &'a Cell<RegType>, Rc<String>, &'a Box<Term>),
|
||||
CompleteString(Level, &'a Cell<RegType>, Rc<String>),
|
||||
Var(Level, &'a Cell<VarReg>, VarPtr),
|
||||
}
|
||||
|
||||
/*
|
||||
impl<'a> TermRef<'a> {
|
||||
pub(crate) fn level(&self) -> Level {
|
||||
match self {
|
||||
TermRef::AnonVar(lvl) |
|
||||
TermRef::Cons(lvl, ..) |
|
||||
TermRef::Literal(lvl, ..) |
|
||||
TermRef::Var(lvl, ..) |
|
||||
TermRef::Clause(lvl, ..) |
|
||||
TermRef::CompleteString(lvl, ..) |
|
||||
TermRef::PartialString(lvl, ..) => *lvl,
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum TermIterState<'a> {
|
||||
@@ -44,9 +29,9 @@ pub(crate) enum TermIterState<'a> {
|
||||
Literal(Level, &'a Cell<RegType>, &'a Literal),
|
||||
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||
FinalCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||
InitialPartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
|
||||
FinalPartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
|
||||
CompleteString(Level, &'a Cell<RegType>, Atom),
|
||||
InitialPartialString(Level, &'a Cell<RegType>, Rc<String>, &'a Box<Term>),
|
||||
FinalPartialString(Level, &'a Cell<RegType>, Rc<String>, &'a Box<Term>),
|
||||
CompleteString(Level, &'a Cell<RegType>, Rc<String>),
|
||||
Var(Level, &'a Cell<VarReg>, VarPtr),
|
||||
}
|
||||
|
||||
@@ -62,9 +47,11 @@ impl<'a> TermIterState<'a> {
|
||||
}
|
||||
Term::Literal(cell, constant) => TermIterState::Literal(lvl, cell, constant),
|
||||
Term::PartialString(cell, string_buf, tail) => {
|
||||
TermIterState::InitialPartialString(lvl, cell, string_buf, tail)
|
||||
TermIterState::InitialPartialString(lvl, cell, string_buf.clone(), tail)
|
||||
}
|
||||
Term::CompleteString(cell, string) => {
|
||||
TermIterState::CompleteString(lvl, cell, string.clone())
|
||||
}
|
||||
Term::CompleteString(cell, atom) => TermIterState::CompleteString(lvl, cell, *atom),
|
||||
Term::Var(cell, var_ptr) => TermIterState::Var(lvl, cell, var_ptr.clone()),
|
||||
}
|
||||
}
|
||||
@@ -182,11 +169,11 @@ impl<'a> Iterator for QueryIterator<'a> {
|
||||
.push(TermIterState::FinalPartialString(lvl, cell, string, tail));
|
||||
self.push_subterm(lvl.child_level(), tail);
|
||||
}
|
||||
TermIterState::FinalPartialString(lvl, cell, atom, tail) => {
|
||||
return Some(TermRef::PartialString(lvl, cell, atom, tail));
|
||||
TermIterState::FinalPartialString(lvl, cell, string, tail) => {
|
||||
return Some(TermRef::PartialString(lvl, cell, string, tail));
|
||||
}
|
||||
TermIterState::CompleteString(lvl, cell, atom) => {
|
||||
return Some(TermRef::CompleteString(lvl, cell, atom));
|
||||
TermIterState::CompleteString(lvl, cell, string) => {
|
||||
return Some(TermRef::CompleteString(lvl, cell, string));
|
||||
}
|
||||
TermIterState::FinalCons(lvl, cell, head, tail) => {
|
||||
return Some(TermRef::Cons(lvl, cell, head, tail));
|
||||
@@ -242,16 +229,20 @@ impl<'a> FactIterator<'a> {
|
||||
head.as_ref(),
|
||||
tail.as_ref(),
|
||||
)],
|
||||
Term::PartialString(cell, string_buf, tail) => {
|
||||
Term::PartialString(cell, string, tail) => {
|
||||
vec![TermIterState::InitialPartialString(
|
||||
Level::Root,
|
||||
cell,
|
||||
string_buf,
|
||||
string.clone(),
|
||||
tail,
|
||||
)]
|
||||
}
|
||||
Term::CompleteString(cell, atom) => {
|
||||
vec![TermIterState::CompleteString(Level::Root, cell, *atom)]
|
||||
Term::CompleteString(cell, string) => {
|
||||
vec![TermIterState::CompleteString(
|
||||
Level::Root,
|
||||
cell,
|
||||
string.clone(),
|
||||
)]
|
||||
}
|
||||
Term::Literal(cell, constant) => {
|
||||
vec![TermIterState::Literal(Level::Root, cell, constant)]
|
||||
@@ -314,14 +305,14 @@ impl<'a> Iterator for FactIterator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn post_order_iter(term: &'_ Term) -> QueryIterator {
|
||||
pub(crate) fn post_order_iter(term: &Term) -> QueryIterator<'_> {
|
||||
QueryIterator::from_term(term)
|
||||
}
|
||||
|
||||
pub(crate) fn breadth_first_iter(
|
||||
term: &'_ Term,
|
||||
term: &Term,
|
||||
iterable_root: RootIterationPolicy,
|
||||
) -> FactIterator {
|
||||
) -> FactIterator<'_> {
|
||||
FactIterator::new(term, iterable_root)
|
||||
}
|
||||
|
||||
@@ -336,7 +327,7 @@ pub(crate) enum ClauseItem<'a> {
|
||||
FirstBranch(usize),
|
||||
NextBranch,
|
||||
BranchEnd(usize),
|
||||
Chunk(&'a VecDeque<QueryTerm>),
|
||||
Chunk { terms: &'a VecDeque<QueryTerm> },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -345,7 +336,7 @@ pub(crate) struct ClauseIterator<'a> {
|
||||
remaining_chunks_on_stack: usize,
|
||||
}
|
||||
|
||||
fn state_from_chunked_terms(chunk_vec: &'_ VecDeque<ChunkedTerms>) -> ClauseIteratorState {
|
||||
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);
|
||||
@@ -412,8 +403,8 @@ impl<'a> Iterator for ClauseIterator<'a> {
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingBranches(branches, 0));
|
||||
}
|
||||
ChunkedTerms::Chunk(chunk) => {
|
||||
return Some(ClauseItem::Chunk(chunk));
|
||||
ChunkedTerms::Chunk { ref terms } => {
|
||||
return Some(ClauseItem::Chunk { terms });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
src/lib.rs
73
src/lib.rs
@@ -1,60 +1,83 @@
|
||||
//! A free software ISO Prolog system.
|
||||
#![recursion_limit = "4112"]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate maplit;
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub(crate) mod macros;
|
||||
#[macro_use]
|
||||
pub mod atom_table;
|
||||
pub(crate) mod atom_table;
|
||||
#[macro_use]
|
||||
pub mod arena;
|
||||
pub(crate) mod arena;
|
||||
pub(crate) mod offset_table;
|
||||
#[macro_use]
|
||||
pub mod parser;
|
||||
pub(crate) mod parser;
|
||||
#[macro_use]
|
||||
pub(crate) mod functor_macro;
|
||||
mod allocator;
|
||||
mod arithmetic;
|
||||
pub mod codegen;
|
||||
pub(crate) mod codegen;
|
||||
mod debray_allocator;
|
||||
#[cfg(feature = "ffi")]
|
||||
mod ffi;
|
||||
mod forms;
|
||||
mod heap_iter;
|
||||
pub mod heap_print;
|
||||
pub(crate) mod heap_print;
|
||||
#[cfg(feature = "http")]
|
||||
mod http;
|
||||
mod indexing;
|
||||
mod variable_records;
|
||||
#[macro_use]
|
||||
pub mod instructions {
|
||||
pub(crate) mod instructions {
|
||||
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
|
||||
}
|
||||
mod iterators;
|
||||
pub mod machine;
|
||||
pub(crate) mod machine;
|
||||
mod raw_block;
|
||||
pub mod read;
|
||||
pub(crate) mod read;
|
||||
#[cfg(feature = "repl")]
|
||||
mod repl_helper;
|
||||
mod targets;
|
||||
pub mod types;
|
||||
pub(crate) mod types;
|
||||
|
||||
use instructions::instr;
|
||||
|
||||
mod rcu;
|
||||
// Re-exports
|
||||
pub use machine::config::*;
|
||||
pub use machine::lib_machine::*;
|
||||
pub use machine::Machine;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
pub mod wasm;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[wasm_bindgen]
|
||||
pub fn eval_code(s: &str) -> String {
|
||||
use machine::mock_wam::*;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
/// The entry point for the Scryer Prolog CLI.
|
||||
pub fn run_binary() -> std::process::ExitCode {
|
||||
use crate::atom_table::Atom;
|
||||
use crate::machine::INTERRUPT;
|
||||
|
||||
console_error_panic_hook::set_once();
|
||||
#[cfg(feature = "repl")]
|
||||
ctrlc::set_handler(move || {
|
||||
INTERRUPT.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut wam = Machine::with_test_streams();
|
||||
let bytes = wam.test_load_string(s);
|
||||
String::from_utf8_lossy(&bytes).to_string()
|
||||
#[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 = MachineBuilder::default()
|
||||
.with_streams(StreamConfig::stdio())
|
||||
.build();
|
||||
wam.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 0))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ Assocs are Key-Value associations implemented as a balanced binary tree
|
||||
Authors: R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
|
||||
*/
|
||||
|
||||
:- meta_predicate map_assoc(1, ?).
|
||||
:- meta_predicate map_assoc(2, ?, ?).
|
||||
:- meta_predicate(map_assoc(1, ?)).
|
||||
:- meta_predicate(map_assoc(2, ?, ?)).
|
||||
|
||||
%% empty_assoc(?Assoc) is semidet.
|
||||
%
|
||||
@@ -299,7 +299,7 @@ map_assoc_(t(Key,Val,B,L0,R0), Pred, t(Key,Ans,B,L1,R1)) :-
|
||||
map_assoc_(R0, Pred, R1).
|
||||
|
||||
|
||||
%% max_assoc(+Assoc, -Key, -Value) is semidet.
|
||||
%% max_assoc(+Assoc, -Key, -Value) is semidet.
|
||||
%
|
||||
% True if Key-Value is in Assoc and Key is the largest key.
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ call(_, _, _, _, _, _, _, _).
|
||||
call(_, _, _, _, _, _, _, _, _).
|
||||
|
||||
|
||||
:- meta_predicate catch(0, ?, 0).
|
||||
:- meta_predicate(catch(0, ?, 0)).
|
||||
|
||||
% flags.
|
||||
|
||||
@@ -125,12 +125,12 @@ call(_, _, _, _, _, _, _, _, _).
|
||||
%
|
||||
% The flags that Scryer Prolog support are:
|
||||
%
|
||||
% * `max_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 1023. Read only.
|
||||
% * `max_arity`: The max arity a predicate can have in Prolog. On Scryer is set to 255. Read only.
|
||||
% * `bounded`: `true` if integer arithmethic is bounded between some min/max values. On Scryer is always set
|
||||
% to `false` since it supports unbounded integer arithmethic. Read only.
|
||||
% * `integer_rounding_function`: Describes the rounding donde by `//` and `rem` functions. On Scryer is
|
||||
% always set to `toward_zero`. Read only
|
||||
% * `double_quotes`: Determines how double quoted strings are red by Prolog. Scryer uses `chars` by default
|
||||
% * `double_quotes`: Determines how double quoted strings are read by Prolog. Scryer uses `chars` by default
|
||||
% which is a list of one-character atoms. Other values are codes (list of integers representing characters),
|
||||
% and atom which creates a whole atom for the string value. Read and write.
|
||||
% * `max_integer`: Maximum integer supported by the system. As Scryer Prolog has unbounded integer arithmethic,
|
||||
@@ -145,8 +145,8 @@ call(_, _, _, _, _, _, _, _, _).
|
||||
% `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).
|
||||
current_prolog_flag(Flag, Value) :- Flag == max_arity, !, Value = 255.
|
||||
current_prolog_flag(max_arity, 255).
|
||||
current_prolog_flag(Flag, Value) :- Flag == bounded, !, Value = false.
|
||||
current_prolog_flag(bounded, false).
|
||||
current_prolog_flag(Flag, Value) :- Flag == integer_rounding_function, !, Value == toward_zero.
|
||||
@@ -248,7 +248,7 @@ flag_domain_error(Flag, Value) :-
|
||||
fail :- '$fail'.
|
||||
|
||||
|
||||
:- meta_predicate \+(0).
|
||||
:- meta_predicate(\+(0)).
|
||||
|
||||
%% \+(Goal)
|
||||
%
|
||||
@@ -263,7 +263,7 @@ X \= X :- !, '$fail'.
|
||||
_ \= _.
|
||||
|
||||
|
||||
:- meta_predicate once(0).
|
||||
:- meta_predicate(once(0)).
|
||||
|
||||
%% once(Goal)
|
||||
%
|
||||
@@ -278,11 +278,11 @@ repeat.
|
||||
repeat :- repeat.
|
||||
|
||||
|
||||
:- meta_predicate ','(0,0).
|
||||
:- meta_predicate(','(0,0)).
|
||||
|
||||
:- meta_predicate ;(0,0).
|
||||
:- meta_predicate(;(0,0)).
|
||||
|
||||
:- meta_predicate ->(0,0).
|
||||
:- meta_predicate(->(0,0)).
|
||||
|
||||
%% ->(G1, G2)
|
||||
%
|
||||
@@ -364,7 +364,7 @@ cont_list_goal(Conts, '$call'(builtins:dispatch_call_list(Conts))).
|
||||
|
||||
dispatch_prep(Gs, B, Conts) :-
|
||||
( callable(Gs) ->
|
||||
strip_module(Gs, M, Gs0),
|
||||
loader:strip_module(Gs, M, Gs0),
|
||||
( nonvar(Gs0),
|
||||
dispatch_prep_(Gs0, B, Conts) ->
|
||||
true
|
||||
@@ -553,7 +553,7 @@ get_args([Arg|Args], Func, I0, N) :-
|
||||
get_args(Args, Func, I1, N).
|
||||
|
||||
|
||||
:- meta_predicate parse_options_list(?, 2, ?, ?, ?).
|
||||
:- meta_predicate(parse_options_list(?, 2, ?, ?, ?)).
|
||||
|
||||
parse_options_list(Options, Selector, DefaultPairs, OptionValues, Stub) :-
|
||||
'$skip_max_list'(_, _, Options, Tail),
|
||||
@@ -661,7 +661,7 @@ write_term(Term, Options) :-
|
||||
|
||||
%% write_term(+Stream, +Term, +Options).
|
||||
%
|
||||
% Write Term to the stream Stream according to some output syntax options. The options avaibale are:
|
||||
% Write Term to the stream Stream according to some output syntax options. The options available are:
|
||||
%
|
||||
% * `ignore_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false`
|
||||
% (default), operators do not use that generic term representation.
|
||||
@@ -766,6 +766,9 @@ read(Term) :-
|
||||
read_term(Stream, Term, []).
|
||||
% read(Stream, Term).
|
||||
|
||||
%% read(+Stream, -Term).
|
||||
%
|
||||
% Same as read_term/3 with all default options.
|
||||
read(Stream, Term) :-
|
||||
read_term(Stream, Term, []).
|
||||
|
||||
@@ -813,7 +816,7 @@ catch(G,C,R) :-
|
||||
'$get_current_block'(Bb),
|
||||
catch(G,C,R,Bb).
|
||||
|
||||
:- meta_predicate catch(0, ?, 0, ?).
|
||||
:- meta_predicate(catch(0, ?, 0, ?)).
|
||||
|
||||
:- non_counted_backtracking catch/4.
|
||||
|
||||
@@ -881,7 +884,7 @@ throw(Ball) :-
|
||||
truncate_lh_to(LhLength) :- '$truncate_lh_to'(LhLength).
|
||||
|
||||
|
||||
:- meta_predicate findall(?, 0, ?).
|
||||
:- meta_predicate(findall(?, 0, ?)).
|
||||
|
||||
:- non_counted_backtracking findall_cleanup/2.
|
||||
|
||||
@@ -924,7 +927,7 @@ findall(Template, Goal, Solutions) :-
|
||||
'$get_lh_from_offset_diff'(LhOffset, Solutions0, Solutions1).
|
||||
|
||||
|
||||
:- meta_predicate findall(?, 0, ?, ?).
|
||||
:- meta_predicate(findall(?, 0, ?, ?)).
|
||||
|
||||
:- non_counted_backtracking findall/4.
|
||||
|
||||
@@ -1042,7 +1045,7 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses)
|
||||
).
|
||||
|
||||
|
||||
:- meta_predicate bagof(?, 0, ?).
|
||||
:- meta_predicate(bagof(?, 0, ?)).
|
||||
|
||||
:- non_counted_backtracking bagof/3.
|
||||
|
||||
@@ -1088,7 +1091,7 @@ iterate_variants_and_sort([_|GroupSolutions], Ws, Solution) :-
|
||||
iterate_variants_and_sort(GroupSolutions, Ws, Solution).
|
||||
|
||||
|
||||
:- meta_predicate setof(?, 0, ?).
|
||||
:- meta_predicate(setof(?, 0, ?)).
|
||||
|
||||
:- non_counted_backtracking setof/3.
|
||||
|
||||
@@ -1165,14 +1168,14 @@ clause(H, B) :-
|
||||
).
|
||||
|
||||
|
||||
:- meta_predicate asserta(:).
|
||||
:- meta_predicate(asserta(:)).
|
||||
|
||||
%% asserta(Clause).
|
||||
%
|
||||
% Asserts (inserts) a new clause (rule or fact) into the current module.
|
||||
% The clause will be inserted at the beginning of the module.
|
||||
asserta(Clause0) :-
|
||||
loader:strip_subst_module(Clause0, user, Module, Clause),
|
||||
loader:strip_module(Clause0, Module, Clause),
|
||||
asserta_(Module, Clause).
|
||||
|
||||
asserta_(Module, (Head :- Body)) :-
|
||||
@@ -1181,14 +1184,14 @@ asserta_(Module, (Head :- Body)) :-
|
||||
asserta_(Module, Fact) :-
|
||||
'$asserta'(Module, Fact, true).
|
||||
|
||||
:- meta_predicate assertz(:).
|
||||
:- meta_predicate(assertz(:)).
|
||||
|
||||
%% assertz(Clause).
|
||||
%
|
||||
% Asserts (inserts) a new clause (rule or fact) into the current module.
|
||||
% The clase will be inserted at the end of the module.
|
||||
assertz(Clause0) :-
|
||||
loader:strip_subst_module(Clause0, user, Module, Clause),
|
||||
loader:strip_module(Clause0, Module, Clause),
|
||||
assertz_(Module, Clause).
|
||||
|
||||
assertz_(Module, (Head :- Body)) :-
|
||||
@@ -1198,7 +1201,7 @@ assertz_(Module, Fact) :-
|
||||
'$assertz'(Module, Fact, true).
|
||||
|
||||
|
||||
:- meta_predicate retract(:).
|
||||
:- meta_predicate(retract(:)).
|
||||
|
||||
%% retract(Clause)
|
||||
%
|
||||
@@ -1207,16 +1210,10 @@ assertz_(Module, Fact) :-
|
||||
retract(Clause0) :-
|
||||
loader:strip_module(Clause0, Module, Clause),
|
||||
( Clause \= (_ :- _) ->
|
||||
loader:strip_module(Clause, Module, Head),
|
||||
( var(Module) -> Module = user
|
||||
; true
|
||||
),
|
||||
loader:strip_subst_module(Clause, Module, InnerModule, Head),
|
||||
Body = true,
|
||||
retract_module_clause(Head, Body, Module)
|
||||
retract_module_clause(Head, Body, InnerModule)
|
||||
; Clause = (Head :- Body) ->
|
||||
( var(Module) -> Module = user
|
||||
; true
|
||||
),
|
||||
retract_module_clause(Head, Body, Module)
|
||||
).
|
||||
|
||||
@@ -1281,7 +1278,7 @@ retract_module_clause(Head, Body, Module) :-
|
||||
; throw(error(type_error(callable, Head), retract/1))
|
||||
).
|
||||
|
||||
:- meta_predicate retractall(:).
|
||||
:- meta_predicate(retractall(:)).
|
||||
|
||||
%% retractall(Head)
|
||||
%
|
||||
@@ -1321,7 +1318,7 @@ module_abolish(Pred, Module) :-
|
||||
; throw(error(type_error(predicate_indicator, Module:Pred), abolish/1))
|
||||
).
|
||||
|
||||
:- meta_predicate abolish(:).
|
||||
:- meta_predicate(abolish(:)).
|
||||
|
||||
%% abolish(Pred).
|
||||
%
|
||||
@@ -1370,11 +1367,7 @@ current_predicate(Pred) :-
|
||||
( var(Pred) ->
|
||||
'$get_db_refs'(_, _, _, PIs),
|
||||
lists:member(Pred, PIs)
|
||||
; '$strip_module'(Pred, Module, UnqualifiedPred),
|
||||
( var(Module),
|
||||
\+ functor(Pred, (:), 2)
|
||||
; atom(Module)
|
||||
),
|
||||
; loader:strip_module(Pred, Module, UnqualifiedPred),
|
||||
UnqualifiedPred = Name/Arity ->
|
||||
( ( nonvar(Name), \+ atom(Name)
|
||||
; nonvar(Arity), \+ integer(Arity)
|
||||
@@ -1726,20 +1719,27 @@ must_be_number(N, PI) :-
|
||||
).
|
||||
|
||||
|
||||
chars_or_vars(Cs, _) :-
|
||||
chars_or_vars(Cs, PI) :-
|
||||
( '$is_partial_string'(Cs) ->
|
||||
% use a fast test for the expected case
|
||||
true
|
||||
; chars_or_vars_(Cs, PI)
|
||||
).
|
||||
|
||||
chars_or_vars_(Cs, _) :-
|
||||
( var(Cs) ->
|
||||
!
|
||||
; Cs == [] ->
|
||||
!
|
||||
).
|
||||
chars_or_vars([C|Cs], PI) :-
|
||||
chars_or_vars_([C|Cs], PI) :-
|
||||
( nonvar(C) ->
|
||||
( atom(C),
|
||||
atom_length(C, 1) ->
|
||||
chars_or_vars(Cs, PI)
|
||||
chars_or_vars_(Cs, PI)
|
||||
; throw(error(type_error(character, C), PI))
|
||||
)
|
||||
; chars_or_vars(Cs, PI)
|
||||
; chars_or_vars_(Cs, PI)
|
||||
).
|
||||
|
||||
|
||||
@@ -2195,12 +2195,10 @@ stream_property(S, P) :-
|
||||
%% at_end_of_stream(+Stream).
|
||||
%
|
||||
% True iff the stream Stream has ended
|
||||
at_end_of_stream(S_or_a) :-
|
||||
( var(S_or_a) ->
|
||||
at_end_of_stream(S) :-
|
||||
( var(S) ->
|
||||
throw(error(instantiation_error, at_end_of_stream/1))
|
||||
; atom(S_or_a) ->
|
||||
stream_property(S, alias(S_or_a))
|
||||
; S = S_or_a
|
||||
; true
|
||||
),
|
||||
stream_property(S, end_of_stream(E)),
|
||||
( E = at -> true ; E = past ).
|
||||
@@ -2212,7 +2210,7 @@ at_end_of_stream :-
|
||||
current_input(S),
|
||||
stream_property(S, end_of_stream(E)),
|
||||
!,
|
||||
( E = at ; E = past ).
|
||||
( E = at -> true ; E = past ).
|
||||
|
||||
%% set_stream_position(+Stream, +Position).
|
||||
%
|
||||
@@ -2228,7 +2226,7 @@ set_stream_position(S_or_a, Position) :-
|
||||
|
||||
%% callable(X).
|
||||
%
|
||||
% True iff X is bound o an atom or a compund term.
|
||||
% True iff X is bound to an atom or a compound term.
|
||||
|
||||
:- non_counted_backtracking callable/1.
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ get_single_char(C) :-
|
||||
; type_error(in_character, C, get_single_char/1)
|
||||
).
|
||||
|
||||
%% read_from_chars(+Chars, -Term).
|
||||
%% read_from_chars(+Chars, ?Term).
|
||||
%
|
||||
% Given a string made of chars which contains a representation of
|
||||
% a Prolog term, Term is the Prolog term represented. Example:
|
||||
@@ -206,10 +206,10 @@ get_single_char(C) :-
|
||||
% ```
|
||||
read_from_chars(Chars, Term) :-
|
||||
must_be(chars, Chars),
|
||||
must_be(var, Term),
|
||||
'$read_from_chars'(Chars, Term).
|
||||
'$read_from_chars'(Chars, Term0),
|
||||
Term = Term0.
|
||||
|
||||
%% read_term_from_chars(+Chars, -Term, +Options).
|
||||
%% read_term_from_chars(+Chars, ?Term, +Options).
|
||||
%
|
||||
% Like `read_from_chars`, except the reader is configured according to
|
||||
% `Options` which are those of `read_term`.
|
||||
@@ -220,9 +220,9 @@ read_from_chars(Chars, Term) :-
|
||||
% ```
|
||||
read_term_from_chars(Chars, Term, Options) :-
|
||||
must_be(chars, Chars),
|
||||
must_be(var, Term),
|
||||
builtins:parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term_from_chars/3),
|
||||
'$read_term_from_chars'(Chars, Term, Singletons, Variables, VariableNames).
|
||||
'$read_term_from_chars'(Chars, Term0, Singletons, Variables, VariableNames),
|
||||
Term = Term0.
|
||||
|
||||
%% write_term_to_chars(+Term, +Options, -Chars).
|
||||
%
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Author: Markus Triska
|
||||
E-mail: triska@metalevel.at
|
||||
WWW: https://www.metalevel.at
|
||||
Copyright (C): 2019-2023 Markus Triska
|
||||
Copyright (C): 2019-2025 Markus Triska
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
@@ -1150,8 +1150,9 @@ verify_attributes(Var, Other, Gs) :-
|
||||
( integer(Other) ->
|
||||
( between(0, 1, Other) ->
|
||||
root_get_formula_bdd(Root, Sat, BDD0),
|
||||
bdd_restriction(BDD0, I, Other, BDD),
|
||||
root_put_formula_bdd(Root, Sat, BDD),
|
||||
Gs = [bdd_restriction(BDD0,I,Other,BDD),satisfiable_bdd(BDD)]
|
||||
Gs = [satisfiable_bdd(BDD)]
|
||||
; no_truth_value(Other)
|
||||
)
|
||||
; atom(Other) ->
|
||||
@@ -1550,11 +1551,15 @@ random_bindings(VNum, Node) -->
|
||||
{ node_var_low_high(Node, Var, Low, High),
|
||||
bdd_count(Node, VNum, Total),
|
||||
bdd_count(Low, VNum, LCount) },
|
||||
( { maybe(LCount, Total) } ->
|
||||
( { weighted_maybe(LCount, Total) } ->
|
||||
[Var=0], random_bindings(VNum, Low)
|
||||
; [Var=1], random_bindings(VNum, High)
|
||||
).
|
||||
|
||||
weighted_maybe(K, N) :-
|
||||
random_integer(0, N, X),
|
||||
X < K.
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Find solutions with maximum weight.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
@@ -458,7 +458,7 @@ replacements of these low-level arithmetic predicates, often yielding
|
||||
more general programs. See [`n_factorial/2`](#clpz-factorial) for an
|
||||
example.
|
||||
|
||||
This library uses goal_expansion/2 to automatically rewrite
|
||||
This library uses `goal_expansion/2` to automatically rewrite
|
||||
constraints at compilation time so that low-level arithmetic
|
||||
predicates are _automatically_ used whenever possible. For example,
|
||||
the predicate:
|
||||
@@ -5088,11 +5088,21 @@ run_propagator(pmod(X,Y,Z), MState) -->
|
||||
; nonvar(Z), nonvar(X) ->
|
||||
( Z > 0 ->
|
||||
( X < 0 -> true
|
||||
; X >= Z
|
||||
; X >= Z,
|
||||
% due to X = Z+Y*_ and Y > Z
|
||||
( X-Z > 0 ->
|
||||
X-Z > Z
|
||||
; true
|
||||
)
|
||||
)
|
||||
; Z < 0 ->
|
||||
( X > 0 -> true
|
||||
; X =< Z
|
||||
; X =< Z,
|
||||
% due to X = Z+Y*_ and Y < Z
|
||||
( X-Z < 0 ->
|
||||
X-Z < Z
|
||||
; true
|
||||
)
|
||||
)
|
||||
; Z =:= 0 % Multiple solutions so do nothing special.
|
||||
),
|
||||
@@ -5100,7 +5110,23 @@ run_propagator(pmod(X,Y,Z), MState) -->
|
||||
YU < X, X =< 0 } -> kill(MState), Z =:= X
|
||||
; { fd_get(Y, _, n(YL), _, _),
|
||||
YL > X, X >= 0 } -> kill(MState), Z =:= X
|
||||
; ( Z > 0 ->
|
||||
; ( Z > 0, X < 0 ->
|
||||
{ fd_get(Y, YD, YPs),
|
||||
YMin is Z+1,
|
||||
YMax is Z-X,
|
||||
domain_remove_smaller_than(YD, YMin, YD1),
|
||||
domain_remove_greater_than(YD1, YMax, YD2) },
|
||||
fd_put(Y, YD2, YPs)
|
||||
% queue_goal((Y #> Z, Y #=< Z-X))
|
||||
; Z < 0, X > 0 ->
|
||||
{ fd_get(Y, YD, YPs),
|
||||
YMax is Z-1,
|
||||
YMin is Z-X,
|
||||
domain_remove_greater_than(YD, YMax, YD1),
|
||||
domain_remove_smaller_than(YD1, YMin, YD2) },
|
||||
fd_put(Y, YD2, YPs)
|
||||
% queue_goal((Y #< Z, Y #>= Z-X))
|
||||
; Z > 0 ->
|
||||
{ fd_get(Y, YD, YPs),
|
||||
YMin is Z + 1,
|
||||
domain_remove_smaller_than(YD, YMin, YD1) },
|
||||
@@ -5112,7 +5138,16 @@ run_propagator(pmod(X,Y,Z), MState) -->
|
||||
domain_remove_greater_than(YD, YMax, YD1) },
|
||||
fd_put(Y, YD1, YPs)
|
||||
% queue_goal(Y #< Z)
|
||||
; true
|
||||
; Z =:= 0,
|
||||
( X =:= 0 ->
|
||||
kill(MState) % trivial
|
||||
; % only 4 solutions {-abs(X),-1,1,abs(X)}
|
||||
{ YL is -abs(X), YU is abs(X),
|
||||
fd_get(Y, YD0, YPs),
|
||||
domain_remove_smaller_than(YD0, YL, YD1),
|
||||
domain_remove_greater_than(YD1, YU, YD) },
|
||||
fd_put(Y, YD, YPs)
|
||||
)
|
||||
)
|
||||
)
|
||||
; run_propagator(pmodz(X,Y,Z), MState),
|
||||
@@ -6138,7 +6173,7 @@ difference_arcs([V|Vs], FL0) -->
|
||||
|
||||
writeln(T) :- write(T), nl.
|
||||
|
||||
:- meta_predicate must_succeed(0).
|
||||
:- meta_predicate(must_succeed(0)).
|
||||
|
||||
must_succeed(G) :-
|
||||
( G -> true
|
||||
@@ -6262,7 +6297,7 @@ put_free(F) :- put_attr(F, free, true).
|
||||
|
||||
free_node(F) :- get_attr(F, free, true).
|
||||
|
||||
:- meta_predicate with_local_attributes(?, 0, ?).
|
||||
:- meta_predicate(with_local_attributes(?, 0, ?)).
|
||||
|
||||
:- dynamic(nat_copy/1).
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
:- module(cont, [reset/3, shift/1]).
|
||||
|
||||
:- meta_predicate reset(0, ?, ?).
|
||||
:- meta_predicate(reset(0, ?, ?)).
|
||||
|
||||
reset(Goal, Ball, Cont) :-
|
||||
call(Goal),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020-2024 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020-2025 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
/** Predicates for cryptographic applications.
|
||||
@@ -10,10 +10,6 @@
|
||||
|
||||
Especially for cryptographic applications, it is an advantage that
|
||||
using strings leaves little trace of what was processed in the system.
|
||||
|
||||
For predicates that accept an `encoding/1` option to specify the encoding
|
||||
of the input data, if `encoding(octet)` is used, then the input can also
|
||||
be specified as a list of _bytes_, i.e., integers between 0 and 255.
|
||||
*/
|
||||
|
||||
:- module(crypto,
|
||||
@@ -91,9 +87,36 @@ bytes_hex([B|Bs]) --> [C0,C1],
|
||||
},
|
||||
bytes_hex(Bs).
|
||||
|
||||
char_hexval(C, H) :-
|
||||
integer(H),
|
||||
!,
|
||||
hexval_char(H, C).
|
||||
char_hexval(C, H) :- nth0(H, "0123456789abcdef", C), !.
|
||||
char_hexval(C, H) :- nth0(H, "0123456789ABCDEF", C), !.
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
We specialize char_hexval/2 for use if the value is given,
|
||||
so that it works in constant time in this case.
|
||||
|
||||
The security of HMAC verification depends on this property.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
hexval_char(0, '0').
|
||||
hexval_char(1, '1').
|
||||
hexval_char(2, '2').
|
||||
hexval_char(3, '3').
|
||||
hexval_char(4, '4').
|
||||
hexval_char(5, '5').
|
||||
hexval_char(6, '6').
|
||||
hexval_char(7, '7').
|
||||
hexval_char(8, '8').
|
||||
hexval_char(9, '9').
|
||||
hexval_char(0xa, a).
|
||||
hexval_char(0xb, b).
|
||||
hexval_char(0xc, c).
|
||||
hexval_char(0xd, d).
|
||||
hexval_char(0xe, e).
|
||||
hexval_char(0xf, f).
|
||||
|
||||
must_be_bytes(Bytes, Context) :-
|
||||
must_be(list, Bytes),
|
||||
@@ -190,12 +213,19 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
|
||||
%
|
||||
% - `encoding(+Encoding)`
|
||||
% The default encoding is `utf8`. The alternative is `octet`, to
|
||||
% treat the input as a list of raw bytes.
|
||||
% use the character code of each character in Data as a byte
|
||||
% value.
|
||||
%
|
||||
% - `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`.
|
||||
% algorithms `sha256`, `sha384` and `sha512`. If `Hash` is
|
||||
% instantiated, then it is compared with the computed HMAC
|
||||
% in such a way that no information about the expected HMAC
|
||||
% is revealed, using a comparison of strings that always takes
|
||||
% the same time independent of whether and where the strings
|
||||
% differ. This option can therefore also be used to safely
|
||||
% _verify_ a given HMAC.
|
||||
%
|
||||
% Example:
|
||||
%
|
||||
@@ -222,10 +252,26 @@ crypto_data_hash(Data0, Hash, Options0) :-
|
||||
( 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).
|
||||
'$crypto_hmac'(Data, Encoding, Ks, HashBytes, A),
|
||||
( var(Hash) ->
|
||||
hex_bytes(Hash, HashBytes)
|
||||
; must_be(chars, Hash),
|
||||
hex_bytes(HashMAC, HashBytes),
|
||||
chars_equal_constant_time(Hash, HashMAC)
|
||||
)
|
||||
; '$crypto_data_hash'(Data, Encoding, HashBytes, A),
|
||||
hex_bytes(Hash, HashBytes)
|
||||
).
|
||||
|
||||
chars_equal_constant_time(As, Bs) :-
|
||||
maplist(chars_xor, As, Bs, Xs),
|
||||
sum_list(Xs, Sum),
|
||||
Sum =:= 0.
|
||||
|
||||
chars_xor(A, B, Xor) :-
|
||||
char_code(A, CA),
|
||||
char_code(B, CB),
|
||||
Xor is xor(CA,CB).
|
||||
|
||||
hmac_algorithm(sha256).
|
||||
hmac_algorithm(sha384).
|
||||
@@ -285,7 +331,8 @@ hash_algorithm(blake2b512).
|
||||
% default is all zeroes.
|
||||
% - `encoding(+Encoding)`
|
||||
% The default encoding is `utf8`. The alternative is `octet`,
|
||||
% to treat the input as a list of raw bytes.
|
||||
% to use the character code of each character in Data as a byte
|
||||
% value.
|
||||
%
|
||||
% The `info/1` option can be used to generate multiple keys from a
|
||||
% single master key, using for example values such as "key" and
|
||||
@@ -392,7 +439,7 @@ dollar_segments(Ls, Segments) :-
|
||||
% storing passwords, store these hashes. Later, you can verify the
|
||||
% validity of a password with `crypto_password_hash/2`, comparing the
|
||||
% then entered password to the stored hash. If you need to export this
|
||||
% atom, you should treat it as opaque ASCII data with up to 255 bytes
|
||||
% string, you should treat it as opaque ASCII data with up to 255 bytes
|
||||
% of length. The maximal length may increase in the future.
|
||||
%
|
||||
% Admissible options are:
|
||||
@@ -491,8 +538,9 @@ bytes_base64(Bytes, Base64) :-
|
||||
% Options:
|
||||
%
|
||||
% - `encoding(+Encoding)`
|
||||
% Encoding to use for PlainText. Default is utf8. The alternative
|
||||
% is octet to treat PlainText as raw bytes.
|
||||
% Encoding to use for PlainText. The default is `utf8`. The
|
||||
% alternative is `octet`, to use the character code of each
|
||||
% character in PlainText as a byte value.
|
||||
%
|
||||
% - `tag(-List)`
|
||||
% For authenticated encryption schemes, List is unified with a
|
||||
@@ -578,8 +626,9 @@ algorithm_key_iv('chacha20-poly1305', Key, IV) :-
|
||||
% Options is a list of:
|
||||
%
|
||||
% - `encoding(+Encoding)`
|
||||
% Encoding to use for PlainText. The default is utf8. The
|
||||
% alternative is octet, which is used if the data are raw bytes.
|
||||
% Encoding to use for PlainText. The default is `utf8`. The
|
||||
% alternative is `octet`, to obtain a list of characters where each
|
||||
% character code corresponds to a decrypted octet of CipherText.
|
||||
%
|
||||
% - `tag(+Tag)`
|
||||
% For authenticated encryption schemes, the tag must be specified as
|
||||
@@ -615,6 +664,8 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :-
|
||||
encoding_chars(octet, Bs, Cs) :-
|
||||
must_be(list, Bs),
|
||||
( maplist(integer, Bs) ->
|
||||
% the ability to use integers is deprecated and a
|
||||
% candidate for removal in the future!
|
||||
maplist(char_code, Cs, Bs)
|
||||
; Bs = Cs
|
||||
),
|
||||
@@ -701,6 +752,146 @@ 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.
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Side-channel attacks on Ed25519 predicates
|
||||
==========================================
|
||||
|
||||
Ed25519 predicates where the private key occurs as part of the
|
||||
arguments are potentially subject to side-channel attacks, since
|
||||
key pairs are represented as strings in the context of Ed25519.
|
||||
|
||||
The compact string representation used by Scryer Prolog means that
|
||||
different characters may occupy different numbers of bytes: Due
|
||||
to UTF-8 encoding, characters with codes 1..127 occupy exactly 1 byte,
|
||||
characters with codes 128..2047 occupy exactly 2 bytes, and '0'
|
||||
is represented as a list element occupying an entire cell and
|
||||
dedicated list constructor in addition to string termination and
|
||||
possibly padding.
|
||||
|
||||
This difference is located at the level of the Rust engine. To
|
||||
Prolog code, any two characters look conceptually the same (i.e.,
|
||||
they are both atoms of length 1), and the internal difference in
|
||||
representation cannot be observed at all.
|
||||
|
||||
Very precise timing information or other measurements about
|
||||
operations that reason about such strings may yield information
|
||||
that is meant to stay secret. For example, if Ks is a secret key
|
||||
stored as a list of characters, then the time it takes to run
|
||||
phrase(..., Ks) may reveal the number of bytes in Ks that are 0 or
|
||||
greater than 127.
|
||||
|
||||
To test whether it is possible to detect such differences, I use
|
||||
exp(N) which succeeds exactly 2^N times:
|
||||
|
||||
exp(E) :-
|
||||
N is 2^E,
|
||||
between(1, N, _).
|
||||
|
||||
Here is an example query that uses partial_string/1 to traverse
|
||||
various strings consisting uniformly of characters with the same
|
||||
code, such as 0, 32, 255 and others, in the hope to detect
|
||||
differences in timing if only in such extreme cases:
|
||||
|
||||
?- length(Ls, 256),
|
||||
member(Code, [12,0,55,0,0,32,255,10,0,127,64]),
|
||||
portray_clause(byte=Code),
|
||||
maplist(=(Code), Ls),
|
||||
atom_codes(A, Ls),
|
||||
atom_chars(A, Cs),
|
||||
time((exp(21),partial_string(Cs),false)).
|
||||
%@ byte=12.
|
||||
%@ % CPU time: 1.537s, 14_680_107 inferences
|
||||
%@ byte=0.
|
||||
%@ % CPU time: 1.526s, 14_680_107 inferences
|
||||
%@ byte=55.
|
||||
%@ % CPU time: 1.534s, 14_680_107 inferences
|
||||
%@ byte=0.
|
||||
%@ % CPU time: 1.556s, 14_680_107 inferences
|
||||
%@ byte=0.
|
||||
%@ % CPU time: 1.520s, 14_680_107 inferences
|
||||
%@ byte=32.
|
||||
%@ % CPU time: 1.524s, 14_680_107 inferences
|
||||
%@ byte=255.
|
||||
%@ % CPU time: 1.522s, 14_680_107 inferences
|
||||
%@ byte=10.
|
||||
%@ % CPU time: 1.526s, 14_680_107 inferences
|
||||
%@ byte=0.
|
||||
%@ % CPU time: 1.522s, 14_680_107 inferences
|
||||
%@ byte=127.
|
||||
%@ % CPU time: 1.522s, 14_680_107 inferences
|
||||
%@ byte=64.
|
||||
%@ % CPU time: 1.517s, 14_680_107 inferences
|
||||
%@ false.
|
||||
|
||||
This shows that there is enough variety between runs that
|
||||
traversing a list with 256 elements that are all '\x0\' may even,
|
||||
and unexpectedly, be faster than traversing a list consisting
|
||||
entirely of characters with character code 32, which in turn may be
|
||||
slower than processing a list with 256 characters that all have
|
||||
code 255 and thus occupy twice as much space. This holds even over
|
||||
millions of runs. Reasons for such variety can include CPU power
|
||||
saving mechanisms, dynamic optimizations, prefetching heuristics,
|
||||
branch prediction algorithms, varying system loads etc.
|
||||
|
||||
This gives rise to the suspicion that any such timing differences
|
||||
would be extremely hard to exploit, at least on the architecture I
|
||||
tested it on, also since partial_string/1 is a very low-level
|
||||
operation and any actual processing (using phrase/2 etc.) would
|
||||
introduce additional overheads that in all likelihood far outweigh
|
||||
any differences that can be measured with partial_string/1.
|
||||
|
||||
Any resulting differences in timing and resource use, if they are
|
||||
measurable at all in any way, can at most reveal one bit per byte.
|
||||
Note also that Ed25519 private keys are chosen randomly, and hence
|
||||
half of their bytes are expected to be in 128..255. The predicates
|
||||
remain completely safe to use in all scenarios where no information
|
||||
about the private key can be gathered by unauthorized parties.
|
||||
|
||||
Still, the concern remains: We know that different keys may occupy
|
||||
different numbers of bytes in the internal compact representation
|
||||
of strings used by Scryer Prolog, and it may be possible to exploit
|
||||
these differences to obtain information that is meant to be kept
|
||||
secret. We must therefore keep an eye on this issue. For example,
|
||||
it may become a concern on very slow devices such as ID-cards where
|
||||
Scryer Prolog may be deployed in the future and where such timing
|
||||
differences may be detectable, or if Scryer Prolog itself becomes
|
||||
so fast that the relative overhead of such low-level operations
|
||||
becomes greater and thus more easily measurable.
|
||||
|
||||
Possible mitigations in such situations would be to:
|
||||
|
||||
1. use lists of integers to represent Ed25519 key pairs, resulting
|
||||
in a 24-fold space increase. This may be prohibitive in
|
||||
applications that manage a great number of keys. The Rust code
|
||||
would not be affected by this change, since it already operates
|
||||
on bytes. A Prolog application may implement this privately, and
|
||||
also encourage an API change of this library.
|
||||
2. introduce a compact internal representation for lists of bytes,
|
||||
which appear to Prolog programs as lists of characters.
|
||||
|
||||
(2) seems to be the better solution despite the implementation
|
||||
overhead: In addition to the improved security properties due to
|
||||
the elimination of side-channel attacks when reasoning about keys,
|
||||
all applications that reason about binary data would benefit from a
|
||||
more compact representation of binary data. Such an additional
|
||||
compact representation should only be attempted if the amount of
|
||||
Rust code it impacts is kept to the absolute minimum, certainly
|
||||
much smaller than what the compact string representation as it is
|
||||
currently implemented affects.
|
||||
|
||||
*No* solution would be to:
|
||||
|
||||
- eliminate the compact string representation from the engine and
|
||||
use plain lists of characters to represent Ed25519 key pairs,
|
||||
- continue to use lists of characters for Ed25519 key pairs,
|
||||
and ensure that they are never coalesced into compact strings
|
||||
(a future GC compaction step may need to be adapted for this)
|
||||
|
||||
This is because atom names are still represented in UTF-8 encoding,
|
||||
and are hence also susceptible to side-channel attacks due to their
|
||||
using different numbers of bytes for different codes.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
ed25519_sign(KeyPair, Data0, Signature, Options) :-
|
||||
must_be_octet_chars(KeyPair, ed25519_sign/4),
|
||||
length(Prefix, 16),
|
||||
@@ -722,7 +913,8 @@ ed25519_sign(KeyPair, Data0, Signature, Options) :-
|
||||
%
|
||||
% - `encoding(+Encoding)`
|
||||
% The default encoding of Data is `utf8`. The alternative is `octet`,
|
||||
% which treats Data as a list of raw bytes.
|
||||
% to use the character code of each character in Data as a byte
|
||||
% value.
|
||||
|
||||
ed25519_verify(Key, Data0, Signature0, Options) :-
|
||||
must_be_octet_chars(Key, ed25519_verify/4),
|
||||
|
||||
@@ -12,8 +12,8 @@ to learn more about them.
|
||||
[op(1105, xfy, '|'),
|
||||
phrase/2,
|
||||
phrase/3,
|
||||
phrase/4,
|
||||
phrase/5,
|
||||
phrase//2,
|
||||
phrase//3,
|
||||
seq//1,
|
||||
seqq//1,
|
||||
... //0,
|
||||
@@ -25,13 +25,17 @@ to learn more about them.
|
||||
:- use_module(library(lists), [append/3, member/2]).
|
||||
:- use_module(library(loader), [strip_module/3]).
|
||||
|
||||
:- meta_predicate phrase(2, ?).
|
||||
:- meta_predicate(phrase(2, ?)).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?).
|
||||
:- meta_predicate(phrase(2, ?, ?)).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?, ?).
|
||||
:- meta_predicate(phrase(3, ?, ?, ?)).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?, ?, ?).
|
||||
:- meta_predicate(phrase(4, ?, ?, ?, ?)).
|
||||
|
||||
:- meta_predicate(','(2, 2, ?, ?)).
|
||||
|
||||
:- meta_predicate(;(2, 2, ?, ?)).
|
||||
|
||||
%% phrase(+Body, ?Ls).
|
||||
%
|
||||
@@ -210,10 +214,13 @@ dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :-
|
||||
dcg_body(GRIf, S0, S1, If),
|
||||
dcg_body(GRThen, S1, S, Then).
|
||||
|
||||
|
||||
% When DCG expansion throws an exception – remove offending term and rethrow.
|
||||
user:term_expansion(throw_dcg_expansion_error(E), _) :-
|
||||
throw(E).
|
||||
user:term_expansion(Term0, Term) :-
|
||||
nonvar(Term0),
|
||||
dcg_rule(Term0, Term).
|
||||
|
||||
catch(dcg_rule(Term0, Term), E, Term = throw_dcg_expansion_error(E)).
|
||||
|
||||
%% seq(Seq)//
|
||||
%
|
||||
@@ -242,6 +249,9 @@ seqq([Es|Ess]) --> seq(Es), seqq(Ess).
|
||||
Cs0 = Cs.
|
||||
... --> [] | [_], ... .
|
||||
|
||||
% defer instantiation errors until runtime. instantiations may be made
|
||||
% then.
|
||||
error_goal(error(instantiation_error, _Context), _).
|
||||
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),
|
||||
@@ -255,7 +265,11 @@ user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
|
||||
E,
|
||||
dcgs:error_goal(E, GRBody1)
|
||||
),
|
||||
( GRBody = (_:_) ->
|
||||
( E = error(instantiation_error, _),
|
||||
GRBody0 = [T|Ts] ->
|
||||
GRBody2 = (error:must_be(list, [T|Ts]),
|
||||
lists:append([T|Ts], S0, S))
|
||||
; GRBody = (_:_) ->
|
||||
GRBody2 = M:GRBody1
|
||||
; GRBody2 = GRBody1
|
||||
).
|
||||
|
||||
@@ -29,9 +29,9 @@
|
||||
|
||||
:- use_module(library(format), [portray_clause/1]).
|
||||
|
||||
:- meta_predicate *(0).
|
||||
:- meta_predicate $(0).
|
||||
:- meta_predicate $-(0).
|
||||
:- meta_predicate(*(0)).
|
||||
:- meta_predicate($(0)).
|
||||
:- meta_predicate($-(0)).
|
||||
|
||||
%% $-(Goal)
|
||||
%
|
||||
|
||||
131
src/lib/error.pl
131
src/lib/error.pl
@@ -1,44 +1,49 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2018-2023 by Markus Triska (triska@metalevel.at)
|
||||
Written 2018-2025 by Markus Triska (triska@metalevel.at)
|
||||
I place this code in the public domain. Use it in any way you want.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(error, [must_be/2,
|
||||
can_be/2,
|
||||
instantiation_error/0,
|
||||
domain_error/2,
|
||||
type_error/2,
|
||||
representation_error/1,
|
||||
resource_error/1,
|
||||
instantiation_error/1,
|
||||
domain_error/3,
|
||||
type_error/3
|
||||
type_error/3,
|
||||
call_with_error_context/2
|
||||
]).
|
||||
|
||||
|
||||
:- meta_predicate check_(1, ?, ?).
|
||||
:- meta_predicate(check_(1, ?, ?)).
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
must_be(Type, Term)
|
||||
|
||||
This predicate is intended for type-checks of built-in predicates.
|
||||
|
||||
It asserts that Term is:
|
||||
|
||||
1) instantiated *and*
|
||||
2) instantiated to an instance of the given Type.
|
||||
|
||||
It corresponds to usage mode +Term.
|
||||
|
||||
Currently, the following types are supported:
|
||||
|
||||
- atom
|
||||
- boolean
|
||||
- character
|
||||
- chars
|
||||
- in_character
|
||||
- integer
|
||||
- list
|
||||
- octet_character
|
||||
- octet_chars
|
||||
- term
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
%% must_be(Type, Term)
|
||||
%
|
||||
% This predicate is intended for type-checks of built-in predicates.
|
||||
%
|
||||
% It asserts that Term is:
|
||||
%
|
||||
% 1) instantiated *and*
|
||||
% 2) instantiated to an instance of the given Type.
|
||||
%
|
||||
% It corresponds to usage mode +Term.
|
||||
%
|
||||
% Currently, the following types are supported:
|
||||
%
|
||||
% - atom
|
||||
% - boolean
|
||||
% - character
|
||||
% - chars
|
||||
% - in_character
|
||||
% - integer
|
||||
% - list
|
||||
% - octet_character
|
||||
% - octet_chars
|
||||
% - pair
|
||||
% - term
|
||||
|
||||
must_be(Type, Term) :-
|
||||
must_be_(type, Type),
|
||||
@@ -84,6 +89,7 @@ must_be_(octet_chars, Cs) :-
|
||||
must_be_(list, Term) :- check_(error:ilist, list, Term).
|
||||
must_be_(type, Term) :- check_(error:type, type, Term).
|
||||
must_be_(boolean, Term) :- check_(error:boolean, boolean, Term).
|
||||
must_be_(pair, Term) :- check_(error:pair, pair, Term).
|
||||
must_be_(term, Term) :-
|
||||
( acyclic_term(Term) ->
|
||||
( ground(Term) -> true
|
||||
@@ -106,6 +112,9 @@ check_(Pred, Type, Term) :-
|
||||
; type_error(Type, Term, must_be/2)
|
||||
).
|
||||
|
||||
|
||||
pair(_-_).
|
||||
|
||||
boolean(B) :- ( B == true ; B == false ).
|
||||
|
||||
character(C) :-
|
||||
@@ -141,20 +150,18 @@ type(var).
|
||||
type(boolean).
|
||||
type(term).
|
||||
type(not_less_than_zero).
|
||||
type(pair).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
can_be(Type, Term)
|
||||
|
||||
This predicate is intended for type-checks of built-in predicates.
|
||||
|
||||
It asserts that there is a substitution which, if applied to Term,
|
||||
makes it an instance of Type.
|
||||
|
||||
It corresponds to usage mode ?Term.
|
||||
|
||||
It supports the same types as must_be/2.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
%% can_be(Type, Term)
|
||||
%
|
||||
% This predicate is intended for type-checks of built-in predicates.
|
||||
%
|
||||
% It asserts that there is a substitution which, if applied to Term,
|
||||
% makes it an instance of Type.
|
||||
%
|
||||
% It corresponds to usage mode ?Term.
|
||||
%
|
||||
% It supports the same types as must_be/2.
|
||||
|
||||
can_be(Type, Term) :-
|
||||
must_be(type, Type),
|
||||
@@ -192,6 +199,7 @@ can_(octet_chars, Cs) :-
|
||||
).
|
||||
can_(list, Term) :- list_or_partial_list(Term).
|
||||
can_(boolean, Term) :- boolean(Term).
|
||||
can_(pair, Term) :- pair(Term).
|
||||
can_(term, Term) :-
|
||||
( acyclic_term(Term) ->
|
||||
true
|
||||
@@ -212,6 +220,30 @@ list_or_partial_list(Ls) :-
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Shorthands for throwing ISO errors.
|
||||
|
||||
The variants without context promote the use of
|
||||
call_with_error_context/2.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
instantiation_error :-
|
||||
throw(error(instantiation_error, [])).
|
||||
|
||||
domain_error(Type, Term) :-
|
||||
throw(error(domain_error(Type, Term), [])).
|
||||
|
||||
type_error(Type, Term) :-
|
||||
throw(error(type_error(Type, Term), [])).
|
||||
|
||||
representation_error(Flag) :-
|
||||
throw(error(representation_error(Flag), [])).
|
||||
|
||||
resource_error(Resource) :-
|
||||
throw(error(resource_error(Resource), [])).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
The variants *with* context would not have been needed if
|
||||
call_with_error_context/2 had been found earlier. In the future,
|
||||
we may be able to remove them.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
instantiation_error(Context) :-
|
||||
@@ -222,3 +254,20 @@ domain_error(Type, Term, Context) :-
|
||||
|
||||
type_error(Type, Term, Context) :-
|
||||
throw(error(type_error(Type, Term), Context)).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
call_with_error_context/2
|
||||
|
||||
See https://github.com/mthom/scryer-prolog/discussions/2839 .
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
%% call_with_error_context(+Goal, +Pair)
|
||||
%
|
||||
% Call _Goal_ with error context _Pair_.
|
||||
%
|
||||
% Examples of error contexts: `predicate-PI`, `file-Filename` etc.
|
||||
|
||||
:- meta_predicate(call_with_error_context(0,+)).
|
||||
call_with_error_context(G_0, Pair) :-
|
||||
must_be(pair, Pair),
|
||||
catch(G_0, error(E,Pairs), throw(error(E,[Pair|Pairs]))).
|
||||
|
||||
120
src/lib/ffi.pl
120
src/lib/ffi.pl
@@ -1,4 +1,4 @@
|
||||
:- module(ffi, [use_foreign_module/2, foreign_struct/2]).
|
||||
:- module(ffi, [use_foreign_module/2, foreign_struct/2, with_locals/2, allocate/4, deallocate/3, read_ptr/3, array_type/3]).
|
||||
|
||||
/** Foreign Function Interface
|
||||
|
||||
@@ -11,8 +11,8 @@ The main predicate is `use_foreign_module/2`. It takes a library name (which dep
|
||||
operating system could be a `.so`, `.dylib` or `.dll` file). and a list of functions. Each
|
||||
function is defined by its name, a list of the type of the arguments, and the return argument.
|
||||
|
||||
Types available are: `sint8`, `uint8`, `sint16`, `uint16`, `sint32`, `uint32`, `sint64`,
|
||||
`uint64`, `f32`, `f64`, `cstr`, `void`, `bool`, `ptr` and custom structs, which can be defined
|
||||
Types available are: `sint8`/`i8`, `uint8`/`u8`, `sint16`/`i16`, `uint16`/`u16`, `sint32`/`i32`, `uint32`/`u32`, `sint64`/`i64`,
|
||||
`uint64`/`u64`, `f32`, `f64`, `cstr`, `void`, `bool`, `ptr` and custom structs, which can be defined
|
||||
with `foreign_struct/2`.
|
||||
|
||||
After that, each function on the lists maps to a predicate created in the ffi module which
|
||||
@@ -27,6 +27,12 @@ ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN, -ReturnArg). % for all return typ
|
||||
ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN). % for void and bool
|
||||
```
|
||||
|
||||
## Notes regarding cstr
|
||||
|
||||
- When using `cstr` as an argument type the string will be deallocated once the function returns.
|
||||
- When using `cstr` as a return type the string will be copied and won't be deallocated.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
For example, let's see how to define a function from the [raylib](https://www.raylib.com/) library.
|
||||
@@ -46,6 +52,9 @@ And a new window should pop up!
|
||||
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(iso_ext)).
|
||||
|
||||
%% foreign_struct(+Name, +Elements).
|
||||
%
|
||||
@@ -63,6 +72,21 @@ And a new window should pop up!
|
||||
foreign_struct(Name, Elements) :-
|
||||
'$define_foreign_struct'(Name, Elements).
|
||||
|
||||
|
||||
%% use_foreign_module(+LibName, +Predicates)
|
||||
%
|
||||
% - LibName the path to the shared library to load/bind
|
||||
% - Predicates list of function definitions
|
||||
%
|
||||
% Each function definition is a functor of arity 2.
|
||||
% The functor name is the name of the function to bind,
|
||||
% the first argument is the list of arguments of the function,
|
||||
% the second argument is the return type of the function.
|
||||
%
|
||||
% This will define a predicate in the ffi module with the defined name,
|
||||
% for void and bool return type functions the arity will match the length of the arguments list,
|
||||
% for other return types there will be an additional out parameter.
|
||||
%
|
||||
use_foreign_module(LibName, Predicates) :-
|
||||
'$load_foreign_lib'(LibName, Predicates),
|
||||
maplist(assert_predicate, Predicates).
|
||||
@@ -102,3 +126,93 @@ assert_predicate(PredicateDefinition) :-
|
||||
),
|
||||
Predicate = (Head:-Body),
|
||||
assertz(ffi:Predicate).
|
||||
|
||||
%% allocate(+Allocator, +Type, +Args, -Ptr)
|
||||
%
|
||||
% Using the Allocator allocate Type initialized with Args and
|
||||
% unify Ptr with a pointer to that allocation.
|
||||
%
|
||||
allocate(Allocator, Type, Args, Ptr) :-
|
||||
must_be(var, Ptr),
|
||||
must_be(atom, Type),
|
||||
must_be(atom, Allocator),
|
||||
'$ffi_allocate'(Allocator, Type, Args, Ptr).
|
||||
|
||||
|
||||
%% read_ptr(+Type, +Ptr, -Value)
|
||||
%
|
||||
% Read a value of Type from the pointer Ptr and unify the read value with Value
|
||||
%
|
||||
% For type cstr take read a nul-terminated utf-8 string starting at Ptr.
|
||||
%
|
||||
read_ptr(Type, Ptr, Value) :-
|
||||
must_be(atom, Type),
|
||||
must_be(integer, Ptr),
|
||||
'$ffi_read_ptr'(Type, Ptr, Value).
|
||||
|
||||
%% deallocate(+Allocator, +Type, +Ptr)
|
||||
%
|
||||
% Deallocate the allocation at Ptr of Type allocated with Allocator
|
||||
%
|
||||
deallocate(Allocator, Type, Ptr) :-
|
||||
must_be(atom, Allocator),
|
||||
must_be(integer, Ptr),
|
||||
'$ffi_deallocate'(Allocator, Type, Ptr).
|
||||
|
||||
:- dynamic(is_array_type_defined/1).
|
||||
|
||||
%% array_type(+ElemType, +Len, -ArrayType)
|
||||
%
|
||||
% unify the ffi type for an array of lenth Len with element type ElemType with ArrayType
|
||||
%
|
||||
array_type(ElemType, Len, ArrayType) :-
|
||||
(Len =< 0 -> domain_error(greater_than_zero, Len, array_type/3); true),
|
||||
phrase(format_("$[~a;~d]", [ElemType, Len]), ArrayTypeName),
|
||||
atom_chars(ArrayType, ArrayTypeName),
|
||||
(is_array_type_defined(ArrayType) -> true
|
||||
; length(Fields, Len),
|
||||
maplist(=(ElemType), Fields),
|
||||
foreign_struct(ArrayType, Fields),
|
||||
assertz(is_array_type_defined(ArrayType))
|
||||
).
|
||||
|
||||
:- meta_predicate(with_locals(?, 0)).
|
||||
|
||||
%% with_locals(+Locals, :Goal)
|
||||
%
|
||||
% Allocate the Locals, evaluate the Goal and deallocate the Locals.
|
||||
% The Locals will also be cleandup when Goal fails or throws an error.
|
||||
%
|
||||
% Locals is a list of local variable definitions let(-Ptr, +Type, +Args).
|
||||
% Ptr will be unified with the pointer to the local of Type initialized with Args.
|
||||
%
|
||||
with_locals(Locals, Goal) :-
|
||||
verify_locals(Locals),
|
||||
setup_call_cleanup(
|
||||
allocate_locals(Locals),
|
||||
Goal,
|
||||
deallocate_locals(Locals)
|
||||
).
|
||||
|
||||
verify_locals(Locals) :-
|
||||
must_be(list, Locals),
|
||||
( maplist(verify_local, Locals) -> true
|
||||
; domain_error(locals_decl_list, Locals, [verify_locals/1])
|
||||
).
|
||||
|
||||
verify_local(let(Var, Type, Init)) :-
|
||||
must_be(var, Var),
|
||||
must_be(atom, Type),
|
||||
ground(Init).
|
||||
|
||||
allocate_locals([]).
|
||||
allocate_locals([let(Var, Type, Init) | Ls]) :-
|
||||
allocate(rust, Type, Init , Var),
|
||||
(catch(allocate_locals(Ls), E, (deallocate_locals([let(Var, Type, Init)]), throw(E))) -> true
|
||||
; deallocate_locals([let(Var, Type, Init)]), false
|
||||
).
|
||||
|
||||
deallocate_locals([]).
|
||||
deallocate_locals([let(Var, Type, _) | Ls]) :-
|
||||
deallocate(rust, Type, Var),
|
||||
deallocate_locals(Ls).
|
||||
|
||||
@@ -18,7 +18,7 @@ _lists of characters_. This is an ideal representation:
|
||||
*/
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020, 2022 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020-2025 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
Predicates for reasoning about files and directories.
|
||||
@@ -89,17 +89,17 @@ _lists of characters_. This is an ideal representation:
|
||||
|
||||
%% directory_files(+Directory, -Files).
|
||||
%
|
||||
% Returns the list of files *and* directories available at a specific
|
||||
% directory in the current system.
|
||||
% True if `Files` are the files *and* directories available at a specific
|
||||
% `Directory` in the current system.
|
||||
|
||||
directory_files(Directory, Files) :-
|
||||
must_be(chars, Directory),
|
||||
can_be(list, Files),
|
||||
'$directory_files'(Directory, Files).
|
||||
|
||||
%% file_size(+File, -Size).
|
||||
%% file_size(+File, ?Size).
|
||||
%
|
||||
% Returns the size (in bytes) of a file. The file must exist.
|
||||
% True iff `Size` is the size (in bytes) of `File`. The file must exist.
|
||||
|
||||
file_size(File, Size) :-
|
||||
file_must_exist(File, file_size/2),
|
||||
@@ -108,22 +108,25 @@ file_size(File, Size) :-
|
||||
|
||||
%% file_exists(+File).
|
||||
%
|
||||
% Succeeds if File is a file that exists in the current system.
|
||||
% True iff `File` is a file that exists in the current system.
|
||||
|
||||
file_exists(File) :-
|
||||
must_be(chars, File),
|
||||
'$file_exists'(File).
|
||||
|
||||
%% directory_exists(+Directory).
|
||||
%
|
||||
% Succeeds if Directory is a directory that exists in the current system.
|
||||
% True iff `Directory` is a directory that exists in the current system.
|
||||
|
||||
directory_exists(Directory) :-
|
||||
must_be(chars, Directory),
|
||||
'$directory_exists'(Directory).
|
||||
|
||||
%% make_directory(+Directory).
|
||||
%
|
||||
% Succeeds if it creates a new directory named Directory in the current system.
|
||||
% Creates a new directory named `Directory`.
|
||||
% If you want to create a nested directory, use `make_directory_path/1`.
|
||||
|
||||
make_directory(Directory) :-
|
||||
must_be(chars, Directory),
|
||||
'$make_directory'(Directory).
|
||||
@@ -216,25 +219,26 @@ path_canonical(Ps, Cs) :-
|
||||
For two time stamps A and B, if A precedes B, then A @< B holds.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
%% file_modification_time(+File, -T).
|
||||
%% file_modification_time(+File, ?T).
|
||||
%
|
||||
% For a file File that must exist, it returns a time stamp T with the modification time
|
||||
% For a file `File` that must exist, `T` is the modification time.
|
||||
%
|
||||
% T is a time stamp compatible with `library(time)`.
|
||||
% T is a time stamp compatible with `library(time)`.
|
||||
file_modification_time(File, T) :-
|
||||
file_time_(File, modification, T).
|
||||
|
||||
%% file_access_time(+File, -T).
|
||||
%% file_access_time(+File, ?T).
|
||||
%
|
||||
% For a file File that must exist, it returns a time stamp T with the access time
|
||||
% For a file `File` that must exist, `T` is the access time.
|
||||
%
|
||||
% T is a time stamp compatible with `library(time)`.
|
||||
|
||||
file_access_time(File, T) :-
|
||||
file_time_(File, access, T).
|
||||
|
||||
%% file_creation_time(+File, -T).
|
||||
%% file_creation_time(+File, ?T).
|
||||
%
|
||||
% For a file File that must exist, it returns a time stamp T with the creation time
|
||||
% For a file `File` that must exist, `T` is the creation time.
|
||||
%
|
||||
% T is a time stamp compatible with `library(time)`.
|
||||
file_creation_time(File, T) :-
|
||||
@@ -246,7 +250,7 @@ file_time_(File, Which, T) :-
|
||||
read_from_chars(T0, T).
|
||||
|
||||
|
||||
%% path_segments(Ps, Segments).
|
||||
%% path_segments(?Ps, ?Segments).
|
||||
%
|
||||
% True iff Segments are the segments of Ps.
|
||||
%
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020-2024 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020-2025 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.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
@@ -42,7 +42,6 @@
|
||||
% FormatString are used literally, except for the following tokens
|
||||
% with special meaning:
|
||||
%
|
||||
% | `~w` | use the next available argument from Arguments here |
|
||||
% | `~q` | use the next argument here, formatted as by `writeq/1` |
|
||||
% | `~a` | use the next argument here, which must be an atom |
|
||||
% | `~s` | use the next argument here, which must be a string |
|
||||
@@ -72,6 +71,7 @@
|
||||
% | `~Nn` | N newlines |
|
||||
% | `~i` | ignore the next argument |
|
||||
% | `~~` | the literal ~ |
|
||||
% | `~w` | format like `write/1` would; consider using `~q`, `~d`, etc. |
|
||||
%
|
||||
% Instead of `~N`, you can write `~*` to use the next argument from
|
||||
% Arguments as the numeric argument.
|
||||
@@ -84,12 +84,66 @@
|
||||
% ```
|
||||
|
||||
format_(Fs, Args) -->
|
||||
{ must_be(list, Fs),
|
||||
must_be(list, Args),
|
||||
unique_variable_names(Args, VNs),
|
||||
phrase(cells(Fs,Args,0,[],VNs), Cells) },
|
||||
{ format_args_cells(Fs, Args, Cells) },
|
||||
format_cells(Cells).
|
||||
|
||||
format_args_cells(Fs, Args, Cells) :-
|
||||
must_be(chars, Fs),
|
||||
must_be(list, Args),
|
||||
unique_variable_names(fabricated, Args, VNs),
|
||||
phrase(cells(Fs,Args,0,[],VNs), Cells).
|
||||
|
||||
unique_variable_names(Type, Term, VNs) :-
|
||||
term_variables(Term, Vs),
|
||||
foldl(var_name(Type), Vs, VNs, 0, _).
|
||||
|
||||
var_name(Type, V, Name=V, Num0, Num) :-
|
||||
charsio:fabricate_var_name(Type, Name, Num0),
|
||||
Num is Num0 + 1.
|
||||
|
||||
user:goal_expansion(format_(Fs,Args,Cs0,Cs),
|
||||
format:format_cells(Cells, Cs0, Cs)) :-
|
||||
catch(format_args_cells(Fs,Args,Cells),
|
||||
E,
|
||||
% no partial evaluation for uses of format_//2 that
|
||||
% cannot be compiled statically, for example those where
|
||||
% the argument list is a variable, or where ~*n occurs
|
||||
% in the format string, or a domain error occurs
|
||||
( ( E = error(instantiation_error,_)
|
||||
; E = error(domain_error(_,_), _)
|
||||
) ->
|
||||
false
|
||||
; throw(E)
|
||||
)).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Partial evaluation of goals involving conditions that can be
|
||||
checked at compilation time. This is especially useful for the
|
||||
common case of conditions that test a numeric argument against 0.
|
||||
It is currently used for the goals of ~d.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
goal_pe(G0, G) :- var(G0), !, G = G0.
|
||||
goal_pe((A0,B0), (A,B)) :- !, goal_pe(A0, A), goal_pe(B0, B).
|
||||
goal_pe((Body0 ; Else0), Body) :-
|
||||
nonvar(Body0),
|
||||
Body0 = ( If -> Then0 ),
|
||||
!,
|
||||
( ground(If) ->
|
||||
( If ->
|
||||
goal_pe(Then0, Body)
|
||||
; goal_pe(Else0, Body)
|
||||
)
|
||||
; goal_pe(Then0, Then),
|
||||
goal_pe(Else0, Else),
|
||||
Body = ( If -> Then ; Else )
|
||||
).
|
||||
goal_pe(Goal, Goal).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
format_cells//1 is an interpreter for cells, describing a string.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
format_cells([]) --> [].
|
||||
format_cells([Cell|Cells]) -->
|
||||
format_cell(Cell),
|
||||
@@ -125,6 +179,7 @@ format_element(glue(Fill,Num)) -->
|
||||
{ length(Ls, Num),
|
||||
maplist(=(Fill), Ls) },
|
||||
seq(Ls).
|
||||
format_element(goal(_)) --> [].
|
||||
|
||||
elements_gluevars([], N, N) --> [].
|
||||
elements_gluevars([E|Es], N0, N) -->
|
||||
@@ -132,9 +187,11 @@ elements_gluevars([E|Es], N0, N) -->
|
||||
elements_gluevars(Es, N1, N).
|
||||
|
||||
element_gluevar(chars(Cs), N0, N) -->
|
||||
{ length(Cs, L),
|
||||
{ must_be(chars, Cs),
|
||||
length(Cs, L),
|
||||
N is N0 + L }.
|
||||
element_gluevar(glue(_,V), N, N) --> [V].
|
||||
element_gluevar(goal(G), N, N) --> { G }.
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Our key datastructure is a list of cells and newlines.
|
||||
@@ -142,12 +199,16 @@ element_gluevar(glue(_,V), N, N) --> [V].
|
||||
From and To denote the positions of surrounding tab stops.
|
||||
|
||||
Elements is a list of elements that occur in a cell,
|
||||
namely terms of the form chars(Cs) and glue(Char, Var).
|
||||
namely terms of the form chars(Cs), glue(Char, Var)
|
||||
and goal(G).
|
||||
|
||||
"glue" elements (TeX terminology) are evenly stretched
|
||||
to fill the remaining whitespace in the cell. For each
|
||||
glue element, the character Char is used for filling,
|
||||
and Var is a free variable that is used when the
|
||||
available space is distributed.
|
||||
available space is distributed. Goals are dynamically
|
||||
executed to obtain characters. In this way, format strings
|
||||
can be parsed and compiled statically when possible.
|
||||
|
||||
newline is used if ~n occurs in a format string.
|
||||
It is used because a newline character does not
|
||||
@@ -161,54 +222,55 @@ cells([], Args, Tab, Es, _) --> !,
|
||||
cells([~,~|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [chars("~")|Es], VNs).
|
||||
cells([~,w|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ write_term_to_chars(Arg, [numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
{ G = write_term_to_chars(Arg, [numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
|
||||
cells([~,q|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ write_term_to_chars(Arg, [quoted(true),numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
{ G = write_term_to_chars(Arg, [quoted(true),numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
|
||||
cells([~,a|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ atom_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
{ G = atom_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [d|Fs], Args0, [Arg0|Args]) },
|
||||
!,
|
||||
{ Arg is Arg0, % evaluate compound expression
|
||||
must_be(integer, Arg),
|
||||
number_chars(Arg, Cs0) },
|
||||
( { Num =:= 0 } -> { Cs = Cs0 }
|
||||
; { length(Cs0, L),
|
||||
( L =< Num ->
|
||||
Delta is Num - L,
|
||||
length(Zs, Delta),
|
||||
maplist(=('0'), Zs),
|
||||
phrase(("0.",seq(Zs),seq(Cs0)), Cs)
|
||||
; BeforeComma is L - Num,
|
||||
length(Bs, BeforeComma),
|
||||
append(Bs, Ds, Cs0),
|
||||
phrase((seq(Bs),".",seq(Ds)), Cs)
|
||||
) }
|
||||
),
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G0 = ( Arg is Arg0, % evaluate compound expression
|
||||
must_be(integer, Arg),
|
||||
number_chars(Arg, Cs0),
|
||||
( Num =:= 0 -> Cs = Cs0
|
||||
; length(Cs0, L),
|
||||
( L =< Num ->
|
||||
Delta is Num - L,
|
||||
length(Zs, Delta),
|
||||
maplist(=('0'), Zs),
|
||||
phrase(("0.",seq(Zs),seq(Cs0)), Cs)
|
||||
; BeforeComma is L - Num,
|
||||
length(Bs, BeforeComma),
|
||||
append(Bs, Ds, Cs0),
|
||||
phrase((seq(Bs),".",seq(Ds)), Cs)
|
||||
)
|
||||
)),
|
||||
goal_pe(G0, G) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['D'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ separate_digits_fractional(Arg, ',', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = separate_digits_fractional(Arg, ',', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['U'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ separate_digits_fractional(Arg, '_', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = separate_digits_fractional(Arg, '_', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num0, ['L'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ ( Num0 =:= 0 ->
|
||||
Num = 72
|
||||
; Num = Num0
|
||||
),
|
||||
phrase(format_("~d", [Arg]), Cs0),
|
||||
phrase(split_lines_width(Cs0, Num), Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = (( Num0 =:= 0 ->
|
||||
Num = 72
|
||||
; Num = Num0
|
||||
),
|
||||
phrase(format_("~d", [Arg]), Cs0),
|
||||
phrase(split_lines_width(Cs0, Num), Cs) ) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~,i|Fs], [_|Args], Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, Es, VNs).
|
||||
cells([~,n|Fs], Args, Tab, Es, VNs) --> !,
|
||||
@@ -223,59 +285,40 @@ cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
cells(Fs, Args, 0, [], VNs).
|
||||
cells([~,s|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [chars(Arg)|Es], VNs).
|
||||
cells([~,f|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ format_number_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
cells([~,f|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells([~,'6',f|Fs], Args, Tab, Es, VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [f|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ format_number_chars(Arg, Cs0),
|
||||
phrase(upto_what(Bs, .), Cs0, Cs),
|
||||
( Num =:= 0 -> Chars = Bs
|
||||
; ( Cs = ['.'|Rest] ->
|
||||
length(Rest, L),
|
||||
( Num < L ->
|
||||
length(Ds, Num),
|
||||
append(Ds, _, Rest)
|
||||
; Num =:= L ->
|
||||
Ds = Rest
|
||||
; Num > L,
|
||||
Delta is Num - L,
|
||||
% we should look into the float with
|
||||
% greater accuracy here, and use the
|
||||
% actual digits instead of 0.
|
||||
length(Zs, Delta),
|
||||
maplist(=('0'), Zs),
|
||||
append(Rest, Zs, Ds)
|
||||
)
|
||||
; length(Ds, Num),
|
||||
maplist(=('0'), Ds)
|
||||
),
|
||||
append(Bs, ['.'|Ds], Chars)
|
||||
) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
{ G = phrase(float_with_n_decimal_digits(Arg, Num), Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
|
||||
cells([~,r|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells([~,'8',r|Fs], Args, Tab, Es, VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [r|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ integer_to_radix(Arg, Num, lowercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = integer_to_radix(Arg, Num, lowercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~,'R'|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells([~,'8','R'|Fs], Args, Tab, Es, VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['R'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ integer_to_radix(Arg, Num, uppercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
{ G = integer_to_radix(Arg, Num, uppercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs),goal(G)|Es], VNs).
|
||||
cells([~,'`',Char,t|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [glue(Char,_)|Es], VNs).
|
||||
cells([~,t|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [glue(' ',_)|Es], VNs).
|
||||
cells([~,'|'|Fs], Args, Tab0, Es, VNs) --> !,
|
||||
{ phrase(elements_gluevars(Es, 0, Width), _),
|
||||
Tab is Tab0 + Width },
|
||||
cell(Tab0, Tab, Es),
|
||||
( { ground(Tab0), Es = [chars(Cs)], ground(Cs) } ->
|
||||
{ length(Cs, Width),
|
||||
Tab is Tab0 + Width },
|
||||
cell(Tab0, Tab, Es)
|
||||
; { G = (phrase(elements_gluevars(Es, 0, Width), _),
|
||||
Tab is Tab0 + Width) },
|
||||
cell(Tab0, Tab, [goal(G)|Es])
|
||||
),
|
||||
cells(Fs, Args, Tab, [], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['|'|Fs], Args0, Args) },
|
||||
@@ -285,8 +328,12 @@ cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
cells([~|Fs0], Args0, Tab0, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [+|Fs], Args0, Args) },
|
||||
!,
|
||||
{ Tab is Tab0 + Num },
|
||||
cell(Tab0, Tab, Es),
|
||||
( { ground(Tab0+Num) } ->
|
||||
{ Tab is Tab0 + Num },
|
||||
cell(Tab0, Tab, Es)
|
||||
; { G = (Tab is Tab0 + Num) },
|
||||
cell(Tab0, Tab, [goal(G)|Es])
|
||||
),
|
||||
cells(Fs, Args, Tab, [], VNs).
|
||||
cells([~|Cs], Args, _, _, _) -->
|
||||
( { Args == [] } ->
|
||||
@@ -298,9 +345,18 @@ cells(Fs0, Args, Tab, Es, VNs) -->
|
||||
Fs1 = [_|_] },
|
||||
cells(Fs, Args, Tab, [chars(Fs1)|Es], VNs).
|
||||
|
||||
format_number_chars(N0, Chars) :-
|
||||
N is N0, % evaluate compound expression
|
||||
number_chars(N, Chars).
|
||||
float_with_n_decimal_digits(F, N) -->
|
||||
{ Fr is abs(float_fractional_part(F)),
|
||||
FrR0 is round(Fr*10^N),
|
||||
I0 is truncate(F),
|
||||
( FrR0 >= 10^N
|
||||
-> I is I0+truncate(sign(F)), FrR = FrR0
|
||||
; I = I0, FrR is FrR0+10^N
|
||||
)
|
||||
},
|
||||
( { I=0, F<0, FrR>10^N } -> "-0" ; { number_chars(I, Is) }, seq(Is) ),
|
||||
".",
|
||||
( { FrR = 1 } -> "0" ; { number_chars(FrR, ['1'|FrRs]) }, seq(FrRs) ).
|
||||
|
||||
n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N).
|
||||
n_newlines(0) --> [].
|
||||
@@ -419,9 +475,11 @@ digits(uppercase, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").
|
||||
% advantage of this is that an ideal implementation writes the
|
||||
% characters as they become known, without manifesting the list.
|
||||
|
||||
format(Fs, Args) :-
|
||||
current_output(Stream),
|
||||
format(Stream, Fs, Args).
|
||||
format(_, _) :- not_used.
|
||||
|
||||
user:goal_expansion(format(Fs, Args),
|
||||
( current_output(Stream),
|
||||
format(Stream, Fs, Args))).
|
||||
|
||||
%% format(Stream, FormatString, Arguments)
|
||||
%
|
||||
@@ -429,9 +487,11 @@ format(Fs, Args) :-
|
||||
% binary stream, then the code of each emitted character must be in
|
||||
% 0..255.
|
||||
|
||||
format(Stream, Fs, Args) :-
|
||||
phrase_to_stream(format_(Fs, Args), Stream),
|
||||
flush_output(Stream).
|
||||
format(_, _, _) :- not_used.
|
||||
|
||||
user:goal_expansion(format(Stream, Fs, Args),
|
||||
( pio:phrase_to_stream(format:format_(Fs, Args), Stream),
|
||||
flush_output(Stream))).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
?- phrase(format:cells("hello", [], 0, [], []), Cs).
|
||||
@@ -514,20 +574,29 @@ portray_clause(Stream, Term) :-
|
||||
flush_output(Stream).
|
||||
|
||||
portray_clause_(Term) -->
|
||||
{ unique_variable_names(Term, VNs) },
|
||||
{ unique_variable_names(numbervars, Term, VNs) },
|
||||
portray_(Term, VNs), ".\n".
|
||||
|
||||
unique_variable_names(Term, VNs) :-
|
||||
term_variables(Term, Vs),
|
||||
foldl(var_name, Vs, VNs, 0, _).
|
||||
|
||||
var_name(V, Name=V, Num0, Num) :-
|
||||
charsio:fabricate_var_name(numbervars, Name, Num0),
|
||||
Num is Num0 + 1.
|
||||
|
||||
literal(Lit, VNs) -->
|
||||
{ write_term_to_chars(Lit, [quoted(true),variable_names(VNs),double_quotes(true)], Ls) },
|
||||
seq(Ls).
|
||||
( { nonvar(Lit),
|
||||
\+ number(Lit),
|
||||
functor(Lit, F, A),
|
||||
current_op(Pri, _, F),
|
||||
( A =:= 0
|
||||
; Pri >= 1000
|
||||
) } ->
|
||||
"(", seq(Ls), ")"
|
||||
; seq(Ls)
|
||||
).
|
||||
|
||||
literal_(Lit, VNs) -->
|
||||
{ phrase(literal(Lit, VNs), Ls) },
|
||||
seq(Ls),
|
||||
( { phrase((...,[Last]), Ls), char_type(Last, graphic_token) } ->
|
||||
" "
|
||||
; ""
|
||||
).
|
||||
|
||||
portray_(Var, VNs) --> { var(Var) }, !, literal(Var, VNs).
|
||||
portray_((Head :- Body), VNs) --> !,
|
||||
@@ -536,7 +605,7 @@ portray_((Head :- Body), VNs) --> !,
|
||||
portray_((Head --> Body), VNs) --> !,
|
||||
literal(Head, VNs), " -->\n",
|
||||
body_(Body, 0, 3, VNs).
|
||||
portray_(Any, VNs) --> literal(Any, VNs).
|
||||
portray_(Any, VNs) --> literal_(Any, VNs).
|
||||
|
||||
|
||||
body_(Var, C, I, VNs) --> { var(Var) }, !,
|
||||
@@ -561,7 +630,7 @@ body_((A;B), C, I, VNs) --> !,
|
||||
body_(A, C1, C1, VNs), "\n",
|
||||
else_branch(B, I, VNs).
|
||||
body_(Goal, C, I, VNs) -->
|
||||
indent_to(C, I), literal(Goal, VNs).
|
||||
indent_to(C, I), literal_(Goal, VNs).
|
||||
|
||||
|
||||
% True iff Body has the shape ( If -> Then ; Else ).
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
:- use_module(library(atts)).
|
||||
:- use_module(library(dcgs)).
|
||||
|
||||
:- meta_predicate freeze(-, 0).
|
||||
:- meta_predicate(freeze(-, 0)).
|
||||
|
||||
:- attribute frozen/1.
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ as one HTTP method (in lowercase) and followed by a Route Match and a predicate
|
||||
which will handle the call.
|
||||
|
||||
```
|
||||
:- use_module(library(http/http_server)).
|
||||
|
||||
text_handler(Request, Response) :-
|
||||
http_status_code(Response, 200),
|
||||
http_body(Response, text("Welcome to Scryer Prolog!")).
|
||||
@@ -24,10 +26,11 @@ text_handler(Request, Response) :-
|
||||
parameter_handler(User, Request, Response) :-
|
||||
http_body(Response, text(User)).
|
||||
|
||||
http_listen(7890, [
|
||||
get(echo, text_handler), % GET /echo
|
||||
post(user/User, parameter_handler(User)) % POST /user/<User>
|
||||
]).
|
||||
run:-
|
||||
http_listen(7890, [
|
||||
get(echo, text_handler), % GET /echo
|
||||
post(user/User, parameter_handler(User)) % POST /user/<User>
|
||||
]).
|
||||
```
|
||||
|
||||
Every handler predicate will have at least 2-arity, with Request and Response.
|
||||
@@ -62,10 +65,10 @@ Some things that are still missing:
|
||||
http_basic_auth/4
|
||||
]).
|
||||
|
||||
:- meta_predicate http_listen(?, :).
|
||||
:- meta_predicate http_listen(?, :, ?).
|
||||
:- meta_predicate(http_listen(?, :)).
|
||||
:- meta_predicate(http_listen(?, :, ?)).
|
||||
|
||||
:- meta_predicate http_basic_auth(:, :, ?, ?).
|
||||
:- meta_predicate(http_basic_auth(:, :, ?, ?)).
|
||||
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(crypto)).
|
||||
|
||||
@@ -22,6 +22,7 @@ but they're not part of the ISO Prolog standard at the moment.
|
||||
copy_term/3]).
|
||||
|
||||
:- use_module(library(error), [can_be/2,
|
||||
must_be/2,
|
||||
domain_error/3,
|
||||
instantiation_error/1,
|
||||
type_error/3]).
|
||||
@@ -223,6 +224,7 @@ run_cleaners_without_handling(Cp) :-
|
||||
%% call_with_inference_limit(Goal, Limit, Result).
|
||||
%
|
||||
% Similar to `call(Goal)` but it limits the number of inferences for each solution of Goal.
|
||||
% Calls to it may be nested, but only the last limit will be in power.
|
||||
call_with_inference_limit(G, L, R) :-
|
||||
( integer(L) ->
|
||||
( L < 0 ->
|
||||
@@ -274,17 +276,15 @@ call_with_inference_limit(_, _, R, Bb, B) :-
|
||||
; nonvar(R)
|
||||
).
|
||||
|
||||
%% partial_string(String, L, L0)
|
||||
%% partial_string(String, Ls0, Ls)
|
||||
%
|
||||
% Explicitly construct a partial string "manually". It can be used as an optimized append/3.
|
||||
% It's not recommended to use this predicate in application code.
|
||||
partial_string(String, L, L0) :-
|
||||
partial_string(String, Ls0, Ls) :-
|
||||
must_be(chars, String),
|
||||
( String == [] ->
|
||||
L = L0
|
||||
; catch(atom_chars(Atom, String),
|
||||
error(E, _),
|
||||
throw(error(E, partial_string/3))),
|
||||
'$create_partial_string'(Atom, L, L0)
|
||||
Ls0 = Ls
|
||||
; '$create_partial_string'(String, Ls0, Ls)
|
||||
).
|
||||
|
||||
%% partial_string(+String)
|
||||
@@ -399,7 +399,7 @@ copy_term(Term, Copy, Gs) :-
|
||||
; true
|
||||
).
|
||||
|
||||
:- meta_predicate call_residue_vars(0, ?).
|
||||
:- meta_predicate(call_residue_vars(0, ?)).
|
||||
|
||||
call_residue_vars(Goal, Vars) :-
|
||||
can_be(list, Vars),
|
||||
|
||||
@@ -117,32 +117,32 @@ Further discussions
|
||||
@author Ulrich Neumerkel
|
||||
*/
|
||||
|
||||
:- meta_predicate ^(?,0,?).
|
||||
:- meta_predicate ^(?,1,?,?).
|
||||
:- meta_predicate ^(?,2,?,?,?).
|
||||
:- meta_predicate ^(?,3,?,?,?,?).
|
||||
:- meta_predicate ^(?,4,?,?,?,?,?).
|
||||
:- meta_predicate ^(?,5,?,?,?,?,?,?).
|
||||
:- meta_predicate ^(?,6,?,?,?,?,?,?,?).
|
||||
:- meta_predicate ^(?,7,?,?,?,?,?,?,?,?).
|
||||
:- meta_predicate \(0).
|
||||
:- meta_predicate \(1,?).
|
||||
:- meta_predicate \(2,?,?).
|
||||
:- meta_predicate \(3,?,?,?).
|
||||
:- meta_predicate \(4,?,?,?,?).
|
||||
:- meta_predicate \(5,?,?,?,?,?).
|
||||
:- meta_predicate \(6,?,?,?,?,?,?).
|
||||
:- meta_predicate \(7,?,?,?,?,?,?,?).
|
||||
:- meta_predicate +\(?,0).
|
||||
:- meta_predicate +\(?,1,?).
|
||||
:- meta_predicate +\(?,2,?,?).
|
||||
:- meta_predicate +\(?,3,?,?,?).
|
||||
:- meta_predicate +\(?,4,?,?,?,?).
|
||||
:- meta_predicate +\(?,5,?,?,?,?,?).
|
||||
:- meta_predicate +\(?,6,?,?,?,?,?,?).
|
||||
:- meta_predicate +\(?,7,?,?,?,?,?,?,?).
|
||||
:- meta_predicate(^(?,0,?)).
|
||||
:- meta_predicate(^(?,1,?,?)).
|
||||
:- meta_predicate(^(?,2,?,?,?)).
|
||||
:- meta_predicate(^(?,3,?,?,?,?)).
|
||||
:- meta_predicate(^(?,4,?,?,?,?,?)).
|
||||
:- meta_predicate(^(?,5,?,?,?,?,?,?)).
|
||||
:- meta_predicate(^(?,6,?,?,?,?,?,?,?)).
|
||||
:- meta_predicate(^(?,7,?,?,?,?,?,?,?,?)).
|
||||
:- meta_predicate(\(0)).
|
||||
:- meta_predicate(\(1,?)).
|
||||
:- meta_predicate(\(2,?,?)).
|
||||
:- meta_predicate(\(3,?,?,?)).
|
||||
:- meta_predicate(\(4,?,?,?,?)).
|
||||
:- meta_predicate(\(5,?,?,?,?,?)).
|
||||
:- meta_predicate(\(6,?,?,?,?,?,?)).
|
||||
:- meta_predicate(\(7,?,?,?,?,?,?,?)).
|
||||
:- meta_predicate(+\(?,0)).
|
||||
:- meta_predicate(+\(?,1,?)).
|
||||
:- meta_predicate(+\(?,2,?,?)).
|
||||
:- meta_predicate(+\(?,3,?,?,?)).
|
||||
:- meta_predicate(+\(?,4,?,?,?,?)).
|
||||
:- meta_predicate(+\(?,5,?,?,?,?,?)).
|
||||
:- meta_predicate(+\(?,6,?,?,?,?,?,?)).
|
||||
:- meta_predicate(+\(?,7,?,?,?,?,?,?,?)).
|
||||
|
||||
:- meta_predicate no_hat_call(0).
|
||||
:- meta_predicate(no_hat_call(0)).
|
||||
|
||||
^(V1,C_0,V1) :-
|
||||
no_hat_call(C_0).
|
||||
|
||||
@@ -42,17 +42,17 @@ List manipulation predicates
|
||||
:- use_module(library(error)).
|
||||
|
||||
|
||||
:- meta_predicate maplist(1, ?).
|
||||
:- meta_predicate maplist(2, ?, ?).
|
||||
:- meta_predicate maplist(3, ?, ?, ?).
|
||||
:- meta_predicate maplist(4, ?, ?, ?, ?).
|
||||
:- meta_predicate maplist(5, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate maplist(6, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate maplist(7, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate maplist(8, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate(maplist(1, ?)).
|
||||
:- meta_predicate(maplist(2, ?, ?)).
|
||||
:- meta_predicate(maplist(3, ?, ?, ?)).
|
||||
:- meta_predicate(maplist(4, ?, ?, ?, ?)).
|
||||
:- meta_predicate(maplist(5, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(maplist(6, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(maplist(7, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(maplist(8, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
|
||||
:- meta_predicate foldl(3, ?, ?, ?).
|
||||
:- meta_predicate foldl(4, ?, ?, ?, ?).
|
||||
:- meta_predicate(foldl(3, ?, ?, ?)).
|
||||
:- meta_predicate(foldl(4, ?, ?, ?, ?)).
|
||||
|
||||
:- use_module(library(error)).
|
||||
|
||||
|
||||
187
src/lib/numerics/quadtests.pl
Normal file
187
src/lib/numerics/quadtests.pl
Normal file
@@ -0,0 +1,187 @@
|
||||
% Efforts toward literate tests with quads
|
||||
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(pio)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(reif)).
|
||||
:- use_module(library(debug)).
|
||||
:- use_module(library(lambda)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(time)).
|
||||
|
||||
:- use_module(testutils).
|
||||
:- use_module(special_functions).
|
||||
|
||||
portray_term(Stream) :-
|
||||
read_term(Stream, Term, []),
|
||||
portray_clause(Term).
|
||||
|
||||
?- check_module_quads(special_functions, _).
|
||||
% Checking 11 quads ..
|
||||
% CHECKING.. (?-A=0.6174468790806071,erf(A,A),B is-A,erf(B,B)).
|
||||
% CHECKING.. (?-try_falsify(odd_t(erf,real(A)))).
|
||||
% CHECKING.. (?-witness(odd_t(erf,real(A),false))).
|
||||
% CHECKING.. (?-witness((real(A),erf(A,B),erf(-A,C),abs(B+C)>0))).
|
||||
% CHECKING.. (?-length(A,B)).
|
||||
% CHECKING.. (?-real(A),erf(A,B),erfc(A,C),abs(B+C-1)>epsilon).
|
||||
% CHECKING.. (?-try_falsify(δ_inverses_t(40*epsilon,erf,inverf,interval(-2,2,A)))).
|
||||
% CHECKING.. (?-try_falsify(δ_inverses_t(40*epsilon,erfc,inverfc,interval(-2,2,A)))).
|
||||
% CHECKING.. (?-A=10,B is A+1,gamma(B,C),int_realfact(A,D)).
|
||||
% CHECKING.. (?-gamma_P_Q(1.2,2.3,A,B),abs(A+B-1)<epsilon).
|
||||
% CHECKING.. (?-A=1.5,B=0.7,invgammp(A,B,C),gamma_P_Q(A,C,D,E),abs(B-D)<epsilon).
|
||||
true.
|
||||
|
||||
check_module_quads(Module, Quads) :-
|
||||
use_module(Module),
|
||||
read_quads(Module, Quads),
|
||||
zip(Qs, ADs, Quads),
|
||||
length(Qs, NQ),
|
||||
format("% Checking ~d quads ..~n", [NQ]),
|
||||
maplist(check_qu_ad, Qs, ADs).
|
||||
|
||||
read_quads(Module, Quads) :-
|
||||
module_terms(Module, Terms),
|
||||
terms_quads(Terms, Quads).
|
||||
|
||||
module_terms(Module, Terms) :-
|
||||
module_file(Module, File),
|
||||
setup_call_cleanup(
|
||||
open(File, read, Stream, [type(text)]),
|
||||
read_terms(Stream, Terms),
|
||||
close(Stream)
|
||||
).
|
||||
|
||||
module_file(Module, File) :- atom_concat(Module, '.pl', File).
|
||||
|
||||
% Given a list of terms, filter out the predicate clauses.
|
||||
% TODO: Arg 1 is really a list of Term-VarNames _pairs_;
|
||||
% it would be very nice to find a less unsightly
|
||||
% name than 'TermVN' for these!
|
||||
terms_quads([Term|Terms], Quads) :-
|
||||
( term_type(Term, clause) -> terms_quads(Terms, Quads)
|
||||
; Quads = [Term|Quads_],
|
||||
terms_quads(Terms, Quads_)
|
||||
).
|
||||
terms_quads([], []).
|
||||
|
||||
term_type(Term-_, Type) :-
|
||||
( Term = (?- _) -> Type = query
|
||||
; Term = (_,_) -> Type = answer_description
|
||||
; Term = (_;_) -> Type = answer_description
|
||||
; Term == true -> Type = answer_description
|
||||
; Term == false -> Type = answer_description
|
||||
; Type = clause
|
||||
).
|
||||
|
||||
?- term_type(test("erf is odd",try_falsify(odd_t(erf,real(_L))))-_, Type).
|
||||
Type = clause.
|
||||
|
||||
read_terms(Stream, Terms) :-
|
||||
read_terms_(Stream, [], Terms).
|
||||
read_terms_(Stream, Terms0, Terms) :-
|
||||
Options = [variable_names(VarNames)],
|
||||
read_term(Stream, Term, Options),
|
||||
( Term = end_of_file -> reverse(Terms0, Terms)
|
||||
; read_terms_(Stream, [Term-VarNames|Terms0], Terms)
|
||||
).
|
||||
|
||||
%% zip(+Xs, +Ys, ?XYs)
|
||||
%% zip(?Xs, ?Ys, +XYs)
|
||||
%
|
||||
% List XYs interleaves same-length lists Xs and Ys.
|
||||
zip([X|Xs], [Y|Ys], [X,Y|XYs]) :-
|
||||
zip(Xs, Ys, XYs).
|
||||
zip([], [], []).
|
||||
|
||||
?- zip(Xs, Ys, XYs). % MGQ does not terminate
|
||||
error('$interrupt_thrown',repl/0).
|
||||
|
||||
% The following suggested by Ulrich via Quad Works chat
|
||||
?- zip(Xs, Ys, XYs), false. % loops
|
||||
|
||||
?- zip(X, [4,5,6], [1,4,2,5,3,6]).
|
||||
X = [1,2,3].
|
||||
|
||||
?- zip([1,2,3], Y, [1,4,2,5,3,6]).
|
||||
Y = [4,5,6].
|
||||
|
||||
?- zip([1,2,3], [4,5,6], Z).
|
||||
Z = [1,4,2,5,3,6].
|
||||
|
||||
?- zip(Xs, Ys, [1,4,2,5,3,6]).
|
||||
Xs = [1,2,3], Ys = [4,5,6].
|
||||
|
||||
% 3. Demonstrate checking 1 quad, the top two elements of a QAs list.
|
||||
check_qu_ad(Q-QVN, A-AVN) :-
|
||||
Q = ?-(G),
|
||||
phrase(portray_clause_(Q), LitQ), % NB: LitQ terminates w/ newline
|
||||
format("% CHECKING.. ",[]),
|
||||
( A == true -> call(G)
|
||||
; A == false -> ( call(G) -> false
|
||||
; true
|
||||
)
|
||||
; phrase(unconj(A), As) ->
|
||||
( length(As, N),
|
||||
n_answers(N, A, AVN, ADs),
|
||||
n_answers(N, G, QVN, Answers),
|
||||
maplist(contains, ADs, Answers)
|
||||
)
|
||||
; % Otherwise, we have the ',' case of a solitary answer
|
||||
call(G),
|
||||
call(A),
|
||||
QVN == AVN
|
||||
),
|
||||
format("~s", [LitQ]).
|
||||
|
||||
% Answer-description AD (qua set-of-bindings) contains Answer.
|
||||
contains(AD, Answer) :- append(Answer, _, AD).
|
||||
|
||||
?- contains(['Xs'=[C],'L'=1,'_A'=C,'_B'=D], ['Xs'=[A],'L'=1]).
|
||||
C = A.
|
||||
|
||||
?- check_qu_ad((?-length(_F,_G))-['Xs'=_F,'L'=_G],(_H=[],_I=0;_H=[_J],_I=1;_H=[_J,_K],_I=2;...)-['Xs'=_H,'L'=_I,'_A'=_J,'_B'=_K]).
|
||||
% CHECKING.. (?-length(A,B)).
|
||||
_F = [_A,_B], _G = 2, _H = [_J,_K], _I = 2.
|
||||
|
||||
% Unravel the nested (;)/1 applications of multiple-AD structures.
|
||||
unconj(Conj) --> { Conj = (Elt;Conj_) },
|
||||
[Elt],
|
||||
unconj(Conj_).
|
||||
unconj(...) --> [].
|
||||
|
||||
?- phrase(unconj((_H=[],_I=0;_H=[_J],_I=1;_H=[_J,_K],_I=2;...)), List).
|
||||
List = [(_H=[],_I=0),(_H=[_J],_I=1),(_H=[_J,_K],_I=2)].
|
||||
|
||||
empty_anstack :-
|
||||
( retract('$anstack'(_)), fail
|
||||
; asserta('$anstack'([]))
|
||||
).
|
||||
|
||||
push(VN) :-
|
||||
retract('$anstack'(As)),
|
||||
asserta('$anstack'([VN|As])).
|
||||
|
||||
backtrack(N) :-
|
||||
( '$anstack'(Ans),
|
||||
length(Ans, N) -> true
|
||||
; fail
|
||||
).
|
||||
|
||||
n_answers(N, G, VN, ADs) :-
|
||||
must_be(integer, N),
|
||||
( N > 0 -> n_answers_(N, G, VN, ADs)
|
||||
; domain_error(not_less_than_zero, N, n_answers/4)
|
||||
).
|
||||
|
||||
n_answers_(N, G, VN, ADs) :-
|
||||
empty_anstack,
|
||||
call(G), push(VN),
|
||||
backtrack(N),
|
||||
!,
|
||||
retract('$anstack'(As)),
|
||||
reverse(As, ADs).
|
||||
|
||||
?- n_answers(3, length(Xs, L), ('Xs'=Xs,'Len'=L), ADs).
|
||||
Xs = [_A,_B], L = 2, ADs = [('Xs'=[],'Len'=0),('Xs'=[_C],'Len'=1),('Xs'=[_D,_E],'Len'=2)].
|
||||
257
src/lib/numerics/special_functions.pl
Normal file
257
src/lib/numerics/special_functions.pl
Normal file
@@ -0,0 +1,257 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2025 by David C. Norris (david@precisionmethods.guru)
|
||||
As with all things floating-point, use at your own risk.
|
||||
Part of Scryer Prolog.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/** Special math functions in the Error, Gamma and Beta families
|
||||
|
||||
The underlying Rust implementations come from the
|
||||
[puruspe](https://docs.rs/puruspe/latest/puruspe/) crate.
|
||||
*/
|
||||
|
||||
:- module(special_functions, [
|
||||
erf/2
|
||||
,erfc/2
|
||||
,inverf/2
|
||||
,inverfc/2
|
||||
,gamma/2
|
||||
,gamma/3
|
||||
,gamma_P_Q/4
|
||||
,invgammp/3
|
||||
,log_gamma/2
|
||||
,beta/3
|
||||
,betai/4
|
||||
,invbetai/4
|
||||
,test/2
|
||||
,test_special_functions/0
|
||||
,try_falsify/1
|
||||
,witness/1
|
||||
]).
|
||||
|
||||
:- use_module(library(numerics/testutils)).
|
||||
|
||||
%% erf(+Xexpr, -Erf)
|
||||
%
|
||||
% X is Xexpr ∈ ℝ, Erf is erf(X).
|
||||
%
|
||||
% [DLMF §7.2.1](https://dlmf.nist.gov/7.2#E1),
|
||||
% [`puruspe::error::erf`](https://docs.rs/puruspe/latest/puruspe/error/fn.erf.html)
|
||||
erf(Xexpr, Erf) :-
|
||||
X is Xexpr,
|
||||
builtins:must_be_number(X, erf/2),
|
||||
'$erf'(X, Erf).
|
||||
|
||||
% Demonstrate the roots of x - erf(x))
|
||||
?- X0 = 0.6174468790806071, erf(X0, X0), _X0 is -X0, erf(_X0, _X0).
|
||||
X0 = 0.6174468790806071, _X0 = -0.6174468790806071.
|
||||
|
||||
% erf is an odd function:
|
||||
?- try_falsify(odd_t(erf, real(_))).
|
||||
false.
|
||||
|
||||
% Another way to say the same thing..
|
||||
?- witness(odd_t(erf, real(_), false)).
|
||||
false.
|
||||
|
||||
% ..and yet one more:
|
||||
?- witness((real(X), erf(X,Erf), erf(-X,_Erf), abs(Erf+_Erf) > 0)).
|
||||
false.
|
||||
|
||||
%% erfc(+X, -Erfc)
|
||||
%
|
||||
% Erfc is erfc(X) for X ∈ ℝ.
|
||||
%
|
||||
% [DLMF §7.2.2](https://dlmf.nist.gov/7.2#E2),
|
||||
% [`puruspe::error::erfc`](https://docs.rs/puruspe/latest/puruspe/error/fn.erfc.html)
|
||||
erfc(X, Erfc) :-
|
||||
builtins:must_be_number(X, erfc/2),
|
||||
'$erfc'(X, Erfc).
|
||||
|
||||
?- real(X), erf(X, Erf), erfc(X, Erfc), abs(Erf+Erfc-1) > epsilon.
|
||||
false.
|
||||
|
||||
%% inverf(+ErfX, -X)
|
||||
%
|
||||
% X is erf⁻¹(ErfX) for ErfX ∈ (-1,1).
|
||||
inverf(ErfX, X) :-
|
||||
builtins:must_be_number(ErfX, inverf/2),
|
||||
'$inverf'(ErfX, X).
|
||||
|
||||
?- try_falsify(δ_inverses_t(40*epsilon, erf, inverf, interval(-2,2,_))).
|
||||
false.
|
||||
|
||||
%% inverfc(+ErfcX, -X)
|
||||
%
|
||||
% X is erfc⁻¹(ErfcX) for ErfcX ∈ (0,2).
|
||||
inverfc(ErfcX, X) :-
|
||||
builtins:must_be_number(ErfcX, inverfc/2),
|
||||
'$inverfc'(ErfcX, X).
|
||||
|
||||
?- try_falsify(δ_inverses_t(40*epsilon, erfc, inverfc, interval(-2,2,_))).
|
||||
false.
|
||||
|
||||
%% gamma(+X, -Gamma)
|
||||
%
|
||||
% Gamma is Γ(X), the [ordinary] gamma function evaluated at X ∈ ℝ.
|
||||
%
|
||||
% [DLMF §5.2.1](https://dlmf.nist.gov/5.2#E1)
|
||||
% [`puruspe::gamma::gamma`](https://docs.rs/puruspe/latest/puruspe/gamma/fn.gamma.html)
|
||||
gamma(X, Gamma) :-
|
||||
builtins:must_be_number(X, gamma/2),
|
||||
'$gamma'(X, Gamma).
|
||||
|
||||
% Γ(n+1) ≡ n!
|
||||
?- N = 10, N1 is N+1, gamma(N1, ΓN1), int_realfact(N, Γ11).
|
||||
N = 10, N1 = 11, ΓN1 = 3628800.0, Γ11 = 3628800.0.
|
||||
|
||||
|
||||
%% gamma(+A, +X, -Gamma)
|
||||
%
|
||||
% Gamma is Γ(A,X), the upper incomplete gamma function, where A > 0
|
||||
% is the shape parameter and X ≥ 0 is the lower limit of integration.
|
||||
%
|
||||
% [DLMF §8.2.2](https://dlmf.nist.gov/8.2#E2),
|
||||
gamma(A, X, Gamma) :-
|
||||
builtins:must_be_number(A, gammq/3),
|
||||
builtins:must_be_number(X, gammq/3),
|
||||
'$gammq'(A, X, Q),
|
||||
gamma(A, GammaA),
|
||||
Gamma is Q*GammaA.
|
||||
|
||||
%% gamma_P_Q(+A, +X, -P, -Q)
|
||||
%
|
||||
% For shape parameter A > 0 and lower limit of integration X ≥ 0,
|
||||
%
|
||||
% * P is γ(A,X)/Γ(X), the regularized _lower_ incomplete gamma function, and
|
||||
%
|
||||
% * Q is Γ(A,X)/Γ(X), the regularized _upper_ incomplete gamma function.
|
||||
%
|
||||
% [DLMF §8.2.4](https://dlmf.nist.gov/8.2#E4),
|
||||
% [`puruspe::gammp::gammp`](https://docs.rs/puruspe/latest/puruspe/gamma/fn.gammp.html),
|
||||
% [`puruspe::gammp::gammq`](https://docs.rs/puruspe/latest/puruspe/gamma/fn.gammq.html)
|
||||
gamma_P_Q(A, X, P, Q) :-
|
||||
builtins:must_be_number(A, gamma_P_Q/4),
|
||||
builtins:must_be_number(X, gamma_P_Q/4),
|
||||
'$gammp'(A, X, P),
|
||||
'$gammq'(A, X, Q).
|
||||
|
||||
% P + Q ≈ 1
|
||||
?- gamma_P_Q(1.2, 2.3, P, Q), abs(P + Q - 1) < epsilon.
|
||||
P = 0.8621845438106976, Q = 0.1378154561893024.
|
||||
|
||||
%% invgammp(+A, +P, -X)
|
||||
%
|
||||
% Given shape parameter A > 0 and probability P ∈ [0,1),
|
||||
%
|
||||
% X is the unique solution of P = P(A,X), where P(-,-) is the
|
||||
% regularized lower incomplete gamma function.
|
||||
%
|
||||
% [`puruspe::gamma::invgammp`](https://docs.rs/puruspe/latest/puruspe/gamma/fn.invgammp.html)
|
||||
invgammp(A, P, X) :-
|
||||
builtins:must_be_number(A, invgammp/3),
|
||||
builtins:must_be_number(P, invgammp/3),
|
||||
'$invgammp'(P, A, X).
|
||||
|
||||
?- A = 1.5, P = 0.7, invgammp(A, P, X), gamma_P_Q(A, X, P_, _), abs(P-P_) < epsilon.
|
||||
A = 1.5, P = 0.7, X = 1.8324353915624363, P_ = 0.7000000000000001.
|
||||
|
||||
%% log_gamma(+X, -LogGamma)
|
||||
%
|
||||
% LogGamma is ln(Γ(X)), the natural logarithm of Γ(X).
|
||||
%
|
||||
% [`puruspe::gamma::ln_gamma`](https://docs.rs/puruspe/latest/puruspe/gamma/fn.ln_gamma.html)
|
||||
log_gamma(X, LnGamma) :-
|
||||
builtins:must_be_number(X, log_gamma/2),
|
||||
'$ln_gamma'(X, LnGamma).
|
||||
|
||||
%% beta(+X, +Y, -B)
|
||||
%
|
||||
% B is B(X,Y) ≡ Γ(X)*Γ(Y)/Γ(X+Y)
|
||||
%
|
||||
% [DLMF §5.12.1](https://dlmf.nist.gov/5.12#E1)
|
||||
% [`puruspe::beta::beta`](https://docs.rs/puruspe/latest/puruspe/beta/fn.beta.html)
|
||||
beta(X, Y, B) :-
|
||||
builtins:must_be_number(X, beta/3),
|
||||
builtins:must_be_number(Y, beta/3),
|
||||
'$beta'(X, Y, B).
|
||||
|
||||
%% betai(+A, +B, +X, -Ix)
|
||||
%
|
||||
% Given:
|
||||
%
|
||||
% * shape parameters A > 0 and B > 0,
|
||||
% * upper limit of integration X ∈ [0,1],
|
||||
%
|
||||
% Ix is Iₓ(A,B) ≡ B(X;A,B)/B(A,B), the regularized incomplete beta function;
|
||||
%
|
||||
% [DLMF §8.17.2](https://dlmf.nist.gov/8.17#E2),
|
||||
% [`puruspe::beta::betai`](https://docs.rs/puruspe/latest/puruspe/beta/fn.betai.html)
|
||||
betai(A, B, X, Ix) :-
|
||||
builtins:must_be_number(A, betai/4),
|
||||
builtins:must_be_number(B, betai/4),
|
||||
builtins:must_be_number(X, betai/4),
|
||||
'$betai'(A, B, X, Ix).
|
||||
|
||||
%% invbetai(+A, +B, +P, -X)
|
||||
%
|
||||
% Given:
|
||||
%
|
||||
% * shape parameters A > 0 and B > 0,
|
||||
% * probability P ∈ [0,1],
|
||||
%
|
||||
% X ∈ [0,1] is the unique solution of P = Iₓ(A,B) ≡ B(X;A,B)/B(A,B).
|
||||
%
|
||||
% [`puruspe::beta::invbetai`](https://docs.rs/puruspe/latest/puruspe/beta/fn.invbetai.html)
|
||||
invbetai(A, B, P, X) :-
|
||||
builtins:must_be_number(A, invbetai/4),
|
||||
builtins:must_be_number(B, invbetai/4),
|
||||
builtins:must_be_number(P, invbetai/4),
|
||||
'$invbetai'(P, A, B, X).
|
||||
|
||||
|
||||
% ============================== TESTS ==============================
|
||||
|
||||
%% test_special_functions
|
||||
%
|
||||
% Run all tests defined in this module. (These tests _succeed_ when
|
||||
% they find counterexamples, so the 'desirable' result is `false`.)
|
||||
test_special_functions :-
|
||||
format("Seeking counterexamples to assertions:~n", []),
|
||||
test(T, G), format("% ~s ~n", [T]),
|
||||
call(G).
|
||||
|
||||
% We default to 1M falsification attempts per assertion, and -- more
|
||||
% importantly -- use the (unexported) testutils:try_falsify_/2, to
|
||||
% avoid testutils:reproducibly/0 fixing an RNG seed. Thus we obtain
|
||||
% truly pseudorandom tests untainted by seed-hacking impropriety.
|
||||
:- meta_predicate(try_falsify(1)).
|
||||
try_falsify(G) :- testutils:try_falsify_(10^6, G).
|
||||
|
||||
% A unary witness/1 predicate similarly renders queries more concise.
|
||||
:- meta_predicate(witness(0)).
|
||||
witness(G) :- testutils:witness(10^6, G).
|
||||
|
||||
:- discontiguous(test/2).
|
||||
|
||||
%% test(+Name, ?Goal)
|
||||
%
|
||||
% Tests have the signature established by @bakaq's test_framework,
|
||||
% each with a user-facing string Name, and a Goal which serves as an
|
||||
% _assertion_ by succeeding iff the Name'd desirable property holds.
|
||||
test("erf is odd", try_falsify(odd_t(erf, real(_)))).
|
||||
|
||||
test("pos root of erf(x)-x", \+ (X0 = 0.6174468790806071, erf(X0, X0))).
|
||||
|
||||
test("erfc ≈ 1 - erf", try_falsify(erf_plus_erfc_unity_t(real(_)))).
|
||||
|
||||
erf_plus_erfc_unity_t(Any, T) :-
|
||||
call_free(Any, X), erf(X, Erf), erfc(X, Erfc),
|
||||
( abs(Erf + Erfc - 1) < epsilon -> T = true
|
||||
; T = false
|
||||
).
|
||||
|
||||
test("inverf ≈ erf⁻¹",
|
||||
try_falsify(δ_inverses_t(40*epsilon, erf, inverf, interval(-2,2,_)))).
|
||||
|
||||
test("('false' is good)", false).
|
||||
147
src/lib/numerics/testutils.pl
Normal file
147
src/lib/numerics/testutils.pl
Normal file
@@ -0,0 +1,147 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2025 by David C. Norris (david@precisionmethods.guru)
|
||||
As with all things floating-point, use at your own risk.
|
||||
Part of Scryer Prolog.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/** Utility predicates for testing numerics
|
||||
|
||||
*/
|
||||
|
||||
:- module(testutils, [
|
||||
try_falsify/2
|
||||
,witness/2
|
||||
,real/1
|
||||
,posreal/1
|
||||
,interval/3
|
||||
,call_free/2
|
||||
,odd_t/3
|
||||
,δ_inverses_t/5
|
||||
,int_realfact/2
|
||||
]).
|
||||
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(random)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(format)).
|
||||
|
||||
%% real(-X)
|
||||
%
|
||||
% X = tan(U) for uniform U ~ U[-π,π).
|
||||
real(X) :-
|
||||
random(U),
|
||||
X is tan(pi*(U - 0.5)).
|
||||
|
||||
%% posreal(-X)
|
||||
%
|
||||
% X = 1/U for uniform U ~ U[0,1).
|
||||
posreal(X) :-
|
||||
random(U),
|
||||
X is 1/U - 1.
|
||||
|
||||
%% interval(+A, +B, -X)
|
||||
%
|
||||
% X ~ U[A,B).
|
||||
interval(A, B, X) :-
|
||||
random(U),
|
||||
X is A + (B-A)*U.
|
||||
|
||||
:- meta_predicate(try_falsify(+, 1)).
|
||||
|
||||
% NOTE: NON-reproducible tests would in fact be MORE STRINGENT,
|
||||
% creating opportunities to detect rare error cases over time.
|
||||
reproducibly :- set_random(seed(2025)).
|
||||
|
||||
%% try_falsify(+IntExpr, ?G_1)
|
||||
%
|
||||
% Make (N is IntExpr) attempts to falsify the partial goal G/1,
|
||||
% reporting the first counterexample found.
|
||||
try_falsify(N, G_1) :- reproducibly -> try_falsify_(N, G_1).
|
||||
try_falsify_(IntExpr, G_1) :- N is IntExpr, must_be(integer, N), N > 0,
|
||||
( call(G_1, false) -> counterexample(G_1)
|
||||
; N_ is N - 1,
|
||||
try_falsify_(N_, G_1)
|
||||
).
|
||||
|
||||
%% call_free(?G, -V)
|
||||
%
|
||||
% Call goal G, the single free variable of which is bound to V.
|
||||
call_free(G, V) :- term_variables(G, [V]), call(G).
|
||||
|
||||
:- meta_predicate(odd_t(2, 0, ?)).
|
||||
|
||||
:- meta_predicate(witness(+, 0)).
|
||||
|
||||
%% witness(+N, ?OhNo)
|
||||
%
|
||||
% Make N attempts to satisfy the goal OhNo, reporting the first
|
||||
% counterexample found.
|
||||
witness(N, OhNo) :- N > 0,
|
||||
( call(OhNo) -> counterexample(OhNo)
|
||||
; N_ is N - 1,
|
||||
witness(N_, OhNo)
|
||||
).
|
||||
|
||||
% The goal below is obviously _designed_ to succeed 10% of the time,
|
||||
% giving us plenty of chances to see 'counterexamples':
|
||||
?- witness(10, (random(X), format("% X = ~f~n", [X]), X > 0.9)).
|
||||
% X = 0.8084510175379878
|
||||
% X = 0.3173976152322231
|
||||
% X = 0.6016479707924276
|
||||
% X = 0.2782828276608216
|
||||
% X = 0.5737059731916494
|
||||
% X = 0.4300520177737992
|
||||
% X = 0.5411038305190763
|
||||
% X = 0.6901983097300066
|
||||
% X = 0.05013429563996663
|
||||
% X = 0.7321078902421636
|
||||
false.
|
||||
% X = 0.6401599325865659
|
||||
% X = 0.7024957773981413
|
||||
% X = 0.5798797162672757
|
||||
% X = 0.3107013295892864
|
||||
% X = 0.3792925200660049
|
||||
% X = 0.3424593598278811
|
||||
% X = 0.0589899862019303
|
||||
% X = 0.9692529829158145
|
||||
% COUNTEREXAMPLE: user:random(0.9692529829158145),(current_output(user_output),pio:phrase_to_stream(format:format_([%, ,X, ,=, ,~,f,~,n],[0.9692529829158145]),user_output),flush_output(user_output)),0.9692529829158145>0.9
|
||||
X = 0.9692529829158145.
|
||||
|
||||
%% odd_t(+F_2, +Any, ?T)
|
||||
%
|
||||
% T is the truth-value from testing that function F_2 is
|
||||
% [odd](https://en.wikipedia.org/wiki/Even_and_odd_functions) at a
|
||||
% value X obtained via call_free(Any, X).
|
||||
odd_t(F, Any, T) :-
|
||||
( call_free(Any, X),
|
||||
_X is -X,
|
||||
call(F, X, Fx),
|
||||
call(F, _X, _Fx),
|
||||
_Fx is -Fx -> T = true
|
||||
; T = false
|
||||
).
|
||||
|
||||
:- meta_predicate(δ_inverses_t(?, 2, 2, 0, ?)).
|
||||
|
||||
%% δ_inverses_t(+Δ, +F_2, +Finv_2, +Any, ?T)
|
||||
%
|
||||
% For given functions F_2 and Finv_2, T is the truth-value from
|
||||
% testing that Finv_2 ∘ F_2 is within Δ of the identity at a value X
|
||||
% obtained via call_free(Any, X).
|
||||
δ_inverses_t(Δ, F, Finv, Any, T) :-
|
||||
( call_free(Any, X),
|
||||
call(F, X, Fx),
|
||||
call(Finv, Fx, X_),
|
||||
( abs(X - X_) < Δ -> T = true
|
||||
; T = false
|
||||
)
|
||||
).
|
||||
|
||||
%% int_realfact(+N, -FactN)
|
||||
%
|
||||
% FactN is floating-point N!
|
||||
int_realfact(N, FactN) :-
|
||||
N > 0, N_ is N - 1, int_realfact(N_, FactN_), FactN is N*FactN_.
|
||||
int_realfact(0, 1.0).
|
||||
|
||||
counterexample(G) :- format("% COUNTEREXAMPLE: ~w~n", [G]).
|
||||
@@ -3,11 +3,16 @@
|
||||
% module resolution operator.
|
||||
:- op(600, xfy, :).
|
||||
|
||||
:- op(1199, fx, meta_predicate).
|
||||
|
||||
/* this is an implementation specific declarative operator used to implement call_with_inference_limit/3
|
||||
and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice
|
||||
instructions are unchanged. */
|
||||
% Implementation specific declarative operator used to implement
|
||||
% call_with_inference_limit/3 and setup_call_cleanup/3. Compiler switches
|
||||
% to the default trust_me, retry_me_else and some other instructions for all
|
||||
% predicates that are marked with it. Indexing choice instructions are unchanged.
|
||||
%
|
||||
% Implementation details:
|
||||
% Default instructions are not subject to inference counting, so their
|
||||
% execution will not be considered if they happen to be called by
|
||||
% call_with_inference_limit/3.
|
||||
%
|
||||
:- op(700, fx, non_counted_backtracking).
|
||||
|
||||
% arithmetic operators.
|
||||
@@ -19,7 +24,6 @@
|
||||
:- op(200, xfy, ^).
|
||||
:- op(500, yfx, /\).
|
||||
:- op(500, yfx, \/).
|
||||
:- op(500, yfx, xor).
|
||||
:- op(400, yfx, div).
|
||||
:- op(400, yfx, //).
|
||||
:- op(400, yfx, rdiv).
|
||||
@@ -60,70 +64,70 @@
|
||||
:- op(1200, xfx, -->).
|
||||
|
||||
% meta_predicate declarations for call/{1, 66}.
|
||||
:- meta_predicate call(0).
|
||||
:- meta_predicate call(1, ?).
|
||||
:- meta_predicate call(2, ?, ?).
|
||||
:- meta_predicate call(3, ?, ?, ?).
|
||||
:- meta_predicate call(4, ?, ?, ?, ?).
|
||||
:- meta_predicate call(5, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(6, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(7, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(8, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(9, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(10, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(11, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(12, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(13, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(14, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(15, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(16, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(17, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(18, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(19, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(20, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(21, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(22, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(23, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(24, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(25, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(26, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(27, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(28, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(29, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(30, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(31, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(32, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(33, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(34, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(35, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(36, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(37, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(38, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(39, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(40, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(41, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(42, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(43, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(44, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(45, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(46, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(47, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(48, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(49, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(50, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(51, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(52, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(53, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(54, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(55, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(56, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(57, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(58, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(59, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(60, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(60, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(61, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(62, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(63, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(64, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate call(65, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
:- meta_predicate(call(0)).
|
||||
:- meta_predicate(call(1, ?)).
|
||||
:- meta_predicate(call(2, ?, ?)).
|
||||
:- meta_predicate(call(3, ?, ?, ?)).
|
||||
:- meta_predicate(call(4, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(5, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(6, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(7, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(8, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(9, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(10, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(11, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(12, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(13, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(14, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(15, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(16, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(17, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(18, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(19, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(20, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(21, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(22, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(23, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(24, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(25, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(26, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(27, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(28, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(29, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(30, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(31, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(32, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(33, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(34, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(35, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(36, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(37, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(38, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(39, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(40, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(41, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(42, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(43, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(44, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(45, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(46, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(47, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(48, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(49, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(50, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(51, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(52, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(53, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(54, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(55, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(56, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(57, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(58, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(59, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(60, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(60, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(61, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(62, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(63, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(64, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
:- meta_predicate(call(65, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
map_list_to_pairs/3]).
|
||||
|
||||
|
||||
:- meta_predicate map_list_to_pairs(2, ?, ?).
|
||||
:- meta_predicate(map_list_to_pairs(2, ?, ?)).
|
||||
|
||||
%% pairs_keys_values(?Pairs, ?Keys, ?Values)
|
||||
%
|
||||
|
||||
@@ -39,7 +39,8 @@
|
||||
% represented as a list of characters.
|
||||
|
||||
phrase_from_stream(GRBody, Stream) :-
|
||||
stream_to_lazy_list(Stream, Ls),
|
||||
stream_property(Stream, reposition(Reposition)),
|
||||
stream_to_lazy_list(Reposition, Stream, Ls),
|
||||
phrase(GRBody, Ls).
|
||||
|
||||
%% phrase_from_file(+GRBody, +File)
|
||||
@@ -64,7 +65,7 @@ phrase_from_file(NT, File, Options) :-
|
||||
; Type = text
|
||||
),
|
||||
setup_call_cleanup(
|
||||
open(File, read, Stream, Options),
|
||||
open(File, read, Stream, [reposition(true)|Options]),
|
||||
phrase_from_stream(NT, Stream),
|
||||
close(Stream)
|
||||
)
|
||||
@@ -73,34 +74,41 @@ phrase_from_file(NT, File, Options) :-
|
||||
% How many chars to read from stream and buffer in each step
|
||||
chars_to_read(4096).
|
||||
|
||||
stream_to_lazy_list(Stream, Ls) :-
|
||||
get_stream_buffer_position(Stream, Pos),
|
||||
freeze(Ls, render_step(Stream, Pos, Ls)).
|
||||
stream_to_lazy_list(Reposition, Stream, Ls) :-
|
||||
get_stream_buffer_position(Reposition, Stream, Pos),
|
||||
freeze(Ls, render_step(Reposition, Stream, Pos, Ls)).
|
||||
|
||||
render_step(Stream, Pos, Ls) :-
|
||||
set_stream_buffer_position(Stream, Pos),
|
||||
( buffer_at_end_of_stream(Stream) ->
|
||||
render_step(Reposition, Stream, Pos, Ls) :-
|
||||
set_stream_buffer_position(Reposition, Stream, Pos),
|
||||
( buffer_at_end_of_stream(Reposition, Stream) ->
|
||||
Ls = []
|
||||
; chars_to_read(CharsToRead),
|
||||
buffer_get_n_chars(Stream, CharsToRead, Chars),
|
||||
buffer_get_n_chars(Reposition, Stream, CharsToRead, Chars),
|
||||
partial_string(Chars, Ls, Ls0),
|
||||
stream_to_lazy_list(Stream, Ls0)
|
||||
stream_to_lazy_list(Reposition, Stream, Ls0)
|
||||
).
|
||||
|
||||
buffer_at_end_of_stream(Stream) :-
|
||||
buffer_at_end_of_stream(true, Stream) :- at_end_of_stream(Stream).
|
||||
buffer_at_end_of_stream(false, Stream) :-
|
||||
stream_bufferids(Stream, _, BufferPosId, _),
|
||||
bb_get(BufferPosId, Pos),
|
||||
Pos = eof.
|
||||
|
||||
get_stream_buffer_position(Stream, Pos) :-
|
||||
get_stream_buffer_position(true, Stream, Pos) :-
|
||||
stream_property(Stream, position(Pos)).
|
||||
get_stream_buffer_position(false, Stream, Pos) :-
|
||||
stream_bufferids(Stream, _, BufferPosId, _),
|
||||
bb_get(BufferPosId, Pos).
|
||||
|
||||
set_stream_buffer_position(Stream, Pos) :-
|
||||
set_stream_buffer_position(true, Stream, Pos) :-
|
||||
set_stream_position(Stream, Pos).
|
||||
set_stream_buffer_position(false, Stream, Pos) :-
|
||||
stream_bufferids(Stream, _, BufferPosId, _),
|
||||
bb_put(BufferPosId, Pos).
|
||||
|
||||
buffer_get_n_chars(Stream, N, Chars) :-
|
||||
buffer_get_n_chars(true, Stream, N, Chars) :-
|
||||
get_n_chars(Stream, N, Chars).
|
||||
buffer_get_n_chars(false, Stream, N, Chars) :-
|
||||
stream_bufferids(Stream, BufferId, BufferPosId, BufferLenId),
|
||||
buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N),
|
||||
bb_get(BufferId, Buffer),
|
||||
|
||||
224
src/lib/process.pl
Normal file
224
src/lib/process.pl
Normal file
@@ -0,0 +1,224 @@
|
||||
:- module(process, [
|
||||
process_create/3,
|
||||
process_id/2,
|
||||
process_release/1,
|
||||
process_wait/2,
|
||||
process_wait/3,
|
||||
process_kill/1
|
||||
]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(lists), [member/2, maplist/2, maplist/3, append/2]).
|
||||
:- use_module(library(reif), [tfilter/3, memberd_t/3]).
|
||||
|
||||
|
||||
%% process_create(+Exe, +Args:list, +Options).
|
||||
%
|
||||
% Create a new process by executing the executable Exe and passing it the Arguments Args.
|
||||
%
|
||||
% Note: On windows please take note of [windows argument splitting](https://doc.rust-lang.org/std/process/index.html#windows-argument-splitting).
|
||||
%
|
||||
% Options is a list consisting of the following options:
|
||||
%
|
||||
% * `cwd(+Path)` Set the processes working directory to `Path`
|
||||
% * `process(-Process)` `Process` will be assigned a process handle for the spawned process
|
||||
% * `env(+List)` Don't inherit environment variables and set the variables defined in `List`
|
||||
% * `environment(+List)` Inherit environment variables and set/override the variables defined in `List`
|
||||
% * `stdin(Spec)`, `stdout(Spec)` or `stderr(Spec)` defines how to redirect the spawned processes io streams
|
||||
%
|
||||
% The elements of `List` in `env(List)`/`environment(List)` List must be string pairs using `=/2`.
|
||||
% `env/1` and `environment/1` may not be both specified.
|
||||
%
|
||||
% The following stdio `Spec` are available:
|
||||
%
|
||||
% * `std` inherit the current processes original stdio streams (does currently not account for stdio being changed by `set_input` or `set_output`)
|
||||
% * `file(+Path)` attach the strea to the file at `Path`
|
||||
% * `null` discards writes and behaves as eof for read. Equivalent to using `file(/dev/null)`
|
||||
% * `pipe(-Steam)` create a new pipe and assigne one end to the created process and the other end to `Stream`
|
||||
%
|
||||
% Specifying an option multiple times is an error, when an option is not specified the following defaults apply:
|
||||
%
|
||||
% - `cwd(".")`
|
||||
% - `environment([])`
|
||||
% - `stdin(std)`, `stdout(std)`, `stderr(std)`
|
||||
%
|
||||
process_create(Exe, Args, Options) :- call_with_error_context(process_create_(Exe, Args, Options), predicate-process_create/3).
|
||||
|
||||
process_create_(Exe, Args, Options) :-
|
||||
must_be(chars, Exe),
|
||||
must_be(list, Args),
|
||||
maplist(must_be(chars), Args),
|
||||
must_be(list, Options),
|
||||
check_options(
|
||||
[
|
||||
option([stdin], valid_stdio, stdin(std), stdin(Stdin)),
|
||||
option([stdout], valid_stdio, stdout(std), stdout(Stdout)),
|
||||
option([stderr], valid_stdio, stderr(std), stderr(Stderr)),
|
||||
option([env, environment], valid_env, environment([]), Env),
|
||||
option([process], valid_uninit_process, process(_), process(Process)),
|
||||
option([cwd], valid_cwd, cwd("."), cwd(Cwd))
|
||||
],
|
||||
Options,
|
||||
process_create_option
|
||||
),
|
||||
Stdin =.. Stdin1,
|
||||
Stdout =.. Stdout1,
|
||||
Stderr =.. Stderr1,
|
||||
simplify_env(Env, Env1),
|
||||
'$process_create'(Exe, Args, Stdin1, Stdout1, Stderr1, Env1, Cwd, Process).
|
||||
|
||||
%% process_id(+Process, -Pid).
|
||||
%
|
||||
process_id(Process, Pid) :- call_with_error_context(process_id_(Process, Pid), predicate-process_id/2).
|
||||
|
||||
process_id_(Process, Pid) :-
|
||||
valid_process(Process),
|
||||
must_be(var, Pid),
|
||||
'$process_id'(Process, Pid).
|
||||
|
||||
%% process_wait(+Process, Status).
|
||||
%
|
||||
% See `process_create/3` with `Options = []`
|
||||
%
|
||||
process_wait(Process, Status) :- call_with_error_context(process_wait(Process, Status, []), predicate-process_wait/2).
|
||||
|
||||
|
||||
%% process_wait(+Process, Status, Options).
|
||||
%
|
||||
% Wait for the process behind the process handle `Process` to exit.
|
||||
%
|
||||
% When the process exits regulary `Status` will be unified with `exit(Exit)` where `Exit` is the processes exit code.
|
||||
% When the process exits was killed `Status` will be unified with `killed(Signal)` where `Signal` is the signal number that killed the process.
|
||||
% When the process doesn't exit before the timeout `Status` will be unified with `timeout`.
|
||||
%
|
||||
% `Options` is a a list of the following options
|
||||
%
|
||||
% * timeout(Timeout) supported values for `Timeout` are 0 or `infinite`
|
||||
%
|
||||
% Each options may be specified at most once, when an option is not specified the following defaults apply:
|
||||
%
|
||||
% - timeout(infinite)
|
||||
%
|
||||
process_wait(Process, Status, Options) :- call_with_error_context(process_wait_(Process, Status, Options), predicate-process_wait/3).
|
||||
|
||||
process_wait_(Process, Status, Options) :-
|
||||
valid_process(Process),
|
||||
check_options(
|
||||
[
|
||||
option([timeout], valid_timeout, timeout(infinite), timeout(Timeout))
|
||||
],
|
||||
Options,
|
||||
process_wait_option
|
||||
),
|
||||
'$process_wait'(Process, Exit, Timeout),
|
||||
Exit = Status.
|
||||
|
||||
valid_timeout(timeout(infinite)).
|
||||
valid_timeout(timeout(0)).
|
||||
|
||||
|
||||
%% process_kill(+Process).
|
||||
%
|
||||
% Kill the process using the process handle `Process`.
|
||||
% On Unix this sends SIGKILL.
|
||||
%
|
||||
% Only works for processes spawned with `process_create/3` that have not yet been release with `process_release/1`
|
||||
%
|
||||
process_kill(Process) :- call_with_error_context(process_kill_(Process), predicate-process_kill/1).
|
||||
|
||||
process_kill_(Process) :-
|
||||
valid_process(Process),
|
||||
'$process_kill'(Process).
|
||||
|
||||
%% process_release(+Process)
|
||||
%
|
||||
% wait for the process to exit (if not already) and release process handle `Process`
|
||||
%
|
||||
% It's an error if `Process` is not a valid process handle
|
||||
%
|
||||
process_release(Process) :- call_with_error_context(process_release_(Process), predicate-process_release/1).
|
||||
|
||||
process_release_(Process) :-
|
||||
valid_process(Process),
|
||||
process_wait(Process, _),
|
||||
'$process_release'(Process).
|
||||
|
||||
|
||||
must_be_known_options(Valid, Options, Domain) :- call_with_error_context(must_be_known_options_(Valid, [], Options, Domain),predicate-must_be_known_options/3).
|
||||
|
||||
must_be_known_options_(_, _, [], _).
|
||||
must_be_known_options_(Valid, Found, [X|XS], Domain) :-
|
||||
( functor(X, Option, 1) -> true
|
||||
; domain_error(Domain, X, [])
|
||||
) ,
|
||||
( member(Option, Found) -> domain_error(non_duplicate_options, Option , [])
|
||||
; member(Option, Valid) -> true
|
||||
; domain_error(Domain, Option, [])
|
||||
),
|
||||
must_be_known_options_(Valid, [Option | Found], XS, Domain).
|
||||
|
||||
check_options(KnownOptions, Options, Domain) :- call_with_error_context(check_options_(KnownOptions, Options, Domain), predicate-check_options/3).
|
||||
|
||||
check_options_(KnownOptions, Options, Domain) :-
|
||||
maplist(option_names, KnownOptions, Namess),
|
||||
append(Namess, Names),
|
||||
must_be_known_options(Names, Options, Domain),
|
||||
extract_options(KnownOptions, Options).
|
||||
|
||||
option_names(option(Names,_,_,_), Names).
|
||||
|
||||
extract_options(KnownOptions, Options) :- call_with_error_context(extract_options_(KnownOptions, Options), predicate-extract_options/2).
|
||||
|
||||
extract_options_([], _).
|
||||
extract_options_([X | XS], Options) :-
|
||||
option(Kinds, Pred, Default, Choice) = X,
|
||||
tfilter(find_option(Kinds), Options, Solutions),
|
||||
( Solutions = [] -> Choice = Default
|
||||
; Solutions = [Provided] -> functor(Pred, Name, Arity), ArityP1 is Arity+1, call_with_error_context(call(Pred, Provided),predicate-Name/ArityP1), Choice = Provided
|
||||
; domain_error(non_conflicting_options, Solutions, [])
|
||||
),
|
||||
extract_options_(XS, Options).
|
||||
|
||||
find_option(Names, Found, T) :-
|
||||
functor(Found, Name, 1),
|
||||
memberd_t(Name, Names, T).
|
||||
|
||||
valid_stdio(IO) :- arg(1, IO, Arg),
|
||||
( var(Arg) -> instantiation_error([])
|
||||
; valid_stdio_(Arg) -> true
|
||||
; domain_error(stdio_spec, Arg, [])
|
||||
).
|
||||
|
||||
valid_stdio_(std).
|
||||
valid_stdio_(null).
|
||||
valid_stdio_(pipe(Stream)) :- must_be(var, Stream).
|
||||
valid_stdio_(file(Path)) :- must_be(chars, Path).
|
||||
|
||||
valid_env(env(E)) :-
|
||||
must_be(list, E),
|
||||
( valid_env_(E) -> true
|
||||
; domain_error(process_create_option, env(E), [])
|
||||
).
|
||||
valid_env(environment(E)) :-
|
||||
must_be(list, E),
|
||||
( valid_env_(E) -> true
|
||||
; domain_error(process_create_option, environment(E), [])
|
||||
).
|
||||
|
||||
valid_env_([]).
|
||||
valid_env_([N=V|ES]) :-
|
||||
must_be(chars, N),
|
||||
must_be(chars, V),
|
||||
valid_env_(ES).
|
||||
|
||||
valid_uninit_process(process(Process)) :- must_be(var, Process).
|
||||
|
||||
valid_process(Process) :- var(Process) -> instantiation_error([]) ; true.
|
||||
|
||||
valid_cwd(cwd(Cwd)) :- must_be(chars, Cwd).
|
||||
|
||||
simplify_env(E, [Kind, Envs1]) :- E =.. [Kind, Envs], simplify_env_(Envs, Envs1).
|
||||
|
||||
simplify_env_([],[]).
|
||||
simplify_env_([N=V|E],[[N, V]|E1]) :- simplify_env_(E, E1).
|
||||
@@ -15,16 +15,14 @@ the random seed. This makes everything completely reproducible.
|
||||
% Succeeds with probability 0.5.
|
||||
maybe :- '$maybe'.
|
||||
|
||||
% The higher the precision, the slower it gets.
|
||||
random_number_precision(64).
|
||||
|
||||
%% random(-R).
|
||||
%
|
||||
% Generates a random floating number between 0 (inclusive) and 1 (exclusive).
|
||||
random(R) :-
|
||||
var(R),
|
||||
random_number_precision(N),
|
||||
rnd(N, R).
|
||||
N is 2^50,
|
||||
'$random_integer'(0, N, K),
|
||||
R is K/N.
|
||||
|
||||
%% random_integer(+Lower, +Upper, -R).
|
||||
%
|
||||
@@ -41,25 +39,10 @@ random_integer(Lower, Upper, R) :-
|
||||
type_error(integer, Lower, random_integer/3)
|
||||
; \+ integer(Upper) ->
|
||||
type_error(integer, Upper, random_integer/3)
|
||||
; Upper > Lower,
|
||||
random(R0),
|
||||
R is floor((Upper - Lower) * R0 + Lower)
|
||||
; Lower < Upper,
|
||||
'$random_integer'(Lower, Upper, R)
|
||||
).
|
||||
|
||||
rnd(N, R) :-
|
||||
rnd_(N, 0, R).
|
||||
|
||||
rnd_(0, R, R) :- !.
|
||||
rnd_(N, R0, R) :-
|
||||
maybe,
|
||||
!,
|
||||
N1 is N - 1,
|
||||
rnd_(N1, R0, R).
|
||||
rnd_(N, R0, R) :-
|
||||
N1 is N - 1,
|
||||
R1 is R0 + 1.0 / 2.0 ^ N,
|
||||
rnd_(N1, R1, R).
|
||||
|
||||
%% set_random(+Seed).
|
||||
%
|
||||
% Sets a seed that will be used for subsequent random generations in this library.
|
||||
|
||||
@@ -96,14 +96,14 @@ is_sgml_source([]).
|
||||
is_sgml_source([C|Cs]) :- must_be(chars, [C|Cs]).
|
||||
|
||||
load_structure_([], [], _, _).
|
||||
load_structure_([C|Cs], [E], Options, What) :-
|
||||
load_(What, [C|Cs], E, Options).
|
||||
load_structure_(file(Fs), [E], Options, What) :-
|
||||
load_structure_([C|Cs], [E|Es], Options, What) :-
|
||||
load_(What, [C|Cs], [E|Es], Options).
|
||||
load_structure_(file(Fs), [E|Es], Options, What) :-
|
||||
once(phrase_from_file(seq(Cs), Fs)),
|
||||
load_(What, Cs, E, Options).
|
||||
load_structure_(stream(Stream), [E], Options, What) :-
|
||||
load_(What, Cs, [E|Es], Options).
|
||||
load_structure_(stream(Stream), [E|Es], Options, What) :-
|
||||
get_n_chars(Stream, _, Cs),
|
||||
load_(What, Cs, E, Options).
|
||||
load_(What, Cs, [E|Es], Options).
|
||||
|
||||
load_(html, Cs, E, Options) :- '$load_html'(Cs, E, Options).
|
||||
load_(xml, Cs, E, Options) :- '$load_xml'(Cs, E, Options).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** Tabling, also called SLG resolution.
|
||||
|
||||
SLG resolution is an alternative execution strategy that sometimes
|
||||
helps to improve termination and performance characters of Prolog
|
||||
helps to improve termination and performance characteristics of Prolog
|
||||
predicates.
|
||||
|
||||
To enable this execution strategy for a Prolog predicate, add a
|
||||
@@ -93,7 +93,7 @@ table_and_status_for_variant(V,T,S) :-
|
||||
tbd_table_status(T,S).
|
||||
|
||||
|
||||
:- meta_predicate start_tabling(?, :).
|
||||
:- meta_predicate(start_tabling(?, :)).
|
||||
|
||||
start_tabling(Wrapper,Worker) :-
|
||||
put_new_trie_table_link,
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020-2023 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020-2024 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/** This library provides predicates for reasoning about time.
|
||||
*/
|
||||
|
||||
:- module(time, [max_sleep_time/1, sleep/1, time/1, current_time/1, format_time//2]).
|
||||
:- module(time, [max_sleep_time/1,
|
||||
sleep/1,
|
||||
time/1,
|
||||
current_time/1,
|
||||
format_time//2,
|
||||
statistics/2
|
||||
]).
|
||||
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(iso_ext)).
|
||||
@@ -91,9 +97,18 @@ sleep(T) :-
|
||||
).
|
||||
|
||||
|
||||
% '$cpu_now' can be replaced by statistics/2 once that is implemented.
|
||||
%% statistics(?Keyword, ?List)
|
||||
%
|
||||
% Preliminary support for statistics/2, yielding timing information.
|
||||
% The only supported `Keyword` is `runtime`. The first element of
|
||||
% `List` is the CPU time in milliseconds, the second element is
|
||||
% currently not supported.
|
||||
|
||||
:- meta_predicate time(0).
|
||||
statistics(runtime, [T,unsupported]) :-
|
||||
'$cpu_now'(T0),
|
||||
T is T0*1000.
|
||||
|
||||
:- meta_predicate(time(0)).
|
||||
|
||||
:- dynamic(time_id/1).
|
||||
:- dynamic(time_state/3).
|
||||
|
||||
@@ -19,6 +19,7 @@ Provides the predicate `when/2`.
|
||||
%% when(Condition, Goal).
|
||||
%
|
||||
% Executes Goal when Condition becomes true.
|
||||
% Condition may consist of `ground(T)`, `nonvar(T)`, `C1,C2`, `C1;C2`.
|
||||
when(Condition, Goal) :-
|
||||
( when_condition(Condition) ->
|
||||
( Condition ->
|
||||
|
||||
275
src/loader.pl
275
src/loader.pl
@@ -19,10 +19,9 @@ write_error(Error) :-
|
||||
% '$fetch_global_var' is the core system call of bb_get/2, but
|
||||
% bb_get may not exist when write_error is first called, so fall
|
||||
% back on '$fetch_global_var'.
|
||||
( '$fetch_global_var'('$first_answer', false) ->
|
||||
( '$fetch_global_var'('$answer_count', C), C =\= 0 ->
|
||||
true
|
||||
; write(' ') % if '$first_answer' isn't defined yet or true,
|
||||
% print indentation.
|
||||
; write(' ') % if still in the first answer print indentation.
|
||||
),
|
||||
( current_prolog_flag(double_quotes, chars) ->
|
||||
DQ = true
|
||||
@@ -106,7 +105,7 @@ run_initialization_goals(Module) :-
|
||||
; true
|
||||
).
|
||||
|
||||
:- meta_predicate success_or_warning(0).
|
||||
:- meta_predicate(success_or_warning(0)).
|
||||
|
||||
success_or_warning(Goal) :-
|
||||
( call(Goal) ->
|
||||
@@ -200,22 +199,61 @@ warn_about_singletons([Singleton|Singletons], LinesRead) :-
|
||||
).
|
||||
|
||||
|
||||
load_loop(Stream, Evacuable) :-
|
||||
stream_next_term(Stream, Term, LinesRead, Singletons) :-
|
||||
( '$devour_whitespace'(Stream) ->
|
||||
stream_property(Stream, position(position_and_lines_read(_, LinesRead))),
|
||||
read_term(Stream, Term, [singletons(Singletons)])
|
||||
; Term = end_of_file
|
||||
),
|
||||
).
|
||||
|
||||
load_loop(Stream, Evacuable) :-
|
||||
stream_next_term(Stream, Term, LinesRead, Singletons),
|
||||
( Term == end_of_file ->
|
||||
close(Stream),
|
||||
'$conclude_load'(Evacuable)
|
||||
; var(Term) ->
|
||||
instantiation_error(load/1)
|
||||
; Term = (?- _Query) ->
|
||||
devour_answer_descriptions(Stream, Evacuable)
|
||||
; LineNum is LinesRead + 1,
|
||||
warn_about_singletons(Singletons, LineNum),
|
||||
compile_term(Term, Evacuable),
|
||||
load_loop(Stream, Evacuable)
|
||||
).
|
||||
|
||||
devour_answer_descriptions(Stream, Evacuable) :-
|
||||
stream_next_term(Stream, Term, LinesRead, Singletons),
|
||||
( Term == end_of_file ->
|
||||
close(Stream),
|
||||
'$conclude_load'(Evacuable)
|
||||
; var(Term) ->
|
||||
instantiation_error(load/1)
|
||||
; Term = (?- _Query) ->
|
||||
devour_answer_descriptions(Stream, Evacuable)
|
||||
; answer_description(Term) ->
|
||||
devour_answer_descriptions(Stream, Evacuable)
|
||||
; warn_about_singletons(Singletons, LinesRead),
|
||||
compile_term(Term, Evacuable),
|
||||
load_loop(Stream, Evacuable)
|
||||
).
|
||||
|
||||
answer_description(true).
|
||||
answer_description(false).
|
||||
answer_description(_V=_T).
|
||||
answer_description((_A,_As)).
|
||||
answer_description((_Answer;_Answers)).
|
||||
answer_description(...).
|
||||
answer_description(loops).
|
||||
answer_description(throw(_Ball)).
|
||||
answer_description(error(_Error_Term, _Impdef)).
|
||||
answer_description(instantiation_error).
|
||||
answer_description(type_error(_Type,_Culprit)).
|
||||
answer_description(domain_error(_Domain, _Culprit)).
|
||||
answer_description(syntax_error(_Error)).
|
||||
answer_description(representation_error(_Flag)).
|
||||
answer_description(resource_error(_Ressource)).
|
||||
answer_description(uninstantiation_error(_Culprit)).
|
||||
answer_description('|'(_AD,_ADs)).
|
||||
|
||||
compile_term(Term, Evacuable) :-
|
||||
expand_terms_and_goals(Term, Terms),
|
||||
@@ -223,8 +261,8 @@ compile_term(Term, Evacuable) :-
|
||||
( var(Terms) ->
|
||||
instantiation_error(load/1)
|
||||
; Terms = [_|_] ->
|
||||
compile_dispatch_or_clause_on_list(Terms, Evacuable)
|
||||
; compile_dispatch_or_clause(Terms, Evacuable)
|
||||
compile_dispatch_or_clause_on_list(list(Term), Terms, Evacuable)
|
||||
; compile_dispatch_or_clause(term(Term), Terms, Evacuable)
|
||||
).
|
||||
|
||||
complete_partial_goal(N, HeadArg, InnerHeadArgs, SuppArgs, CompleteHeadArg) :-
|
||||
@@ -299,7 +337,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('$call'(loader:expand_goal(Body0, Target, Body1, HeadVars, [])),
|
||||
error(type_error(callable, Pred), _),
|
||||
( loader:print_goal_expansion_warning(Pred),
|
||||
builtins:(Body1 = Body0)
|
||||
@@ -309,7 +347,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('$call'(loader:expand_goal(Body0, Target, Body1, HeadVars, [])),
|
||||
error(type_error(callable, Pred), _),
|
||||
( loader:print_goal_expansion_warning(Pred),
|
||||
builtins:(Body1 = Body0)
|
||||
@@ -330,18 +368,18 @@ expand_terms_and_goals(Term, Terms) :-
|
||||
).
|
||||
|
||||
|
||||
compile_dispatch_or_clause_on_list([], Evacuable).
|
||||
compile_dispatch_or_clause_on_list([Term | Terms], Evacuable) :-
|
||||
compile_dispatch_or_clause(Term, Evacuable),
|
||||
compile_dispatch_or_clause_on_list(Terms, Evacuable).
|
||||
compile_dispatch_or_clause_on_list(OrigTerm, [], Evacuable).
|
||||
compile_dispatch_or_clause_on_list(OrigTerm, [Term | Terms], Evacuable) :-
|
||||
compile_dispatch_or_clause(OrigTerm, Term, Evacuable),
|
||||
compile_dispatch_or_clause_on_list(OrigTerm, Terms, Evacuable).
|
||||
|
||||
|
||||
compile_dispatch_or_clause(Term, Evacuable) :-
|
||||
compile_dispatch_or_clause(OrigTerm, Term, Evacuable) :-
|
||||
( var(Term) ->
|
||||
instantiation_error(load/1)
|
||||
; compile_dispatch(Term, Evacuable) ->
|
||||
'$flush_term_queue'(Evacuable)
|
||||
; compile_clause(Term, Evacuable)
|
||||
; compile_clause(OrigTerm, Term, Evacuable)
|
||||
).
|
||||
|
||||
|
||||
@@ -403,7 +441,7 @@ predicate_indicator_sequence(PI_Seq) :-
|
||||
)
|
||||
).
|
||||
|
||||
:- meta_predicate add_predicate_declaration(3, ?).
|
||||
:- meta_predicate(add_predicate_declaration(3, ?)).
|
||||
|
||||
add_predicate_declaration(Handler, Name/Arity) :-
|
||||
predicate_indicator(Name/Arity),
|
||||
@@ -467,39 +505,46 @@ compile_declaration(non_counted_backtracking(Name/Arity), Evacuable) :-
|
||||
; domain_error(not_less_than_zero, Arity, load/1)
|
||||
).
|
||||
|
||||
recompile_term(list(OrigTerm), Term, Evacuable) :-
|
||||
% since OrigTerm expanded to a list, its contents are considered a
|
||||
% unit to be compiled simultaneously, and so its clauses are not
|
||||
% re-expanded when their predecessors are compiled.
|
||||
compile_clause(list(OrigTerm), Term, Evacuable).
|
||||
recompile_term(term(OrigTerm), _Term, Evacuable) :-
|
||||
compile_term(OrigTerm, Evacuable).
|
||||
|
||||
compile_clause((Target:Head :- Body), Evacuable) :-
|
||||
compile_clause(OrigTerm, (Target:Head :- Body), Evacuable) :-
|
||||
!,
|
||||
functor(Head, Name, Arity),
|
||||
( '$is_consistent_with_term_queue'(Target, Name, Arity, Evacuable) ->
|
||||
'$scoped_clause_to_evacuable'(Target, (Head :- Body), Evacuable)
|
||||
; '$flush_term_queue'(Evacuable),
|
||||
compile_term((Target:Head :- Body), Evacuable)
|
||||
recompile_term(OrigTerm, (Target:Head :- Body), Evacuable)
|
||||
).
|
||||
compile_clause(Target:Head, Evacuable) :-
|
||||
compile_clause(OrigTerm, Target:Head, Evacuable) :-
|
||||
!,
|
||||
functor(Head, Name, Arity),
|
||||
( '$is_consistent_with_term_queue'(Target, Name, Arity, Evacuable) ->
|
||||
'$scoped_clause_to_evacuable'(Target, Head, Evacuable)
|
||||
; '$flush_term_queue'(Evacuable),
|
||||
compile_term(Target:Head, Evacuable)
|
||||
recompile_term(OrigTerm, Target:Head, Evacuable)
|
||||
).
|
||||
compile_clause((Head :- Body), Evacuable) :-
|
||||
compile_clause(OrigTerm, (Head :- Body), Evacuable) :-
|
||||
!,
|
||||
prolog_load_context(module, Target),
|
||||
functor(Head, Name, Arity),
|
||||
( '$is_consistent_with_term_queue'(Target, Name, Arity, Evacuable) ->
|
||||
'$clause_to_evacuable'((Head :- Body), Evacuable)
|
||||
; '$flush_term_queue'(Evacuable),
|
||||
compile_term((Head :- Body), Evacuable)
|
||||
recompile_term(OrigTerm, (Head :- Body), Evacuable)
|
||||
).
|
||||
compile_clause(Head, Evacuable) :-
|
||||
compile_clause(OrigTerm, Head, Evacuable) :-
|
||||
prolog_load_context(module, Target),
|
||||
functor(Head, Name, Arity),
|
||||
( '$is_consistent_with_term_queue'(Target, Name, Arity, Evacuable) ->
|
||||
'$clause_to_evacuable'(Head, Evacuable)
|
||||
; '$flush_term_queue'(Evacuable),
|
||||
compile_term(Head, Evacuable)
|
||||
recompile_term(OrigTerm, Head, Evacuable)
|
||||
).
|
||||
|
||||
|
||||
@@ -685,16 +730,21 @@ predicate_property(Callable, Property) :-
|
||||
).
|
||||
|
||||
strip_module(Goal, M, G) :-
|
||||
'$strip_module'(Goal, M, G).
|
||||
|
||||
'$strip_module'(Goal, MQ, G),
|
||||
( MQ = specified(M) ->
|
||||
true
|
||||
; MQ = unspecified,
|
||||
load_context(M)
|
||||
).
|
||||
|
||||
:- non_counted_backtracking strip_subst_module/4.
|
||||
|
||||
strip_subst_module(Goal, M1, M2, G) :-
|
||||
'$strip_module'(Goal, M2, G),
|
||||
( var(M2), \+ functor(Goal, (:), 2) ->
|
||||
M2 = M1
|
||||
; true
|
||||
'$strip_module'(Goal, MQ, G),
|
||||
( MQ = specified(M2) ->
|
||||
true
|
||||
; MQ = unspecified,
|
||||
M1 = M2
|
||||
).
|
||||
|
||||
/*
|
||||
@@ -783,7 +833,7 @@ qualified_spec((:)).
|
||||
qualified_spec(MS) :- integer(MS), MS >= 0.
|
||||
|
||||
|
||||
:- non_counted_backtracking expand_meta_predicate_subgoals/5.
|
||||
:- non_counted_backtracking expand_meta_predicate_subgoals/6.
|
||||
|
||||
expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars, TGs) :-
|
||||
( var(SG) ->
|
||||
@@ -880,9 +930,15 @@ expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals) :-
|
||||
UnexpandedGoals = ExpandedGoals
|
||||
; goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1),
|
||||
( Module \== user ->
|
||||
goal_expansion(UnexpandedGoals1, user, ExpandedGoals)
|
||||
; ExpandedGoals = UnexpandedGoals1
|
||||
goal_expansion(UnexpandedGoals1, user, Goals)
|
||||
; Goals = UnexpandedGoals1
|
||||
),
|
||||
( predicate_property(Module:Goals, meta_predicate(MetaSpecs0)),
|
||||
MetaSpecs0 =.. [_ | MetaSpecs] ->
|
||||
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, [], [])
|
||||
; ExpandedGoals = Goals
|
||||
)
|
||||
; ExpandedGoals = UnexpandedGoals1
|
||||
).
|
||||
|
||||
:- non_counted_backtracking transitive_goal/3.
|
||||
@@ -962,17 +1018,16 @@ thread_goals(Goals0, Goals1, Hole, Functor) :-
|
||||
% length(Args, N),
|
||||
% Head =.. [call, G | Args],
|
||||
% N1 is N + 1,
|
||||
% StripModule =.. ['$strip_module', G, M1, G1],
|
||||
% CallModule = call_module(G, M0, G0),
|
||||
% FastCall =.. ['$fast_call', G | Args],
|
||||
% PrepareCallClause =.. [ '$prepare_call_clause', G2, G1 | Args],
|
||||
% ModuleCall =.. ['$module_call', M2, G4],
|
||||
% PrepareCallClause =.. [ '$prepare_call_clause', G1, G0 | Args],
|
||||
% ModuleCall =.. ['$module_call', M2, G3],
|
||||
% Clauses = [(Head :- var(G),
|
||||
% instantiation_error(call/N1)),
|
||||
% (Head :- FastCall),
|
||||
% (Head :- StripModule,
|
||||
% PrepareCallClause,
|
||||
% expand_call_goal(G2, M1, G3),
|
||||
% strip_subst_module(G3, M1, M2, G4),
|
||||
% (Head :- PrepareCallClause,
|
||||
% expand_call_goal(G1, M1, G2),
|
||||
% strip_subst_module(G2, M1, M2, G3),
|
||||
% ModuleCall)].
|
||||
%
|
||||
% generate_call_forms :-
|
||||
@@ -989,15 +1044,21 @@ thread_goals(Goals0, Goals1, Hole, Functor) :-
|
||||
% The '$call' functor is an escape hatch from goal expansion. So far,
|
||||
% it is used only to avoid infinite recursion into expand_call_goal/3.
|
||||
|
||||
:- non_counted_backtracking call/1.
|
||||
call_module(G0, M, G1) :-
|
||||
'$strip_module'(G0, MQ, G1),
|
||||
( MQ = specified(M) ->
|
||||
true
|
||||
; load_context(M)
|
||||
).
|
||||
|
||||
:- non_counted_backtracking call/1.
|
||||
call(G) :-
|
||||
var(G),
|
||||
instantiation_error(call/1).
|
||||
call(G) :-
|
||||
'$fast_call'(G).
|
||||
call(G0) :-
|
||||
'$strip_module'(G0, M0, G1),
|
||||
call_module(G0, M0, G1),
|
||||
expand_call_goal(G1, M0, G2),
|
||||
strip_subst_module(G2, M0, M1, G3),
|
||||
'$module_call'(M1, G3).
|
||||
@@ -1009,7 +1070,7 @@ call(A,B) :-
|
||||
call(A,B) :-
|
||||
'$fast_call'(A,B).
|
||||
call(A,B) :-
|
||||
'$strip_module'(A,C,D),
|
||||
call_module(A,C,D),
|
||||
'$prepare_call_clause'(E,D,B),
|
||||
expand_call_goal(E,C,F),
|
||||
strip_subst_module(F,C,G,H),
|
||||
@@ -1022,7 +1083,7 @@ call(A,B,C) :-
|
||||
call(A,B,C) :-
|
||||
'$fast_call'(A,B,C).
|
||||
call(A,B,C) :-
|
||||
'$strip_module'(A,D,E),
|
||||
call_module(A,D,E),
|
||||
'$prepare_call_clause'(F,E,B,C),
|
||||
expand_call_goal(F,D,G),
|
||||
strip_subst_module(G,D,H,I),
|
||||
@@ -1035,7 +1096,7 @@ call(A,B,C,D) :-
|
||||
call(A,B,C,D) :-
|
||||
'$fast_call'(A,B,C,D).
|
||||
call(A,B,C,D) :-
|
||||
'$strip_module'(A,E,F),
|
||||
call_module(A,E,F),
|
||||
'$prepare_call_clause'(G,F,B,C,D),
|
||||
expand_call_goal(G,E,H),
|
||||
strip_subst_module(H,E,I,J),
|
||||
@@ -1048,7 +1109,7 @@ call(A,B,C,D,E) :-
|
||||
call(A,B,C,D,E) :-
|
||||
'$fast_call'(A,B,C,D,E).
|
||||
call(A,B,C,D,E) :-
|
||||
'$strip_module'(A,F,G),
|
||||
call_module(A,F,G),
|
||||
'$prepare_call_clause'(H,G,B,C,D,E),
|
||||
expand_call_goal(H,F,I),
|
||||
strip_subst_module(I,F,J,K),
|
||||
@@ -1061,7 +1122,7 @@ call(A,B,C,D,E,F) :-
|
||||
call(A,B,C,D,E,F) :-
|
||||
'$fast_call'(A,B,C,D,E,F).
|
||||
call(A,B,C,D,E,F) :-
|
||||
'$strip_module'(A,G,H),
|
||||
call_module(A,G,H),
|
||||
'$prepare_call_clause'(I,H,B,C,D,E,F),
|
||||
expand_call_goal(I,G,J),
|
||||
strip_subst_module(J,G,K,L),
|
||||
@@ -1074,7 +1135,7 @@ call(A,B,C,D,E,F,G) :-
|
||||
call(A,B,C,D,E,F,G) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G).
|
||||
call(A,B,C,D,E,F,G) :-
|
||||
'$strip_module'(A,H,I),
|
||||
call_module(A,H,I),
|
||||
'$prepare_call_clause'(J,I,B,C,D,E,F,G),
|
||||
expand_call_goal(J,H,K),
|
||||
strip_subst_module(K,H,L,M),
|
||||
@@ -1087,7 +1148,7 @@ call(A,B,C,D,E,F,G,H) :-
|
||||
call(A,B,C,D,E,F,G,H) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H).
|
||||
call(A,B,C,D,E,F,G,H) :-
|
||||
'$strip_module'(A,I,J),
|
||||
call_module(A,I,J),
|
||||
'$prepare_call_clause'(K,J,B,C,D,E,F,G,H),
|
||||
expand_call_goal(K,I,L),
|
||||
strip_subst_module(L,I,M,N),
|
||||
@@ -1100,7 +1161,7 @@ call(A,B,C,D,E,F,G,H,I) :-
|
||||
call(A,B,C,D,E,F,G,H,I) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I).
|
||||
call(A,B,C,D,E,F,G,H,I) :-
|
||||
'$strip_module'(A,J,K),
|
||||
call_module(A,J,K),
|
||||
'$prepare_call_clause'(L,K,B,C,D,E,F,G,H,I),
|
||||
expand_call_goal(L,J,M),
|
||||
strip_subst_module(M,J,N,O),
|
||||
@@ -1113,7 +1174,7 @@ call(A,B,C,D,E,F,G,H,I,J) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J).
|
||||
call(A,B,C,D,E,F,G,H,I,J) :-
|
||||
'$strip_module'(A,K,L),
|
||||
call_module(A,K,L),
|
||||
'$prepare_call_clause'(M,L,B,C,D,E,F,G,H,I,J),
|
||||
expand_call_goal(M,K,N),
|
||||
strip_subst_module(N,K,O,P),
|
||||
@@ -1126,7 +1187,7 @@ call(A,B,C,D,E,F,G,H,I,J,K) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K) :-
|
||||
'$strip_module'(A,L,M),
|
||||
call_module(A,L,M),
|
||||
'$prepare_call_clause'(N,M,B,C,D,E,F,G,H,I,J,K),
|
||||
expand_call_goal(N,L,O),
|
||||
strip_subst_module(O,L,P,Q),
|
||||
@@ -1139,7 +1200,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L) :-
|
||||
'$strip_module'(A,M,N),
|
||||
call_module(A,M,N),
|
||||
'$prepare_call_clause'(O,N,B,C,D,E,F,G,H,I,J,K,L),
|
||||
expand_call_goal(O,M,P),
|
||||
strip_subst_module(P,M,Q,R),
|
||||
@@ -1152,7 +1213,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M) :-
|
||||
'$strip_module'(A,N,O),
|
||||
call_module(A,N,O),
|
||||
'$prepare_call_clause'(P,O,B,C,D,E,F,G,H,I,J,K,L,M),
|
||||
expand_call_goal(P,N,Q),
|
||||
strip_subst_module(Q,N,R,S),
|
||||
@@ -1165,7 +1226,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N) :-
|
||||
'$strip_module'(A,O,P),
|
||||
call_module(A,O,P),
|
||||
'$prepare_call_clause'(Q,P,B,C,D,E,F,G,H,I,J,K,L,M,N),
|
||||
expand_call_goal(Q,O,R),
|
||||
strip_subst_module(R,O,S,T),
|
||||
@@ -1178,7 +1239,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O) :-
|
||||
'$strip_module'(A,P,Q),
|
||||
call_module(A,P,Q),
|
||||
'$prepare_call_clause'(R,Q,B,C,D,E,F,G,H,I,J,K,L,M,N,O),
|
||||
expand_call_goal(R,P,S),
|
||||
strip_subst_module(S,P,T,U),
|
||||
@@ -1191,7 +1252,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P) :-
|
||||
'$strip_module'(A,Q,R),
|
||||
call_module(A,Q,R),
|
||||
'$prepare_call_clause'(S,R,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P),
|
||||
expand_call_goal(S,Q,T),
|
||||
strip_subst_module(T,Q,U,V),
|
||||
@@ -1204,7 +1265,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q) :-
|
||||
'$strip_module'(A,R,S),
|
||||
call_module(A,R,S),
|
||||
'$prepare_call_clause'(T,S,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q),
|
||||
expand_call_goal(T,R,U),
|
||||
strip_subst_module(U,R,V,W),
|
||||
@@ -1217,7 +1278,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R) :-
|
||||
'$strip_module'(A,S,T),
|
||||
call_module(A,S,T),
|
||||
'$prepare_call_clause'(U,T,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R),
|
||||
expand_call_goal(U,S,V),
|
||||
strip_subst_module(V,S,W,X),
|
||||
@@ -1230,7 +1291,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S) :-
|
||||
'$strip_module'(A,T,U),
|
||||
call_module(A,T,U),
|
||||
'$prepare_call_clause'(V,U,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S),
|
||||
expand_call_goal(V,T,W),
|
||||
strip_subst_module(W,T,X,Y),
|
||||
@@ -1243,7 +1304,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) :-
|
||||
'$strip_module'(A,U,V),
|
||||
call_module(A,U,V),
|
||||
'$prepare_call_clause'(W,V,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T),
|
||||
expand_call_goal(W,U,X),
|
||||
strip_subst_module(X,U,Y,Z),
|
||||
@@ -1256,7 +1317,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U) :-
|
||||
'$strip_module'(A,V,W),
|
||||
call_module(A,V,W),
|
||||
'$prepare_call_clause'(X,W,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U),
|
||||
expand_call_goal(X,V,Y),
|
||||
strip_subst_module(Y,V,Z,A1),
|
||||
@@ -1269,7 +1330,7 @@ call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V) :-
|
||||
'$strip_module'(A,W,X),
|
||||
call_module(A,W,X),
|
||||
'$prepare_call_clause'(Y,X,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V),
|
||||
expand_call_goal(Y,W,Z),
|
||||
strip_subst_module(Z,W,A1,B1),
|
||||
@@ -1282,7 +1343,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W) :-
|
||||
'$strip_module'(A,X,Y),
|
||||
call_module(A,X,Y),
|
||||
'$prepare_call_clause'(Z,Y,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W),
|
||||
expand_call_goal(Z,X,A1),
|
||||
strip_subst_module(A1,X,B1,C1),
|
||||
@@ -1295,7 +1356,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X) :-
|
||||
'$strip_module'(A,Y,Z),
|
||||
call_module(A,Y,Z),
|
||||
'$prepare_call_clause'(A1,Z,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X),
|
||||
expand_call_goal(A1,Y,B1),
|
||||
strip_subst_module(B1,Y,C1,D1),
|
||||
@@ -1308,7 +1369,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y) :-
|
||||
'$strip_module'(A,Z,A1),
|
||||
call_module(A,Z,A1),
|
||||
'$prepare_call_clause'(B1,A1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y),
|
||||
expand_call_goal(B1,Z,C1),
|
||||
strip_subst_module(C1,Z,D1,E1),
|
||||
@@ -1321,7 +1382,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) :-
|
||||
'$strip_module'(A,A1,B1),
|
||||
call_module(A,A1,B1),
|
||||
'$prepare_call_clause'(C1,B1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z),
|
||||
expand_call_goal(C1,A1,D1),
|
||||
strip_subst_module(D1,A1,E1,F1),
|
||||
@@ -1334,7 +1395,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1) :-
|
||||
'$strip_module'(A,B1,C1),
|
||||
call_module(A,B1,C1),
|
||||
'$prepare_call_clause'(D1,C1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1),
|
||||
expand_call_goal(D1,B1,E1),
|
||||
strip_subst_module(E1,B1,F1,G1),
|
||||
@@ -1347,7 +1408,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1) :-
|
||||
'$strip_module'(A,C1,D1),
|
||||
call_module(A,C1,D1),
|
||||
'$prepare_call_clause'(E1,D1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1),
|
||||
expand_call_goal(E1,C1,F1),
|
||||
strip_subst_module(F1,C1,G1,H1),
|
||||
@@ -1360,7 +1421,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1) :-
|
||||
'$strip_module'(A,D1,E1),
|
||||
call_module(A,D1,E1),
|
||||
'$prepare_call_clause'(F1,E1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1),
|
||||
expand_call_goal(F1,D1,G1),
|
||||
strip_subst_module(G1,D1,H1,I1),
|
||||
@@ -1373,7 +1434,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1) :-
|
||||
'$strip_module'(A,E1,F1),
|
||||
call_module(A,E1,F1),
|
||||
'$prepare_call_clause'(G1,F1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1),
|
||||
expand_call_goal(G1,E1,H1),
|
||||
strip_subst_module(H1,E1,I1,J1),
|
||||
@@ -1386,7 +1447,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1) :-
|
||||
'$strip_module'(A,F1,G1),
|
||||
call_module(A,F1,G1),
|
||||
'$prepare_call_clause'(H1,G1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1),
|
||||
expand_call_goal(H1,F1,I1),
|
||||
strip_subst_module(I1,F1,J1,K1),
|
||||
@@ -1399,7 +1460,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) :-
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1) :-
|
||||
'$strip_module'(A,G1,H1),
|
||||
call_module(A,G1,H1),
|
||||
'$prepare_call_clause'(I1,H1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1),
|
||||
expand_call_goal(I1,G1,J1),
|
||||
strip_subst_module(J1,G1,K1,L1),
|
||||
@@ -1412,7 +1473,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) :
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1) :-
|
||||
'$strip_module'(A,H1,I1),
|
||||
call_module(A,H1,I1),
|
||||
'$prepare_call_clause'(J1,I1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1),
|
||||
expand_call_goal(J1,H1,K1),
|
||||
strip_subst_module(K1,H1,L1,M1),
|
||||
@@ -1425,7 +1486,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1) :-
|
||||
'$strip_module'(A,I1,J1),
|
||||
call_module(A,I1,J1),
|
||||
'$prepare_call_clause'(K1,J1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1),
|
||||
expand_call_goal(K1,I1,L1),
|
||||
strip_subst_module(L1,I1,M1,N1),
|
||||
@@ -1438,7 +1499,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1) :-
|
||||
'$strip_module'(A,J1,K1),
|
||||
call_module(A,J1,K1),
|
||||
'$prepare_call_clause'(L1,K1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1),
|
||||
expand_call_goal(L1,J1,M1),
|
||||
strip_subst_module(M1,J1,N1,O1),
|
||||
@@ -1451,7 +1512,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1) :-
|
||||
'$strip_module'(A,K1,L1),
|
||||
call_module(A,K1,L1),
|
||||
'$prepare_call_clause'(M1,L1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1),
|
||||
expand_call_goal(M1,K1,N1),
|
||||
strip_subst_module(N1,K1,O1,P1),
|
||||
@@ -1464,7 +1525,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1) :-
|
||||
'$strip_module'(A,L1,M1),
|
||||
call_module(A,L1,M1),
|
||||
'$prepare_call_clause'(N1,M1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1),
|
||||
expand_call_goal(N1,L1,O1),
|
||||
strip_subst_module(O1,L1,P1,Q1),
|
||||
@@ -1477,7 +1538,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1) :-
|
||||
'$strip_module'(A,M1,N1),
|
||||
call_module(A,M1,N1),
|
||||
'$prepare_call_clause'(O1,N1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1),
|
||||
expand_call_goal(O1,M1,P1),
|
||||
strip_subst_module(P1,M1,Q1,R1),
|
||||
@@ -1490,7 +1551,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1) :-
|
||||
'$strip_module'(A,N1,O1),
|
||||
call_module(A,N1,O1),
|
||||
'$prepare_call_clause'(P1,O1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1),
|
||||
expand_call_goal(P1,N1,Q1),
|
||||
strip_subst_module(Q1,N1,R1,S1),
|
||||
@@ -1503,7 +1564,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1) :-
|
||||
'$strip_module'(A,O1,P1),
|
||||
call_module(A,O1,P1),
|
||||
'$prepare_call_clause'(Q1,P1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1),
|
||||
expand_call_goal(Q1,O1,R1),
|
||||
strip_subst_module(R1,O1,S1,T1),
|
||||
@@ -1516,7 +1577,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1) :-
|
||||
'$strip_module'(A,P1,Q1),
|
||||
call_module(A,P1,Q1),
|
||||
'$prepare_call_clause'(R1,Q1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1),
|
||||
expand_call_goal(R1,P1,S1),
|
||||
strip_subst_module(S1,P1,T1,U1),
|
||||
@@ -1529,7 +1590,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1) :-
|
||||
'$strip_module'(A,Q1,R1),
|
||||
call_module(A,Q1,R1),
|
||||
'$prepare_call_clause'(S1,R1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1),
|
||||
expand_call_goal(S1,Q1,T1),
|
||||
strip_subst_module(T1,Q1,U1,V1),
|
||||
@@ -1542,7 +1603,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1) :-
|
||||
'$strip_module'(A,R1,S1),
|
||||
call_module(A,R1,S1),
|
||||
'$prepare_call_clause'(T1,S1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1),
|
||||
expand_call_goal(T1,R1,U1),
|
||||
strip_subst_module(U1,R1,V1,W1),
|
||||
@@ -1555,7 +1616,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1) :-
|
||||
'$strip_module'(A,S1,T1),
|
||||
call_module(A,S1,T1),
|
||||
'$prepare_call_clause'(U1,T1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1),
|
||||
expand_call_goal(U1,S1,V1),
|
||||
strip_subst_module(V1,S1,W1,X1),
|
||||
@@ -1568,7 +1629,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1) :-
|
||||
'$strip_module'(A,T1,U1),
|
||||
call_module(A,T1,U1),
|
||||
'$prepare_call_clause'(V1,U1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1),
|
||||
expand_call_goal(V1,T1,W1),
|
||||
strip_subst_module(W1,T1,X1,Y1),
|
||||
@@ -1581,7 +1642,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1) :-
|
||||
'$strip_module'(A,U1,V1),
|
||||
call_module(A,U1,V1),
|
||||
'$prepare_call_clause'(W1,V1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1),
|
||||
expand_call_goal(W1,U1,X1),
|
||||
strip_subst_module(X1,U1,Y1,Z1),
|
||||
@@ -1594,7 +1655,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1) :-
|
||||
'$strip_module'(A,V1,W1),
|
||||
call_module(A,V1,W1),
|
||||
'$prepare_call_clause'(X1,W1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1),
|
||||
expand_call_goal(X1,V1,Y1),
|
||||
strip_subst_module(Y1,V1,Z1,A2),
|
||||
@@ -1607,7 +1668,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1) :-
|
||||
'$strip_module'(A,W1,X1),
|
||||
call_module(A,W1,X1),
|
||||
'$prepare_call_clause'(Y1,X1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1),
|
||||
expand_call_goal(Y1,W1,Z1),
|
||||
strip_subst_module(Z1,W1,A2,B2),
|
||||
@@ -1620,7 +1681,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1) :-
|
||||
'$strip_module'(A,X1,Y1),
|
||||
call_module(A,X1,Y1),
|
||||
'$prepare_call_clause'(Z1,Y1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1),
|
||||
expand_call_goal(Z1,X1,A2),
|
||||
strip_subst_module(A2,X1,B2,C2),
|
||||
@@ -1633,7 +1694,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1) :-
|
||||
'$strip_module'(A,Y1,Z1),
|
||||
call_module(A,Y1,Z1),
|
||||
'$prepare_call_clause'(A2,Z1,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1),
|
||||
expand_call_goal(A2,Y1,B2),
|
||||
strip_subst_module(B2,Y1,C2,D2),
|
||||
@@ -1646,7 +1707,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1) :-
|
||||
'$strip_module'(A,Z1,A2),
|
||||
call_module(A,Z1,A2),
|
||||
'$prepare_call_clause'(B2,A2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1),
|
||||
expand_call_goal(B2,Z1,C2),
|
||||
strip_subst_module(C2,Z1,D2,E2),
|
||||
@@ -1659,7 +1720,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1) :-
|
||||
'$strip_module'(A,A2,B2),
|
||||
call_module(A,A2,B2),
|
||||
'$prepare_call_clause'(C2,B2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1),
|
||||
expand_call_goal(C2,A2,D2),
|
||||
strip_subst_module(D2,A2,E2,F2),
|
||||
@@ -1672,7 +1733,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2) :-
|
||||
'$strip_module'(A,B2,C2),
|
||||
call_module(A,B2,C2),
|
||||
'$prepare_call_clause'(D2,C2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2),
|
||||
expand_call_goal(D2,B2,E2),
|
||||
strip_subst_module(E2,B2,F2,G2),
|
||||
@@ -1685,7 +1746,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2) :-
|
||||
'$strip_module'(A,C2,D2),
|
||||
call_module(A,C2,D2),
|
||||
'$prepare_call_clause'(E2,D2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2),
|
||||
expand_call_goal(E2,C2,F2),
|
||||
strip_subst_module(F2,C2,G2,H2),
|
||||
@@ -1698,7 +1759,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2) :-
|
||||
'$strip_module'(A,D2,E2),
|
||||
call_module(A,D2,E2),
|
||||
'$prepare_call_clause'(F2,E2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2),
|
||||
expand_call_goal(F2,D2,G2),
|
||||
strip_subst_module(G2,D2,H2,I2),
|
||||
@@ -1711,7 +1772,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2) :-
|
||||
'$strip_module'(A,E2,F2),
|
||||
call_module(A,E2,F2),
|
||||
'$prepare_call_clause'(G2,F2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2),
|
||||
expand_call_goal(G2,E2,H2),
|
||||
strip_subst_module(H2,E2,I2,J2),
|
||||
@@ -1724,7 +1785,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2) :-
|
||||
'$strip_module'(A,F2,G2),
|
||||
call_module(A,F2,G2),
|
||||
'$prepare_call_clause'(H2,G2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2),
|
||||
expand_call_goal(H2,F2,I2),
|
||||
strip_subst_module(I2,F2,J2,K2),
|
||||
@@ -1737,7 +1798,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2) :-
|
||||
'$strip_module'(A,G2,H2),
|
||||
call_module(A,G2,H2),
|
||||
'$prepare_call_clause'(I2,H2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2),
|
||||
expand_call_goal(I2,G2,J2),
|
||||
strip_subst_module(J2,G2,K2,L2),
|
||||
@@ -1750,7 +1811,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2) :-
|
||||
'$strip_module'(A,H2,I2),
|
||||
call_module(A,H2,I2),
|
||||
'$prepare_call_clause'(J2,I2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2),
|
||||
expand_call_goal(J2,H2,K2),
|
||||
strip_subst_module(K2,H2,L2,M2),
|
||||
@@ -1763,7 +1824,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2) :-
|
||||
'$strip_module'(A,I2,J2),
|
||||
call_module(A,I2,J2),
|
||||
'$prepare_call_clause'(K2,J2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2),
|
||||
expand_call_goal(K2,I2,L2),
|
||||
strip_subst_module(L2,I2,M2,N2),
|
||||
@@ -1776,7 +1837,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2) :-
|
||||
'$strip_module'(A,J2,K2),
|
||||
call_module(A,J2,K2),
|
||||
'$prepare_call_clause'(L2,K2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2),
|
||||
expand_call_goal(L2,J2,M2),
|
||||
strip_subst_module(M2,J2,N2,O2),
|
||||
@@ -1789,7 +1850,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2) :-
|
||||
'$strip_module'(A,K2,L2),
|
||||
call_module(A,K2,L2),
|
||||
'$prepare_call_clause'(M2,L2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2),
|
||||
expand_call_goal(M2,K2,N2),
|
||||
strip_subst_module(N2,K2,O2,P2),
|
||||
@@ -1802,7 +1863,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2) :-
|
||||
'$strip_module'(A,L2,M2),
|
||||
call_module(A,L2,M2),
|
||||
'$prepare_call_clause'(N2,M2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2),
|
||||
expand_call_goal(N2,L2,O2),
|
||||
strip_subst_module(O2,L2,P2,Q2),
|
||||
@@ -1815,7 +1876,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2) :-
|
||||
'$strip_module'(A,M2,N2),
|
||||
call_module(A,M2,N2),
|
||||
'$prepare_call_clause'(O2,N2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2),
|
||||
expand_call_goal(O2,M2,P2),
|
||||
strip_subst_module(P2,M2,Q2,R2),
|
||||
@@ -1828,7 +1889,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
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,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) :-
|
||||
'$fast_call'(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2).
|
||||
call(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2) :-
|
||||
'$strip_module'(A,N2,O2),
|
||||
call_module(A,N2,O2),
|
||||
'$prepare_call_clause'(P2,O2,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1,A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2),
|
||||
expand_call_goal(P2,N2,Q2),
|
||||
strip_subst_module(Q2,N2,R2,S2),
|
||||
|
||||
@@ -11,20 +11,18 @@ use crate::forms::*;
|
||||
use crate::heap_iter::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::offset_table::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::types::*;
|
||||
|
||||
use crate::fixnum;
|
||||
|
||||
use ordered_float::*;
|
||||
use ordered_float::{Float, OrderedFloat};
|
||||
|
||||
use std::cmp;
|
||||
use std::convert::TryFrom;
|
||||
use std::f64;
|
||||
use std::mem;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! try_numeric_result {
|
||||
($e: expr, $stub_gen: expr) => {
|
||||
match $e {
|
||||
@@ -51,7 +49,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() -> MachineStub + 'static) -> MachineStubGen {
|
||||
Box::new(move |machine_st| {
|
||||
let eval_error = machine_st.evaluation_error(EvalError::ZeroDivisor);
|
||||
let stub = stub_gen();
|
||||
@@ -60,7 +58,7 @@ fn zero_divisor_eval_error(stub_gen: impl Fn() -> FunctorStub + 'static) -> Mach
|
||||
})
|
||||
}
|
||||
|
||||
fn undefined_eval_error(stub_gen: impl Fn() -> FunctorStub + 'static) -> MachineStubGen {
|
||||
fn undefined_eval_error(stub_gen: impl Fn() -> MachineStub + 'static) -> MachineStubGen {
|
||||
Box::new(move |machine_st| {
|
||||
let eval_error = machine_st.evaluation_error(EvalError::Undefined);
|
||||
let stub = stub_gen();
|
||||
@@ -72,7 +70,7 @@ fn undefined_eval_error(stub_gen: impl Fn() -> FunctorStub + 'static) -> Machine
|
||||
fn numerical_type_error(
|
||||
valid_type: ValidType,
|
||||
n: Number,
|
||||
stub_gen: impl Fn() -> FunctorStub + 'static,
|
||||
stub_gen: impl Fn() -> MachineStub + 'static,
|
||||
) -> MachineStubGen {
|
||||
Box::new(move |machine_st| {
|
||||
let type_error = machine_st.type_error(valid_type, n);
|
||||
@@ -200,11 +198,10 @@ pub(crate) fn neg(n: Number, arena: &mut Arena) -> Number {
|
||||
pub(crate) fn abs(n: Number, arena: &mut Arena) -> Number {
|
||||
match n {
|
||||
Number::Fixnum(n) => {
|
||||
if let Some(n) = n.get_num().checked_abs() {
|
||||
fixnum!(Number, n, arena)
|
||||
if let Some(n) = n.checked_abs() {
|
||||
Number::Fixnum(n)
|
||||
} else {
|
||||
let arena_int = Integer::from(n.get_num());
|
||||
Number::arena_from(arena_int.abs(), arena)
|
||||
Number::arena_from(Integer::from(Fixnum::MAX + 1), arena)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => {
|
||||
@@ -458,13 +455,8 @@ pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
|
||||
Ok(Number::Fixnum(n2))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
if n1 > n2 {
|
||||
Ok(Number::Integer(n1))
|
||||
} else {
|
||||
Ok(Number::Integer(n2))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Integer(cmp::max(n1, n2))),
|
||||
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Rational(cmp::max(r1, r2))),
|
||||
(n1, n2) => {
|
||||
let stub_gen = || {
|
||||
let max_atom = atom!("max");
|
||||
@@ -474,7 +466,15 @@ pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
|
||||
let f1 = try_numeric_result!(result_f(&n1), stub_gen)?;
|
||||
let f2 = try_numeric_result!(result_f(&n2), stub_gen)?;
|
||||
|
||||
Ok(Number::Float(cmp::max(OrderedFloat(f1), OrderedFloat(f2))))
|
||||
match OrderedFloat(f1).cmp(&OrderedFloat(f2)) {
|
||||
cmp::Ordering::Less => Ok(n2),
|
||||
cmp::Ordering::Equal => {
|
||||
// Note: n1 and n2 were compared as floats,
|
||||
// so we return the second argument as a floating point value.
|
||||
Ok(Number::Float(OrderedFloat(f2)))
|
||||
}
|
||||
cmp::Ordering::Greater => Ok(n1),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -502,13 +502,8 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
|
||||
Ok(Number::Fixnum(n2))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||
if n1 < n2 {
|
||||
Ok(Number::Integer(n1))
|
||||
} else {
|
||||
Ok(Number::Integer(n2))
|
||||
}
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Integer(cmp::min(n1, n2))),
|
||||
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Rational(cmp::min(r1, r2))),
|
||||
(n1, n2) => {
|
||||
let stub_gen = || {
|
||||
let min_atom = atom!("min");
|
||||
@@ -518,20 +513,28 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
|
||||
let f1 = try_numeric_result!(result_f(&n1), stub_gen)?;
|
||||
let f2 = try_numeric_result!(result_f(&n2), stub_gen)?;
|
||||
|
||||
Ok(Number::Float(cmp::min(OrderedFloat(f1), OrderedFloat(f2))))
|
||||
match OrderedFloat(f1).cmp(&OrderedFloat(f2)) {
|
||||
cmp::Ordering::Less => Ok(n1),
|
||||
cmp::Ordering::Equal => {
|
||||
// Note: n1 and n2 were compared as floats,
|
||||
// so we return the first argument as a floating point value.
|
||||
Ok(Number::Float(OrderedFloat(f1)))
|
||||
}
|
||||
cmp::Ordering::Greater => Ok(n2),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rational_from_number(
|
||||
n: Number,
|
||||
stub_gen: impl Fn() -> FunctorStub + 'static,
|
||||
stub_gen: impl Fn() -> MachineStub + 'static,
|
||||
arena: &mut Arena,
|
||||
) -> Result<TypedArenaPtr<Rational>, MachineStubGen> {
|
||||
match n {
|
||||
Number::Fixnum(n) => Ok(arena_alloc!(Rational::from(n.get_num()), arena)),
|
||||
Number::Rational(r) => Ok(r),
|
||||
Number::Float(OrderedFloat(f)) => match Rational::simplest_from_f64(f) {
|
||||
Number::Float(OrderedFloat(f)) => match Rational::try_from(f).ok() {
|
||||
Some(r) => Ok(arena_alloc!(r, arena)),
|
||||
None => Err(Box::new(move |machine_st| {
|
||||
let instantiation_error = machine_st.instantiation_error();
|
||||
@@ -626,111 +629,110 @@ pub(crate) fn int_floor_div(
|
||||
idiv(n1, n2, arena)
|
||||
}
|
||||
|
||||
pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
|
||||
pub(crate) fn shr(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
|
||||
let stub_gen = || {
|
||||
let shr_atom = atom!(">>");
|
||||
functor_stub(shr_atom, 2)
|
||||
};
|
||||
|
||||
if n2.is_integer() && n2.is_negative() {
|
||||
return shl(n1, neg(n2, arena), arena);
|
||||
if rhs.is_integer() && rhs.is_negative() {
|
||||
return shl(lhs, neg(rhs, arena), arena);
|
||||
}
|
||||
|
||||
match (n1, n2) {
|
||||
(Number::Fixnum(n1), Number::Fixnum(n2)) => {
|
||||
let n1_i = n1.get_num();
|
||||
let n2_i = n2.get_num();
|
||||
match lhs {
|
||||
Number::Fixnum(lhs) => {
|
||||
let rhs = match rhs {
|
||||
Number::Fixnum(fix) => fix.get_num().try_into().unwrap_or(u32::MAX),
|
||||
Number::Integer(int) => (&*int).try_into().unwrap_or(u32::MAX),
|
||||
other => {
|
||||
return Err(numerical_type_error(ValidType::Integer, other, stub_gen));
|
||||
}
|
||||
};
|
||||
|
||||
let n1 = Integer::from(n1_i);
|
||||
|
||||
if let Ok(n2) = usize::try_from(n2_i) {
|
||||
Ok(Number::arena_from(n1 >> n2, arena))
|
||||
} else {
|
||||
Ok(Number::arena_from(n1 >> usize::max_value(), arena))
|
||||
}
|
||||
let res = lhs.get_num().checked_shr(rhs).unwrap_or(0);
|
||||
Ok(Number::arena_from(res, arena))
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
Number::Integer(lhs) => {
|
||||
// Note: bigints require `log(n)` bits of space. If `rhs > usize::MAX`,
|
||||
// then this clamping only becomes an issue when `lhs < 2 ^ (usize::MAX)`:
|
||||
// - on 32-bit systems, `lhs` would need to be 512MiB big (1/8th of the addressable memory)
|
||||
// - on 64-bit systems, `lhs` would need to be 2EiB big (!!!)
|
||||
let rhs = match rhs {
|
||||
Number::Fixnum(fix) => fix.get_num().try_into().unwrap_or(usize::MAX),
|
||||
Number::Integer(int) => (&*int).try_into().unwrap_or(usize::MAX),
|
||||
other => {
|
||||
return Err(numerical_type_error(ValidType::Integer, other, stub_gen));
|
||||
}
|
||||
};
|
||||
|
||||
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)),
|
||||
}
|
||||
Ok(Number::arena_from(Integer::from(&*lhs >> rhs), 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)) => {
|
||||
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)),
|
||||
other => Err(numerical_type_error(ValidType::Integer, other, stub_gen)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
|
||||
pub(crate) fn shl(lhs: Number, rhs: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
|
||||
let stub_gen = || {
|
||||
let shl_atom = atom!("<<");
|
||||
functor_stub(shl_atom, 2)
|
||||
};
|
||||
|
||||
if n2.is_integer() && n2.is_negative() {
|
||||
return shr(n1, neg(n2, arena), arena);
|
||||
if rhs.is_integer() && rhs.is_negative() {
|
||||
return shr(lhs, neg(rhs, arena), arena);
|
||||
}
|
||||
|
||||
match (n1, n2) {
|
||||
(Number::Fixnum(n1), Number::Fixnum(n2)) => {
|
||||
let n1_i = n1.get_num();
|
||||
let n2_i = n2.get_num();
|
||||
let rhs = match rhs {
|
||||
Number::Fixnum(fix) => fix.get_num().try_into().unwrap_or(usize::MAX),
|
||||
Number::Integer(int) => (&*int).try_into().unwrap_or(usize::MAX),
|
||||
other => {
|
||||
return Err(numerical_type_error(ValidType::Integer, other, stub_gen));
|
||||
}
|
||||
};
|
||||
|
||||
let n1 = Integer::from(n1_i);
|
||||
match lhs {
|
||||
Number::Fixnum(lhs) => {
|
||||
let lhs = lhs.get_num();
|
||||
|
||||
if let Ok(n2) = usize::try_from(n2_i) {
|
||||
Ok(Number::arena_from(n1 << n2, arena))
|
||||
if let Some(res) = checked_signed_shl(lhs, rhs) {
|
||||
Ok(Number::arena_from(res, arena))
|
||||
} else {
|
||||
Ok(Number::arena_from(n1 << usize::max_value(), arena))
|
||||
let lhs = Integer::from(lhs);
|
||||
Ok(Number::arena_from(
|
||||
Integer::from(lhs << (rhs as usize)),
|
||||
arena,
|
||||
))
|
||||
}
|
||||
}
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||
let n1 = Integer::from(n1.get_num());
|
||||
Number::Integer(lhs) => Ok(Number::arena_from(
|
||||
Integer::from(&*lhs << (rhs as usize)),
|
||||
arena,
|
||||
)),
|
||||
other => Err(numerical_type_error(ValidType::Integer, other, stub_gen)),
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
}
|
||||
/// Returns `x << shift`, checking for overflow and for values of `shift` that are too big.
|
||||
#[inline]
|
||||
fn checked_signed_shl(x: i64, shift: usize) -> Option<i64> {
|
||||
if shift == 0 {
|
||||
return Some(x);
|
||||
}
|
||||
|
||||
if x >= 0 {
|
||||
// Note: for unsigned integers, the condition would usually be spelled
|
||||
// `shift <= x.leading_zeros()`, but since the MSB for signed integers
|
||||
// controls the sign, we need to make sure that `shift` is at most
|
||||
// `x.leading_zeros() - 1`.
|
||||
if shift < x.leading_zeros() as usize {
|
||||
Some(x << shift)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(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).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)),
|
||||
(n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
|
||||
} else {
|
||||
let y = x.checked_neg()?;
|
||||
// FIXME: incorrectly rejects `-2 ^ 62 << 1`. This is currently a non-issue,
|
||||
// since the bitshift is then done as a `Number::Integer`
|
||||
checked_signed_shl(y, shift).and_then(|res| res.checked_neg())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -958,8 +960,7 @@ 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 n2: isize = (&*n2).try_into().unwrap();
|
||||
let value: Integer = (&*n1).gcd(&Integer::from(n2)).into();
|
||||
let value: Integer = (&*n1).gcd(&*n2).into();
|
||||
Ok(Number::arena_from(value, arena))
|
||||
}
|
||||
(Number::Float(f), _) | (_, Number::Float(f)) => {
|
||||
@@ -1055,7 +1056,12 @@ pub(crate) fn sqrt(n1: Number) -> Result<f64, MachineStubGen> {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn floor(n1: Number, arena: &mut Arena) -> Number {
|
||||
rnd_i(&n1, arena)
|
||||
rnd_i(&n1, arena).unwrap_or_else(|_err| {
|
||||
// FIXME: Currently floor/1 (and several call sites) are infallible,
|
||||
// but the failing cases (when `n1` is `Number::Float(NAN)` or `Number::Float(INFINITY)`)
|
||||
// are not reachable with standard is/2 operations.
|
||||
todo!("Make floor/1 fallible");
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1078,21 +1084,27 @@ pub(crate) fn truncate(n: Number, arena: &mut Arena) -> Number {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn round(n: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
|
||||
let stub_gen = || {
|
||||
let is_atom = atom!("is");
|
||||
functor_stub(is_atom, 2)
|
||||
pub(crate) fn round(num: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
|
||||
let res = match num {
|
||||
Number::Fixnum(_) | Number::Integer(_) => num,
|
||||
Number::Rational(rat) => Number::arena_from(rat.round(), arena),
|
||||
Number::Float(f) => Number::Float(OrderedFloat((*f).round())),
|
||||
};
|
||||
|
||||
let result = add(n, Number::Float(OrderedFloat(0.5f64)), arena);
|
||||
let result = try_numeric_result!(result, stub_gen)?;
|
||||
// FIXME: make round/1 return EvalError
|
||||
rnd_i(&res, arena).map_err(|err| -> MachineStubGen {
|
||||
Box::new(move |machine_st| {
|
||||
let eval_error = machine_st.evaluation_error(err);
|
||||
let stub = functor_stub(atom!("round"), 1);
|
||||
|
||||
Ok(floor(result, arena))
|
||||
machine_st.error_form(eval_error, stub)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn bitwise_complement(n1: Number, arena: &mut Arena) -> Result<Number, MachineStubGen> {
|
||||
match n1 {
|
||||
Number::Fixnum(n) => Ok(Number::Fixnum(Fixnum::build_with(!n.get_num()))),
|
||||
Number::Fixnum(n) => Ok(Number::Fixnum(!n)),
|
||||
Number::Integer(n1) => Ok(Number::arena_from(Integer::from(!&*n1), arena)),
|
||||
_ => {
|
||||
let stub_gen = || {
|
||||
@@ -1112,9 +1124,12 @@ impl MachineState {
|
||||
&ArithmeticTerm::Reg(r) => {
|
||||
let value = self.store(self.deref(self[r]));
|
||||
|
||||
match Number::try_from(value) {
|
||||
match Number::try_from((value, &self.arena.f64_tbl)) {
|
||||
Ok(n) => Ok(n),
|
||||
Err(_) => self.arith_eval_by_metacall(value),
|
||||
Err(_) => {
|
||||
self.heap[0] = value;
|
||||
self.arith_eval_by_metacall(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
&ArithmeticTerm::Interm(i) => Ok(mem::replace(
|
||||
@@ -1128,7 +1143,7 @@ impl MachineState {
|
||||
pub fn get_rational(
|
||||
&mut self,
|
||||
at: &ArithmeticTerm,
|
||||
caller: impl Fn() -> FunctorStub + 'static,
|
||||
caller: impl Fn() -> MachineStub + 'static,
|
||||
) -> Result<TypedArenaPtr<Rational>, MachineStub> {
|
||||
let n = self.get_number(at)?;
|
||||
|
||||
@@ -1140,11 +1155,11 @@ impl MachineState {
|
||||
|
||||
pub(crate) fn arith_eval_by_metacall(
|
||||
&mut self,
|
||||
value: HeapCellValue,
|
||||
term_loc: usize,
|
||||
) -> Result<Number, MachineStub> {
|
||||
let stub_gen = || functor_stub(atom!("is"), 2);
|
||||
let mut iter =
|
||||
stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, value);
|
||||
stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, term_loc);
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
if value.get_forwarding_bit() {
|
||||
@@ -1157,7 +1172,7 @@ impl MachineState {
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
cell_as_atom_cell!(self.heap[s]).get_name_and_arity()
|
||||
}
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset |
|
||||
(HeapCellValueTag::Lis | // HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset |
|
||||
HeapCellValueTag::PStrLoc) => {
|
||||
(atom!("."), 2)
|
||||
}
|
||||
@@ -1373,8 +1388,9 @@ impl MachineState {
|
||||
(HeapCellValueTag::Fixnum, n) => {
|
||||
self.interms.push(Number::Fixnum(n));
|
||||
}
|
||||
(HeapCellValueTag::F64, fl) => {
|
||||
self.interms.push(Number::Float(*fl));
|
||||
(HeapCellValueTag::F64Offset, offset) => {
|
||||
let fl = self.arena.f64_tbl.get_entry(offset);
|
||||
self.interms.push(Number::Float(fl));
|
||||
}
|
||||
(HeapCellValueTag::Cons, ptr) => {
|
||||
match_untyped_arena_ptr!(ptr,
|
||||
@@ -1423,22 +1439,21 @@ 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));
|
||||
op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("-"), Fixity::Pre), OpDesc::build_with(200, FY));
|
||||
op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
|
||||
let term_write_result =
|
||||
parse_and_write_parsed_term_to_heap(&mut wam, "3 + 4 - 1 + 2.", &op_dir).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
wam.arith_eval_by_metacall(heap_loc_as_cell!(term_write_result.heap_loc)),
|
||||
wam.arith_eval_by_metacall(term_write_result.heap_loc),
|
||||
Ok(Number::Fixnum(Fixnum::build_with(8))),
|
||||
);
|
||||
|
||||
@@ -1448,7 +1463,7 @@ mod tests {
|
||||
parse_and_write_parsed_term_to_heap(&mut wam, "5 * 4 - 1.", &op_dir).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
wam.arith_eval_by_metacall(heap_loc_as_cell!(term_write_result.heap_loc)),
|
||||
wam.arith_eval_by_metacall(term_write_result.heap_loc),
|
||||
Ok(Number::Fixnum(Fixnum::build_with(19))),
|
||||
);
|
||||
|
||||
@@ -1458,7 +1473,7 @@ mod tests {
|
||||
parse_and_write_parsed_term_to_heap(&mut wam, "sign(-1).", &op_dir).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
wam.arith_eval_by_metacall(heap_loc_as_cell!(term_write_result.heap_loc)),
|
||||
wam.arith_eval_by_metacall(term_write_result.heap_loc),
|
||||
Ok(Number::Fixnum(Fixnum::build_with(-1)))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use crate::heap_iter::*;
|
||||
use crate::machine::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::temp_v;
|
||||
use crate::types::*;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::vec::IntoIter;
|
||||
|
||||
pub(super) type Bindings = Vec<(usize, HeapCellValue)>;
|
||||
|
||||
@@ -56,32 +54,36 @@ impl MachineState {
|
||||
self.attr_var_init.bindings.push((h, addr));
|
||||
}
|
||||
|
||||
fn populate_var_and_value_lists(&mut self) -> (HeapCellValue, HeapCellValue) {
|
||||
fn populate_var_and_value_lists(&mut self) -> Result<(HeapCellValue, HeapCellValue), usize> {
|
||||
let size = self.attr_var_init.bindings.len();
|
||||
|
||||
let iter = self
|
||||
.attr_var_init
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|(ref h, _)| attr_var_as_cell!(*h));
|
||||
|
||||
let var_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
|
||||
let var_list_addr = sized_iter_to_heap_list(&mut self.heap, size, iter)?;
|
||||
let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v);
|
||||
let value_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
|
||||
let value_list_addr = sized_iter_to_heap_list(&mut self.heap, size, iter)?;
|
||||
|
||||
(var_list_addr, value_list_addr)
|
||||
Ok((var_list_addr, value_list_addr))
|
||||
}
|
||||
|
||||
fn verify_attributes(&mut self) {
|
||||
fn verify_attributes(&mut self) -> Result<(), usize> {
|
||||
for (h, _) in &self.attr_var_init.bindings {
|
||||
self.heap[*h] = attr_var_as_cell!(*h);
|
||||
}
|
||||
|
||||
let (var_list_addr, value_list_addr) = self.populate_var_and_value_lists();
|
||||
let (var_list_addr, value_list_addr) = self.populate_var_and_value_lists()?;
|
||||
|
||||
self[temp_v!(1)] = var_list_addr;
|
||||
self[temp_v!(2)] = value_list_addr;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn gather_attr_vars_created_since(&mut self, b: usize) -> IntoIter<HeapCellValue> {
|
||||
pub(super) fn gather_attr_vars_created_since(&mut self, b: usize) -> Vec<HeapCellValue> {
|
||||
let mut attr_vars: Vec<_> = if b >= self.attr_var_init.attr_var_queue.len() {
|
||||
vec![]
|
||||
} else {
|
||||
@@ -105,10 +107,10 @@ impl MachineState {
|
||||
});
|
||||
|
||||
attr_vars.dedup();
|
||||
attr_vars.into_iter()
|
||||
attr_vars
|
||||
}
|
||||
|
||||
pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) {
|
||||
pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) -> Result<(), usize> {
|
||||
self.allocate(arity + 3);
|
||||
|
||||
let e = self.e;
|
||||
@@ -118,23 +120,40 @@ 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!(
|
||||
/* FIXME this is not safe */
|
||||
unsafe { Fixnum::build_with_unchecked(self.b0 as i64) }
|
||||
);
|
||||
and_frame[arity + 2] =
|
||||
fixnum_as_cell!(
|
||||
/* FIXME this is not safe */
|
||||
unsafe { Fixnum::build_with_unchecked(self.num_of_args as i64) }
|
||||
);
|
||||
and_frame[arity + 3] =
|
||||
fixnum_as_cell!(
|
||||
/* FIXME this is not safe */
|
||||
unsafe { Fixnum::build_with_unchecked(self.attr_var_init.cp as i64) }
|
||||
);
|
||||
|
||||
self.verify_attributes();
|
||||
self.verify_attributes()?;
|
||||
|
||||
self.num_of_args = 3;
|
||||
self.b0 = self.b;
|
||||
self.p = p;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn attr_vars_of_term(&mut self, cell: HeapCellValue) -> Vec<HeapCellValue> {
|
||||
debug_assert!(cell.is_ref());
|
||||
|
||||
let mut seen_set = IndexSet::new();
|
||||
let mut seen_vars = vec![];
|
||||
|
||||
let mut iter =
|
||||
stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, cell);
|
||||
self.heap[0] = cell;
|
||||
|
||||
let mut iter = stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, 0);
|
||||
|
||||
while let Some(value) = iter.next() {
|
||||
read_heap_cell!(value,
|
||||
|
||||
@@ -41,7 +41,7 @@ fn capture_offset(line: &Instruction, index: usize, stack: &mut Vec<usize>) -> b
|
||||
/* This function walks the code of a single predicate, supposed to
|
||||
* begin in code at the offset p. Each instruction is passed to the
|
||||
* walker function.
|
||||
*/
|
||||
*/
|
||||
pub(crate) fn walk_code(code: &Code, p: usize, mut walker: impl FnMut(&Instruction)) {
|
||||
let mut stack = vec![p];
|
||||
let mut visited_indices = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
|
||||
@@ -133,7 +133,7 @@ fn merge_indices(
|
||||
);
|
||||
|
||||
retraction_info.push_record(RetractionRecord::AddedIndex(
|
||||
skeleton[clause_index].opt_arg_index_key.clone(),
|
||||
skeleton[clause_index].opt_arg_index_key,
|
||||
clause_loc,
|
||||
));
|
||||
} else {
|
||||
@@ -246,7 +246,7 @@ fn remove_index_from_subsequence(
|
||||
// appear anywhere inside an Internal record.
|
||||
retraction_info.push_record(RetractionRecord::RemovedIndex(
|
||||
index_loc,
|
||||
opt_arg_index_key.clone(),
|
||||
*opt_arg_index_key,
|
||||
offset,
|
||||
));
|
||||
}
|
||||
@@ -700,31 +700,25 @@ fn remove_non_leading_clause(
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize_retract(
|
||||
fn finalize_retract<'a, LS: LoadState<'a>>(
|
||||
payload: &mut <LS as LoadState<'a>>::LoaderFieldType,
|
||||
key: PredicateKey,
|
||||
compilation_target: CompilationTarget,
|
||||
skeleton: &mut PredicateSkeleton,
|
||||
code_index: CodeIndex,
|
||||
target_pos: usize,
|
||||
index_ptr_opt: Option<IndexPtr>,
|
||||
retraction_info: &mut RetractionInfo,
|
||||
) -> usize {
|
||||
let clause_clause_loc = delete_from_skeleton(
|
||||
compilation_target,
|
||||
key,
|
||||
skeleton,
|
||||
target_pos,
|
||||
retraction_info,
|
||||
&mut payload.retraction_info,
|
||||
);
|
||||
|
||||
if let Some(index_ptr) = index_ptr_opt {
|
||||
set_code_index(
|
||||
retraction_info,
|
||||
&compilation_target,
|
||||
key,
|
||||
code_index,
|
||||
index_ptr,
|
||||
);
|
||||
set_code_index::<LS>(payload, &compilation_target, key, code_index, index_ptr);
|
||||
}
|
||||
|
||||
clause_clause_loc
|
||||
@@ -814,7 +808,7 @@ fn prepend_compiled_clause(
|
||||
skeleton.clauses[0].clause_start = clause_loc + 2;
|
||||
|
||||
retraction_info.push_record(RetractionRecord::AddedIndex(
|
||||
skeleton.clauses[0].opt_arg_index_key.clone(),
|
||||
skeleton.clauses[0].opt_arg_index_key,
|
||||
skeleton.clauses[0].clause_start,
|
||||
));
|
||||
|
||||
@@ -1076,7 +1070,7 @@ fn append_compiled_clause(
|
||||
skeleton.clauses[target_pos].opt_arg_index_key += index_loc - 1;
|
||||
|
||||
retraction_info.push_record(RetractionRecord::AddedIndex(
|
||||
skeleton.clauses[target_pos].opt_arg_index_key.clone(),
|
||||
skeleton.clauses[target_pos].opt_arg_index_key,
|
||||
skeleton.clauses[target_pos].clause_start,
|
||||
));
|
||||
|
||||
@@ -1194,11 +1188,8 @@ fn print_overwrite_warning(
|
||||
key: PredicateKey,
|
||||
is_dynamic: bool,
|
||||
) {
|
||||
if let CompilationTarget::Module(module_name) = compilation_target {
|
||||
match module_name {
|
||||
atom!("builtins") | atom!("loader") => return,
|
||||
_ => {}
|
||||
}
|
||||
if let CompilationTarget::Module(atom!("builtins") | atom!("loader")) = compilation_target {
|
||||
return;
|
||||
}
|
||||
|
||||
match code_ptr.tag() {
|
||||
@@ -1240,12 +1231,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
settings: CodeGenSettings,
|
||||
) -> Result<StandaloneCompileResult, SessionError> {
|
||||
let mut preprocessor = Preprocessor::new(settings);
|
||||
let clause = preprocessor.try_term_to_tl(self, term)?;
|
||||
|
||||
let clause = self.try_term_to_tl(term, &mut preprocessor)?;
|
||||
// let queue = preprocessor.parse_queue(self)?;
|
||||
|
||||
let mut cg = CodeGenerator::new(&LS::machine_st(&mut self.payload).atom_tbl, settings);
|
||||
|
||||
let mut cg = CodeGenerator::new(settings);
|
||||
let clause_code = cg.compile_predicate(vec![clause])?;
|
||||
|
||||
Ok(StandaloneCompileResult {
|
||||
@@ -1260,7 +1248,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
mut predicates: PredicateQueue,
|
||||
settings: CodeGenSettings,
|
||||
) -> Result<CodeIndex, SessionError> {
|
||||
let code_index = self.get_or_insert_code_index(key, predicates.compilation_target);
|
||||
let code_idx = self.get_or_insert_code_index(key, predicates.compilation_target);
|
||||
|
||||
LS::err_on_builtin_overwrite(self, key)?;
|
||||
|
||||
@@ -1271,11 +1259,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let mut preprocessor = Preprocessor::new(settings);
|
||||
|
||||
for term in predicates.predicates.drain(0..) {
|
||||
clauses.push(self.try_term_to_tl(term, &mut preprocessor)?);
|
||||
clauses.push(preprocessor.try_term_to_tl(self, term)?);
|
||||
}
|
||||
|
||||
let mut cg = CodeGenerator::new(&LS::machine_st(&mut self.payload).atom_tbl, settings);
|
||||
|
||||
let mut cg = CodeGenerator::new(settings);
|
||||
let mut code = cg.compile_predicate(clauses)?;
|
||||
|
||||
if settings.is_extensible {
|
||||
@@ -1334,9 +1321,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
);
|
||||
}
|
||||
|
||||
let index_ptr = LS::machine_st(&mut self.payload)
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(code_idx.into());
|
||||
|
||||
print_overwrite_warning(
|
||||
&predicates.compilation_target,
|
||||
code_index.get(),
|
||||
index_ptr,
|
||||
key,
|
||||
settings.is_dynamic(),
|
||||
);
|
||||
@@ -1347,16 +1339,16 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
IndexPtr::index(code_ptr)
|
||||
};
|
||||
|
||||
set_code_index(
|
||||
&mut self.payload.retraction_info,
|
||||
set_code_index::<LS>(
|
||||
&mut self.payload,
|
||||
&predicates.compilation_target,
|
||||
key,
|
||||
code_index,
|
||||
code_idx,
|
||||
index_ptr,
|
||||
);
|
||||
|
||||
self.wam_prelude.code.extend(code);
|
||||
Ok(code_index)
|
||||
Ok(code_idx)
|
||||
}
|
||||
|
||||
fn extend_local_predicate_skeleton(
|
||||
@@ -1558,19 +1550,19 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
self.push_back_to_local_predicate_skeleton(&compilation_target, &key, code_len);
|
||||
|
||||
let code_index = self.get_or_insert_code_index(key, compilation_target);
|
||||
let code_idx = self.get_or_insert_code_index(key, compilation_target);
|
||||
|
||||
if let Some(new_code_ptr) = result {
|
||||
set_code_index(
|
||||
&mut self.payload.retraction_info,
|
||||
set_code_index::<LS>(
|
||||
&mut self.payload,
|
||||
&compilation_target,
|
||||
key,
|
||||
code_index,
|
||||
code_idx,
|
||||
new_code_ptr,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(code_index)
|
||||
Ok(code_idx)
|
||||
}
|
||||
AppendOrPrepend::Prepend => {
|
||||
let clause_index_info = standalone_skeleton.clauses.pop_back().unwrap();
|
||||
@@ -1600,17 +1592,17 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
self.push_front_to_local_predicate_skeleton(&compilation_target, &key, code_len);
|
||||
|
||||
let code_index = self.get_or_insert_code_index(key, compilation_target);
|
||||
let code_idx = self.get_or_insert_code_index(key, compilation_target);
|
||||
|
||||
set_code_index(
|
||||
&mut self.payload.retraction_info,
|
||||
set_code_index::<LS>(
|
||||
&mut self.payload,
|
||||
&compilation_target,
|
||||
key,
|
||||
code_index,
|
||||
code_idx,
|
||||
new_code_ptr,
|
||||
);
|
||||
|
||||
Ok(code_index)
|
||||
Ok(code_idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1658,7 +1650,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
pub(super) fn retract_clause(&mut self, key: PredicateKey, target_pos: usize) -> usize {
|
||||
let payload_compilation_target = self.payload.compilation_target;
|
||||
let code_index = self.get_or_insert_code_index(key, payload_compilation_target);
|
||||
let code_idx_offset = self.get_or_insert_code_index(key, payload_compilation_target);
|
||||
|
||||
let skeleton = self
|
||||
.wam_prelude
|
||||
@@ -1736,14 +1728,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
None
|
||||
};
|
||||
|
||||
return finalize_retract(
|
||||
return finalize_retract::<LS>(
|
||||
&mut self.payload,
|
||||
key,
|
||||
payload_compilation_target,
|
||||
skeleton,
|
||||
code_index,
|
||||
code_idx_offset,
|
||||
target_pos,
|
||||
index_ptr_opt,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
@@ -1765,14 +1757,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
)
|
||||
};
|
||||
|
||||
return finalize_retract(
|
||||
return finalize_retract::<LS>(
|
||||
&mut self.payload,
|
||||
key,
|
||||
payload_compilation_target,
|
||||
skeleton,
|
||||
code_index,
|
||||
code_idx_offset,
|
||||
target_pos,
|
||||
index_ptr_opt,
|
||||
&mut self.payload.retraction_info,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1995,14 +1987,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
};
|
||||
|
||||
finalize_retract(
|
||||
finalize_retract::<LS>(
|
||||
&mut self.payload,
|
||||
key,
|
||||
payload_compilation_target,
|
||||
skeleton,
|
||||
code_index,
|
||||
code_idx_offset,
|
||||
target_pos,
|
||||
index_ptr_opt,
|
||||
&mut self.payload.retraction_info,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2229,18 +2221,22 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
};
|
||||
|
||||
let predicates = self.payload.predicates.take();
|
||||
let code_index = self.compile(key, predicates, settings)?;
|
||||
let offset = self.compile(key, predicates, settings)?;
|
||||
|
||||
if let Some(filename) = self.listing_src_file_name() {
|
||||
if let Some(ref mut module) = self.wam_prelude.indices.modules.get_mut(&filename) {
|
||||
let index_ptr = code_index.get();
|
||||
let code_index = *module.code_dir.entry(key).or_insert(code_index);
|
||||
let index_ptr = LS::machine_st(&mut self.payload)
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(offset.into());
|
||||
|
||||
set_code_index(
|
||||
&mut self.payload.retraction_info,
|
||||
let offset = *module.code_dir.entry(key).or_insert(offset);
|
||||
|
||||
set_code_index::<LS>(
|
||||
&mut self.payload,
|
||||
&CompilationTarget::Module(filename),
|
||||
key,
|
||||
code_index,
|
||||
offset,
|
||||
index_ptr,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,32 +1,368 @@
|
||||
pub struct MachineConfig {
|
||||
pub streams: StreamConfig,
|
||||
pub toplevel: &'static str,
|
||||
}
|
||||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
|
||||
pub enum StreamConfig {
|
||||
Stdio,
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
|
||||
use crate::Machine;
|
||||
|
||||
use super::{
|
||||
bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module, Arena, Atom,
|
||||
Callback, CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState, Stream,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
enum OutputStreamConfigInner {
|
||||
#[default]
|
||||
Memory,
|
||||
Stdout,
|
||||
Stderr,
|
||||
Callback(Callback),
|
||||
}
|
||||
|
||||
impl Default for MachineConfig {
|
||||
impl std::fmt::Debug for OutputStreamConfigInner {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Memory => write!(f, "Memory"),
|
||||
Self::Stdout => write!(f, "Stdout"),
|
||||
Self::Stderr => write!(f, "Stderr"),
|
||||
Self::Callback(_) => f.debug_tuple("Callback").field(&"<callback>").finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for an output stream.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct OutputStreamConfig {
|
||||
inner: OutputStreamConfigInner,
|
||||
}
|
||||
|
||||
impl OutputStreamConfig {
|
||||
/// Sends output to stdout.
|
||||
pub fn stdout() -> Self {
|
||||
Self {
|
||||
inner: OutputStreamConfigInner::Stdout,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends output to stderr.
|
||||
pub fn stderr() -> Self {
|
||||
Self {
|
||||
inner: OutputStreamConfigInner::Stderr,
|
||||
}
|
||||
}
|
||||
|
||||
/// Keeps output in a memory buffer.
|
||||
pub fn memory() -> Self {
|
||||
Self {
|
||||
inner: OutputStreamConfigInner::Memory,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls a callback with the output whenever the stream is written to.
|
||||
pub fn callback(callback: Callback) -> Self {
|
||||
Self {
|
||||
inner: OutputStreamConfigInner::Callback(callback),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_stream(self, arena: &mut Arena) -> Stream {
|
||||
match self.inner {
|
||||
OutputStreamConfigInner::Memory => Stream::from_owned_string("".to_owned(), arena),
|
||||
OutputStreamConfigInner::Stdout => Stream::stdout(arena),
|
||||
OutputStreamConfigInner::Stderr => Stream::stderr(arena),
|
||||
OutputStreamConfigInner::Callback(callback) => Stream::from_callback(callback, arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum InputStreamConfigInner {
|
||||
String(Cow<'static, str>),
|
||||
Stdin,
|
||||
Channel(Receiver<Vec<u8>>),
|
||||
}
|
||||
|
||||
impl Default for InputStreamConfigInner {
|
||||
fn default() -> Self {
|
||||
MachineConfig {
|
||||
streams: StreamConfig::Stdio,
|
||||
toplevel: include_str!("../toplevel.pl"),
|
||||
Self::String("".into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for an input stream;
|
||||
#[derive(Debug, Default)]
|
||||
pub struct InputStreamConfig {
|
||||
inner: InputStreamConfigInner,
|
||||
}
|
||||
|
||||
impl InputStreamConfig {
|
||||
/// Gets input from string.
|
||||
pub fn string(s: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self {
|
||||
inner: InputStreamConfigInner::String(s.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets input from stdin.
|
||||
pub fn stdin() -> Self {
|
||||
Self {
|
||||
inner: InputStreamConfigInner::Stdin,
|
||||
}
|
||||
}
|
||||
|
||||
/// Connects the input to the receiving end of a channel.
|
||||
pub fn channel() -> (UserInput, Self) {
|
||||
let (sender, receiver) = channel();
|
||||
(
|
||||
UserInput { inner: sender },
|
||||
Self {
|
||||
inner: InputStreamConfigInner::Channel(receiver),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn into_stream(self, arena: &mut Arena, add_history: bool) -> Stream {
|
||||
match self.inner {
|
||||
InputStreamConfigInner::String(s) => match s {
|
||||
Cow::Owned(s) => Stream::from_owned_string(s, arena),
|
||||
Cow::Borrowed(s) => Stream::from_static_string(s, arena),
|
||||
},
|
||||
InputStreamConfigInner::Stdin => Stream::stdin(arena, add_history),
|
||||
InputStreamConfigInner::Channel(channel) => Stream::input_channel(channel, arena),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineConfig {
|
||||
pub fn in_memory() -> Self {
|
||||
MachineConfig {
|
||||
streams: StreamConfig::Memory,
|
||||
..Default::default()
|
||||
/// Describes how the streams of a [`Machine`](crate::Machine) will be handled.
|
||||
pub struct StreamConfig {
|
||||
user_input: InputStreamConfig,
|
||||
user_output: OutputStreamConfig,
|
||||
user_error: OutputStreamConfig,
|
||||
}
|
||||
|
||||
impl Default for StreamConfig {
|
||||
fn default() -> Self {
|
||||
Self::in_memory()
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamConfig {
|
||||
/// Binds the input, output and error streams to stdin, stdout and stderr.
|
||||
pub fn stdio() -> Self {
|
||||
StreamConfig {
|
||||
user_input: InputStreamConfig::stdin(),
|
||||
user_output: OutputStreamConfig::stdout(),
|
||||
user_error: OutputStreamConfig::stderr(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_toplevel(mut self, toplevel: &'static str) -> Self {
|
||||
self.toplevel = toplevel;
|
||||
/// Binds the output and error streams to memory buffers and has an empty input.
|
||||
pub fn in_memory() -> Self {
|
||||
StreamConfig {
|
||||
user_input: InputStreamConfig::string(String::new()),
|
||||
user_output: OutputStreamConfig::memory(),
|
||||
user_error: OutputStreamConfig::memory(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls the given callbacks when the respective streams are written to.
|
||||
///
|
||||
/// This also returns a handler to the stdin of the [`Machine`](crate::Machine).
|
||||
pub fn from_callbacks(stdout: Option<Callback>, stderr: Option<Callback>) -> (UserInput, Self) {
|
||||
let (user_input, channel_stream) = InputStreamConfig::channel();
|
||||
(
|
||||
user_input,
|
||||
StreamConfig {
|
||||
user_input: channel_stream,
|
||||
user_output: stdout
|
||||
.map_or_else(OutputStreamConfig::memory, OutputStreamConfig::callback),
|
||||
user_error: stderr
|
||||
.map_or_else(OutputStreamConfig::memory, OutputStreamConfig::callback),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Configures the `user_input` stream.
|
||||
pub fn with_user_input(self, user_input: InputStreamConfig) -> Self {
|
||||
Self { user_input, ..self }
|
||||
}
|
||||
|
||||
/// Configures the `user_output` stream.
|
||||
pub fn with_user_output(self, user_output: OutputStreamConfig) -> Self {
|
||||
Self {
|
||||
user_output,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures the `user_error` stream.
|
||||
pub fn with_user_error(self, user_error: OutputStreamConfig) -> Self {
|
||||
Self { user_error, ..self }
|
||||
}
|
||||
|
||||
fn into_streams(self, arena: &mut Arena, add_history: bool) -> (Stream, Stream, Stream) {
|
||||
(
|
||||
self.user_input.into_stream(arena, add_history),
|
||||
self.user_output.into_stream(arena),
|
||||
self.user_error.into_stream(arena),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler for the stdin of the [`Machine`](crate::Machine).
|
||||
#[derive(Debug)]
|
||||
pub struct UserInput {
|
||||
inner: Sender<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Write for UserInput {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.inner
|
||||
.send(buf.into())
|
||||
.map(|_| buf.len())
|
||||
.map_err(|_| std::io::ErrorKind::BrokenPipe.into())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes how a [`Machine`](crate::Machine) will be configured.
|
||||
pub struct MachineBuilder {
|
||||
pub(crate) streams: StreamConfig,
|
||||
pub(crate) toplevel: Cow<'static, str>,
|
||||
}
|
||||
|
||||
impl Default for MachineBuilder {
|
||||
/// Defaults to using in-memory streams.
|
||||
fn default() -> Self {
|
||||
MachineBuilder {
|
||||
streams: Default::default(),
|
||||
toplevel: default_toplevel().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineBuilder {
|
||||
/// Creates a default configuration.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Uses the given `crate::StreamConfig` in this configuration.
|
||||
pub fn with_streams(mut self, streams: StreamConfig) -> Self {
|
||||
self.streams = streams;
|
||||
self
|
||||
}
|
||||
|
||||
/// Uses the given toplevel in this configuration.
|
||||
pub fn with_toplevel(mut self, toplevel: impl Into<Cow<'static, str>>) -> Self {
|
||||
self.toplevel = toplevel.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the [`Machine`](crate::Machine) from this configuration.
|
||||
pub fn build(self) -> Machine {
|
||||
let args = MachineArgs::new();
|
||||
let mut machine_st = MachineState::new();
|
||||
|
||||
let (user_input, user_output, user_error) = self
|
||||
.streams
|
||||
.into_streams(&mut machine_st.arena, args.add_history);
|
||||
|
||||
let mut wam = Machine {
|
||||
machine_st,
|
||||
indices: IndexStore::new(),
|
||||
code: vec![],
|
||||
user_input,
|
||||
user_output,
|
||||
user_error,
|
||||
load_contexts: vec![],
|
||||
#[cfg(feature = "ffi")]
|
||||
foreign_function_table: Default::default(),
|
||||
rng: StdRng::from_entropy(),
|
||||
};
|
||||
|
||||
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::get("ops_and_meta_predicates")
|
||||
.expect("library ops_and_meta_predicates should exist"),
|
||||
&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::get("builtins").expect("library builtins should exist"),
|
||||
&mut wam.machine_st.arena,
|
||||
),
|
||||
&mut wam,
|
||||
ListingSource::from_file_and_path(atom!("builtins.pl"), lib_path.clone()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let Some(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(self.toplevel);
|
||||
wam.configure_streams();
|
||||
|
||||
wam
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a static string slice to the default toplevel
|
||||
pub fn default_toplevel() -> &'static str {
|
||||
include_str!("../toplevel.pl")
|
||||
}
|
||||
|
||||
@@ -1,12 +1,71 @@
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use crate::atom_table::*;
|
||||
use crate::machine::get_structure_index;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::stack::*;
|
||||
use crate::types::*;
|
||||
|
||||
use std::mem;
|
||||
use std::ops::IndexMut;
|
||||
use scryer_modular_bitfield::specifiers::*;
|
||||
use scryer_modular_bitfield::*;
|
||||
|
||||
type Trail = Vec<(Ref, HeapCellValue)>;
|
||||
use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
use std::ops::{IndexMut, Range};
|
||||
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug)]
|
||||
#[bits = 6]
|
||||
enum TrailRefTag {
|
||||
HeapCell = 0b001011,
|
||||
StackCell = 0b001101,
|
||||
AttrVar = 0b010001,
|
||||
PStrLoc = 0b001111,
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
struct TrailRef {
|
||||
val: B56,
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
#[allow(unused)]
|
||||
f: bool,
|
||||
tag: TrailRefTag,
|
||||
}
|
||||
|
||||
impl TrailRef {
|
||||
#[inline(always)]
|
||||
fn heap_cell(h: usize) -> Self {
|
||||
TrailRef::new()
|
||||
.with_tag(TrailRefTag::HeapCell)
|
||||
.with_val(h as u64)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn stack_cell(h: usize) -> Self {
|
||||
TrailRef::new()
|
||||
.with_tag(TrailRefTag::StackCell)
|
||||
.with_val(h as u64)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn attr_var(h: usize) -> Self {
|
||||
TrailRef::new()
|
||||
.with_tag(TrailRefTag::AttrVar)
|
||||
.with_val(h as u64)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn pstr_loc(h: usize) -> Self {
|
||||
TrailRef::new()
|
||||
.with_tag(TrailRefTag::PStrLoc)
|
||||
.with_val(h as u64)
|
||||
}
|
||||
}
|
||||
|
||||
type Trail = Vec<(TrailRef, HeapCellValue)>;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AttrVarPolicy {
|
||||
@@ -17,22 +76,39 @@ pub enum AttrVarPolicy {
|
||||
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;
|
||||
// returns the tail location of the pstr on success
|
||||
fn as_slice_from<'a>(&'a self, from: usize) -> Box<dyn Iterator<Item = u8> + 'a>;
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize>;
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize>;
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize>;
|
||||
}
|
||||
|
||||
pub(crate) fn copy_term<T: CopierTarget>(
|
||||
target: T,
|
||||
addr: HeapCellValue,
|
||||
attr_var_policy: AttrVarPolicy,
|
||||
) {
|
||||
) -> Result<usize, usize> {
|
||||
let mut copy_term_state = CopyTermState::new(target, attr_var_policy);
|
||||
let old_threshold = copy_term_state.target.threshold();
|
||||
|
||||
copy_term_state.copy_term_impl(addr);
|
||||
copy_term_state.copy_attr_var_lists();
|
||||
copy_term_state.copy_term_impl(addr)?;
|
||||
copy_term_state.copy_attr_var_lists()?;
|
||||
copy_term_state.unwind_trail();
|
||||
|
||||
let new_threshold = copy_term_state.target.threshold();
|
||||
copy_term_state.copy_pstrs()?;
|
||||
|
||||
Ok(new_threshold - old_threshold)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PStrData {
|
||||
pre_old_h_tail_loc: usize,
|
||||
post_old_h_tail_loc: usize,
|
||||
post_old_h_pstr_loc_locs: IndexSet<usize, FxBuildHasher>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -43,6 +119,9 @@ struct CopyTermState<T: CopierTarget> {
|
||||
target: T,
|
||||
attr_var_policy: AttrVarPolicy,
|
||||
attr_var_list_locs: Vec<(usize, HeapCellValue)>,
|
||||
// keys of pstr_loc_locs are byte indices rounded down to the
|
||||
// nearest cell boundary
|
||||
pstr_loc_locs: BTreeMap<usize, PStrData>,
|
||||
}
|
||||
|
||||
impl<T: CopierTarget> CopyTermState<T> {
|
||||
@@ -54,6 +133,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
target,
|
||||
attr_var_policy,
|
||||
attr_var_list_locs: vec![],
|
||||
pstr_loc_locs: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,17 +144,17 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
|
||||
fn trail_list_cell(&mut self, addr: usize, threshold: usize) {
|
||||
let trail_item = mem::replace(&mut self.target[addr], list_loc_as_cell!(threshold));
|
||||
self.trail.push((Ref::heap_cell(addr), trail_item));
|
||||
self.trail.push((TrailRef::heap_cell(addr), trail_item));
|
||||
}
|
||||
|
||||
fn copy_list(&mut self, addr: usize) {
|
||||
fn copy_list(&mut self, addr: usize) -> Result<(), usize> {
|
||||
for offset in 0..2 {
|
||||
read_heap_cell!(self.target[addr + offset],
|
||||
(HeapCellValueTag::Lis, h) => {
|
||||
if h >= self.old_h {
|
||||
*self.value_at_scan() = list_loc_as_cell!(h);
|
||||
self.scan += 1;
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -83,14 +163,10 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
}
|
||||
|
||||
let threshold = self.target.threshold();
|
||||
self.target.copy_slice_to_end(addr..addr + 2)?;
|
||||
|
||||
*self.value_at_scan() = list_loc_as_cell!(threshold);
|
||||
|
||||
for i in 0..2 {
|
||||
let hcv = self.target[addr + i];
|
||||
self.target.push(hcv);
|
||||
}
|
||||
|
||||
let cdr = self
|
||||
.target
|
||||
.store(self.target.deref(heap_loc_as_cell!(addr + 1)));
|
||||
@@ -113,80 +189,108 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
}
|
||||
|
||||
self.scan += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_partial_string(&mut self, scan_tag: HeapCellValueTag, pstr_loc: usize) {
|
||||
read_heap_cell!(self.target[pstr_loc],
|
||||
(HeapCellValueTag::PStrLoc, h) => {
|
||||
debug_assert!(h >= self.old_h);
|
||||
|
||||
*self.value_at_scan() = match scan_tag {
|
||||
HeapCellValueTag::PStrLoc => {
|
||||
pstr_loc_as_cell!(h)
|
||||
}
|
||||
tag => {
|
||||
debug_assert_eq!(tag, HeapCellValueTag::PStrOffset);
|
||||
pstr_offset_as_cell!(h)
|
||||
}
|
||||
};
|
||||
|
||||
fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), usize> {
|
||||
match self.pstr_loc_locs.range_mut(..=pstr_loc).next_back() {
|
||||
Some((
|
||||
_prev_pstr_loc,
|
||||
&mut PStrData {
|
||||
pre_old_h_tail_loc,
|
||||
ref mut post_old_h_pstr_loc_locs,
|
||||
..
|
||||
},
|
||||
)) if pre_old_h_tail_loc >= cell_index!(pstr_loc) => {
|
||||
post_old_h_pstr_loc_locs.insert(self.scan);
|
||||
self.scan += 1;
|
||||
return;
|
||||
}
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
debug_assert!(h >= self.old_h);
|
||||
debug_assert_eq!(scan_tag, HeapCellValueTag::PStrOffset);
|
||||
|
||||
*self.value_at_scan() = pstr_offset_as_cell!(h);
|
||||
self.scan += 1;
|
||||
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
);
|
||||
}
|
||||
|
||||
let threshold = self.target.threshold();
|
||||
let offset = self
|
||||
.target
|
||||
.as_slice_from(pstr_loc)
|
||||
.take_while(|b| *b != 0u8)
|
||||
.count();
|
||||
|
||||
let replacement = read_heap_cell!(self.target[pstr_loc],
|
||||
(HeapCellValueTag::CStr) => {
|
||||
debug_assert_eq!(scan_tag, HeapCellValueTag::PStrOffset);
|
||||
let left_pstr_boundary = cell_index!(pstr_loc + offset);
|
||||
let flag = u64::from_be_bytes(self.target[left_pstr_boundary].into_bytes());
|
||||
let pstr_loc_idx = cell_index!(pstr_loc);
|
||||
|
||||
*self.value_at_scan() = pstr_offset_as_cell!(threshold);
|
||||
self.target.push(self.target[pstr_loc]);
|
||||
if flag == 1 {
|
||||
if left_pstr_boundary != pstr_loc_idx {
|
||||
let mut pstr_data = self
|
||||
.pstr_loc_locs
|
||||
.remove(&heap_index!(left_pstr_boundary))
|
||||
.unwrap();
|
||||
|
||||
heap_loc_as_cell!(threshold)
|
||||
pstr_data.post_old_h_pstr_loc_locs.insert(self.scan);
|
||||
self.pstr_loc_locs
|
||||
.insert(heap_index!(cell_index!(pstr_loc)), pstr_data);
|
||||
|
||||
let old_cell = self.target[pstr_loc_idx];
|
||||
self.target[pstr_loc_idx] = HeapCellValue::from_bytes(u64::to_be_bytes(1));
|
||||
self.trail
|
||||
.push((TrailRef::pstr_loc(pstr_loc_idx), old_cell));
|
||||
} else {
|
||||
let pstr_data = self
|
||||
.pstr_loc_locs
|
||||
.get_mut(&heap_index!(left_pstr_boundary))
|
||||
.unwrap();
|
||||
pstr_data.post_old_h_pstr_loc_locs.insert(self.scan);
|
||||
}
|
||||
_ => {
|
||||
*self.value_at_scan() = if scan_tag == HeapCellValueTag::PStrLoc {
|
||||
pstr_loc_as_cell!(threshold)
|
||||
} else {
|
||||
debug_assert_eq!(scan_tag, HeapCellValueTag::PStrOffset);
|
||||
pstr_offset_as_cell!(threshold)
|
||||
};
|
||||
} else {
|
||||
let old_cell = self.target[pstr_loc_idx];
|
||||
self.target[pstr_loc_idx] = HeapCellValue::from_bytes(u64::to_be_bytes(1));
|
||||
self.trail
|
||||
.push((TrailRef::pstr_loc(pstr_loc_idx), old_cell));
|
||||
|
||||
self.target.push(self.target[pstr_loc]);
|
||||
self.target.push(self.target[pstr_loc + 1]);
|
||||
let old_tail_idx = if (pstr_loc + offset + 1) % Heap::heap_cell_alignment() == 0 {
|
||||
cell_index!(pstr_loc + offset) + 2
|
||||
} else {
|
||||
cell_index!(pstr_loc + offset) + 1
|
||||
};
|
||||
|
||||
pstr_loc_as_cell!(threshold)
|
||||
}
|
||||
);
|
||||
let tail_cell = self.target[old_tail_idx];
|
||||
|
||||
let new_tail_idx = self.target.threshold();
|
||||
let mut writer = self.target.reserve(1)?;
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(tail_cell);
|
||||
});
|
||||
|
||||
let mut post_old_h_pstr_loc_locs = IndexSet::with_hasher(FxBuildHasher::default());
|
||||
post_old_h_pstr_loc_locs.insert(self.scan);
|
||||
|
||||
let pstr_data = PStrData {
|
||||
pre_old_h_tail_loc: old_tail_idx,
|
||||
post_old_h_tail_loc: new_tail_idx,
|
||||
post_old_h_pstr_loc_locs,
|
||||
};
|
||||
|
||||
self.pstr_loc_locs
|
||||
.insert(heap_index!(pstr_loc_idx), pstr_data);
|
||||
}
|
||||
|
||||
self.scan += 1;
|
||||
|
||||
let trail_item = mem::replace(&mut self.target[pstr_loc], replacement);
|
||||
self.trail.push((Ref::heap_cell(pstr_loc), trail_item));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_attr_var_lists(&mut self) {
|
||||
fn copy_attr_var_lists(&mut self) -> Result<(), usize> {
|
||||
while !self.attr_var_list_locs.is_empty() {
|
||||
let iter = std::mem::take(&mut self.attr_var_list_locs);
|
||||
let mut list_loc_vec = std::mem::take(&mut self.attr_var_list_locs);
|
||||
|
||||
for (threshold, list_loc) in iter {
|
||||
while let Some((threshold, list_loc)) = list_loc_vec.pop() {
|
||||
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);
|
||||
self.copy_attr_var_list(list_loc)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -194,48 +298,49 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
* structure which is ensured by this function and not at all by
|
||||
* the vanilla copier.
|
||||
*/
|
||||
fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) {
|
||||
fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) -> Result<(), usize> {
|
||||
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_cell = self.target[str_loc];
|
||||
let mut writer = self.target.reserve(3).unwrap();
|
||||
|
||||
self.target.push(heap_loc_as_cell!(threshold + 2));
|
||||
self.target.push(heap_loc_as_cell!(threshold + 1));
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(heap_loc_as_cell!(threshold + 2));
|
||||
section.push_cell(heap_loc_as_cell!(threshold + 1));
|
||||
|
||||
read_heap_cell!(self.target[str_loc],
|
||||
(HeapCellValueTag::Atom) => {
|
||||
self.target.push(self.target[str_loc]);
|
||||
if str_cell.to_atom().is_some() {
|
||||
section.push_cell(str_cell);
|
||||
}
|
||||
(HeapCellValueTag::Str) => {
|
||||
self.copy_term_impl(self.target[str_loc]);
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
debug_assert_eq!(str_cell.get_tag(), HeapCellValueTag::Str);
|
||||
|
||||
self.copy_term_impl(str_cell)?;
|
||||
list_addr = self.target[heap_loc + 1];
|
||||
|
||||
if HeapCellValueTag::Lis == list_addr.get_tag() {
|
||||
self.target[threshold + 1] = list_loc_as_cell!(self.target.threshold());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) {
|
||||
fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) -> Result<(), usize> {
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Var, h) => {
|
||||
self.target[frontier] = heap_loc_as_cell!(frontier);
|
||||
self.target[h] = heap_loc_as_cell!(frontier);
|
||||
|
||||
self.trail.push((Ref::heap_cell(h), heap_loc_as_cell!(h)));
|
||||
self.trail.push((TrailRef::heap_cell(h), heap_loc_as_cell!(h)));
|
||||
}
|
||||
(HeapCellValueTag::StackVar, s) => {
|
||||
self.target[frontier] = heap_loc_as_cell!(frontier);
|
||||
self.target.stack()[s] = heap_loc_as_cell!(frontier);
|
||||
|
||||
self.trail.push((Ref::stack_cell(s), stack_loc_as_cell!(s)));
|
||||
self.trail.push((TrailRef::stack_cell(s), stack_loc_as_cell!(s)));
|
||||
}
|
||||
(HeapCellValueTag::AttrVar, h) => {
|
||||
let threshold = if let AttrVarPolicy::DeepCopy = self.attr_var_policy {
|
||||
@@ -247,14 +352,18 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
self.target[frontier] = heap_loc_as_cell!(threshold);
|
||||
self.target[h] = heap_loc_as_cell!(threshold);
|
||||
|
||||
self.trail.push((Ref::attr_var(h), attr_var_as_cell!(h)));
|
||||
self.trail.push((TrailRef::attr_var(h), attr_var_as_cell!(h)));
|
||||
|
||||
if let AttrVarPolicy::DeepCopy = self.attr_var_policy {
|
||||
self.target.push(attr_var_as_cell!(threshold));
|
||||
self.target.push(heap_loc_as_cell!(threshold + 1));
|
||||
let mut writer = self.target.reserve(2)?;
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(attr_var_as_cell!(threshold));
|
||||
section.push_cell(heap_loc_as_cell!(threshold + 1));
|
||||
});
|
||||
|
||||
let old_list_link = self.target[h + 1];
|
||||
self.trail.push((Ref::heap_cell(h + 1), old_list_link));
|
||||
self.trail.push((TrailRef::heap_cell(h + 1), old_list_link));
|
||||
self.target[h + 1] = heap_loc_as_cell!(threshold + 1);
|
||||
|
||||
if old_list_link.get_tag() == HeapCellValueTag::Lis {
|
||||
@@ -266,9 +375,11 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
unreachable!()
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_var(&mut self, addr: HeapCellValue) {
|
||||
fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), usize> {
|
||||
let index = addr.get_value() as usize;
|
||||
let rd = self.target.deref(addr);
|
||||
let ra = self.target.store(rd);
|
||||
@@ -278,7 +389,7 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
if h >= self.old_h {
|
||||
*self.value_at_scan() = ra;
|
||||
self.scan += 1;
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis, h) => {
|
||||
@@ -292,47 +403,52 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
);
|
||||
|
||||
self.scan += 1;
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
);
|
||||
|
||||
if rd == ra {
|
||||
self.reinstantiate_var(ra, self.scan);
|
||||
self.reinstantiate_var(ra, self.scan)?;
|
||||
self.scan += 1;
|
||||
} else {
|
||||
*self.value_at_scan() = ra;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_structure(&mut self, addr: usize) {
|
||||
fn copy_structure(&mut self, addr: usize) -> Result<(), usize> {
|
||||
read_heap_cell!(self.target[addr],
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
(HeapCellValueTag::Atom, (_name, arity)) => {
|
||||
let threshold = self.target.threshold();
|
||||
|
||||
*self.value_at_scan() = str_loc_as_cell!(threshold);
|
||||
let index_cell = self.target[addr.saturating_sub(1)];
|
||||
|
||||
let str_cell = if get_structure_index(index_cell).is_some() {
|
||||
// copy the index pointer trailing this
|
||||
// inlined or expanded goal.
|
||||
let mut writer = self.target.reserve(1).unwrap();
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(index_cell);
|
||||
});
|
||||
|
||||
str_loc_as_cell!(threshold + 1)
|
||||
} else {
|
||||
str_loc_as_cell!(threshold)
|
||||
};
|
||||
|
||||
*self.value_at_scan() = str_cell;
|
||||
self.target.copy_slice_to_end(addr .. addr + 1 + arity)?;
|
||||
|
||||
let trail_item = mem::replace(
|
||||
&mut self.target[addr],
|
||||
str_loc_as_cell!(threshold),
|
||||
str_cell,
|
||||
);
|
||||
|
||||
self.trail.push((Ref::heap_cell(addr), trail_item));
|
||||
self.target.push(atom_as_cell!(name, arity));
|
||||
|
||||
for i in 0..arity {
|
||||
let hcv = self.target[addr + 1 + i];
|
||||
self.target.push(hcv);
|
||||
}
|
||||
|
||||
let index_cell = self.target[addr + 1 + arity];
|
||||
|
||||
if get_structure_index(index_cell).is_some() {
|
||||
// copy the index pointer trailing this
|
||||
// inlined or expanded goal.
|
||||
self.target.push(index_cell);
|
||||
}
|
||||
self.trail.push((TrailRef::heap_cell(addr), trail_item));
|
||||
}
|
||||
(HeapCellValueTag::Str, h) => {
|
||||
*self.value_at_scan() = str_loc_as_cell!(h);
|
||||
@@ -343,11 +459,16 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
);
|
||||
|
||||
self.scan += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_term_impl(&mut self, addr: HeapCellValue) {
|
||||
fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), usize> {
|
||||
self.scan = self.target.threshold();
|
||||
self.target.push(addr);
|
||||
let mut writer = self.target.reserve(1)?;
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(addr);
|
||||
});
|
||||
|
||||
while self.scan < self.target.threshold() {
|
||||
let addr = *self.value_at_scan();
|
||||
@@ -356,37 +477,64 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
(HeapCellValueTag::Lis, h) => {
|
||||
if h >= self.old_h {
|
||||
self.scan += 1;
|
||||
continue;
|
||||
} else {
|
||||
self.copy_list(h);
|
||||
self.copy_list(h)
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var) => {
|
||||
self.copy_var(addr);
|
||||
self.copy_var(addr)
|
||||
}
|
||||
(HeapCellValueTag::Str, h) => {
|
||||
self.copy_structure(h);
|
||||
self.copy_structure(h)
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc | HeapCellValueTag::PStrOffset, pstr_loc) => {
|
||||
self.copy_partial_string(addr.get_tag(), pstr_loc);
|
||||
(HeapCellValueTag::PStrLoc, pstr_loc) => {
|
||||
self.copy_partial_string(pstr_loc)
|
||||
}
|
||||
_ => {
|
||||
self.scan += 1;
|
||||
continue;
|
||||
}
|
||||
);
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unwind_trail(mut self) {
|
||||
for (r, value) in self.trail {
|
||||
let index = r.get_value() as usize;
|
||||
fn copy_pstrs(&mut self) -> Result<(), usize> {
|
||||
while let Some((least_pstr_loc, pstr_data)) = self.pstr_loc_locs.pop_first() {
|
||||
let threshold = heap_index!(self.target.threshold());
|
||||
|
||||
match r.get_tag() {
|
||||
RefTag::AttrVar | RefTag::HeapCell => {
|
||||
for pstr_loc_loc in pstr_data.post_old_h_pstr_loc_locs {
|
||||
let pstr_loc = self.target[pstr_loc_loc].get_value() as usize;
|
||||
self.target[pstr_loc_loc] =
|
||||
pstr_loc_as_cell!(threshold + pstr_loc - least_pstr_loc);
|
||||
}
|
||||
|
||||
self.target.copy_pstr_to_threshold(least_pstr_loc)?;
|
||||
|
||||
let mut writer = self.target.reserve(1)?;
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(heap_loc_as_cell!(pstr_data.post_old_h_tail_loc));
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unwind_trail(&mut self) {
|
||||
for (r, value) in self.trail.drain(..) {
|
||||
let index = r.val() as usize;
|
||||
|
||||
match r.tag() {
|
||||
TrailRefTag::AttrVar | TrailRefTag::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,
|
||||
TrailRefTag::StackCell => self.target.stack()[index] = value,
|
||||
TrailRefTag::PStrLoc => self.target[index] = value,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -395,20 +543,26 @@ impl<T: CopierTarget> CopyTermState<T> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::functor_macro::*;
|
||||
use crate::machine::mock_wam::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
|
||||
fn copier_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
// clear the heap of resource error data etc
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
let f_atom = atom!("f");
|
||||
let a_atom = atom!("a");
|
||||
let b_atom = atom!("b");
|
||||
|
||||
wam.machine_st
|
||||
.heap
|
||||
.extend(functor!(f_atom, [atom(a_atom), atom(b_atom)]));
|
||||
let mut functor_writer = Heap::functor_writer(functor!(
|
||||
f_atom,
|
||||
[atom_as_cell(a_atom), atom_as_cell(b_atom)]
|
||||
));
|
||||
|
||||
functor_writer(&mut wam.machine_st.heap).unwrap();
|
||||
|
||||
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));
|
||||
@@ -416,7 +570,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let wam = TermCopyingMockWAM { wam: &mut wam };
|
||||
copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy);
|
||||
copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap();
|
||||
}
|
||||
|
||||
// check that the original heap state is still intact.
|
||||
@@ -431,69 +585,266 @@ mod tests {
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
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];
|
||||
let mut writer = wam.machine_st.heap.reserve(4).unwrap();
|
||||
|
||||
wam.machine_st.heap.pop();
|
||||
wam.machine_st.heap.push(pstr_loc_as_cell!(2));
|
||||
writer.write_with(|section| {
|
||||
section.push_pstr("abc ");
|
||||
section.push_cell(pstr_loc_as_cell!(heap_index!(2)));
|
||||
|
||||
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_offset_as_cell!(0));
|
||||
wam.machine_st
|
||||
.heap
|
||||
.push(fixnum_as_cell!(Fixnum::build_with(0i64)));
|
||||
section.push_pstr("def");
|
||||
section.push_cell(pstr_loc_as_cell!(0));
|
||||
});
|
||||
|
||||
{
|
||||
let wam = TermCopyingMockWAM { wam: &mut wam };
|
||||
copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy);
|
||||
copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap();
|
||||
}
|
||||
|
||||
print_heap_terms(wam.machine_st.heap[6..].iter(), 6);
|
||||
|
||||
assert_eq!(wam.machine_st.heap[0], pstr_cell);
|
||||
assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(2));
|
||||
assert_eq!(wam.machine_st.heap[2], pstr_second_cell);
|
||||
assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(4));
|
||||
assert_eq!(wam.machine_st.heap[4], pstr_offset_as_cell!(0));
|
||||
assert_eq!(wam.machine_st.heap.slice_to_str(0, "abc ".len()), "abc ");
|
||||
assert_eq!(wam.machine_st.heap[1], pstr_loc_as_cell!(heap_index!(2)));
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[5],
|
||||
fixnum_as_cell!(Fixnum::build_with(0i64))
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(2), "def".len()),
|
||||
"def"
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(0));
|
||||
|
||||
assert_eq!(wam.machine_st.heap[4], pstr_loc_as_cell!(heap_index!(7)));
|
||||
assert_eq!(wam.machine_st.heap[5], pstr_loc_as_cell!(heap_index!(9)));
|
||||
assert_eq!(wam.machine_st.heap[6], pstr_loc_as_cell!(heap_index!(7)));
|
||||
|
||||
assert_eq!(wam.machine_st.heap[7], pstr_cell);
|
||||
assert_eq!(wam.machine_st.heap[8], pstr_loc_as_cell!(9));
|
||||
assert_eq!(wam.machine_st.heap[9], pstr_second_cell);
|
||||
assert_eq!(wam.machine_st.heap[10], pstr_loc_as_cell!(11));
|
||||
assert_eq!(wam.machine_st.heap[11], pstr_offset_as_cell!(7));
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[12],
|
||||
fixnum_as_cell!(Fixnum::build_with(0i64))
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(7), "abc ".len()),
|
||||
"abc "
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[8], heap_loc_as_cell!(5));
|
||||
assert_eq!(
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(9), "def".len()),
|
||||
"def"
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[10], heap_loc_as_cell!(6));
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
wam.machine_st.heap.extend(functor!(
|
||||
f_atom,
|
||||
[
|
||||
atom(a_atom),
|
||||
atom(b_atom),
|
||||
atom(a_atom),
|
||||
cell(str_loc_as_cell!(0))
|
||||
]
|
||||
));
|
||||
let mut writer = wam.machine_st.heap.reserve(4).unwrap();
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_pstr("abc ");
|
||||
section.push_cell(pstr_loc_as_cell!(heap_index!(2) + 9));
|
||||
|
||||
section.push_pstr("defdefdefdef");
|
||||
section.push_cell(pstr_loc_as_cell!(0));
|
||||
});
|
||||
|
||||
{
|
||||
let wam = TermCopyingMockWAM { wam: &mut wam };
|
||||
copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy);
|
||||
copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(wam.machine_st.heap.slice_to_str(0, "abc ".len()), "abc ");
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[1],
|
||||
pstr_loc_as_cell!(heap_index!(2) + 9)
|
||||
);
|
||||
assert_eq!(
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(2), "defdefdefdef".len()),
|
||||
"defdefdefdef"
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[4], pstr_loc_as_cell!(0));
|
||||
|
||||
assert_eq!(wam.machine_st.heap[5], pstr_loc_as_cell!(heap_index!(8)));
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[6],
|
||||
pstr_loc_as_cell!(heap_index!(10) + 1)
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[7], pstr_loc_as_cell!(heap_index!(8)));
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(8), "abc ".len()),
|
||||
"abc "
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[9], heap_loc_as_cell!(6));
|
||||
assert_eq!(
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(10), "fdef".len()),
|
||||
"fdef"
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[11], heap_loc_as_cell!(7));
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
let mut writer = wam.machine_st.heap.reserve(4).unwrap();
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_pstr("012345678912345");
|
||||
section.push_cell(pstr_loc_as_cell!(heap_index!(0)));
|
||||
});
|
||||
|
||||
{
|
||||
let wam = TermCopyingMockWAM { wam: &mut wam };
|
||||
copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap.slice_to_str(0, "012345678912345".len()),
|
||||
"012345678912345"
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(heap_index!(0)));
|
||||
|
||||
assert_eq!(wam.machine_st.heap[4], pstr_loc_as_cell!(heap_index!(6)));
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(6), "012345678912345".len()),
|
||||
"012345678912345"
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[5], pstr_loc_as_cell!(heap_index!(6)));
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
let mut writer = wam.machine_st.heap.reserve(4).unwrap();
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_pstr("012345678912345");
|
||||
section.push_cell(pstr_loc_as_cell!(heap_index!(0) + 9));
|
||||
});
|
||||
|
||||
{
|
||||
let wam = TermCopyingMockWAM { wam: &mut wam };
|
||||
copy_term(wam, pstr_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap.slice_to_str(0, "012345678912345".len()),
|
||||
"012345678912345"
|
||||
);
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[3],
|
||||
pstr_loc_as_cell!(heap_index!(0) + 9)
|
||||
);
|
||||
|
||||
assert_eq!(wam.machine_st.heap[4], pstr_loc_as_cell!(heap_index!(6)));
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[5],
|
||||
pstr_loc_as_cell!(heap_index!(6) + 9)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(6), "012345678912345".len()),
|
||||
"012345678912345"
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[9], heap_loc_as_cell!(5));
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
let mut writer = wam.machine_st.heap.reserve(4).unwrap();
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_pstr("012345678912345");
|
||||
section.push_cell(pstr_loc_as_cell!(heap_index!(0) + 7));
|
||||
});
|
||||
|
||||
{
|
||||
let wam = TermCopyingMockWAM { wam: &mut wam };
|
||||
copy_term(wam, pstr_loc_as_cell!(11), AttrVarPolicy::DeepCopy).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap.slice_to_str(0, "012345678912345".len()),
|
||||
"012345678912345"
|
||||
);
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[3],
|
||||
pstr_loc_as_cell!(heap_index!(0) + 7)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[4],
|
||||
pstr_loc_as_cell!(heap_index!(6) + 11)
|
||||
);
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[5],
|
||||
pstr_loc_as_cell!(heap_index!(6) + 7)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(6), "012345678912345".len()),
|
||||
"012345678912345"
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[9], heap_loc_as_cell!(5));
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
let mut writer = wam.machine_st.heap.reserve(4).unwrap();
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_pstr("012345678912345");
|
||||
section.push_cell(pstr_loc_as_cell!(heap_index!(0) + 12));
|
||||
});
|
||||
|
||||
{
|
||||
let wam = TermCopyingMockWAM { wam: &mut wam };
|
||||
copy_term(wam, pstr_loc_as_cell!(11), AttrVarPolicy::DeepCopy).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap.slice_to_str(0, "012345678912345".len()),
|
||||
"012345678912345"
|
||||
);
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[3],
|
||||
pstr_loc_as_cell!(heap_index!(0) + 12)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[4],
|
||||
pstr_loc_as_cell!(heap_index!(6) + 3)
|
||||
);
|
||||
assert_eq!(
|
||||
wam.machine_st.heap[5],
|
||||
pstr_loc_as_cell!(heap_index!(6) + 4)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
wam.machine_st
|
||||
.heap
|
||||
.slice_to_str(heap_index!(6), "8912345".len()),
|
||||
"8912345"
|
||||
);
|
||||
assert_eq!(wam.machine_st.heap[8], heap_loc_as_cell!(5));
|
||||
|
||||
wam.machine_st.heap.clear();
|
||||
|
||||
let mut functor_writer = Heap::functor_writer(functor!(
|
||||
f_atom,
|
||||
[
|
||||
atom_as_cell(a_atom),
|
||||
atom_as_cell(b_atom),
|
||||
atom_as_cell(a_atom),
|
||||
str_loc_as_cell(0)
|
||||
]
|
||||
));
|
||||
|
||||
functor_writer(&mut wam.machine_st.heap).unwrap();
|
||||
|
||||
{
|
||||
let wam = TermCopyingMockWAM { wam: &mut wam };
|
||||
copy_term(wam, str_loc_as_cell!(0), AttrVarPolicy::DeepCopy).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(wam.machine_st.heap[0], atom_as_cell!(f_atom, 4));
|
||||
@@ -501,7 +852,6 @@ mod tests {
|
||||
assert_eq!(wam.machine_st.heap[2], atom_as_cell!(b_atom));
|
||||
assert_eq!(wam.machine_st.heap[3], atom_as_cell!(a_atom));
|
||||
assert_eq!(wam.machine_st.heap[4], str_loc_as_cell!(0));
|
||||
|
||||
assert_eq!(wam.machine_st.heap[5], str_loc_as_cell!(6));
|
||||
assert_eq!(wam.machine_st.heap[6], atom_as_cell!(f_atom, 4));
|
||||
assert_eq!(wam.machine_st.heap[7], atom_as_cell!(a_atom));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::atom_table::*;
|
||||
use crate::machine::heap::*;
|
||||
use crate::types::*;
|
||||
|
||||
/* Use the pointer reversal technique of the Deutsch-Schorr-Waite
|
||||
@@ -11,18 +12,18 @@ use crate::types::*;
|
||||
* - 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;
|
||||
* 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],
|
||||
pub(crate) heap: &'a mut Heap,
|
||||
start: usize,
|
||||
current: usize,
|
||||
next: u64,
|
||||
@@ -31,7 +32,7 @@ pub(crate) struct CycleDetectingIter<'a, const STOP_AT_CYCLES: bool> {
|
||||
}
|
||||
|
||||
impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
pub(crate) fn new(heap: &'a mut [HeapCellValue], start: usize) -> Self {
|
||||
pub(crate) fn new(heap: &'a mut Heap, start: usize) -> Self {
|
||||
heap[start].set_forwarding_bit(true);
|
||||
let next = heap[start].get_value();
|
||||
|
||||
@@ -127,7 +128,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
self.current = next;
|
||||
self.next = temp;
|
||||
|
||||
if self.next < self.heap.len() as u64 {
|
||||
if self.next < self.heap.cell_len() as u64 {
|
||||
return Some(HeapCellValue::build_with(tag, next as u64));
|
||||
}
|
||||
}
|
||||
@@ -136,10 +137,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
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,
|
||||
};
|
||||
let last_cell_loc = self.traverse_subterm(h + 1, arity)?;
|
||||
|
||||
if last_cell_loc == h {
|
||||
if self.backward() {
|
||||
@@ -170,10 +168,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
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,
|
||||
};
|
||||
let last_cell_loc = self.traverse_subterm(self.next as usize, 2)?;
|
||||
|
||||
if self.cycle_detection_active() {
|
||||
for idx in (self.next as usize..last_cell_loc).rev() {
|
||||
@@ -205,10 +200,9 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
}
|
||||
HeapCellValueTag::PStrLoc => {
|
||||
let h = self.next as usize;
|
||||
let cell = self.heap[h];
|
||||
let last_cell_loc = h + 1;
|
||||
let tail_idx = self.heap.scan_slice_to_str(h).tail_idx;
|
||||
|
||||
if self.heap[last_cell_loc].get_forwarding_bit() {
|
||||
if self.heap[tail_idx].get_forwarding_bit() {
|
||||
if self.cycle_detection_active() {
|
||||
self.cycle_found = true;
|
||||
return None;
|
||||
@@ -219,45 +213,13 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.heap[last_cell_loc].set_forwarding_bit(true);
|
||||
self.heap[tail_idx].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;
|
||||
self.next = self.heap[tail_idx].get_value();
|
||||
self.heap[tail_idx].set_value(self.current as u64);
|
||||
self.current = tail_idx;
|
||||
|
||||
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);
|
||||
return Some(pstr_loc_as_cell!(h));
|
||||
}
|
||||
tag @ HeapCellValueTag::Atom => {
|
||||
let cell = HeapCellValue::build_with(tag, self.next);
|
||||
@@ -269,11 +231,6 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
HeapCellValueTag::PStr => {
|
||||
if self.backward() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Some(self.backward_and_return());
|
||||
}
|
||||
|
||||
@@ -95,11 +95,6 @@ pub struct ChunkInfo {
|
||||
vars: Vec<VarInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BranchArm {
|
||||
pub arm_terms: Vec<QueryTerm>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct BranchInfo {
|
||||
branch_num: BranchNumber,
|
||||
@@ -205,10 +200,12 @@ impl VarData {
|
||||
|
||||
match build_stack.front_mut() {
|
||||
Some(ChunkedTerms::Branch(_)) => {
|
||||
build_stack.push_front(ChunkedTerms::Chunk(VecDeque::from(vec![term])));
|
||||
build_stack.push_front(ChunkedTerms::Chunk {
|
||||
terms: VecDeque::from(vec![term]),
|
||||
});
|
||||
}
|
||||
Some(ChunkedTerms::Chunk(chunk)) => {
|
||||
chunk.push_front(term);
|
||||
Some(ChunkedTerms::Chunk { terms, .. }) => {
|
||||
terms.push_front(term);
|
||||
}
|
||||
None => {
|
||||
unreachable!()
|
||||
@@ -428,7 +425,6 @@ impl VariableClassifier {
|
||||
// "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();
|
||||
|
||||
if needs_new_branch {
|
||||
@@ -583,7 +579,7 @@ impl VariableClassifier {
|
||||
mut terms,
|
||||
) if terms.len() == 3 => {
|
||||
if let Some(last_arg) = terms.last() {
|
||||
if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg {
|
||||
if let Term::Literal(_, Literal::CodeIndexOffset(_)) = last_arg {
|
||||
terms.pop();
|
||||
state_stack.push(TraversalState::Term(Term::Clause(
|
||||
Cell::default(),
|
||||
@@ -806,7 +802,7 @@ impl VariableClassifier {
|
||||
self.call_policy,
|
||||
));
|
||||
}
|
||||
Term::Literal(_, Literal::Atom(atom!("!")) | Literal::Char('!')) => {
|
||||
Term::Literal(_, Literal::Atom(atom!("!"))) => {
|
||||
let (var_num, is_global) =
|
||||
if let Some(var_num) = self.global_cut_var_num_override {
|
||||
(var_num, false)
|
||||
@@ -884,10 +880,10 @@ 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);
|
||||
let context = var_info.chunk_type.to_gen_context(chunk.chunk_num);
|
||||
temp_var_data
|
||||
.use_set
|
||||
.insert((term_loc, var_info.classify_info.arg_c));
|
||||
.insert((context, var_info.classify_info.arg_c));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1567
src/machine/gc.rs
1567
src/machine/gc.rs
File diff suppressed because it is too large
Load Diff
1268
src/machine/heap.rs
1268
src/machine/heap.rs
File diff suppressed because it is too large
Load Diff
@@ -1,612 +0,0 @@
|
||||
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"))
|
||||
);
|
||||
}
|
||||
}
|
||||
621
src/machine/lib_machine/mod.rs
Normal file
621
src/machine/lib_machine/mod.rs
Normal file
@@ -0,0 +1,621 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::atom_table;
|
||||
use crate::heap_iter::{stackful_post_order_iter, NonListElider};
|
||||
use crate::machine::machine_indices::VarKey;
|
||||
use crate::machine::mock_wam::CompositeOpDir;
|
||||
use crate::machine::{
|
||||
ArenaHeaderTag, Fixnum, Number, BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS,
|
||||
};
|
||||
use crate::offset_table::*;
|
||||
use crate::parser::ast::{Var, VarPtr};
|
||||
use crate::parser::parser::{Parser, Tokens};
|
||||
use crate::read::{write_term_to_heap, TermWriteResult};
|
||||
use crate::types::UntypedArenaPtr;
|
||||
|
||||
use dashu::{Integer, Rational};
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use super::{streams::Stream, Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// Represents a leaf answer from a query.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum LeafAnswer {
|
||||
/// A `true` leaf answer.
|
||||
True,
|
||||
/// A `false` leaf answer.
|
||||
///
|
||||
/// This means that there are no more answers for the query.
|
||||
False,
|
||||
/// An exception leaf answer.
|
||||
Exception(Term),
|
||||
/// A leaf answer with bindings.
|
||||
#[non_exhaustive]
|
||||
LeafAnswer {
|
||||
/// The bindings of variables in the query.
|
||||
bindings: BTreeMap<String, Term>,
|
||||
//residual_goals: Vec<Term>,
|
||||
},
|
||||
}
|
||||
|
||||
impl LeafAnswer {
|
||||
/// Creates a leaf answer with no residual goals.
|
||||
pub fn from_bindings<S: Into<String>>(bindings: impl IntoIterator<Item = (S, Term)>) -> Self {
|
||||
LeafAnswer::LeafAnswer {
|
||||
bindings: bindings.into_iter().map(|(k, v)| (k.into(), v)).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a Prolog term.
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Term {
|
||||
/// An arbitrary precision integer.
|
||||
Integer(Integer),
|
||||
/// An arbitrary precision rational.
|
||||
Rational(Rational),
|
||||
/// A float.
|
||||
Float(f64),
|
||||
/// A Prolog atom.
|
||||
Atom(String),
|
||||
/// A Prolog string.
|
||||
///
|
||||
/// In particular, this represents Prolog lists of characters.
|
||||
String(String),
|
||||
/// A Prolog list.
|
||||
List(Vec<Term>),
|
||||
/// A Prolog compound term.
|
||||
Compound(String, Vec<Term>),
|
||||
/// A Prolog variable.
|
||||
Var(String),
|
||||
}
|
||||
|
||||
impl Term {
|
||||
/// Creates an integer term.
|
||||
pub fn integer(value: impl Into<Integer>) -> Self {
|
||||
Term::Integer(value.into())
|
||||
}
|
||||
|
||||
/// Creates a rational term.
|
||||
pub fn rational(value: impl Into<Rational>) -> Self {
|
||||
Term::Rational(value.into())
|
||||
}
|
||||
|
||||
/// Creates a float term.
|
||||
pub fn float(value: impl Into<f64>) -> Self {
|
||||
Term::Float(value.into())
|
||||
}
|
||||
|
||||
/// Creates an atom term.
|
||||
pub fn atom(value: impl Into<String>) -> Self {
|
||||
Term::Atom(value.into())
|
||||
}
|
||||
|
||||
/// Creates a string term.
|
||||
///
|
||||
/// In specific, this represents a list of chars in Prolog.
|
||||
pub fn string(value: impl Into<String>) -> Self {
|
||||
Term::String(value.into())
|
||||
}
|
||||
|
||||
/// Creates a list term.
|
||||
pub fn list(value: impl IntoIterator<Item = Term>) -> Self {
|
||||
Term::List(value.into_iter().collect())
|
||||
}
|
||||
|
||||
/// Creates a compound term.
|
||||
pub fn compound(functor: impl Into<String>, args: impl IntoIterator<Item = Term>) -> Self {
|
||||
Term::Compound(functor.into(), args.into_iter().collect())
|
||||
}
|
||||
|
||||
/// Creates a variable.
|
||||
pub fn variable(value: impl Into<String>) -> Self {
|
||||
Term::Var(value.into())
|
||||
}
|
||||
|
||||
/// Creates a conjunction, giving the atom `true` if empty.
|
||||
pub fn conjunction(value: impl IntoIterator<Item = Term>) -> Self {
|
||||
Term::try_conjunction(value).unwrap_or(Term::atom("true"))
|
||||
}
|
||||
|
||||
/// Creates a conjunction, giving `None` if empty.
|
||||
pub fn try_conjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> {
|
||||
let mut iter = value.into_iter();
|
||||
iter.next().map(|first| {
|
||||
Term::try_conjunction(iter)
|
||||
.map(|rest| Term::compound(",", [first.clone(), rest]))
|
||||
.unwrap_or(first)
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a disjunction, giving the atom `false` if empty.
|
||||
pub fn disjunction(value: impl IntoIterator<Item = Term>) -> Self {
|
||||
Term::try_disjunction(value).unwrap_or(Term::atom("false"))
|
||||
}
|
||||
|
||||
/// Creates a disjunction, giving `None` if empty.
|
||||
pub fn try_disjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> {
|
||||
let mut iter = value.into_iter();
|
||||
iter.next().map(|first| {
|
||||
Term::try_disjunction(iter)
|
||||
.map(|rest| Term::compound(";", [first.clone(), rest]))
|
||||
.unwrap_or(first)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// This is an auxiliary function to turn a count into names of anonymous variables like _A, _B,
|
||||
/// _AB, etc...
|
||||
fn count_to_letter_code(mut count: usize) -> String {
|
||||
let mut letters = Vec::new();
|
||||
|
||||
loop {
|
||||
let letter_idx = (count % 26) as u32;
|
||||
letters.push(char::from_u32('A' as u32 + letter_idx).unwrap());
|
||||
count /= 26;
|
||||
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
letters.into_iter().chain("_".chars()).rev().collect()
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub(crate) fn from_heapcell(
|
||||
machine: &mut Machine,
|
||||
heap_cell: HeapCellValue,
|
||||
var_names: &mut IndexMap<HeapCellValue, VarPtr>,
|
||||
) -> Self {
|
||||
// Adapted from MachineState::read_term_from_heap
|
||||
let mut term_stack = vec![];
|
||||
|
||||
machine.machine_st.heap[0] = heap_cell;
|
||||
|
||||
let mut iter = stackful_post_order_iter::<NonListElider>(
|
||||
&mut machine.machine_st.heap,
|
||||
&mut machine.machine_st.stack,
|
||||
0,
|
||||
);
|
||||
|
||||
let mut anon_count: usize = 0;
|
||||
let var_ptr_cmp = |a, b| match a {
|
||||
Var::Named(name_a) => match b {
|
||||
Var::Named(name_b) => name_a.cmp(&name_b),
|
||||
_ => Ordering::Less,
|
||||
},
|
||||
_ => match b {
|
||||
Var::Named(_) => Ordering::Greater,
|
||||
_ => Ordering::Equal,
|
||||
},
|
||||
};
|
||||
|
||||
while let Some(addr) = iter.next() {
|
||||
let addr = unmark_cell_bits!(addr);
|
||||
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Lis) => {
|
||||
let tail = term_stack.pop().unwrap();
|
||||
let head = term_stack.pop().unwrap();
|
||||
|
||||
let list = match tail {
|
||||
Term::Atom(atom) if atom == "[]" => match head {
|
||||
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => {
|
||||
// Handle lists of char as strings
|
||||
Term::String(a.to_string())
|
||||
}
|
||||
_ => Term::List(vec![head]),
|
||||
},
|
||||
Term::List(elems) if elems.is_empty() => match head {
|
||||
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => {
|
||||
// Handle lists of char as strings
|
||||
Term::String(a.to_string())
|
||||
},
|
||||
_ => Term::List(vec![head]),
|
||||
},
|
||||
Term::List(mut elems) => {
|
||||
elems.insert(0, head);
|
||||
Term::List(elems)
|
||||
},
|
||||
Term::String(mut elems) => match head {
|
||||
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => {
|
||||
// Handle lists of char as strings
|
||||
elems.insert(0, a.chars().next().unwrap());
|
||||
Term::String(elems)
|
||||
},
|
||||
_ => {
|
||||
let mut elems: Vec<Term> = elems
|
||||
.chars()
|
||||
.map(|x| Term::Atom(x.into()))
|
||||
.collect();
|
||||
elems.insert(0, head);
|
||||
Term::List(elems)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
Term::Compound(".".into(), vec![head, tail])
|
||||
}
|
||||
};
|
||||
term_stack.push(list);
|
||||
}
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar) => {
|
||||
let var = var_names.get(&addr).map(|x| x.borrow().clone());
|
||||
match var {
|
||||
Some(Var::Named(name)) => term_stack.push(Term::Var(name.as_ref().to_owned())),
|
||||
_ => {
|
||||
let anon_name = loop {
|
||||
// Generate a name for the anonymous variable
|
||||
let anon_name = Rc::new(count_to_letter_code(anon_count));
|
||||
|
||||
// Find if this name is already being used
|
||||
var_names.sort_by(|_, a, _, b| {
|
||||
var_ptr_cmp(a.borrow().clone(), b.borrow().clone())
|
||||
});
|
||||
let binary_result = var_names.binary_search_by(|_,a| {
|
||||
let var_ptr = Var::Named(anon_name.clone());
|
||||
var_ptr_cmp(a.borrow().clone(), var_ptr.clone())
|
||||
});
|
||||
|
||||
match binary_result {
|
||||
Ok(_) => anon_count += 1, // Name already used
|
||||
Err(_) => {
|
||||
// Name not used, assign it to this variable
|
||||
let var_ptr = VarPtr::from(Var::Named(anon_name.clone()));
|
||||
var_names.insert(addr, var_ptr);
|
||||
break anon_name;
|
||||
},
|
||||
}
|
||||
};
|
||||
term_stack.push(Term::Var(anon_name.as_ref().to_owned()));
|
||||
},
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::F64Offset, offset) => {
|
||||
let f = machine.machine_st.arena.f64_tbl.get_entry(offset);
|
||||
term_stack.push(Term::Float(f.into()));
|
||||
}
|
||||
(HeapCellValueTag::Fixnum, n) => {
|
||||
term_stack.push(Term::Integer(n.into()));
|
||||
}
|
||||
(HeapCellValueTag::Cons, ptr) => {
|
||||
if let Ok(n) = Number::try_from((addr, &machine.machine_st.arena.f64_tbl)) {
|
||||
match n {
|
||||
Number::Integer(i) => term_stack.push(Term::Integer((*i).clone())),
|
||||
Number::Rational(r) => term_stack.push(Term::Rational((*r).clone())),
|
||||
_ => { unreachable!() },
|
||||
}
|
||||
} else {
|
||||
match_untyped_arena_ptr!(ptr,
|
||||
(ArenaHeaderTag::Stream, stream) => {
|
||||
let stream_term = if let Some(alias) = stream.options().get_alias() {
|
||||
Term::atom(alias.as_str().to_string())
|
||||
} else {
|
||||
Term::compound("$stream", [
|
||||
Term::integer(stream.as_ptr().addr())
|
||||
])
|
||||
};
|
||||
term_stack.push(stream_term);
|
||||
}
|
||||
(ArenaHeaderTag::Dropped, _stream) => {
|
||||
term_stack.push(Term::atom("$dropped_value"));
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
if arity == 0 {
|
||||
let atom_name = name.as_str().to_string();
|
||||
if atom_name == "[]" {
|
||||
term_stack.push(Term::List(vec![]));
|
||||
} else {
|
||||
term_stack.push(Term::Atom(atom_name));
|
||||
}
|
||||
} else {
|
||||
let subterms = term_stack
|
||||
.drain(term_stack.len() - arity ..)
|
||||
.collect();
|
||||
|
||||
term_stack.push(Term::Compound(name.as_str().to_string(), subterms));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, pstr_loc) => {
|
||||
let tail = term_stack.pop().unwrap();
|
||||
let char_iter = iter.base_iter.heap.char_iter(pstr_loc);
|
||||
|
||||
match tail {
|
||||
Term::Atom(atom) => {
|
||||
if atom == "[]" {
|
||||
term_stack.push(Term::String(atom.as_str().to_string()));
|
||||
}
|
||||
},
|
||||
Term::List(l) if l.is_empty() => {
|
||||
term_stack.push(Term::String(char_iter.collect()));
|
||||
}
|
||||
Term::List(l) => {
|
||||
let mut list: Vec<Term> = char_iter
|
||||
.map(|x| Term::Atom(x.to_string()))
|
||||
.collect();
|
||||
list.extend(l.into_iter());
|
||||
term_stack.push(Term::List(list));
|
||||
},
|
||||
_ => {
|
||||
let mut list: Vec<Term> = char_iter
|
||||
.map(|x| Term::Atom(x.to_string()))
|
||||
.collect();
|
||||
|
||||
let mut partial_list = Term::Compound(
|
||||
".".into(),
|
||||
vec![
|
||||
list.pop().unwrap(),
|
||||
tail,
|
||||
],
|
||||
);
|
||||
|
||||
while let Some(last) = list.pop() {
|
||||
partial_list = Term::Compound(
|
||||
".".into(),
|
||||
vec![
|
||||
last,
|
||||
partial_list,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
term_stack.push(partial_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
debug_assert_eq!(term_stack.len(), 1);
|
||||
term_stack.pop().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator though the leaf answers of a query.
|
||||
pub struct QueryState<'a> {
|
||||
machine: &'a mut Machine,
|
||||
term: TermWriteResult,
|
||||
stub_b: usize,
|
||||
var_names: IndexMap<HeapCellValue, VarPtr>,
|
||||
called: bool,
|
||||
}
|
||||
|
||||
impl Drop for QueryState<'_> {
|
||||
fn drop(&mut self) {
|
||||
// FIXME: This may be wrong if the iterator is not fully consumend, but from testing it
|
||||
// seems fine. Is this really ok?
|
||||
self.machine.trust_me();
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for QueryState<'_> {
|
||||
type Item = Result<LeafAnswer, Term>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let var_names = &mut self.var_names;
|
||||
let term_write_result = &self.term;
|
||||
let machine = &mut self.machine;
|
||||
|
||||
// No more choicepoints, end iteration
|
||||
if self.called && machine.machine_st.b <= self.stub_b {
|
||||
return None;
|
||||
}
|
||||
|
||||
machine.dispatch_loop();
|
||||
|
||||
self.called = true;
|
||||
|
||||
if !machine.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 h = machine.machine_st.heap.cell_len();
|
||||
|
||||
if let Err(resource_err_loc) = machine
|
||||
.machine_st
|
||||
.heap
|
||||
.append(&machine.machine_st.ball.stub)
|
||||
{
|
||||
return Some(Err(Term::from_heapcell(
|
||||
machine,
|
||||
machine.machine_st.heap[resource_err_loc],
|
||||
&mut IndexMap::new(),
|
||||
)));
|
||||
}
|
||||
|
||||
let exception_term =
|
||||
Term::from_heapcell(machine, machine.machine_st.heap[h], &mut var_names.clone());
|
||||
|
||||
if let Term::Compound(functor, args) = &exception_term {
|
||||
if functor == "error" && args.len() == 2 {
|
||||
// We have an error
|
||||
return Some(Err(exception_term));
|
||||
}
|
||||
}
|
||||
|
||||
// We have an exception that is not an error
|
||||
return Some(Ok(LeafAnswer::Exception(exception_term)));
|
||||
}
|
||||
|
||||
if machine.machine_st.p == LIB_QUERY_SUCCESS {
|
||||
if term_write_result.var_dict.is_empty() {
|
||||
self.machine.machine_st.backtrack();
|
||||
return Some(Ok(LeafAnswer::True));
|
||||
}
|
||||
} else if machine.machine_st.p == BREAK_FROM_DISPATCH_LOOP_LOC {
|
||||
return Some(Ok(LeafAnswer::False));
|
||||
}
|
||||
|
||||
let mut bindings: BTreeMap<String, Term> = BTreeMap::new();
|
||||
|
||||
let var_dict = &term_write_result.var_dict;
|
||||
|
||||
for (var_key, term_to_be_printed) in var_dict.iter() {
|
||||
let mut var_name = var_key.to_string();
|
||||
if var_name.starts_with('_') {
|
||||
let should_print = var_names.values().any(|x| match x.borrow().clone() {
|
||||
Var::Named(v) => *v == *var_name,
|
||||
_ => false,
|
||||
});
|
||||
if !should_print {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut term =
|
||||
Term::from_heapcell(machine, *term_to_be_printed, &mut var_names.clone());
|
||||
|
||||
if let Term::Var(ref term_str) = term {
|
||||
if *term_str == var_name {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Var dict is in the order things appear in the query. If var_name appears
|
||||
// after term in the query, switch their places.
|
||||
let var_name_idx = var_dict
|
||||
.get_index_of(&VarKey::VarPtr(Var::from(var_name.clone()).into()))
|
||||
.unwrap();
|
||||
let term_idx =
|
||||
var_dict.get_index_of(&VarKey::VarPtr(Var::from(term_str.clone()).into()));
|
||||
if let Some(idx) = term_idx {
|
||||
if idx < var_name_idx {
|
||||
let new_term = Term::Var(var_name);
|
||||
let new_var_name = term_str.into();
|
||||
term = new_term;
|
||||
var_name = new_var_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bindings.insert(var_name, term);
|
||||
}
|
||||
|
||||
// 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.machine_st.backtrack();
|
||||
|
||||
Some(Ok(LeafAnswer::LeafAnswer { bindings }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
/// Loads a module into the [`Machine`] from a string.
|
||||
pub fn load_module_string(&mut self, module_name: &str, program: impl Into<String>) {
|
||||
let stream = Stream::from_owned_string(program.into(), &mut self.machine_st.arena);
|
||||
self.load_file(module_name, stream);
|
||||
}
|
||||
|
||||
/// Consults a module into the [`Machine`] from a string.
|
||||
pub fn consult_module_string(&mut self, module_name: &str, program: impl Into<String>) {
|
||||
let stream = Stream::from_owned_string(program.into(), &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));
|
||||
}
|
||||
|
||||
pub(crate) 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.cell_len();
|
||||
self.machine_st.block = stub_b;
|
||||
}
|
||||
|
||||
/// Runs a query.
|
||||
pub fn run_query(&mut self, query: impl Into<String>) -> QueryState<'_> {
|
||||
let mut parser = Parser::new(
|
||||
Stream::from_owned_string(query.into(), &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)
|
||||
.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))
|
||||
.cloned()
|
||||
.map(|offset| {
|
||||
self.machine_st
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(offset.into())
|
||||
.p() as usize
|
||||
})
|
||||
.expect("couldn't get code index");
|
||||
|
||||
self.machine_st.execute_at_index(1, call_index_p);
|
||||
|
||||
let stub_b = self.machine_st.b;
|
||||
QueryState {
|
||||
machine: self,
|
||||
term: term_write_result,
|
||||
stub_b,
|
||||
var_names,
|
||||
called: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
610
src/machine/lib_machine/tests.rs
Normal file
610
src/machine/lib_machine/tests.rs
Normal file
@@ -0,0 +1,610 @@
|
||||
use super::*;
|
||||
use crate::MachineBuilder;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn programatic_query() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
String::from(
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
),
|
||||
);
|
||||
|
||||
let query = r#"triple("a",P,"b")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[
|
||||
LeafAnswer::from_bindings([("P", Term::string("p1")),]),
|
||||
LeafAnswer::from_bindings([("P", Term::string("p2")),]),
|
||||
],
|
||||
);
|
||||
|
||||
let query = r#"triple("a","p1","b")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
|
||||
assert_eq!(complete_answer, [LeafAnswer::True],);
|
||||
|
||||
let query = r#"triple("x","y","z")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
|
||||
assert_eq!(complete_answer, [LeafAnswer::False],);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn failing_query() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
let query = r#"triple("a",P,"b")."#;
|
||||
let complete_answer: Result<Vec<_>, _> = machine.run_query(query).collect();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
Err(Term::compound(
|
||||
"error",
|
||||
[
|
||||
Term::compound(
|
||||
"existence_error",
|
||||
[
|
||||
Term::atom("procedure"),
|
||||
Term::compound("/", [Term::atom("triple"), Term::integer(3)]),
|
||||
]
|
||||
),
|
||||
Term::compound("/", [Term::atom("triple"), Term::integer(3)]),
|
||||
],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn complex_results() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
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"}]').
|
||||
"#,
|
||||
);
|
||||
|
||||
let complete_answer: Vec<_> = machine
|
||||
.run_query(r#"subject_class("Todo", C), constructor(C, Actions)."#)
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[LeafAnswer::from_bindings([
|
||||
("C", Term::atom("c")),
|
||||
(
|
||||
"Actions",
|
||||
Term::atom(
|
||||
r#"[{action: "addLink", source: "this", predicate: "todo://state", target: "todo://ready"}]"#
|
||||
)
|
||||
),
|
||||
])],
|
||||
);
|
||||
|
||||
let complete_answer: Vec<_> = machine
|
||||
.run_query(r#"subject_class("Recipe", C), constructor(C, Actions)."#)
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[LeafAnswer::from_bindings([
|
||||
("C", Term::atom("xyz")),
|
||||
(
|
||||
"Actions",
|
||||
Term::atom(
|
||||
r#"[{action: "addLink", source: "this", predicate: "recipe://title", target: "literal://string:Meta%20Muffins"}]"#
|
||||
)
|
||||
),
|
||||
])],
|
||||
);
|
||||
|
||||
let complete_answer: Vec<_> = machine
|
||||
.run_query("subject_class(Class, _).")
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[
|
||||
LeafAnswer::from_bindings([("Class", Term::string("Todo"))]),
|
||||
LeafAnswer::from_bindings([("Class", Term::string("Recipe"))]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn empty_predicate() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
:- discontiguous(subject_class/2).
|
||||
"#,
|
||||
);
|
||||
|
||||
let complete_answer: Vec<_> = machine
|
||||
.run_query("subject_class(X, _).")
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::False]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn list_results() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
list([1,2,3]).
|
||||
"#,
|
||||
);
|
||||
|
||||
let complete_answer: Vec<_> = machine
|
||||
.run_query("list(X).")
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[LeafAnswer::from_bindings([(
|
||||
"X",
|
||||
Term::list([Term::integer(1), Term::integer(2), Term::integer(3)]),
|
||||
)])],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn consult() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
);
|
||||
|
||||
let query = r#"triple("a",P,"b")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[
|
||||
LeafAnswer::from_bindings([("P", Term::string("p1"))]),
|
||||
LeafAnswer::from_bindings([("P", Term::string("p2"))]),
|
||||
],
|
||||
);
|
||||
|
||||
let query = r#"triple("a","p1","b")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::True],);
|
||||
|
||||
let query = r#"triple("x","y","z")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::False],);
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
triple("a", "new", "b").
|
||||
"#,
|
||||
);
|
||||
|
||||
let query = r#"triple("a","p1","b")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::False],);
|
||||
|
||||
let query = r#"triple("a","new","b")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::True]);
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
#[ignore = "uses old flawed interface"]
|
||||
fn integration_test() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
// 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. {i}=======\n{last_result}\n===============");
|
||||
assert_eq!(last_result.to_string(), result.to_string().trim(),)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn findall() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
);
|
||||
|
||||
let query = r#"findall([Predicate, Target], triple(_,Predicate,Target), Result)."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[LeafAnswer::from_bindings([(
|
||||
"Result",
|
||||
Term::list([
|
||||
Term::list([Term::string("p1"), Term::string("b")]),
|
||||
Term::list([Term::string("p2"), Term::string("b")]),
|
||||
])
|
||||
)])]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn dont_return_partial_matches() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
:- discontiguous(property_resolve/2).
|
||||
subject_class("Todo", c).
|
||||
"#,
|
||||
);
|
||||
|
||||
let query = r#"property_resolve(C, "isLiked"), subject_class("Todo", C)."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::False]);
|
||||
|
||||
let query = r#"subject_class("Todo", C), property_resolve(C, "isLiked")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::False]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn dont_return_partial_matches_without_discountiguous() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
a("true for a").
|
||||
b("true for b").
|
||||
"#,
|
||||
);
|
||||
|
||||
let query = r#"a("true for a")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::True]);
|
||||
|
||||
let query = r#"a("true for a"), b("true for b")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::True]);
|
||||
|
||||
let query = r#"a("true for b"), b("true for b")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::False]);
|
||||
|
||||
let query = r#"a("true for a"), b("true for a")."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
assert_eq!(complete_answer, [LeafAnswer::False]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn non_existent_predicate_should_not_cause_panic_when_other_predicates_are_defined() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
machine.consult_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
triple("a", "p1", "b").
|
||||
triple("a", "p2", "b").
|
||||
"#,
|
||||
);
|
||||
|
||||
let query = r#"non_existent_predicate("a","p1","b")."#;
|
||||
let complete_answer: Result<Vec<_>, _> = machine.run_query(query).collect();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
Err(Term::compound(
|
||||
"error",
|
||||
[
|
||||
Term::compound(
|
||||
"existence_error",
|
||||
[
|
||||
Term::atom("procedure"),
|
||||
Term::compound(
|
||||
"/",
|
||||
[Term::atom("non_existent_predicate"), Term::integer(3)],
|
||||
),
|
||||
],
|
||||
),
|
||||
Term::compound(
|
||||
"/",
|
||||
[Term::atom("non_existent_predicate"), Term::integer(3)]
|
||||
),
|
||||
],
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn atom_quoting() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
let query = "X = '.'.";
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[LeafAnswer::from_bindings([("X", Term::atom("."))])]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn rational_number() {
|
||||
use crate::parser::dashu::rational::RBig;
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
let query = "X is 1 rdiv 2.";
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[LeafAnswer::from_bindings([(
|
||||
"X",
|
||||
Term::rational(RBig::from_parts(1.into(), 2u32.into()))
|
||||
)])]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn big_integer() {
|
||||
use crate::parser::dashu::integer::IBig;
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
let query = "X is 10^100.";
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[LeafAnswer::from_bindings([(
|
||||
"X",
|
||||
Term::integer(IBig::from(10).pow(100))
|
||||
)])],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn complicated_term() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
let query = r#"X = a("asdf", [42, 2.54, asdf, a, [a,b|_], Z])."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
|
||||
let expected = Term::Compound(
|
||||
// Compound term
|
||||
"a".into(),
|
||||
vec![
|
||||
Term::String("asdf".into()), // String
|
||||
Term::List(vec![
|
||||
Term::Integer(42.into()), // Fixnum
|
||||
Term::Float(2.54), // Float
|
||||
Term::Atom("asdf".into()), // Atom
|
||||
Term::Atom("a".into()), // Char
|
||||
Term::Compound(
|
||||
// Partial string
|
||||
".".into(),
|
||||
vec![
|
||||
Term::Atom("a".into()),
|
||||
Term::Compound(
|
||||
".".into(),
|
||||
vec![
|
||||
Term::Atom("b".into()),
|
||||
Term::Var("_A".into()), // Anonymous variable
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Term::Var("Z".into()), // Named variable
|
||||
]),
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[LeafAnswer::from_bindings([("X", expected)])]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||
fn issue_2341() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
machine.load_module_string(
|
||||
"facts",
|
||||
r#"
|
||||
male(stephen).
|
||||
parent(albert,edward).
|
||||
father(F,C):-parent(F,C),male(F).
|
||||
"#,
|
||||
);
|
||||
|
||||
let query = r#"father(F,C)."#;
|
||||
let complete_answer: Vec<_> = machine.run_query(query).collect::<Result<_, _>>().unwrap();
|
||||
|
||||
assert_eq!(complete_answer, [LeafAnswer::False]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn query_iterator_determinism() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
{
|
||||
let mut iterator = machine.run_query("X = 1.");
|
||||
|
||||
iterator.next();
|
||||
assert_eq!(iterator.next(), None);
|
||||
}
|
||||
|
||||
{
|
||||
let mut iterator = machine.run_query("X = 1 ; false.");
|
||||
|
||||
iterator.next();
|
||||
|
||||
assert_eq!(iterator.next(), Some(Ok(LeafAnswer::False)));
|
||||
assert_eq!(iterator.next(), None);
|
||||
}
|
||||
|
||||
{
|
||||
let mut iterator = machine.run_query("false.");
|
||||
|
||||
assert_eq!(iterator.next(), Some(Ok(LeafAnswer::False)));
|
||||
assert_eq!(iterator.next(), None);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn query_iterator_backtracking_when_no_variables() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
let mut iterator = machine.run_query("true;false.");
|
||||
|
||||
assert_eq!(iterator.next(), Some(Ok(LeafAnswer::True)));
|
||||
assert_eq!(iterator.next(), Some(Ok(LeafAnswer::False)));
|
||||
assert_eq!(iterator.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn differentiate_anonymous_variables() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
let complete_answer: Vec<_> = machine
|
||||
.run_query("A = [_,_], _B = 1 ; B = [_,_].")
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[
|
||||
LeafAnswer::from_bindings([
|
||||
(
|
||||
"A",
|
||||
Term::list([Term::variable("_A"), Term::variable("_C")])
|
||||
),
|
||||
("_B", Term::integer(1)),
|
||||
]),
|
||||
LeafAnswer::from_bindings([(
|
||||
"B",
|
||||
Term::list([Term::variable("_A"), Term::variable("_C")])
|
||||
),]),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn order_of_variables_in_binding() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
let complete_answer: Vec<_> = machine
|
||||
.run_query("X = Y, Z = W.")
|
||||
.collect::<Result<_, _>>()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[LeafAnswer::from_bindings([
|
||||
("X", Term::variable("Y")),
|
||||
("Z", Term::variable("W")),
|
||||
])]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn errors_and_exceptions() {
|
||||
let mut machine = MachineBuilder::default().build();
|
||||
|
||||
let complete_answer: Vec<_> = machine.run_query("functor(_,_,_).").collect();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[Err(Term::compound(
|
||||
"error",
|
||||
[
|
||||
Term::atom("instantiation_error"),
|
||||
Term::compound("/", [Term::atom("functor"), Term::integer(3)]),
|
||||
],
|
||||
))]
|
||||
);
|
||||
|
||||
let complete_answer: Vec<_> = machine.run_query("throw(a).").collect();
|
||||
|
||||
assert_eq!(
|
||||
complete_answer,
|
||||
[Ok(LeafAnswer::Exception(Term::atom("a")))]
|
||||
);
|
||||
}
|
||||
@@ -2,14 +2,12 @@ use crate::forms::*;
|
||||
use crate::machine::loader::*;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::machine::preprocessor::*;
|
||||
use crate::machine::term_stream::*;
|
||||
use crate::machine::*;
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexSet;
|
||||
pub use ref_thread_local::RefThreadLocal;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
@@ -17,35 +15,38 @@ use std::mem;
|
||||
|
||||
pub(super) type ModuleOpExports = Vec<(OpDecl, Option<OpDesc>)>;
|
||||
|
||||
pub(super) fn set_code_index(
|
||||
retraction_info: &mut RetractionInfo,
|
||||
pub(super) fn set_code_index<'a, LS: LoadState<'a>>(
|
||||
payload: &mut <LS as LoadState<'a>>::LoaderFieldType,
|
||||
compilation_target: &CompilationTarget,
|
||||
key: PredicateKey,
|
||||
mut code_index: CodeIndex,
|
||||
code_idx: CodeIndex,
|
||||
code_ptr: IndexPtr,
|
||||
) {
|
||||
let record = match compilation_target {
|
||||
CompilationTarget::User => {
|
||||
if IndexPtrTag::Undefined == code_index.get().tag() {
|
||||
code_index.set(code_ptr);
|
||||
RetractionRecord::AddedUserPredicate(key)
|
||||
} else {
|
||||
let replaced = code_index.replace(code_ptr);
|
||||
RetractionRecord::ReplacedUserPredicate(key, replaced)
|
||||
let record = LS::machine_st(payload).arena.code_index_tbl.with_entry_mut(
|
||||
code_idx.into(),
|
||||
|code_idx_ptr| match compilation_target {
|
||||
CompilationTarget::User => {
|
||||
if IndexPtrTag::Undefined == code_idx_ptr.tag() {
|
||||
*code_idx_ptr = code_ptr;
|
||||
RetractionRecord::AddedUserPredicate(key)
|
||||
} else {
|
||||
let replaced = mem::replace(code_idx_ptr, code_ptr);
|
||||
RetractionRecord::ReplacedUserPredicate(key, replaced)
|
||||
}
|
||||
}
|
||||
}
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
if IndexPtrTag::Undefined == code_index.get().tag() {
|
||||
code_index.set(code_ptr);
|
||||
RetractionRecord::AddedModulePredicate(*module_name, key)
|
||||
} else {
|
||||
let replaced = code_index.replace(code_ptr);
|
||||
RetractionRecord::ReplacedModulePredicate(*module_name, key, replaced)
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
if IndexPtrTag::Undefined == code_idx_ptr.tag() {
|
||||
*code_idx_ptr = code_ptr;
|
||||
RetractionRecord::AddedModulePredicate(*module_name, key)
|
||||
} else {
|
||||
let replaced = mem::replace(code_idx_ptr, code_ptr);
|
||||
RetractionRecord::ReplacedModulePredicate(*module_name, key, replaced)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
retraction_info.push_record(record);
|
||||
payload.retraction_info.push_record(record);
|
||||
}
|
||||
|
||||
fn add_op_decl_as_module_export<'a, LS: LoadState<'a>>(
|
||||
@@ -135,21 +136,28 @@ 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 code_idx_tbl = &mut LS::machine_st(payload).arena.code_index_tbl;
|
||||
|
||||
let target_code_index = *code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::default(arena));
|
||||
.or_insert_with(|| CodeIndex::default(code_idx_tbl));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
let src_code_index_ptr = code_idx_tbl.get_entry(src_code_index.into());
|
||||
|
||||
set_code_index::<LS>(
|
||||
payload,
|
||||
compilation_target,
|
||||
key,
|
||||
target_code_index,
|
||||
src_code_index.get(),
|
||||
src_code_index_ptr,
|
||||
);
|
||||
|
||||
if src_code_index.is_dynamic_undefined() {
|
||||
if LS::machine_st(payload)
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(src_code_index.into())
|
||||
.is_dynamic_undefined()
|
||||
{
|
||||
code_dir.insert(key, src_code_index);
|
||||
}
|
||||
} else {
|
||||
@@ -191,19 +199,20 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
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;
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() {
|
||||
let code_index_tbl = &mut LS::machine_st(payload).arena.code_index_tbl;
|
||||
|
||||
let src_code_ptr = code_index_tbl.get_entry(src_code_index.into());
|
||||
let target_code_index = *code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::default(arena));
|
||||
.or_insert_with(|| CodeIndex::default(code_index_tbl));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
set_code_index::<LS>(
|
||||
payload,
|
||||
compilation_target,
|
||||
key,
|
||||
target_code_index,
|
||||
src_code_index.get(),
|
||||
src_code_ptr,
|
||||
);
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
@@ -244,21 +253,21 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>(
|
||||
.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;
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() {
|
||||
let code_index_tbl = &mut LS::machine_st(payload).arena.code_index_tbl;
|
||||
|
||||
let target_code_index = *wam_prelude
|
||||
.indices
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
let src_code_ptr = code_index_tbl.get_entry(src_code_index.into());
|
||||
let target_code_index =
|
||||
*wam_prelude.indices.code_dir.entry(key).or_insert_with(|| {
|
||||
CodeIndex::new(IndexPtr::undefined(), code_index_tbl)
|
||||
});
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
set_code_index::<LS>(
|
||||
payload,
|
||||
compilation_target,
|
||||
key,
|
||||
target_code_index,
|
||||
src_code_index.get(),
|
||||
src_code_ptr,
|
||||
);
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
@@ -305,19 +314,20 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>(
|
||||
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;
|
||||
if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() {
|
||||
let code_index_tbl = &mut LS::machine_st(payload).arena.code_index_tbl;
|
||||
|
||||
let src_code_ptr = code_index_tbl.get_entry(src_code_index.into());
|
||||
let target_code_index = *code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), code_index_tbl));
|
||||
|
||||
set_code_index(
|
||||
&mut payload.retraction_info,
|
||||
set_code_index::<LS>(
|
||||
payload,
|
||||
&payload_compilation_target,
|
||||
key,
|
||||
target_code_index,
|
||||
src_code_index.get(),
|
||||
src_code_ptr,
|
||||
);
|
||||
} else {
|
||||
return Err(SessionError::ModuleDoesNotContainExport(
|
||||
@@ -435,19 +445,6 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
self.retract_local_clauses_impl(clause_clause_compilation_target, key, clause_locs);
|
||||
}
|
||||
|
||||
pub(super) fn try_term_to_tl(
|
||||
&mut self,
|
||||
term: Term,
|
||||
preprocessor: &mut Preprocessor,
|
||||
) -> Result<PredicateClause, SessionError> {
|
||||
let tl = preprocessor.try_term_to_tl(self, term)?;
|
||||
|
||||
Ok(match tl {
|
||||
TopLevel::Fact(fact, var_data) => PredicateClause::Fact(fact, var_data),
|
||||
TopLevel::Rule(rule, var_data) => PredicateClause::Rule(rule, var_data),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn remove_module_op_exports(&mut self) {
|
||||
for (mut op_decl, record) in self.payload.module_op_exports.drain(0..) {
|
||||
@@ -461,7 +458,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
|
||||
pub(super) fn remove_replaced_in_situ_module(&mut self, module_name: Atom) {
|
||||
let mut removed_module = match self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
let mut removed_module = match self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
Some(module) => module,
|
||||
None => return,
|
||||
};
|
||||
@@ -483,11 +480,14 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.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()
|
||||
});
|
||||
let old_index_ptr = code_index.replace(
|
||||
&mut LS::machine_st(&mut self.payload).arena.code_index_tbl,
|
||||
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),
|
||||
@@ -501,8 +501,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !code_index.is_undefined() && !code_index.is_dynamic_undefined() {
|
||||
let old_index_ptr = code_index.replace(IndexPtr::undefined());
|
||||
let code_index_tbl = &mut LS::machine_st(&mut self.payload).arena.code_index_tbl;
|
||||
let code_ptr = code_index_tbl.get_entry((*code_index).into());
|
||||
|
||||
if !code_ptr.is_undefined() && !code_ptr.is_dynamic_undefined() {
|
||||
let old_index_ptr = code_index.replace(code_index_tbl, IndexPtr::undefined());
|
||||
|
||||
self.payload.retraction_info.push_record(
|
||||
RetractionRecord::ReplacedModulePredicate(module_name, *key, old_index_ptr),
|
||||
@@ -527,40 +530,47 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
|
||||
pub(super) fn remove_module_exports(&mut self, module_name: Atom) {
|
||||
let removed_module = match self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
let removed_module = match self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
Some(module) => module,
|
||||
None => return,
|
||||
};
|
||||
|
||||
fn remove_module_exports(
|
||||
fn remove_module_exports<'b, LS: LoadState<'b>>(
|
||||
payload: &mut <LS as LoadState<'b>>::LoaderFieldType,
|
||||
removed_module: &Module,
|
||||
code_dir: &mut CodeDir,
|
||||
op_dir: &mut OpDir,
|
||||
retraction_info: &mut RetractionInfo,
|
||||
predicate_retractor: impl Fn(PredicateKey, IndexPtr) -> RetractionRecord,
|
||||
op_retractor: impl Fn(OpDecl, OpDesc) -> RetractionRecord,
|
||||
) {
|
||||
for export in removed_module.module_decl.exports.iter() {
|
||||
match export {
|
||||
ModuleExport::PredicateKey(ref key) => {
|
||||
match (removed_module.code_dir.get(key), code_dir.get_mut(key)) {
|
||||
(Some(module_code_index), Some(target_code_index))
|
||||
if module_code_index.get() == target_code_index.get() =>
|
||||
{
|
||||
if let (Some(module_code_idx), Some(target_code_idx)) = (
|
||||
removed_module.code_dir.get(key).cloned(),
|
||||
code_dir.get_mut(key).cloned(),
|
||||
) {
|
||||
let code_index_tbl = &mut LS::machine_st(payload).arena.code_index_tbl;
|
||||
let module_code_ptr = code_index_tbl.get_entry(module_code_idx.into());
|
||||
let target_code_ptr = code_index_tbl.get_entry(target_code_idx.into());
|
||||
|
||||
if module_code_ptr == target_code_ptr {
|
||||
let old_index_ptr =
|
||||
target_code_index.replace(IndexPtr::undefined());
|
||||
retraction_info
|
||||
target_code_idx.replace(code_index_tbl, IndexPtr::undefined());
|
||||
payload
|
||||
.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)));
|
||||
.swap_remove(&(op_decl.name, op_decl.op_desc.get_spec().fixity()));
|
||||
|
||||
if let Some(op_desc) = op_dir_value_opt {
|
||||
retraction_info.push_record(op_retractor(*op_decl, op_desc));
|
||||
payload
|
||||
.retraction_info
|
||||
.push_record(op_retractor(*op_decl, op_desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -569,11 +579,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
match self.payload.compilation_target {
|
||||
CompilationTarget::User => {
|
||||
remove_module_exports(
|
||||
remove_module_exports::<LS>(
|
||||
&mut self.payload,
|
||||
&removed_module,
|
||||
&mut self.wam_prelude.indices.code_dir,
|
||||
&mut self.wam_prelude.indices.op_dir,
|
||||
&mut self.payload.retraction_info,
|
||||
RetractionRecord::ReplacedUserPredicate,
|
||||
RetractionRecord::ReplacedUserOp,
|
||||
);
|
||||
@@ -593,11 +603,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.modules
|
||||
.get_mut(&target_module_name)
|
||||
{
|
||||
remove_module_exports(
|
||||
remove_module_exports::<LS>(
|
||||
&mut self.payload,
|
||||
&removed_module,
|
||||
&mut module.code_dir,
|
||||
&mut module.op_dir,
|
||||
&mut self.payload.retraction_info,
|
||||
predicate_retractor,
|
||||
op_retractor,
|
||||
);
|
||||
@@ -623,7 +633,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| {
|
||||
CodeIndex::new(
|
||||
IndexPtr::undefined(),
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
&mut LS::machine_st(&mut self.payload).arena.code_index_tbl,
|
||||
)
|
||||
}),
|
||||
None => {
|
||||
@@ -633,7 +643,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| {
|
||||
CodeIndex::new(
|
||||
IndexPtr::undefined(),
|
||||
&mut LS::machine_st(&mut self.payload).arena,
|
||||
&mut LS::machine_st(&mut self.payload).arena.code_index_tbl,
|
||||
)
|
||||
}),
|
||||
None => {
|
||||
@@ -649,7 +659,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
key: PredicateKey,
|
||||
compilation_target: CompilationTarget,
|
||||
) -> CodeIndex {
|
||||
let arena = &mut LS::machine_st(&mut self.payload).arena;
|
||||
let code_index_tbl = &mut LS::machine_st(&mut self.payload).arena.code_index_tbl;
|
||||
|
||||
match compilation_target {
|
||||
CompilationTarget::User => *self
|
||||
@@ -657,7 +667,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.indices
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)),
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), code_index_tbl)),
|
||||
CompilationTarget::Module(module_name) => {
|
||||
self.get_or_insert_local_code_index(module_name, key)
|
||||
}
|
||||
@@ -669,15 +679,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
module_name: Atom,
|
||||
key: PredicateKey,
|
||||
) -> CodeIndex {
|
||||
let arena = &mut LS::machine_st(&mut self.payload).arena;
|
||||
let code_index_tbl = &mut LS::machine_st(&mut self.payload).arena.code_index_tbl;
|
||||
|
||||
if module_name == atom!("user") {
|
||||
return *self
|
||||
*self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), code_index_tbl))
|
||||
} else {
|
||||
self.get_or_insert_local_code_index(module_name, key)
|
||||
}
|
||||
@@ -1018,7 +1028,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
self.reset_in_situ_module(module_decl.clone(), &listing_src);
|
||||
|
||||
let mut module = match self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
let mut module = match self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
Some(mut module) => {
|
||||
module.listing_src = listing_src;
|
||||
module
|
||||
@@ -1060,7 +1070,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
|
||||
pub(super) fn import_module(&mut self, module_name: Atom) -> Result<(), SessionError> {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
let payload_compilation_target = self.payload.compilation_target;
|
||||
|
||||
match &payload_compilation_target {
|
||||
@@ -1116,7 +1126,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
module_name: Atom,
|
||||
exports: IndexSet<ModuleExport>,
|
||||
) -> Result<(), SessionError> {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.remove(&module_name) {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.swap_remove(&module_name) {
|
||||
let payload_compilation_target = self.payload.compilation_target;
|
||||
|
||||
let result = match &payload_compilation_target {
|
||||
@@ -1176,7 +1186,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
ListingSource::File(filename, path_buf),
|
||||
)
|
||||
}
|
||||
ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) {
|
||||
ModuleSource::Library(library) => match libraries::get(&library.as_str()) {
|
||||
Some(code) => {
|
||||
if let Some(module) = self.wam_prelude.indices.modules.get(&library) {
|
||||
if let ListingSource::DynamicallyGenerated = &module.listing_src {
|
||||
@@ -1257,7 +1267,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
ListingSource::File(filename, path_buf),
|
||||
)
|
||||
}
|
||||
ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) {
|
||||
ModuleSource::Library(library) => match libraries::get(&library.as_str()) {
|
||||
Some(code) => {
|
||||
if self.wam_prelude.indices.modules.contains_key(&library) {
|
||||
return self.import_qualified_module(library, exports);
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
|
||||
/*
|
||||
* The loader compiles Prolog terms read from a TermStream instance,
|
||||
@@ -45,7 +46,7 @@ use std::ops::{Deref, DerefMut};
|
||||
* loader fail later, all changes must be rolled back, restoring the
|
||||
* WAM to its prior state. Retraction records describe individual changes
|
||||
* made by the loader, and they may be used later.
|
||||
*/
|
||||
*/
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum RetractionRecord {
|
||||
@@ -107,7 +108,7 @@ pub(crate) enum RetractionRecord {
|
||||
* "extent" of a code vector is its length prior to an attempted
|
||||
* module load. The only code vector of the WAM's IndexStore, "code",
|
||||
* is shared by all modules, including the default "user" module.
|
||||
*/
|
||||
*/
|
||||
|
||||
pub(super) struct RetractionInfo {
|
||||
orig_code_extent: usize,
|
||||
@@ -211,7 +212,6 @@ impl PredicateQueue {
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! predicate_queue {
|
||||
[$($v:expr),*] => (
|
||||
PredicateQueue {
|
||||
@@ -304,11 +304,15 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
||||
|
||||
#[inline(always)]
|
||||
fn evacuate(mut loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError> {
|
||||
loader
|
||||
.payload
|
||||
.load_state
|
||||
.set_tag(ArenaHeaderTag::InactiveLoadState);
|
||||
Ok(loader.payload.load_state)
|
||||
if loader.payload.load_state.get_tag() != ArenaHeaderTag::Dropped {
|
||||
loader
|
||||
.payload
|
||||
.load_state
|
||||
.set_tag(ArenaHeaderTag::InactiveLoadState);
|
||||
Ok(loader.payload.load_state)
|
||||
} else {
|
||||
unreachable!("we never evacuate after dropping")
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -319,8 +323,8 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
||||
#[inline(always)]
|
||||
fn reset_machine(loader: &mut Loader<'a, Self>) {
|
||||
if loader.payload.load_state.get_tag() != ArenaHeaderTag::Dropped {
|
||||
loader.payload.load_state.set_tag(ArenaHeaderTag::Dropped);
|
||||
loader.reset_machine();
|
||||
loader.payload.load_state.drop_payload();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,7 +357,7 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
||||
|
||||
#[inline]
|
||||
fn err_on_builtin_module_overwrite(module_name: Atom) -> Result<(), SessionError> {
|
||||
if LIBRARIES.borrow().contains_key(&*module_name.as_str()) {
|
||||
if libraries::contains(&module_name.as_str()) {
|
||||
Err(SessionError::CannotOverwriteBuiltInModule(module_name))
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -379,7 +383,6 @@ impl<'a> LoadState<'a> for BootstrappingLoadState<'a> {
|
||||
let repo_len = loader.wam_prelude.code.len();
|
||||
|
||||
loader.payload.retraction_info.reset(repo_len);
|
||||
|
||||
loader.remove_module_op_exports();
|
||||
|
||||
Ok(loader.payload.compilation_target)
|
||||
@@ -570,7 +573,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
RetractionRecord::AddedMetaPredicate(target_module_name, key) => {
|
||||
match target_module_name {
|
||||
atom!("user") => {
|
||||
self.wam_prelude.indices.meta_predicates.remove(&key);
|
||||
self.wam_prelude.indices.meta_predicates.swap_remove(&key);
|
||||
}
|
||||
_ => match self
|
||||
.wam_prelude
|
||||
@@ -579,7 +582,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
.get_mut(&target_module_name)
|
||||
{
|
||||
Some(ref mut module) => {
|
||||
module.meta_predicates.remove(&key);
|
||||
module.meta_predicates.swap_remove(&key);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
@@ -613,7 +616,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
}
|
||||
}
|
||||
RetractionRecord::AddedModule(module_name) => {
|
||||
self.wam_prelude.indices.modules.remove(&module_name);
|
||||
self.wam_prelude.indices.modules.swap_remove(&module_name);
|
||||
}
|
||||
RetractionRecord::ReplacedModule(
|
||||
module_decl,
|
||||
@@ -709,7 +712,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
if let Some(ref mut module) =
|
||||
self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
module.code_dir.remove(&key);
|
||||
module.code_dir.swap_remove(&key);
|
||||
}
|
||||
}
|
||||
RetractionRecord::ReplacedModulePredicate(module_name, key, old_code_idx) => {
|
||||
@@ -717,7 +720,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
self.wam_prelude.indices.modules.get_mut(&module_name)
|
||||
{
|
||||
if let Some(code_idx) = module.code_dir.get_mut(&key) {
|
||||
code_idx.set(old_code_idx)
|
||||
let code_index_tbl =
|
||||
&mut LS::machine_st(&mut self.payload).arena.code_index_tbl;
|
||||
code_idx.set(code_index_tbl, old_code_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -734,11 +739,13 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
op_decl.insert_into_op_dir(&mut self.wam_prelude.indices.op_dir);
|
||||
}
|
||||
RetractionRecord::AddedUserPredicate(key) => {
|
||||
self.wam_prelude.indices.code_dir.remove(&key);
|
||||
self.wam_prelude.indices.code_dir.swap_remove(&key);
|
||||
}
|
||||
RetractionRecord::ReplacedUserPredicate(key, old_code_idx) => {
|
||||
if let Some(code_idx) = self.wam_prelude.indices.code_dir.get_mut(&key) {
|
||||
code_idx.set(old_code_idx)
|
||||
let code_index_tbl =
|
||||
&mut LS::machine_st(&mut self.payload).arena.code_index_tbl;
|
||||
code_idx.set(code_index_tbl, old_code_idx)
|
||||
}
|
||||
}
|
||||
RetractionRecord::AddedIndex(index_key, clause_loc) => {
|
||||
@@ -759,7 +766,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
) => {
|
||||
remove_constant_indices(
|
||||
constant,
|
||||
&overlapping_constants,
|
||||
overlapping_constants,
|
||||
indexing_code,
|
||||
clause_loc - index_loc, // WAS: &inner_index_locs,
|
||||
);
|
||||
@@ -1043,8 +1050,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
let cell = machine_st[r];
|
||||
|
||||
let export_list = machine_st.read_term_from_heap(cell);
|
||||
let atom_tbl = &mut LS::machine_st(&mut self.payload).atom_tbl;
|
||||
let export_list = setup_module_export_list(export_list, atom_tbl)?;
|
||||
let export_list = setup_module_export_list(export_list)?;
|
||||
|
||||
Ok(export_list.into_iter().collect())
|
||||
}
|
||||
@@ -1057,7 +1063,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
self.payload.clause_clauses.push((head, body));
|
||||
}
|
||||
head @ Term::Literal(_, Literal::Atom(..)) | head @ Term::Clause(..) => {
|
||||
head @ (Term::Clause(..) | Term::Literal(_, Literal::Atom(_))) => {
|
||||
let body = Term::Literal(Cell::default(), Literal::Atom(atom!("true")));
|
||||
self.payload.clause_clauses.push((head, body));
|
||||
}
|
||||
@@ -1215,14 +1221,18 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
* but to multifile and discontiguous predicates as well.
|
||||
*/
|
||||
|
||||
let code_index = self.get_or_insert_code_index(key, compilation_target);
|
||||
let offset = self.get_or_insert_code_index(key, compilation_target);
|
||||
let code_idx_ptr = LS::machine_st(&mut self.payload)
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(offset.into());
|
||||
|
||||
if code_index.is_undefined() {
|
||||
set_code_index(
|
||||
&mut self.payload.retraction_info,
|
||||
if code_idx_ptr.is_undefined() {
|
||||
set_code_index::<LS>(
|
||||
&mut self.payload,
|
||||
&compilation_target,
|
||||
key,
|
||||
code_index,
|
||||
offset,
|
||||
IndexPtr::dynamic_undefined(),
|
||||
);
|
||||
}
|
||||
@@ -1286,13 +1296,16 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
||||
|
||||
fn add_clause_clause_if_dynamic(&mut self, term: &Term) -> Result<(), SessionError> {
|
||||
if let Some(predicate_name) = ClauseInfo::name(term) {
|
||||
let arity = ClauseInfo::arity(term);
|
||||
let predicate_arity = ClauseInfo::arity(term);
|
||||
let predicates_compilation_target = self.payload.predicates.compilation_target;
|
||||
|
||||
let is_dynamic = self
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_skeleton(&predicates_compilation_target, &(predicate_name, arity))
|
||||
.get_predicate_skeleton(
|
||||
&predicates_compilation_target,
|
||||
&(predicate_name, predicate_arity),
|
||||
)
|
||||
.map(|skeleton| skeleton.core.is_dynamic)
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -1348,7 +1361,7 @@ impl<'a> MachinePreludeView<'a> {
|
||||
pub(super) fn composite_op_dir(
|
||||
&self,
|
||||
compilation_target: &CompilationTarget,
|
||||
) -> CompositeOpDir {
|
||||
) -> CompositeOpDir<'_, '_> {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => CompositeOpDir::new(&self.indices.op_dir, None),
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
@@ -1366,12 +1379,22 @@ impl<'a> MachinePreludeView<'a> {
|
||||
impl MachineState {
|
||||
pub(super) fn read_term_from_heap(&mut self, term_addr: HeapCellValue) -> Term {
|
||||
let mut term_stack = vec![];
|
||||
self.heap[0] = term_addr;
|
||||
let mut iter =
|
||||
stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, term_addr);
|
||||
stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, 0);
|
||||
|
||||
while let Some(addr) = iter.next() {
|
||||
let addr = unmark_cell_bits!(addr);
|
||||
|
||||
let addr = if addr.is_var() {
|
||||
unmark_cell_bits!(heap_bound_store(
|
||||
iter.base_iter.heap,
|
||||
heap_bound_deref(iter.base_iter.heap, addr)
|
||||
))
|
||||
} else {
|
||||
addr
|
||||
};
|
||||
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Lis) => {
|
||||
use crate::parser::parser::as_partial_string;
|
||||
@@ -1381,42 +1404,31 @@ impl MachineState {
|
||||
|
||||
match as_partial_string(head, tail) {
|
||||
Ok((string, Some(tail))) => {
|
||||
term_stack.push(Term::PartialString(Cell::default(), string, tail));
|
||||
term_stack.push(Term::PartialString(Cell::default(), Rc::new(string), tail));
|
||||
}
|
||||
Ok((string, None)) => {
|
||||
let atom = AtomTable::build_with(&self.atom_tbl, &string);
|
||||
term_stack.push(Term::CompleteString(Cell::default(), atom));
|
||||
term_stack.push(Term::CompleteString(Cell::default(), Rc::new(string)));
|
||||
}
|
||||
Err(cons_term) => term_stack.push(cons_term),
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar, h) => {
|
||||
term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("_{}", h))));
|
||||
(HeapCellValueTag::Cons | HeapCellValueTag::Fixnum | HeapCellValueTag::F64Offset) => {
|
||||
term_stack.push(Term::Literal(Cell::default(), Literal::try_from((addr, &self.arena.f64_tbl)).unwrap()));
|
||||
}
|
||||
(HeapCellValueTag::Cons | HeapCellValueTag::CStr | HeapCellValueTag::Fixnum |
|
||||
HeapCellValueTag::Char | HeapCellValueTag::F64) => {
|
||||
term_stack.push(Term::Literal(Cell::default(), Literal::try_from(addr).unwrap()));
|
||||
(HeapCellValueTag::StackVar, h) => {
|
||||
term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("s_{h}"))));
|
||||
}
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
|
||||
term_stack.push(Term::Var(Cell::default(), VarPtr::from(format!("_{h}"))));
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
let h = iter.focus().value() as usize;
|
||||
let mut arity = arity;
|
||||
let value = iter.heap[h.saturating_sub(1)];
|
||||
|
||||
if iter.heap.len() > h + arity + 1 {
|
||||
let value = iter.heap[h + arity + 1];
|
||||
|
||||
if let Some(idx) = get_structure_index(value) {
|
||||
// in the second condition, arity == 0,
|
||||
// meaning idx cannot pertain to this atom
|
||||
// if it is the direct subterm of a larger
|
||||
// structure.
|
||||
if arity > 0 || !iter.direct_subterm_of_str(h) {
|
||||
term_stack.push(
|
||||
Term::Literal(Cell::default(), Literal::CodeIndex(idx))
|
||||
);
|
||||
|
||||
arity += 1;
|
||||
}
|
||||
}
|
||||
if let Some(idx) = get_structure_index(value) {
|
||||
term_stack.push(Term::Literal(Cell::default(), Literal::CodeIndexOffset(idx.into())));
|
||||
arity += 1;
|
||||
}
|
||||
|
||||
if arity == 0 {
|
||||
@@ -1429,28 +1441,22 @@ impl MachineState {
|
||||
term_stack.push(Term::Clause(Cell::default(), name, subterms));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStr, atom) => {
|
||||
let tail = term_stack.pop().unwrap();
|
||||
|
||||
if let Term::Literal(_, Literal::Atom(atom!("[]"))) = &tail {
|
||||
term_stack.push(Term::CompleteString(Cell::default(), atom));
|
||||
} else {
|
||||
term_stack.push(Term::PartialString(
|
||||
Cell::default(),
|
||||
atom.as_str().to_owned(),
|
||||
Box::new(tail),
|
||||
));
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, h) => {
|
||||
let atom = cell_as_atom_cell!(iter.heap[h]).get_name();
|
||||
let HeapStringScan { string, .. } = iter.heap.scan_slice_to_str(h);
|
||||
let tail = term_stack.pop().unwrap();
|
||||
|
||||
term_stack.push(Term::PartialString(
|
||||
Cell::default(),
|
||||
atom.as_str().to_owned(),
|
||||
Box::new(tail),
|
||||
));
|
||||
term_stack.push(if matches!(tail, Term::Literal(_, Literal::Atom(atom!("[]")))) {
|
||||
Term::CompleteString(
|
||||
Cell::default(),
|
||||
Rc::new(string.to_owned()),
|
||||
)
|
||||
} else {
|
||||
Term::PartialString(
|
||||
Cell::default(),
|
||||
Rc::new(string.to_owned()),
|
||||
Box::new(tail),
|
||||
)
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
@@ -1599,7 +1605,7 @@ impl Machine {
|
||||
let predicate_name = cell_as_atom!(self.deref_register(2));
|
||||
|
||||
let arity = self.deref_register(3);
|
||||
let arity = match Number::try_from(arity) {
|
||||
let arity = match Number::try_from((arity, &self.machine_st.arena.f64_tbl)) {
|
||||
Ok(Number::Integer(n)) if *n >= Integer::ZERO && *n <= Integer::from(MAX_ARITY) => {
|
||||
let value: usize = (&*n).try_into().unwrap();
|
||||
Ok(value)
|
||||
@@ -1757,7 +1763,7 @@ impl Machine {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn push_load_state_payload(&mut self) {
|
||||
let payload = arena_alloc!(
|
||||
let payload: TypedArenaPtr<LiveLoadState> = arena_alloc!(
|
||||
LoadStatePayload::new(self.code.len(), LiveTermStream::new(ListingSource::User),),
|
||||
&mut self.machine_st.arena
|
||||
);
|
||||
@@ -1784,11 +1790,8 @@ impl Machine {
|
||||
(HeapCellValueTag::Cons, cons_ptr) => {
|
||||
match_untyped_arena_ptr!(cons_ptr,
|
||||
(ArenaHeaderTag::LiveLoadState, payload) => {
|
||||
unsafe {
|
||||
std::ptr::drop_in_place(
|
||||
payload.as_ptr() as *mut LiveLoadState,
|
||||
);
|
||||
}
|
||||
let mut payload = payload;
|
||||
payload.drop_payload()
|
||||
}
|
||||
_ => {}
|
||||
);
|
||||
@@ -1805,7 +1808,7 @@ impl Machine {
|
||||
pub(crate) fn push_load_context(&mut self) -> CallResult {
|
||||
let stream = self.machine_st.get_stream_or_alias(
|
||||
self.machine_st.registers[1],
|
||||
&self.indices.stream_aliases,
|
||||
&self.indices,
|
||||
atom!("$push_load_context"),
|
||||
2,
|
||||
)?;
|
||||
@@ -1978,7 +1981,6 @@ impl Machine {
|
||||
};
|
||||
|
||||
let stub_gen = || functor_stub(key.0, key.1);
|
||||
|
||||
let head = self.deref_register(2);
|
||||
|
||||
if head.is_var() {
|
||||
@@ -2014,7 +2016,14 @@ impl Machine {
|
||||
.wam_prelude
|
||||
.indices
|
||||
.get_predicate_code_index(name, arity, module_name)
|
||||
.map(|code_idx| code_idx.get_tag())
|
||||
.map(|offset| {
|
||||
loader
|
||||
.payload
|
||||
.machine_st
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.with_entry(offset.into(), |idx| idx.tag())
|
||||
})
|
||||
.unwrap_or(IndexPtrTag::DynamicUndefined);
|
||||
|
||||
if idx_tag == IndexPtrTag::Index {
|
||||
@@ -2120,14 +2129,16 @@ impl Machine {
|
||||
.indices
|
||||
.remove_predicate_skeleton(&compilation_target, &key)
|
||||
.map(|skeleton| {
|
||||
let mut clause_clause_skeleton = loader
|
||||
.wam_prelude
|
||||
.indices
|
||||
.remove_predicate_skeleton(
|
||||
let mut clause_clause_skeleton =
|
||||
match loader.wam_prelude.indices.remove_predicate_skeleton(
|
||||
&clause_clause_compilation_target,
|
||||
&(atom!("$clause"), 2),
|
||||
)
|
||||
.unwrap();
|
||||
) {
|
||||
Some(skeleton) => skeleton,
|
||||
None => {
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
|
||||
let result = skeleton
|
||||
.core
|
||||
@@ -2157,9 +2168,16 @@ impl Machine {
|
||||
.indices
|
||||
.remove_predicate_skeleton(&compilation_target, &key);
|
||||
|
||||
let mut code_index = loader.get_or_insert_code_index(key, compilation_target);
|
||||
let offset = loader.get_or_insert_code_index(key, compilation_target);
|
||||
|
||||
code_index.set(IndexPtr::undefined());
|
||||
loader
|
||||
.payload
|
||||
.machine_st
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.with_entry_mut(offset.into(), |code_idx| {
|
||||
*code_idx = IndexPtr::undefined();
|
||||
});
|
||||
|
||||
loader.payload.compilation_target = clause_clause_compilation_target;
|
||||
|
||||
@@ -2189,7 +2207,7 @@ impl Machine {
|
||||
.machine_st
|
||||
.store(self.machine_st.deref(self.machine_st[temp_v!(3)]));
|
||||
|
||||
let target_pos = match Number::try_from(target_pos) {
|
||||
let target_pos = match Number::try_from((target_pos, &self.machine_st.arena.f64_tbl)) {
|
||||
Ok(Number::Integer(n)) => {
|
||||
let value: usize = (&*n).try_into().unwrap();
|
||||
value
|
||||
@@ -2339,29 +2357,38 @@ impl Machine {
|
||||
.get_meta_predicate_spec(predicate_name, arity, &compilation_target)
|
||||
{
|
||||
Some(meta_specs) => {
|
||||
let term_loc = self.machine_st.heap.len();
|
||||
let term_loc = self.machine_st.heap.cell_len();
|
||||
|
||||
self.machine_st
|
||||
.heap
|
||||
.push(atom_as_cell!(predicate_name, arity));
|
||||
self.machine_st
|
||||
.heap
|
||||
.extend(meta_specs.iter().map(|meta_spec| match meta_spec {
|
||||
MetaSpec::Minus => atom_as_cell!(atom!("+")),
|
||||
MetaSpec::Plus => atom_as_cell!(atom!("-")),
|
||||
MetaSpec::Either => atom_as_cell!(atom!("?")),
|
||||
MetaSpec::Colon => atom_as_cell!(atom!(":")),
|
||||
MetaSpec::RequiresExpansionWithArgument(ref arg_num) => {
|
||||
fixnum_as_cell!(Fixnum::build_with(*arg_num as i64))
|
||||
}
|
||||
}));
|
||||
let mut writer = match self.machine_st.heap.reserve(3 + meta_specs.len()) {
|
||||
Ok(writer) => writer,
|
||||
Err(err_loc) => {
|
||||
self.machine_st.throw_resource_error(err_loc);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let heap_loc = self.machine_st.heap.len();
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(atom_as_cell!(predicate_name, arity));
|
||||
|
||||
self.machine_st
|
||||
.heap
|
||||
.push(atom_as_cell!(atom!("meta_predicate"), 1));
|
||||
self.machine_st.heap.push(str_loc_as_cell!(term_loc));
|
||||
for meta_spec in meta_specs.iter() {
|
||||
section.push_cell(match meta_spec {
|
||||
MetaSpec::Minus => atom_as_cell!(atom!("+")),
|
||||
MetaSpec::Plus => atom_as_cell!(atom!("-")),
|
||||
MetaSpec::Either => atom_as_cell!(atom!("?")),
|
||||
MetaSpec::Colon => atom_as_cell!(atom!(":")),
|
||||
MetaSpec::RequiresExpansionWithArgument(ref arg_num) => {
|
||||
fixnum_as_cell!(/* FIXME this is not safe */ unsafe {
|
||||
Fixnum::build_with_unchecked(*arg_num as i64)
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
section.push_cell(atom_as_cell!(atom!("meta_predicate"), 1));
|
||||
section.push_cell(str_loc_as_cell!(term_loc));
|
||||
});
|
||||
|
||||
let heap_loc = self.machine_st.heap.cell_len() - 2;
|
||||
|
||||
unify!(
|
||||
self.machine_st,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,15 @@
|
||||
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::machine::loader::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::streams::Stream;
|
||||
use crate::machine::streams::{Stream, StreamOptions};
|
||||
use crate::machine::ClauseType;
|
||||
use crate::machine::MachineStubGen;
|
||||
use crate::offset_table::*;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
@@ -15,11 +18,8 @@ use scryer_modular_bitfield::{bitfield, BitfieldSpecifier};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeSet;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::types::*;
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub(crate) struct OrderedOpDirKey(pub(crate) Atom, pub(crate) Fixity);
|
||||
|
||||
// 7.2
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@@ -125,10 +125,19 @@ impl IndexPtr {
|
||||
pub(crate) fn is_dynamic_undefined(&self) -> bool {
|
||||
matches!(self.tag(), IndexPtrTag::DynamicUndefined)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn local(&self) -> Option<usize> {
|
||||
match self.tag() {
|
||||
IndexPtrTag::Index => Some(self.p() as usize),
|
||||
IndexPtrTag::DynamicIndex => Some(self.p() as usize),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Ord, Hash, PartialOrd, Eq, PartialEq)]
|
||||
pub struct CodeIndex(TypedArenaPtr<IndexPtr>);
|
||||
#[derive(Debug, Clone, Copy)] // , Ord, Hash, PartialOrd, Eq, PartialEq)]
|
||||
pub struct CodeIndex(CodeIndexOffset);
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const_assert!(std::mem::align_of::<CodeIndex>() == 4);
|
||||
@@ -136,85 +145,53 @@ const_assert!(std::mem::align_of::<CodeIndex>() == 4);
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const_assert!(std::mem::align_of::<CodeIndex>() == 8);
|
||||
|
||||
impl Deref for CodeIndex {
|
||||
type Target = TypedArenaPtr<IndexPtr>;
|
||||
|
||||
impl From<CodeIndex> for HeapCellValue {
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &TypedArenaPtr<IndexPtr> {
|
||||
&self.0
|
||||
fn from(idx: CodeIndex) -> HeapCellValue {
|
||||
HeapCellValue::from(idx.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for CodeIndex {
|
||||
impl From<CodeIndexOffset> for CodeIndex {
|
||||
#[inline(always)]
|
||||
fn deref_mut(&mut self) -> &mut TypedArenaPtr<IndexPtr> {
|
||||
&mut self.0
|
||||
fn from(offset: CodeIndexOffset) -> CodeIndex {
|
||||
CodeIndex(offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CodeIndex> for UntypedArenaPtr {
|
||||
impl From<CodeIndex> for CodeIndexOffset {
|
||||
#[inline(always)]
|
||||
fn from(ptr: CodeIndex) -> UntypedArenaPtr {
|
||||
UntypedArenaPtr::build_with(ptr.0.as_ptr() as usize)
|
||||
fn from(value: CodeIndex) -> CodeIndexOffset {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UntypedArenaPtr> for CodeIndex {
|
||||
impl From<&'_ CodeIndex> for CodeIndexOffset {
|
||||
#[inline(always)]
|
||||
fn from(ptr: UntypedArenaPtr) -> CodeIndex {
|
||||
CodeIndex(TypedArenaPtr::new(ptr.get_ptr() as *mut IndexPtr))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TypedArenaPtr<IndexPtr>> for CodeIndex {
|
||||
#[inline(always)]
|
||||
fn from(ptr: TypedArenaPtr<IndexPtr>) -> CodeIndex {
|
||||
CodeIndex(ptr)
|
||||
fn from(value: &'_ CodeIndex) -> CodeIndexOffset {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeIndex {
|
||||
#[inline]
|
||||
pub(crate) fn new(ptr: IndexPtr, arena: &mut Arena) -> Self {
|
||||
CodeIndex(arena_alloc!(ptr, arena))
|
||||
pub(crate) fn new(ptr: IndexPtr, code_index_tbl: &mut CodeIndexTable) -> Self {
|
||||
CodeIndex(code_index_tbl.build_with(ptr))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn default(arena: &mut Arena) -> Self {
|
||||
CodeIndex::new(IndexPtr::undefined(), arena)
|
||||
}
|
||||
|
||||
pub(crate) fn local(&self) -> Option<usize> {
|
||||
match self.0.tag() {
|
||||
IndexPtrTag::Index => Some(self.0.p() as usize),
|
||||
IndexPtrTag::DynamicIndex => Some(self.0.p() as usize),
|
||||
_ => None,
|
||||
}
|
||||
pub(crate) fn default(code_index_tbl: &mut CodeIndexTable) -> Self {
|
||||
CodeIndex::new(IndexPtr::undefined(), code_index_tbl)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn get(&self) -> IndexPtr {
|
||||
*self.0.deref()
|
||||
pub(crate) fn set(&self, code_index_tbl: &mut CodeIndexTable, value: IndexPtr) {
|
||||
code_index_tbl.with_entry_mut(self.0, |idx| *idx = value);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn set(&mut self, value: IndexPtr) {
|
||||
*self.0.deref_mut() = value;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_tag(self) -> IndexPtrTag {
|
||||
self.0.tag()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn replace(&mut self, value: IndexPtr) -> IndexPtr {
|
||||
std::mem::replace(self.0.deref_mut(), value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn as_ptr(&self) -> *const IndexPtr {
|
||||
self.0.as_ptr()
|
||||
pub(crate) fn replace(&self, code_index_tbl: &mut CodeIndexTable, value: IndexPtr) -> IndexPtr {
|
||||
code_index_tbl.with_entry_mut(self.0, |idx| std::mem::replace(idx, value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +206,7 @@ impl VarKey {
|
||||
#[inline]
|
||||
pub(crate) fn to_string(&self) -> String {
|
||||
match self {
|
||||
VarKey::AnonVar(h) => format!("_{}", h),
|
||||
VarKey::AnonVar(h) => format!("_{h}"),
|
||||
VarKey::VarPtr(var) => var.borrow().to_string(),
|
||||
}
|
||||
}
|
||||
@@ -268,8 +245,8 @@ pub struct IndexStore {
|
||||
pub(super) meta_predicates: MetaPredicateDir,
|
||||
pub(super) modules: ModuleDir,
|
||||
pub(super) op_dir: OpDir,
|
||||
pub(super) streams: StreamDir,
|
||||
pub(super) stream_aliases: StreamAliasDir,
|
||||
streams: StreamDir,
|
||||
stream_aliases: StreamAliasDir,
|
||||
}
|
||||
|
||||
impl IndexStore {
|
||||
@@ -287,8 +264,27 @@ impl IndexStore {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn goal_expansion_defined(&self, key: PredicateKey) -> bool {
|
||||
self.goal_expansion_indices.contains(&key)
|
||||
pub(crate) fn goal_expansion_defined(&self, key: PredicateKey, module_name: Atom) -> bool {
|
||||
let compilation_target = match module_name {
|
||||
atom!("user") => CompilationTarget::User,
|
||||
_ => CompilationTarget::Module(module_name),
|
||||
};
|
||||
|
||||
match key {
|
||||
_ if self.goal_expansion_indices.contains(&key) => true,
|
||||
_ => self
|
||||
.get_meta_predicate_spec(key.0, key.1, &compilation_target)
|
||||
.map(|meta_specs| {
|
||||
meta_specs.iter().find(|meta_spec| {
|
||||
matches!(
|
||||
meta_spec,
|
||||
MetaSpec::Colon | MetaSpec::RequiresExpansionWithArgument(_)
|
||||
)
|
||||
})
|
||||
})
|
||||
.map(|meta_spec_opt| meta_spec_opt.is_some())
|
||||
.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_predicate_skeleton_mut(
|
||||
@@ -385,10 +381,10 @@ impl IndexStore {
|
||||
key: &PredicateKey,
|
||||
) -> Option<PredicateSkeleton> {
|
||||
match compilation_target {
|
||||
CompilationTarget::User => self.extensible_predicates.remove(key),
|
||||
CompilationTarget::User => self.extensible_predicates.swap_remove(key),
|
||||
CompilationTarget::Module(ref module_name) => {
|
||||
if let Some(module) = self.modules.get_mut(module_name) {
|
||||
module.extensible_predicates.remove(key)
|
||||
module.extensible_predicates.swap_remove(key)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -447,6 +443,113 @@ impl IndexStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_stream(
|
||||
&mut self,
|
||||
stream: Stream,
|
||||
stub_name: Atom,
|
||||
stub_arity: usize,
|
||||
) -> Result<(), MachineStubGen> {
|
||||
if let Some(alias) = stream.options().get_alias() {
|
||||
if self.stream_aliases.contains_key(&alias) {
|
||||
return Err(Box::new(move |machine_st| {
|
||||
machine_st.occupied_alias_permission_error(alias, stub_name, stub_arity)
|
||||
}));
|
||||
}
|
||||
|
||||
self.stream_aliases.insert(alias, stream);
|
||||
}
|
||||
|
||||
self.streams.insert(stream);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn remove_stream(&mut self, stream: Stream) {
|
||||
if let Some(alias) = stream.options().get_alias() {
|
||||
debug_assert_eq!(self.stream_aliases.get(&alias), Some(&stream));
|
||||
assert!(!is_protected_alias(alias));
|
||||
|
||||
self.stream_aliases.swap_remove(&alias);
|
||||
}
|
||||
self.streams.remove(&stream);
|
||||
}
|
||||
|
||||
pub(crate) fn update_stream_options<F: Fn(&mut StreamOptions)>(
|
||||
&mut self,
|
||||
mut stream: Stream,
|
||||
callback: F,
|
||||
) {
|
||||
if let Some(prev_alias) = stream.options().get_alias() {
|
||||
debug_assert_eq!(self.stream_aliases.get(&prev_alias), Some(&stream));
|
||||
}
|
||||
let options = stream.options_mut();
|
||||
let prev_alias = options.get_alias();
|
||||
|
||||
callback(options);
|
||||
|
||||
if options.get_alias() != prev_alias {
|
||||
if prev_alias.map(is_protected_alias).unwrap_or(false)
|
||||
|| options
|
||||
.get_alias()
|
||||
.map(|alias| self.has_stream(alias))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// user_input, user_output and user_error cannot be realiased,
|
||||
// and realiasing cannot shadow an existing stream.
|
||||
options.set_alias_to_atom_opt(prev_alias);
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(prev_alias) = prev_alias {
|
||||
self.stream_aliases.swap_remove(&prev_alias);
|
||||
}
|
||||
if let Some(new_alias) = options.get_alias() {
|
||||
self.stream_aliases.insert(new_alias, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn has_stream(&self, alias: Atom) -> bool {
|
||||
self.stream_aliases.contains_key(&alias)
|
||||
}
|
||||
|
||||
/// ## Warning
|
||||
///
|
||||
/// The returned stream's options should only be modified through
|
||||
/// [`IndexStore::update_stream_options`], to avoid breaking the
|
||||
/// invariants of [`IndexStore`].
|
||||
pub(crate) fn get_stream(&self, alias: Atom) -> Option<Stream> {
|
||||
self.stream_aliases.get(&alias).copied()
|
||||
}
|
||||
|
||||
pub(crate) fn iter_streams<'a, R: std::ops::RangeBounds<Stream>>(
|
||||
&'a self,
|
||||
range: R,
|
||||
) -> impl Iterator<Item = Stream> + 'a {
|
||||
self.streams.range(range).copied()
|
||||
}
|
||||
|
||||
/// Forcibly sets `alias` to `stream`.
|
||||
/// If there was a previous stream with that alias, it will lose that alias.
|
||||
///
|
||||
/// Consider using [`add_stream`](Self::add_stream) if you wish to instead
|
||||
/// return an error when stream aliases conflict.
|
||||
pub(crate) fn set_stream(&mut self, alias: Atom, mut stream: Stream) {
|
||||
if let Some(mut prev_stream) = self.get_stream(alias) {
|
||||
if prev_stream == stream {
|
||||
// Nothing to do, as the stream is already present
|
||||
return;
|
||||
}
|
||||
|
||||
prev_stream.options_mut().set_alias_to_atom_opt(None);
|
||||
}
|
||||
|
||||
stream.options_mut().set_alias_to_atom_opt(Some(alias));
|
||||
|
||||
self.stream_aliases.insert(alias, stream);
|
||||
self.streams.insert(stream);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn new() -> Self {
|
||||
index_store!(
|
||||
@@ -456,3 +559,13 @@ impl IndexStore {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A stream is said to have a "protected" alias if modifying its
|
||||
/// alias would cause breakage in other parts of the code.
|
||||
///
|
||||
/// A stream with a protected alias cannot be realiased through
|
||||
/// [`IndexStore::update_stream_options`]. Instead, one has to use
|
||||
/// [`IndexStore::set_stream`] to do so.
|
||||
fn is_protected_alias(alias: Atom) -> bool {
|
||||
alias == atom!("user_input") || alias == atom!("user_output") || alias == atom!("user_error")
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use indexmap::IndexMap;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::ops::{Index, IndexMut, Range};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) type Registers = [HeapCellValue; MAX_ARITY + 1];
|
||||
@@ -35,8 +35,8 @@ pub(super) enum MachineMode {
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) enum HeapPtr {
|
||||
HeapCell(usize),
|
||||
PStrChar(usize, usize),
|
||||
PStrLocation(usize, usize),
|
||||
PStr(usize), // Char(usize),
|
||||
// PStrLocation(usize),
|
||||
}
|
||||
|
||||
impl Default for HeapPtr {
|
||||
@@ -183,47 +183,62 @@ impl IndexMut<RegType> for MachineState {
|
||||
}
|
||||
}
|
||||
|
||||
pub type CallResult = Result<(), Vec<HeapCellValue>>;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn pstr_loc_and_offset(heap: &[HeapCellValue], index: usize) -> (usize, Fixnum) {
|
||||
read_heap_cell!(heap[index],
|
||||
(HeapCellValueTag::PStr | HeapCellValueTag::CStr) => {
|
||||
(index, Fixnum::build_with(0))
|
||||
}
|
||||
(HeapCellValueTag::PStrOffset, h) => {
|
||||
(h, cell_as_fixnum!(heap[index+1]))
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
)
|
||||
}
|
||||
pub type CallResult<Ok = ()> = Result<Ok, MachineStub>;
|
||||
|
||||
// size may be an upper bound.
|
||||
// true_size is calculated to compute the exact offset.
|
||||
fn push_var_eq_functors<'a>(
|
||||
heap: &mut Heap,
|
||||
size: usize,
|
||||
iter: impl Iterator<Item = (&'a VarKey, &'a HeapCellValue)>,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Vec<HeapCellValue> {
|
||||
let mut list_of_var_eqs = vec![];
|
||||
) -> Result<HeapCellValue, usize> {
|
||||
let src_h = heap.cell_len();
|
||||
|
||||
for (var, binding) in iter {
|
||||
let var_atom = AtomTable::build_with(atom_tbl, &var.to_string());
|
||||
let h = heap.len();
|
||||
let true_size = if size > 0 {
|
||||
let mut writer = heap.reserve(2 + 5 * size)?;
|
||||
|
||||
heap.push(atom_as_cell!(atom!("="), 2));
|
||||
heap.push(atom_as_cell!(var_atom));
|
||||
heap.push(*binding);
|
||||
writer
|
||||
.write_with(|section| {
|
||||
let mut size = 0;
|
||||
|
||||
list_of_var_eqs.push(str_loc_as_cell!(h));
|
||||
}
|
||||
for (var, binding) in iter {
|
||||
let var_atom = AtomTable::build_with(atom_tbl, &var.to_string());
|
||||
|
||||
list_of_var_eqs
|
||||
section.push_cell(atom_as_cell!(atom!("="), 2));
|
||||
section.push_cell(atom_as_cell!(var_atom));
|
||||
section.push_cell(*binding);
|
||||
|
||||
size += 1;
|
||||
}
|
||||
|
||||
for idx in 0..size {
|
||||
section.push_cell(list_loc_as_cell!(section.cell_len() + 1));
|
||||
section.push_cell(str_loc_as_cell!(src_h + 3 * idx));
|
||||
}
|
||||
|
||||
if size > 0 {
|
||||
section.push_cell(empty_list_as_cell!());
|
||||
}
|
||||
|
||||
size
|
||||
})
|
||||
.result
|
||||
} else {
|
||||
size
|
||||
};
|
||||
|
||||
Ok(if true_size > 0 {
|
||||
heap_loc_as_cell!(src_h + 3 * true_size)
|
||||
} else {
|
||||
empty_list_as_cell!()
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Ball {
|
||||
pub(super) boundary: usize,
|
||||
pub(super) pstr_boundary: usize,
|
||||
pub(super) stub: Heap,
|
||||
}
|
||||
|
||||
@@ -231,23 +246,44 @@ impl Ball {
|
||||
pub(super) fn new() -> Self {
|
||||
Ball {
|
||||
boundary: 0,
|
||||
pstr_boundary: 0,
|
||||
stub: Heap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn reset(&mut self) {
|
||||
self.boundary = 0;
|
||||
self.pstr_boundary = 0;
|
||||
self.stub.clear();
|
||||
}
|
||||
|
||||
pub(super) fn copy_and_align(&self, h: usize) -> Heap {
|
||||
pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result<usize, usize> {
|
||||
let h = dest.cell_len();
|
||||
let diff = self.boundary as i64 - h as i64;
|
||||
|
||||
self.stub
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|heap_value| heap_value - diff)
|
||||
.collect()
|
||||
let mut dest_writer = dest.reserve(self.stub.cell_len())?;
|
||||
|
||||
dest_writer.write_with(|section| {
|
||||
for idx in 0..self.pstr_boundary {
|
||||
section.push_cell(self.stub[idx] - diff);
|
||||
}
|
||||
});
|
||||
|
||||
let mut pstr_threshold = heap_index!(self.pstr_boundary);
|
||||
|
||||
while pstr_threshold < heap_index!(self.stub.cell_len()) {
|
||||
let HeapStringScan { string, tail_idx } = self.stub.scan_slice_to_str(pstr_threshold);
|
||||
|
||||
pstr_threshold += dest_writer
|
||||
.write_with(|section| {
|
||||
if section.push_pstr(string).is_some() {
|
||||
section.push_cell(self.stub[tail_idx] - diff);
|
||||
}
|
||||
})
|
||||
.bytes_written;
|
||||
}
|
||||
|
||||
Ok(h)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,21 +315,6 @@ impl<'a> IndexMut<usize> for CopyTerm<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CopierTarget for CopyTerm<'a> {
|
||||
#[inline(always)]
|
||||
fn threshold(&self) -> usize {
|
||||
self.state.heap.len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn push(&mut self, hcv: HeapCellValue) {
|
||||
self.state.heap.push(hcv);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn 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)
|
||||
@@ -304,10 +325,40 @@ impl<'a> CopierTarget for CopyTerm<'a> {
|
||||
self.state.deref(value)
|
||||
}
|
||||
|
||||
#[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 stack(&mut self) -> &mut Stack {
|
||||
&mut self.state.stack
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn threshold(&self) -> usize {
|
||||
self.state.heap.cell_len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_slice_from<'b>(&'b self, from: usize) -> Box<dyn Iterator<Item = u8> + 'b> {
|
||||
Box::new(self.state.heap.as_slice()[from..].iter().cloned())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> {
|
||||
self.state.heap.copy_pstr_within(pstr_loc)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> {
|
||||
self.state.heap.reserve(num_cells)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> {
|
||||
self.state.heap.copy_slice_to_end(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -315,7 +366,6 @@ pub(super) struct CopyBallTerm<'a> {
|
||||
attr_var_queue: &'a mut Vec<usize>,
|
||||
stack: &'a mut Stack,
|
||||
heap: &'a mut Heap,
|
||||
heap_boundary: usize,
|
||||
stub: &'a mut Heap,
|
||||
}
|
||||
|
||||
@@ -326,13 +376,10 @@ impl<'a> CopyBallTerm<'a> {
|
||||
heap: &'a mut Heap,
|
||||
stub: &'a mut Heap,
|
||||
) -> Self {
|
||||
let hb = heap.len();
|
||||
|
||||
CopyBallTerm {
|
||||
attr_var_queue,
|
||||
stack,
|
||||
heap,
|
||||
heap_boundary: hb,
|
||||
stub,
|
||||
}
|
||||
}
|
||||
@@ -342,10 +389,10 @@ impl<'a> Index<usize> for CopyBallTerm<'a> {
|
||||
type Output = HeapCellValue;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
if index < self.heap_boundary {
|
||||
if index < self.heap.cell_len() {
|
||||
&self.heap[index]
|
||||
} else {
|
||||
let index = index - self.heap_boundary;
|
||||
let index = index - self.heap.cell_len();
|
||||
&self.stub[index]
|
||||
}
|
||||
}
|
||||
@@ -353,10 +400,10 @@ impl<'a> Index<usize> for CopyBallTerm<'a> {
|
||||
|
||||
impl<'a> IndexMut<usize> for CopyBallTerm<'a> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
if index < self.heap_boundary {
|
||||
if index < self.heap.cell_len() {
|
||||
&mut self.heap[index]
|
||||
} else {
|
||||
let index = index - self.heap_boundary;
|
||||
let index = index - self.heap.cell_len();
|
||||
&mut self.stub[index]
|
||||
}
|
||||
}
|
||||
@@ -364,11 +411,7 @@ impl<'a> IndexMut<usize> for CopyBallTerm<'a> {
|
||||
|
||||
impl<'a> CopierTarget for CopyBallTerm<'a> {
|
||||
fn threshold(&self) -> usize {
|
||||
self.heap_boundary + self.stub.len()
|
||||
}
|
||||
|
||||
fn push(&mut self, value: HeapCellValue) {
|
||||
self.stub.push(value);
|
||||
self.heap.cell_len() + self.stub.cell_len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -379,10 +422,10 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
|
||||
fn store(&self, value: HeapCellValue) -> HeapCellValue {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
|
||||
if h < self.heap_boundary {
|
||||
if h < self.heap.cell_len() {
|
||||
self.heap[h]
|
||||
} else {
|
||||
let index = h - self.heap_boundary;
|
||||
let index = h - self.heap.cell_len();
|
||||
self.stub[index]
|
||||
}
|
||||
}
|
||||
@@ -411,6 +454,45 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
|
||||
fn stack(&mut self) -> &mut Stack {
|
||||
self.stack
|
||||
}
|
||||
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> {
|
||||
debug_assert!(pstr_loc < self.heap.byte_len());
|
||||
|
||||
let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(pstr_loc);
|
||||
self.stub.allocate_pstr(string)?;
|
||||
Ok(tail_idx)
|
||||
}
|
||||
|
||||
fn as_slice_from<'b>(&'b self, from: usize) -> Box<dyn Iterator<Item = u8> + 'b> {
|
||||
if from < self.heap.byte_len() {
|
||||
Box::new(
|
||||
self.heap.as_slice()[from..]
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(self.stub.as_slice().iter().cloned()),
|
||||
)
|
||||
} else {
|
||||
Box::new(self.stub.as_slice()[from..].iter().cloned())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> {
|
||||
self.stub.reserve(num_cells)
|
||||
}
|
||||
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> {
|
||||
let len = bounds.end - bounds.start;
|
||||
let mut stub_writer = self.stub.reserve(len)?;
|
||||
|
||||
stub_writer.write_with(|section| {
|
||||
for idx in bounds {
|
||||
section.push_cell(self.heap[idx]);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineState {
|
||||
@@ -461,10 +543,6 @@ impl MachineState {
|
||||
let addr = self.store(self.deref(addr));
|
||||
|
||||
read_heap_cell!(addr,
|
||||
(HeapCellValueTag::Char, c) => {
|
||||
chars.push(c);
|
||||
continue;
|
||||
}
|
||||
(HeapCellValueTag::Atom, (name, arity)) => {
|
||||
if arity == 0 {
|
||||
if let Some(c) = name.as_char() {
|
||||
@@ -537,39 +615,26 @@ impl MachineState {
|
||||
pub fn write_read_term_options(
|
||||
&mut self,
|
||||
mut var_list: Vec<(VarKey, HeapCellValue, usize)>,
|
||||
singleton_var_list: Vec<HeapCellValue>,
|
||||
singleton_heap_list: HeapCellValue,
|
||||
) -> CallResult {
|
||||
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))
|
||||
}
|
||||
}),
|
||||
&self.atom_tbl,
|
||||
);
|
||||
|
||||
let singleton_addr = self.registers[3];
|
||||
let singletons_offset = heap_loc_as_cell!(iter_to_heap_list(
|
||||
&mut self.heap,
|
||||
singleton_var_list.into_iter()
|
||||
));
|
||||
|
||||
unify_fn!(*self, singletons_offset, singleton_addr);
|
||||
unify_fn!(*self, singleton_heap_list, singleton_addr);
|
||||
|
||||
if self.fail {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let vars_addr = self.registers[4];
|
||||
let vars_offset = heap_loc_as_cell!(iter_to_heap_list(
|
||||
&mut self.heap,
|
||||
var_list.into_iter().map(|(_, cell, _)| cell)
|
||||
));
|
||||
let vars_offset = resource_error_call_result!(
|
||||
self,
|
||||
sized_iter_to_heap_list(
|
||||
&mut self.heap,
|
||||
var_list.len(),
|
||||
var_list.iter().map(|(_, cell, _)| *cell),
|
||||
)
|
||||
);
|
||||
|
||||
unify_fn!(*self, vars_offset, vars_addr);
|
||||
|
||||
@@ -578,24 +643,27 @@ 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()
|
||||
));
|
||||
|
||||
Ok(unify_fn!(*self, var_names_offset, var_names_addr))
|
||||
let var_names_offset = resource_error_call_result!(
|
||||
self,
|
||||
push_var_eq_functors(
|
||||
&mut self.heap,
|
||||
var_list.len(),
|
||||
var_list.iter().filter_map(|(var_name, var, _)| {
|
||||
if var_name.is_anon() {
|
||||
None
|
||||
} else {
|
||||
Some((var_name, var))
|
||||
}
|
||||
}),
|
||||
&self.atom_tbl,
|
||||
)
|
||||
);
|
||||
unify_fn!(*self, var_names_offset, var_names_addr);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_term_body(&mut self, mut term_write_result: TermWriteResult) -> CallResult {
|
||||
let heap_loc = read_heap_cell!(self.heap[term_write_result.heap_loc],
|
||||
(HeapCellValueTag::PStr | HeapCellValueTag::PStrOffset) => {
|
||||
pstr_loc_as_cell!(term_write_result.heap_loc)
|
||||
}
|
||||
_ => {
|
||||
heap_loc_as_cell!(term_write_result.heap_loc)
|
||||
}
|
||||
);
|
||||
|
||||
let heap_loc = heap_loc_as_cell!(term_write_result.heap_loc);
|
||||
unify_fn!(*self, heap_loc, self.registers[2]);
|
||||
|
||||
if self.fail {
|
||||
@@ -607,10 +675,9 @@ impl MachineState {
|
||||
}
|
||||
|
||||
let mut singleton_var_set: IndexMap<Ref, bool> = IndexMap::new();
|
||||
self.heap[0] = heap_loc;
|
||||
|
||||
for cell in
|
||||
stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, heap_loc)
|
||||
{
|
||||
for cell in stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, 0) {
|
||||
let cell = unmark_cell_bits!(cell);
|
||||
|
||||
if let Some(var) = cell.as_var() {
|
||||
@@ -622,29 +689,29 @@ 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;
|
||||
}
|
||||
let singleton_var_list = resource_error_call_result!(
|
||||
self,
|
||||
push_var_eq_functors(
|
||||
&mut self.heap,
|
||||
term_write_result.var_dict.len(),
|
||||
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
|
||||
}
|
||||
}),
|
||||
&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() {
|
||||
*var = heap_bound_deref(&self.heap, *var);
|
||||
}
|
||||
|
||||
let mut var_list = Vec::with_capacity(singleton_var_set.len());
|
||||
|
||||
for (var_name, addr) in term_write_result.var_dict {
|
||||
@@ -678,27 +745,50 @@ impl MachineState {
|
||||
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(&self.atom_tbl);
|
||||
return self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Stream::Byte(_) = stream {
|
||||
return self.read_term(
|
||||
match stream {
|
||||
#[cfg(feature = "http")]
|
||||
Stream::HttpRead(_) => self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
);
|
||||
}
|
||||
),
|
||||
#[cfg(feature = "tls")]
|
||||
Stream::NamedTls(_) => self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
),
|
||||
Stream::Readline(ptr) => {
|
||||
let readline = unsafe { ptr.as_ptr().as_mut() }.unwrap();
|
||||
readline.set_atoms_for_completion(&self.atom_tbl);
|
||||
self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
)
|
||||
}
|
||||
Stream::Byte(_)
|
||||
| Stream::InputChannel(_)
|
||||
| Stream::InputFile(_)
|
||||
| Stream::NamedTcp(_)
|
||||
| Stream::Null(_)
|
||||
| Stream::PipeReader(_)
|
||||
| Stream::StaticString(_) => self.read_term(
|
||||
stream,
|
||||
indices,
|
||||
MachineState::read_term_from_user_input_eof_handler,
|
||||
),
|
||||
_ => {
|
||||
let stub = functor_stub(atom!("read_term_from_user_input"), 3);
|
||||
let err = self.permission_error(
|
||||
Permission::InputStream,
|
||||
atom!("stream"),
|
||||
atom_as_cell!(atom!("user_input")),
|
||||
);
|
||||
|
||||
unreachable!("Stream must be a Stream::Readline(_)")
|
||||
Err(self.error_form(err, stub))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_term_eof_handler(&mut self, mut stream: Stream) -> Result<OnEOF, MachineStub> {
|
||||
@@ -743,7 +833,8 @@ impl MachineState {
|
||||
CompilationError::ParserError(e) if e.is_unexpected_eof() => {
|
||||
match eof_handler(self, stream)? {
|
||||
OnEOF::Return => {
|
||||
return self.write_read_term_options(vec![], vec![])
|
||||
return self
|
||||
.write_read_term_options(vec![], empty_list_as_cell!());
|
||||
}
|
||||
OnEOF::Continue => continue,
|
||||
}
|
||||
@@ -792,19 +883,16 @@ impl MachineState {
|
||||
}
|
||||
|
||||
read_heap_cell!(atom,
|
||||
(HeapCellValueTag::Char, c) => {
|
||||
var_names.insert(var, VarPtr::from(c.to_string()));
|
||||
}
|
||||
(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().to_owned()));
|
||||
}
|
||||
(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().to_owned()));
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
@@ -883,13 +971,15 @@ impl MachineState {
|
||||
}
|
||||
);
|
||||
|
||||
self.heap[0] = term_to_be_printed;
|
||||
|
||||
let mut printer = HCPrinter::new(
|
||||
&mut self.heap,
|
||||
Arc::clone(&self.atom_tbl),
|
||||
&mut self.stack,
|
||||
&self.arena,
|
||||
op_dir,
|
||||
PrinterOutputter::new(),
|
||||
term_to_be_printed,
|
||||
0,
|
||||
);
|
||||
|
||||
printer.ignore_ops = ignore_ops;
|
||||
@@ -897,7 +987,7 @@ impl MachineState {
|
||||
printer.quoted = quoted;
|
||||
printer.double_quotes = double_quotes;
|
||||
|
||||
match Number::try_from(max_depth) {
|
||||
match Number::try_from((max_depth, &self.arena.f64_tbl)) {
|
||||
Ok(Number::Fixnum(n)) => {
|
||||
if let Ok(n) = usize::try_from(n.get_num()) {
|
||||
printer.max_depth = n;
|
||||
@@ -922,7 +1012,6 @@ impl MachineState {
|
||||
}
|
||||
|
||||
printer.var_names = var_names;
|
||||
|
||||
printer
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -939,7 +1028,10 @@ impl MachineState {
|
||||
arity: HeapCellValue,
|
||||
) -> (Atom, usize) {
|
||||
let name = cell_as_atom!(self.store(self.deref(name)));
|
||||
let arity = cell_as_fixnum!(self.store(self.deref(arity)));
|
||||
let arity = unsafe {
|
||||
self.store(self.deref(arity))
|
||||
.to_fixnum_or_cut_point_unchecked()
|
||||
};
|
||||
|
||||
(name, usize::try_from(arity.get_num()).unwrap())
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,26 @@
|
||||
pub use crate::arena::*;
|
||||
pub use crate::atom_table::*;
|
||||
use crate::heap_print::*;
|
||||
pub use crate::machine::heap::*;
|
||||
pub use crate::machine::machine_state::*;
|
||||
pub use crate::machine::stack::*;
|
||||
pub use crate::machine::streams::*;
|
||||
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;
|
||||
use crate::read::TermWriteResult;
|
||||
|
||||
#[cfg(test)]
|
||||
use std::ops::{Deref, DerefMut, Index, IndexMut};
|
||||
use std::ops::{Deref, DerefMut, Index, IndexMut, Range};
|
||||
|
||||
// a mini-WAM for test purposes.
|
||||
|
||||
pub struct MockWAM {
|
||||
pub machine_st: MachineState,
|
||||
pub op_dir: OpDir,
|
||||
pub flags: MachineFlags,
|
||||
//pub flags: MachineFlags,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl MockWAM {
|
||||
pub fn new() -> Self {
|
||||
let op_dir = default_op_dir();
|
||||
@@ -33,7 +28,6 @@ impl MockWAM {
|
||||
Self {
|
||||
machine_st: MachineState::new(),
|
||||
op_dir,
|
||||
flags: MachineFlags::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,16 +51,15 @@ impl MockWAM {
|
||||
term_string: &'static str,
|
||||
) -> Result<String, CompilationError> {
|
||||
let term_write_result = self.parse_and_write_parsed_term_to_heap(term_string)?;
|
||||
|
||||
print_heap_terms(self.machine_st.heap.iter(), term_write_result.heap_loc);
|
||||
print_heap_terms(&self.machine_st.heap, term_write_result.heap_loc);
|
||||
|
||||
let mut printer = HCPrinter::new(
|
||||
&mut self.machine_st.heap,
|
||||
Arc::clone(&self.machine_st.atom_tbl),
|
||||
&mut self.machine_st.stack,
|
||||
&self.machine_st.arena,
|
||||
&self.op_dir,
|
||||
PrinterOutputter::new(),
|
||||
heap_loc_as_cell!(term_write_result.heap_loc),
|
||||
term_write_result.heap_loc,
|
||||
);
|
||||
|
||||
printer.var_names = term_write_result
|
||||
@@ -97,6 +90,7 @@ pub struct TermCopyingMockWAM<'a> {
|
||||
impl<'a> Index<usize> for TermCopyingMockWAM<'a> {
|
||||
type Output = HeapCellValue;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &HeapCellValue {
|
||||
&self.wam.machine_st.heap[index]
|
||||
}
|
||||
@@ -114,6 +108,7 @@ impl<'a> IndexMut<usize> for TermCopyingMockWAM<'a> {
|
||||
impl<'a> Deref for TermCopyingMockWAM<'a> {
|
||||
type Target = MockWAM;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.wam
|
||||
}
|
||||
@@ -121,6 +116,7 @@ impl<'a> Deref for TermCopyingMockWAM<'a> {
|
||||
|
||||
#[cfg(test)]
|
||||
impl<'a> DerefMut for TermCopyingMockWAM<'a> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.wam
|
||||
}
|
||||
@@ -155,10 +151,6 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, val: HeapCellValue) {
|
||||
self.wam.machine_st.heap.push(val);
|
||||
}
|
||||
|
||||
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
|
||||
self.wam
|
||||
.machine_st
|
||||
@@ -172,43 +164,66 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
|
||||
}
|
||||
|
||||
fn threshold(&self) -> usize {
|
||||
self.wam.machine_st.heap.len()
|
||||
self.wam.machine_st.heap.cell_len()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> {
|
||||
self.wam.machine_st.heap.copy_pstr_within(pstr_loc)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_slice_from<'b>(&'b self, from: usize) -> Box<dyn Iterator<Item = u8> + 'b> {
|
||||
Box::new(self.wam.machine_st.heap.as_slice()[from..].iter().cloned())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> {
|
||||
self.wam.machine_st.heap.reserve(num_cells)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> {
|
||||
self.wam.machine_st.heap.copy_slice_to_end(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn all_cells_marked_and_unforwarded(heap: &[HeapCellValue]) {
|
||||
for (idx, cell) in heap.iter().enumerate() {
|
||||
pub fn all_cells_marked_and_unforwarded(heap: &Heap, offset: usize) {
|
||||
for curr_idx in offset..heap.cell_len() {
|
||||
let cell = heap[curr_idx];
|
||||
|
||||
assert!(
|
||||
cell.get_mark_bit(),
|
||||
"cell {:?} at index {} is not marked",
|
||||
cell,
|
||||
idx
|
||||
"cell {cell:?} at index {curr_idx} is not marked"
|
||||
);
|
||||
assert!(
|
||||
!cell.get_forwarding_bit(),
|
||||
"cell {:?} at index {} is forwarded",
|
||||
cell,
|
||||
idx
|
||||
"cell {cell:?} at index {curr_idx} is forwarded"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn all_cells_unmarked(heap: &Heap) {
|
||||
for (idx, cell) in heap.iter().enumerate() {
|
||||
assert!(
|
||||
!cell.get_mark_bit(),
|
||||
"cell {:?} at index {} is still marked",
|
||||
cell,
|
||||
idx
|
||||
);
|
||||
pub fn unmark_all_cells(heap: &mut Heap, offset: usize) {
|
||||
for idx in offset..heap.cell_len() {
|
||||
heap[idx].set_mark_bit(false);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn all_cells_unmarked(iter: &impl SizedHeap) {
|
||||
let mut idx = 0;
|
||||
let cell_len = iter.cell_len();
|
||||
|
||||
while idx < cell_len {
|
||||
let curr_idx = idx;
|
||||
idx += 1;
|
||||
let cell = iter[curr_idx];
|
||||
|
||||
assert!(
|
||||
!cell.get_forwarding_bit(),
|
||||
"cell {:?} at index {} is still forwarded",
|
||||
cell,
|
||||
idx
|
||||
!cell.get_mark_bit(),
|
||||
"cell {cell:?} at index {curr_idx} is still marked"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -233,10 +248,8 @@ pub(crate) fn parse_and_write_parsed_term_to_heap(
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn with_test_streams() -> Self {
|
||||
Machine::new(MachineConfig::in_memory())
|
||||
}
|
||||
|
||||
/// For use in tests.
|
||||
#[allow(clippy::unbuffered_bytes)]
|
||||
pub fn test_load_file(&mut self, file: &str) -> Vec<u8> {
|
||||
let stream = Stream::from_owned_string(
|
||||
std::fs::read_to_string(AsRef::<std::path::Path>::as_ref(file)).unwrap(),
|
||||
@@ -247,6 +260,8 @@ impl Machine {
|
||||
self.user_output.bytes().map(|b| b.unwrap()).collect()
|
||||
}
|
||||
|
||||
/// For use in tests.
|
||||
#[allow(clippy::unbuffered_bytes)]
|
||||
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);
|
||||
|
||||
@@ -259,17 +274,18 @@ impl Machine {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::functor_macro::FunctorElement;
|
||||
|
||||
#[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));
|
||||
op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
op_dir.insert((atom!("="), Fixity::In), OpDesc::build_with(700, XFX));
|
||||
|
||||
{
|
||||
parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
|
||||
@@ -392,36 +408,66 @@ mod tests {
|
||||
|
||||
wam.heap.clear();
|
||||
|
||||
wam.heap.push(pstr_as_cell!(atom!("this is a string")));
|
||||
wam.heap.push(heap_loc_as_cell!(1));
|
||||
let mut writer = wam.heap.reserve(96).unwrap();
|
||||
|
||||
wam.heap.push(pstr_as_cell!(atom!("this is a string")));
|
||||
wam.heap.push(pstr_loc_as_cell!(4));
|
||||
writer.write_with(|section| {
|
||||
section.push_pstr("this is a string"); // 0
|
||||
|
||||
wam.heap.push(pstr_offset_as_cell!(0));
|
||||
wam.heap.push(fixnum_as_cell!(Fixnum::build_with(6)));
|
||||
let h = section.cell_len();
|
||||
assert_eq!(h, 3);
|
||||
|
||||
unify!(wam, pstr_loc_as_cell!(0), pstr_loc_as_cell!(2));
|
||||
section.push_cell(heap_loc_as_cell!(h)); // 3
|
||||
section.push_pstr("this is a string"); // 4
|
||||
|
||||
let h = section.cell_len();
|
||||
assert_eq!(h + 1, 8);
|
||||
|
||||
section.push_cell(pstr_loc_as_cell!(heap_index!(h + 1))); // 7
|
||||
section.push_pstr("this is a string"); // 8
|
||||
|
||||
section.push_cell(pstr_loc_as_cell!(heap_index!(h + 1)));
|
||||
});
|
||||
|
||||
unify!(wam, pstr_loc_as_cell!(0), pstr_loc_as_cell!(heap_index!(4)));
|
||||
|
||||
assert!(!wam.fail);
|
||||
|
||||
assert_eq!(wam.heap[1], pstr_loc_as_cell!(4));
|
||||
|
||||
all_cells_unmarked(&wam.heap);
|
||||
assert_eq!(
|
||||
wam.heap
|
||||
.slice_to_str(heap_index!(0), "this is a string".len()),
|
||||
"this is a string"
|
||||
);
|
||||
assert_eq!(wam.heap[3], pstr_loc_as_cell!(heap_index!(8)));
|
||||
assert_eq!(
|
||||
wam.heap
|
||||
.slice_to_str(heap_index!(4), "this is a string".len()),
|
||||
"this is a string"
|
||||
);
|
||||
assert_eq!(wam.heap[7], pstr_loc_as_cell!(heap_index!(8)));
|
||||
assert_eq!(
|
||||
wam.heap
|
||||
.slice_to_str(heap_index!(8), "this is a string".len()),
|
||||
"this is a string"
|
||||
);
|
||||
assert_eq!(wam.heap[11], pstr_loc_as_cell!(heap_index!(8)));
|
||||
|
||||
wam.heap.clear();
|
||||
|
||||
wam.heap.push(list_loc_as_cell!(1));
|
||||
wam.heap.push(atom_as_cell!(atom!("a")));
|
||||
wam.heap.push(list_loc_as_cell!(3));
|
||||
wam.heap.push(atom_as_cell!(atom!("b")));
|
||||
wam.heap.push(heap_loc_as_cell!(0));
|
||||
let mut writer = wam.heap.reserve(96).unwrap();
|
||||
|
||||
wam.heap.push(list_loc_as_cell!(6));
|
||||
wam.heap.push(atom_as_cell!(atom!("a")));
|
||||
wam.heap.push(list_loc_as_cell!(8));
|
||||
wam.heap.push(atom_as_cell!(atom!("b")));
|
||||
wam.heap.push(heap_loc_as_cell!(5));
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(list_loc_as_cell!(1));
|
||||
section.push_cell(atom_as_cell!(atom!("a")));
|
||||
section.push_cell(list_loc_as_cell!(3));
|
||||
section.push_cell(atom_as_cell!(atom!("b")));
|
||||
section.push_cell(heap_loc_as_cell!(0));
|
||||
|
||||
section.push_cell(list_loc_as_cell!(6));
|
||||
section.push_cell(atom_as_cell!(atom!("a")));
|
||||
section.push_cell(list_loc_as_cell!(8));
|
||||
section.push_cell(atom_as_cell!(atom!("b")));
|
||||
section.push_cell(heap_loc_as_cell!(5));
|
||||
});
|
||||
|
||||
unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5));
|
||||
|
||||
@@ -431,17 +477,21 @@ mod tests {
|
||||
|
||||
wam.heap.clear();
|
||||
|
||||
wam.heap.push(list_loc_as_cell!(1));
|
||||
wam.heap.push(atom_as_cell!(atom!("a")));
|
||||
wam.heap.push(list_loc_as_cell!(3));
|
||||
wam.heap.push(atom_as_cell!(atom!("b")));
|
||||
wam.heap.push(heap_loc_as_cell!(0));
|
||||
let mut writer = wam.heap.reserve(96).unwrap();
|
||||
|
||||
wam.heap.push(list_loc_as_cell!(6));
|
||||
wam.heap.push(atom_as_cell!(atom!("a")));
|
||||
wam.heap.push(list_loc_as_cell!(8));
|
||||
wam.heap.push(atom_as_cell!(atom!("c")));
|
||||
wam.heap.push(heap_loc_as_cell!(5));
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(list_loc_as_cell!(1));
|
||||
section.push_cell(atom_as_cell!(atom!("a")));
|
||||
section.push_cell(list_loc_as_cell!(3));
|
||||
section.push_cell(atom_as_cell!(atom!("b")));
|
||||
section.push_cell(heap_loc_as_cell!(0));
|
||||
|
||||
section.push_cell(list_loc_as_cell!(6));
|
||||
section.push_cell(atom_as_cell!(atom!("a")));
|
||||
section.push_cell(list_loc_as_cell!(8));
|
||||
section.push_cell(atom_as_cell!(atom!("c")));
|
||||
section.push_cell(heap_loc_as_cell!(5));
|
||||
});
|
||||
|
||||
unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5));
|
||||
|
||||
@@ -449,19 +499,24 @@ mod tests {
|
||||
|
||||
wam.fail = false;
|
||||
all_cells_unmarked(&wam.heap);
|
||||
|
||||
wam.heap.clear();
|
||||
|
||||
wam.heap.push(list_loc_as_cell!(1));
|
||||
wam.heap.push(atom_as_cell!(atom!("a")));
|
||||
wam.heap.push(list_loc_as_cell!(3));
|
||||
wam.heap.push(atom_as_cell!(atom!("b")));
|
||||
wam.heap.push(heap_loc_as_cell!(5));
|
||||
let mut writer = wam.heap.reserve(96).unwrap();
|
||||
|
||||
wam.heap.push(list_loc_as_cell!(6));
|
||||
wam.heap.push(atom_as_cell!(atom!("a")));
|
||||
wam.heap.push(list_loc_as_cell!(8));
|
||||
wam.heap.push(atom_as_cell!(atom!("b")));
|
||||
wam.heap.push(heap_loc_as_cell!(0));
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(list_loc_as_cell!(1));
|
||||
section.push_cell(atom_as_cell!(atom!("a")));
|
||||
section.push_cell(list_loc_as_cell!(3));
|
||||
section.push_cell(atom_as_cell!(atom!("b")));
|
||||
section.push_cell(heap_loc_as_cell!(5));
|
||||
|
||||
section.push_cell(list_loc_as_cell!(6));
|
||||
section.push_cell(atom_as_cell!(atom!("a")));
|
||||
section.push_cell(list_loc_as_cell!(8));
|
||||
section.push_cell(atom_as_cell!(atom!("b")));
|
||||
section.push_cell(heap_loc_as_cell!(0));
|
||||
});
|
||||
|
||||
unify!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(5));
|
||||
|
||||
@@ -473,7 +528,7 @@ mod tests {
|
||||
let term_write_result_1 =
|
||||
parse_and_write_parsed_term_to_heap(&mut wam, "X = g(X,y).", &op_dir).unwrap();
|
||||
|
||||
print_heap_terms(wam.heap.iter(), term_write_result_1.heap_loc);
|
||||
print_heap_terms(&wam.heap, term_write_result_1.heap_loc);
|
||||
|
||||
unify!(wam, heap_loc_as_cell!(2), str_loc_as_cell!(4));
|
||||
|
||||
@@ -482,15 +537,14 @@ 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));
|
||||
op_dir.insert((atom!("-"), Fixity::In), OpDesc::build_with(500, YFX));
|
||||
op_dir.insert((atom!("*"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
op_dir.insert((atom!("/"), Fixity::In), OpDesc::build_with(400, YFX));
|
||||
|
||||
{
|
||||
parse_and_write_parsed_term_to_heap(&mut wam, "f(X,X).", &op_dir).unwrap();
|
||||
@@ -516,8 +570,15 @@ mod tests {
|
||||
|
||||
let mut wam = MachineState::new();
|
||||
|
||||
wam.heap.push(heap_loc_as_cell!(0));
|
||||
wam.heap.push(heap_loc_as_cell!(1));
|
||||
// clear the heap of resource error data etc
|
||||
wam.heap.clear();
|
||||
|
||||
let mut writer = wam.heap.reserve(96).unwrap();
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(heap_loc_as_cell!(0));
|
||||
section.push_cell(heap_loc_as_cell!(1));
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, wam.heap[0], wam.heap[1]),
|
||||
@@ -539,12 +600,10 @@ mod tests {
|
||||
Some(Ordering::Equal)
|
||||
);
|
||||
|
||||
let cstr_cell = wam.heap.allocate_cstr("string").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
atom_as_cell!(atom!("atom")),
|
||||
atom_as_cstr_cell!(atom!("string"))
|
||||
),
|
||||
compare_term_test!(wam, atom_as_cell!(atom!("atom")), cstr_cell),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
|
||||
@@ -577,8 +636,12 @@ mod tests {
|
||||
|
||||
wam.heap.clear();
|
||||
|
||||
wam.heap.push(atom_as_cell!(atom!("f"), 1));
|
||||
wam.heap.push(heap_loc_as_cell!(1));
|
||||
let mut writer = wam.heap.reserve(96).unwrap();
|
||||
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(atom_as_cell!(atom!("f"), 1));
|
||||
section.push_cell(heap_loc_as_cell!(1));
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(0)),
|
||||
@@ -592,21 +655,25 @@ mod tests {
|
||||
|
||||
wam.heap.clear();
|
||||
|
||||
// [1,2,3]
|
||||
wam.heap.push(list_loc_as_cell!(1));
|
||||
wam.heap.push(fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
wam.heap.push(list_loc_as_cell!(3));
|
||||
wam.heap.push(fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
wam.heap.push(list_loc_as_cell!(5));
|
||||
wam.heap.push(fixnum_as_cell!(Fixnum::build_with(3)));
|
||||
wam.heap.push(empty_list_as_cell!());
|
||||
let mut writer = wam.heap.reserve(96).unwrap();
|
||||
|
||||
// [1,2]
|
||||
wam.heap.push(list_loc_as_cell!(8));
|
||||
wam.heap.push(fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
wam.heap.push(list_loc_as_cell!(10));
|
||||
wam.heap.push(fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
wam.heap.push(empty_list_as_cell!());
|
||||
writer.write_with(|section| {
|
||||
// [1,2,3]
|
||||
section.push_cell(list_loc_as_cell!(1));
|
||||
section.push_cell(fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
section.push_cell(list_loc_as_cell!(3));
|
||||
section.push_cell(fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
section.push_cell(list_loc_as_cell!(5));
|
||||
section.push_cell(fixnum_as_cell!(Fixnum::build_with(3)));
|
||||
section.push_cell(empty_list_as_cell!());
|
||||
|
||||
// [1,2]
|
||||
section.push_cell(list_loc_as_cell!(8));
|
||||
section.push_cell(fixnum_as_cell!(Fixnum::build_with(1)));
|
||||
section.push_cell(list_loc_as_cell!(10));
|
||||
section.push_cell(fixnum_as_cell!(Fixnum::build_with(2)));
|
||||
section.push_cell(empty_list_as_cell!());
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(wam, heap_loc_as_cell!(7), heap_loc_as_cell!(7)),
|
||||
@@ -632,12 +699,10 @@ mod tests {
|
||||
Some(Ordering::Greater)
|
||||
);
|
||||
|
||||
let cstr_cell = wam.heap.allocate_cstr("string").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
compare_term_test!(
|
||||
wam,
|
||||
empty_list_as_cell!(),
|
||||
atom_as_cstr_cell!(atom!("string"))
|
||||
),
|
||||
compare_term_test!(wam, empty_list_as_cell!(), cstr_cell),
|
||||
Some(Ordering::Less)
|
||||
);
|
||||
|
||||
@@ -668,55 +733,63 @@ mod tests {
|
||||
fn is_cyclic_term_tests() {
|
||||
let mut wam = MachineState::new();
|
||||
|
||||
assert!(!wam.is_cyclic_term(atom_as_cell!(atom!("f"))));
|
||||
assert!(!wam.is_cyclic_term(fixnum_as_cell!(Fixnum::build_with(555))));
|
||||
let mut writer = wam.heap.reserve(96).unwrap();
|
||||
|
||||
wam.heap.push(heap_loc_as_cell!(0));
|
||||
writer.write_with(|section| {
|
||||
section.push_cell(atom_as_cell!(atom!("f")));
|
||||
section.push_cell(fixnum_as_cell!(Fixnum::build_with(555)));
|
||||
section.push_cell(heap_loc_as_cell!(0));
|
||||
});
|
||||
|
||||
assert!(!wam.is_cyclic_term(heap_loc_as_cell!(0)));
|
||||
assert!(!wam.is_cyclic_term(0));
|
||||
assert!(!wam.is_cyclic_term(1));
|
||||
assert!(!wam.is_cyclic_term(2));
|
||||
|
||||
all_cells_unmarked(&wam.heap);
|
||||
wam.heap.clear();
|
||||
|
||||
wam.heap
|
||||
.extend(functor!(atom!("f"), [atom(atom!("a")), atom(atom!("b"))]));
|
||||
let mut functor_writer = Heap::functor_writer(functor!(
|
||||
atom!("f"),
|
||||
[atom_as_cell((atom!("a"))), atom_as_cell((atom!("b")))]
|
||||
));
|
||||
|
||||
assert!(!wam.is_cyclic_term(str_loc_as_cell!(0)));
|
||||
functor_writer(&mut wam.heap).unwrap();
|
||||
|
||||
let h = wam.heap.cell_len();
|
||||
wam.heap.push_cell(str_loc_as_cell!(0)).unwrap();
|
||||
|
||||
assert!(!wam.is_cyclic_term(h));
|
||||
|
||||
all_cells_unmarked(&wam.heap);
|
||||
|
||||
assert!(!wam.is_cyclic_term(heap_loc_as_cell!(1)));
|
||||
assert!(!wam.is_cyclic_term(1));
|
||||
|
||||
all_cells_unmarked(&wam.heap);
|
||||
|
||||
assert!(!wam.is_cyclic_term(heap_loc_as_cell!(2)));
|
||||
assert!(!wam.is_cyclic_term(2));
|
||||
|
||||
all_cells_unmarked(&wam.heap);
|
||||
|
||||
wam.heap[2] = str_loc_as_cell!(0);
|
||||
|
||||
print_heap_terms(wam.heap.iter(), 0);
|
||||
print_heap_terms(&wam.heap, 0);
|
||||
|
||||
assert!(wam.is_cyclic_term(str_loc_as_cell!(0)));
|
||||
assert!(wam.is_cyclic_term(2));
|
||||
|
||||
all_cells_unmarked(&wam.heap);
|
||||
|
||||
wam.heap[2] = atom_as_cell!(atom!("b"));
|
||||
wam.heap[1] = str_loc_as_cell!(0);
|
||||
|
||||
assert!(wam.is_cyclic_term(str_loc_as_cell!(0)));
|
||||
|
||||
all_cells_unmarked(&wam.heap);
|
||||
|
||||
assert!(wam.is_cyclic_term(heap_loc_as_cell!(1)));
|
||||
assert!(wam.is_cyclic_term(1));
|
||||
|
||||
all_cells_unmarked(&wam.heap);
|
||||
|
||||
wam.heap.clear();
|
||||
|
||||
wam.heap.push(pstr_as_cell!(atom!("a string")));
|
||||
wam.heap.push(empty_list_as_cell!());
|
||||
let h = wam.heap.cell_len();
|
||||
wam.heap.allocate_cstr("a string").unwrap();
|
||||
|
||||
assert!(!wam.is_cyclic_term(pstr_loc_as_cell!(0)));
|
||||
assert!(!wam.is_cyclic_term(h));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod args;
|
||||
#[macro_use]
|
||||
pub mod arithmetic_ops;
|
||||
pub mod attributed_variables;
|
||||
pub mod code_walker;
|
||||
@@ -19,7 +20,6 @@ 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 preprocessor;
|
||||
pub mod stack;
|
||||
@@ -45,6 +45,7 @@ use crate::machine::machine_indices::*;
|
||||
use crate::machine::machine_state::*;
|
||||
use crate::machine::stack::*;
|
||||
use crate::machine::streams::*;
|
||||
use crate::offset_table::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::dashu::{Integer, Rational};
|
||||
use crate::types::*;
|
||||
@@ -54,20 +55,21 @@ use lazy_static::lazy_static;
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::env;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use self::config::MachineConfig;
|
||||
use self::parsed_results::*;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
|
||||
}
|
||||
|
||||
/// An instance of Scryer Prolog.
|
||||
///
|
||||
/// Created with [`MachineBuilder::build`](crate::machine::config::MachineBuilder::build).
|
||||
#[derive(Debug)]
|
||||
pub struct Machine {
|
||||
pub(super) machine_st: MachineState,
|
||||
@@ -110,10 +112,33 @@ impl LoadContext {
|
||||
|
||||
#[inline]
|
||||
fn current_dir() -> PathBuf {
|
||||
env::current_dir().unwrap_or(PathBuf::from("./"))
|
||||
if !cfg!(miri) {
|
||||
env::current_dir().unwrap_or(PathBuf::from("./"))
|
||||
} else {
|
||||
PathBuf::from("./")
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
|
||||
mod libraries {
|
||||
use indexmap::IndexMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
static LIBRARIES: LazyLock<IndexMap<&'static str, &'static str>> = LazyLock::new(|| {
|
||||
let mut m = IndexMap::new();
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
|
||||
|
||||
m
|
||||
});
|
||||
|
||||
pub(crate) fn contains(name: &str) -> bool {
|
||||
LIBRARIES.contains_key(name)
|
||||
}
|
||||
|
||||
pub(crate) fn get(name: &str) -> Option<&'static str> {
|
||||
LIBRARIES.get(name).copied()
|
||||
}
|
||||
}
|
||||
|
||||
pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0;
|
||||
pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1;
|
||||
@@ -183,13 +208,8 @@ pub(crate) fn import_builtin_impls(code_dir: &CodeDir, builtins: &mut Module) {
|
||||
#[inline]
|
||||
pub(crate) fn get_structure_index(value: HeapCellValue) -> Option<CodeIndex> {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Cons, cons_ptr) => {
|
||||
match_untyped_arena_ptr!(cons_ptr,
|
||||
(ArenaHeaderTag::IndexPtr, ip) => {
|
||||
return Some(CodeIndex::from(ip));
|
||||
}
|
||||
_ => {}
|
||||
);
|
||||
(HeapCellValueTag::CodeIndexOffset, offset) => {
|
||||
return Some(CodeIndex::from(offset));
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
@@ -200,7 +220,7 @@ pub(crate) fn get_structure_index(value: HeapCellValue) -> Option<CodeIndex> {
|
||||
|
||||
impl Machine {
|
||||
#[inline]
|
||||
pub fn prelude_view_and_machine_st(&mut self) -> (MachinePreludeView, &mut MachineState) {
|
||||
fn prelude_view_and_machine_st(&mut self) -> (MachinePreludeView<'_>, &mut MachineState) {
|
||||
(
|
||||
MachinePreludeView {
|
||||
indices: &mut self.indices,
|
||||
@@ -211,6 +231,7 @@ impl Machine {
|
||||
)
|
||||
}
|
||||
|
||||
/// Gets the current inference count.
|
||||
pub fn get_inference_count(&mut self) -> u64 {
|
||||
self.machine_st
|
||||
.cwil
|
||||
@@ -220,22 +241,27 @@ impl Machine {
|
||||
.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);
|
||||
let err = self.machine_st.error_form(err, stub);
|
||||
|
||||
self.machine_st.throw_exception(err);
|
||||
}
|
||||
|
||||
pub fn run_module_predicate(
|
||||
/// Runs the predicate `key` in `module_name` until completion.
|
||||
/// Silently ignores failure, thrown errors and choice points.
|
||||
///
|
||||
/// Consider using [`Machine::run_query`] if you wish to handle
|
||||
/// predicates that may fail, leave a choice point or throw.
|
||||
pub(crate) 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(code_index) = module.code_dir.get(&key) {
|
||||
let p = code_index.local().unwrap();
|
||||
if let Some(code_idx) = module.code_dir.get(&key) {
|
||||
let index_ptr = self
|
||||
.machine_st
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(code_idx.into());
|
||||
let p = index_ptr.local().unwrap();
|
||||
|
||||
// Leave a halting choice point to backtrack to in case the predicate fails or throws.
|
||||
self.allocate_stub_choice_point();
|
||||
|
||||
self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC;
|
||||
self.machine_st.p = p;
|
||||
@@ -247,21 +273,24 @@ impl Machine {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
pub fn load_file(&mut self, path: &str, stream: Stream) {
|
||||
self.machine_st.registers[1] = stream_as_cell!(stream);
|
||||
fn load_file(&mut self, path: &str, stream: Stream) {
|
||||
self.machine_st.registers[1] = stream.into();
|
||||
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, program: &'static str) {
|
||||
fn load_top_level(&mut self, program: Cow<'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(program, &mut self.machine_st.arena);
|
||||
let toplevel_stream = match program {
|
||||
Cow::Borrowed(s) => Stream::from_static_string(s, &mut self.machine_st.arena),
|
||||
Cow::Owned(s) => Stream::from_owned_string(s, &mut self.machine_st.arena),
|
||||
};
|
||||
|
||||
self.load_file(path, toplevel_stream);
|
||||
|
||||
@@ -301,21 +330,17 @@ impl Machine {
|
||||
self.load_file(path_buf.to_str().unwrap(), stream);
|
||||
|
||||
if let Some(module) = self.indices.modules.get(&atom!("$atts")) {
|
||||
if let Some(code_index) = module.code_dir.get(&(atom!("driver"), 2)) {
|
||||
self.machine_st.attr_var_init.verify_attrs_loc = code_index.local().unwrap();
|
||||
if let Some(code_idx) = module.code_dir.get(&(atom!("driver"), 2)) {
|
||||
let index_ptr = self
|
||||
.machine_st
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(code_idx.into());
|
||||
self.machine_st.attr_var_init.verify_attrs_loc = index_ptr.local().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_user_input(&mut self, input: String) {
|
||||
self.user_input = Stream::from_owned_string(input, &mut self.machine_st.arena);
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -325,13 +350,16 @@ impl Machine {
|
||||
for arity in 1..66 {
|
||||
let key = (atom!("call"), arity);
|
||||
|
||||
match loader.code_dir.get(&key) {
|
||||
match loader.code_dir.get(&key).cloned() {
|
||||
Some(src_code_index) => {
|
||||
let target_code_index = target_code_dir
|
||||
.entry(key)
|
||||
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
|
||||
let code_index_tbl = &mut arena.code_index_tbl;
|
||||
|
||||
target_code_index.set(src_code_index.get());
|
||||
let target_code_index = target_code_dir.entry(key).or_insert_with(|| {
|
||||
CodeIndex::new(IndexPtr::undefined(), code_index_tbl)
|
||||
});
|
||||
|
||||
let src_code_ptr = code_index_tbl.get_entry(src_code_index.into());
|
||||
target_code_index.set(code_index_tbl, src_code_ptr);
|
||||
}
|
||||
None => {
|
||||
unreachable!();
|
||||
@@ -440,150 +468,37 @@ impl Machine {
|
||||
key,
|
||||
CodeIndex::new(
|
||||
IndexPtr::index(p + impls_offset),
|
||||
&mut self.machine_st.arena,
|
||||
&mut self.machine_st.arena.code_index_tbl,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[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, 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,
|
||||
indices: IndexStore::new(),
|
||||
code: vec![],
|
||||
user_input,
|
||||
user_output,
|
||||
user_error,
|
||||
load_contexts: vec![],
|
||||
#[cfg(feature = "ffi")]
|
||||
foreign_function_table: Default::default(),
|
||||
rng: StdRng::from_entropy(),
|
||||
};
|
||||
|
||||
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(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(config.toplevel);
|
||||
wam.configure_streams();
|
||||
|
||||
wam
|
||||
}
|
||||
|
||||
/// Ensures that [`Machine::indices`] properly reflects
|
||||
/// the streams stored in [`Machine::user_input`], [`Machine::user_output`]
|
||||
/// and [`Machine::user_error`].
|
||||
pub(crate) fn configure_streams(&mut self) {
|
||||
self.user_input
|
||||
.options_mut()
|
||||
.set_alias_to_atom_opt(Some(atom!("user_input")));
|
||||
self.indices
|
||||
.set_stream(atom!("user_input"), self.user_input);
|
||||
self.indices
|
||||
.set_stream(atom!("user_output"), self.user_output);
|
||||
self.indices
|
||||
.set_stream(atom!("user_error"), self.user_error);
|
||||
|
||||
let mut null_options = StreamOptions::default();
|
||||
null_options.set_alias_to_atom_opt(Some(atom!("null_stream")));
|
||||
|
||||
self.indices
|
||||
.stream_aliases
|
||||
.insert(atom!("user_input"), self.user_input);
|
||||
|
||||
self.indices.streams.insert(self.user_input);
|
||||
|
||||
self.user_output
|
||||
.options_mut()
|
||||
.set_alias_to_atom_opt(Some(atom!("user_output")));
|
||||
|
||||
self.indices
|
||||
.stream_aliases
|
||||
.insert(atom!("user_output"), self.user_output);
|
||||
|
||||
self.indices.streams.insert(self.user_output);
|
||||
|
||||
self.indices
|
||||
.stream_aliases
|
||||
.insert(atom!("user_error"), self.user_error);
|
||||
|
||||
self.indices.streams.insert(self.user_error);
|
||||
.set_stream(atom!("null_stream"), Stream::Null(null_options));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn run_verify_attr_interrupt(&mut self, arity: usize) {
|
||||
let p = self.machine_st.attr_var_init.verify_attrs_loc;
|
||||
self.machine_st.verify_attr_interrupt(p, arity);
|
||||
step_or_resource_error!(
|
||||
self.machine_st,
|
||||
self.machine_st.verify_attr_interrupt(p, arity)
|
||||
);
|
||||
}
|
||||
|
||||
fn next_clause_applicable(&mut self, mut offset: usize) -> bool {
|
||||
@@ -607,8 +522,8 @@ impl Machine {
|
||||
.select_switch_on_term_index(cell, v, c, l, s)
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(hm)) => {
|
||||
let lit = self.machine_st.constant_to_literal(cell);
|
||||
hm.get(&lit).cloned().unwrap_or(IndexingCodePtr::Fail)
|
||||
// let lit = self.machine_st.constant_to_literal(cell);
|
||||
hm.get(&cell).cloned().unwrap_or(IndexingCodePtr::Fail)
|
||||
}
|
||||
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(hm)) => {
|
||||
self.machine_st.select_switch_on_structure_index(cell, hm)
|
||||
@@ -634,34 +549,8 @@ impl Machine {
|
||||
|
||||
if cell.is_var() {
|
||||
offset += 1;
|
||||
} else if lit.get_tag() == HeapCellValueTag::CStr {
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::CStr) => {
|
||||
if cell == lit {
|
||||
offset += 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => {
|
||||
offset += 1;
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
if name == atom!(".") && arity == 2 {
|
||||
offset += 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
self.machine_st.write_literal_to_var(cell, lit);
|
||||
unify!(self.machine_st, cell, lit);
|
||||
|
||||
if self.machine_st.fail {
|
||||
self.machine_st.fail = false;
|
||||
@@ -675,7 +564,7 @@ impl Machine {
|
||||
let cell = self.deref_register(t);
|
||||
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc | HeapCellValueTag::CStr) => {
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => {// | HeapCellValueTag::CStr) => {
|
||||
offset += 1;
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
@@ -714,27 +603,24 @@ impl Machine {
|
||||
}
|
||||
);
|
||||
}
|
||||
&Instruction::GetPartialString(
|
||||
Level::Shallow,
|
||||
string,
|
||||
RegType::Temp(t),
|
||||
has_tail,
|
||||
) => {
|
||||
&Instruction::GetPartialString(Level::Shallow, ref string, RegType::Temp(t)) => {
|
||||
let cell = self.deref_register(t);
|
||||
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::CStr, cstr) => {
|
||||
if !has_tail && string != cstr {
|
||||
return false;
|
||||
}
|
||||
(HeapCellValueTag::PStrLoc, pstr_loc) => {
|
||||
let heap_slice = &self.machine_st.heap.as_slice()[pstr_loc ..];
|
||||
|
||||
offset += 1;
|
||||
match compare_pstr_slices(heap_slice, string.as_bytes()) {
|
||||
PStrSegmentCmpResult::Continue(..) => offset += 1,
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
(HeapCellValueTag::Lis | HeapCellValueTag::PStrLoc) => {
|
||||
(HeapCellValueTag::Lis) => {
|
||||
offset += 1;
|
||||
}
|
||||
(HeapCellValueTag::Str, s) => {
|
||||
let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s]).get_name_and_arity();
|
||||
let (name, arity) = cell_as_atom_cell!(self.machine_st.heap[s])
|
||||
.get_name_and_arity();
|
||||
|
||||
if name == atom!(".") && arity == 2 {
|
||||
offset += 1;
|
||||
@@ -857,7 +743,7 @@ impl Machine {
|
||||
or_frame.prelude.boip = 0;
|
||||
or_frame.prelude.biip = 0;
|
||||
or_frame.prelude.tr = self.machine_st.tr;
|
||||
or_frame.prelude.h = self.machine_st.heap.len();
|
||||
or_frame.prelude.h = self.machine_st.heap.cell_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();
|
||||
@@ -868,7 +754,7 @@ impl Machine {
|
||||
or_frame[i] = self.machine_st.registers[i + 1];
|
||||
}
|
||||
|
||||
self.machine_st.hb = self.machine_st.heap.len();
|
||||
self.machine_st.hb = self.machine_st.heap.cell_len();
|
||||
}
|
||||
|
||||
self.machine_st.p += 1;
|
||||
@@ -889,7 +775,7 @@ impl Machine {
|
||||
or_frame.prelude.boip = self.machine_st.oip;
|
||||
or_frame.prelude.biip = self.machine_st.iip + iip_offset; // 1
|
||||
or_frame.prelude.tr = self.machine_st.tr;
|
||||
or_frame.prelude.h = self.machine_st.heap.len();
|
||||
or_frame.prelude.h = self.machine_st.heap.cell_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();
|
||||
@@ -900,7 +786,7 @@ impl Machine {
|
||||
or_frame[i] = self.machine_st.registers[i + 1];
|
||||
}
|
||||
|
||||
self.machine_st.hb = self.machine_st.heap.len();
|
||||
self.machine_st.hb = self.machine_st.heap.cell_len();
|
||||
|
||||
// self.machine_st.oip = 0;
|
||||
// self.machine_st.iip = 0;
|
||||
@@ -1167,13 +1053,15 @@ impl Machine {
|
||||
|
||||
if module_name == atom!("user") {
|
||||
if let Some(idx) = self.indices.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_call(name, arity, idx.get())
|
||||
let index_ptr = self.machine_st.arena.code_index_tbl.get_entry(idx.into());
|
||||
self.try_call(name, arity, index_ptr)
|
||||
} 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())
|
||||
let index_ptr = self.machine_st.arena.code_index_tbl.get_entry(idx.into());
|
||||
self.try_call(name, arity, index_ptr)
|
||||
} else {
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
@@ -1196,14 +1084,24 @@ impl Machine {
|
||||
let (name, arity) = key;
|
||||
|
||||
if module_name == atom!("user") {
|
||||
if let Some(idx) = self.indices.code_dir.get(&(name, arity)).cloned() {
|
||||
self.try_execute(name, arity, idx.get())
|
||||
if let Some(offset) = self.indices.code_dir.get(&(name, arity)).cloned() {
|
||||
let index_ptr = self
|
||||
.machine_st
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(offset.into());
|
||||
self.try_execute(name, arity, index_ptr)
|
||||
} 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())
|
||||
if let Some(offset) = module.code_dir.get(&(name, arity)).cloned() {
|
||||
let index_ptr = self
|
||||
.machine_st
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(offset.into());
|
||||
self.try_execute(name, arity, index_ptr)
|
||||
} else {
|
||||
self.undefined_procedure(name, arity)
|
||||
}
|
||||
@@ -1235,41 +1133,47 @@ impl Machine {
|
||||
|
||||
#[inline(always)]
|
||||
fn run_cleaners(&mut self) -> bool {
|
||||
use std::sync::Once;
|
||||
static CLEANER_INIT: OnceLock<(usize, usize)> = OnceLock::new();
|
||||
|
||||
static CLEANER_INIT: Once = Once::new();
|
||||
let (r_c_w_h, r_c_wo_h) = *CLEANER_INIT.get_or_init(|| {
|
||||
let r_c_w_h_atom = atom!("run_cleaners_with_handling");
|
||||
let r_c_wo_h_atom = atom!("run_cleaners_without_handling");
|
||||
let iso_ext = atom!("iso_ext");
|
||||
|
||||
static mut RCWH: usize = 0;
|
||||
static mut RCWOH: usize = 0;
|
||||
|
||||
let (r_c_w_h, r_c_wo_h) = unsafe {
|
||||
CLEANER_INIT.call_once(|| {
|
||||
let r_c_w_h_atom = atom!("run_cleaners_with_handling");
|
||||
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, RCWOH)
|
||||
};
|
||||
let r_c_w_h = self
|
||||
.indices
|
||||
.get_predicate_code_index(r_c_w_h_atom, 0, iso_ext)
|
||||
.and_then(|code_idx| {
|
||||
self.machine_st
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(code_idx.into())
|
||||
.local()
|
||||
})
|
||||
.unwrap();
|
||||
let r_c_wo_h = self
|
||||
.indices
|
||||
.get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext)
|
||||
.and_then(|code_idx| {
|
||||
self.machine_st
|
||||
.arena
|
||||
.code_index_tbl
|
||||
.get_entry(code_idx.into())
|
||||
.local()
|
||||
})
|
||||
.unwrap();
|
||||
(r_c_w_h, r_c_wo_h)
|
||||
});
|
||||
|
||||
if let Some(&(_, b_cutoff, prev_block)) = self.machine_st.cont_pts.last() {
|
||||
if self.machine_st.b < b_cutoff {
|
||||
let (idx, arity) = if self.machine_st.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!(
|
||||
/* FIXME this is not safe */
|
||||
unsafe { Fixnum::build_with_unchecked(b_cutoff as i64) }
|
||||
);
|
||||
|
||||
(r_c_wo_h, 1)
|
||||
};
|
||||
@@ -1341,3 +1245,24 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::config::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn test_run_module_predicate_throw() {
|
||||
let mut machine = MachineBuilder::default()
|
||||
.with_toplevel(
|
||||
r#"
|
||||
:- module('$toplevel', []).
|
||||
repl :- throw(kaboom).
|
||||
"#,
|
||||
)
|
||||
.build();
|
||||
|
||||
machine.run_module_predicate(atom!("$toplevel"), (atom!("repl"), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,353 +0,0 @@
|
||||
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())
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,42 +11,73 @@ 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> {
|
||||
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)),
|
||||
atom!("yfx") => Ok(OpDecl::new(OpDesc::build_with(prec, YFX as u8), name)),
|
||||
atom!("fx") => Ok(OpDecl::new(OpDesc::build_with(prec, FX as u8), name)),
|
||||
atom!("fy") => Ok(OpDecl::new(OpDesc::build_with(prec, FY as u8), name)),
|
||||
atom!("xf") => Ok(OpDecl::new(OpDesc::build_with(prec, XF as u8), name)),
|
||||
atom!("yf") => Ok(OpDecl::new(OpDesc::build_with(prec, YF as u8), name)),
|
||||
_ => Err(CompilationError::InconsistentEntry),
|
||||
}
|
||||
pub(crate) fn to_op_decl(prec: u16, spec: OpDeclSpec, name: Atom) -> OpDecl {
|
||||
OpDecl::new(OpDesc::build_with(prec, spec), name)
|
||||
}
|
||||
|
||||
fn setup_op_decl(mut terms: Vec<Term>, atom_tbl: &AtomTable) -> Result<OpDecl, CompilationError> {
|
||||
pub(crate) fn to_op_decl_spec(spec: Atom) -> Result<OpDeclSpec, CompilationError> {
|
||||
OpDeclSpec::try_from(spec).map_err(|_err| {
|
||||
CompilationError::InvalidDirective(DirectiveError::InvalidOpDeclSpecValue(spec))
|
||||
})
|
||||
}
|
||||
|
||||
fn setup_op_decl(mut terms: Vec<Term>) -> Result<OpDecl, CompilationError> {
|
||||
// should allow non-partial lists?
|
||||
let name = match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Atom(name)) => name,
|
||||
Term::Literal(_, Literal::Char(c)) => AtomTable::build_with(atom_tbl, &c.to_string()),
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
other => {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidOpDeclNameType(other),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let spec = match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Atom(name)) => name,
|
||||
Term::Literal(_, Literal::Char(c)) => AtomTable::build_with(atom_tbl, &c.to_string()),
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
other => {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidOpDeclSpecDomain(other),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let spec = to_op_decl_spec(spec)?;
|
||||
|
||||
let prec = match terms.pop().unwrap() {
|
||||
Term::Literal(_, Literal::Fixnum(bi)) => match u16::try_from(bi.get_num()) {
|
||||
Ok(n) if n <= 1200 => n,
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
_ => {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidOpDeclPrecDomain(bi),
|
||||
));
|
||||
}
|
||||
},
|
||||
_ => return Err(CompilationError::InconsistentEntry),
|
||||
other => {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidOpDeclPrecType(other),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
to_op_decl(prec, spec, name)
|
||||
if name == "[]" || name == "{}" {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::ShallNotCreate(name),
|
||||
));
|
||||
}
|
||||
|
||||
if name == "," {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::ShallNotModify(name),
|
||||
));
|
||||
}
|
||||
|
||||
if name == "|" && (prec < 1001 || !spec.is_infix()) {
|
||||
return Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::ShallNotCreate(name),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(to_op_decl(prec, spec, name))
|
||||
}
|
||||
|
||||
fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, CompilationError> {
|
||||
@@ -80,16 +111,13 @@ fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, Compilatio
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_module_export(
|
||||
mut term: Term,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Result<ModuleExport, CompilationError> {
|
||||
fn setup_module_export(mut term: Term) -> Result<ModuleExport, CompilationError> {
|
||||
setup_predicate_indicator(&mut term)
|
||||
.map(ModuleExport::PredicateKey)
|
||||
.or_else(|_| {
|
||||
if let Term::Clause(_, name, terms) = term {
|
||||
if terms.len() == 3 && name == atom!("op") {
|
||||
Ok(ModuleExport::OpDecl(setup_op_decl(terms, atom_tbl)?))
|
||||
Ok(ModuleExport::OpDecl(setup_op_decl(terms)?))
|
||||
} else {
|
||||
Err(CompilationError::InvalidModuleDecl)
|
||||
}
|
||||
@@ -108,12 +136,11 @@ 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: &AtomTable,
|
||||
) -> Result<Vec<ModuleExport>, CompilationError> {
|
||||
let mut exports = vec![];
|
||||
|
||||
while let Term::Cons(_, t1, t2) = export_list {
|
||||
let module_export = setup_module_export(*t1, atom_tbl)?;
|
||||
let module_export = setup_module_export(*t1)?;
|
||||
|
||||
exports.push(module_export);
|
||||
export_list = *t2;
|
||||
@@ -126,10 +153,7 @@ pub(super) fn setup_module_export_list(
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_module_decl(
|
||||
mut terms: Vec<Term>,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Result<ModuleDecl, CompilationError> {
|
||||
fn setup_module_decl(mut terms: Vec<Term>) -> Result<ModuleDecl, CompilationError> {
|
||||
let export_list = terms.pop().unwrap();
|
||||
let name = terms.pop().unwrap();
|
||||
|
||||
@@ -139,8 +163,7 @@ fn setup_module_decl(
|
||||
}
|
||||
.ok_or(CompilationError::InvalidModuleDecl)?;
|
||||
|
||||
let exports = setup_module_export_list(export_list, atom_tbl)?;
|
||||
|
||||
let exports = setup_module_export_list(export_list)?;
|
||||
Ok(ModuleDecl { name, exports })
|
||||
}
|
||||
|
||||
@@ -159,10 +182,7 @@ fn setup_use_module_decl(mut terms: Vec<Term>) -> Result<ModuleSource, Compilati
|
||||
|
||||
type UseModuleExport = (ModuleSource, IndexSet<ModuleExport>);
|
||||
|
||||
fn setup_qualified_import(
|
||||
mut terms: Vec<Term>,
|
||||
atom_tbl: &AtomTable,
|
||||
) -> Result<UseModuleExport, CompilationError> {
|
||||
fn setup_qualified_import(mut terms: Vec<Term>) -> 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 => {
|
||||
@@ -178,7 +198,7 @@ fn setup_qualified_import(
|
||||
let mut exports = IndexSet::new();
|
||||
|
||||
while let Term::Cons(_, t1, t2) = export_list {
|
||||
exports.insert(setup_module_export(*t1, atom_tbl)?);
|
||||
exports.insert(setup_module_export(*t1)?);
|
||||
export_list = *t2;
|
||||
}
|
||||
|
||||
@@ -193,7 +213,7 @@ fn setup_qualified_import(
|
||||
* setup_meta_predicate tries to extract meta-predicate information
|
||||
* from an appropriately formed declaration
|
||||
*
|
||||
* :- meta_predicate maplist(:, ?, ?).
|
||||
* :- meta_predicate(maplist(:, ?, ?)).
|
||||
*
|
||||
* indicating that, for each QueryTerm call to maplist/3, the first
|
||||
* argument is to be expanded with the call resolution ((:)/2)
|
||||
@@ -220,13 +240,13 @@ fn setup_qualified_import(
|
||||
* contained in src/lib/ops_and_meta_predicates.pl, which is loaded before
|
||||
* src/lib/builtins.pl.
|
||||
*
|
||||
* Meta-specs have three forms:
|
||||
* Meta-specs have four forms:
|
||||
*
|
||||
* (:) (the argument should be expanded with (:)/2 as described above)
|
||||
* + (mode declarations under the mode syntax, which currently have no effect)
|
||||
* -
|
||||
* ?
|
||||
*/
|
||||
*/
|
||||
|
||||
fn setup_meta_predicate<'a, LS: LoadState<'a>>(
|
||||
mut terms: Vec<Term>,
|
||||
@@ -308,32 +328,28 @@ pub(super) fn setup_declaration<'a, LS: LoadState<'a>>(
|
||||
let (name, arity) = setup_predicate_indicator(&mut terms.pop().unwrap())?;
|
||||
Ok(Declaration::Dynamic(name, arity))
|
||||
}
|
||||
(atom!("module"), 2) => {
|
||||
let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
|
||||
Ok(Declaration::Module(setup_module_decl(terms, atom_tbl)?))
|
||||
}
|
||||
(atom!("op"), 3) => {
|
||||
let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
|
||||
Ok(Declaration::Op(setup_op_decl(terms, atom_tbl)?))
|
||||
}
|
||||
(atom!("module"), 2) => Ok(Declaration::Module(setup_module_decl(terms)?)),
|
||||
(atom!("op"), 3) => Ok(Declaration::Op(setup_op_decl(terms)?)),
|
||||
(atom!("non_counted_backtracking"), 1) => {
|
||||
let (name, arity) = setup_predicate_indicator(&mut terms.pop().unwrap())?;
|
||||
Ok(Declaration::NonCountedBacktracking(name, arity))
|
||||
}
|
||||
(atom!("use_module"), 1) => Ok(Declaration::UseModule(setup_use_module_decl(terms)?)),
|
||||
(atom!("use_module"), 2) => {
|
||||
let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
|
||||
let (name, exports) = setup_qualified_import(terms, atom_tbl)?;
|
||||
|
||||
let (name, exports) = setup_qualified_import(terms)?;
|
||||
Ok(Declaration::UseQualifiedModule(name, exports))
|
||||
}
|
||||
(atom!("meta_predicate"), 1) => {
|
||||
let (module_name, name, meta_specs) = setup_meta_predicate(terms, loader)?;
|
||||
Ok(Declaration::MetaPredicate(module_name, name, meta_specs))
|
||||
}
|
||||
_ => Err(CompilationError::InconsistentEntry),
|
||||
_ => Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::InvalidDirective(name, terms.len()),
|
||||
)),
|
||||
},
|
||||
_ => Err(CompilationError::InconsistentEntry),
|
||||
other => Err(CompilationError::InvalidDirective(
|
||||
DirectiveError::ExpectedDirective(other),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,7 +424,7 @@ 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() {
|
||||
if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = terms.last() {
|
||||
arg_terms
|
||||
.push(process_term(module_name, Term::Clause(cell, name, terms)));
|
||||
|
||||
@@ -417,7 +433,10 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>(
|
||||
|
||||
let idx = loader.get_or_insert_qualified_code_index(module_name, key);
|
||||
|
||||
terms.push(Term::Literal(Cell::default(), Literal::CodeIndex(idx)));
|
||||
terms.push(Term::Literal(
|
||||
Cell::default(),
|
||||
Literal::CodeIndexOffset(idx.into()),
|
||||
));
|
||||
process_term(module_name, Term::Clause(cell, name, terms))
|
||||
}
|
||||
Term::Literal(cell, Literal::Atom(name)) => {
|
||||
@@ -428,7 +447,10 @@ fn build_meta_predicate_clause<'a, LS: LoadState<'a>>(
|
||||
Term::Clause(
|
||||
cell,
|
||||
name,
|
||||
vec![Term::Literal(Cell::default(), Literal::CodeIndex(idx))],
|
||||
vec![Term::Literal(
|
||||
Cell::default(),
|
||||
Literal::CodeIndexOffset(idx.into()),
|
||||
)],
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -453,7 +475,7 @@ pub(super) fn clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
mut terms: Vec<Term>,
|
||||
call_policy: CallPolicy,
|
||||
) -> QueryTerm {
|
||||
if let Some(Term::Literal(_, Literal::CodeIndex(_))) = terms.last() {
|
||||
if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = terms.last() {
|
||||
// supplementary code vector indices are unnecessary for
|
||||
// root-level clauses.
|
||||
terms.pop();
|
||||
@@ -488,7 +510,7 @@ pub(super) fn qualified_clause_to_query_term<'a, LS: LoadState<'a>>(
|
||||
mut terms: Vec<Term>,
|
||||
call_policy: CallPolicy,
|
||||
) -> QueryTerm {
|
||||
if let Some(Term::Literal(_, Literal::CodeIndex(_))) = terms.last() {
|
||||
if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = terms.last() {
|
||||
// supplementary code vector indices are unnecessary for
|
||||
// root-level clauses.
|
||||
terms.pop();
|
||||
@@ -569,7 +591,7 @@ impl Preprocessor {
|
||||
&mut self,
|
||||
loader: &mut Loader<'a, LS>,
|
||||
term: Term,
|
||||
) -> Result<TopLevel, CompilationError> {
|
||||
) -> Result<PredicateClause, CompilationError> {
|
||||
match term {
|
||||
Term::Clause(r, name, mut terms) => {
|
||||
let is_rule = name == atom!(":-") && terms.len() == 2;
|
||||
@@ -579,16 +601,16 @@ impl Preprocessor {
|
||||
let head = terms.pop().unwrap();
|
||||
|
||||
let (rule, var_data) = self.setup_rule(loader, head, tail)?;
|
||||
Ok(TopLevel::Rule(rule, var_data))
|
||||
Ok(PredicateClause::Rule(rule, var_data))
|
||||
} else {
|
||||
let term = Term::Clause(r, name, terms);
|
||||
let (fact, var_data) = self.setup_fact(term)?;
|
||||
Ok(TopLevel::Fact(fact, var_data))
|
||||
Ok(PredicateClause::Fact(fact, var_data))
|
||||
}
|
||||
}
|
||||
term => {
|
||||
let (fact, var_data) = self.setup_fact(term)?;
|
||||
Ok(TopLevel::Fact(fact, var_data))
|
||||
Ok(PredicateClause::Fact(fact, var_data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ impl RawBlockTraits for Stack {
|
||||
|
||||
#[inline]
|
||||
fn align() -> usize {
|
||||
mem::align_of::<HeapCellValue>()
|
||||
mem::align_of::<OrFrame>()
|
||||
.max(mem::align_of::<AndFrame>())
|
||||
.max(mem::align_of::<HeapCellValue>())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +59,10 @@ impl Index<usize> for AndFrame {
|
||||
|
||||
unsafe {
|
||||
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)
|
||||
// This address falls outside the provenance for self, therefore we have to get it
|
||||
// from exposed provenance.
|
||||
&*std::ptr::with_exposed_provenance(ptr.addr() + prelude_offset + index_offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,10 +73,11 @@ impl IndexMut<usize> for AndFrame {
|
||||
let index_offset = (index - 1) * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = self as *mut crate::machine::stack::AndFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
let ptr = self as *mut crate::machine::stack::AndFrame as *mut u8;
|
||||
|
||||
&mut *(ptr as *mut HeapCellValue)
|
||||
// This address falls outside the provenance for self, therefore we have to get it
|
||||
// from exposed provenance.
|
||||
&mut *std::ptr::with_exposed_provenance_mut(ptr.addr() + prelude_offset + index_offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,20 +87,14 @@ impl Index<usize> for Stack {
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
unsafe {
|
||||
let ptr = self.buf.base as usize + index;
|
||||
&*(ptr as *const HeapCellValue)
|
||||
}
|
||||
unsafe { &*self.buf.base.add(index).cast() }
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Stack {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
unsafe {
|
||||
let ptr = self.buf.base as usize + index;
|
||||
&mut *(ptr as *mut HeapCellValue)
|
||||
}
|
||||
unsafe { &mut *self.buf.base.add(index).cast_mut().cast() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,9 +128,10 @@ impl Index<usize> for OrFrame {
|
||||
|
||||
unsafe {
|
||||
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)
|
||||
// This address falls outside the provenance for self, therefore we have to get it
|
||||
// from exposed provenance.
|
||||
&*std::ptr::with_exposed_provenance(ptr.addr() + prelude_offset + index_offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,10 +143,11 @@ impl IndexMut<usize> for OrFrame {
|
||||
let index_offset = index * mem::size_of::<HeapCellValue>();
|
||||
|
||||
unsafe {
|
||||
let ptr = self as *mut crate::machine::stack::OrFrame as *const u8;
|
||||
let ptr = ptr as usize + prelude_offset + index_offset;
|
||||
let ptr = self as *mut crate::machine::stack::OrFrame as *mut u8;
|
||||
|
||||
&mut *(ptr as *mut HeapCellValue)
|
||||
// This address falls outside the provenance for self, therefore we have to get it
|
||||
// from exposed provenance.
|
||||
&mut *std::ptr::with_exposed_provenance_mut(ptr.addr() + prelude_offset + index_offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +172,9 @@ impl Stack {
|
||||
let ptr = self.buf.alloc(frame_size);
|
||||
|
||||
if ptr.is_null() {
|
||||
self.buf.grow();
|
||||
if !self.buf.grow() {
|
||||
panic!("growing the stack failed")
|
||||
}
|
||||
} else {
|
||||
return ptr;
|
||||
}
|
||||
@@ -183,15 +185,19 @@ impl Stack {
|
||||
let frame_size = AndFrame::size_of(num_cells);
|
||||
|
||||
unsafe {
|
||||
let e = (*self.buf.ptr.get_mut()) as usize - self.buf.base as usize;
|
||||
let e = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr();
|
||||
let new_ptr = self.alloc(frame_size);
|
||||
let mut offset = prelude_size::<AndFramePrelude>();
|
||||
|
||||
for idx in 0..num_cells {
|
||||
ptr::write(
|
||||
new_ptr.add(offset) as *mut HeapCellValue,
|
||||
stack_loc_as_cell!(AndFrame, e, idx + 1),
|
||||
);
|
||||
let cell_ptr = new_ptr.add(offset) as *mut HeapCellValue;
|
||||
ptr::write(cell_ptr, stack_loc_as_cell!(AndFrame, e, idx + 1));
|
||||
|
||||
// Because in the Index and IndexMut inplementations we need to get this from
|
||||
// exposed provenance, we need to expose the provenance here, even though we don't
|
||||
// actually use the value for anything. This is a reminder that `expose_provenance`
|
||||
// isn't just a cast from a pointer to an integer but has actual side effects.
|
||||
cell_ptr.expose_provenance();
|
||||
|
||||
offset += mem::size_of::<HeapCellValue>();
|
||||
}
|
||||
@@ -204,22 +210,26 @@ impl Stack {
|
||||
}
|
||||
|
||||
pub(crate) fn top(&self) -> usize {
|
||||
unsafe { (*self.buf.ptr.get()) as usize - self.buf.base as usize }
|
||||
unsafe { (*self.buf.ptr.get()).addr() - self.buf.base.addr() }
|
||||
}
|
||||
|
||||
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.get_mut()) as usize - self.buf.base as usize;
|
||||
let b = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr();
|
||||
let new_ptr = self.alloc(frame_size);
|
||||
let mut offset = prelude_size::<OrFramePrelude>();
|
||||
|
||||
for idx in 0..num_cells {
|
||||
ptr::write(
|
||||
(new_ptr as usize + offset) as *mut HeapCellValue,
|
||||
stack_loc_as_cell!(OrFrame, b, idx),
|
||||
);
|
||||
let cell_ptr = new_ptr.byte_add(offset) as *mut HeapCellValue;
|
||||
ptr::write(cell_ptr, stack_loc_as_cell!(OrFrame, b, idx));
|
||||
|
||||
// Because in the Index and IndexMut inplementations we need to get this from
|
||||
// exposed provenance, we need to expose the provenance here, even though we don't
|
||||
// actually use the value for anything. This is a reminder that `expose_provenance`
|
||||
// isn't just a cast from a pointer to an integer but has actual side effects.
|
||||
cell_ptr.expose_provenance();
|
||||
|
||||
offset += mem::size_of::<HeapCellValue>();
|
||||
}
|
||||
@@ -233,10 +243,7 @@ impl Stack {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn index_and_frame(&self, e: usize) -> &AndFrame {
|
||||
unsafe {
|
||||
let ptr = self.buf.base as usize + e;
|
||||
&*(ptr as *const AndFrame)
|
||||
}
|
||||
unsafe { &*self.buf.base.add(e).cast() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -250,26 +257,20 @@ impl Stack {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn index_or_frame(&self, b: usize) -> &OrFrame {
|
||||
unsafe {
|
||||
let ptr = self.buf.base as usize + b;
|
||||
&*(ptr as *const OrFrame)
|
||||
}
|
||||
unsafe { &*self.buf.base.add(b).cast() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn index_or_frame_mut(&mut self, b: usize) -> &mut OrFrame {
|
||||
unsafe {
|
||||
let ptr = self.buf.base as usize + b;
|
||||
&mut *(ptr as *mut OrFrame)
|
||||
}
|
||||
unsafe { &mut *self.buf.base.add(b).cast_mut().cast() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn truncate(&mut self, b: usize) {
|
||||
let base = self.buf.base as usize + b;
|
||||
let base = unsafe { self.buf.base.add(b) };
|
||||
|
||||
if base < (*self.buf.ptr.get_mut()) as usize {
|
||||
*self.buf.ptr.get_mut() = base as *mut _;
|
||||
if base < (*self.buf.ptr.get_mut()) {
|
||||
*self.buf.ptr.get_mut() = base.cast_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -281,7 +282,6 @@ mod tests {
|
||||
use crate::machine::mock_wam::*;
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn stack_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
70
src/machine/streams/compat.rs
Normal file
70
src/machine/streams/compat.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
#[cfg(rust_version = "1.87.0")]
|
||||
pub use ge_1_87_0::{PipeReader, PipeWriter};
|
||||
|
||||
#[cfg(not(rust_version = "1.87.0"))]
|
||||
pub use lt_1_87_0::{PipeReader, PipeWriter};
|
||||
|
||||
#[cfg(not(rust_version = "1.87.0"))]
|
||||
pub(crate) use lt_1_87_0::PipeReaderInner;
|
||||
|
||||
#[cfg(not(rust_version = "1.87.0"))]
|
||||
mod lt_1_87_0 {
|
||||
use std::process::{ChildStderr, ChildStdout};
|
||||
|
||||
pub type PipeWriter = std::process::ChildStdin;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PipeReader(pub(crate) PipeReaderInner);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum PipeReaderInner {
|
||||
Stdout(ChildStdout),
|
||||
Stderr(ChildStderr),
|
||||
}
|
||||
|
||||
impl std::io::Read for PipeReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read(buf),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_vectored(
|
||||
&mut self,
|
||||
bufs: &mut [std::io::IoSliceMut<'_>],
|
||||
) -> std::io::Result<usize> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_vectored(bufs),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_vectored(bufs),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_to_end(buf),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_to_end(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_to_string(buf),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_to_string(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
|
||||
match &mut self.0 {
|
||||
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_exact(buf),
|
||||
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_exact(buf),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(rust_version = "1.87.0")]
|
||||
mod ge_1_87_0 {
|
||||
pub type PipeReader = std::io::PipeReader;
|
||||
pub type PipeWriter = std::io::PipeWriter;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user