Compare commits
3539 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e7d9f9010 | ||
|
|
d3edffb7fc | ||
|
|
295a642034 | ||
|
|
ae0999a3bb | ||
|
|
11fd39bbaf | ||
|
|
eb06f19c32 | ||
|
|
ea88ca552d | ||
|
|
eabbf67e86 | ||
|
|
7a636b0394 | ||
|
|
71ea74a077 | ||
|
|
091af28368 | ||
|
|
fbc5345c9a | ||
|
|
9d49415449 | ||
|
|
83218bf0da | ||
|
|
fcd6c3f127 | ||
|
|
efbddeaeee | ||
|
|
daaab1cb37 | ||
|
|
9b73536cdd | ||
|
|
e12d3f14c1 | ||
|
|
ffb77473b2 | ||
|
|
49c0cd3733 | ||
|
|
a236d00d7d | ||
|
|
bb5a59170a | ||
|
|
2b757de73a | ||
|
|
03f796e9ff | ||
|
|
d8be99b6ad | ||
|
|
2a04025419 | ||
|
|
bc14e3aedd | ||
|
|
bcb9dd0860 | ||
|
|
18dd939159 | ||
|
|
1cbc95bd06 | ||
|
|
d1e8ea4dd5 | ||
|
|
068959bd07 | ||
|
|
3a2d57db33 | ||
|
|
3ce16656ee | ||
|
|
732f3ae10a | ||
|
|
a4993284cd | ||
|
|
8aeb326e5c | ||
|
|
304cf424ed | ||
|
|
3cd40419eb | ||
|
|
f8f7b3b7e2 | ||
|
|
39c850b4f4 | ||
|
|
8b6d68a4cd | ||
|
|
149d3932be | ||
|
|
36cbeb3d6c | ||
|
|
875ae5a773 | ||
|
|
70d046499a | ||
|
|
b6db1cde25 | ||
|
|
257209df75 | ||
|
|
d5d514ec96 | ||
|
|
533594b6b6 | ||
|
|
43b6afcdf3 | ||
|
|
2c23f84808 | ||
|
|
ad13aa3801 | ||
|
|
79597b63bf | ||
|
|
bdba7ba0f5 | ||
|
|
6c7d5e8278 | ||
|
|
61ebcdb56e | ||
|
|
f2195c2362 | ||
|
|
6150ca90d3 | ||
|
|
938f588769 | ||
|
|
61d94dceae | ||
|
|
1411417a44 | ||
|
|
609514b4b8 | ||
|
|
1fd9246b16 | ||
|
|
19974e580b | ||
|
|
550efb8416 | ||
|
|
e0c491e9f3 | ||
|
|
f16850e5f8 | ||
|
|
ca0d4e9aac | ||
|
|
6e50268efb | ||
|
|
54b3034905 | ||
|
|
cf9e667b50 | ||
|
|
3f0b471227 | ||
|
|
bfca60aa86 | ||
|
|
6b9a291f4e | ||
|
|
777637001f | ||
|
|
9fcd7776a9 | ||
|
|
4f166f50a8 | ||
|
|
1547839c33 | ||
|
|
5a1423d0f0 | ||
|
|
3faa838ee2 | ||
|
|
e4792429d3 | ||
|
|
d50d425099 | ||
|
|
0df5f7e576 | ||
|
|
b9f1443eb0 | ||
|
|
ac18aed5a7 | ||
|
|
2c60e4183f | ||
|
|
2bbb6487ec | ||
|
|
095fa1227a | ||
|
|
f395d554a5 | ||
|
|
ad614b684c | ||
|
|
898b6b2b25 | ||
|
|
3c5818a040 | ||
|
|
0c313a0849 | ||
|
|
7c9075b4ee | ||
|
|
4c14cbe39a | ||
|
|
afe744aa57 | ||
|
|
89dde3f370 | ||
|
|
f8ddd78776 | ||
|
|
327a677a7c | ||
|
|
48a34aa2cc | ||
|
|
af54fb2aba | ||
|
|
b2dad66270 | ||
|
|
eac7ff680c | ||
|
|
b4db85c8c3 | ||
|
|
6765921666 | ||
|
|
ff293a56e6 | ||
|
|
ab2b652c84 | ||
|
|
83add732e0 | ||
|
|
f8c4679850 | ||
|
|
3da2b8fba2 | ||
|
|
f7dc1d72f6 | ||
|
|
8dffd72db5 | ||
|
|
703a6dddd1 | ||
|
|
5ea262e1d3 | ||
|
|
010b00f349 | ||
|
|
974722bc1c | ||
|
|
695389bda1 | ||
|
|
9cf0d8b484 | ||
|
|
381b4f2857 | ||
|
|
35aa4d9048 | ||
|
|
8e2dcd905b | ||
|
|
081f3c0827 | ||
|
|
8bbb484f94 | ||
|
|
717c3c2db8 | ||
|
|
0aa6abe517 | ||
|
|
4a9f690c16 | ||
|
|
e3d020ea74 | ||
|
|
82378c6338 | ||
|
|
f456c98c5b | ||
|
|
4db325e4b8 | ||
|
|
8f33eaf7d1 | ||
|
|
eba16a2213 | ||
|
|
87f97c7e5c | ||
|
|
2fec440ba6 | ||
|
|
3e2f3ecfdb | ||
|
|
2b6b11e6be | ||
|
|
7da2ba2015 | ||
|
|
bb9c1cf5a2 | ||
|
|
28f8da5ce9 | ||
|
|
c065b52778 | ||
|
|
6a10c6d6a5 | ||
|
|
0623e8e7df | ||
|
|
3c9c7ade8d | ||
|
|
13c85c74db | ||
|
|
5fc21241d6 | ||
|
|
833c3bd77f | ||
|
|
153f04b72a | ||
|
|
8aab887388 | ||
|
|
79a9b950cb | ||
|
|
fba2854f21 | ||
|
|
1c9fd1501a | ||
|
|
b0b89a0aca | ||
|
|
025da8315b | ||
|
|
8002b9a78b | ||
|
|
02f86587b5 | ||
|
|
4d144ea819 | ||
|
|
02c927bb83 | ||
|
|
4a8388a6e9 | ||
|
|
7bf014cdf5 | ||
|
|
b41563e976 | ||
|
|
ead2fccdbb | ||
|
|
817c584993 | ||
|
|
e00112b92f | ||
|
|
4bd0ff0fcb | ||
|
|
ed59aa05b0 | ||
|
|
16ed1040d2 | ||
|
|
fa21ff4813 | ||
|
|
891f7fc892 | ||
|
|
fae9ca1bb9 | ||
|
|
ac71cff338 | ||
|
|
8f39bee463 | ||
|
|
2dc904aa73 | ||
|
|
0ceab41b39 | ||
|
|
2a79d6fc40 | ||
|
|
49e49b425d | ||
|
|
7d0d61d789 | ||
|
|
5ef5a3f30a | ||
|
|
337d9c5583 | ||
|
|
26523139a8 | ||
|
|
65aadb3b88 | ||
|
|
dfad71bc6e | ||
|
|
d8c6fa1fe5 | ||
|
|
18b476af28 | ||
|
|
bd233fedfd | ||
|
|
083546442d | ||
|
|
3343188756 | ||
|
|
ed36aa7313 | ||
|
|
0dc94ecc0b | ||
|
|
453a88f03e | ||
|
|
c79fd74331 | ||
|
|
e446b13376 | ||
|
|
a83f412251 | ||
|
|
1a8c4f9b03 | ||
|
|
c2e1ded852 | ||
|
|
70220687f4 | ||
|
|
69a367d1c9 | ||
|
|
e2bdf59c81 | ||
|
|
6284aa3a3f | ||
|
|
29cd80510b | ||
|
|
9089f9ddb4 | ||
|
|
16dc10ee96 | ||
|
|
4e8f338870 | ||
|
|
b14e363d5e | ||
|
|
87f89545bb | ||
|
|
5064760b1e | ||
|
|
eca4262be6 | ||
|
|
73c26bed2a | ||
|
|
172b6a6f8f | ||
|
|
129cca53ed | ||
|
|
cd1ed106ed | ||
|
|
00aba96185 | ||
|
|
616f071cd9 | ||
|
|
580572aec6 | ||
|
|
47e908bf76 | ||
|
|
11901b5fde | ||
|
|
d63b0a192c | ||
|
|
2fc08dde1e | ||
|
|
193cfabc66 | ||
|
|
7c3ffd0e87 | ||
|
|
797a8f8611 | ||
|
|
f570e566f8 | ||
|
|
96a9dfcc1b | ||
|
|
6fdcd17f32 | ||
|
|
652e824e59 | ||
|
|
f595eaee2e | ||
|
|
9ddeeed683 | ||
|
|
b287f0aedb | ||
|
|
3b019fca1d | ||
|
|
4bd16b8a2e | ||
|
|
f2e044e5b8 | ||
|
|
1cd55d8131 | ||
|
|
14265118f1 | ||
|
|
b66824ffce | ||
|
|
6ec8c9064f | ||
|
|
d4f2f7ba2b | ||
|
|
a05dc79505 | ||
|
|
e90c813528 | ||
|
|
516848e214 | ||
|
|
6063783e9d | ||
|
|
e9e97fe82a | ||
|
|
5686b69013 | ||
|
|
248b05c992 | ||
|
|
a283c8bdc2 | ||
|
|
6b5ec3fab8 | ||
|
|
a0b4162d7a | ||
|
|
119761ad10 | ||
|
|
bf8651db29 | ||
|
|
11d0215963 | ||
|
|
16fd7a8d51 | ||
|
|
5357ecf8e3 | ||
|
|
e7c288f8f0 | ||
|
|
95abc4017e | ||
|
|
b91ce76052 | ||
|
|
617a551a56 | ||
|
|
31247545c3 | ||
|
|
15d112485c | ||
|
|
768f7ce9a7 | ||
|
|
e4d9692535 | ||
|
|
f050e75d81 | ||
|
|
3dcb42af3c | ||
|
|
fdcd92db08 | ||
|
|
c82f1ccde1 | ||
|
|
2b85a307ae | ||
|
|
d7d5dfe059 | ||
|
|
109796122e | ||
|
|
476c1718f9 | ||
|
|
f8b9944f5c | ||
|
|
6e7dbfc75e | ||
|
|
1add5a9a75 | ||
|
|
777e6de9ee | ||
|
|
2443af57d0 | ||
|
|
ed03d5b1b4 | ||
|
|
9376bc6369 | ||
|
|
2f43c3ff40 | ||
|
|
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 | ||
|
|
b70f121e46 | ||
|
|
641765858f | ||
|
|
ba362e2fe0 | ||
|
|
84d5ce0d1e | ||
|
|
419623d158 | ||
|
|
1dd0c599c6 | ||
|
|
c7934ca246 | ||
|
|
27852eafd7 | ||
|
|
d4bde5008d | ||
|
|
74a525a672 | ||
|
|
e9982dc447 | ||
|
|
7028baaf8f | ||
|
|
7837c76c74 | ||
|
|
e5ad70c093 | ||
|
|
7fce688769 | ||
|
|
90c874777c | ||
|
|
70795134af | ||
|
|
8f2e9c6b94 | ||
|
|
ce34ca8f1f | ||
|
|
7c632cf165 | ||
|
|
b3c5a8db80 | ||
|
|
1d2961e047 | ||
|
|
b43f0979d9 | ||
|
|
539a1aee2c | ||
|
|
8e323b9dfa | ||
|
|
53b7d9eec9 | ||
|
|
89d3451767 | ||
|
|
aa98a7e7d6 | ||
|
|
614850ab1d | ||
|
|
140a15f9cb | ||
|
|
ec4a8745e7 | ||
|
|
673329ddb7 | ||
|
|
6a421dd8b0 | ||
|
|
de6c460a51 | ||
|
|
75a94fd0b3 | ||
|
|
7573c64087 | ||
|
|
cbb422f69d | ||
|
|
06f198bc57 | ||
|
|
6586657658 | ||
|
|
a0e598b97e | ||
|
|
8a6ea29c45 | ||
|
|
e1b0ba466b | ||
|
|
f35d6287ab | ||
|
|
53028a9c2a | ||
|
|
33fc2ed10c | ||
|
|
bc02fb3754 | ||
|
|
48b2379fe5 | ||
|
|
7de693eb23 | ||
|
|
7bb9c00356 | ||
|
|
9aacfff35d | ||
|
|
2e728c7051 | ||
|
|
7e973a6da6 | ||
|
|
cb014095ad | ||
|
|
6fe8f64835 | ||
|
|
6111f72b24 | ||
|
|
43041971b7 | ||
|
|
3505cc3ba0 | ||
|
|
05ba5f4358 | ||
|
|
bc616ca7d8 | ||
|
|
99131131af | ||
|
|
44b945a5f3 | ||
|
|
3bff923331 | ||
|
|
a6ccf95076 | ||
|
|
665f319a0e | ||
|
|
fe31afce6b | ||
|
|
ac07d2dfc8 | ||
|
|
71165c6984 | ||
|
|
eab3bff78b | ||
|
|
81dba11ab1 | ||
|
|
388fa5baa9 | ||
|
|
fe3241c07c | ||
|
|
0559ddca2a | ||
|
|
98a046500f | ||
|
|
58cd0d1669 | ||
|
|
6421fe10f8 | ||
|
|
99c85459a7 | ||
|
|
958bf51648 | ||
|
|
e3aa85e2a2 | ||
|
|
29ced36a79 | ||
|
|
f9a5c2d341 | ||
|
|
dde03718e1 | ||
|
|
60d34bea70 | ||
|
|
6fb3b61441 | ||
|
|
11ca168175 | ||
|
|
f02c0eacd8 | ||
|
|
72a566d2f8 | ||
|
|
b56ae28c45 | ||
|
|
cd89d71e0c | ||
|
|
902cd5c3ea | ||
|
|
f9eadc8e6a | ||
|
|
7b8c8fdda7 | ||
|
|
1f3de74cbd | ||
|
|
770ead9c05 | ||
|
|
fe371ff1d1 | ||
|
|
2f783f0aef | ||
|
|
92b85d4ba6 | ||
|
|
e702fe5c68 | ||
|
|
ed92e1b4f4 | ||
|
|
f2b63d1689 | ||
|
|
d51defed06 | ||
|
|
851ea2c45b | ||
|
|
ec97ee5d41 | ||
|
|
78b83ca1e1 | ||
|
|
ea0130d114 | ||
|
|
e66e7c5034 | ||
|
|
ff010678c3 | ||
|
|
5abc72cc8b | ||
|
|
fac8866fb4 | ||
|
|
d58e91303b | ||
|
|
8bd0317e9c | ||
|
|
76b1167842 | ||
|
|
7537686d8f | ||
|
|
6fc340c4db | ||
|
|
38f25af47d | ||
|
|
91df533834 | ||
|
|
9a05ca0667 | ||
|
|
ccf581d86f | ||
|
|
1dc546ac36 | ||
|
|
372b4bca46 | ||
|
|
5c89029462 | ||
|
|
a475a8a899 | ||
|
|
47b5ae7984 | ||
|
|
f5d9a67f36 | ||
|
|
1f90dbec20 | ||
|
|
d713456e29 | ||
|
|
d9d90d1ae8 | ||
|
|
62b61107e0 | ||
|
|
1e0fa56786 | ||
|
|
97d5bf7e97 | ||
|
|
75302ab716 | ||
|
|
742de9e77c | ||
|
|
921046e886 | ||
|
|
781f4afe25 | ||
|
|
10158f62e0 | ||
|
|
42d6749501 | ||
|
|
2fa46e6b6c | ||
|
|
47ec5eb6c6 | ||
|
|
799035c692 | ||
|
|
6c07256dc5 | ||
|
|
20c6a327ea | ||
|
|
bcfdc812f9 | ||
|
|
373b08a91e | ||
|
|
a7154968d8 | ||
|
|
46e432c4d9 | ||
|
|
44a61fa72c | ||
|
|
299df50066 | ||
|
|
335330690f | ||
|
|
f30cee7655 | ||
|
|
b943afb7c7 | ||
|
|
30f3818c66 | ||
|
|
13624568b5 | ||
|
|
e4f64581ec | ||
|
|
d132227860 | ||
|
|
45abb4703d | ||
|
|
a66b93c96f | ||
|
|
e5ea95db8d | ||
|
|
8938331915 | ||
|
|
749767539a | ||
|
|
399b50b4d7 | ||
|
|
82f3731c97 | ||
|
|
658258c8c3 | ||
|
|
d14cce2374 | ||
|
|
f411fb1eb3 | ||
|
|
6a30ccc907 | ||
|
|
1e1426cab0 | ||
|
|
2e750671ca | ||
|
|
147e47c8eb | ||
|
|
7208916230 | ||
|
|
43556f6df0 | ||
|
|
c0c33392f1 | ||
|
|
77ce5a9586 | ||
|
|
8665722367 | ||
|
|
57b52fbe90 | ||
|
|
b052b62b16 | ||
|
|
4df7e94d56 | ||
|
|
fcf2e2db05 | ||
|
|
1c52de9ab1 | ||
|
|
9cd762d88a | ||
|
|
eddcdaabef | ||
|
|
2c76943d35 | ||
|
|
24e3e1794e | ||
|
|
99055b553a | ||
|
|
576f13df60 | ||
|
|
004201abda | ||
|
|
6899c6b63c | ||
|
|
7f5c56a2a8 | ||
|
|
92e07d2be1 | ||
|
|
aff1fdc6aa | ||
|
|
b0b1efcc49 | ||
|
|
eb575b9882 | ||
|
|
e667abb143 | ||
|
|
347ee0211b | ||
|
|
4f4feba6fc | ||
|
|
8cb4dfef62 | ||
|
|
de452bb2c2 | ||
|
|
e8d8b09e52 | ||
|
|
631674db29 | ||
|
|
bc3ae52c7a | ||
|
|
4c795018b4 | ||
|
|
651b7b2f59 | ||
|
|
5cb207d8b3 | ||
|
|
de5de0cfaf | ||
|
|
b8ef367824 | ||
|
|
f1458b772e | ||
|
|
4181c9fab2 | ||
|
|
1fbc7f9842 | ||
|
|
73ed08a412 | ||
|
|
7cac13aefa | ||
|
|
2e114df4c7 | ||
|
|
36ab590c4e | ||
|
|
20da3427c1 | ||
|
|
2811660fa7 | ||
|
|
ef8eb935d4 | ||
|
|
11832b524b | ||
|
|
503e45eae1 | ||
|
|
041ec06dbc | ||
|
|
3841b29db8 | ||
|
|
1c94624958 | ||
|
|
c6f3700de1 | ||
|
|
186bba9d75 | ||
|
|
a3e83d59be | ||
|
|
54166b91eb | ||
|
|
1e5bb2d3da | ||
|
|
8d9a759a7d | ||
|
|
f32b035287 | ||
|
|
a8e6930d19 | ||
|
|
7fa4cb7ee8 | ||
|
|
df5855dbda | ||
|
|
e7f5ba6ca1 | ||
|
|
bcb68fcc3d | ||
|
|
e5ee918063 | ||
|
|
4be320abc8 | ||
|
|
300f5f817f | ||
|
|
574a4d0758 | ||
|
|
a02bd46094 | ||
|
|
cab80d3fa2 | ||
|
|
98d8b00d7a | ||
|
|
40608dc8d4 | ||
|
|
3b7d4a7b36 | ||
|
|
f704fcb41d | ||
|
|
8570f119c0 | ||
|
|
1656d9d7ad | ||
|
|
50c64b8512 | ||
|
|
83b9c6184c | ||
|
|
0ad479498c | ||
|
|
2dd1f6e880 | ||
|
|
da0018efdf | ||
|
|
16f768197b | ||
|
|
d6fe5b5aad | ||
|
|
dfe4fbfdc5 | ||
|
|
9444e62df9 | ||
|
|
dddffb01a6 | ||
|
|
4520bedf8d | ||
|
|
126d7bba57 | ||
|
|
575245c62f | ||
|
|
59264c0aa5 | ||
|
|
ee1bd9e006 | ||
|
|
dfd9e43405 | ||
|
|
8f06ef965a | ||
|
|
ae7cf15c70 | ||
|
|
469d2bd104 | ||
|
|
682b2ada4c | ||
|
|
f4e5426e97 | ||
|
|
6bae789bd6 | ||
|
|
ff19db0084 | ||
|
|
f10d7c05f9 | ||
|
|
f80dff851b | ||
|
|
63cbeb9a3c | ||
|
|
773d3f81fd | ||
|
|
4ab3e23b1f | ||
|
|
d588b18c39 | ||
|
|
97b899f4a3 | ||
|
|
6a913bc4cc | ||
|
|
bf46c4b5c1 | ||
|
|
69a0725c30 | ||
|
|
0c9740fe52 | ||
|
|
43f0b6c28d | ||
|
|
2776beb842 | ||
|
|
d7fa6c0ade | ||
|
|
025412aac0 | ||
|
|
bfa7d3cf41 | ||
|
|
9990780b82 | ||
|
|
dff2e73842 | ||
|
|
e2000859b6 | ||
|
|
3a6aee72a3 | ||
|
|
3fb2e451a2 | ||
|
|
7875b96956 | ||
|
|
d96c9e00b7 | ||
|
|
379c252b89 | ||
|
|
307cb56ef5 | ||
|
|
62e6ca02f9 | ||
|
|
c6fcbe20e1 | ||
|
|
a1b71f0440 | ||
|
|
dc08c26d9f | ||
|
|
669023914a | ||
|
|
e4a677ceea | ||
|
|
8aadc99f1d | ||
|
|
1ea397a807 | ||
|
|
b348f54c33 | ||
|
|
721cf20cf7 | ||
|
|
6ed9a99832 | ||
|
|
1163d14ea1 | ||
|
|
b83631fb20 | ||
|
|
902b08e657 | ||
|
|
b5fdde08aa | ||
|
|
4962c3df11 | ||
|
|
8de3498e07 | ||
|
|
26fdf83a48 | ||
|
|
32af047925 | ||
|
|
282633c877 | ||
|
|
77de570aa4 | ||
|
|
c9df19ca30 | ||
|
|
7c1cd18a06 | ||
|
|
0ca2356be5 | ||
|
|
8329d222cb | ||
|
|
1c3df1cdd7 | ||
|
|
b149805b9e | ||
|
|
99348ec309 | ||
|
|
c6976c0f92 | ||
|
|
cacc7f3193 | ||
|
|
4b9cf0952e | ||
|
|
6d37684e9c | ||
|
|
743412de33 | ||
|
|
11c1ee4481 | ||
|
|
f34703a279 | ||
|
|
5cce8ddd7d | ||
|
|
ff63eacf2c | ||
|
|
8121dce2a4 | ||
|
|
4d910f6bfe | ||
|
|
51c00fce57 | ||
|
|
1ab14ea519 | ||
|
|
c5c7c1913a | ||
|
|
3fc969b38b | ||
|
|
7ed38d6c6c | ||
|
|
fa68fa211c | ||
|
|
69cf2c36bc | ||
|
|
640c637ca8 | ||
|
|
0ad4427f83 | ||
|
|
1bfdea7527 | ||
|
|
6c36d067d7 | ||
|
|
1e60eeef34 | ||
|
|
005570c90a | ||
|
|
c070fbec62 | ||
|
|
f9d44c93fd | ||
|
|
fab5ca9440 | ||
|
|
7b128a9f00 | ||
|
|
fd14869ddc | ||
|
|
07d7d3b13b | ||
|
|
7c10683e46 | ||
|
|
3ffea2d987 | ||
|
|
770665a7b8 | ||
|
|
93ff049e54 | ||
|
|
f3b848537a | ||
|
|
27b971cbfa | ||
|
|
a1ceeb697a | ||
|
|
4a8aa0acbd | ||
|
|
6fa00b5b55 | ||
|
|
c2218faf47 | ||
|
|
21c61b6e3a | ||
|
|
b065e1cd53 | ||
|
|
7d6ce119f5 | ||
|
|
62c23166fa | ||
|
|
25afc11168 | ||
|
|
9e713406d3 | ||
|
|
f33f641f11 | ||
|
|
ca2ddfeed0 | ||
|
|
11cd42379b | ||
|
|
40d3345cd5 | ||
|
|
035e214ef5 | ||
|
|
f630a8cc1a | ||
|
|
0bfb08e464 | ||
|
|
5f6ae3857b | ||
|
|
750544dd2a | ||
|
|
660860bccf | ||
|
|
841aaa78b4 | ||
|
|
93f46a4142 | ||
|
|
a86db1bde8 | ||
|
|
4163437d5a | ||
|
|
a6535c28ea | ||
|
|
2efe95f2fb | ||
|
|
56992570d8 | ||
|
|
39934208c3 | ||
|
|
193bb313fd | ||
|
|
e8334f9b67 | ||
|
|
280ff8b5d0 | ||
|
|
172bb9e96a | ||
|
|
c04f1dea48 | ||
|
|
9114c982e0 | ||
|
|
f644a76281 | ||
|
|
63bb993c02 | ||
|
|
c50291cec8 | ||
|
|
2fe79b5fc3 | ||
|
|
b4fab5a806 | ||
|
|
35d0042be1 | ||
|
|
75dcc3f276 | ||
|
|
9c43974747 | ||
|
|
4be5fc6d43 | ||
|
|
f03336b3a2 | ||
|
|
a5db117ef6 | ||
|
|
142e0c2c3a | ||
|
|
1c8cd85f6c | ||
|
|
c547f67c54 | ||
|
|
c26e9436b4 | ||
|
|
b3239abea1 | ||
|
|
81edd4592f | ||
|
|
7484433e2b | ||
|
|
cb79e83510 | ||
|
|
cac52c0537 | ||
|
|
f5c23fbb16 | ||
|
|
a64a765f32 | ||
|
|
eecfeb2d03 | ||
|
|
54a0313d72 | ||
|
|
7cf6e77f4d | ||
|
|
4968fa0024 | ||
|
|
5e55625733 | ||
|
|
65f64e428e | ||
|
|
d2f5291412 | ||
|
|
9dc1c339ef | ||
|
|
c86304b18e | ||
|
|
7c83a1fb8e | ||
|
|
f02728aab3 | ||
|
|
48283c4dbc | ||
|
|
6bdd7f3a3f | ||
|
|
6e0dd371a0 | ||
|
|
5b1df8c4b3 | ||
|
|
5fa68e253c | ||
|
|
3e14ef634f | ||
|
|
adb5fcf708 | ||
|
|
136463c92e | ||
|
|
ef56193c44 | ||
|
|
4077040d03 | ||
|
|
de440a8c92 | ||
|
|
c75f74f681 | ||
|
|
46c78ad338 | ||
|
|
2b7a8875c0 | ||
|
|
2ee131c52c | ||
|
|
f3901cd8b8 | ||
|
|
7f0536b51d | ||
|
|
af76659830 | ||
|
|
fb658c4918 | ||
|
|
85cc4a80d0 | ||
|
|
6fe85c5779 | ||
|
|
b38a56e7d3 | ||
|
|
cbf18e5c3a | ||
|
|
96bd0d267c | ||
|
|
ec67752db4 | ||
|
|
ad4c17fbb6 | ||
|
|
672979c515 | ||
|
|
efcc2b81cd | ||
|
|
640f29fe0f | ||
|
|
13a0085408 | ||
|
|
74440d503e | ||
|
|
f4769de5c4 | ||
|
|
b843b76b7a | ||
|
|
182afe3b7d | ||
|
|
920e0c6b56 | ||
|
|
b6ce6b7cdb | ||
|
|
a0b5a24853 | ||
|
|
fa11d6bdd4 | ||
|
|
a9aef2bc84 | ||
|
|
bf2b73706a | ||
|
|
83ebce86b6 | ||
|
|
1967518fa2 | ||
|
|
5928d64d7a | ||
|
|
c7ec5a13a5 | ||
|
|
01aeb7515d | ||
|
|
d54c3369b3 | ||
|
|
d24e6100a7 | ||
|
|
cd0e45b70e | ||
|
|
0f0018abe4 | ||
|
|
a95c5e26e6 | ||
|
|
70b6cc8e55 | ||
|
|
13cbff7eab | ||
|
|
86166dbf25 | ||
|
|
b2130c2a48 | ||
|
|
5585e83fd6 | ||
|
|
7b921fc767 | ||
|
|
cfd67c8337 | ||
|
|
dc498d4de8 | ||
|
|
2c67106f26 | ||
|
|
b105c33b79 | ||
|
|
2ccc238119 | ||
|
|
e693b7d33b | ||
|
|
aca0de06cd | ||
|
|
85f4bdbe0b | ||
|
|
1257ba165f | ||
|
|
1c33d2a2ed | ||
|
|
99bd3d3b1a | ||
|
|
d8aed0ac4f | ||
|
|
5ebd4bb2a4 | ||
|
|
c934e06171 | ||
|
|
8a0685a3e9 | ||
|
|
2b018be392 | ||
|
|
4d19c437e0 | ||
|
|
44c274b9e9 | ||
|
|
66f6399b8a | ||
|
|
53a1be78cc | ||
|
|
78b35dc57d | ||
|
|
c65d1204af | ||
|
|
3bfed50d81 | ||
|
|
4523bb5b81 | ||
|
|
75aec69de1 | ||
|
|
9b96735615 | ||
|
|
f74d74fe6e | ||
|
|
77394ba914 | ||
|
|
d13173942d | ||
|
|
afbadd9ea2 | ||
|
|
c25888288a | ||
|
|
4fd059be5f | ||
|
|
bfb3164a0d | ||
|
|
81712f4c82 | ||
|
|
7e97f16f41 | ||
|
|
98086de77e | ||
|
|
24456e9703 | ||
|
|
7f024f3b8d | ||
|
|
67c1b171c7 | ||
|
|
04fbb0c1ce | ||
|
|
327423ab84 | ||
|
|
20cfffdff5 | ||
|
|
d8e126044f | ||
|
|
61f975ea18 | ||
|
|
25d4950216 | ||
|
|
5aa1521819 | ||
|
|
dd030fa18b | ||
|
|
013df58fea | ||
|
|
745ddc2c87 | ||
|
|
f6d3b2f896 | ||
|
|
7da321ab4f | ||
|
|
92fdc7e380 | ||
|
|
2e811de0f5 | ||
|
|
f02cd0ad3c | ||
|
|
a36df33a67 | ||
|
|
4a6bf5fd5f | ||
|
|
26c0b4fc75 | ||
|
|
ce1c8aac4c | ||
|
|
72ceceb7ae | ||
|
|
cf63b588bc | ||
|
|
4e8f7f0a1b | ||
|
|
579816a04f | ||
|
|
cc04872933 | ||
|
|
fad363e64a | ||
|
|
094cf2ac5d | ||
|
|
cc82727d20 | ||
|
|
924750f826 | ||
|
|
ffbf630b5c | ||
|
|
8073a4ba87 | ||
|
|
48a4835819 | ||
|
|
3cf3c0ea99 | ||
|
|
ac61055d43 | ||
|
|
65eb93793c | ||
|
|
ec450fc567 | ||
|
|
2c05ebbded | ||
|
|
0ddda0a864 | ||
|
|
3ff02da314 | ||
|
|
df048a4f42 | ||
|
|
21c36880f1 | ||
|
|
02328d818c | ||
|
|
0f55ba7218 | ||
|
|
1c089a2bbb | ||
|
|
54a887cdc3 | ||
|
|
ca28c76e52 | ||
|
|
a70157003b | ||
|
|
6aa3c7d5d6 | ||
|
|
4ef8c5c47d | ||
|
|
31d17f135a | ||
|
|
03f7b01109 | ||
|
|
c2658dc6da | ||
|
|
30dac8ea41 | ||
|
|
0d28404aad | ||
|
|
2f99bb025c | ||
|
|
bff48e7c7f | ||
|
|
3b67ffa814 | ||
|
|
49b4e4cbcb | ||
|
|
287c308bc3 | ||
|
|
af44d91568 | ||
|
|
40b6890c54 | ||
|
|
7248425a76 | ||
|
|
1dcc1ca524 | ||
|
|
0e17d6acd7 | ||
|
|
c89217903a | ||
|
|
762e6d3ba4 | ||
|
|
e9ae80e250 | ||
|
|
fd7f24e265 | ||
|
|
24450a8827 | ||
|
|
57e8ed65b0 | ||
|
|
3f819e2dfd | ||
|
|
9a7862c322 | ||
|
|
92c77cdff5 | ||
|
|
554e956ef5 | ||
|
|
9fd6e18d59 | ||
|
|
d8a9475460 | ||
|
|
60d9d01a55 | ||
|
|
cb79552dd0 | ||
|
|
f310ff24a5 | ||
|
|
b1963864d2 | ||
|
|
9e85be11fe | ||
|
|
b4e7000eb2 | ||
|
|
0b833bd2f3 | ||
|
|
7c93450aa7 | ||
|
|
e529e7ba21 | ||
|
|
cae32d6a00 | ||
|
|
836f6c1d5b | ||
|
|
1697cd5c7f | ||
|
|
dcd7360b17 | ||
|
|
4fd247f881 | ||
|
|
85bc544fb9 | ||
|
|
14646074be | ||
|
|
1ba040c24d | ||
|
|
56f6772422 | ||
|
|
db43d461b9 | ||
|
|
42a50474da | ||
|
|
cf367024fd | ||
|
|
644559b7f7 | ||
|
|
bb95ed3ad0 | ||
|
|
3947390877 | ||
|
|
e7f1e32ee3 | ||
|
|
5f8cc3c64b | ||
|
|
67d198ac77 | ||
|
|
a154a34f87 | ||
|
|
86c90d77dd | ||
|
|
4e1a4dae6c | ||
|
|
cf345d8174 | ||
|
|
a18da368a1 | ||
|
|
5d3295c40c | ||
|
|
de10ccfdee | ||
|
|
65a8ce8e22 | ||
|
|
5a7da721cd | ||
|
|
b234ef7ea3 | ||
|
|
617c961f88 | ||
|
|
e95355e56e | ||
|
|
ff5e9a793b | ||
|
|
101d0548db | ||
|
|
b6a81c51ab | ||
|
|
ba2cd43144 | ||
|
|
bd720b49f3 | ||
|
|
9590d5200c | ||
|
|
12f890e4a2 | ||
|
|
a9cb826bf3 | ||
|
|
4163cb038d | ||
|
|
b051f39145 | ||
|
|
cfc49243c8 | ||
|
|
29430ec88b | ||
|
|
d520046a4f | ||
|
|
814ce2d672 | ||
|
|
4fd37335f5 | ||
|
|
1791bd8626 | ||
|
|
3f5dbc1680 | ||
|
|
44052cb373 | ||
|
|
8c33da11ce | ||
|
|
ab80c84714 | ||
|
|
c0dd94c8a3 | ||
|
|
f65675836c | ||
|
|
f324c9591d | ||
|
|
3347f830c7 | ||
|
|
568abef5b8 | ||
|
|
703efdb22d | ||
|
|
bb420e9347 | ||
|
|
2f45f0cfed | ||
|
|
112d398175 | ||
|
|
95b31146b5 | ||
|
|
55a1f8d3da | ||
|
|
fba7790637 | ||
|
|
918dfca409 | ||
|
|
d18f128a3c | ||
|
|
c8b9059289 | ||
|
|
b8a6882a27 | ||
|
|
fb8e3071f2 | ||
|
|
067b5998ee | ||
|
|
2e26f37f5e | ||
|
|
6525c1f543 | ||
|
|
4db0b385f3 | ||
|
|
b7f77d1747 | ||
|
|
811ff65209 | ||
|
|
fd70d8975b | ||
|
|
b746a8f9ab | ||
|
|
483e4568a2 | ||
|
|
9ff1b660f1 | ||
|
|
5ffdd2d91a | ||
|
|
5b81bee941 | ||
|
|
3b9b9e75c4 | ||
|
|
faef45fd68 | ||
|
|
b5b45dde9d | ||
|
|
5ab087bc1e | ||
|
|
7683367c0e | ||
|
|
d2db66b9f7 | ||
|
|
c58d8804a1 | ||
|
|
5d09449c95 | ||
|
|
076a75d138 | ||
|
|
7f69ac3f4a | ||
|
|
7f159a7ed2 | ||
|
|
42274ef3ae | ||
|
|
ab893be418 | ||
|
|
f5e7573bd6 | ||
|
|
9cdad087ef | ||
|
|
e36f96fd47 | ||
|
|
cb25a90250 | ||
|
|
38a9d23174 | ||
|
|
521118265a | ||
|
|
a09306c585 | ||
|
|
8140ff9154 | ||
|
|
3cbe78cb9b | ||
|
|
699afb2c00 | ||
|
|
d079a18459 | ||
|
|
b0566e4150 | ||
|
|
c38a26e6cd | ||
|
|
31030738a4 | ||
|
|
caf84a259e | ||
|
|
1620824d3a | ||
|
|
1d2f5053d6 | ||
|
|
42282c6e6e | ||
|
|
a3f8ddd24a | ||
|
|
bfe808a779 | ||
|
|
28065b0565 | ||
|
|
4953cfd10e | ||
|
|
db972de40c | ||
|
|
330e9ba4ef | ||
|
|
bb09de1805 | ||
|
|
a6a0cef9fc | ||
|
|
c36bd4dc07 | ||
|
|
83f352b95e | ||
|
|
c84a5c3282 | ||
|
|
58af615dd4 | ||
|
|
c4b13a2176 | ||
|
|
039fffb339 | ||
|
|
8613513b9c | ||
|
|
ceb276b249 | ||
|
|
16f281e3d1 | ||
|
|
b593fffc7d | ||
|
|
ce890799bc | ||
|
|
aa65287c3b | ||
|
|
a6522d6317 | ||
|
|
0b45d42912 | ||
|
|
d9829a3606 | ||
|
|
59766e2db4 | ||
|
|
e52a4fbfc0 | ||
|
|
c05afb4705 | ||
|
|
9f209dadd9 | ||
|
|
2ec45b7413 | ||
|
|
bf581879e6 | ||
|
|
9bc3757a9e | ||
|
|
18d0a74f23 | ||
|
|
7a188744da | ||
|
|
7f45ac3f7a | ||
|
|
612861e010 | ||
|
|
fcae0d9fcf | ||
|
|
f446939770 | ||
|
|
92853a6a12 | ||
|
|
4ad113a6f8 | ||
|
|
89ed1aa8de | ||
|
|
d7f5675727 | ||
|
|
4d982d22c1 | ||
|
|
5ed1802f0f | ||
|
|
2716381e7b | ||
|
|
c5c83d724a | ||
|
|
749dedf477 | ||
|
|
911c49c43f | ||
|
|
770a682d8b | ||
|
|
73ca37ecca | ||
|
|
e0f49e8f43 | ||
|
|
dae34b6009 | ||
|
|
5850125d97 | ||
|
|
97bd778745 | ||
|
|
43df2e2649 | ||
|
|
c2f2623471 | ||
|
|
8e4465315f | ||
|
|
86beb222ae | ||
|
|
5e124ccf44 | ||
|
|
47d4e6d2f9 | ||
|
|
33f65210ee | ||
|
|
9ea6cb4cab | ||
|
|
0e583d620a | ||
|
|
b205abe949 | ||
|
|
cb59c3003a | ||
|
|
942095baa7 | ||
|
|
097849385e | ||
|
|
c4783062ff | ||
|
|
063cf0c608 | ||
|
|
a66d666bed | ||
|
|
170818759d | ||
|
|
e41d1b319b | ||
|
|
b9c9de5222 | ||
|
|
d565f5901b | ||
|
|
46317c3a39 | ||
|
|
bcc5bff376 | ||
|
|
5244d71570 | ||
|
|
9fac289d9a | ||
|
|
de665c05d8 | ||
|
|
98b0ab3409 | ||
|
|
3c344b176b | ||
|
|
6093c2858d | ||
|
|
e06ab1ca1c | ||
|
|
5154314786 | ||
|
|
fa07306f09 | ||
|
|
07115ce4f5 | ||
|
|
b656700294 | ||
|
|
7bc7f0ad06 | ||
|
|
495df8846a | ||
|
|
05d48cdcc3 | ||
|
|
dc02be4944 | ||
|
|
462097d956 | ||
|
|
0e374c2e96 | ||
|
|
94a1313916 | ||
|
|
b54a4afad6 | ||
|
|
c71b8e05f0 | ||
|
|
021c01dfd0 | ||
|
|
30f222b837 | ||
|
|
f7d9237bb9 | ||
|
|
49addc7b04 | ||
|
|
2f9996f9ac | ||
|
|
c1218fc986 | ||
|
|
57ef706eb0 | ||
|
|
e4b19dc1dd | ||
|
|
4e60cc46a2 | ||
|
|
d8edf7bfff | ||
|
|
e951db662d | ||
|
|
c5a3ec3ba8 | ||
|
|
f12a90351a | ||
|
|
b162c40007 | ||
|
|
402100fd52 | ||
|
|
2a1b8f37ec | ||
|
|
7d2e59ab64 | ||
|
|
f35298a227 | ||
|
|
198e925430 | ||
|
|
8b7281fad0 | ||
|
|
502574dfc3 | ||
|
|
7a0f4e5787 | ||
|
|
f277f660f3 | ||
|
|
9c037d6028 | ||
|
|
396528189d | ||
|
|
4b882c465c | ||
|
|
8937cac47d | ||
|
|
5a3e2899dd | ||
|
|
3a5ed4723b | ||
|
|
dcf4c44173 | ||
|
|
5763a4b9df | ||
|
|
82200a21eb | ||
|
|
fc7d98d748 | ||
|
|
5dce7d9075 | ||
|
|
f08f539768 | ||
|
|
be45672e22 | ||
|
|
94efb9ffe3 | ||
|
|
fd1e902492 | ||
|
|
8d6e3d7d56 | ||
|
|
a197bb6815 | ||
|
|
9de8456c7f | ||
|
|
ebf8091dba | ||
|
|
47ce84b273 | ||
|
|
449e097f4a | ||
|
|
fe27605497 | ||
|
|
8792ee438c | ||
|
|
7279062d3c | ||
|
|
b87fe1e21f | ||
|
|
d6ac125425 | ||
|
|
b79d8732ea | ||
|
|
3df0806017 | ||
|
|
c7759aa737 | ||
|
|
8ab1155fc5 | ||
|
|
adc77985d7 | ||
|
|
d85fc7c9f8 | ||
|
|
41b083c962 | ||
|
|
4ee6a7bfb8 | ||
|
|
6e53d08d40 | ||
|
|
01285f12c3 | ||
|
|
cd586aab8c | ||
|
|
4da646252b | ||
|
|
cc8bb38abc | ||
|
|
04ba9bc11a | ||
|
|
9b35a316c9 | ||
|
|
3a522f3c98 | ||
|
|
4c44859132 | ||
|
|
ecd77f7512 | ||
|
|
56bd596af3 | ||
|
|
21acb9361a | ||
|
|
c3477d8476 | ||
|
|
cba09d4ea1 | ||
|
|
884b0ca10e | ||
|
|
c9ecfb11d9 | ||
|
|
9288dcabe9 | ||
|
|
73df96244d | ||
|
|
7396630627 | ||
|
|
0ac93751d0 | ||
|
|
400ca21213 | ||
|
|
3286e78cd2 | ||
|
|
24eb9ce483 | ||
|
|
3dc6ed79d2 | ||
|
|
f94294dbd9 | ||
|
|
04ba58067a | ||
|
|
92b262d599 | ||
|
|
7ffb40e0ad | ||
|
|
997161c740 | ||
|
|
84c95c59e9 | ||
|
|
669242a8ce | ||
|
|
2a04d5e799 | ||
|
|
9d52d2a653 | ||
|
|
95f6ebc000 | ||
|
|
6e9cd072c5 | ||
|
|
729f2b1cb9 | ||
|
|
1a01438064 | ||
|
|
34ec6d3167 | ||
|
|
c9295323f6 | ||
|
|
92d543b8a8 | ||
|
|
3f445c76be | ||
|
|
a6e416f13d | ||
|
|
601ff567e3 | ||
|
|
56783b8e4b | ||
|
|
326f18ea75 | ||
|
|
491472a8c5 | ||
|
|
359619e035 | ||
|
|
9454d670c7 | ||
|
|
2fcec4fff9 | ||
|
|
2c2a9fe01e | ||
|
|
165a55dac6 | ||
|
|
196e9c1e47 | ||
|
|
17450520ba | ||
|
|
97702f3071 | ||
|
|
e8408ca93f | ||
|
|
e0dcf88b68 | ||
|
|
cc38cf15a4 | ||
|
|
da4c0a359b | ||
|
|
ce56a7303e | ||
|
|
5667ec8699 | ||
|
|
36b3150225 | ||
|
|
28c338416f | ||
|
|
26a8fc1a37 | ||
|
|
f347baafa3 | ||
|
|
95278c221b | ||
|
|
5a28366158 | ||
|
|
ac0cea8a73 | ||
|
|
814b631543 | ||
|
|
0d8c7f8785 | ||
|
|
5bae8fcaf8 | ||
|
|
4110cfcfcb | ||
|
|
2bdba261ea | ||
|
|
58fb851717 | ||
|
|
1118b37c92 | ||
|
|
39606a2277 | ||
|
|
275d306b69 | ||
|
|
e948413c09 | ||
|
|
f8bb3148d0 | ||
|
|
e7cf70936a | ||
|
|
409287e8f8 | ||
|
|
87d6ef16f7 | ||
|
|
3c08eba559 | ||
|
|
d399b68d01 | ||
|
|
2e9ec653a8 | ||
|
|
7ca782b92d | ||
|
|
996496c3f5 | ||
|
|
90cf713186 | ||
|
|
ca4aaf44de | ||
|
|
b04d845ec0 | ||
|
|
a8ea2b0f97 | ||
|
|
a29227d0d4 | ||
|
|
7f8f137aa0 | ||
|
|
cc7e721611 | ||
|
|
058cbcf19a | ||
|
|
22b815dc5c | ||
|
|
c709853aa7 | ||
|
|
d755bb7e12 | ||
|
|
6d99912b7b | ||
|
|
82dd9e0596 | ||
|
|
6ba3d49034 | ||
|
|
64be8e0fba | ||
|
|
43a297b691 | ||
|
|
df266378c4 | ||
|
|
ec55765cb2 | ||
|
|
de2ff8118f | ||
|
|
c30c9dc98f | ||
|
|
1f872c3f0c | ||
|
|
b6204eb6f1 | ||
|
|
af9f0f81d8 | ||
|
|
1c08b56e05 | ||
|
|
6cb8020f62 | ||
|
|
d742d4cde9 | ||
|
|
b7d06540e6 | ||
|
|
909f2e1058 | ||
|
|
26d39c3617 | ||
|
|
371bc3b231 | ||
|
|
bdeabcdd89 | ||
|
|
7f177c3d03 | ||
|
|
4ed36a12e9 | ||
|
|
a7e93db363 | ||
|
|
789715f31a | ||
|
|
7cc900d437 | ||
|
|
ff0f6f4fc2 | ||
|
|
5698e8f21a | ||
|
|
ddcae2c906 | ||
|
|
468d096ccb | ||
|
|
bca79d12c0 | ||
|
|
03eba9594b | ||
|
|
5e0e3e2754 | ||
|
|
04fd835267 | ||
|
|
f072761150 | ||
|
|
46d1e3bee3 | ||
|
|
f8e6e0252d | ||
|
|
f9e3bdb6b0 | ||
|
|
a80aab48cd | ||
|
|
3a4aa2a541 | ||
|
|
84583da5b8 | ||
|
|
f213956ceb | ||
|
|
c5caa9d311 | ||
|
|
0c4d93f01f | ||
|
|
a4e8bfc1ba | ||
|
|
542b9e1976 | ||
|
|
73a1ee59fa | ||
|
|
2771109427 | ||
|
|
cc420bd31a | ||
|
|
2fe1d2ef53 | ||
|
|
bb624cc971 | ||
|
|
aa7b8e52f3 | ||
|
|
07f358c91b | ||
|
|
8ae0a1a4af | ||
|
|
7647ad14b8 | ||
|
|
21e4347b3e | ||
|
|
f2d041ea23 | ||
|
|
56c1c4e43c | ||
|
|
820011412b | ||
|
|
81afa6942f | ||
|
|
6cb8d7596a | ||
|
|
a2d46af5ac | ||
|
|
6d94b8ab75 | ||
|
|
0166c3bf0f | ||
|
|
6a995fb62b | ||
|
|
d804d8a92e | ||
|
|
cf63e8375d | ||
|
|
1eff758751 | ||
|
|
0a8fc70ba9 | ||
|
|
b4b72a3166 | ||
|
|
a5054c0064 | ||
|
|
705f421d53 | ||
|
|
4dc0114c52 | ||
|
|
5f2c77fa74 | ||
|
|
5f703afed1 | ||
|
|
c6aa2068e2 | ||
|
|
209f7a239a | ||
|
|
0d404ad374 | ||
|
|
d5db0c641c | ||
|
|
21d6220f3f | ||
|
|
e0464d5447 | ||
|
|
309e5b320e | ||
|
|
afa9703eb5 | ||
|
|
56e5da6680 | ||
|
|
a8810d73e4 | ||
|
|
4b31c30cab | ||
|
|
df4b56148f | ||
|
|
f22a48f576 | ||
|
|
d660e4244f | ||
|
|
d383e5eb8b | ||
|
|
e1dc114517 | ||
|
|
73780ebf63 | ||
|
|
a19f7a0b9f | ||
|
|
e07473d155 | ||
|
|
d429b263eb | ||
|
|
b2a0d0c1c7 | ||
|
|
8bafd7adb1 | ||
|
|
76d24fe4e3 | ||
|
|
3bdcc3aba9 | ||
|
|
cb25b27963 | ||
|
|
b3df81c143 | ||
|
|
68b3c480c9 | ||
|
|
9fd1bf5574 | ||
|
|
b491a06c6d | ||
|
|
d16312a314 | ||
|
|
d19a8d6b98 | ||
|
|
c90dd80ece | ||
|
|
eb9d865635 | ||
|
|
694c87cb61 | ||
|
|
c41aba6b90 | ||
|
|
28ea672e36 | ||
|
|
9366a48d6d | ||
|
|
c7e1f5d568 | ||
|
|
d91ee5b77c | ||
|
|
fd97b84916 | ||
|
|
23f59970cb | ||
|
|
8781e03863 | ||
|
|
f2940ddfcf | ||
|
|
140149f051 | ||
|
|
9055370326 | ||
|
|
1af62c8d11 | ||
|
|
ec5430826e | ||
|
|
b2cccab768 | ||
|
|
579aa8acd4 | ||
|
|
9c2cb144b5 | ||
|
|
3d04689660 | ||
|
|
a891cc4edf | ||
|
|
706ab2ae5b | ||
|
|
9c1de8e00b | ||
|
|
1109e05e06 | ||
|
|
ce313b8a6b | ||
|
|
7c96b91663 | ||
|
|
87e966e185 | ||
|
|
1e018681de | ||
|
|
5b324680ba | ||
|
|
abca5fc405 | ||
|
|
6a455d2866 | ||
|
|
70da818101 | ||
|
|
5ec837acb4 | ||
|
|
76e33d051c | ||
|
|
7c5318b784 | ||
|
|
47892bf24a | ||
|
|
8a9cd7779c | ||
|
|
d4c0277065 | ||
|
|
069e132c0e | ||
|
|
4e6c138099 | ||
|
|
0ab355eada | ||
|
|
44825826df | ||
|
|
612f09893c | ||
|
|
e9f507b868 | ||
|
|
78278c804f | ||
|
|
b51460a59a | ||
|
|
181be5be3f | ||
|
|
91d4e91f53 | ||
|
|
ad3ae7991b | ||
|
|
c7caf6b7a9 | ||
|
|
4422ffe39f | ||
|
|
1ff52f70aa | ||
|
|
fce45167a5 | ||
|
|
b8f384045c | ||
|
|
ea95a7900c | ||
|
|
edea1273c8 | ||
|
|
ec9c763211 | ||
|
|
5a08117e75 | ||
|
|
0aec980aa8 | ||
|
|
6b05ee5130 | ||
|
|
1ffbf63d20 | ||
|
|
b905d2758d | ||
|
|
311d985145 | ||
|
|
a9a06b5297 | ||
|
|
6b8e620495 | ||
|
|
577f85099d | ||
|
|
607c84a79b | ||
|
|
2eec6499ff | ||
|
|
cd1150c11d | ||
|
|
987bbdecf5 | ||
|
|
a68394b6f2 | ||
|
|
3d6fbabc86 | ||
|
|
1479b5d38c | ||
|
|
336311ecc8 | ||
|
|
e9bb35c895 | ||
|
|
ab4f93dcee | ||
|
|
140a199805 | ||
|
|
232b66cfc3 | ||
|
|
11ba7e47fd | ||
|
|
c36d4a9c9a | ||
|
|
4b5c22864e | ||
|
|
5e1faeb5d2 | ||
|
|
4b7c2ba6c8 | ||
|
|
b9285f8de1 | ||
|
|
084fc84590 | ||
|
|
7a9b71cc03 | ||
|
|
6b49653754 | ||
|
|
d5fa8cc211 | ||
|
|
10a11c293e | ||
|
|
e486862db8 | ||
|
|
1c8aae0839 | ||
|
|
6feb1f8b82 | ||
|
|
bef8eb538c | ||
|
|
4e3b066555 | ||
|
|
4d23542ef3 | ||
|
|
47a6b498e3 | ||
|
|
b6f77f4e6f | ||
|
|
b96cd06129 | ||
|
|
e8eb6765bd | ||
|
|
8df552e952 | ||
|
|
18c52e076f | ||
|
|
1810dabc14 | ||
|
|
609a3a229f | ||
|
|
dff56643f0 | ||
|
|
77f8d52271 | ||
|
|
1e9821ec0c | ||
|
|
66e047083a | ||
|
|
e6438e79d8 | ||
|
|
a5e72679bc | ||
|
|
15cc916333 | ||
|
|
2f3de51e55 | ||
|
|
cea1353fbb | ||
|
|
bfbafd4168 | ||
|
|
27a15a2464 | ||
|
|
c2a87f898e | ||
|
|
aaa75c3ffc | ||
|
|
2dfa108034 | ||
|
|
4895cb7b22 | ||
|
|
2d3eb8483f | ||
|
|
36ef988514 | ||
|
|
d58dd7cc91 | ||
|
|
7e06abebd5 | ||
|
|
8dd7f51ae3 | ||
|
|
6030fae685 | ||
|
|
0f502fff84 | ||
|
|
789f662ec8 | ||
|
|
72536037ca | ||
|
|
e4eefc92f4 | ||
|
|
23c4e935b4 | ||
|
|
68b4951bc9 | ||
|
|
ed89c43e48 | ||
|
|
16137ace0e | ||
|
|
b4ef7556da | ||
|
|
eea83d9474 | ||
|
|
9d5264c5a3 | ||
|
|
66075bf45b | ||
|
|
35b8f69f92 | ||
|
|
2d5fe21b05 | ||
|
|
b448681872 | ||
|
|
05c14d5780 | ||
|
|
bb9de52a53 | ||
|
|
7595ec16e5 | ||
|
|
d8cf0f320d | ||
|
|
d644a3996e | ||
|
|
525050c407 | ||
|
|
468088fd6b | ||
|
|
95c192a988 | ||
|
|
d6287fae06 | ||
|
|
9e98f75495 | ||
|
|
56681af1b6 | ||
|
|
8bf3d4ea71 | ||
|
|
00eab4d415 | ||
|
|
0876d46880 | ||
|
|
ed803d30cd | ||
|
|
b6504bf377 | ||
|
|
85b4f235ef | ||
|
|
064bfa5e0d | ||
|
|
4a6ffb6b9e | ||
|
|
d952b7ace8 | ||
|
|
3b6138eaaa | ||
|
|
f3167f6b5f | ||
|
|
8c99d748e2 | ||
|
|
0ab928063e | ||
|
|
c9a34bde3f | ||
|
|
7f85c682b8 | ||
|
|
181185bad6 | ||
|
|
01a9fd9e25 | ||
|
|
3be00045cf | ||
|
|
86176dce8d | ||
|
|
1fa8a0a969 | ||
|
|
d5fe2e88d1 | ||
|
|
9177dbce7f | ||
|
|
55a896367d | ||
|
|
f2e49c84cc | ||
|
|
afa0bea1b1 | ||
|
|
f7e31fceac | ||
|
|
c490d818fb | ||
|
|
32bdedde6d | ||
|
|
48960f1beb | ||
|
|
cf65cbe2f1 | ||
|
|
45c35eca3e | ||
|
|
bb2a8c9090 | ||
|
|
6798b22b49 | ||
|
|
3acbe2a418 | ||
|
|
c45cdd6ea0 | ||
|
|
f520c38b60 | ||
|
|
046a2175ab | ||
|
|
83def95383 | ||
|
|
012fefa37e | ||
|
|
4878e42e07 | ||
|
|
1a7992e524 | ||
|
|
94d7c6dc5f | ||
|
|
23bfb4da50 | ||
|
|
1f21a5fe62 | ||
|
|
2173a8e3cb | ||
|
|
067e2633af | ||
|
|
d916da9ba8 | ||
|
|
c2fc33afec | ||
|
|
3d4e400ed2 | ||
|
|
75d719d204 | ||
|
|
172e3e9f34 | ||
|
|
8e32bdd8db | ||
|
|
895b02b641 | ||
|
|
d06b5f7ab5 | ||
|
|
e88ec6736c | ||
|
|
922abf21e7 | ||
|
|
c55bfa2c6e | ||
|
|
5463c1d47f | ||
|
|
3513a9c7ba | ||
|
|
45db236ec7 | ||
|
|
5848ff5735 | ||
|
|
64c9ac4071 | ||
|
|
75fc922898 | ||
|
|
59e2c3fd5e | ||
|
|
5bd73741cf | ||
|
|
fcfa3beaa3 | ||
|
|
dfbdaa9dab | ||
|
|
5f1f07e5a1 | ||
|
|
eace0d9b37 | ||
|
|
d83b32a200 | ||
|
|
9cefb33388 | ||
|
|
bd5d6d0686 | ||
|
|
ecebaf8216 | ||
|
|
8762647b55 | ||
|
|
664c0ee77c | ||
|
|
7a9c3d351d | ||
|
|
69e52d1ed8 | ||
|
|
bd594e0c05 | ||
|
|
3bf42860e2 | ||
|
|
0532f51cc2 | ||
|
|
1808597258 | ||
|
|
2744989cf1 | ||
|
|
0d04abb5c7 | ||
|
|
495168e7bb | ||
|
|
475fbf9561 | ||
|
|
c6264cf098 | ||
|
|
bc9123affb | ||
|
|
7fd12972ad | ||
|
|
2d19243b3b | ||
|
|
8ad4f188f2 | ||
|
|
0c19c56909 | ||
|
|
de35baadf3 | ||
|
|
66c209f9e4 | ||
|
|
d7a3ed2d4a | ||
|
|
0c25ffc26e | ||
|
|
9f864574de | ||
|
|
a38f7c8524 | ||
|
|
bf85cd404c | ||
|
|
102adb3544 | ||
|
|
c2b360d03e | ||
|
|
1bec1b7002 | ||
|
|
1b4b4807a5 | ||
|
|
d4d135f2a9 | ||
|
|
96faad1c01 | ||
|
|
d55d5f15ae | ||
|
|
26e4560429 | ||
|
|
775bd3a08b | ||
|
|
d92d8bce89 | ||
|
|
1b91663244 | ||
|
|
8fb673e93e | ||
|
|
d1372d9b3b | ||
|
|
063f0da565 | ||
|
|
c893247107 | ||
|
|
a5adcfff4c | ||
|
|
d3583276b3 | ||
|
|
55d8de1b23 | ||
|
|
5b829636cb | ||
|
|
68ac92a616 | ||
|
|
db20cda27a | ||
|
|
60dd47c696 | ||
|
|
42da543980 | ||
|
|
e4ea547623 | ||
|
|
33a6c81a07 | ||
|
|
9ba503cd6b | ||
|
|
f3f3dccf8e | ||
|
|
2f0718e885 | ||
|
|
7b8001d060 | ||
|
|
3c022e4332 | ||
|
|
37e34c5209 | ||
|
|
4b71607215 | ||
|
|
e62875ac90 | ||
|
|
835345b89e | ||
|
|
4ddacc707d | ||
|
|
0d653a2ce6 | ||
|
|
3208260ef8 | ||
|
|
2e57789d10 | ||
|
|
b3006f6fd8 | ||
|
|
142ddcd57a | ||
|
|
a562793fce | ||
|
|
e6c4ecfc10 | ||
|
|
5ff579f793 | ||
|
|
8df346f377 | ||
|
|
af76068297 | ||
|
|
5dddf0a460 | ||
|
|
1854338ff4 | ||
|
|
11b96875e0 | ||
|
|
6462c524f4 | ||
|
|
b703303dd4 | ||
|
|
ab77b6b28e | ||
|
|
0da9d1c036 | ||
|
|
88436b8627 | ||
|
|
f6116510a1 | ||
|
|
5a132aaff4 | ||
|
|
59992c8af2 | ||
|
|
ff3ec78df9 | ||
|
|
b980ae1e8c | ||
|
|
97b9d488d4 | ||
|
|
3ebf8d5db9 | ||
|
|
89e33903b2 | ||
|
|
6a610ac57d | ||
|
|
413f797155 | ||
|
|
6c8dc49216 | ||
|
|
9ded1447ec | ||
|
|
516ed1fd5b | ||
|
|
1a4f8f992b | ||
|
|
23adfef281 | ||
|
|
20b6816562 | ||
|
|
c6400550e1 | ||
|
|
f26749ad79 | ||
|
|
177c98fa95 | ||
|
|
7baa187863 | ||
|
|
11d504e8f4 | ||
|
|
83378e4373 | ||
|
|
06abe2302b | ||
|
|
f3e5f7879d | ||
|
|
bc5125b719 | ||
|
|
c6d23f9a0b | ||
|
|
3b8afce7c7 | ||
|
|
39e28f1f2a | ||
|
|
08363b1d73 | ||
|
|
91aa0f200e | ||
|
|
d9e190096c | ||
|
|
4dca86d5cf | ||
|
|
d3b628bb24 | ||
|
|
cfbb05fb1b | ||
|
|
340d428c88 | ||
|
|
d22bbc54c7 | ||
|
|
c5cda99f9d | ||
|
|
67854e0720 | ||
|
|
cfe257495a | ||
|
|
5f4e701461 | ||
|
|
e3622e0860 | ||
|
|
f0e6b8ca47 | ||
|
|
ef0b239e70 | ||
|
|
590d80268e | ||
|
|
a5de329712 | ||
|
|
2889a4438b | ||
|
|
f5952088e3 | ||
|
|
bcacb49c05 | ||
|
|
976b8e426d | ||
|
|
90ecd34cfd | ||
|
|
35670c8654 | ||
|
|
07bc97fb31 | ||
|
|
d01806ee2d | ||
|
|
a54e42961c | ||
|
|
2e46aa33c0 | ||
|
|
d90cf6384c | ||
|
|
af3f676683 | ||
|
|
abf980d603 | ||
|
|
693b6d2547 | ||
|
|
06313cac64 | ||
|
|
cf1e315d72 | ||
|
|
955e1799c8 | ||
|
|
3db86f1e25 | ||
|
|
6f9b6a29c4 | ||
|
|
b056d40eba | ||
|
|
708c3bc3ce | ||
|
|
39efc12dc8 | ||
|
|
4211151fe6 | ||
|
|
3974b6e6cd | ||
|
|
b24df6e195 | ||
|
|
bcd33dc8e3 | ||
|
|
6c66c236fb | ||
|
|
520121b2b2 | ||
|
|
48c1d05151 | ||
|
|
8ba61a1da1 | ||
|
|
cd129e32a7 | ||
|
|
2b3e43f160 | ||
|
|
d7bf04d2c0 | ||
|
|
4cde8cd501 | ||
|
|
fb4e627e62 | ||
|
|
ef3a97cedd | ||
|
|
1a86ad5cec | ||
|
|
529c401eee | ||
|
|
a87236fea2 | ||
|
|
975c1ca62c | ||
|
|
d02b9d848c | ||
|
|
f340f9ac94 | ||
|
|
1b3f290037 | ||
|
|
b551ef315f | ||
|
|
10bb6ab3bb | ||
|
|
4af57b0dd3 | ||
|
|
bc613eeff9 | ||
|
|
7507e88406 | ||
|
|
073f281f1e | ||
|
|
3355b49724 | ||
|
|
0e2db4a23e | ||
|
|
addc817cca | ||
|
|
a0a86d0f62 | ||
|
|
b24e7cce38 | ||
|
|
ffd1b7069f | ||
|
|
0404c3bd94 | ||
|
|
d0b74a95f4 | ||
|
|
0288d5dc19 | ||
|
|
9d06229cba | ||
|
|
c60ada8421 | ||
|
|
74a1d5cf38 | ||
|
|
43532e5322 | ||
|
|
68cd1d6631 | ||
|
|
afc18cc390 | ||
|
|
57d15936cb | ||
|
|
14406dbf76 | ||
|
|
12c561cee0 | ||
|
|
f79b8a1ca5 | ||
|
|
637daa5bda | ||
|
|
2eae6b4be7 | ||
|
|
dc5e935ecd | ||
|
|
538085169a | ||
|
|
7e8a635e7e | ||
|
|
74e76b6f97 | ||
|
|
cb6309d370 | ||
|
|
8065889862 | ||
|
|
e7a8950d09 | ||
|
|
88e9dc2177 | ||
|
|
2c555c969e | ||
|
|
77c04c3a14 | ||
|
|
48cea6efdf | ||
|
|
89a90d9522 | ||
|
|
0026f3fdef | ||
|
|
ee054fd99c | ||
|
|
7abdade7c1 | ||
|
|
79a50de697 | ||
|
|
e47aef5615 | ||
|
|
b96ae1bac0 | ||
|
|
7dafcb0860 | ||
|
|
5a6a686f42 | ||
|
|
b576bb55ef | ||
|
|
fc8205d375 | ||
|
|
7eb0669de5 | ||
|
|
86161ccf2a | ||
|
|
5e55732cb0 | ||
|
|
a87f0481b8 | ||
|
|
c3d61361f8 | ||
|
|
a48934e31c | ||
|
|
ac75f67e2a | ||
|
|
45cfa6c8d8 | ||
|
|
25a06b0bea | ||
|
|
91af72e8b1 | ||
|
|
fd9b354c70 | ||
|
|
1647e67dd4 | ||
|
|
803e120a51 | ||
|
|
6ba374c62b | ||
|
|
ca28f24e42 | ||
|
|
0d4c38138a | ||
|
|
fe291e90f0 | ||
|
|
220e1e8d83 | ||
|
|
2c0fb3adb0 | ||
|
|
b851eefb03 | ||
|
|
eaad2d8b1f | ||
|
|
575d235296 | ||
|
|
faf74519dc | ||
|
|
66becaf91c | ||
|
|
6610ba67c4 | ||
|
|
e05dd5ebb5 | ||
|
|
cf77f29988 | ||
|
|
2be8ed886b | ||
|
|
21e5b0ab52 | ||
|
|
d72cb74ffa | ||
|
|
3c988d555f | ||
|
|
5981a65cc2 | ||
|
|
9900762747 | ||
|
|
0db2ca6659 | ||
|
|
8e68626f14 | ||
|
|
72a88d1f91 | ||
|
|
bd75e9c184 | ||
|
|
23df16ecf9 | ||
|
|
387732d2f9 | ||
|
|
75b01d5021 | ||
|
|
b7fa5db570 | ||
|
|
297f126f66 | ||
|
|
bd6400f17f | ||
|
|
21e55023a4 | ||
|
|
49e024bdd8 | ||
|
|
57877107d8 | ||
|
|
e9b4a99c8f | ||
|
|
7bc4876071 | ||
|
|
e7899ee15e | ||
|
|
7ea20d9ce9 | ||
|
|
3045f327d5 | ||
|
|
2d1f182c49 | ||
|
|
0677f51717 | ||
|
|
a9bfeb0e96 | ||
|
|
c44d27fb7f | ||
|
|
a0a01b8ba7 | ||
|
|
e32215e19a | ||
|
|
893fb0e3cc | ||
|
|
1d2a838717 | ||
|
|
8457a19792 | ||
|
|
0b2638b201 | ||
|
|
4dce57a1b6 | ||
|
|
658b835a70 | ||
|
|
52af955463 | ||
|
|
09e886db0b | ||
|
|
f668640e3d | ||
|
|
afcd44deaa | ||
|
|
8c8c21c63b | ||
|
|
55dabbe16a | ||
|
|
494bd7b79c | ||
|
|
320ee072e6 | ||
|
|
67ef5fe8e6 | ||
|
|
7c5c700bb8 | ||
|
|
dd51908439 | ||
|
|
6a0f638d40 | ||
|
|
2480c6633c | ||
|
|
495025dafb | ||
|
|
ca62e54652 | ||
|
|
44dd9ae9fb | ||
|
|
3f4d62952a | ||
|
|
391cda919f | ||
|
|
44fc61ed86 | ||
|
|
f70685d53c | ||
|
|
807abfef4f | ||
|
|
810d4c51f9 | ||
|
|
1a0684cd20 | ||
|
|
47d811c250 | ||
|
|
4d0998ef72 | ||
|
|
5d2b0377d8 | ||
|
|
f6d69b6051 | ||
|
|
490496f381 | ||
|
|
bc4f719931 | ||
|
|
ded4a75b9c | ||
|
|
9e75eb35a0 | ||
|
|
326fb99849 | ||
|
|
fe1ced9f58 | ||
|
|
00d0502cbd | ||
|
|
c30bd51b91 | ||
|
|
9391dd9d51 | ||
|
|
3cd112b60f | ||
|
|
dce0c43e26 | ||
|
|
80e228f236 | ||
|
|
8ee2545b05 | ||
|
|
9bcc56e337 | ||
|
|
3c5a94452a | ||
|
|
fce7f8aa68 | ||
|
|
9e13f18463 | ||
|
|
2905f6b465 | ||
|
|
9d3f3eb013 | ||
|
|
75b5afa759 | ||
|
|
78c2f19e72 | ||
|
|
9a66a626f7 | ||
|
|
f42b7f4efa | ||
|
|
bf9654e138 | ||
|
|
4decd1d784 | ||
|
|
3e7cd24814 | ||
|
|
3e0bece53a | ||
|
|
c1b89b3b06 | ||
|
|
f5eadb2957 | ||
|
|
ee393c66dd | ||
|
|
72a7765b58 | ||
|
|
176daeec03 | ||
|
|
066f740819 | ||
|
|
04121644a3 | ||
|
|
1e37a0d9e6 | ||
|
|
fc8d33a98a | ||
|
|
2089727c7f | ||
|
|
fbbf705d10 | ||
|
|
e08c302756 | ||
|
|
87ef3519d9 | ||
|
|
9002c33046 | ||
|
|
b9ad6f4fd2 | ||
|
|
57db17853e | ||
|
|
58555d598b | ||
|
|
0eeae24049 | ||
|
|
3def7c66ad | ||
|
|
eeac3bc436 | ||
|
|
bdebc7f32e | ||
|
|
e7d6811948 | ||
|
|
0e263dd753 | ||
|
|
56bbcb1cb3 | ||
|
|
f02648022a | ||
|
|
9bb1f4e6f8 | ||
|
|
d219f0bdc6 | ||
|
|
c8d0f6ff20 | ||
|
|
2813c75292 | ||
|
|
581e055359 | ||
|
|
88a2cfc5e1 | ||
|
|
fb29830521 | ||
|
|
6540fea4da | ||
|
|
4fd33b015e | ||
|
|
d2fcdb3b6c | ||
|
|
c2fe0876c2 | ||
|
|
39aebd7144 | ||
|
|
6a7d0b8c0a | ||
|
|
7b92bee596 | ||
|
|
f7eda362c7 | ||
|
|
0fb8459bc1 | ||
|
|
42d9b57733 | ||
|
|
ebe143d9c9 | ||
|
|
8acbdfbf1d | ||
|
|
6eb36226b1 | ||
|
|
21984f0bbe | ||
|
|
5eac945bc0 | ||
|
|
2736b4198a | ||
|
|
a4f2f7faa6 | ||
|
|
94923175a8 | ||
|
|
f1c629e7bf | ||
|
|
5d0c60bd24 | ||
|
|
59f08cd651 | ||
|
|
63db194140 | ||
|
|
38db8d4e1a | ||
|
|
c2e8cbb846 | ||
|
|
3c567b895f | ||
|
|
673c75fb72 | ||
|
|
7a576ee36e | ||
|
|
73c09e3365 | ||
|
|
07dd2b279e | ||
|
|
c7943d2521 | ||
|
|
9b483d381f | ||
|
|
4e25337779 | ||
|
|
1e5041b8b0 | ||
|
|
3f4bbe9b9e | ||
|
|
56eaf883dc | ||
|
|
d8c0af7ded | ||
|
|
8724a6b6e2 | ||
|
|
b968b2c438 | ||
|
|
533d1ea9ab | ||
|
|
c9e32c449a | ||
|
|
d9e42bfcba | ||
|
|
f552564fc1 | ||
|
|
b21a096516 | ||
|
|
d0b25de554 | ||
|
|
bba836dc31 | ||
|
|
14fc8e2efa | ||
|
|
d8bd4fbea6 | ||
|
|
7cc89da879 | ||
|
|
baed12f45e | ||
|
|
dfbf291725 | ||
|
|
cb07074246 | ||
|
|
0198fe90b6 | ||
|
|
061221b073 | ||
|
|
7e4cfede7d | ||
|
|
48225c3c0a | ||
|
|
02b89362b2 | ||
|
|
e0e812f95c | ||
|
|
2cbc7e4f9a | ||
|
|
7bec37bfd8 | ||
|
|
828550687c | ||
|
|
d3caba4073 | ||
|
|
7e802706fe | ||
|
|
c87e3eac08 | ||
|
|
5b1b12ec9e | ||
|
|
c0c6f13d44 | ||
|
|
20f192daa1 | ||
|
|
0e73b53803 | ||
|
|
e2923c378e | ||
|
|
216d4da85a | ||
|
|
407e775282 | ||
|
|
e71afa63b0 | ||
|
|
30fd99679a | ||
|
|
a9ef15cdfa | ||
|
|
b08442b46f | ||
|
|
f56e2fc48e | ||
|
|
5452b55e38 | ||
|
|
bec8d36961 | ||
|
|
8188e3d0cf | ||
|
|
10e92eec32 | ||
|
|
37f2336eee | ||
|
|
290cb1b517 | ||
|
|
7520fe7000 | ||
|
|
9f861dfe89 | ||
|
|
fffb87d013 | ||
|
|
0cb731c584 | ||
|
|
233faea200 | ||
|
|
1b9015a049 | ||
|
|
111de1462c | ||
|
|
7fb0b4a8df | ||
|
|
627c49c5db | ||
|
|
665b1ad58a | ||
|
|
4623e9d7fc | ||
|
|
a3f0290432 | ||
|
|
69b1798af7 | ||
|
|
10af206024 | ||
|
|
6c23d7aec8 | ||
|
|
7937ccee30 | ||
|
|
b98e8c34eb | ||
|
|
5c2059b4e8 | ||
|
|
f4be0cf4b3 | ||
|
|
5f7abda22d | ||
|
|
51424aed32 | ||
|
|
914fb09ed0 | ||
|
|
7ea7e5c951 | ||
|
|
c69807b416 | ||
|
|
08320119f1 | ||
|
|
49dcd9bb4a | ||
|
|
8f41603101 | ||
|
|
d437609365 | ||
|
|
e1c681fffe | ||
|
|
d4d47182b4 | ||
|
|
bc2d0191ff | ||
|
|
bd222ed2bf | ||
|
|
ceac824e2b | ||
|
|
93b835ae0d | ||
|
|
aa2d57cf37 | ||
|
|
f6d4821a68 | ||
|
|
fa025bcf39 | ||
|
|
842176a595 | ||
|
|
0fb74b56b3 | ||
|
|
87abcd6a52 | ||
|
|
0a71e40030 | ||
|
|
1b9db035ba | ||
|
|
34c3035c2b | ||
|
|
d92951ba5b | ||
|
|
c5749cbbb1 | ||
|
|
a2e5bcc137 | ||
|
|
761d707b69 | ||
|
|
d327a05e12 | ||
|
|
6477d21e24 | ||
|
|
d3612e956e | ||
|
|
5a3ee3a46e | ||
|
|
23cb743ce2 | ||
|
|
40e5a2dc35 | ||
|
|
73eb4079ef | ||
|
|
5b60c8aa7e | ||
|
|
8c5a688566 | ||
|
|
7ea9706c94 | ||
|
|
5976e2d873 | ||
|
|
d32d452583 | ||
|
|
b77bdabca6 | ||
|
|
e8971e0d8b | ||
|
|
498c4660d0 | ||
|
|
54c142fc0d | ||
|
|
6079402dc4 | ||
|
|
3d4a7f97e1 | ||
|
|
d6e04beb95 | ||
|
|
9a225e6244 | ||
|
|
a03f00628b | ||
|
|
360485d830 | ||
|
|
3f1cfd2995 | ||
|
|
08e2b601f1 | ||
|
|
101ed9a633 | ||
|
|
81ecd17b93 | ||
|
|
5a78f02dcb | ||
|
|
0a08464d4f | ||
|
|
a638d42d92 | ||
|
|
90256ea2f5 | ||
|
|
90aefd35f0 | ||
|
|
2f428b7261 | ||
|
|
f935060b2b | ||
|
|
a367812348 | ||
|
|
8e6a89b279 | ||
|
|
0747697d10 | ||
|
|
8c7494885f | ||
|
|
590a0b8077 | ||
|
|
064d261357 | ||
|
|
adb96710bf | ||
|
|
dd64268bc7 | ||
|
|
aecbd0eda3 | ||
|
|
2c1b7e3b14 | ||
|
|
3eb6357892 | ||
|
|
e0c98a5c79 | ||
|
|
dfb60ec2ed | ||
|
|
b85e260b92 | ||
|
|
4d72845c58 | ||
|
|
9067f76bd6 | ||
|
|
6c447da730 | ||
|
|
1a9f6f06df | ||
|
|
5f8bdc564b | ||
|
|
f6498f2a7b | ||
|
|
0ef5f7f9b1 | ||
|
|
a323a4dd8e | ||
|
|
d5581ffd05 | ||
|
|
77968ab33a | ||
|
|
265a5955d7 | ||
|
|
465ab2fa34 | ||
|
|
d60bfef924 | ||
|
|
5225587ff5 | ||
|
|
fec790e108 | ||
|
|
eb5ea28bcd | ||
|
|
a9fe2ab5c4 | ||
|
|
f2db0886dd | ||
|
|
f6926a9642 | ||
|
|
d3442bb08f | ||
|
|
a08f5c3016 | ||
|
|
eff892ccb8 | ||
|
|
b565eaaf5a | ||
|
|
3831a371a4 | ||
|
|
77ec8ebfc3 | ||
|
|
c272e4d1e8 | ||
|
|
d69b7f41f2 | ||
|
|
c8a47d839a | ||
|
|
b3db8913c6 | ||
|
|
e8f8f34a76 | ||
|
|
5bec2c87cb | ||
|
|
d4d283480f | ||
|
|
71a524662d | ||
|
|
172aef7e26 | ||
|
|
395b5faa2d | ||
|
|
f4a765c5ed | ||
|
|
f5ad845d57 | ||
|
|
2a70ca375c | ||
|
|
396c589743 | ||
|
|
7670b81633 | ||
|
|
f9b98f97b6 | ||
|
|
00bf39204d | ||
|
|
e9ba3ad223 | ||
|
|
6b6666be47 | ||
|
|
e2a413df78 | ||
|
|
b24f68eee7 | ||
|
|
72c1a0222c | ||
|
|
bd97083268 | ||
|
|
e7cf5d4f3d | ||
|
|
2429971bcd | ||
|
|
170a71bd02 | ||
|
|
14efbb1356 | ||
|
|
20cd0dd77a | ||
|
|
2db1fac1eb | ||
|
|
ce8490ed41 | ||
|
|
7e208984de | ||
|
|
4d29a3ae3c | ||
|
|
547c63b28e | ||
|
|
9004aae597 | ||
|
|
1bdfc5a1a4 | ||
|
|
195273f01d | ||
|
|
605aea2211 | ||
|
|
d1afcb941f | ||
|
|
cc7e21170f | ||
|
|
30602c0849 | ||
|
|
1dabe95899 | ||
|
|
4995c0ed94 | ||
|
|
df82dbe5f5 | ||
|
|
de9c74e1d9 | ||
|
|
d89eb9ff0e | ||
|
|
5041042925 | ||
|
|
0d983e63a1 | ||
|
|
3f7a60d84b | ||
|
|
1eb9fcf521 | ||
|
|
1571690bbb | ||
|
|
eb2133e648 | ||
|
|
9107b3ddbe | ||
|
|
64433bd8dd | ||
|
|
bc6159c538 | ||
|
|
8e5954f36f | ||
|
|
b53ef148a0 | ||
|
|
3f950490f9 | ||
|
|
49b1c1368e | ||
|
|
bdb5df104a | ||
|
|
700778e574 | ||
|
|
a104b35cea | ||
|
|
bbbf95705b | ||
|
|
927871d73d | ||
|
|
62419e975f | ||
|
|
3fc2c4223b | ||
|
|
84da884211 | ||
|
|
1bc8e9aebf | ||
|
|
a228e46a39 | ||
|
|
81913a5987 | ||
|
|
2d7f31a60d | ||
|
|
804858d736 | ||
|
|
222be9cf6c | ||
|
|
75a52f032b | ||
|
|
a9e0a51059 | ||
|
|
164b993064 | ||
|
|
62b103fa23 | ||
|
|
e17eb01f76 | ||
|
|
2fe55e4715 | ||
|
|
a8a82e45a0 | ||
|
|
fcba140ffe | ||
|
|
b96ff781bc | ||
|
|
94392f248c | ||
|
|
24e6c31c44 | ||
|
|
cab0e4395a | ||
|
|
ae66e299e6 | ||
|
|
88ad2ee103 | ||
|
|
48f3f4ca37 | ||
|
|
f3ab17a3c0 | ||
|
|
5749d43fb6 | ||
|
|
f5d808e68f | ||
|
|
88f1160e2b | ||
|
|
b7218d2279 | ||
|
|
e928a0aaea | ||
|
|
a2447ecaa3 | ||
|
|
09a089d5f1 | ||
|
|
137b0cd837 | ||
|
|
4ffdd9eb15 | ||
|
|
4e29099ed9 | ||
|
|
8900df6f13 | ||
|
|
814c034683 | ||
|
|
e1ec4bee75 | ||
|
|
6ed7767512 | ||
|
|
08b18af8a0 | ||
|
|
75908ab88f | ||
|
|
fcba33997f | ||
|
|
134be307d3 | ||
|
|
092f8e6482 | ||
|
|
cf5960afad | ||
|
|
e5204e55d3 | ||
|
|
61b14d1cd9 | ||
|
|
c2e3b47d29 | ||
|
|
a239007db0 | ||
|
|
c5e5eb8b86 | ||
|
|
a4d15bfb88 | ||
|
|
b33158b92e | ||
|
|
3b30c4d487 | ||
|
|
dfd7ac633a | ||
|
|
213f8a604e | ||
|
|
13499352fd | ||
|
|
70aa4934c5 | ||
|
|
1d538ee70c | ||
|
|
2ed2e6d62f | ||
|
|
aea0570d2b | ||
|
|
def33a77da | ||
|
|
29db677a9e | ||
|
|
d362ca3a6d | ||
|
|
3796792421 | ||
|
|
b8020db029 | ||
|
|
a90030ca2d | ||
|
|
3f8d3afe5c | ||
|
|
155004bdbb | ||
|
|
534c74b67c | ||
|
|
5428935ab1 | ||
|
|
fc14d089b1 | ||
|
|
4e543238e1 | ||
|
|
6579a542b8 | ||
|
|
27e3dcea6c | ||
|
|
01f73b11d8 | ||
|
|
dd07226ab4 | ||
|
|
67d856e4b7 | ||
|
|
6ad1e57123 | ||
|
|
27a52ef56a | ||
|
|
d290596c1d | ||
|
|
342b4a3b52 | ||
|
|
19652e6367 | ||
|
|
1c76c869ef | ||
|
|
706d842102 | ||
|
|
2889631b61 | ||
|
|
5b9f0f45a9 | ||
|
|
ecc059bf8b | ||
|
|
e1f4a50e65 | ||
|
|
ec2c5a9b2c | ||
|
|
4efbc20a4f | ||
|
|
657e4f12bb | ||
|
|
22ffb1f53f | ||
|
|
4854da4805 | ||
|
|
33a0df20c3 | ||
|
|
e4d1e4b7a0 | ||
|
|
dbc193ab2a | ||
|
|
7bde174b40 | ||
|
|
cf8014582b | ||
|
|
e2796dd351 | ||
|
|
0696a18e0b | ||
|
|
0cc9388af0 | ||
|
|
a73529969a | ||
|
|
c55de17ca8 | ||
|
|
41a751e7f5 | ||
|
|
b9a53e441e | ||
|
|
add62c2093 | ||
|
|
de01ac233c | ||
|
|
c9e926f451 | ||
|
|
8d405849be | ||
|
|
5fbdb1af9f | ||
|
|
83350f8866 | ||
|
|
b4ccd889af | ||
|
|
35a3f2dc91 | ||
|
|
ed7a4514de | ||
|
|
2e295e354a | ||
|
|
d71d9cc8ef | ||
|
|
108b62d839 | ||
|
|
46dfaa5b28 | ||
|
|
323e9c3eb3 | ||
|
|
d677d3d2ef | ||
|
|
c39b239d78 | ||
|
|
5f3ab823fd | ||
|
|
03d1da4bf2 | ||
|
|
78656d220b | ||
|
|
7e7aa7992a | ||
|
|
a53b4df8b6 | ||
|
|
3ba2b8ade6 | ||
|
|
79cb4cd6a5 | ||
|
|
d3ab4b5def | ||
|
|
cdeb07520f | ||
|
|
cc77ef680d | ||
|
|
e75ebd9b6e | ||
|
|
62c9b8390b | ||
|
|
0724c044d6 | ||
|
|
bed4afe74f | ||
|
|
d4263cc8b9 | ||
|
|
b4b11465a1 | ||
|
|
daaebc59cb | ||
|
|
79a74038ac | ||
|
|
314baabf1d | ||
|
|
a24fbb8f61 | ||
|
|
32eaab0783 | ||
|
|
e185b626bd | ||
|
|
74dc94f6bc | ||
|
|
099d9aaca6 | ||
|
|
5fa3b9f2fb | ||
|
|
ad8e2ad4f6 | ||
|
|
8203eff47b | ||
|
|
ad333047d9 | ||
|
|
6e5d2d6a36 | ||
|
|
4b610b6293 | ||
|
|
2bd998e82f | ||
|
|
c55cc3c472 | ||
|
|
674483a4c6 | ||
|
|
a16f84560d | ||
|
|
1b4500339e | ||
|
|
3143468751 | ||
|
|
f627b32355 | ||
|
|
10ba6fb773 | ||
|
|
2d3f1e51ec | ||
|
|
1c23336cff | ||
|
|
a622ffddfe | ||
|
|
79cf0c63c4 | ||
|
|
d57a592273 | ||
|
|
4f15802fbc | ||
|
|
357ae7a8aa | ||
|
|
ac00ae4daf | ||
|
|
249f034a38 | ||
|
|
32c612b747 | ||
|
|
386f7cf65d | ||
|
|
8dc07882b6 | ||
|
|
320802633f | ||
|
|
dcc4b91b35 | ||
|
|
e7c27ed999 | ||
|
|
928c9c9aad | ||
|
|
4a90f13dee | ||
|
|
61dddcee3f | ||
|
|
4c510001ce | ||
|
|
82dfa690d2 | ||
|
|
2768beec4c | ||
|
|
bd0aa16322 | ||
|
|
a1746a5bf1 | ||
|
|
fb502fb66b | ||
|
|
c9a69263b6 | ||
|
|
f0c8056334 | ||
|
|
17dd9239b6 | ||
|
|
6c691d9223 | ||
|
|
920644afaf | ||
|
|
8668aee7fd | ||
|
|
4f386b3e5e | ||
|
|
0f4667d942 | ||
|
|
5705c42820 | ||
|
|
f5f0d00a75 | ||
|
|
ec75e21898 | ||
|
|
94e9e17c79 | ||
|
|
5134e64cae | ||
|
|
53d39ccb4b | ||
|
|
b227e160f0 | ||
|
|
88a2b82f7e | ||
|
|
0016365446 | ||
|
|
0fa8feb475 | ||
|
|
eb872f8fca | ||
|
|
5fd78320dd | ||
|
|
3195042c7d | ||
|
|
b0234fa8b7 | ||
|
|
2a629fee37 | ||
|
|
bf13700a95 | ||
|
|
83268f5ff6 | ||
|
|
1eb42654e0 | ||
|
|
e05c34d1ee | ||
|
|
8325bb11e2 | ||
|
|
c1b577f2c7 | ||
|
|
29a61db275 | ||
|
|
e2c61485a1 | ||
|
|
65f200e02c | ||
|
|
aa9432e24f | ||
|
|
30d1585468 | ||
|
|
376c5263f0 | ||
|
|
bcd6d4e8a7 | ||
|
|
32eb896c56 | ||
|
|
4603163e88 | ||
|
|
b3c9fb2fea | ||
|
|
d66ea07ba4 | ||
|
|
496279c92d | ||
|
|
e0ae925ca3 | ||
|
|
1799c19933 | ||
|
|
94eddd3b42 | ||
|
|
79829cb6d2 | ||
|
|
0d20ca0bf2 | ||
|
|
3ede5ea28f | ||
|
|
cfd15f4e02 | ||
|
|
1f9ff2dd88 | ||
|
|
7740a39a4d | ||
|
|
5eda2dfe0e | ||
|
|
1509c6a471 | ||
|
|
19acd608a5 | ||
|
|
bc81ff28d8 | ||
|
|
ba867e95c3 | ||
|
|
53f99ca434 | ||
|
|
8958ea036e | ||
|
|
d3563c30e7 | ||
|
|
0f017339b7 | ||
|
|
4d3a3d806f | ||
|
|
10a0f708b9 | ||
|
|
409d3c168d | ||
|
|
005220b6d4 | ||
|
|
8462b039d3 | ||
|
|
2e3cabce6b | ||
|
|
2c6b615b8c | ||
|
|
f910e013a6 | ||
|
|
5f1b03261c | ||
|
|
862197fc96 | ||
|
|
44ebd894d5 | ||
|
|
b85ff7db36 | ||
|
|
22fd216de5 | ||
|
|
2f02c428cc | ||
|
|
1732413ee1 | ||
|
|
33325f1574 | ||
|
|
4f288c6fd9 | ||
|
|
5ffb4597b3 | ||
|
|
d9de38b935 | ||
|
|
ad459b83c0 | ||
|
|
d96ea6b815 | ||
|
|
40c3e31955 | ||
|
|
f84c831cda | ||
|
|
904cf97f7d | ||
|
|
791e22dc24 | ||
|
|
c82a0b3d2d | ||
|
|
2fc887724b | ||
|
|
e16762a137 | ||
|
|
4a07ffac69 | ||
|
|
9331c4ce9e | ||
|
|
184ef94e7e | ||
|
|
d67740d44d | ||
|
|
7b30daf88d | ||
|
|
d5e76da475 | ||
|
|
5d91c1af81 | ||
|
|
bb013e78d2 | ||
|
|
c22816de08 | ||
|
|
9bb851d437 | ||
|
|
c8a9f8dcf5 | ||
|
|
e8653ffe32 | ||
|
|
5499df418a | ||
|
|
b586618411 | ||
|
|
3b5730be54 | ||
|
|
961c4a1b9c | ||
|
|
72c5908f09 | ||
|
|
2a588ff943 | ||
|
|
24f11b5ae9 | ||
|
|
1d44d99875 | ||
|
|
eebcdfb24f | ||
|
|
526b56a650 | ||
|
|
20675f56e6 | ||
|
|
e5fbdeb3a9 | ||
|
|
7cc92ec1c1 | ||
|
|
fe0bf6f2ef | ||
|
|
828c19d478 | ||
|
|
15db77558c | ||
|
|
18cf4a3bd3 | ||
|
|
e0378acfc2 | ||
|
|
53cb2af202 | ||
|
|
f74005a166 | ||
|
|
b0df981b01 | ||
|
|
888d844ec5 | ||
|
|
3a7eb9fc0c | ||
|
|
46276aacd9 | ||
|
|
3a7a29868f | ||
|
|
0060c8988a | ||
|
|
458f482c2a | ||
|
|
cc9d6587a3 | ||
|
|
dadf1b6ff9 | ||
|
|
db56bb92f0 | ||
|
|
e82e36f51e | ||
|
|
f9aad34d28 | ||
|
|
b2d720b853 | ||
|
|
c10c05e924 | ||
|
|
8909246725 | ||
|
|
48c0b0ab3c | ||
|
|
cbbed310c5 | ||
|
|
9e9c3b6342 | ||
|
|
fefd262c2e | ||
|
|
1108c99688 | ||
|
|
93da831ef0 | ||
|
|
72bf45912e | ||
|
|
5d94276c4f | ||
|
|
b43f27030e | ||
|
|
2845f55157 | ||
|
|
e254d84710 | ||
|
|
0ef7b5488d | ||
|
|
56b04f8df8 | ||
|
|
2d036c6b25 | ||
|
|
ad3be5c848 | ||
|
|
a0544cc345 | ||
|
|
3245261162 | ||
|
|
21b7348c6a | ||
|
|
54d4dc6e1e | ||
|
|
e00d864199 | ||
|
|
ad6fc89a24 | ||
|
|
fc4823ce8e | ||
|
|
0bd9831eec | ||
|
|
dec2ef70c7 | ||
|
|
d19d8ea770 | ||
|
|
34f7752c0f | ||
|
|
da4d061067 | ||
|
|
85155439be | ||
|
|
dd32e69061 | ||
|
|
1c0c660c32 | ||
|
|
2eac902c33 | ||
|
|
9f5322d309 | ||
|
|
f5c2f6f9e9 | ||
|
|
e0e3b180e7 | ||
|
|
041dc039d4 | ||
|
|
ac755b0ada | ||
|
|
c14259060c | ||
|
|
a423eb5323 | ||
|
|
fd732550d8 | ||
|
|
fac7ba70c8 | ||
|
|
e9f8b35918 | ||
|
|
23034dd4f5 | ||
|
|
70ad44adfd | ||
|
|
95ca8a2630 | ||
|
|
a7fd3c50e5 | ||
|
|
5639f40994 | ||
|
|
33a8262334 | ||
|
|
9b4f7ad696 | ||
|
|
eecd809d9a | ||
|
|
240d3cb8f1 | ||
|
|
5720b7b94c | ||
|
|
3855d7ea02 | ||
|
|
9f3351469d | ||
|
|
2c4a0cf65b | ||
|
|
a8f360c8fd | ||
|
|
b3cff6555f | ||
|
|
69706ecab0 | ||
|
|
05d3b97eae | ||
|
|
a38fb41e37 | ||
|
|
204ae377e3 | ||
|
|
3e5a652874 | ||
|
|
862ae26631 | ||
|
|
9e0208ec71 | ||
|
|
510530973c | ||
|
|
4e69301562 | ||
|
|
df06d4b9a2 | ||
|
|
b60561c3bb | ||
|
|
fffa503968 | ||
|
|
8754157810 | ||
|
|
0763686892 | ||
|
|
50776748a7 | ||
|
|
c07274a6fa | ||
|
|
4084005ee7 | ||
|
|
40fce574cf | ||
|
|
fd761735d4 | ||
|
|
9cb98bdcdc | ||
|
|
b5b1f7ff8a | ||
|
|
a86019d53b | ||
|
|
8cd7d08573 | ||
|
|
6d6f6ed8fc | ||
|
|
c9aed1381f | ||
|
|
49ba5f30c5 | ||
|
|
3c6149fd27 | ||
|
|
23f804eda3 | ||
|
|
4e321db189 | ||
|
|
716fde5784 | ||
|
|
c70b873397 | ||
|
|
4c0da691b8 | ||
|
|
d1d36f9af0 | ||
|
|
56efb4ffba | ||
|
|
540bc71873 | ||
|
|
c1df7ead1a | ||
|
|
d170c807e3 | ||
|
|
50c0abc786 | ||
|
|
c5057127ee | ||
|
|
ea7b1a9592 | ||
|
|
26d483f2d7 | ||
|
|
67fce58708 | ||
|
|
ea5771c442 | ||
|
|
fa8a1faeb3 | ||
|
|
d60a000af6 | ||
|
|
3d68e2b74f | ||
|
|
42f6d48abf | ||
|
|
5587957602 | ||
|
|
1d313ebf25 | ||
|
|
cf92349c1d | ||
|
|
b0ae44bc25 | ||
|
|
0c94124fdf | ||
|
|
22298ddb41 | ||
|
|
6f927b9941 | ||
|
|
dc5626f34d | ||
|
|
2717ec2759 | ||
|
|
d75449ea63 | ||
|
|
034f5b16bd | ||
|
|
ff420f1e67 | ||
|
|
5be771842f | ||
|
|
a131c93a14 | ||
|
|
3da3546ef8 | ||
|
|
47f5223c37 | ||
|
|
b8c50eb0fe | ||
|
|
fe05082ddc | ||
|
|
414acda9e0 | ||
|
|
47e3a5e75a | ||
|
|
573df892bc | ||
|
|
bfced59949 | ||
|
|
9a63b1751b | ||
|
|
53ae8b9bd1 | ||
|
|
bf3cc5a367 | ||
|
|
ac00bc6b70 | ||
|
|
ab62603c5a | ||
|
|
dd247cd541 | ||
|
|
0d7a9f32d6 | ||
|
|
0d85ef4404 | ||
|
|
8e9273a96a | ||
|
|
2dfdaaa4ba | ||
|
|
407094cdcb | ||
|
|
0111db6744 | ||
|
|
a49230c0e1 | ||
|
|
d04abc2021 | ||
|
|
b5368207d6 | ||
|
|
b8400e0c92 | ||
|
|
ef6b6cd279 | ||
|
|
e5903d8d6e | ||
|
|
e2a3db1b82 | ||
|
|
9e220e8416 | ||
|
|
605c233753 | ||
|
|
aafe18c2b5 | ||
|
|
262e8af813 | ||
|
|
75aa4ce8a0 | ||
|
|
a494cdaf92 | ||
|
|
848425418b | ||
|
|
989bed88fe | ||
|
|
99f66200f7 | ||
|
|
b642bd2cfd | ||
|
|
c062b7c9da | ||
|
|
7df7901532 | ||
|
|
9dabce819a | ||
|
|
a019109857 | ||
|
|
46cdf14ecc | ||
|
|
fd5ff20b68 | ||
|
|
81f9666e82 | ||
|
|
aa8659d5c7 | ||
|
|
9df2df0f9f | ||
|
|
98d38d763b | ||
|
|
319a4622b3 | ||
|
|
59b4c6dacb | ||
|
|
ec6d725587 | ||
|
|
6f19649186 | ||
|
|
6a583b9b5b | ||
|
|
6617b79cd8 | ||
|
|
46b695d093 | ||
|
|
de8a017e80 | ||
|
|
b71ec91f57 | ||
|
|
1b7c226779 | ||
|
|
ca5a5b4392 | ||
|
|
6ae7ae0210 | ||
|
|
b911d2fda4 | ||
|
|
847a92f580 | ||
|
|
a346493de1 | ||
|
|
efd5d6efab | ||
|
|
b6df5a4b7f | ||
|
|
34ae56e319 | ||
|
|
5870824d01 | ||
|
|
1dbadbfc35 | ||
|
|
d3f1cd7411 | ||
|
|
a821daadd3 | ||
|
|
a39f4b4487 | ||
|
|
f7b401b9b6 | ||
|
|
c342d18f92 | ||
|
|
e0e52a3090 | ||
|
|
6f92480315 | ||
|
|
490d008edf | ||
|
|
ccee17c573 | ||
|
|
dd4832e40b | ||
|
|
ff5b870a9f | ||
|
|
a371580201 | ||
|
|
b499c575bc | ||
|
|
f5a6268cef | ||
|
|
2df4083602 | ||
|
|
2495fb796d | ||
|
|
f7b49740c1 | ||
|
|
7e765fe726 | ||
|
|
1d339f74d1 | ||
|
|
a3bb288f01 | ||
|
|
0e1226573a | ||
|
|
8151b65d12 | ||
|
|
eefd36b50d | ||
|
|
6f4d769187 | ||
|
|
20b76a703a | ||
|
|
dcc09b7bb5 | ||
|
|
4ea0dee90a | ||
|
|
e61116d35a | ||
|
|
f7256c75d5 | ||
|
|
2696fd1291 | ||
|
|
b139620fba | ||
|
|
97115a9c1c | ||
|
|
36134c61b2 | ||
|
|
77cf0fd87b | ||
|
|
ab77a1cbc0 | ||
|
|
8490892493 | ||
|
|
5f35dffa34 | ||
|
|
89a4b5a6ff | ||
|
|
931de7e39c | ||
|
|
ff41d6aef9 | ||
|
|
8e1ba58551 | ||
|
|
2b1692a860 | ||
|
|
7a2981e21c | ||
|
|
f8717359d7 | ||
|
|
fe395e3a8c | ||
|
|
eb9fe851af | ||
|
|
d61851ad02 | ||
|
|
de7a408903 | ||
|
|
4b0f60fe71 | ||
|
|
a167bcff5e | ||
|
|
fb812e6335 | ||
|
|
3fd557c74a | ||
|
|
9b1272da51 | ||
|
|
ae2402f4a7 | ||
|
|
c98e869564 | ||
|
|
e1a019ec56 | ||
|
|
2d7fcbf23d | ||
|
|
b604177ca1 | ||
|
|
f27597cab5 | ||
|
|
40619c8184 | ||
|
|
b0a5df2838 | ||
|
|
ae9232a2cb | ||
|
|
ad28d0025a | ||
|
|
d6772c5ca5 | ||
|
|
a2400767ed | ||
|
|
1873b655a8 | ||
|
|
e27c2b59c8 | ||
|
|
4b50608642 | ||
|
|
e3a7f9eb7a | ||
|
|
c130b3a92e | ||
|
|
20fb6d6669 | ||
|
|
caf20cd43d | ||
|
|
608d661355 | ||
|
|
31c868818b | ||
|
|
443c4b6cd0 | ||
|
|
ee425bb7f0 | ||
|
|
9d1c026231 | ||
|
|
6d7612ead6 | ||
|
|
b3e31094ff | ||
|
|
c9750fab2c | ||
|
|
5a1348e4d1 | ||
|
|
b64d45a74e | ||
|
|
ba5a4c3945 | ||
|
|
9ba37a932b | ||
|
|
8ce377d033 | ||
|
|
a7f3dafd81 | ||
|
|
fb38b4b906 | ||
|
|
f0a1a90b0e | ||
|
|
fb331d39a6 | ||
|
|
7a3e6e19b1 | ||
|
|
5a063c541d | ||
|
|
eef25ec638 | ||
|
|
7141fb18c1 | ||
|
|
2c92be5e3d | ||
|
|
cae0733149 | ||
|
|
5d6ca7a88e | ||
|
|
23b8a4d364 | ||
|
|
c0df8cb578 | ||
|
|
4693c23a49 | ||
|
|
86364ff278 | ||
|
|
60f1f1cb3d | ||
|
|
ab3c520006 | ||
|
|
3798dcb98d | ||
|
|
9daf290432 | ||
|
|
3438f88c1e | ||
|
|
2c8858ff2d | ||
|
|
45ed9405b0 | ||
|
|
075ca78dd2 | ||
|
|
1d964ef921 | ||
|
|
0c6d9b1d3b | ||
|
|
1713f1222b | ||
|
|
450591d5a9 | ||
|
|
5c4863dfb1 | ||
|
|
c6a82cbc07 | ||
|
|
cc8f074d33 | ||
|
|
196d74c3a0 | ||
|
|
f28e87b240 | ||
|
|
2d77ef4245 | ||
|
|
3f578e24b5 | ||
|
|
fb5ee5e190 | ||
|
|
63e8378310 | ||
|
|
c24ebaf506 | ||
|
|
d3a1f11dfe | ||
|
|
f96e694ff8 | ||
|
|
d5d635e50f | ||
|
|
1ad4ce9822 | ||
|
|
3f971174e4 | ||
|
|
16e257ea32 | ||
|
|
37f1f8ac14 | ||
|
|
474fa9b3ec | ||
|
|
6d7b14ceb5 | ||
|
|
af14832b6c | ||
|
|
9ef185978f | ||
|
|
38bb44664c | ||
|
|
124d924ff5 | ||
|
|
e8091d8add | ||
|
|
500bd53b6a | ||
|
|
6996ca832d | ||
|
|
2e15ab44ab | ||
|
|
2ae5472872 | ||
|
|
98a32790cd | ||
|
|
6dcefcfb71 | ||
|
|
988366e37f | ||
|
|
98a37905b8 | ||
|
|
bd832d0fbb | ||
|
|
1f7e18f2a9 | ||
|
|
105e9c8e88 | ||
|
|
99e1a5f117 | ||
|
|
b35b49f7b3 | ||
|
|
5d064b18e6 | ||
|
|
11ea92288d | ||
|
|
1ea6ae9fd9 | ||
|
|
57d739673a | ||
|
|
4d8f76e922 | ||
|
|
fb41d4ab0f | ||
|
|
023ee688d3 | ||
|
|
5f8f0d8573 | ||
|
|
f56e0a6586 | ||
|
|
2c96420bc4 | ||
|
|
8d95eb57fa | ||
|
|
a9a5e79e85 | ||
|
|
2b720b8d0b | ||
|
|
93a0ab7f0a | ||
|
|
18dd55d1f4 | ||
|
|
c9074d8d96 | ||
|
|
f6c995157e | ||
|
|
a059a9fbdc | ||
|
|
2ac6596ba9 | ||
|
|
2d1f57e839 | ||
|
|
7799ed36f0 | ||
|
|
db5ed345bc | ||
|
|
082c6a9e64 | ||
|
|
b74a4d5225 | ||
|
|
16a572c250 | ||
|
|
6a8a2d6f56 | ||
|
|
237c855f10 | ||
|
|
15d18484e3 | ||
|
|
378e9b5539 | ||
|
|
7a135bd87c | ||
|
|
9e907e36e2 | ||
|
|
9ac71f304d | ||
|
|
ab9b604d02 | ||
|
|
155645307a | ||
|
|
4ca7ac4851 | ||
|
|
02eb24cd57 | ||
|
|
787fbe1bb6 | ||
|
|
7678858bab | ||
|
|
bf7a28f07c |
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
target
|
||||||
|
Dockerfile
|
||||||
|
README.md
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.gitmodules
|
||||||
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Resolved all lints and formatted the codebase
|
||||||
|
9444e62df9820d6bfd96dbd8849e177bc5cecc2e
|
||||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*.png binary
|
||||||
|
*.pl text eol=lf
|
||||||
|
*.rs text eol=lf diff=rust
|
||||||
|
*.md text eol=lf diff=markdown
|
||||||
63
.github/actions/setup-rust/action.yml
vendored
Normal file
63
.github/actions/setup-rust/action.yml
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
name: 'Setup Rust'
|
||||||
|
description: |
|
||||||
|
Setup the rust toolchain and environment for the selected toolchain
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
rust-version:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
targets:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
components:
|
||||||
|
required: false
|
||||||
|
default:
|
||||||
|
cache-context:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # master
|
||||||
|
id: toolchain
|
||||||
|
with:
|
||||||
|
toolchain: ${{ inputs.rust-version }}
|
||||||
|
targets: ${{ inputs.targets }}
|
||||||
|
components: ${{ inputs.components }}
|
||||||
|
|
||||||
|
- name: Install i686 dependencies
|
||||||
|
if: "contains(inputs.targets,'i686')"
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo dpkg --add-architecture i386
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install libssl-dev:i386 gcc-multilib clang -y
|
||||||
|
echo "CC=clang" >> $GITHUB_ENV
|
||||||
|
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Install s390x cross-compilation toolchain
|
||||||
|
if: ${{ matrix.target == 's390x-unknown-linux-gnu' }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y gcc-s390x-linux-gnu
|
||||||
|
echo "CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_LINKER=s390x-linux-gnu-gcc" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/bin/
|
||||||
|
~/.cargo/registry/index/
|
||||||
|
~/.cargo/registry/cache/
|
||||||
|
~/.cargo/git/db/
|
||||||
|
target/
|
||||||
|
key: ${{ inputs.cache-context }}_${{ inputs.targets }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
|
||||||
|
# Remove build artifacts for the current crate, since it will be rebuilt every
|
||||||
|
# run anyway, but keep dependency artifacts to cache them.
|
||||||
|
# Must be placed after actions/cache so its post step runs first.
|
||||||
|
- uses: pyTooling/Actions/with-post-step@41aed3e559cbf69bfdb46abb25d7c535900ed5b6 # v7.7.0
|
||||||
|
with:
|
||||||
|
main: bash ./.github/actions/setup-rust/cleanup.sh
|
||||||
|
post: bash ./.github/actions/setup-rust/cleanup.sh
|
||||||
13
.github/actions/setup-rust/cleanup.sh
vendored
Executable file
13
.github/actions/setup-rust/cleanup.sh
vendored
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo Cleanup workspace build artifacts and extra target output
|
||||||
|
|
||||||
|
# clean just the direct members of the current workspace, use cargo metadata to generalize to all rust projects
|
||||||
|
cargo clean --workspace
|
||||||
|
|
||||||
|
# remove directories in /target/ that are not named `debug` or `release`
|
||||||
|
before=`du -s target | awk '{print $1}'`
|
||||||
|
find ./target -maxdepth 1 -type d ! -name debug ! -name release ! -name target -exec rm -r {} \;
|
||||||
|
after=`du -s target | awk '{print $1}'`
|
||||||
|
echo Deleted $(($before - $after)) bytes from target directory
|
||||||
52
.github/dependabot.yml
vendored
Normal file
52
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
updates:
|
||||||
|
# Enable version updates for cargo
|
||||||
|
- package-ecosystem: "cargo"
|
||||||
|
# Look for `Cargo.toml` and `Cargo.lock` files in the `root` directory
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
cooldown:
|
||||||
|
default-days: "7"
|
||||||
|
groups:
|
||||||
|
cargo-incompatible:
|
||||||
|
applies-to: version-updates
|
||||||
|
# TODO: use incompatible update-type once available
|
||||||
|
# see issue https://github.com/dependabot/dependabot-core/issues/9681
|
||||||
|
update-types:
|
||||||
|
- "major"
|
||||||
|
- "minor" # pre-1.0 dependencies
|
||||||
|
cargo-compatible:
|
||||||
|
applies-to: version-updates
|
||||||
|
# TODO: use compatible update-type once available
|
||||||
|
# see issue https://github.com/dependabot/dependabot-core/issues/9681
|
||||||
|
update-types:
|
||||||
|
- "patch"
|
||||||
|
ignore:
|
||||||
|
# ignore all cargo updates for now while dependabot does not respect msrv/rust-version
|
||||||
|
# see issue https://github.com/dependabot/dependabot-core/issues/5423
|
||||||
|
- dependency-name: "*"
|
||||||
|
|
||||||
|
# Enable version updates for Docker
|
||||||
|
- package-ecosystem: "docker"
|
||||||
|
# Look for a `Dockerfile` in the `root` directory
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
cooldown:
|
||||||
|
default-days: "7"
|
||||||
|
|
||||||
|
# Enable version updates for GitHub Actions
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
# Workflow files stored in the default location of `.github/workflows`
|
||||||
|
# You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
cooldown:
|
||||||
|
default-days: "7"
|
||||||
|
ignore:
|
||||||
|
# these actions doesn't have proper version tags
|
||||||
|
- dependency-name: "dtolnay/rust-toolchain"
|
||||||
|
- dependency-name: "logtalk-actions/setup-logtalk"
|
||||||
12
.github/workflows/check_features.sh
vendored
Executable file
12
.github/workflows/check_features.sh
vendored
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Checking all feature at once"
|
||||||
|
cargo check -q --all-targets --all-features "$@"
|
||||||
|
|
||||||
|
features=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name = "scryer-prolog") | .features | keys | join(" ")')
|
||||||
|
|
||||||
|
for feature in ${features} ; do
|
||||||
|
echo "Checking feature ${feature} in isolation"
|
||||||
|
cargo check -q --all-targets --no-default-features --features=${feature} "$@"
|
||||||
|
done
|
||||||
248
.github/workflows/ci.yml
vendored
Normal file
248
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- rebis-dev
|
||||||
|
tags:
|
||||||
|
- "v**"
|
||||||
|
pull_request:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
style:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: ./.github/actions/setup-rust
|
||||||
|
with:
|
||||||
|
rust-version: stable
|
||||||
|
targets: x86_64-unknown-linux-gnu
|
||||||
|
components: clippy, rustfmt
|
||||||
|
cache-context: style
|
||||||
|
|
||||||
|
- name: Check formatting
|
||||||
|
run: cargo fmt --check
|
||||||
|
- name: Check clippy
|
||||||
|
run: cargo clippy --no-deps --all-targets
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
|
||||||
|
read-msrv:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
outputs:
|
||||||
|
msrv: ${{ steps.read-declared-msrv.outputs.msrv }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- 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, check-features: true }
|
||||||
|
- { os: macos-latest, rust-version: stable, target: 'x86_64-apple-darwin', publish: true, check-features: true }
|
||||||
|
# architectures
|
||||||
|
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true, check-features: 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', 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', miri: true, components: "miri" }
|
||||||
|
# run miri for a big-endian target, with all features that are simple to get cross-compiled
|
||||||
|
- { os: ubuntu-22.04, rust-version: nightly, target: 's390x-unknown-linux-gnu', miri: true, components: "miri", args: '--no-default-features --features=all-simple-cross', test-args: '--no-run --no-default-features --features=all-simple-cross' }
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- uses: actionhippie/swap-space@0cffa893f224708cfb6b011690d8ba819d69c10f # v1.1.0
|
||||||
|
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
|
||||||
|
run: cargo build --all-targets --target ${{ matrix.target }} ${{ matrix.args }} --verbose
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
|
||||||
|
|
||||||
|
- name: Check features
|
||||||
|
if: matrix.check-features
|
||||||
|
run: bash ./.github/workflows/check_features.sh --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Check miri
|
||||||
|
if: matrix.miri
|
||||||
|
run: cargo miri test --target ${{ matrix.target }} ${{ matrix.args }}
|
||||||
|
|
||||||
|
# On stable rust builds, build a binary and publish as a github actions
|
||||||
|
# artifact. These binaries could be useful for testing the pipeline but
|
||||||
|
# are only retained by github for 90 days.
|
||||||
|
- name: Build release binary
|
||||||
|
if: matrix.publish
|
||||||
|
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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
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-22.04
|
||||||
|
needs: [build-test]
|
||||||
|
steps:
|
||||||
|
# Download prebuilt ubuntu binary from build-test job, setup logtalk
|
||||||
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
|
||||||
|
with:
|
||||||
|
name: scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu
|
||||||
|
- run: |
|
||||||
|
chmod +x release/scryer-prolog
|
||||||
|
echo "$PWD/release" >> "$GITHUB_PATH"
|
||||||
|
- name: Install Logtalk
|
||||||
|
uses: logtalk-actions/setup-logtalk@4ea002fe3037199afcf1c6c91bf1f57de0f995e6 # master
|
||||||
|
with:
|
||||||
|
logtalk-version: "3.70.0"
|
||||||
|
logtalk-tool-dependencies: false
|
||||||
|
|
||||||
|
# Run logtalk tests.
|
||||||
|
- name: Run Logtalk's prolog compliance test suite
|
||||||
|
working-directory: ${{ env.LOGTALKUSER }}/tests/prolog/
|
||||||
|
run: |
|
||||||
|
pwd
|
||||||
|
scryerlgt -g '{ack(tester)},halt.'
|
||||||
|
logtalk_tester -p scryer -g "set_logtalk_flag(clean,off)" -w -t 360 \
|
||||||
|
-f xunit \
|
||||||
|
-s "$LOGTALKUSER/tests/prolog" \
|
||||||
|
|| 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: logtalk-test-logs
|
||||||
|
path: '${{ env.LOGTALKUSER }}/tests/prolog/logtalk_tester_logs'
|
||||||
|
- name: Publish Logtalk test results artifact
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: logtalk-test-results
|
||||||
|
path: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml'
|
||||||
|
- name: Publish Logtalk test summary
|
||||||
|
uses: EnricoMi/publish-unit-test-result-action/composite@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2.23.0
|
||||||
|
with:
|
||||||
|
check_name: Logtalk test summary
|
||||||
|
files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml'
|
||||||
|
fail_on: nothing
|
||||||
|
comment_mode: off
|
||||||
|
|
||||||
|
report:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: ./.github/actions/setup-rust
|
||||||
|
with:
|
||||||
|
rust-version: stable
|
||||||
|
targets: x86_64-unknown-linux-gnu
|
||||||
|
cache-context: report
|
||||||
|
- name: Install CLI tools
|
||||||
|
run: |
|
||||||
|
cargo install cargo2junit --force
|
||||||
|
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-get update -y
|
||||||
|
sudo apt-get 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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: cargo-test-results
|
||||||
|
path: cargo_test_results.xml
|
||||||
|
- name: Publish cargo test summary
|
||||||
|
uses: EnricoMi/publish-unit-test-result-action/composite@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2.23.0
|
||||||
|
with:
|
||||||
|
check_name: Cargo test summary
|
||||||
|
files: cargo_test_results.xml
|
||||||
|
fail_on: nothing
|
||||||
|
comment_mode: off
|
||||||
|
|
||||||
|
- run: cargo build --all-targets --release
|
||||||
|
- run: cargo test --bench setup --release
|
||||||
|
- run: cargo bench --bench run_iai -- --save-summary=json
|
||||||
|
- run: cargo bench --bench run_criterion
|
||||||
|
- run: cargo bench --bench run_criterion -- --profile-time 60
|
||||||
|
|
||||||
|
- name: Publish benchmark results
|
||||||
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: benchmark-results
|
||||||
|
path: |
|
||||||
|
target/criterion/*
|
||||||
|
target/iai/*
|
||||||
|
target/benchmark_inference_counts.json
|
||||||
|
|
||||||
|
# Publish binaries when building for a tag
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
needs: [build-test]
|
||||||
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
|
steps:
|
||||||
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
|
||||||
|
- name: Zip binaries for release
|
||||||
|
run: |
|
||||||
|
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@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
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
|
||||||
61
.github/workflows/docker-publish.yml
vendored
Normal file
61
.github/workflows/docker-publish.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
name: Docker Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
tags:
|
||||||
|
- 'v*.*.*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
# 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@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||||
|
|
||||||
|
# Login against Docker registry
|
||||||
|
- name: Log into registry
|
||||||
|
# https://github.com/docker/login-action
|
||||||
|
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Extract Docker image tag from git tag. E.g. if git tag is "v0.19.1" then use
|
||||||
|
# Docker image tag "0.19.1". The "latest" tag reflects the most recent build on
|
||||||
|
# master.
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
# https://github.com/docker/metadata-action
|
||||||
|
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
|
||||||
|
with:
|
||||||
|
images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/scryer-prolog
|
||||||
|
tags: |
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
# type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
|
||||||
|
|
||||||
|
# Build and push Docker image with Buildx
|
||||||
|
- name: Build and push Docker image
|
||||||
|
id: build-and-push
|
||||||
|
# https://github.com/docker/build-push-action
|
||||||
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||||
|
# 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
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,7 @@
|
|||||||
|
src/static_atoms.rs
|
||||||
target/
|
target/
|
||||||
|
.direnv/
|
||||||
|
|
||||||
|
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
14
.travis.yml
14
.travis.yml
@@ -1,14 +0,0 @@
|
|||||||
language: rust
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
- nightly
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- rust: nightly
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
script:
|
|
||||||
- cargo build --verbose --all
|
|
||||||
- cargo test --verbose --all
|
|
||||||
- cargo test --verbose --all --no-default-features --features num
|
|
||||||
4478
Cargo.lock
generated
4478
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
191
Cargo.toml
191
Cargo.toml
@@ -1,32 +1,179 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "scryer-prolog"
|
name = "scryer-prolog"
|
||||||
version = "0.8.118"
|
version = "0.10.0"
|
||||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||||
build = "build.rs"
|
edition = "2024"
|
||||||
repository = "https://github.com/mthom/scryer-prolog"
|
|
||||||
description = "A modern Prolog implementation written mostly in Rust."
|
description = "A modern Prolog implementation written mostly in Rust."
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/mthom/scryer-prolog"
|
||||||
license = "BSD-3-Clause"
|
license = "BSD-3-Clause"
|
||||||
edition = "2018"
|
keywords = ["prolog", "prolog-interpreter", "prolog-system"]
|
||||||
|
categories = ["command-line-utilities"]
|
||||||
|
build = "build/main.rs"
|
||||||
|
# Remember to check CI
|
||||||
|
rust-version = "1.93.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[lib]
|
||||||
indexmap = "1.0.2"
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rug", "prolog_parser/rug"]
|
default = ["all-simple-cross", "tls", "http"]
|
||||||
num = ["num-rug-adapter", "prolog_parser/num"]
|
# activates all features that depend on no non pure-rust dependencies
|
||||||
|
#
|
||||||
|
# currently does not include
|
||||||
|
# ffi due to libffi
|
||||||
|
# tls, http due to openssl
|
||||||
|
# crypto-full due to ring
|
||||||
|
all-pure = ["repl", "hostname"]
|
||||||
|
# enables all features that are simple to get working for cross-compliation
|
||||||
|
# currently all but tls, http as those depend on openssl
|
||||||
|
all-simple-cross = ["all-pure", "ffi", "crypto-full"]
|
||||||
|
ffi = ["dep:libffi"]
|
||||||
|
repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"]
|
||||||
|
hostname = ["dep:hostname"]
|
||||||
|
tls = ["dep:native-tls"]
|
||||||
|
http = ["dep:warp", "dep:reqwest"]
|
||||||
|
# crypto function that require non pure-rust dependencies
|
||||||
|
crypto-full = ["dep:ring"]
|
||||||
|
|
||||||
|
[lints.clippy]
|
||||||
|
collapsible_match = "allow"
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
unexpected_cfgs = "deny"
|
||||||
|
function_casts_as_integer = "deny"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
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"
|
||||||
|
walkdir = "2"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossterm = "0.16.0"
|
arcu = { version = "0.1.2", features = ["thread_local_counter"] }
|
||||||
dirs = "2.0.2"
|
base64 = "0.22.1"
|
||||||
downcast = "0.10.0"
|
bit-set = "0.8.0"
|
||||||
git-version = "0.3.4"
|
bitvec = "1"
|
||||||
indexmap = "1.0.2"
|
blake2 = "0.10.6"
|
||||||
lazy_static = "1.4.0"
|
bytes = "1"
|
||||||
libc = "0.2.62"
|
chrono = "0.4.38"
|
||||||
nix = "0.15.0"
|
cpu-time = "1.0.0"
|
||||||
num-rug-adapter = { optional = true, version = "0.1.1" }
|
crrl = "0.9.0"
|
||||||
ordered-float = "0.5.0"
|
dashu = { version = "0.4.2", features = ["rand"] }
|
||||||
prolog_parser = { version = "0.8.49", default-features = false }
|
derive_more = "0.99.18"
|
||||||
ref_thread_local = "0.0.0"
|
dirs-next = "2.0.0"
|
||||||
rug = { version = "1.4.0", optional = true }
|
divrem = "1.0.0"
|
||||||
rustyline = "6.0.0"
|
futures = "0.3"
|
||||||
|
fxhash = "0.2.1"
|
||||||
|
git-version = "0.3.9"
|
||||||
|
indexmap = "2.3.0"
|
||||||
|
lexical = "7.0.4"
|
||||||
|
libc = "0.2.155"
|
||||||
|
libloading = "0.8"
|
||||||
|
modular-bitfield = "0.13.1"
|
||||||
|
num-order = { version = "1.2.0" }
|
||||||
|
ordered-float = "5.0.0"
|
||||||
|
phf = { version = "0.11", features = ["macros"] }
|
||||||
|
puruspe = "0.4.1"
|
||||||
|
rand = "0.8.5"
|
||||||
|
ring = { version = "0.17.8", features = [
|
||||||
|
"wasm32_unknown_unknown_js",
|
||||||
|
], optional = true }
|
||||||
|
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"
|
||||||
|
|
||||||
|
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.28.1", optional = true }
|
||||||
|
ctrlc = { version = "3.4.4", optional = true }
|
||||||
|
hostname = { version = "0.4.0", optional = true }
|
||||||
|
libffi = { version = "5.1.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 = "18.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.15", features = ["js"] }
|
||||||
|
tokio = { version = "1.39.2", features = [
|
||||||
|
"sync",
|
||||||
|
"macros",
|
||||||
|
"io-util",
|
||||||
|
"rt",
|
||||||
|
"time",
|
||||||
|
] }
|
||||||
|
|
||||||
|
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
|
||||||
|
console_error_panic_hook = "0.1"
|
||||||
|
wasm-bindgen = "0.2.92"
|
||||||
|
wasm-bindgen-futures = "0.4"
|
||||||
|
serde-wasm-bindgen = "0.6"
|
||||||
|
web-sys = { version = "0.3", features = [
|
||||||
|
"Document",
|
||||||
|
"Window",
|
||||||
|
"Element",
|
||||||
|
"Performance",
|
||||||
|
] }
|
||||||
|
js-sys = "0.3"
|
||||||
|
ouroboros = "0.18"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
current_platform = "0.2.0"
|
||||||
|
maplit = "1.0.2"
|
||||||
|
serial_test = "3.1.1"
|
||||||
|
|
||||||
|
[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies]
|
||||||
|
criterion = "0.5.1"
|
||||||
|
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"] }
|
||||||
|
|
||||||
|
[profile.bench]
|
||||||
|
lto = true
|
||||||
|
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"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "run_iai"
|
||||||
|
harness = false
|
||||||
|
|||||||
34
Dockerfile
Normal file
34
Dockerfile
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# See https://github.com/LukeMathWalker/cargo-chef
|
||||||
|
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
|
||||||
|
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
|
||||||
|
WORKDIR /scryer-prolog
|
||||||
|
COPY . .
|
||||||
|
# Copy over the cached dependencies
|
||||||
|
COPY --from=cacher /scryer-prolog/target target
|
||||||
|
COPY --from=cacher $CARGO_HOME $CARGO_HOME
|
||||||
|
RUN cargo build --release --bin scryer-prolog
|
||||||
|
|
||||||
|
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)
|
||||||
|
# then fail the build
|
||||||
|
RUN scryer-prolog --version
|
||||||
|
ENTRYPOINT ["/usr/local/bin/scryer-prolog"]
|
||||||
79
INDEX.dj
Normal file
79
INDEX.dj
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Scryer Prolog
|
||||||
|
|
||||||
|
```
|
||||||
|
?- append("Hello, ", X, "Hello, Scryer Prolog!").
|
||||||
|
X = "Scryer Prolog!".
|
||||||
|
```
|
||||||
|
|
||||||
|
{width=128 style=float:right;} [Scryer Prolog](https://github.com/mthom/scryer-prolog) is a free software ISO Prolog system intended to be an industrial
|
||||||
|
strength production environment *and* a testbed for bleeding edge research in
|
||||||
|
logic and constraint programming.
|
||||||
|
|
||||||
|
Some of the Scryer Prolog features are:
|
||||||
|
|
||||||
|
* ISO standard compliant
|
||||||
|
* Integrated constraint programming libraries: [clp(B)](/clpb.html), [clp(Z)](/clpz.html).
|
||||||
|
* [Definite Clause Grammars](/dcgs.html)
|
||||||
|
* Coroutining support ([`dif/2`](/dif.html), [`freeze/2`](/freeze.html), ...)
|
||||||
|
* [Tabling and SLG resolution](/tabling.html)
|
||||||
|
* Compact string representation
|
||||||
|
* Network libraries ([TCP sockets](/sockets.html), [HTTP server](/http/http_server.html), [HTTP client](/http/http_open.html), ...)
|
||||||
|
* [Cryptographical predicates](/crypto.html)
|
||||||
|
* [Foreign Function Interface](/ffi.html)
|
||||||
|
* WebAssembly support
|
||||||
|
* Usable as a library
|
||||||
|
* WAM based engine, cross-platform made in Rust
|
||||||
|
* _and more..._
|
||||||
|
|
||||||
|
Try Scryer Prolog without any installation! Use [Scryer Playground](https://play.scryer.pl), which uses the WASM version of Scryer Prolog.
|
||||||
|
|
||||||
|
## What is Prolog?
|
||||||
|
|
||||||
|
Prolog is a logic programming language created by [Alain Colmerauer](https://en.wikipedia.org/wiki/Alain_Colmerauer) and [Robert Kowalski](https://en.wikipedia.org/wiki/Robert_Kowalski) in 1972.
|
||||||
|
The idea behind Prolog is try to express a task in language similar to First Order Logic.
|
||||||
|
Prolog systems include _unification_ and _non-determinism_ as key concepts upon which we build programs.
|
||||||
|
|
||||||
|
A Prolog program is made up of predicates which define a relation between its arguments. A predicate
|
||||||
|
is made from clauses. A clause can be either a fact or a rule. There's also a toplevel, which we
|
||||||
|
can use to ask and reason about our task.
|
||||||
|
|
||||||
|
It's still to this day one of the best examples and one of the most popular languages in the field
|
||||||
|
of logic programming. That's because Prolog allows us to elegantly solve many tasks with short and
|
||||||
|
general programs.
|
||||||
|
|
||||||
|
If you want a more detailed description of Prolog, check [A Tour of Prolog](https://www.youtube.com/watch?v=8XUutFBbUrg).
|
||||||
|
|
||||||
|
If you want to learn more about Prolog history, check the videos [l'Aventure Prolog](https://www.youtube.com/watch?v=74Ig_QKndvE) and [50 years of Prolog and beyond](https://prologyear.logicprogramming.org/videos/PrologDay_Session_1_talk.mp4).
|
||||||
|
|
||||||
|
## Where can I learn Prolog?
|
||||||
|
|
||||||
|
There are a lot of classical Prolog books. Those books can teach you the basics of Prolog. Some
|
||||||
|
examples are: _The Art of Prolog (Shapiro)_, _Programming in Prolog (Clocksin, Mellish)_ and _The Craft
|
||||||
|
of Prolog (O'Keefe)_. However, most of them are not updated to _modern_ Prolog.
|
||||||
|
We recommend _[The Power of Prolog (Markus Triska)](https://www.metalevel.at/prolog)_ for modern Prolog. For reference about
|
||||||
|
the builtin Prolog modules and libraries in Scryer, check the documentation site. It's this!
|
||||||
|
|
||||||
|
## Downloads
|
||||||
|
|
||||||
|
The latest version of Scryer Prolog is *0.10.0*. And it's already useful for lots of tasks.
|
||||||
|
|
||||||
|
| Windows (64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_windows-latest_x86_64-pc-windows-msvc.zip) |
|
||||||
|
| macOS (Intel) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_macos-11_x86_64-apple-darwin.zip) |
|
||||||
|
| macOS (ARM) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog--0.10.0.arm64_tahoe.bottle.tar.gz) |
|
||||||
|
| Linux (Ubuntu 22.04, 64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/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-0100/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.
|
||||||
|
|
||||||
|
If you're in Linux, maybe your distribution already has an Scryer Prolog package.
|
||||||
|
|
||||||
|
There's also a [Docker image](https://github.com/mthom/scryer-prolog#docker-install) available.
|
||||||
|
|
||||||
|
## Support and discussions
|
||||||
|
|
||||||
|
If Scryer Prolog crashes or yields unexpected errors, consider filing
|
||||||
|
an [issue](https://github.com/mthom/scryer-prolog/issues).
|
||||||
|
|
||||||
|
To get in touch with the Scryer Prolog community, participate in
|
||||||
|
[discussions](https://github.com/mthom/scryer-prolog/discussions)
|
||||||
|
or visit our #scryer IRC channel on [Libera](https://libera.chat)!
|
||||||
773
README.md
773
README.md
@@ -5,12 +5,20 @@ source industrial strength production environment that is also a
|
|||||||
testbed for bleeding edge research in logic and constraint
|
testbed for bleeding edge research in logic and constraint
|
||||||
programming, which is itself written in a high-level language.
|
programming, which is itself written in a high-level language.
|
||||||
|
|
||||||
|
**Scryer Prolog passes all tests** of
|
||||||
|
[syntactic conformity](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing),
|
||||||
|
[`variable_names/1`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/variable_names) and
|
||||||
|
[`dif/2`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dif).
|
||||||
|
|
||||||
|
The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Phase 1
|
## Phase 1
|
||||||
|
|
||||||
Produce an implementation of the Warren Abstract Machine in Rust, done
|
Produce an implementation of the Warren Abstract Machine in Rust, done
|
||||||
according to the progression of languages in [Warren's Abstract
|
according to the progression of languages in [Warren's Abstract
|
||||||
Machine: A Tutorial
|
Machine: A Tutorial Reconstruction](https://github.com/mthom/scryer-prolog/blob/master/wambook/wambook.pdf).
|
||||||
Reconstruction](http://wambook.sourceforge.net/wambook.pdf).
|
|
||||||
|
|
||||||
Phase 1 has been completed in that Scryer Prolog implements in some form
|
Phase 1 has been completed in that Scryer Prolog implements in some form
|
||||||
all of the WAM book, including lists, cuts, Debray allocation, first
|
all of the WAM book, including lists, cuts, Debray allocation, first
|
||||||
@@ -40,8 +48,8 @@ Extend Scryer Prolog to include the following, among other features:
|
|||||||
- [x] Support for `verify_attributes/3`
|
- [x] Support for `verify_attributes/3`
|
||||||
- [x] Support for `attribute_goals/2` and `project_attributes/2`
|
- [x] Support for `attribute_goals/2` and `project_attributes/2`
|
||||||
- [x] `call_residue_vars/2`
|
- [x] `call_residue_vars/2`
|
||||||
- [x] `if_` and related predicates, following the developments of the
|
- [x] `if_/3` and related predicates, following the developments of the
|
||||||
paper "Indexing `dif/2`".
|
paper "[Indexing `dif/2`](https://arxiv.org/abs/1607.01590)".
|
||||||
- [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`).
|
- [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`).
|
||||||
- [x] Clause creation and destruction (`asserta/1`, `assertz/1`,
|
- [x] Clause creation and destruction (`asserta/1`, `assertz/1`,
|
||||||
`retract/1`, `abolish/1`) with logical update semantics.
|
`retract/1`, `abolish/1`) with logical update semantics.
|
||||||
@@ -49,16 +57,26 @@ Extend Scryer Prolog to include the following, among other features:
|
|||||||
`bb_put/2` (non-backtrackable) and `bb_b_put/2`
|
`bb_put/2` (non-backtrackable) and `bb_b_put/2`
|
||||||
(backtrackable).
|
(backtrackable).
|
||||||
- [x] Delimited continuations based on reset/3, shift/1 (documented in
|
- [x] Delimited continuations based on reset/3, shift/1 (documented in
|
||||||
"Delimited Continuations for Prolog").
|
"[Delimited Continuations for Prolog](https://biblio.ugent.be/publication/5646080/file/5646081)").
|
||||||
- [x] Tabling library based on delimited continuations
|
- [x] Tabling library based on delimited continuations
|
||||||
(documented in "Tabling as a Library with Delimited Control").
|
(documented in "[Tabling as a Library with Delimited Control](https://biblio.ugent.be/publication/6880648/file/6885145.pdf)").
|
||||||
- [x] A _redone_ representation of strings as difference lists of
|
- [x] A _redone_ representation of strings as difference lists of
|
||||||
characters, using a packed internal representation.
|
characters, using a packed internal representation.
|
||||||
- [x] clp(B) and clp(ℤ) as builtin libraries.
|
- [x] clp(B) and clp(ℤ) as builtin libraries.
|
||||||
- [ ] Streams and predicates for stream control (_in progress_).
|
- [x] Streams and predicates for stream control.
|
||||||
- [ ] A compacting garbage collector satisfying the five
|
- [x] A simple sockets library representing TCP connections as streams.
|
||||||
properties of "Precise Garbage Collection in Prolog."
|
- [x] Incremental compilation and loading process, newly written,
|
||||||
- [ ] Mode declarations.
|
primarily in Prolog.
|
||||||
|
- [ ] Improvements to the WAM compiler and heap representation:
|
||||||
|
- [ ] Replacing choice points pivoting on inlined semi-deterministic predicates
|
||||||
|
(`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.
|
||||||
|
- [x] Storing short atoms to heap cells without writing them to the atom table.
|
||||||
|
- [ ] Configurable JIT/on-demand indexing over all arguments
|
||||||
|
(documented in "[Demand-Driven Indexing of Prolog Clauses](https://user.it.uu.se/~kostis/Papers/iclp07.pdf)"). (_in progress_)
|
||||||
|
- [ ] 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_)
|
||||||
|
|
||||||
## Phase 3
|
## Phase 3
|
||||||
|
|
||||||
@@ -76,22 +94,29 @@ nice to have in the future. They'd make a good project for anyone wanting
|
|||||||
to contribute code to Scryer Prolog.
|
to contribute code to Scryer Prolog.
|
||||||
|
|
||||||
1. Implement the global analysis techniques described in Peter van
|
1. Implement the global analysis techniques described in Peter van
|
||||||
Roy's thesis, "Can Logic Programming Execute as Fast as Imperative
|
Roy's thesis, "[Can Logic Programming Execute as Fast as Imperative
|
||||||
Programming?"
|
Programming?](https://www.info.ucl.ac.be/~pvr/Peter.thesis/Peter.thesis.html)"
|
||||||
|
|
||||||
2. Add unum representation and arithmetic, using either an existing
|
2. Add unum representation and arithmetic, using either an existing
|
||||||
unum implementation or an ad hoc one. Unums are described in
|
unum implementation or an ad hoc one. Unums are described in
|
||||||
Gustafson's book "The End of Error."
|
Gustafson's book "[The End of Error](http://www.johngustafson.net/unums.html)."
|
||||||
|
|
||||||
3. Add concurrent tables to manage shared references to atoms and
|
3. Add concurrent tables to manage shared references to atoms and
|
||||||
strings.
|
strings.
|
||||||
|
|
||||||
4. Add some form of JIT predicate indexing.
|
|
||||||
|
|
||||||
## Installing Scryer Prolog
|
## Installing Scryer Prolog
|
||||||
|
|
||||||
|
### Binaries
|
||||||
|
|
||||||
|
Precompiled binaries for several platforms are available for download
|
||||||
|
at:
|
||||||
|
|
||||||
|
**https://github.com/mthom/scryer-prolog/releases/latest**
|
||||||
|
|
||||||
|
### Native Compilation
|
||||||
|
|
||||||
First, install the latest stable version of
|
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
|
preferred method. Scryer tends to use features from newer Rust
|
||||||
releases, whereas Rust packages in Linux distributions, Macports,
|
releases, whereas Rust packages in Linux distributions, Macports,
|
||||||
etc. tend to lag behind. [rustup](http://rustup.rs) will keep your
|
etc. tend to lag behind. [rustup](http://rustup.rs) will keep your
|
||||||
@@ -99,90 +124,293 @@ Rust updated to the latest stable release; any existing Rust
|
|||||||
distribution should be uninstalled from your system before rustup is
|
distribution should be uninstalled from your system before rustup is
|
||||||
used.
|
used.
|
||||||
|
|
||||||
Scryer Prolog can be installed with cargo, like so:
|
|
||||||
|
|
||||||
```
|
> [!NOTE]
|
||||||
$> cargo install scryer-prolog
|
> 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
|
||||||
|
|
||||||
cargo will download and install the libraries Scryer Prolog uses
|
### From a local git checkout
|
||||||
automatically from crates.io. You can find the `scryer-prolog`
|
|
||||||
executable in `~/.cargo/bin`.
|
|
||||||
|
|
||||||
Publishing Rust crates to crates.io and pushing to git are entirely
|
|
||||||
distinct, independent processes, so to be sure you have the latest
|
|
||||||
commit, it is recommended to clone directly from this git repository,
|
|
||||||
which can be done as follows:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$> git clone https://github.com/mthom/scryer-prolog
|
$> git clone https://github.com/mthom/scryer-prolog
|
||||||
$> cd scryer-prolog
|
$> cd scryer-prolog
|
||||||
$> cargo run [--release]
|
$> cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
The optional `--release` flag will perform various optimizations,
|
The `--release` flag performs various optimizations, producing a
|
||||||
producing a faster executable.
|
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 latest 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 references 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.
|
||||||
|
|
||||||
|
To build a Windows Installer, you'll need first Scryer Prolog compiled in release mode, then, with WiX Toolset installed, execute:
|
||||||
|
```
|
||||||
|
candle.exe scryer-prolog.wxs
|
||||||
|
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.
|
||||||
|
|
||||||
|
### Building WebAssembly
|
||||||
|
|
||||||
|
Scryer Prolog has basic WebAssembly support. You can follow `wasm-pack`'s [official instructions](https://rustwasm.github.io/docs/wasm-pack/quickstart.html) to install `wasm-pack` and build it in any way you like.
|
||||||
|
|
||||||
|
However, none of the [default features](https://doc.rust-lang.org/cargo/reference/features.html#the-default-feature) are currently supported. The preferred way of disabling them is passing [extra options](https://rustwasm.github.io/wasm-pack/book/commands/build.html#extra-options) to `wasm-pack`.
|
||||||
|
|
||||||
|
For example, if you want a minimal working package without using any bundler like `webpack`, you can do this:
|
||||||
|
```
|
||||||
|
wasm-pack build --target web -- --no-default-features
|
||||||
|
```
|
||||||
|
Then a `pkg` directory will be created, containing everything you need for a webapp. You can test whether the package is successfully built by creating an html file, adapted from `wasm-bindgen`'s [official example](https://rustwasm.github.io/wasm-bindgen/examples/without-a-bundler.html) like this:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Scryer Prolog - Sudoku Solver Example</title>
|
||||||
|
<script type="module">
|
||||||
|
import 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can serve it with your favorite http server like `python -m http.server` or `npx serve`, and access the page with your browser.
|
||||||
|
|
||||||
|
### Docker Install
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Once Docker is installed, you can download and run Scryer Prolog with a single
|
||||||
|
command:
|
||||||
|
```
|
||||||
|
$> docker run -it mjt128/scryer-prolog
|
||||||
|
```
|
||||||
|
|
||||||
|
To consult your Prolog files, bind mount your programs folder as a
|
||||||
|
[Docker volume](https://docs.docker.com/storage/volumes/):
|
||||||
|
|
||||||
|
```
|
||||||
|
$> docker run -v /home/user/prolog:/mnt -it mjt128/scryer-prolog
|
||||||
|
?- consult('/mnt/program.pl').
|
||||||
|
true.
|
||||||
|
```
|
||||||
|
|
||||||
|
This works on Windows too:
|
||||||
|
|
||||||
|
```
|
||||||
|
$> docker run -v C:\Users\user\Documents\prolog:/mnt -it mjt128/scryer-prolog
|
||||||
|
?- consult('/mnt/program.pl').
|
||||||
|
true.
|
||||||
|
```
|
||||||
|
|
||||||
## Tutorial
|
## Tutorial
|
||||||
To enter a multi-clause predicate, the directive "[user]" is used.
|
|
||||||
|
|
||||||
For example,
|
Prolog files are loaded by specifying them as arguments on the command
|
||||||
```
|
line. For example, to load `program.pl`, use:
|
||||||
?- [user].
|
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
|
||||||
p(f(f(X)), h(W), Y) :- g(W), h(W), f(X).
|
|
||||||
p(X, Y, Z) :- h(Y), z(Z).
|
|
||||||
?- [user].
|
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
|
||||||
h(x). h(y).
|
|
||||||
h(z).
|
|
||||||
```
|
|
||||||
In the example, `Enter + Ctrl-D` is used to terminate the standard
|
|
||||||
input stream. The instructive message is always printed.
|
|
||||||
|
|
||||||
Queries are issued as
|
|
||||||
```
|
```
|
||||||
?- p(X, Y, Z).
|
$> scryer-prolog program.pl
|
||||||
```
|
```
|
||||||
|
|
||||||
Pressing `SPACE` will backtrack through other possible answers, if any exist.
|
Loading a Prolog file is also called “consulting” it. The built-in
|
||||||
Pressing `.` will abort the search and return to the prompt.
|
predicate `consult/1` can be used to consult a file from within
|
||||||
|
Prolog:
|
||||||
|
|
||||||
Wildcards work as well:
|
```
|
||||||
|
?- consult('program.pl').
|
||||||
|
```
|
||||||
|
|
||||||
|
As an abbreviation for `consult/1`, you can specify a *list* of
|
||||||
|
program files, given as *atoms*:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- ['program.pl'].
|
||||||
|
```
|
||||||
|
|
||||||
|
The special notation `[user]` is used to read Prolog text from
|
||||||
|
standard input. For example,
|
||||||
|
|
||||||
```
|
```
|
||||||
?- [user].
|
?- [user].
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
hello(declarative_world).
|
||||||
member(X, [X|_]).
|
hello(pure_world).
|
||||||
member(X, [_|Xs]) :- member(X, Xs).
|
|
||||||
?- member(X, [a, b, c]).
|
|
||||||
X = a
|
|
||||||
; X = b
|
|
||||||
; X = c
|
|
||||||
; false.
|
|
||||||
```
|
|
||||||
and so do conjunctive queries:
|
|
||||||
```
|
|
||||||
?- [user].
|
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
|
||||||
f(X) :- g(X).
|
|
||||||
g(x). g(y). g(z).
|
|
||||||
h(call(f, X)).
|
|
||||||
?- h(X), X.
|
|
||||||
X = call(f,x)
|
|
||||||
; X = call(f,y)
|
|
||||||
; X = call(f,z).
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the values of variables belonging to successful queries are
|
Pressing `RETURN` followed by `Ctrl-d` stops reading from
|
||||||
printed out, on one line each. Uninstantiated variables are denoted by
|
standard input and consults the entered Prolog text.
|
||||||
a number preceded by an underscore (`X = _0` in an example above).
|
|
||||||
|
After a program is consulted, you can ask *queries* about the
|
||||||
|
predicates it defines. For example, with the program shown above:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- hello(What).
|
||||||
|
What = declarative_world
|
||||||
|
; What = pure_world.
|
||||||
|
```
|
||||||
|
|
||||||
|
Press `SPACE` to show further answers, if any exist. Press `RETURN`
|
||||||
|
or `.` to abort the search and return to the
|
||||||
|
toplevel prompt. Press `f` to see up to the next multiple of
|
||||||
|
5 answers, and `a` to see all answers. Press `h` to show a help
|
||||||
|
message.
|
||||||
|
|
||||||
|
Use `TAB` to complete atoms and predicate names in queries. For
|
||||||
|
instance, after consulting the program above, typing `decl` followed
|
||||||
|
by `TAB` yields `declarative_world`. Press `TAB` repeatedly
|
||||||
|
to cycle through alternative completions.
|
||||||
|
|
||||||
|
To quit Scryer Prolog, use the standard predicate `halt/0`:
|
||||||
|
|
||||||
To quit scryer-prolog, type
|
|
||||||
```
|
```
|
||||||
?- halt.
|
?- halt.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Starting Scryer Prolog
|
||||||
|
|
||||||
|
Scryer Prolog can be started from the command line by specifying
|
||||||
|
options, files and additional arguments. All components are optional:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
scryer-prolog [OPTIONS] [FILES] [-- ARGUMENTS]
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
The supported options are:
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help Display help message
|
||||||
|
-v, --version Print version information and exit
|
||||||
|
-g, --goal GOAL Run the query GOAL after consulting files
|
||||||
|
-f Fast startup. Do not load initialization file (~/.scryerrc)
|
||||||
|
--no-add-history Prevent adding input to history file (~/.scryer_history)
|
||||||
|
```
|
||||||
|
|
||||||
|
All specified Prolog files are consulted.
|
||||||
|
|
||||||
|
After Prolog files, application-specific arguments can be specified on
|
||||||
|
the command line. These arguments can be accessed from within Prolog
|
||||||
|
applications with the predicate `argv/1`, which yields the list
|
||||||
|
of arguments represented as strings.
|
||||||
|
|
||||||
|
Prolog files can also be turned into *shell scripts* as explained in
|
||||||
|
https://github.com/mthom/scryer-prolog/issues/2170#issuecomment-1821713993.
|
||||||
|
|
||||||
### Dynamic operators
|
### Dynamic operators
|
||||||
|
|
||||||
Scryer supports dynamic operators. Using the built-in
|
Scryer supports dynamic operators. Using the built-in
|
||||||
@@ -190,17 +418,68 @@ arithmetic operators with the usual precedences,
|
|||||||
|
|
||||||
```
|
```
|
||||||
?- write_canonical(-5 + 3 - (2 * 4) // 8), nl.
|
?- write_canonical(-5 + 3 - (2 * 4) // 8), nl.
|
||||||
-(+(-5,3),//(*(2,4),8))
|
-(+(-5,3),//(*(2,4),8))
|
||||||
true.
|
true.
|
||||||
```
|
```
|
||||||
|
|
||||||
New operators can be defined using the `op` declaration.
|
New operators can be defined using the `op` declaration.
|
||||||
|
|
||||||
|
### First instantiated argument indexing
|
||||||
|
|
||||||
|
Scryer Prolog indexes on the leftmost argument that is not a variable
|
||||||
|
in all clauses of a predicate's definition. We call this strategy
|
||||||
|
first *instantiated* argument indexing.
|
||||||
|
|
||||||
|
A key motivation for first instantiated argument indexing is to enable
|
||||||
|
indexing for meta-predicates such as `maplist/N` and `foldl/N`, whose
|
||||||
|
first argument is a partial goal that is a variable in the definition
|
||||||
|
of these predicates and therefore cannot be used for indexing.
|
||||||
|
|
||||||
|
For example, a natural definition of `maplist/2` reads:
|
||||||
|
|
||||||
|
```
|
||||||
|
maplist(_, []).
|
||||||
|
maplist(Goal_1, [L|Ls]) :-
|
||||||
|
call(Goal_1, L),
|
||||||
|
maplist(Goal_1, Ls).
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, first instantiated argument indexing automatically uses
|
||||||
|
the *second* argument for indexing, and thus prevents choicepoints for
|
||||||
|
calls with lists of fixed lengths (and deterministic goals).
|
||||||
|
Conveniently, no auxiliary predicates with reordered arguments are
|
||||||
|
needed to benefit from indexing in such cases.
|
||||||
|
|
||||||
|
Conventional first argument indexing naturally arises as a
|
||||||
|
special case of this strategy, if the first argument is instantiated
|
||||||
|
in any clause of a predicate's definition.
|
||||||
|
|
||||||
### Strings and partial strings
|
### Strings and partial strings
|
||||||
|
|
||||||
|
A very compact internal representation of *strings* is one of the key
|
||||||
|
innovations of Scryer Prolog. This means that terms which appear as
|
||||||
|
lists of characters to Prolog programs are stored in packed
|
||||||
|
UTF-8 encoding by the engine.
|
||||||
|
|
||||||
|
Without this innovation, storing a list of characters in memory would
|
||||||
|
use one WAM memory cell per character, one cell per list
|
||||||
|
constructor, and one cell for each tail that occurs in the list. Since
|
||||||
|
one cell takes 8 bytes in the WAM as implemented by
|
||||||
|
Scryer Prolog, the packed representation yields an up to
|
||||||
|
**24-fold reduction** of memory usage, and corresponding
|
||||||
|
reduction of memory accesses when creating and processing
|
||||||
|
strings.
|
||||||
|
|
||||||
|
Scryer Prolog's compact internal string representation makes it
|
||||||
|
ideally suited for the use case Prolog was originally developed for:
|
||||||
|
efficient and convenient text processing, especially with definite
|
||||||
|
clause grammars (DCGs) as provided by
|
||||||
|
[`library(dcgs)`](src/lib/dcgs.pl) and
|
||||||
|
[`library(pio)`](src/lib/pio.pl) to transparently apply DCGs to files.
|
||||||
|
|
||||||
In Scryer Prolog, the default value of the Prolog flag `double_quotes`
|
In Scryer Prolog, the default value of the Prolog flag `double_quotes`
|
||||||
is `chars`, which is also the recommended setting. This means that
|
is `chars`, which is also the recommended setting. This means that
|
||||||
double-quoted strings are interpreted as lists of *characters*, in the
|
lists of characters can be written as double-quoted strings, in the
|
||||||
tradition of Marseille Prolog.
|
tradition of Marseille Prolog.
|
||||||
|
|
||||||
For example, the following query succeeds:
|
For example, the following query succeeds:
|
||||||
@@ -210,15 +489,9 @@ For example, the following query succeeds:
|
|||||||
true.
|
true.
|
||||||
```
|
```
|
||||||
|
|
||||||
Internally, strings are represented very compactly in packed
|
This shows that the string `"abc"`, which is represented as a sequence
|
||||||
UTF-8 encoding. A naive representation of strings as lists of
|
of 3 bytes internally, appears to Prolog programs as a list of
|
||||||
characters would use one memory cell per character, one
|
characters.
|
||||||
memory cell per list constructor, and one memory cell for
|
|
||||||
each tail that occurs in the list. Since one memory cell takes
|
|
||||||
8 bytes on 64-bit machines, the packed representation used by
|
|
||||||
Scryer Prolog yields an up to **24-fold reduction** of
|
|
||||||
memory usage, and corresponding reduction of memory accesses when
|
|
||||||
creating and processing strings.
|
|
||||||
|
|
||||||
Scryer Prolog uses the same efficient encoding for *partial* strings,
|
Scryer Prolog uses the same efficient encoding for *partial* strings,
|
||||||
which appear to Prolog code as partial lists of characters. The
|
which appear to Prolog code as partial lists of characters. The
|
||||||
@@ -241,15 +514,120 @@ the above example, posting <tt>Ls0 = [a,b,c|Ls]</tt> yields
|
|||||||
the exact same internal representation, and has the advantage that
|
the exact same internal representation, and has the advantage that
|
||||||
only the standard predicate `(=)/2` is used.
|
only the standard predicate `(=)/2` is used.
|
||||||
|
|
||||||
Definite clause grammars as provided by `library(dcgs)` are ideally
|
The efficient internal representation of strings and partial strings
|
||||||
suited for reasoning about strings.
|
was first proposed and explained by Ulrich Neumerkel in
|
||||||
|
issues [#24](https://github.com/mthom/scryer-prolog/issues/24)
|
||||||
|
and [#95](https://github.com/mthom/scryer-prolog/issues/95), and
|
||||||
|
Scryer Prolog is the first Prolog system that implements it.
|
||||||
|
|
||||||
|
### Occurs check and cyclic terms
|
||||||
|
|
||||||
|
The *occurs check* is an element of algorithms that perform
|
||||||
|
syntactic unification, causing the unification to fail if a variable
|
||||||
|
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, unifications which succeed only if the
|
||||||
|
*occurs check* is omitted yield *cyclic terms*, also called
|
||||||
|
*rational trees*. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- X = f(X), Y = g(X,Y).
|
||||||
|
X = f(X), Y = g(f(X),Y).
|
||||||
|
```
|
||||||
|
|
||||||
|
The creation of cyclic terms often indicates a programming mistake in
|
||||||
|
the formulation of Prolog predicates, and to obtain logically sound
|
||||||
|
results it is desirable to either perform all unifications with
|
||||||
|
*occurs check* enabled, or let Prolog throw an error if enabling
|
||||||
|
the *occurs check* is necessary to prevent a unification.
|
||||||
|
|
||||||
|
Scryer Prolog supports this via the Prolog flag `occurs_check`. It can
|
||||||
|
be set to one of the following values to obtain the desired behaviour:
|
||||||
|
|
||||||
|
- `false`
|
||||||
|
Do not perform the *occurs check*. This is the default.
|
||||||
|
- `true`
|
||||||
|
Perform all unifications with the *occurs check* enabled.
|
||||||
|
- `error`
|
||||||
|
Yield an error if a unification is performed that the
|
||||||
|
*occurs check* would have prevented.
|
||||||
|
|
||||||
|
Especially when starting with Prolog, we recommend to add the
|
||||||
|
following directive to the `~/.scryerrc` configuration file so that
|
||||||
|
programming mistakes in predicates that lead to the creation of cyclic
|
||||||
|
terms are indicated by errors:
|
||||||
|
|
||||||
|
```
|
||||||
|
:- set_prolog_flag(occurs_check, error).
|
||||||
|
```
|
||||||
|
|
||||||
|
Scryer Prolog implements specialized reasoning to make unifications
|
||||||
|
fast in many frequently occurring situations also if the
|
||||||
|
*occurs check* is enabled.
|
||||||
|
|
||||||
|
### Tabling (SLG resolution)
|
||||||
|
|
||||||
|
One of the foremost attractions of Prolog is that logical consequences
|
||||||
|
of pure programs can be derived by various execution strategies
|
||||||
|
that differ regarding essential properties such as termination,
|
||||||
|
completeness and efficiency.
|
||||||
|
|
||||||
|
The default execution strategy of Prolog is depth-first search with
|
||||||
|
chronological backtracking. This strategy is very efficient. Its main
|
||||||
|
drawback is that it is *incomplete*: It may fail to find any solution
|
||||||
|
even if one exists.
|
||||||
|
|
||||||
|
Scryer Prolog supports an alternative execution strategy which is
|
||||||
|
called *tabling* and also known as tabled execution and
|
||||||
|
SLG resolution. To enable tabled execution for a predicate, use
|
||||||
|
[`library(tabling)`](src/lib/tabling.pl) and add a `(table)/1`
|
||||||
|
directive for the desired predicate indicator. For example, if we
|
||||||
|
write:
|
||||||
|
|
||||||
|
```
|
||||||
|
:- use_module(library(tabling)).
|
||||||
|
:- table a/0.
|
||||||
|
|
||||||
|
a :- a.
|
||||||
|
```
|
||||||
|
|
||||||
|
Then the query `?- a.` *terminates* (and fails), whereas it
|
||||||
|
does not terminate with the default execution strategy.
|
||||||
|
|
||||||
|
Scryer Prolog implements tabling via *delimited continuations* as
|
||||||
|
described in [*Tabling as a Library with Delimited
|
||||||
|
Control*](https://biblio.ugent.be/publication/6880648/file/6885145.pdf)
|
||||||
|
by Desouter et. al.
|
||||||
|
|
||||||
|
### Constraint Logic Programming (CLP)
|
||||||
|
|
||||||
|
Scryer Prolog provides excellent support for Constraint Logic
|
||||||
|
Programming (CLP), which is the amalgamation of
|
||||||
|
Logic Programming (LP) and Constraints.
|
||||||
|
|
||||||
|
In addition to built-in support for [`dif/2`](src/lib/dif.pl),
|
||||||
|
[`freeze/2`](src/lib/freeze.pl),
|
||||||
|
[CLP(B)](src/lib/clpb.pl) and [CLP(ℤ)](src/lib/clpz.pl),
|
||||||
|
Scryer provides a convenient way to implement new user-defined
|
||||||
|
constraints: *Attributed variables* are available via
|
||||||
|
[`library(atts)`](src/lib/atts.pl) as in SICStus Prolog,
|
||||||
|
which is one of the most sophisticated and fastest constraint systems
|
||||||
|
in existence. In [`library(iso_ext)`](src/lib/iso_ext.pl),
|
||||||
|
Scryer provides predicates for backtrackable (`bb_b_put/2`) and
|
||||||
|
non-backtrackable (`bb_put/2`) global variables, which are needed to
|
||||||
|
implement certain types of constraint solvers.
|
||||||
|
|
||||||
|
These features make Scryer Prolog an ideal platform for teaching,
|
||||||
|
learning and developing portable CLP applications.
|
||||||
|
|
||||||
### Modules
|
### Modules
|
||||||
|
|
||||||
Scryer has a simple predicate-based module system. It provides a
|
Scryer has a simple predicate-based module system. It provides a
|
||||||
way to separate units of code into distinct namespaces, for both
|
way to separate units of code into distinct namespaces, for both
|
||||||
predicates and operators. See the files
|
predicates and operators. See the files
|
||||||
[`src/prolog/lib/*.pl`](src/prolog/lib) for
|
[`src/lib/*.pl`](src/lib) for
|
||||||
examples.
|
examples.
|
||||||
|
|
||||||
At the time of this writing, many predicates reside in their own
|
At the time of this writing, many predicates reside in their own
|
||||||
@@ -257,31 +635,31 @@ modules that need to be imported before they can be used.
|
|||||||
The modules that ship with Scryer Prolog are also called
|
The modules that ship with Scryer Prolog are also called
|
||||||
*library* modules or *libraries*, and include:
|
*library* modules or *libraries*, and include:
|
||||||
|
|
||||||
* [`lists`](src/prolog/lib/lists.pl)
|
* [`lists`](src/lib/lists.pl)
|
||||||
providing `length/2`, `member/2`, `select/3`, `append/[2,3]`,
|
providing `length/2`, `member/2`, `select/3`, `append/[2,3]`,
|
||||||
`foldl/[4,5]`, `maplist/[2-9]`, `same_length/2`, `transpose/2` etc.
|
`foldl/[4,5]`, `maplist/[2-9]`, `same_length/2`, `transpose/2` etc.
|
||||||
* [`dcgs`](src/prolog/lib/dcgs.pl)
|
* [`dcgs`](src/lib/dcgs.pl)
|
||||||
Definite Clause Grammars (DCGs), a built-in grammar mechanism
|
Definite Clause Grammars (DCGs), a built-in grammar mechanism
|
||||||
that uses the operator `(-->)/2` to define grammar rules,
|
that uses the operator `(-->)/2` to define grammar rules,
|
||||||
and the predicates `phrase/[2,3]` to invoke them.
|
and the predicates `phrase/[2,3]` to invoke them.
|
||||||
* [`dif`](src/prolog/lib/dif.pl)
|
* [`dif`](src/lib/dif.pl)
|
||||||
The predicate `dif/2` provides declarative disequality:
|
The predicate `dif/2` provides declarative disequality:
|
||||||
It is true if and only if its arguments are different, and
|
It is true if and only if its arguments are different, and
|
||||||
delays the test until a sound decision can be made.
|
delays the test until a sound decision can be made.
|
||||||
* [`reif`](src/prolog/lib/reif.pl)
|
* [`reif`](src/lib/reif.pl)
|
||||||
providing `if_/3`, `tfilter/3` and related predicates
|
providing `if_/3`, `tfilter/3` and related predicates
|
||||||
as described in *Indexing dif/2*.
|
as described in *Indexing dif/2*.
|
||||||
* [`clpz`](src/prolog/lib/clpz.pl)
|
* [`clpz`](src/lib/clpz.pl)
|
||||||
CLP(ℤ): Constraint Logic Programming over Integers,
|
CLP(ℤ): Constraint Logic Programming over Integers,
|
||||||
providing declarative integer arithmetic via `(#=)/2`, `(#\=)/2`,
|
providing declarative integer arithmetic via `(#=)/2`, `(#\=)/2`,
|
||||||
`(#>=)/2` etc., and various global constraints and
|
`(#>=)/2` etc., and various global constraints and
|
||||||
enumeration predicates for solving combinatorial tasks.
|
enumeration predicates for solving combinatorial tasks.
|
||||||
* [`pairs`](src/prolog/lib/pairs.pl)
|
* [`pairs`](src/lib/pairs.pl)
|
||||||
By convention, *pairs* are Prolog terms with
|
By convention, *pairs* are Prolog terms with
|
||||||
principal functor `(-)/2`, written as `Key-Value`.
|
principal functor `(-)/2`, written as `Key-Value`.
|
||||||
This library provides `pairs_keys_values/3`,
|
This library provides `pairs_keys_values/3`,
|
||||||
`pairs_keys/2`, and other predicates to reason about pairs.
|
`pairs_keys/2`, and other predicates to reason about pairs.
|
||||||
* [`si`](src/prolog/lib/si.pl)
|
* [`si`](src/lib/si.pl)
|
||||||
The predicates `atom_si/1`, `integer_si/1`, `atomic_si/1`
|
The predicates `atom_si/1`, `integer_si/1`, `atomic_si/1`
|
||||||
and `list_si/1` implement sound type checks. They raise
|
and `list_si/1` implement sound type checks. They raise
|
||||||
instantiation errors if no decision can be made.
|
instantiation errors if no decision can be made.
|
||||||
@@ -290,25 +668,109 @@ The modules that ship with Scryer Prolog are also called
|
|||||||
write `integer_si(X)` to ensure soundness of your programs.
|
write `integer_si(X)` to ensure soundness of your programs.
|
||||||
"si" stands for *sufficiently instantiated*, and also for
|
"si" stands for *sufficiently instantiated*, and also for
|
||||||
*sound inference*.
|
*sound inference*.
|
||||||
* [`error`](src/prolog/lib/error.pl)
|
* [`debug`](src/lib/debug.pl)
|
||||||
`must_be/2` and `can_be/2` complement the type checks provided
|
Various predicates that allow for declarative debugging.
|
||||||
by `library(si)`, and are especially useful for Prolog library
|
* [`pio`](src/lib/pio.pl)
|
||||||
authors.
|
`phrase_from_file/2` applies a DCG nonterminal to the contents of a
|
||||||
* [`tabling`](src/prolog/lib/tabling.pl)
|
file, reading lazily only as much as is needed. Due to the compact
|
||||||
|
internal string representation, also extremely large files can be
|
||||||
|
efficiently processed with Scryer Prolog in this way.
|
||||||
|
`phrase_to_file/2` and `phrase_to_stream/2` write lists of
|
||||||
|
characters described by DCGs to files and streams, respectively.
|
||||||
|
* [`lambda`](src/lib/lambda.pl)
|
||||||
|
Lambda expressions to simplify higher order programming.
|
||||||
|
* [`charsio`](src/lib/charsio.pl) Various predicates that are useful
|
||||||
|
for parsing and reasoning about characters, notably `char_type/2` to
|
||||||
|
classify characters according to their type, and conversion
|
||||||
|
predicates for different encodings of strings.
|
||||||
|
* [`error`](src/lib/error.pl)
|
||||||
|
`must_be/2` and `can_be/2` complement the type checks provided by
|
||||||
|
[`library(si)`](src/lib/si.pl), and are especially useful for
|
||||||
|
Prolog library authors.
|
||||||
|
* [`tabling`](src/lib/tabling.pl)
|
||||||
The operator `(table)/1` is used in directives that prepare
|
The operator `(table)/1` is used in directives that prepare
|
||||||
predicates for tabled execution (SLG resolution).
|
predicates for tabled execution (SLG resolution).
|
||||||
* [`format`](src/prolog/lib/format.pl)
|
* [`format`](src/lib/format.pl)
|
||||||
The nonterminal `format_//2` is used to describe formatted output,
|
The nonterminal `format_//2` is used to describe formatted output,
|
||||||
arranging arguments according to a given format string.
|
arranging arguments according to a given format string.
|
||||||
The predicate `format/2` is provided for impure output.
|
The predicates `format/[2,3]`, `portray_clause/[1,2]` and `listing/1`
|
||||||
* [`assoc`](src/prolog/lib/assoc.pl)
|
provide formatted *impure* output.
|
||||||
|
* [`assoc`](src/lib/assoc.pl)
|
||||||
providing `empty_assoc/1`, `get_assoc/3`, `put_assoc/4` etc.
|
providing `empty_assoc/1`, `get_assoc/3`, `put_assoc/4` etc.
|
||||||
to manage elements in AVL trees which ensure
|
to manage elements in AVL trees which ensure
|
||||||
*O*(log(*N*)) access.
|
*O*(log(*N*)) access.
|
||||||
* [`clpb`](src/prolog/lib/clpb.pl)
|
* [`ordsets`](src/lib/ordsets.pl)
|
||||||
|
represents ordered sets as lists.
|
||||||
|
* [`clpb`](src/lib/clpb.pl)
|
||||||
CLP(B): Constraint Logic Programming over Boolean variables,
|
CLP(B): Constraint Logic Programming over Boolean variables,
|
||||||
a BDD-based SAT solver provided via the predicates
|
a BDD-based SAT solver provided via the predicates
|
||||||
`sat/1`, `taut/2`, `labeling/1` etc.
|
`sat/1`, `taut/2`, `labeling/1` etc.
|
||||||
|
* [`arithmetic`](src/lib/arithmetic.pl)
|
||||||
|
Arithmetic predicates such as `lsb/2`, `msb/2` and
|
||||||
|
`number_to_rational/2`.
|
||||||
|
* [`time`](src/lib/time.pl) Predicates for reasoning about
|
||||||
|
time, including `time/1` to measure the CPU time of a goal,
|
||||||
|
`current_time/1` to obtain the current system time, the nonterminal
|
||||||
|
`format_time//2` to describe strings with dates and times, and
|
||||||
|
`sleep/1` to slow down a computation.
|
||||||
|
* [`files`](src/lib/files.pl)
|
||||||
|
Predicates for reasoning about files and directories, such as
|
||||||
|
`directory_files/2`, `file_exists/1` and `file_size/2`.
|
||||||
|
* [`cont`](src/lib/cont.pl)
|
||||||
|
Provides *delimited continuations* via `reset/3` and `shift/1`.
|
||||||
|
* [`random`](src/lib/random.pl)
|
||||||
|
Probabilistic predicates and random number generators.
|
||||||
|
* [`http/http_open`](src/lib/http/http_open.pl) Open a stream to
|
||||||
|
read answers from web servers. HTTPS is also supported.
|
||||||
|
* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.1 and HTTP/2.0 web server. Uses [Warp](https://github.com/seanmonstar/warp) as a backend. Supports some query and form handling.
|
||||||
|
* [`sgml`](src/lib/sgml.pl)
|
||||||
|
`load_html/3` and `load_xml/3` represent HTML and XML documents
|
||||||
|
as Prolog terms for convenient and efficient reasoning. Use
|
||||||
|
[`library(xpath)`](src/lib/iso_ext.pl) to extract information from
|
||||||
|
parsed documents.
|
||||||
|
* [`csv`](src/lib/csv.pl)
|
||||||
|
`parse_csv//1` and `parse_csv//2` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
|
||||||
|
or [`phrase/2`](src/lib/dcgs.pl) to parse csv
|
||||||
|
* [`serialization/abnf`](src/lib/serialization/abnf.pl)
|
||||||
|
DCGs describing the
|
||||||
|
[ABNF grammar core (RFC 5234)](https://tools.ietf.org/html/rfc5234#appendix-B.1),
|
||||||
|
which is used to describe many [IETF](https://www.ietf.org/standards/rfcs/)
|
||||||
|
syntaxes, such as [HTTP v1.1](https://www.rfc-editor.org/rfc/rfc7230.html#page-82),
|
||||||
|
[SMTP](https://www.rfc-editor.org/rfc/rfc5321.html),
|
||||||
|
[iCalendar](https://www.rfc-editor.org/rfc/rfc5545.html), and more.
|
||||||
|
* [`serialization/json`](src/lib/serialization/json.pl)
|
||||||
|
`json_chars//1` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
|
||||||
|
or [`phrase/2`](src/lib/dcgs.pl) to parse and generate
|
||||||
|
[JSON](https://www.json.org/json-en.html).
|
||||||
|
* [`xpath`](src/lib/xpath.pl)
|
||||||
|
The predicate `xpath/3` is used for convenient reasoning about HTML
|
||||||
|
and XML documents, inspired by the XPath language. This library
|
||||||
|
is often used together with [`library(sgml)`](src/lib/sgml.pl).
|
||||||
|
* [`sockets`](src/lib/sockets.pl)
|
||||||
|
Predicates for opening and accepting TCP connections as streams.
|
||||||
|
* [`os`](src/lib/os.pl)
|
||||||
|
Predicates for reasoning about environment variables.
|
||||||
|
* [`iso_ext`](src/lib/iso_ext.pl)
|
||||||
|
Conforming extensions to and candidates for inclusion in the Prolog
|
||||||
|
ISO standard, such as `setup_call_cleanup/3`, `call_nth/2` and
|
||||||
|
`call_with_inference_limit/3`.
|
||||||
|
* [`crypto`](src/lib/crypto.pl)
|
||||||
|
Cryptographically secure random numbers and hashes, HMAC-based key
|
||||||
|
derivation (HKDF), password-based key derivation (PBKDF2),
|
||||||
|
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
|
||||||
|
programming problems.
|
||||||
|
|
||||||
To use predicates provided by the `lists` library, write:
|
To use predicates provided by the `lists` library, write:
|
||||||
|
|
||||||
@@ -345,7 +807,6 @@ REPL:
|
|||||||
|
|
||||||
```
|
```
|
||||||
?- [user].
|
?- [user].
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
|
||||||
:- module(test, [local_member/2]).
|
:- module(test, [local_member/2]).
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
@@ -354,3 +815,103 @@ local_member(X, Xs) :- member(X, Xs).
|
|||||||
|
|
||||||
The user listing can also be terminated by placing `end_of_file.` at
|
The user listing can also be terminated by placing `end_of_file.` at
|
||||||
the end of the stream.
|
the end of the stream.
|
||||||
|
|
||||||
|
### Configuration file
|
||||||
|
|
||||||
|
At startup, Scryer Prolog consults the file `~/.scryerrc`, if the file
|
||||||
|
exists. This file is useful to automatically load libraries and define
|
||||||
|
predicates that you need often.
|
||||||
|
|
||||||
|
For example, a sensible starting point for `~/.scryerrc` is:
|
||||||
|
|
||||||
|
```
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(reif)).
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development environment
|
||||||
|
|
||||||
|
To write and edit Prolog programs, we recommend
|
||||||
|
[GNU Emacs](https://www.gnu.org/software/emacs/) with the
|
||||||
|
[Prolog mode](https://bruda.ca/emacs/prolog_mode_for_emacs)
|
||||||
|
maintained by Stefan Bruda.
|
||||||
|
|
||||||
|
Use [ediprolog](https://www.metalevel.at/ediprolog/) to consult
|
||||||
|
Prolog code and evaluate Prolog queries in arbitrary
|
||||||
|
Emacs buffers.
|
||||||
|
|
||||||
|
Emacs definitions that show Prolog terms as trees are available
|
||||||
|
in [tools](tools).
|
||||||
|
|
||||||
|
To *debug* Prolog code, we recommend the predicates from
|
||||||
|
[**`library(debug)`**](src/lib/debug.pl), most notably:
|
||||||
|
|
||||||
|
- `(*)/1` to *"generalize away"* a Prolog goal. Use it to debug
|
||||||
|
unexpected failures by generalizing your definitions until they
|
||||||
|
succeed. Simply place `*` in front of a goal to generalize it away.
|
||||||
|
- `($)/1` to emit a *trace* of the execution, showing when a goal
|
||||||
|
is invoked, and when it has succeeded. Place `$` in front of a goal
|
||||||
|
to emit this information for that goal.
|
||||||
|
|
||||||
|
This way of debugging Prolog code has several major benefits, such as:
|
||||||
|
It stays close to the actual Prolog code under consideration, it does
|
||||||
|
not need additional tools and formalisms for its application, and
|
||||||
|
further, it encourages declarative reasoning that can in principle
|
||||||
|
also be performed automatically.
|
||||||
|
|
||||||
|
## Applications
|
||||||
|
|
||||||
|
Scryer Prolog's strong commitment to the Prolog ISO standard makes it
|
||||||
|
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:
|
||||||
|
|
||||||
|
- [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)
|
||||||
|
- the core tax engine for VAT reasoning and compliance of the
|
||||||
|
Belgian company [VATmiraal](https://vatmiraal.be/)
|
||||||
|
- 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).
|
||||||
|
- [Scryer Prolog Meetup 2025](https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2025/)
|
||||||
|
in Düsseldorf, Germany. Its [announcement and discussion](https://github.com/mthom/scryer-prolog/discussions/2948).
|
||||||
|
- **Attend, present and represent:** The [Scryer Prolog Meetup 2026](https://www.digitalaustria.gv.at/wissenswertes/events/scryerprologmeetup2026.html)
|
||||||
|
will take place on Oct. 24th and 25th 2026 in Vienna, Austria.
|
||||||
|
Its [announcement](https://github.com/mthom/scryer-prolog/discussions/3327).
|
||||||
|
|
||||||
|
## Support and discussions
|
||||||
|
|
||||||
|
If Scryer Prolog crashes or yields unexpected errors, consider filing
|
||||||
|
an [issue](https://github.com/mthom/scryer-prolog/issues).
|
||||||
|
|
||||||
|
To get in touch with the Scryer Prolog community, participate in
|
||||||
|
[discussions](https://github.com/mthom/scryer-prolog/discussions)
|
||||||
|
or visit our #scryer IRC channel on [Libera](https://libera.chat)!
|
||||||
|
|||||||
35
_typos.toml
Normal file
35
_typos.toml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# config for https://github.com/crate-ci/typos
|
||||||
|
|
||||||
|
[default]
|
||||||
|
# example from https://github.com/crate-ci/typos/blob/master/docs/reference.md#example-configurations
|
||||||
|
extend-ignore-re = [
|
||||||
|
"(#|//)\\s*spellchecker:ignore-next-line\\n.*"
|
||||||
|
]
|
||||||
|
|
||||||
|
# correct word key to value
|
||||||
|
# can be used to ignore a typo by adding an entry <typo> = "<typo>"
|
||||||
|
[default.extend-words]
|
||||||
|
|
||||||
|
# correct identifier key to value
|
||||||
|
# can be used to ignore a typo by adding an entry <typo> = "<typo>"
|
||||||
|
[default.extend-identifiers]
|
||||||
|
interm = "interm"
|
||||||
|
IntermReg = "IntermReg"
|
||||||
|
|
||||||
|
[type.rust]
|
||||||
|
extend-glob = [ "*.rs" ]
|
||||||
|
[type.rust.extend-identifiers]
|
||||||
|
consts = "consts" # std::{f32,f64}::consts
|
||||||
|
|
||||||
|
|
||||||
|
[type.prolog]
|
||||||
|
extend-glob = ["*.pl"]
|
||||||
|
check-file = false
|
||||||
|
|
||||||
|
[type.stdout]
|
||||||
|
extend-glob = ["*.stdout"]
|
||||||
|
check-file = false
|
||||||
|
|
||||||
|
|
||||||
|
[files]
|
||||||
|
extend-exclude = ["lib_integration_test_commands.txt"]
|
||||||
95
benches/README.md
Normal file
95
benches/README.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# About benches
|
||||||
|
|
||||||
|
The `benches` directory contains benchmarks that test scryer-prolog performance.
|
||||||
|
|
||||||
|
Benchmarks are run via two harnesses:
|
||||||
|
|
||||||
|
* `criterion` - criterion performs statistical analysis of benchmark runs and is
|
||||||
|
great for benchmarking locally.
|
||||||
|
* `iai-callgrind` - this runs the benchmark with callgrind, which is able to
|
||||||
|
precisely track the number of instructions executed during the run. This is
|
||||||
|
especially helpful in a public CI runner context where neighboring VMs can
|
||||||
|
cause a very high wall time variance. While instructions executed is only
|
||||||
|
correlated with the desired metric (wall time), this is a good tradeoff for CI
|
||||||
|
where that metric is unreliable.
|
||||||
|
|
||||||
|
Run benchmarks with the following commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo bench --bench run_criterion
|
||||||
|
|
||||||
|
# run a particular criterion benchmark
|
||||||
|
cargo bench --bench run_criterion -- <benchmark_name>
|
||||||
|
|
||||||
|
# run in profiling mode which outputs flamegraphs. Set profile time in seconds:
|
||||||
|
cargo bench --bench run_criterion -- --profile-time <time>
|
||||||
|
|
||||||
|
# to run iai, you need valgrind installed and to install iai-callgrind-runner
|
||||||
|
# at the same version as is in Cargo.toml:
|
||||||
|
cargo install iai-callgrind-runner --version 0.7.3
|
||||||
|
|
||||||
|
cargo bench --bench run_iai
|
||||||
|
```
|
||||||
|
|
||||||
|
For consistency, both runners -- `run_iai.rs` and `run_criterion.rs` -- import
|
||||||
|
the same setup code from `setup.rs`.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
`setup.rs` contains the setup code to run benchmarks. `fn prolog_benches()` at
|
||||||
|
the top of the file is where the benchmarks are defined.
|
||||||
|
|
||||||
|
Benchmarks are organized around running queries against a prolog module file.
|
||||||
|
Before a benchmark starts, `benchmark.setup()` is called which reads the module
|
||||||
|
file and initializes a new `scryer_prolog::machine::Machine`.
|
||||||
|
|
||||||
|
Each benchmark measurement is done by running a query against the machine. In
|
||||||
|
the case of criterion each query is run many times, in the case of iai it's run
|
||||||
|
once.
|
||||||
|
|
||||||
|
## Adding benchmarks
|
||||||
|
|
||||||
|
This design is meant to support defining lots of benchmarks.
|
||||||
|
|
||||||
|
To add a new benchmark:
|
||||||
|
|
||||||
|
* Add a new file `benches/[module].pl` that contains setup prolog code. Import
|
||||||
|
libraries, define predicates, etc.
|
||||||
|
* Add a new section in `setup.rs::prolog_benchmarks()` that refers to to the
|
||||||
|
file and write a query to be benchmarked.
|
||||||
|
* If the query mutates the machine, then use `Strategy::Fresh` so the criterion
|
||||||
|
benchmark will recreate a new machine for each benchmark run, otherwise use
|
||||||
|
`Strategy::Reuse` which has lower overhead. (This is not used by the iai
|
||||||
|
benchmark because it only runs once anyway.)
|
||||||
|
|
||||||
|
Some tips:
|
||||||
|
|
||||||
|
* The goal of benchmarking is to know if a library or engine change improved
|
||||||
|
performance or not.
|
||||||
|
* Once a benchmark is defined and named, avoid changing it's definition. If a
|
||||||
|
benchmark needs to change to be more useful, give the new definition a new
|
||||||
|
name instead. This will prevent charts from showing wild changes in
|
||||||
|
performance just because the definition changed (see previous).
|
||||||
|
* Aim for queries to execute in less than 0.5s realtime. Longer runtimes make it
|
||||||
|
easier for humans to see big differences, but benchmarks either run 10x slower
|
||||||
|
(iai) or execute repeatedly to attain statistical significance (criterion) and
|
||||||
|
in both cases benchmarking queries that take longer than about 0.5s are
|
||||||
|
cumbersome to run.
|
||||||
|
* Consider that the library runtime actually parses the text output of the top
|
||||||
|
level. So don't use custom outputs or it will fail to parse. Also keep the
|
||||||
|
output small so it doesn't just benchmark the output parsing code.
|
||||||
|
* DO test the output of the benchmark run, we don't want to count broken
|
||||||
|
benchmarks.
|
||||||
|
|
||||||
|
## CI
|
||||||
|
|
||||||
|
Both benchmark harnesses are run in `.github/workflows/ci.yaml` in the `report`
|
||||||
|
job, and the results are published as build artifacts.
|
||||||
|
|
||||||
|
## Todo
|
||||||
|
|
||||||
|
- [ ] Currently, the execution time to load a module is not benchmarked. It
|
||||||
|
would be nice to have at least one benchmark for loading a module (probably a
|
||||||
|
big one).
|
||||||
|
- [ ] Write a new action that downloads the test and benchmark results
|
||||||
|
artifacts, plots them over time, and publishes a report to github pages.
|
||||||
41
benches/csv.pl
Normal file
41
benches/csv.pl
Normal file
File diff suppressed because one or more lines are too long
130
benches/edges.pl
Normal file
130
benches/edges.pl
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
:- use_module(library(clpb)).
|
||||||
|
:- use_module(library(assoc)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(pairs)).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Contiguous United States and DC as they appear in SGB:
|
||||||
|
http://www-cs-faculty.stanford.edu/~uno/sgb.html
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
edge(al, fl).
|
||||||
|
edge(al, ga).
|
||||||
|
edge(al, ms).
|
||||||
|
edge(al, tn).
|
||||||
|
edge(ar, la).
|
||||||
|
edge(ar, mo).
|
||||||
|
edge(ar, ms).
|
||||||
|
edge(ar, ok).
|
||||||
|
edge(ar, tn).
|
||||||
|
edge(ar, tx).
|
||||||
|
edge(az, ca).
|
||||||
|
edge(az, nm).
|
||||||
|
edge(az, nv).
|
||||||
|
edge(az, ut).
|
||||||
|
edge(ca, nv).
|
||||||
|
edge(ca, or).
|
||||||
|
edge(co, ks).
|
||||||
|
edge(co, ne).
|
||||||
|
edge(co, nm).
|
||||||
|
edge(co, ok).
|
||||||
|
edge(co, ut).
|
||||||
|
edge(co, wy).
|
||||||
|
edge(ct, ma).
|
||||||
|
edge(ct, ny).
|
||||||
|
edge(ct, ri).
|
||||||
|
edge(dc, md).
|
||||||
|
edge(dc, va).
|
||||||
|
edge(de, md).
|
||||||
|
edge(de, nj).
|
||||||
|
edge(de, pa).
|
||||||
|
edge(fl, ga).
|
||||||
|
edge(ga, nc).
|
||||||
|
edge(ga, sc).
|
||||||
|
edge(ga, tn).
|
||||||
|
edge(ia, il).
|
||||||
|
edge(ia, mn).
|
||||||
|
edge(ia, mo).
|
||||||
|
edge(ia, ne).
|
||||||
|
edge(ia, sd).
|
||||||
|
edge(ia, wi).
|
||||||
|
edge(id, mt).
|
||||||
|
edge(id, nv).
|
||||||
|
edge(id, or).
|
||||||
|
edge(id, ut).
|
||||||
|
edge(id, wa).
|
||||||
|
edge(id, wy).
|
||||||
|
edge(il, in).
|
||||||
|
edge(il, ky).
|
||||||
|
edge(il, mo).
|
||||||
|
edge(il, wi).
|
||||||
|
edge(in, ky).
|
||||||
|
edge(in, mi).
|
||||||
|
edge(in, oh).
|
||||||
|
edge(ks, mo).
|
||||||
|
edge(ks, ne).
|
||||||
|
edge(ks, ok).
|
||||||
|
edge(ky, mo).
|
||||||
|
edge(ky, oh).
|
||||||
|
edge(ky, tn).
|
||||||
|
edge(ky, va).
|
||||||
|
edge(ky, wv).
|
||||||
|
edge(la, ms).
|
||||||
|
edge(la, tx).
|
||||||
|
edge(ma, nh).
|
||||||
|
edge(ma, ny).
|
||||||
|
edge(ma, ri).
|
||||||
|
edge(ma, vt).
|
||||||
|
edge(md, pa).
|
||||||
|
edge(md, va).
|
||||||
|
edge(md, wv).
|
||||||
|
edge(me, nh).
|
||||||
|
edge(mi, oh).
|
||||||
|
edge(mi, wi).
|
||||||
|
edge(mn, nd).
|
||||||
|
edge(mn, sd).
|
||||||
|
edge(mn, wi).
|
||||||
|
edge(mo, ne).
|
||||||
|
edge(mo, ok).
|
||||||
|
edge(mo, tn).
|
||||||
|
edge(ms, tn).
|
||||||
|
edge(mt, nd).
|
||||||
|
edge(mt, sd).
|
||||||
|
edge(mt, wy).
|
||||||
|
edge(nc, sc).
|
||||||
|
edge(nc, tn).
|
||||||
|
edge(nc, va).
|
||||||
|
edge(nd, sd).
|
||||||
|
edge(ne, sd).
|
||||||
|
edge(ne, wy).
|
||||||
|
edge(nh, vt).
|
||||||
|
edge(nj, ny).
|
||||||
|
edge(nj, pa).
|
||||||
|
edge(nm, ok).
|
||||||
|
edge(nm, tx).
|
||||||
|
edge(nv, or).
|
||||||
|
edge(nv, ut).
|
||||||
|
edge(ny, pa).
|
||||||
|
edge(ny, vt).
|
||||||
|
edge(oh, pa).
|
||||||
|
edge(oh, wv).
|
||||||
|
edge(ok, tx).
|
||||||
|
edge(or, wa).
|
||||||
|
edge(pa, wv).
|
||||||
|
edge(sd, wy).
|
||||||
|
edge(tn, va).
|
||||||
|
edge(ut, wy).
|
||||||
|
edge(va, wv).
|
||||||
|
|
||||||
|
independent_set(G, *(NBs)) :-
|
||||||
|
findall(U-V, (edge(U, V),G@<U), Edges),
|
||||||
|
setof(U, V^(member(U-V, Edges);member(V-U, Edges)), Nodes),
|
||||||
|
pairs_keys_values(Pairs, Nodes, _),
|
||||||
|
list_to_assoc(Pairs, Assoc),
|
||||||
|
maplist(not_both(Assoc), Edges, NBs).
|
||||||
|
|
||||||
|
not_both(Assoc, U-V, ~BU + ~BV) :-
|
||||||
|
get_assoc(U, Assoc, BU),
|
||||||
|
get_assoc(V, Assoc, BV).
|
||||||
|
|
||||||
|
independent_set_count(G, Count) :- independent_set(G, Sat), sat_count(Sat, Count).
|
||||||
2
benches/numlist.pl
Normal file
2
benches/numlist.pl
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
:- use_module(library(between)).
|
||||||
|
run_numlist(Upper, Head) :- numlist(1, Upper, L), L = [Head|_].
|
||||||
46
benches/run_criterion.rs
Normal file
46
benches/run_criterion.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
use pprof::criterion::{Output, PProfProfiler};
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
mod setup;
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
fn bench_criterion(c: &mut Criterion) {
|
||||||
|
for (&name, bench) in setup::prolog_benches().iter() {
|
||||||
|
match bench.strategy {
|
||||||
|
setup::Strategy::Fresh => c.bench_function(name, |b| {
|
||||||
|
b.iter_batched(|| bench.setup(), |mut r| r(), BatchSize::LargeInput)
|
||||||
|
}),
|
||||||
|
setup::Strategy::Reuse => c.bench_function(name, |b| b.iter(bench.setup())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
fn config() -> Criterion {
|
||||||
|
Criterion::default()
|
||||||
|
.sample_size(20)
|
||||||
|
.with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn config() -> Criterion {
|
||||||
|
Criterion::default().sample_size(20)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
criterion_group!(
|
||||||
|
name = benches;
|
||||||
|
config = config();
|
||||||
|
targets = bench_criterion
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||||
|
fn main() {}
|
||||||
38
benches/run_iai.rs
Normal file
38
benches/run_iai.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
mod setup;
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
mod iai {
|
||||||
|
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||||
|
|
||||||
|
use scryer_prolog::LeafAnswer;
|
||||||
|
|
||||||
|
use super::setup;
|
||||||
|
|
||||||
|
#[library_benchmark]
|
||||||
|
#[bench::count_edges(setup::prolog_benches()["count_edges"].setup())]
|
||||||
|
#[bench::numlist(setup::prolog_benches()["numlist"].setup())]
|
||||||
|
#[bench::csv_codename(setup::prolog_benches()["csv_codename"].setup())]
|
||||||
|
fn bench(mut run: impl FnMut() -> Vec<LeafAnswer>) -> Vec<LeafAnswer> {
|
||||||
|
run()
|
||||||
|
}
|
||||||
|
|
||||||
|
library_benchmark_group!(
|
||||||
|
name = benches;
|
||||||
|
benchmarks = bench
|
||||||
|
);
|
||||||
|
|
||||||
|
main!(library_benchmark_groups = benches);
|
||||||
|
|
||||||
|
pub fn call_main() {
|
||||||
|
main()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
|
fn main() {
|
||||||
|
iai::call_main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||||
|
fn main() {}
|
||||||
140
benches/setup.rs
Normal file
140
benches/setup.rs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
use std::{collections::BTreeMap, fs, path::Path};
|
||||||
|
|
||||||
|
use maplit::btreemap;
|
||||||
|
use scryer_prolog::{LeafAnswer, Machine, MachineBuilder, Term};
|
||||||
|
|
||||||
|
pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"count_edges", // name of the benchmark
|
||||||
|
"benches/edges.pl", // name of the prolog module file to load. use the same file in multiple benchmarks
|
||||||
|
"independent_set_count(ky, Count).", // query to benchmark in the context of the loaded module. consider making the query adjustable to tune the run time to ~0.1s
|
||||||
|
Strategy::Reuse,
|
||||||
|
btreemap! { "Count" => Term::integer(2869176) },
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"numlist",
|
||||||
|
"benches/numlist.pl",
|
||||||
|
"run_numlist(1000000, Head).",
|
||||||
|
Strategy::Reuse,
|
||||||
|
btreemap! { "Head" => Term::integer(1) },
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"csv_codename",
|
||||||
|
"benches/csv.pl",
|
||||||
|
"get_codename(\"0020\",Name).",
|
||||||
|
Strategy::Reuse,
|
||||||
|
btreemap! { "Name" => Term::string("SPACE") },
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.map(|b| {
|
||||||
|
(
|
||||||
|
b.0,
|
||||||
|
PrologBenchmark {
|
||||||
|
name: b.0,
|
||||||
|
filename: b.1,
|
||||||
|
query: b.2,
|
||||||
|
strategy: b.3,
|
||||||
|
bindings: b.4,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Strategy {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
Fresh,
|
||||||
|
Reuse,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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, Term>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrologBenchmark {
|
||||||
|
pub fn make_machine(&self) -> Machine {
|
||||||
|
let program = fs::read_to_string(self.filename).unwrap();
|
||||||
|
let module_name = Path::new(self.filename)
|
||||||
|
.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.unwrap();
|
||||||
|
let mut machine = 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() -> Vec<LeafAnswer> + use<> {
|
||||||
|
let mut machine = self.make_machine();
|
||||||
|
let query = self.query;
|
||||||
|
move || {
|
||||||
|
use criterion::black_box;
|
||||||
|
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::LeafAnswer;
|
||||||
|
use std::{fmt::Write, fs};
|
||||||
|
|
||||||
|
struct BenchResult {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub setup_inference_count: u64,
|
||||||
|
pub query_inference_count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut results: Vec<BenchResult> = vec![];
|
||||||
|
|
||||||
|
for (_, r) in prolog_benches() {
|
||||||
|
let mut machine = r.make_machine();
|
||||||
|
let setup_inference_count = machine.get_inference_count();
|
||||||
|
|
||||||
|
let result: Vec<_> = machine
|
||||||
|
.run_query(r.query)
|
||||||
|
.collect::<Result<_, _>>()
|
||||||
|
.unwrap();
|
||||||
|
let query_inference_count = machine.get_inference_count() - setup_inference_count;
|
||||||
|
|
||||||
|
let expected = [LeafAnswer::from_bindings(r.bindings.clone())];
|
||||||
|
assert_eq!(result, expected, "validating benchmark {}", r.name);
|
||||||
|
|
||||||
|
results.push(BenchResult {
|
||||||
|
name: r.name,
|
||||||
|
setup_inference_count,
|
||||||
|
query_inference_count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut json: String = Default::default();
|
||||||
|
json.push('[');
|
||||||
|
for r in results {
|
||||||
|
json.push('\n');
|
||||||
|
write!(
|
||||||
|
json,
|
||||||
|
r#"{{"name":"{}","setup_inference_count":{},"query_inference_count":{}}},"#,
|
||||||
|
r.name, r.setup_inference_count, r.query_inference_count
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
json.pop(); // trailing comma
|
||||||
|
json.push_str("\n]");
|
||||||
|
fs::write("target/benchmark_inference_counts.json", json).expect("Unable to write file");
|
||||||
|
}
|
||||||
|
}
|
||||||
60
build.rs
60
build.rs
@@ -1,60 +0,0 @@
|
|||||||
extern crate indexmap;
|
|
||||||
|
|
||||||
use indexmap::IndexSet;
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::fs::{File, copy, read_dir};
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
fn main()
|
|
||||||
{
|
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
|
||||||
let dest_path = Path::new(&out_dir).join("libraries.rs");
|
|
||||||
|
|
||||||
let mut libraries = File::create(&dest_path).unwrap();
|
|
||||||
let mut library_index = IndexSet::new();
|
|
||||||
|
|
||||||
let paths = read_dir("./src/prolog/lib").unwrap();
|
|
||||||
|
|
||||||
for item in paths {
|
|
||||||
let item = item.unwrap().path();
|
|
||||||
|
|
||||||
if let Some(file_name) = item.file_name() {
|
|
||||||
if let Some(ext) = item.extension() {
|
|
||||||
if ext == "pl" {
|
|
||||||
let file_stem = item.file_stem().unwrap();
|
|
||||||
let file_str = file_stem.to_string_lossy().to_uppercase();
|
|
||||||
let dest = Path::new(&out_dir).join(file_name);
|
|
||||||
|
|
||||||
match copy(&item, dest) {
|
|
||||||
Ok(_) => {},
|
|
||||||
Err(e) => panic!("die: {:?}", e)
|
|
||||||
};
|
|
||||||
|
|
||||||
let include_line = format!("static {}: &str = include_str!(\"{}.pl\");\n",
|
|
||||||
file_str, file_stem.to_string_lossy());
|
|
||||||
|
|
||||||
libraries.write_all(include_line.as_bytes()).unwrap();
|
|
||||||
library_index.insert(file_stem.to_string_lossy().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
libraries.write_all(b"\nref_thread_local! {
|
|
||||||
pub static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
|
|
||||||
let mut m = IndexMap::new();\n").unwrap();
|
|
||||||
|
|
||||||
for item in library_index {
|
|
||||||
let line = format!("\n m.insert(\"{}\", {});", item, item.to_uppercase());
|
|
||||||
libraries.write_all(line.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
libraries.write_all(b"\n\n m\n };
|
|
||||||
}\n").unwrap();
|
|
||||||
|
|
||||||
libraries.write_all(b"\npub static PROJECT_DIR: &'static str = \"").unwrap();
|
|
||||||
libraries.write_all(env::var("CARGO_MANIFEST_DIR").unwrap().as_bytes()).unwrap();
|
|
||||||
libraries.write_all(b"\";\n").unwrap();
|
|
||||||
}
|
|
||||||
2045
build/instructions_template.rs
Normal file
2045
build/instructions_template.rs
Normal file
File diff suppressed because it is too large
Load Diff
121
build/main.rs
Normal file
121
build/main.rs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
mod instructions_template;
|
||||||
|
mod static_string_indexing;
|
||||||
|
|
||||||
|
use instructions_template::generate_instructions_rs;
|
||||||
|
use static_string_indexing::index_static_strings;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::MAIN_SEPARATOR_STR;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
fn find_prolog_files(path_prefix: &str, current_dir: &Path) -> Vec<(String, PathBuf)> {
|
||||||
|
// use a BTreeMap to get a stable order independent of fs enumeration order
|
||||||
|
let mut libraries = BTreeMap::new();
|
||||||
|
|
||||||
|
let entries = match current_dir.read_dir() {
|
||||||
|
Ok(entries) => entries,
|
||||||
|
Err(_) => return vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
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 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 name = entry.file_stem().unwrap().to_str().unwrap();
|
||||||
|
let lib_name = format!("{path_prefix}{name}");
|
||||||
|
|
||||||
|
libraries.insert(lib_name, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libraries.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let has_rustfmt = Command::new("rustfmt")
|
||||||
|
.arg("--version")
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.status()
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
if !has_rustfmt {
|
||||||
|
println!("Failed to run rustfmt, will skip formatting generated files.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let dest_path = Path::new(&out_dir).join("libraries.rs");
|
||||||
|
|
||||||
|
let mut libraries = File::create(dest_path).unwrap();
|
||||||
|
let lib_path = Path::new("src").join("lib");
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
writeln!(libraries, "}}").unwrap();
|
||||||
|
|
||||||
|
let instructions_path = Path::new(&out_dir).join("instructions.rs");
|
||||||
|
let mut instructions_file = File::create(&instructions_path).unwrap();
|
||||||
|
|
||||||
|
let quoted_output = generate_instructions_rs();
|
||||||
|
|
||||||
|
instructions_file
|
||||||
|
.write_all(quoted_output.to_string().as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if has_rustfmt {
|
||||||
|
format_generated_file(instructions_path.as_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
let static_atoms_path = Path::new(&out_dir).join("static_atoms.rs");
|
||||||
|
let mut static_atoms_file = File::create(&static_atoms_path).unwrap();
|
||||||
|
|
||||||
|
let quoted_output = index_static_strings(&instructions_path);
|
||||||
|
|
||||||
|
static_atoms_file
|
||||||
|
.write_all(quoted_output.to_string().as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if has_rustfmt {
|
||||||
|
format_generated_file(static_atoms_path.as_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=src/");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_generated_file(path: &Path) {
|
||||||
|
Command::new("rustfmt")
|
||||||
|
.arg(path.as_os_str())
|
||||||
|
.spawn()
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"{}: rustfmt was detected as available, but failed to format generated file '{}'",
|
||||||
|
err,
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
207
build/static_string_indexing.rs
Normal file
207
build/static_string_indexing.rs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use syn::parse::*;
|
||||||
|
use syn::visit::*;
|
||||||
|
use syn::*;
|
||||||
|
|
||||||
|
struct StaticStrVisitor {
|
||||||
|
static_strs: BTreeSet<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StaticStrVisitor {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
static_strs: BTreeSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MacroFnArgs {
|
||||||
|
args: Vec<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ReadHeapCellExprAndArms {
|
||||||
|
expr: Expr,
|
||||||
|
arms: Vec<Arm>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ReadHeapCellExprAndArms {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let mut arms = vec![];
|
||||||
|
let expr = input.parse()?;
|
||||||
|
|
||||||
|
input.parse::<Token![,]>()?;
|
||||||
|
arms.push(input.parse()?);
|
||||||
|
|
||||||
|
while !input.is_empty() {
|
||||||
|
let _ = input.parse::<Token![,]>();
|
||||||
|
arms.push(input.parse()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ReadHeapCellExprAndArms { expr, arms })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for MacroFnArgs {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let mut args = vec![];
|
||||||
|
|
||||||
|
if !input.is_empty() {
|
||||||
|
args.push(input.parse()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
while !input.is_empty() {
|
||||||
|
let _ = input.parse::<Token![,]>();
|
||||||
|
args.push(input.parse()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(MacroFnArgs { args })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ast> Visit<'ast> for StaticStrVisitor {
|
||||||
|
fn visit_macro(&mut self, m: &'ast Macro) {
|
||||||
|
let Macro { path, .. } = m;
|
||||||
|
|
||||||
|
if path.is_ident("atom") {
|
||||||
|
if let Ok(Lit::Str(string)) = m.parse_body::<Lit>() {
|
||||||
|
self.static_strs.insert(string.value());
|
||||||
|
}
|
||||||
|
} else if path.is_ident("read_heap_cell") || path.is_ident("match_untyped_arena_ptr") {
|
||||||
|
if let Ok(m) = m.parse_body::<ReadHeapCellExprAndArms>() {
|
||||||
|
self.visit_expr(&m.expr);
|
||||||
|
|
||||||
|
for e in m.arms {
|
||||||
|
self.visit_arm(&e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Ok(m) = m.parse_body::<MacroFnArgs>() {
|
||||||
|
for e in m.args {
|
||||||
|
self.visit_expr(&e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::*;
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
fn filter_rust_files(e: &walkdir::DirEntry) -> bool {
|
||||||
|
if e.path().is_dir() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.path().extension().and_then(OsStr::to_str) == Some("rs")
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut visitor = StaticStrVisitor::new();
|
||||||
|
|
||||||
|
fn process_filepath(path: &std::path::Path) -> std::result::Result<syn::File, ()> {
|
||||||
|
let mut src = String::new();
|
||||||
|
|
||||||
|
let mut file = match File::open(path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(_) => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match file.read_to_string(&mut src) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
panic!("error reading file: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let syntax = match syn::parse_file(&src) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
println!("cargo::warning=parse error: {e} in file {path:?}");
|
||||||
|
syn::File {
|
||||||
|
shebang: None,
|
||||||
|
attrs: vec![],
|
||||||
|
items: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(syntax)
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in WalkDir::new("src/")
|
||||||
|
.into_iter()
|
||||||
|
.filter_entry(filter_rust_files)
|
||||||
|
{
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
|
||||||
|
if entry.path().is_dir() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let syntax = match process_filepath(entry.path()) {
|
||||||
|
Ok(syntax) => syntax,
|
||||||
|
Err(_) => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
visitor.visit_file(&syntax);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(syntax) = process_filepath(instruction_rs_path) {
|
||||||
|
visitor.visit_file(&syntax)
|
||||||
|
}
|
||||||
|
|
||||||
|
let static_str_keys: Vec<_> = visitor.static_strs.iter().collect();
|
||||||
|
let mut static_strs = Vec::with_capacity(static_str_keys.len());
|
||||||
|
let mut static_str_indices = Vec::with_capacity(static_str_keys.len());
|
||||||
|
|
||||||
|
let indices: Vec<u64> = visitor
|
||||||
|
.static_strs
|
||||||
|
.iter()
|
||||||
|
.map(|string| {
|
||||||
|
let index = static_string_index(string, static_strs.len());
|
||||||
|
|
||||||
|
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! {
|
||||||
|
static STRINGS: [&str; #static_strs_len] = [
|
||||||
|
#(
|
||||||
|
#static_strs,
|
||||||
|
)*
|
||||||
|
];
|
||||||
|
|
||||||
|
macro_rules! atom {
|
||||||
|
#((#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: #static_str_indices } },)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
26
clippy.toml
Normal file
26
clippy.toml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
disallowed-macros = [
|
||||||
|
# https://rust-lang.github.io/rust-clippy/master/#disallowed_macros
|
||||||
|
|
||||||
|
# list of macros that may panic on allocation failure e.g.
|
||||||
|
|
||||||
|
# "std::vec",
|
||||||
|
]
|
||||||
|
|
||||||
|
disallowed-methods = [
|
||||||
|
# https://rust-lang.github.io/rust-clippy/master/#disallowed_method
|
||||||
|
|
||||||
|
# list of methods that may panic on allocation failure
|
||||||
|
# though not including things that can be used correctly by reversing ahead of time (i.e. std::vec::Vec::try_reserve + std::iter::Extend::extend ).
|
||||||
|
|
||||||
|
# "std::iter::Iter::collect",
|
||||||
|
# { path = "std::vec::Vec::with_capacity", replacement = "std::vec::Vec::new + std::vec::Vec::try_reserve" },
|
||||||
|
# { path = "std::string::String::with_capacity", replacement = "std::string::String::new + std::string::String::try_reserve" },
|
||||||
|
]
|
||||||
|
|
||||||
|
disallowed-types = [
|
||||||
|
# https://rust-lang.github.io/rust-clippy/master/#disallowed_types
|
||||||
|
|
||||||
|
# list of types that can't be used without risking a panic due to allocation failure
|
||||||
|
|
||||||
|
# { path = "std::collections::BTreeMap", reason = "unlike Vec and HashMap BTreeMap cannot reserve capacity ahead of time (i.e. try_reserve) making it unusable without risk of oom panic"},
|
||||||
|
]
|
||||||
13
doclog.config.pl
Normal file
13
doclog.config.pl
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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")
|
||||||
|
]).
|
||||||
|
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 compound 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 arithmetic system of Prolog, but it's valid with clpz. clpz allows us to have a more declarative arithmetic, 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. Additionally 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
|
||||||
|
|
||||||
|

|
||||||
BIN
logo/scryer.png
Normal file
BIN
logo/scryer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
896
logo/scryer.svg
Normal file
896
logo/scryer.svg
Normal file
@@ -0,0 +1,896 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="300.000000pt" height="300.000000pt" viewBox="0 0 300.000000 300.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<path fill="#57d3de" d="M0 0 h 300 v 300 h -300 z"/>
|
||||||
|
<g transform="translate(0.000000,300.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#800080" stroke="none">
|
||||||
|
<path d="M2388 2423 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
|
||||||
|
<path d="M2436 2422 c-3 -5 8 -6 25 -4 16 2 29 6 29 8 0 8 -49 4 -54 -4z"/>
|
||||||
|
<path d="M2310 2416 c0 -10 28 -13 41 -5 10 6 7 9 -13 9 -16 0 -28 -2 -28 -4z"/>
|
||||||
|
<path d="M2501 2415 c3 -2 23 -7 45 -9 86 -10 151 -37 181 -75 8 -10 12 -11
|
||||||
|
13 -4 0 19 -81 69 -130 80 -50 11 -117 16 -109 8z"/>
|
||||||
|
<path d="M2055 2373 c-60 -13 -126 -27 -145 -30 -19 -3 -38 -11 -42 -17 -6 -8
|
||||||
|
-10 -7 -17 2 -7 11 -19 12 -62 3 -30 -6 -82 -12 -116 -14 -46 -1 -63 -6 -66
|
||||||
|
-18 -5 -20 -107 -27 -107 -7 0 9 -3 9 -12 0 -19 -19 -41 -15 -33 6 4 11 3 13
|
||||||
|
-3 7 -6 -5 -12 -19 -14 -30 -2 -11 -9 -19 -16 -18 -6 2 -9 -4 -6 -12 5 -11 11
|
||||||
|
-12 24 -5 30 16 34 11 9 -9 -13 -11 -27 -15 -30 -10 -9 16 -39 -2 -39 -23 0
|
||||||
|
-11 -5 -29 -11 -40 -14 -26 2 -37 45 -32 23 4 27 2 16 -5 -13 -9 -13 -11 3
|
||||||
|
-12 26 -2 31 -4 44 -12 9 -6 11 -3 7 10 -5 17 -4 16 10 -1 8 -10 17 -23 18
|
||||||
|
-28 2 -5 19 -1 39 9 22 11 34 23 31 32 -2 8 0 11 6 7 11 -6 22 16 22 42 -1 11
|
||||||
|
-6 8 -16 -10 -8 -16 -19 -28 -24 -28 -6 0 -10 -7 -10 -15 0 -8 -4 -15 -9 -15
|
||||||
|
-5 0 -16 -3 -25 -6 -11 -4 -16 -1 -16 10 0 16 -10 20 -42 17 -10 -1 -18 3 -18
|
||||||
|
8 0 6 -9 11 -19 11 -47 0 -36 35 23 73 51 32 63 33 94 7 19 -16 21 -23 12 -40
|
||||||
|
-11 -20 -21 -27 -19 -12 4 27 -2 44 -13 35 -7 -5 -26 -10 -43 -11 -64 -4 -47
|
||||||
|
-40 25 -49 95 -13 99 89 4 107 -37 7 -37 8 -8 9 30 1 88 -21 80 -30 -2 -2 2
|
||||||
|
-9 9 -16 10 -9 17 -7 31 9 16 18 17 21 2 15 -9 -3 -18 0 -21 6 -4 12 18 17 78
|
||||||
|
17 20 0 38 10 57 30 15 18 33 28 41 24 8 -3 18 -1 21 5 4 6 16 6 36 -1 23 -7
|
||||||
|
34 -7 45 2 10 8 15 9 15 2 0 -18 -34 -31 -53 -21 -11 7 -23 6 -34 -1 -17 -11
|
||||||
|
-17 -11 0 -6 10 3 28 1 39 -5 16 -8 24 -7 41 8 12 11 29 19 37 18 8 0 22 7 31
|
||||||
|
17 9 10 18 16 20 14 2 -2 23 -1 47 4 25 4 42 4 42 -2 0 -10 42 0 50 12 3 4 25
|
||||||
|
15 50 23 67 24 14 19 -115 -10z m-415 -83 c0 -5 -4 -10 -10 -10 -5 0 -10 5
|
||||||
|
-10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m82 3 c-6 -3 -9 -9 -6 -15 4 -6 1 -9
|
||||||
|
-6 -6 -23 7 -21 24 2 25 13 0 17 -1 10 -4z"/>
|
||||||
|
<path d="M2220 2395 c0 -7 30 -13 34 -7 3 4 -4 9 -15 9 -10 1 -19 0 -19 -2z"/>
|
||||||
|
<path d="M2136 2328 l-21 -17 27 -1 c20 0 26 4 22 15 -5 11 0 15 17 14 13 0
|
||||||
|
18 -3 11 -6 -19 -7 -4 -23 16 -16 15 5 15 4 2 -10 -8 -9 -26 -18 -40 -21 -14
|
||||||
|
-2 -34 -8 -44 -12 -14 -5 -17 -4 -12 4 4 7 3 12 -2 12 -6 0 -12 -6 -15 -14 -4
|
||||||
|
-11 -67 -30 -92 -27 -2 0 -23 -6 -46 -14 -49 -18 -62 -19 -54 -5 4 6 10 8 15
|
||||||
|
5 4 -3 13 2 20 10 21 25 -6 17 -39 -11 -31 -26 -61 -27 -61 -1 0 7 5 18 12 25
|
||||||
|
8 8 8 12 1 12 -6 0 -13 -7 -17 -15 -3 -8 -17 -15 -31 -15 -24 0 -30 9 -26 38
|
||||||
|
1 6 -5 12 -14 12 -18 0 -19 -12 -3 -28 9 -9 8 -12 -7 -12 -10 0 -15 -3 -12 -7
|
||||||
|
4 -3 1 -13 -5 -22 -9 -11 -5 -10 9 2 20 17 50 16 39 -2 -3 -4 5 -8 16 -8 26
|
||||||
|
-2 30 -7 23 -26 -7 -16 -44 -29 -67 -24 -7 2 -10 2 -5 0 17 -10 5 -22 -20 -19
|
||||||
|
-16 2 -22 1 -15 -1 7 -3 10 -9 7 -14 -4 -5 5 -8 19 -7 15 1 26 6 26 11 0 4 5
|
||||||
|
5 10 2 6 -3 10 -2 10 3 0 13 70 49 110 57 19 4 78 19 130 35 52 15 114 33 137
|
||||||
|
39 24 6 47 21 53 31 6 11 25 26 43 33 30 13 29 13 -37 13 -51 0 -74 -4 -90
|
||||||
|
-18z"/>
|
||||||
|
<path d="M2374 2341 c3 -5 15 -7 26 -4 28 7 25 13 -6 13 -14 0 -23 -4 -20 -9z"/>
|
||||||
|
<path d="M1445 2329 c-194 -16 -394 -99 -536 -222 -14 -12 -72 -58 -129 -102
|
||||||
|
-136 -106 -198 -162 -206 -189 -3 -11 -16 -32 -29 -46 -12 -14 -35 -47 -50
|
||||||
|
-75 -15 -27 -32 -57 -39 -65 -10 -13 -9 -13 6 -1 26 20 22 5 -11 -37 -15 -20
|
||||||
|
-42 -75 -58 -122 -17 -47 -38 -102 -47 -123 -9 -22 -12 -36 -7 -33 6 4 2 -13
|
||||||
|
-9 -36 -26 -56 -25 -77 1 -44 11 14 17 30 14 36 -3 5 -1 10 4 10 6 0 11 -8 11
|
||||||
|
-17 0 -15 2 -15 14 2 9 13 11 27 6 41 -11 29 -8 49 13 72 10 11 17 23 16 28
|
||||||
|
-3 20 2 35 10 30 11 -7 23 22 15 35 -10 16 6 31 21 19 9 -8 15 -8 20 0 3 5 1
|
||||||
|
10 -6 10 -8 0 -8 4 1 15 7 8 17 12 24 8 7 -4 6 -1 -1 9 -7 8 -10 18 -6 21 3 4
|
||||||
|
4 7 0 7 -3 0 -13 -7 -21 -16 -17 -16 -36 -11 -36 9 0 8 3 7 9 -2 7 -11 11 -8
|
||||||
|
16 10 4 13 16 30 27 37 10 8 15 19 11 25 -4 7 0 6 10 -2 9 -7 17 -12 18 -10 0
|
||||||
|
2 2 19 4 37 2 19 12 40 22 48 22 16 10 18 -20 2 -12 -6 1 12 30 40 28 29 56
|
||||||
|
52 62 52 13 0 15 27 2 34 -5 3 -8 -2 -7 -11 0 -9 -5 -18 -12 -20 -8 -3 -10 1
|
||||||
|
-5 14 10 28 25 44 50 54 13 5 22 14 21 21 -2 6 6 14 17 16 11 2 25 9 30 14 7
|
||||||
|
8 5 9 -5 4 -8 -4 -4 2 10 13 14 12 35 24 47 27 12 3 29 17 38 31 9 14 23 22
|
||||||
|
30 19 9 -3 12 0 8 9 -3 9 3 18 13 21 11 4 33 17 51 30 34 26 123 43 123 24 0
|
||||||
|
-6 5 -8 12 -4 11 7 26 10 83 18 11 2 24 -2 28 -8 6 -7 7 -6 5 4 -3 8 -15 17
|
||||||
|
-28 20 -20 5 -21 7 -7 18 10 8 17 9 21 3 5 -7 14 -7 29 0 24 11 54 49 38 49
|
||||||
|
-5 0 -11 -5 -13 -11 -2 -6 -23 -11 -46 -11 -23 0 -42 -4 -42 -9 0 -6 -3 -8 -7
|
||||||
|
-6 -5 3 -14 -1 -21 -9 -11 -12 -10 -14 5 -14 16 0 16 -2 2 -15 -17 -17 -53
|
||||||
|
-10 -42 8 4 6 1 7 -7 2 -7 -4 -28 -11 -47 -15 -29 -7 -32 -6 -22 6 7 8 14 12
|
||||||
|
16 10 3 -3 15 7 28 22 20 24 24 25 41 12 10 -8 15 -10 11 -4 -11 19 21 51 60
|
||||||
|
59 21 5 44 9 50 11 7 1 20 9 29 18 17 18 28 21 19 6 -3 -5 3 -10 14 -10 11 0
|
||||||
|
23 5 26 11 4 5 18 14 32 20 13 5 22 13 19 18 -8 12 155 35 203 29 20 -3 47 -2
|
||||||
|
61 2 39 10 -49 16 -135 9z m-940 -679 c3 -5 -1 -10 -10 -10 -9 0 -13 5 -10 10
|
||||||
|
3 6 8 10 10 10 2 0 7 -4 10 -10z"/>
|
||||||
|
<path d="M2490 2321 c-7 -11 -24 -21 -37 -24 -27 -5 -32 9 -5 16 15 4 15 5 -1
|
||||||
|
6 -11 1 -21 -8 -24 -20 -5 -16 -11 -19 -30 -14 -19 4 -23 3 -17 -7 6 -10 5
|
||||||
|
-11 -7 -1 -12 10 -19 8 -33 -8 -11 -13 -25 -19 -37 -16 -11 3 -32 -1 -47 -8
|
||||||
|
-43 -21 -75 -29 -68 -17 4 6 -13 0 -37 -14 -25 -13 -63 -25 -86 -27 -22 -1
|
||||||
|
-41 -5 -41 -9 0 -5 -9 -8 -20 -8 -52 0 -250 -68 -250 -86 0 -3 5 -2 10 1 6 3
|
||||||
|
10 2 10 -4 0 -5 -4 -13 -10 -16 -5 -3 -10 -12 -9 -18 0 -9 2 -9 6 1 2 6 13 12
|
||||||
|
23 12 10 0 22 5 25 10 4 6 11 8 16 5 5 -4 9 -1 9 4 0 6 3 10 8 10 4 -1 26 1
|
||||||
|
49 6 32 6 40 11 36 23 -4 10 -2 13 5 8 7 -4 12 -3 12 2 0 5 6 9 13 9 38 1 102
|
||||||
|
15 115 25 13 11 15 10 9 -4 -4 -12 -2 -15 8 -11 8 3 13 8 11 12 -2 3 9 7 25 8
|
||||||
|
15 1 42 9 59 18 17 9 52 20 78 26 26 5 64 14 84 18 21 5 43 13 50 19 7 6 42
|
||||||
|
15 78 22 36 6 79 16 96 21 41 12 94 11 94 -2 0 -6 -7 -17 -15 -25 -8 -9 -15
|
||||||
|
-11 -15 -5 0 6 -7 8 -15 5 -9 -4 -15 0 -15 10 0 9 -5 16 -11 16 -7 0 -10 -12
|
||||||
|
-7 -30 5 -34 -11 -45 -30 -21 -11 13 -13 13 -9 1 4 -15 -24 -41 -55 -53 -11
|
||||||
|
-4 -21 0 -27 11 -10 16 -10 16 -11 0 0 -18 7 -21 43 -21 13 0 15 -3 6 -13 -14
|
||||||
|
-16 -39 -19 -39 -3 0 5 -5 7 -11 3 -8 -4 -7 -9 2 -15 12 -8 11 -11 -1 -19 -8
|
||||||
|
-5 -20 -10 -27 -10 -6 0 -14 -6 -17 -12 -5 -10 -7 -10 -12 0 -8 18 -24 15 -24
|
||||||
|
-5 0 -9 -3 -14 -6 -10 -4 3 -22 -5 -41 -18 -20 -13 -44 -22 -54 -20 -11 2 -17
|
||||||
|
0 -15 -4 3 -4 -14 -19 -37 -32 -23 -13 -54 -30 -69 -39 -103 -58 -157 -92
|
||||||
|
-170 -107 -8 -10 -25 -45 -37 -78 -12 -33 -33 -74 -47 -92 -14 -18 -22 -33
|
||||||
|
-18 -33 4 0 -1 -10 -12 -22 -17 -19 -56 -132 -69 -200 -2 -9 -8 -25 -13 -35
|
||||||
|
-12 -21 -12 -22 -12 -70 0 -28 -3 -34 -10 -23 -8 12 -10 9 -10 -12 0 -14 -3
|
||||||
|
-34 -7 -44 -5 -13 -3 -16 10 -11 14 5 17 0 17 -28 0 -18 5 -37 10 -40 16 -10
|
||||||
|
12 -25 -7 -25 -11 0 -14 3 -7 8 8 5 6 12 -5 24 -15 15 -16 14 -15 -19 1 -19
|
||||||
|
-2 -49 -7 -66 -5 -17 -5 -35 -1 -39 5 -5 14 9 21 31 15 53 31 61 25 14 -3 -21
|
||||||
|
-6 -57 -8 -81 -1 -23 -8 -48 -15 -55 -10 -10 -12 -8 -9 8 4 24 -33 29 -39 5
|
||||||
|
-4 -13 -3 -13 6 0 15 22 23 7 31 -57 6 -44 8 -50 9 -23 2 28 4 31 10 15 5 -11
|
||||||
|
6 -25 4 -31 -2 -7 0 -29 6 -50 5 -22 13 -59 16 -83 3 -23 11 -54 16 -68 17
|
||||||
|
-45 20 -104 7 -121 -7 -9 -8 -13 -2 -9 7 4 12 -2 12 -18 0 -14 -4 -22 -9 -19
|
||||||
|
-5 2 -2 -11 6 -30 8 -20 15 -46 15 -59 0 -14 13 -35 30 -50 22 -20 30 -23 30
|
||||||
|
-12 0 9 -10 20 -22 26 -18 8 -24 20 -26 51 -3 42 -3 42 18 23 20 -19 21 -19
|
||||||
|
13 1 -4 10 -12 25 -19 31 -7 8 -10 28 -7 52 4 25 -6 84 -26 169 -27 110 -31
|
||||||
|
147 -29 234 1 57 1 172 -1 256 -2 110 1 157 10 168 6 8 10 17 7 20 -3 3 3 37
|
||||||
|
13 75 17 63 51 142 110 249 10 19 29 57 41 84 21 44 33 55 124 110 56 34 144
|
||||||
|
81 195 105 127 59 148 70 219 116 34 22 99 64 146 94 46 29 84 57 84 63 0 5
|
||||||
|
-5 6 -10 3 -6 -4 -26 -1 -45 5 -19 6 -67 12 -107 13 -40 1 -75 5 -78 9 -3 4
|
||||||
|
-12 -1 -20 -11z m-697 -236 c-3 -9 -8 -14 -10 -11 -3 3 -2 9 2 15 9 16 15 13
|
||||||
|
8 -4z"/>
|
||||||
|
<path d="M2025 2320 c-17 -7 -15 -9 13 -9 17 -1 32 4 32 9 0 12 -17 12 -45 0z"/>
|
||||||
|
<path d="M1910 2280 c-20 -12 -10 -19 14 -10 11 3 15 9 10 13 -6 3 -16 2 -24
|
||||||
|
-3z"/>
|
||||||
|
<path d="M1980 2274 c-29 -16 -8 -19 22 -3 16 8 23 8 30 -2 8 -11 9 -11 5 1
|
||||||
|
-5 20 -26 21 -57 4z"/>
|
||||||
|
<path d="M2065 2280 c-3 -5 1 -10 9 -10 9 0 16 5 16 10 0 6 -4 10 -9 10 -6 0
|
||||||
|
-13 -4 -16 -10z"/>
|
||||||
|
<path d="M1806 2271 c-12 -5 -15 -10 -9 -14 6 -4 16 0 23 8 14 17 13 17 -14 6z"/>
|
||||||
|
<path d="M1340 2245 c-14 -8 -37 -14 -51 -15 -14 0 -34 -9 -44 -20 -18 -20
|
||||||
|
-18 -21 -1 -38 14 -14 16 -14 11 -1 -7 19 20 44 57 53 14 3 34 9 44 13 12 4
|
||||||
|
15 3 10 -5 -4 -7 -3 -12 3 -12 6 0 15 9 21 20 9 17 8 20 -7 20 -10 -1 -29 -7
|
||||||
|
-43 -15z"/>
|
||||||
|
<path d="M1190 2209 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
|
||||||
|
-5 -10 -11z"/>
|
||||||
|
<path d="M1140 2199 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
|
||||||
|
-5 -10 -11z"/>
|
||||||
|
<path d="M1330 2201 c0 -5 5 -13 10 -16 6 -3 10 -2 10 4 0 5 -4 13 -10 16 -5
|
||||||
|
3 -10 2 -10 -4z"/>
|
||||||
|
<path d="M1786 2184 c-11 -8 -16 -14 -10 -14 13 0 39 18 34 24 -3 2 -14 -2
|
||||||
|
-24 -10z"/>
|
||||||
|
<path d="M1294 2168 c6 -37 -36 -64 -48 -31 -3 8 -10 11 -16 8 -5 -3 -10 -1
|
||||||
|
-10 6 0 7 -3 9 -6 6 -4 -4 -1 -13 5 -21 7 -8 11 -19 8 -23 -3 -4 16 -8 41 -8
|
||||||
|
34 0 46 -4 44 -13 -1 -8 2 -11 8 -7 6 3 8 11 4 16 -3 6 2 17 12 25 18 14 18
|
||||||
|
14 -3 5 -29 -15 -32 -14 -26 8 3 11 0 28 -6 38 -10 15 -10 14 -7 -9z"/>
|
||||||
|
<path d="M2355 2150 c-3 -5 -3 -10 2 -10 4 -1 -3 -7 -16 -14 -14 -8 -31 -12
|
||||||
|
-38 -9 -8 3 -13 0 -11 -8 2 -7 -9 -15 -27 -19 -25 -5 -27 -7 -11 -13 11 -5 25
|
||||||
|
-2 35 6 26 22 87 47 115 47 14 0 26 4 26 10 0 6 -15 8 -36 4 -25 -4 -34 -2
|
||||||
|
-29 5 3 6 4 11 1 11 -3 0 -8 -4 -11 -10z"/>
|
||||||
|
<path d="M2135 2140 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
|
||||||
|
-8 -4 -11 -10z"/>
|
||||||
|
<path d="M1676 2107 c-11 -8 -17 -18 -15 -23 2 -5 14 1 26 14 24 24 17 30 -11
|
||||||
|
9z"/>
|
||||||
|
<path d="M2025 2109 c-5 -7 -3 -10 6 -7 8 3 22 1 32 -4 11 -5 17 -5 17 2 0 5
|
||||||
|
-4 10 -9 10 -5 0 -16 3 -24 6 -8 3 -18 0 -22 -7z"/>
|
||||||
|
<path d="M2250 2109 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
|
||||||
|
-5 -10 -11z"/>
|
||||||
|
<path d="M2200 2099 c0 -5 -5 -7 -12 -3 -6 4 -8 3 -4 -4 3 -6 2 -13 -4 -17 -5
|
||||||
|
-3 -10 -2 -10 3 0 6 -9 8 -20 5 -11 -3 -20 -9 -20 -14 0 -5 -7 -5 -17 -2 -10
|
||||||
|
4 -14 2 -9 -5 9 -15 -13 -27 -27 -13 -13 13 -41 15 -33 3 11 -19 -15 -37 -55
|
||||||
|
-37 -29 0 -44 -5 -46 -15 -4 -13 -3 -13 6 0 9 13 11 13 11 1 0 -12 3 -12 12
|
||||||
|
-3 14 14 42 16 33 2 -7 -11 23 -3 41 11 6 5 15 5 19 1 5 -4 5 2 1 12 -6 16 -4
|
||||||
|
17 10 6 13 -12 17 -11 24 0 5 8 12 11 16 7 4 -4 17 -1 28 6 12 7 35 12 52 12
|
||||||
|
30 -1 31 0 22 27 -10 29 -18 36 -18 17z"/>
|
||||||
|
<path d="M1967 2093 c-12 -12 -7 -22 8 -17 8 4 15 10 15 15 0 11 -14 12 -23 2z"/>
|
||||||
|
<path d="M1367 2084 c-4 -4 -7 -17 -7 -28 1 -19 1 -20 11 -3 11 19 8 43 -4 31z"/>
|
||||||
|
<path d="M1198 2073 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
|
||||||
|
<path d="M1326 2065 c-3 -8 -1 -15 4 -15 13 0 22 18 11 24 -5 3 -11 -1 -15 -9z"/>
|
||||||
|
<path d="M2000 2069 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
|
||||||
|
-5 -10 -11z"/>
|
||||||
|
<path d="M1065 2055 c-27 -8 -51 -19 -53 -25 -2 -5 -8 -8 -13 -4 -5 3 -14 2
|
||||||
|
-21 -2 -8 -5 -6 -10 8 -15 12 -4 26 -1 36 8 9 9 37 18 61 20 28 3 44 10 44 19
|
||||||
|
0 16 3 16 -62 -1z"/>
|
||||||
|
<path d="M1155 2049 c-23 -9 -21 -10 20 -14 146 -13 178 -14 145 -5 -19 5 -57
|
||||||
|
12 -85 16 -27 3 -52 8 -55 9 -3 1 -14 -1 -25 -6z"/>
|
||||||
|
<path d="M1675 2050 c4 -6 1 -17 -5 -26 -11 -12 -9 -14 11 -10 13 2 32 -1 42
|
||||||
|
-6 13 -6 17 -5 17 7 0 9 -8 18 -17 21 -10 3 -26 10 -37 15 -13 8 -16 7 -11 -1z"/>
|
||||||
|
<path d="M1800 2046 c0 -2 7 -6 15 -10 8 -3 15 -1 15 4 0 6 -7 10 -15 10 -8 0
|
||||||
|
-15 -2 -15 -4z"/>
|
||||||
|
<path d="M903 2029 c3 -9 0 -22 -6 -29 -16 -16 43 10 61 27 11 11 11 13 0 13
|
||||||
|
-8 0 -20 -6 -26 -12 -10 -10 -15 -10 -23 2 -9 13 -10 13 -6 -1z"/>
|
||||||
|
<path d="M1860 2030 c0 -13 11 -13 30 0 12 8 11 10 -7 10 -13 0 -23 -4 -23
|
||||||
|
-10z"/>
|
||||||
|
<path d="M1780 2017 c0 -10 -6 -14 -14 -11 -7 3 -16 -1 -19 -8 -3 -9 2 -14 14
|
||||||
|
-14 19 0 37 32 25 43 -3 4 -6 -1 -6 -10z"/>
|
||||||
|
<path d="M1105 2010 c-3 -5 1 -10 9 -10 9 0 16 5 16 10 0 6 -4 10 -9 10 -6 0
|
||||||
|
-13 -4 -16 -10z"/>
|
||||||
|
<path d="M1160 2005 c0 -17 7 -21 40 -22 22 -1 41 1 44 5 2 4 -12 8 -32 9 -19
|
||||||
|
1 -39 8 -43 15 -6 9 -9 7 -9 -7z"/>
|
||||||
|
<path d="M1896 2008 c3 -5 10 -6 15 -3 13 9 11 12 -6 12 -8 0 -12 -4 -9 -9z"/>
|
||||||
|
<path d="M1020 1990 c-11 -11 -22 -20 -25 -19 -3 0 -11 -2 -18 -4 -6 -3 -2 -6
|
||||||
|
10 -6 12 -1 28 6 36 14 9 8 17 11 20 7 2 -4 2 -1 1 6 -3 8 5 11 24 7 22 -4 24
|
||||||
|
-4 7 4 -29 14 -33 13 -55 -9z"/>
|
||||||
|
<path d="M1558 1993 c11 -19 9 -24 -9 -33 -11 -7 -27 -9 -35 -6 -8 3 -13 2 -9
|
||||||
|
-3 8 -14 -17 -19 -43 -9 -17 6 -23 5 -20 -4 9 -26 68 -20 117 11 26 17 50 31
|
||||||
|
54 31 3 0 5 -12 4 -27 -2 -18 -11 -32 -26 -39 -39 -20 -41 -29 -6 -29 18 0 31
|
||||||
|
3 28 8 -2 4 7 7 21 7 16 0 26 6 26 14 0 8 6 17 13 19 6 3 0 6 -15 6 -33 1 -43
|
||||||
|
15 -22 32 14 11 18 11 28 -1 6 -8 22 -15 36 -16 14 -1 34 -4 45 -8 19 -8 19
|
||||||
|
-7 -1 11 -12 10 -24 26 -27 36 -4 10 -12 15 -18 11 -8 -4 -7 -9 2 -15 11 -6
|
||||||
|
10 -9 -3 -14 -9 -4 -27 0 -40 8 -13 8 -36 13 -53 11 -16 -1 -36 2 -44 9 -12
|
||||||
|
10 -13 9 -3 -10z"/>
|
||||||
|
<path d="M1501 1989 c-14 -5 -36 -9 -50 -8 -44 3 -71 -1 -71 -10 0 -6 12 -6
|
||||||
|
29 -1 16 4 32 6 35 3 8 -9 96 7 96 18 0 11 -8 11 -39 -2z"/>
|
||||||
|
<path d="M815 1980 c-3 -6 1 -7 9 -4 18 7 21 14 7 14 -6 0 -13 -4 -16 -10z"/>
|
||||||
|
<path d="M866 1972 c-4 -10 -13 -19 -21 -20 -8 -1 -22 -3 -30 -4 -8 -1 -12 -4
|
||||||
|
-10 -8 2 -4 18 -5 35 -2 18 2 30 0 30 -7 0 -6 -9 -11 -20 -11 -11 0 -20 -5
|
||||||
|
-20 -11 0 -7 8 -9 23 -4 12 4 46 9 75 12 29 3 55 10 59 16 4 6 -7 7 -31 3 -24
|
||||||
|
-5 -36 -3 -36 4 0 6 5 8 10 5 6 -3 10 -1 10 4 0 15 -11 13 -34 -4 -25 -19 -38
|
||||||
|
-10 -30 21 7 28 -2 33 -10 6z"/>
|
||||||
|
<path d="M1300 1980 c-12 -8 -9 -10 13 -10 15 0 27 5 27 10 0 13 -20 13 -40 0z"/>
|
||||||
|
<path d="M1803 1980 c2 -8 10 -14 17 -12 7 1 20 -3 29 -10 9 -7 13 -8 9 -4 -4
|
||||||
|
5 -2 16 5 24 10 13 9 14 -3 7 -20 -12 -43 -12 -53 0 -5 6 -7 4 -4 -5z"/>
|
||||||
|
<path d="M1160 1948 c0 -4 4 -8 9 -8 6 0 12 4 15 8 3 5 -1 9 -9 9 -8 0 -15 -4
|
||||||
|
-15 -9z"/>
|
||||||
|
<path d="M988 1918 c-21 -21 -11 -24 15 -4 10 8 15 16 10 18 -4 3 -16 -3 -25
|
||||||
|
-14z"/>
|
||||||
|
<path d="M1080 1927 c0 -11 -50 -27 -125 -42 -27 -6 -135 -58 -135 -67 0 -10
|
||||||
|
44 3 55 16 13 16 57 32 125 46 30 7 64 17 75 23 28 15 88 20 80 7 -4 -6 6 -10
|
||||||
|
24 -10 16 0 33 5 36 10 4 7 -3 9 -22 5 -21 -5 -24 -4 -13 4 12 9 9 11 -15 12
|
||||||
|
-16 1 -42 3 -57 5 -17 2 -28 -2 -28 -9z"/>
|
||||||
|
<path d="M735 1910 c-26 -28 -15 -31 12 -3 12 13 19 23 14 23 -4 0 -16 -9 -26
|
||||||
|
-20z"/>
|
||||||
|
<path d="M1667 1894 c-28 -15 -36 -26 -14 -21 7 1 11 -7 10 -18 -1 -11 3 -23
|
||||||
|
9 -26 21 -13 -4 -30 -34 -24 -21 5 -28 4 -24 -4 4 -6 12 -9 17 -6 5 4 9 1 9
|
||||||
|
-4 0 -16 16 -13 34 4 8 8 21 15 28 15 9 1 8 4 -3 10 -17 10 -15 59 4 71 21 14
|
||||||
|
-10 16 -36 3z"/>
|
||||||
|
<path d="M1430 1885 c0 -8 2 -15 4 -15 2 0 6 7 10 15 3 8 1 15 -4 15 -6 0 -10
|
||||||
|
-7 -10 -15z"/>
|
||||||
|
<path d="M1485 1890 c-3 -5 3 -10 15 -10 12 0 18 5 15 10 -3 6 -10 10 -15 10
|
||||||
|
-5 0 -12 -4 -15 -10z"/>
|
||||||
|
<path d="M1255 1880 c3 -5 10 -10 16 -10 5 0 9 5 9 10 0 6 -7 10 -16 10 -8 0
|
||||||
|
-12 -4 -9 -10z"/>
|
||||||
|
<path d="M1720 1880 c-12 -8 -10 -10 8 -10 13 0 20 4 17 10 -7 12 -6 12 -25 0z"/>
|
||||||
|
<path d="M685 1870 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
|
||||||
|
-8 -4 -11 -10z"/>
|
||||||
|
<path d="M844 1864 c4 -11 1 -14 -12 -10 -13 4 -32 -7 -62 -34 -24 -22 -41
|
||||||
|
-43 -38 -46 3 -3 25 13 48 36 22 22 46 38 51 34 13 -7 41 15 33 27 -9 15 -26
|
||||||
|
10 -20 -7z"/>
|
||||||
|
<path d="M1088 1866 c-26 -20 -22 -26 12 -16 19 6 32 5 36 -2 4 -7 2 -8 -4 -4
|
||||||
|
-7 4 -12 2 -12 -3 0 -16 26 -13 40 4 15 19 13 21 -25 29 -18 3 -37 0 -47 -8z"/>
|
||||||
|
<path d="M1525 1870 c-3 -5 1 -10 10 -10 8 0 24 -6 35 -12 15 -10 22 -10 32 0
|
||||||
|
9 9 8 12 -5 12 -25 0 -30 1 -49 11 -10 6 -19 5 -23 -1z"/>
|
||||||
|
<path d="M1315 1849 c-17 -17 -17 -19 -2 -14 20 8 23 -5 5 -23 -21 -21 8 -14
|
||||||
|
33 8 13 11 20 20 14 19 -24 -3 -34 2 -29 16 8 20 3 18 -21 -6z"/>
|
||||||
|
<path d="M1030 1851 c0 -8 -8 -11 -20 -8 -11 3 -20 1 -20 -4 0 -5 -3 -8 -7 -7
|
||||||
|
-5 2 -19 -5 -33 -15 -30 -21 -22 -22 15 -3 15 8 33 12 40 10 14 -6 41 23 31
|
||||||
|
33 -3 3 -6 1 -6 -6z"/>
|
||||||
|
<path d="M1477 1842 c5 -12 1 -14 -20 -8 -14 3 -29 2 -32 -4 -3 -5 -14 -10
|
||||||
|
-24 -10 -10 0 -25 -8 -32 -17 -13 -16 -12 -17 3 -4 10 7 38 14 62 16 24 2 49
|
||||||
|
6 55 9 16 10 14 1 -9 -36 -11 -18 -20 -29 -20 -24 0 5 -6 4 -12 -1 -7 -6 -23
|
||||||
|
-13 -35 -16 -18 -4 -23 -1 -23 15 0 10 -5 16 -10 13 -6 -4 -8 -10 -5 -15 3 -4
|
||||||
|
-6 -10 -18 -14 -13 -3 -36 -17 -52 -30 -16 -14 -31 -24 -34 -24 -10 2 -81 -32
|
||||||
|
-81 -38 0 -3 12 -3 26 0 17 5 23 3 19 -4 -3 -5 -2 -10 4 -10 6 0 15 -15 20
|
||||||
|
-32 9 -30 10 -31 16 -11 7 22 31 37 45 28 4 -3 10 2 14 10 3 8 13 15 23 15 14
|
||||||
|
0 15 2 4 9 -11 7 -6 13 18 26 22 11 31 21 27 31 -3 9 -2 12 5 9 5 -4 27 6 47
|
||||||
|
22 20 15 38 26 39 23 2 -3 10 6 20 19 12 18 13 26 5 29 -7 2 -10 7 -8 11 2 3
|
||||||
|
-6 11 -19 17 -19 10 -22 9 -18 -4z m-87 -111 c0 -13 -82 -77 -107 -83 -13 -4
|
||||||
|
-23 -5 -23 -4 0 12 61 67 69 62 5 -4 15 3 21 14 11 20 40 28 40 11z"/>
|
||||||
|
<path d="M930 1835 c-14 -8 -20 -14 -14 -15 5 0 19 7 30 15 24 18 16 19 -16 0z"/>
|
||||||
|
<path d="M1220 1834 c0 -8 5 -12 10 -9 6 3 10 10 10 16 0 5 -4 9 -10 9 -5 0
|
||||||
|
-10 -7 -10 -16z"/>
|
||||||
|
<path d="M676 1809 c-21 -11 -41 -29 -43 -40 -3 -10 -9 -17 -12 -14 -4 2 -8
|
||||||
|
-3 -8 -12 0 -10 12 -2 32 21 17 20 35 36 41 36 6 0 19 7 30 15 28 21 5 18 -40
|
||||||
|
-6z"/>
|
||||||
|
<path d="M1085 1817 c-22 -13 -31 -37 -15 -37 6 0 10 5 10 11 0 7 8 18 18 25
|
||||||
|
21 17 14 17 -13 1z"/>
|
||||||
|
<path d="M1233 1818 c-13 -6 -23 -15 -23 -20 0 -4 -4 -8 -10 -8 -5 0 -7 7 -4
|
||||||
|
15 5 13 -13 15 -76 5 -3 0 1 -4 8 -9 12 -7 12 -12 1 -30 -8 -11 -18 -20 -24
|
||||||
|
-19 -5 1 -28 -8 -50 -20 -26 -15 -31 -20 -15 -16 14 3 29 10 35 15 5 5 17 9
|
||||||
|
27 9 9 0 21 6 25 14 9 16 43 24 58 14 6 -3 28 9 50 28 40 34 39 42 -2 22z"/>
|
||||||
|
<path d="M1285 1810 c3 -5 8 -10 11 -10 2 0 4 5 4 10 0 6 -5 10 -11 10 -5 0
|
||||||
|
-7 -4 -4 -10z"/>
|
||||||
|
<path d="M955 1774 c-71 -40 -62 -44 21 -9 32 13 53 24 46 25 -7 0 -10 5 -7
|
||||||
|
10 8 12 11 14 -60 -26z"/>
|
||||||
|
<path d="M804 1785 c-15 -11 -16 -14 -3 -15 8 0 24 7 35 15 15 11 16 14 3 15
|
||||||
|
-8 0 -24 -7 -35 -15z"/>
|
||||||
|
<path d="M1028 1779 c-14 -15 -15 -19 -4 -19 8 0 16 -6 19 -12 2 -7 4 2 5 20
|
||||||
|
0 17 0 32 -1 31 -1 0 -10 -9 -19 -20z"/>
|
||||||
|
<path d="M885 1780 c3 -6 -4 -14 -17 -19 -26 -10 -74 -56 -90 -86 -7 -12 5 -5
|
||||||
|
30 18 23 20 52 37 65 38 13 0 16 3 8 6 -11 4 -9 10 9 23 20 15 20 18 6 23 -10
|
||||||
|
4 -15 3 -11 -3z"/>
|
||||||
|
<path d="M1270 1775 c-7 -8 -18 -15 -24 -14 -24 3 -36 -2 -61 -25 -18 -17 -22
|
||||||
|
-26 -13 -29 7 -3 0 -6 -17 -6 -16 -1 -41 -4 -54 -8 -16 -4 -22 -3 -17 5 4 8
|
||||||
|
-3 8 -26 1 -35 -10 -89 -59 -65 -59 8 0 22 10 31 23 10 14 14 16 10 5 -7 -20
|
||||||
|
20 -26 31 -8 3 6 11 10 16 10 6 0 7 -5 3 -12 -5 -8 2 -9 26 -5 27 6 30 5 21
|
||||||
|
-7 -6 -7 -39 -19 -74 -25 -98 -19 -136 -38 -108 -55 5 -3 20 3 34 14 26 21 57
|
||||||
|
27 57 11 0 -10 -61 -41 -80 -41 -22 0 -80 -41 -80 -56 0 -12 4 -12 20 -2 11 7
|
||||||
|
20 18 20 25 0 14 2 15 40 19 14 1 36 6 50 10 24 7 24 7 8 -13 -10 -11 -18 -22
|
||||||
|
-18 -26 0 -3 8 -4 18 0 9 3 0 -11 -20 -33 -21 -22 -38 -45 -38 -52 0 -6 -9
|
||||||
|
-16 -20 -22 -11 -6 -18 -15 -15 -21 4 -5 -1 -9 -9 -9 -8 0 -17 4 -21 10 -3 5
|
||||||
|
-12 7 -20 3 -24 -9 -17 10 20 49 19 21 49 42 67 47 33 9 33 9 5 10 -19 1 -26
|
||||||
|
5 -21 13 5 7 3 8 -5 4 -7 -5 -10 -10 -8 -13 3 -2 -13 -22 -36 -43 -22 -22 -41
|
||||||
|
-50 -42 -62 -1 -13 -7 -22 -13 -21 -5 1 -15 -4 -21 -11 -10 -12 -6 -13 26 -5
|
||||||
|
56 13 73 11 67 -6 -4 -8 1 -17 10 -21 29 -11 13 -89 -32 -151 -16 -23 -19 -31
|
||||||
|
-8 -27 14 5 53 78 70 127 4 11 11 15 18 11 9 -5 8 -2 -1 10 -8 9 -10 16 -5 14
|
||||||
|
5 -1 21 10 37 25 15 16 32 25 39 21 7 -4 8 -3 4 4 -4 7 -3 12 2 12 10 0 35 50
|
||||||
|
28 57 -2 2 -7 -5 -11 -16 -3 -12 -19 -28 -34 -35 -15 -8 -33 -22 -40 -30 -19
|
||||||
|
-23 -34 -20 -26 5 5 15 4 19 -4 15 -25 -16 -9 27 25 64 37 41 70 51 80 25 4
|
||||||
|
-8 15 -15 26 -15 10 0 16 5 13 10 -4 6 -11 7 -17 4 -7 -4 -8 -2 -4 5 4 6 10 9
|
||||||
|
14 7 4 -2 23 4 42 15 19 10 47 24 63 31 15 7 27 16 27 20 0 8 -66 -19 -90 -38
|
||||||
|
-35 -26 -44 -28 -35 -8 4 10 7 27 7 37 0 9 3 15 9 12 5 -4 12 1 15 9 3 9 10
|
||||||
|
16 16 16 5 0 6 -4 3 -10 -8 -13 -1 -13 40 4 18 8 39 24 45 36 l11 21 -41 -21
|
||||||
|
c-57 -29 -73 -20 -25 13 24 16 33 27 23 27 -26 0 -68 -22 -68 -35 0 -7 -20
|
||||||
|
-29 -45 -49 -25 -21 -45 -45 -46 -54 0 -13 -3 -11 -9 5 -10 24 1 76 21 99 7 8
|
||||||
|
21 11 35 8 13 -3 24 -2 24 2 0 15 57 74 64 67 3 -3 6 0 6 8 0 19 23 69 32 69
|
||||||
|
3 0 4 -9 1 -20 -3 -11 0 -20 6 -20 6 0 11 4 11 10 0 5 7 7 15 4 8 -4 15 -1 15
|
||||||
|
5 0 6 6 11 13 11 6 0 21 8 32 16 20 15 19 16 -5 12 -16 -2 -24 2 -22 10 3 16
|
||||||
|
-3 15 -18 -3z m-200 -89 c0 -3 -4 -8 -10 -11 -5 -3 -10 -1 -10 4 0 6 5 11 10
|
||||||
|
11 6 0 10 -2 10 -4z m30 -186 c-6 -11 -18 -20 -27 -20 -14 0 -14 2 2 20 10 11
|
||||||
|
22 20 27 20 5 0 4 -9 -2 -20z"/>
|
||||||
|
<path d="M1542 1778 c-16 -16 -15 -25 3 -25 16 0 28 24 16 31 -4 3 -13 0 -19
|
||||||
|
-6z"/>
|
||||||
|
<path d="M683 1768 c-10 -18 -13 -39 -3 -33 8 5 21 45 15 45 -2 0 -7 -6 -12
|
||||||
|
-12z"/>
|
||||||
|
<path d="M950 1740 c-8 -5 -12 -11 -9 -14 2 -3 11 -1 19 4 8 5 12 11 9 14 -2
|
||||||
|
3 -11 1 -19 -4z"/>
|
||||||
|
<path d="M749 1713 l-24 -28 28 24 c25 23 32 31 24 31 -2 0 -14 -12 -28 -27z"/>
|
||||||
|
<path d="M1656 1725 c-4 -13 -2 -14 14 -5 23 13 26 20 6 20 -8 0 -16 -7 -20
|
||||||
|
-15z"/>
|
||||||
|
<path d="M975 1715 c-16 -7 -34 -11 -39 -7 -5 3 -6 0 -2 -6 4 -6 -2 -17 -14
|
||||||
|
-24 -11 -7 -20 -17 -20 -22 0 -5 11 1 25 14 14 13 28 20 32 15 5 -4 2 -10 -4
|
||||||
|
-12 -7 -3 -13 -10 -13 -16 0 -6 10 -1 23 11 13 12 30 22 38 22 12 0 12 2 0 9
|
||||||
|
-11 7 -11 9 0 13 8 3 12 7 9 10 -3 3 -18 -1 -35 -7z"/>
|
||||||
|
<path d="M1485 1699 c-4 -17 -9 -28 -12 -25 -3 3 -17 -4 -31 -14 -27 -22 -54
|
||||||
|
-28 -34 -8 7 7 12 16 12 21 0 5 -29 -20 -64 -56 -45 -45 -62 -58 -60 -43 2 12
|
||||||
|
-5 2 -16 -21 -12 -26 -31 -46 -50 -55 -17 -7 -30 -18 -30 -23 0 -11 -69 -52
|
||||||
|
-82 -48 -4 2 -5 -3 -2 -10 3 -8 0 -25 -5 -40 -7 -18 -7 -27 2 -30 7 -2 8 -8 4
|
||||||
|
-13 -13 -13 -27 18 -21 46 5 22 3 22 -17 -8 -13 -19 -27 -31 -35 -28 -8 3 -14
|
||||||
|
-1 -14 -9 0 -7 -6 -20 -13 -27 -27 -27 -67 -85 -67 -96 0 -7 5 -12 10 -12 6 0
|
||||||
|
10 4 10 10 0 5 16 32 36 60 29 40 42 50 65 50 40 0 37 -23 -6 -49 -20 -11 -38
|
||||||
|
-29 -42 -38 -3 -10 -15 -30 -25 -45 -16 -23 -17 -28 -4 -28 8 0 16 -8 19 -18
|
||||||
|
2 -9 4 0 3 21 0 39 17 61 41 52 7 -2 10 2 7 9 -5 12 63 92 71 84 4 -4 12 -40
|
||||||
|
14 -62 1 -4 7 -6 15 -3 24 9 39 -19 32 -58 -4 -19 -10 -35 -15 -35 -4 0 -8 -6
|
||||||
|
-9 -12 -1 -7 -4 -26 -7 -43 -5 -27 -3 -26 14 10 11 22 20 48 20 59 1 14 4 16
|
||||||
|
15 7 19 -16 5 -82 -29 -131 -16 -23 -32 -43 -37 -46 -4 -3 -8 -14 -8 -25 0
|
||||||
|
-29 22 -18 31 15 8 29 36 66 50 66 15 0 15 -35 0 -53 -18 -22 -22 -118 -4
|
||||||
|
-124 10 -4 10 37 -1 72 -2 6 1 19 6 30 8 18 8 18 4 -2 -2 -13 0 -23 6 -23 14
|
||||||
|
0 31 20 61 71 17 29 34 46 45 45 18 -1 19 2 21 64 1 28 5 40 16 40 15 0 19 15
|
||||||
|
16 57 -1 11 2 24 7 27 5 3 7 17 5 31 -3 13 -9 22 -14 20 -4 -3 -6 -12 -3 -20
|
||||||
|
3 -8 -1 -21 -9 -29 -8 -8 -12 -25 -10 -39 3 -14 -2 -29 -12 -37 -15 -12 -16
|
||||||
|
-8 -14 53 1 36 6 64 11 61 5 -3 6 6 2 20 -4 16 -2 26 4 26 6 0 18 15 27 33 12
|
||||||
|
25 13 43 6 73 l-9 39 -1 -40 c-2 -56 -11 -85 -28 -85 -8 0 -12 -4 -9 -9 3 -5
|
||||||
|
-12 -24 -33 -42 -22 -17 -42 -40 -44 -51 -3 -10 -11 -18 -17 -18 -8 0 -10 7
|
||||||
|
-5 23 4 12 8 36 9 53 1 17 10 43 20 59 18 27 20 28 27 10 6 -17 8 -15 8 8 1
|
||||||
|
16 7 43 15 60 45 106 49 131 6 42 -16 -33 -30 -53 -30 -45 0 8 -12 -9 -27 -38
|
||||||
|
-15 -29 -30 -52 -34 -52 -5 0 -6 7 -3 15 4 8 2 15 -4 15 -11 0 -22 -23 -22
|
||||||
|
-46 0 -8 3 -13 8 -13 20 3 32 -2 26 -12 -3 -6 -8 -32 -10 -58 -2 -37 0 -48 11
|
||||||
|
-48 8 0 15 5 15 11 0 15 48 66 61 66 7 0 9 -16 6 -42 -3 -22 -1 -55 4 -72 7
|
||||||
|
-24 6 -34 -7 -43 -10 -7 -11 -13 -5 -18 6 -3 11 -1 11 4 0 6 4 11 8 11 5 0 9
|
||||||
|
-7 9 -15 0 -8 -6 -15 -13 -15 -8 0 -21 -14 -30 -32 -9 -18 -23 -38 -30 -46 -8
|
||||||
|
-7 -14 -20 -14 -28 0 -8 -4 -14 -10 -14 -12 0 -12 2 -1 33 8 19 6 27 -5 31 -8
|
||||||
|
3 -14 10 -14 16 0 6 5 8 11 4 9 -5 10 1 5 20 -4 15 -2 26 4 26 6 0 6 9 -1 26
|
||||||
|
-5 15 -7 36 -4 48 5 15 4 17 -3 7 -10 -14 -17 -2 -27 51 -3 12 -9 24 -15 26
|
||||||
|
-5 2 -10 13 -10 25 -1 22 -1 22 -17 2 -15 -19 -15 -18 -9 14 3 19 17 49 31 67
|
||||||
|
27 36 30 44 15 44 -5 0 -10 -7 -10 -15 0 -8 -6 -15 -14 -15 -8 0 -16 -10 -18
|
||||||
|
-22 -5 -36 -33 -28 -36 11 -2 21 0 31 7 26 6 -3 11 -1 11 4 0 6 6 11 13 11 7
|
||||||
|
0 32 16 55 36 23 19 42 32 42 29 0 -4 6 -2 14 4 11 10 15 8 19 -6 3 -10 5 -3
|
||||||
|
6 15 2 61 111 137 111 77 0 -7 -4 -15 -8 -17 -9 -4 -52 -63 -52 -72 0 -19 23
|
||||||
|
-1 31 24 6 16 15 28 19 25 5 -3 15 14 22 37 16 47 36 55 54 21 6 -13 14 -20
|
||||||
|
17 -16 4 3 7 -3 7 -15 0 -14 -5 -20 -13 -16 -14 5 -45 -69 -39 -93 2 -7 -2
|
||||||
|
-13 -8 -13 -5 0 -10 6 -11 13 -4 58 -18 92 -20 46 0 -13 -2 -34 -3 -46 -4 -31
|
||||||
|
11 -53 23 -34 5 7 14 10 20 7 7 -5 11 2 11 18 0 31 18 73 45 102 11 12 26 30
|
||||||
|
34 39 8 10 23 15 37 12 17 -3 28 3 41 22 14 22 14 24 1 13 -9 -7 -25 -15 -37
|
||||||
|
-17 -18 -3 -20 -1 -11 15 19 35 10 34 -25 -4 -19 -21 -39 -35 -46 -31 -7 5 2
|
||||||
|
20 25 44 26 27 33 40 26 51 -7 10 -10 11 -10 3 0 -7 -11 -13 -25 -13 -27 0
|
||||||
|
-34 17 -10 26 8 4 12 10 9 15 -10 17 -22 9 -29 -22z m19 -59 c-15 -16 -31 -27
|
||||||
|
-36 -24 -12 8 31 54 49 54 8 0 3 -12 -13 -30z m-324 -374 c0 -3 -4 -8 -10 -11
|
||||||
|
-5 -3 -10 -1 -10 4 0 6 5 11 10 11 6 0 10 -2 10 -4z"/>
|
||||||
|
<path d="M906 1707 c3 -10 9 -15 12 -12 3 3 0 11 -7 18 -10 9 -11 8 -5 -6z"/>
|
||||||
|
<path d="M1438 1713 c-20 -5 -24 -23 -5 -23 6 0 21 7 31 15 20 15 11 18 -26 8z"/>
|
||||||
|
<path d="M620 1689 c-22 -18 -22 -19 -3 -10 12 6 25 16 28 21 9 15 3 12 -25
|
||||||
|
-11z"/>
|
||||||
|
<path d="M833 1685 l-28 -26 38 22 c37 21 45 29 28 29 -6 0 -23 -11 -38 -25z"/>
|
||||||
|
<path d="M712 1645 c-20 -19 -32 -35 -26 -35 5 0 25 16 44 35 44 45 31 45 -18
|
||||||
|
0z"/>
|
||||||
|
<path d="M870 1670 c-9 -6 -10 -10 -3 -10 6 0 15 5 18 10 8 12 4 12 -15 0z"/>
|
||||||
|
<path d="M1645 1640 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
|
||||||
|
-8 -4 -11 -10z"/>
|
||||||
|
<path d="M950 1626 c0 -8 -6 -17 -12 -19 -10 -4 -10 -6 -1 -6 7 -1 17 8 23 19
|
||||||
|
7 14 7 20 0 20 -5 0 -10 -6 -10 -14z"/>
|
||||||
|
<path d="M719 1613 c-13 -16 -12 -17 4 -4 9 7 17 15 17 17 0 8 -8 3 -21 -13z"/>
|
||||||
|
<path d="M774 1621 c3 -5 -12 -25 -34 -45 -22 -20 -40 -38 -40 -41 0 -13 20
|
||||||
|
-2 53 30 36 35 48 65 26 65 -5 0 -8 -4 -5 -9z"/>
|
||||||
|
<path d="M842 1606 c-15 -17 -19 -25 -10 -28 8 -3 2 -16 -19 -36 -17 -18 -28
|
||||||
|
-32 -23 -32 5 0 12 5 15 10 4 6 10 8 15 5 11 -7 23 13 26 43 2 13 11 33 20 43
|
||||||
|
25 28 3 23 -24 -5z"/>
|
||||||
|
<path d="M555 1609 c-11 -17 1 -21 15 -4 8 9 8 15 2 15 -6 0 -14 -5 -17 -11z"/>
|
||||||
|
<path d="M900 1610 c0 -5 5 -10 11 -10 5 0 7 5 4 10 -3 6 -8 10 -11 10 -2 0
|
||||||
|
-4 -4 -4 -10z"/>
|
||||||
|
<path d="M1627 1579 c-21 -22 -37 -47 -37 -56 0 -8 -9 -26 -20 -40 -15 -19
|
||||||
|
-17 -30 -10 -44 10 -17 5 -60 -8 -84 -10 -16 -11 -55 -2 -55 4 0 7 10 6 23 -2
|
||||||
|
12 3 28 10 35 10 9 14 -4 18 -66 2 -42 8 -96 11 -118 4 -26 3 -44 -4 -48 -6
|
||||||
|
-4 -11 -12 -11 -19 0 -6 11 0 25 15 25 27 33 44 15 33 -5 -3 -7 9 -5 30 4 26
|
||||||
|
1 35 -10 35 -8 0 -15 8 -14 18 0 10 3 12 6 5 10 -26 25 -13 20 17 -4 16 0 48
|
||||||
|
8 71 20 56 19 60 -6 20 -25 -42 -37 -34 -14 11 22 42 36 98 24 98 -8 0 -13
|
||||||
|
-22 -10 -42 0 -5 -3 -8 -9 -8 -5 0 -10 -7 -10 -15 0 -8 -4 -15 -10 -15 -13 0
|
||||||
|
-13 45 1 93 7 27 15 37 25 33 17 -6 18 -1 4 24 -7 13 -7 21 2 26 7 4 8 3 4 -4
|
||||||
|
-4 -7 -5 -12 -2 -12 3 0 15 18 26 40 11 22 19 40 17 40 -2 0 -20 -18 -40 -41z"/>
|
||||||
|
<path d="M1696 1594 c-11 -7 -16 -17 -13 -21 4 -4 15 1 24 10 22 21 14 30 -11
|
||||||
|
11z"/>
|
||||||
|
<path d="M1442 1580 c-9 -14 -9 -20 -1 -20 8 0 7 -6 -2 -17 -11 -15 -11 -16 1
|
||||||
|
-6 7 7 14 24 16 38 3 30 2 31 -14 5z"/>
|
||||||
|
<path d="M652 1561 c-17 -17 -32 -33 -32 -37 0 -5 17 8 37 27 21 18 40 35 42
|
||||||
|
37 2 1 0 2 -5 2 -5 0 -24 -13 -42 -29z"/>
|
||||||
|
<path d="M1765 1581 c-3 -5 -2 -12 3 -15 5 -3 9 1 9 9 0 17 -3 19 -12 6z"/>
|
||||||
|
<path d="M488 1573 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
|
||||||
|
<path d="M1060 1565 c-7 -9 -8 -15 -2 -15 5 0 12 7 16 15 3 8 4 15 2 15 -2 0
|
||||||
|
-9 -7 -16 -15z"/>
|
||||||
|
<path d="M1722 1568 c-18 -18 -15 -38 5 -31 11 4 13 8 5 11 -10 3 -10 7 -1 18
|
||||||
|
14 17 8 19 -9 2z"/>
|
||||||
|
<path d="M784 1561 c7 -11 -28 -43 -38 -34 -4 4 -4 0 0 -10 3 -9 2 -17 -3 -17
|
||||||
|
-5 0 -18 -8 -29 -18 -10 -10 -27 -21 -36 -25 -18 -6 -25 -27 -10 -27 12 0 132
|
||||||
|
118 132 130 0 5 -5 10 -11 10 -5 0 -8 -4 -5 -9z"/>
|
||||||
|
<path d="M528 1553 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
|
||||||
|
<path d="M1544 1524 c3 -9 6 -18 6 -20 0 -3 4 -3 10 1 11 7 2 35 -12 35 -6 0
|
||||||
|
-7 -7 -4 -16z"/>
|
||||||
|
<path d="M1650 1510 c-7 -14 -7 -20 0 -20 5 0 10 6 10 14 0 8 6 17 13 19 9 4
|
||||||
|
9 6 0 6 -7 1 -17 -8 -23 -19z"/>
|
||||||
|
<path d="M820 1490 c-14 -10 -22 -21 -19 -24 3 -3 16 5 29 19 28 30 24 32 -10
|
||||||
|
5z"/>
|
||||||
|
<path d="M1509 1503 c-2 -29 -11 -58 -20 -70 -9 -10 -8 -13 4 -13 13 0 14 -6
|
||||||
|
6 -36 -6 -19 -7 -48 -4 -64 3 -15 2 -31 -4 -34 -6 -4 -7 -14 -4 -22 5 -13 1
|
||||||
|
-15 -18 -9 -20 7 -22 5 -13 -6 7 -8 14 -33 18 -56 4 -34 3 -41 -9 -36 -17 6
|
||||||
|
-20 -14 -5 -37 8 -12 10 -12 10 5 2 44 21 -30 25 -93 1 -35 6 -66 9 -69 3 -4
|
||||||
|
6 0 6 8 1 8 7 -2 14 -21 8 -19 20 -49 27 -66 11 -28 9 -39 -13 -101 -21 -56
|
||||||
|
-23 -70 -12 -70 8 0 13 6 10 13 -3 8 5 20 17 29 20 14 21 15 2 16 -19 0 -19 1
|
||||||
|
-2 8 10 4 18 10 18 14 -1 4 -1 19 -1 33 0 22 2 25 15 14 8 -7 15 -18 15 -24 0
|
||||||
|
-6 9 -17 21 -24 19 -12 20 -12 13 10 -4 13 -8 42 -9 66 -2 24 -6 46 -9 49 -4
|
||||||
|
4 -6 -6 -5 -23 4 -74 -17 -69 -27 7 -4 27 -10 52 -14 54 -5 3 -6 -4 -3 -15 10
|
||||||
|
-37 -8 -20 -29 28 -34 76 -62 282 -39 282 6 0 11 10 11 23 0 13 3 27 6 30 11
|
||||||
|
10 13 67 3 67 -11 0 -6 45 10 83 9 23 8 31 -4 43 -8 9 -15 12 -16 7z"/>
|
||||||
|
<path d="M1665 1471 c-12 -40 -8 -102 9 -119 16 -16 16 -15 7 11 -8 21 -8 27
|
||||||
|
2 26 23 -3 40 1 34 8 -4 3 -2 12 4 20 6 7 8 13 3 13 -5 0 -2 5 6 10 11 7 11
|
||||||
|
10 2 10 -7 0 -18 -13 -23 -30 -14 -38 -30 -29 -25 14 5 41 -9 69 -19 37z"/>
|
||||||
|
<path d="M532 1467 c-17 -20 -12 -40 6 -28 8 5 12 16 10 26 -3 17 -4 17 -16 2z"/>
|
||||||
|
<path d="M638 1473 c6 -2 18 -2 25 0 6 3 1 5 -13 5 -14 0 -19 -2 -12 -5z"/>
|
||||||
|
<path d="M761 1466 c-13 -7 -19 -16 -14 -19 6 -4 18 2 28 13 21 23 19 24 -14
|
||||||
|
6z"/>
|
||||||
|
<path d="M482 1450 c-29 -28 -28 -34 3 -14 14 9 25 20 25 25 0 13 -4 11 -28
|
||||||
|
-11z"/>
|
||||||
|
<path d="M588 1449 l-23 -20 25 13 c14 6 31 11 38 10 17 -4 15 5 -3 11 -7 3
|
||||||
|
-24 -3 -37 -14z"/>
|
||||||
|
<path d="M701 1430 c-13 -11 -18 -20 -13 -20 6 0 14 5 17 10 4 6 10 8 14 6 4
|
||||||
|
-3 8 1 8 9 0 8 -1 15 -1 15 -1 -1 -12 -10 -25 -20z"/>
|
||||||
|
<path d="M815 1440 c-3 -5 3 -10 14 -10 12 0 21 5 21 10 0 6 -6 10 -14 10 -8
|
||||||
|
0 -18 -4 -21 -10z"/>
|
||||||
|
<path d="M913 1428 c-4 -7 -8 -20 -10 -28 -1 -10 3 -9 13 4 14 19 18 36 10 36
|
||||||
|
-3 0 -8 -6 -13 -12z"/>
|
||||||
|
<path d="M1026 1423 c-6 -14 -5 -15 5 -6 7 7 10 15 7 18 -3 3 -9 -2 -12 -12z"/>
|
||||||
|
<path d="M1335 1420 c-5 -14 -2 -28 8 -37 13 -13 14 -11 11 18 -5 41 -10 46
|
||||||
|
-19 19z"/>
|
||||||
|
<path d="M778 1423 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
|
||||||
|
<path d="M545 1400 c-3 -10 -19 -30 -36 -44 -16 -14 -28 -29 -27 -33 2 -5 -7
|
||||||
|
-21 -18 -36 -16 -19 -27 -25 -40 -21 -17 6 -17 5 0 -8 19 -15 19 -16 -10 -48
|
||||||
|
-16 -17 -40 -36 -54 -42 -22 -9 -23 -11 -7 -17 16 -6 15 -9 -9 -31 -15 -14
|
||||||
|
-24 -20 -21 -13 4 6 3 23 -3 38 -8 20 -7 27 4 31 16 6 40 64 27 64 -4 0 -13
|
||||||
|
-11 -19 -24 -6 -13 -18 -23 -28 -23 -15 2 -17 -6 -15 -49 1 -33 -2 -55 -10
|
||||||
|
-60 -9 -5 -7 -9 7 -15 15 -6 35 10 102 79 45 47 81 90 79 96 -6 15 50 84 74
|
||||||
|
91 10 4 17 11 13 16 -4 6 -11 6 -20 -2 -8 -7 -14 -7 -14 -1 0 5 7 15 16 22 21
|
||||||
|
18 37 50 25 50 -5 0 -12 -9 -16 -20z"/>
|
||||||
|
<path d="M609 1400 c-21 -17 -21 -19 -4 -13 11 3 29 12 40 20 16 10 16 13 3
|
||||||
|
13 -9 0 -26 -9 -39 -20z"/>
|
||||||
|
<path d="M785 1400 c-3 -6 1 -7 9 -4 18 7 21 14 7 14 -6 0 -13 -4 -16 -10z"/>
|
||||||
|
<path d="M1429 1403 c0 -5 0 -35 1 -68 1 -33 -4 -73 -10 -89 -11 -30 0 -56 24
|
||||||
|
-56 8 0 8 2 0 8 -6 4 -10 18 -8 32 1 14 3 31 3 39 1 7 7 11 14 8 10 -4 12 7 9
|
||||||
|
46 -2 29 -9 55 -16 59 -8 6 -7 8 2 8 6 0 12 5 12 10 0 11 -30 14 -31 3z"/>
|
||||||
|
<path d="M469 1387 c5 -5 17 -5 28 2 16 9 15 10 -9 9 -16 -2 -24 -6 -19 -11z"/>
|
||||||
|
<path d="M620 1379 c-26 -14 -30 -19 -15 -19 11 0 34 9 50 20 38 25 12 24 -35
|
||||||
|
-1z"/>
|
||||||
|
<path d="M710 1375 c-30 -17 -43 -19 -35 -5 3 6 1 10 -5 10 -7 0 -10 -6 -7
|
||||||
|
-12 5 -14 56 -12 71 3 17 15 2 18 -24 4z"/>
|
||||||
|
<path d="M733 1347 c-27 -29 -27 -29 -4 -17 14 7 33 20 43 30 14 13 15 17 3
|
||||||
|
17 -8 0 -27 -14 -42 -30z"/>
|
||||||
|
<path d="M1402 1345 c0 -16 2 -22 5 -12 2 9 2 23 0 30 -3 6 -5 -1 -5 -18z"/>
|
||||||
|
<path d="M1300 1340 c0 -11 4 -20 8 -20 4 0 8 9 9 20 1 11 -2 20 -8 20 -5 0
|
||||||
|
-9 -9 -9 -20z"/>
|
||||||
|
<path d="M1270 1335 c0 -8 2 -15 4 -15 2 0 6 7 10 15 3 8 1 15 -4 15 -6 0 -10
|
||||||
|
-7 -10 -15z"/>
|
||||||
|
<path d="M574 1314 c-5 -21 -11 -25 -30 -21 -19 3 -22 2 -13 -9 9 -11 7 -15
|
||||||
|
-5 -20 -9 -3 -14 -10 -11 -15 3 -4 -7 -15 -22 -24 -15 -9 -19 -13 -8 -10 11 4
|
||||||
|
28 10 37 12 10 3 15 10 11 16 -4 6 0 8 10 4 10 -3 17 -2 17 3 0 5 10 19 23 31
|
||||||
|
12 12 15 18 7 14 -12 -6 -13 -5 -2 6 14 16 16 39 2 39 -5 0 -13 -12 -16 -26z"/>
|
||||||
|
<path d="M642 1310 c-63 -46 -53 -49 27 -9 20 10 30 18 23 19 -7 0 -10 5 -7
|
||||||
|
10 10 16 0 12 -43 -20z"/>
|
||||||
|
<path d="M410 1314 c0 -8 5 -12 10 -9 6 4 8 11 5 16 -9 14 -15 11 -15 -7z"/>
|
||||||
|
<path d="M803 1313 c-7 -2 -13 -11 -13 -19 0 -8 -7 -14 -16 -14 -10 0 -14 -6
|
||||||
|
-11 -14 8 -20 -16 -26 -29 -8 -10 14 -12 13 -18 -4 -3 -11 -20 -28 -37 -38
|
||||||
|
-17 -10 -28 -20 -25 -23 3 -3 16 1 28 9 13 8 32 18 42 21 15 6 17 5 8 -6 -7
|
||||||
|
-8 -9 -16 -6 -20 3 -3 -7 -13 -23 -23 -15 -10 -30 -19 -32 -20 -1 -2 0 -8 4
|
||||||
|
-14 9 -14 41 19 81 82 16 27 35 48 42 48 7 0 10 5 7 10 -4 6 -1 9 5 8 7 -2 14
|
||||||
|
5 15 15 3 17 -1 19 -22 10z"/>
|
||||||
|
<path d="M900 1309 c0 -19 -52 -129 -61 -129 -5 0 -9 -4 -9 -10 0 -20 28 3 49
|
||||||
|
41 29 51 42 97 30 104 -5 4 -9 1 -9 -6z"/>
|
||||||
|
<path d="M1663 1285 c-7 -21 -8 -46 -3 -63 l8 -27 2 30 c1 17 4 45 6 63 5 42
|
||||||
|
0 41 -13 -3z"/>
|
||||||
|
<path d="M1710 1301 c0 -5 5 -13 10 -16 6 -3 10 -2 10 4 0 5 -4 13 -10 16 -5
|
||||||
|
3 -10 2 -10 -4z"/>
|
||||||
|
<path d="M735 1280 c3 -5 8 -10 11 -10 2 0 4 5 4 10 0 6 -5 10 -11 10 -5 0 -7
|
||||||
|
-4 -4 -10z"/>
|
||||||
|
<path d="M655 1264 c-11 -8 -25 -12 -33 -9 -7 2 -10 0 -7 -5 3 -6 -2 -10 -12
|
||||||
|
-10 -10 -1 -29 -8 -43 -16 -17 -11 -19 -13 -5 -9 25 9 46 13 65 15 17 1 81 50
|
||||||
|
65 50 -5 -1 -19 -7 -30 -16z"/>
|
||||||
|
<path d="M1696 1265 c-3 -8 -1 -15 4 -15 6 0 10 7 10 15 0 8 -2 15 -4 15 -2 0
|
||||||
|
-6 -7 -10 -15z"/>
|
||||||
|
<path d="M842 1239 c-7 -11 -27 -26 -44 -33 -24 -10 -27 -14 -14 -19 21 -8 63
|
||||||
|
18 72 45 8 27 1 31 -14 7z"/>
|
||||||
|
<path d="M1534 1252 c4 -7 2 -12 -5 -12 -8 0 -10 -12 -5 -37 11 -68 17 -97 21
|
||||||
|
-101 2 -2 -1 -15 -6 -29 -6 -16 -6 -31 1 -41 8 -15 10 -15 10 3 1 11 3 29 5
|
||||||
|
40 14 84 13 115 -2 115 -11 0 -14 8 -11 30 3 17 0 34 -5 37 -6 3 -7 1 -3 -5z"/>
|
||||||
|
<path d="M806 1235 c-3 -8 -1 -15 3 -15 5 0 11 7 15 15 3 8 1 15 -3 15 -5 0
|
||||||
|
-11 -7 -15 -15z"/>
|
||||||
|
<path d="M1296 1223 c-8 -25 -6 -39 6 -35 6 2 8 -8 6 -23 -3 -14 -1 -25 4 -25
|
||||||
|
9 0 5 86 -5 96 -3 3 -8 -3 -11 -13z"/>
|
||||||
|
<path d="M1693 1225 c0 -8 4 -15 9 -15 4 0 8 7 8 15 0 8 -4 15 -8 15 -5 0 -9
|
||||||
|
-7 -9 -15z"/>
|
||||||
|
<path d="M1730 1230 c0 -5 5 -10 10 -10 6 0 10 5 10 10 0 6 -4 10 -10 10 -5 0
|
||||||
|
-10 -4 -10 -10z"/>
|
||||||
|
<path d="M1151 1207 c-16 -41 -13 -57 5 -24 8 16 13 34 11 40 -3 7 -10 -1 -16
|
||||||
|
-16z"/>
|
||||||
|
<path d="M1040 1194 c0 -8 5 -12 10 -9 6 4 8 11 5 16 -9 14 -15 11 -15 -7z"/>
|
||||||
|
<path d="M1102 1189 c3 -8 -3 -21 -12 -29 -9 -8 -22 -30 -30 -48 -8 -21 -16
|
||||||
|
-29 -20 -22 -5 9 -9 9 -14 1 -4 -6 -4 -15 -1 -21 3 -5 1 -10 -6 -10 -10 0 -10
|
||||||
|
-3 -2 -11 13 -13 36 -3 28 12 -4 5 -1 9 4 9 6 0 11 6 11 14 0 8 3 16 8 18 4 2
|
||||||
|
19 19 33 38 23 31 25 37 11 49 -13 12 -14 12 -10 0z"/>
|
||||||
|
<path d="M480 1181 c0 -7 -7 -20 -16 -29 -18 -17 -48 -26 -38 -11 3 5 1 9 -4
|
||||||
|
9 -8 0 -23 -22 -26 -40 -1 -3 -13 -22 -28 -43 -16 -25 -28 -34 -33 -27 -4 6 1
|
||||||
|
21 11 34 11 12 8 11 -6 -4 -21 -21 -35 -25 -85 -26 -86 -2 -123 -8 -137 -22
|
||||||
|
-6 -6 -16 -12 -21 -12 -4 0 -28 -14 -53 -30 -29 -21 -44 -38 -44 -52 0 -20 1
|
||||||
|
-20 13 -5 6 9 28 27 47 41 19 14 45 33 57 41 17 13 26 14 42 5 14 -7 21 -7 21
|
||||||
|
0 0 5 9 10 19 10 11 0 23 5 26 10 13 20 53 10 79 -21 31 -37 33 -69 6 -94 -13
|
||||||
|
-12 -18 -25 -14 -38 4 -13 2 -17 -5 -13 -6 4 -11 1 -11 -7 0 -19 -28 -42 -38
|
||||||
|
-32 -4 4 -1 10 6 12 6 3 12 9 12 15 0 16 -28 -11 -43 -40 -14 -29 -63 -69 -73
|
||||||
|
-59 -4 3 6 20 20 38 28 33 29 53 1 25 -20 -20 -26 -20 -19 -1 4 9 0 15 -10 15
|
||||||
|
-9 0 -15 -3 -15 -7 1 -5 1 -15 0 -23 -1 -12 -3 -13 -10 -1 -6 9 -11 10 -15 2
|
||||||
|
-4 -6 -4 -11 0 -11 4 0 -1 -9 -10 -19 -10 -11 -25 -17 -34 -14 -13 4 -12 2 3
|
||||||
|
-10 16 -13 17 -17 6 -31 -12 -14 -12 -16 4 -10 13 5 16 4 11 -4 -4 -7 -2 -12
|
||||||
|
3 -12 6 0 11 7 11 15 0 8 6 15 14 15 8 0 16 5 18 12 3 8 13 2 28 -15 13 -15
|
||||||
|
30 -27 37 -27 19 0 16 -10 -12 -36 -14 -13 -25 -27 -25 -32 0 -14 -57 -61 -66
|
||||||
|
-54 -4 4 -4 1 1 -7 4 -8 2 -17 -6 -22 -8 -5 -9 -9 -3 -9 6 0 21 11 34 25 12
|
||||||
|
13 28 22 34 18 6 -3 8 -3 4 1 -10 12 120 179 133 171 13 -9 21 15 9 30 -9 11
|
||||||
|
-15 9 -35 -11 -14 -14 -25 -29 -25 -34 0 -11 -3 -12 -39 -14 -40 -2 -33 17 20
|
||||||
|
53 60 41 104 94 95 117 -4 10 -1 14 6 12 20 -7 21 -38 3 -58 -11 -12 -13 -20
|
||||||
|
-6 -25 6 -3 11 1 11 9 0 8 5 18 11 22 8 4 9 -1 3 -17 -4 -13 -9 -37 -10 -52
|
||||||
|
-1 -15 -7 -34 -13 -41 -6 -7 -8 -15 -6 -18 3 -3 1 -17 -4 -32 -9 -23 -9 -25 7
|
||||||
|
-15 16 10 19 21 25 89 2 30 11 38 22 20 3 -5 14 -9 23 -9 15 1 15 2 -1 6 -9 2
|
||||||
|
-14 9 -11 15 4 7 2 8 -4 4 -7 -4 -12 -1 -12 8 0 8 10 17 23 19 l22 4 -22 2
|
||||||
|
c-13 0 -23 4 -24 9 -8 75 -5 102 12 102 6 0 7 -10 3 -22 -6 -20 -4 -20 16 5
|
||||||
|
12 15 20 31 16 34 -3 4 -6 1 -6 -5 0 -7 -4 -12 -10 -12 -5 0 -10 7 -10 15 0 9
|
||||||
|
-6 12 -15 9 -8 -4 -15 -2 -15 2 0 12 20 41 20 31 0 -5 8 -1 17 9 13 13 15 18
|
||||||
|
5 22 -9 3 -6 10 9 23 12 10 17 19 11 19 -14 0 76 84 92 86 24 3 56 17 56 25 0
|
||||||
|
16 -30 10 -37 -7 -6 -15 -8 -15 -20 2 -7 10 -13 13 -13 5z m-355 -415 c-18
|
||||||
|
-28 -42 -40 -50 -26 -4 6 0 10 8 10 8 0 22 9 32 20 25 27 29 25 10 -4z"/>
|
||||||
|
<path d="M922 1156 c-21 -20 -42 -33 -46 -29 -3 3 -6 -1 -6 -10 0 -27 22 -19
|
||||||
|
64 23 50 51 41 63 -12 16z"/>
|
||||||
|
<path d="M620 1170 c-12 -8 -10 -10 8 -10 13 0 20 4 17 10 -7 12 -6 12 -25 0z"/>
|
||||||
|
<path d="M743 1156 c-15 -13 -26 -26 -23 -29 3 -3 21 8 40 24 19 16 29 29 23
|
||||||
|
29 -7 0 -25 -11 -40 -24z"/>
|
||||||
|
<path d="M1393 1113 c-11 -38 -26 -67 -32 -65 -6 1 -11 -1 -12 -5 0 -5 -2 -16
|
||||||
|
-4 -27 -3 -14 -1 -16 8 -7 14 14 30 15 22 1 -3 -5 -1 -10 5 -10 7 0 10 6 7 14
|
||||||
|
-3 8 0 33 7 56 17 57 31 35 35 -55 2 -38 7 -75 12 -82 7 -7 6 -14 -1 -18 -14
|
||||||
|
-8 -13 -52 2 -61 6 -4 8 -3 4 4 -3 6 -1 23 6 39 6 15 12 42 13 58 1 17 5 41 8
|
||||||
|
55 4 14 4 19 1 13 -13 -30 -26 -11 -20 28 4 26 3 38 -4 34 -6 -3 -7 0 -4 9 4
|
||||||
|
11 0 16 -13 16 -16 0 -18 6 -15 35 6 53 -2 42 -25 -32z"/>
|
||||||
|
<path d="M1683 1174 c3 -3 1 -17 -4 -31 -6 -16 -6 -31 1 -41 7 -14 9 -11 10
|
||||||
|
15 0 17 3 38 6 47 4 10 1 16 -7 16 -7 0 -10 -3 -6 -6z"/>
|
||||||
|
<path d="M749 1120 c-28 -27 -56 -50 -63 -50 -12 0 -66 -39 -54 -40 4 0 24 9
|
||||||
|
45 19 21 11 45 21 53 23 8 2 27 19 42 38 15 19 32 41 38 48 7 8 7 12 0 12 -5
|
||||||
|
0 -33 -22 -61 -50z"/>
|
||||||
|
<path d="M1263 1149 c-2 -17 1 -18 13 -9 17 15 18 30 1 30 -7 0 -13 -9 -14
|
||||||
|
-21z"/>
|
||||||
|
<path d="M560 1150 c-13 -9 -13 -10 0 -10 8 0 22 5 30 10 13 9 13 10 0 10 -8
|
||||||
|
0 -22 -5 -30 -10z"/>
|
||||||
|
<path d="M1119 1133 c-10 -11 -10 -14 -1 -9 6 4 12 2 12 -3 0 -6 -5 -11 -12
|
||||||
|
-11 -6 0 -8 -3 -4 -7 9 -9 -54 -73 -73 -74 -9 0 -11 -3 -5 -5 22 -9 87 45 101
|
||||||
|
84 15 45 15 42 4 42 -5 0 -15 -8 -22 -17z"/>
|
||||||
|
<path d="M494 1124 c-18 -15 -18 -15 2 -5 12 6 24 7 28 4 3 -3 6 -1 6 5 0 16
|
||||||
|
-12 15 -36 -4z"/>
|
||||||
|
<path d="M591 1124 c0 -11 3 -14 6 -6 3 7 2 16 -1 19 -3 4 -6 -2 -5 -13z"/>
|
||||||
|
<path d="M610 1131 c0 -5 5 -13 10 -16 6 -3 10 -2 10 4 0 5 -4 13 -10 16 -5 3
|
||||||
|
-10 2 -10 -4z"/>
|
||||||
|
<path d="M953 1110 c-9 -11 -20 -15 -23 -10 -3 5 -14 -5 -24 -23 -14 -26 -14
|
||||||
|
-28 -1 -12 9 11 21 19 27 17 5 -1 16 2 22 8 7 5 15 7 18 5 3 -3 8 3 11 15 7
|
||||||
|
26 -8 26 -30 0z"/>
|
||||||
|
<path d="M1280 1115 c-9 -11 -9 -15 -1 -15 7 0 9 -5 5 -12 -4 -7 -3 -10 1 -5
|
||||||
|
8 6 16 47 10 47 -2 0 -8 -7 -15 -15z"/>
|
||||||
|
<path d="M790 1095 c-20 -24 -6 -27 20 -5 17 15 19 20 8 20 -9 0 -21 -7 -28
|
||||||
|
-15z"/>
|
||||||
|
<path d="M547 1084 c4 -10 10 -11 24 -3 24 13 24 14 -5 15 -17 1 -23 -2 -19
|
||||||
|
-12z"/>
|
||||||
|
<path d="M1625 1090 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
|
||||||
|
-8 -4 -11 -10z"/>
|
||||||
|
<path d="M864 1082 c3 -5 -4 -19 -15 -30 -11 -12 -16 -22 -11 -22 12 0 42 34
|
||||||
|
42 48 0 7 -5 12 -11 12 -5 0 -8 -4 -5 -8z"/>
|
||||||
|
<path d="M1261 1074 c0 -11 3 -14 6 -6 3 7 2 16 -1 19 -3 4 -6 -2 -5 -13z"/>
|
||||||
|
<path d="M440 1069 c0 -5 11 -9 25 -9 14 0 25 2 25 4 0 2 -11 6 -25 8 -14 3
|
||||||
|
-25 1 -25 -3z"/>
|
||||||
|
<path d="M967 1062 c-24 -27 -21 -36 4 -11 11 11 19 23 16 25 -2 2 -11 -4 -20
|
||||||
|
-14z"/>
|
||||||
|
<path d="M580 1055 c0 -8 2 -15 4 -15 2 0 6 7 10 15 3 8 1 15 -4 15 -6 0 -10
|
||||||
|
-7 -10 -15z"/>
|
||||||
|
<path d="M1612 1039 c-2 -18 2 -28 8 -24 5 3 8 15 7 25 -5 32 -13 31 -15 -1z"/>
|
||||||
|
<path d="M1143 1048 c-13 -16 -26 -58 -19 -58 8 0 38 61 33 66 -2 3 -9 -1 -14
|
||||||
|
-8z"/>
|
||||||
|
<path d="M694 1035 c-10 -8 -14 -15 -8 -15 6 0 17 7 24 15 16 19 9 19 -16 0z"/>
|
||||||
|
<path d="M722 1028 c-15 -15 -16 -31 0 -26 6 2 12 12 14 21 3 20 2 21 -14 5z"/>
|
||||||
|
<path d="M970 1000 c-17 -22 -27 -40 -21 -40 6 0 11 4 11 9 0 5 14 23 31 40
|
||||||
|
17 17 26 31 21 31 -6 0 -25 -18 -42 -40z"/>
|
||||||
|
<path d="M1095 1020 c-20 -22 -85 -63 -84 -52 5 30 -3 34 -19 9 -10 -15 -28
|
||||||
|
-36 -40 -48 -12 -11 -22 -24 -22 -29 0 -12 30 -14 30 -2 0 4 10 18 21 32 17
|
||||||
|
21 20 22 15 5 -6 -19 -5 -19 8 -2 8 10 17 18 20 18 15 -2 85 52 89 70 6 24 4
|
||||||
|
24 -18 -1z"/>
|
||||||
|
<path d="M1664 1015 c7 -36 12 -41 19 -16 3 11 -2 26 -10 32 -12 10 -13 8 -9
|
||||||
|
-16z"/>
|
||||||
|
<path d="M1710 1030 c0 -5 5 -10 10 -10 6 0 10 5 10 10 0 6 -4 10 -10 10 -5 0
|
||||||
|
-10 -4 -10 -10z"/>
|
||||||
|
<path d="M433 1023 c-7 -2 -13 -9 -13 -15 0 -7 4 -7 13 0 6 5 21 12 32 15 20
|
||||||
|
5 20 5 0 5 -11 0 -26 -2 -32 -5z"/>
|
||||||
|
<path d="M591 1013 c-1 -17 16 -25 25 -12 4 7 3 9 -4 5 -5 -3 -13 0 -15 6 -4
|
||||||
|
9 -6 10 -6 1z"/>
|
||||||
|
<path d="M790 1005 c-6 -8 -10 -19 -8 -24 4 -14 51 10 54 27 1 7 0 8 -3 2 -3
|
||||||
|
-8 -9 -8 -18 0 -9 7 -16 6 -25 -5z"/>
|
||||||
|
<path d="M912 1011 c-11 -6 -10 -10 4 -15 15 -6 13 -11 -14 -37 -18 -17 -32
|
||||||
|
-36 -32 -41 0 -5 -10 -6 -22 -3 -13 3 -18 3 -13 -2 10 -7 11 -15 6 -40 0 -4
|
||||||
|
-6 -2 -13 5 -7 7 -17 12 -22 12 -6 0 -1 -7 11 -15 18 -13 26 -13 43 -3 11 7
|
||||||
|
20 18 20 24 0 6 16 33 35 60 19 27 35 52 35 56 0 10 -22 10 -38 -1z"/>
|
||||||
|
<path d="M241 994 c0 -12 -18 -43 -40 -70 -36 -46 -37 -49 -16 -49 14 0 21 5
|
||||||
|
18 13 -7 17 17 44 33 38 8 -3 20 1 28 9 19 20 26 19 27 -2 0 -15 2 -15 8 2 8
|
||||||
|
21 4 45 -7 43 -24 -3 -41 4 -45 20 -4 13 -5 13 -6 -4z"/>
|
||||||
|
<path d="M455 994 c-16 -7 -33 -10 -37 -7 -5 2 -8 -3 -8 -13 0 -12 4 -14 13
|
||||||
|
-7 12 10 58 24 89 28 30 3 30 12 1 12 -16 0 -41 -6 -58 -13z"/>
|
||||||
|
<path d="M630 980 c-13 -11 -21 -22 -18 -25 3 -3 17 6 32 20 32 30 20 34 -14
|
||||||
|
5z"/>
|
||||||
|
<path d="M116 974 c-3 -8 -10 -13 -15 -9 -5 3 -13 -5 -17 -17 -4 -13 -11 -26
|
||||||
|
-17 -29 -6 -4 -5 -13 2 -25 9 -17 10 -17 11 -1 0 9 6 17 14 17 7 0 16 14 20
|
||||||
|
31 3 17 10 28 16 24 5 -3 10 -2 10 4 0 17 -18 21 -24 5z"/>
|
||||||
|
<path d="M1317 965 c-2 -14 -1 -27 2 -29 4 -3 8 -18 9 -35 2 -24 -1 -29 -13
|
||||||
|
-25 -10 4 -15 0 -15 -12 0 -11 -9 -32 -20 -49 -11 -16 -20 -37 -20 -45 0 -8
|
||||||
|
11 5 25 29 14 23 25 50 25 59 0 14 2 14 13 -1 11 -15 12 -14 14 10 0 15 6 29
|
||||||
|
12 31 6 2 10 22 10 45 -2 31 -3 34 -6 14 -2 -15 -8 -25 -13 -22 -4 3 -6 14 -3
|
||||||
|
24 3 10 0 21 -5 25 -5 3 -12 -6 -15 -19z"/>
|
||||||
|
<path d="M728 964 c-16 -8 -28 -20 -28 -27 0 -9 2 -9 8 0 4 6 20 15 35 18 15
|
||||||
|
4 27 11 27 16 0 12 -10 11 -42 -7z"/>
|
||||||
|
<path d="M475 960 c-3 -5 -14 -10 -25 -10 -10 0 -21 -7 -24 -16 -9 -24 -1 -28
|
||||||
|
17 -9 17 17 31 20 22 5 -12 -19 26 -23 58 -7 31 16 31 16 5 11 -40 -9 -64 2
|
||||||
|
-48 21 7 8 10 15 7 15 -3 0 -9 -4 -12 -10z"/>
|
||||||
|
<path d="M1650 963 c5 -2 8 -9 4 -14 -3 -5 0 -9 6 -9 15 0 7 23 -8 26 -7 1 -8
|
||||||
|
0 -2 -3z"/>
|
||||||
|
<path d="M777 930 c3 -11 10 -23 16 -27 6 -4 7 -1 2 7 -6 10 -4 12 9 7 22 -9
|
||||||
|
11 9 -14 23 -15 8 -17 7 -13 -10z"/>
|
||||||
|
<path d="M1095 920 c-16 -16 -27 -31 -24 -33 2 -2 17 11 32 30 36 41 31 43 -8
|
||||||
|
3z"/>
|
||||||
|
<path d="M1379 928 c0 -13 -2 -33 -3 -46 -2 -13 2 -21 8 -19 13 4 17 87 4 87
|
||||||
|
-4 0 -8 -10 -9 -22z"/>
|
||||||
|
<path d="M1605 940 c3 -5 8 -10 11 -10 2 0 4 5 4 10 0 6 -5 10 -11 10 -5 0 -7
|
||||||
|
-4 -4 -10z"/>
|
||||||
|
<path d="M668 933 c7 -3 16 -2 19 1 4 3 -2 6 -13 5 -11 0 -14 -3 -6 -6z"/>
|
||||||
|
<path d="M1147 927 c-3 -8 1 -14 9 -14 16 0 19 13 5 21 -5 3 -11 0 -14 -7z"/>
|
||||||
|
<path d="M1269 901 c-14 -15 -33 -32 -42 -37 -11 -7 -13 -14 -7 -24 8 -12 13
|
||||||
|
-10 32 14 13 16 31 36 41 45 18 16 23 32 10 30 -5 0 -20 -13 -34 -28z"/>
|
||||||
|
<path d="M1660 894 c0 -19 4 -33 8 -30 8 5 4 66 -4 66 -2 0 -4 -16 -4 -36z"/>
|
||||||
|
<path d="M395 912 c-18 -13 -14 -28 8 -29 16 -2 18 0 8 6 -7 5 -10 14 -7 20 8
|
||||||
|
13 5 14 -9 3z"/>
|
||||||
|
<path d="M574 906 c-10 -8 -17 -17 -14 -19 3 -3 16 3 29 14 27 22 15 26 -15 5z"/>
|
||||||
|
<path d="M1500 901 c-12 -24 -13 -34 -1 -27 9 6 22 46 15 46 -2 0 -8 -9 -14
|
||||||
|
-19z"/>
|
||||||
|
<path d="M26 877 c-10 -22 -15 -44 -13 -50 3 -7 10 2 17 18 l12 30 9 -24 c5
|
||||||
|
-13 12 -21 16 -17 3 3 1 16 -5 29 -6 12 -13 29 -15 37 -3 10 -11 1 -21 -23z"/>
|
||||||
|
<path d="M482 891 c-11 -7 -11 -9 1 -14 9 -3 18 1 21 9 6 16 -2 18 -22 5z"/>
|
||||||
|
<path d="M1173 892 c-6 -4 -14 -21 -17 -37 -6 -28 -6 -29 4 -6 6 14 16 31 22
|
||||||
|
38 12 15 9 16 -9 5z"/>
|
||||||
|
<path d="M1013 863 c-36 -34 -49 -63 -27 -63 8 0 13 4 10 9 -3 4 6 19 19 31
|
||||||
|
14 13 25 18 25 11 0 -7 5 -9 10 -6 12 7 13 45 2 45 -5 0 -22 -12 -39 -27z"/>
|
||||||
|
<path d="M104 865 c-10 -8 -14 -15 -8 -15 6 0 17 7 24 15 16 19 9 19 -16 0z"/>
|
||||||
|
<path d="M1095 869 c-4 -6 -5 -12 -2 -15 2 -3 7 2 10 11 7 17 1 20 -8 4z"/>
|
||||||
|
<path d="M761 854 c0 -11 3 -14 6 -6 3 7 2 16 -1 19 -3 4 -6 -2 -5 -13z"/>
|
||||||
|
<path d="M365 850 c3 -5 8 -10 11 -10 2 0 4 5 4 10 0 6 -5 10 -11 10 -5 0 -7
|
||||||
|
-4 -4 -10z"/>
|
||||||
|
<path d="M900 849 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
|
||||||
|
-5 -10 -11z"/>
|
||||||
|
<path d="M952 848 c-15 -15 -16 -31 0 -26 6 2 12 12 14 21 3 20 2 21 -14 5z"/>
|
||||||
|
<path d="M1411 853 c-1 -7 8 -17 19 -23 14 -7 20 -7 20 0 0 5 -6 10 -14 10 -8
|
||||||
|
0 -17 6 -19 13 -4 9 -6 9 -6 0z"/>
|
||||||
|
<path d="M1469 833 c-18 -47 -26 -58 -38 -59 -6 -1 -14 -2 -18 -3 -4 0 2 -8
|
||||||
|
13 -17 18 -15 18 -15 0 -10 -17 6 -17 5 -5 -11 9 -11 10 -14 1 -9 -8 5 -15 -2
|
||||||
|
-22 -18 -5 -14 -16 -26 -25 -26 -8 0 -15 -7 -15 -16 0 -8 -4 -12 -10 -9 -5 3
|
||||||
|
-10 -1 -10 -9 0 -8 8 -19 17 -24 15 -9 14 -11 -5 -23 -13 -8 -18 -17 -12 -24
|
||||||
|
12 -14 -12 -61 -29 -57 -7 1 -10 -2 -7 -7 4 -5 0 -12 -6 -14 -7 -3 -8 -6 -3
|
||||||
|
-6 20 -1 45 27 65 73 24 58 25 64 3 68 -14 3 -9 10 19 28 21 14 36 31 33 38
|
||||||
|
-2 6 1 12 8 12 13 0 50 74 63 124 8 34 -4 33 -17 -1z"/>
|
||||||
|
<path d="M679 831 c-13 -11 -22 -22 -19 -24 3 -3 16 5 29 19 28 29 22 31 -10
|
||||||
|
5z"/>
|
||||||
|
<path d="M555 821 c-3 -5 -1 -12 4 -15 5 -3 11 1 15 9 6 16 -9 21 -19 6z"/>
|
||||||
|
<path d="M1110 815 c0 -8 -5 -14 -12 -12 -7 1 -13 -5 -14 -13 0 -8 -2 -21 -2
|
||||||
|
-28 -1 -7 -8 -12 -14 -11 -27 3 -38 -1 -38 -15 0 -8 4 -17 9 -20 5 -3 8 0 7 7
|
||||||
|
0 6 4 12 9 12 6 0 9 -4 7 -10 -2 -5 8 4 22 20 29 35 50 85 35 85 -5 0 -9 -7
|
||||||
|
-9 -15z"/>
|
||||||
|
<path d="M1166 814 c-4 -9 -4 -19 -1 -22 2 -3 7 3 11 12 4 9 4 19 1 22 -2 3
|
||||||
|
-7 -3 -11 -12z"/>
|
||||||
|
<path d="M1200 809 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
|
||||||
|
-5 -10 -11z"/>
|
||||||
|
<path d="M1 736 c-1 -64 0 -69 12 -46 7 14 10 27 5 31 -4 3 -3 21 2 39 13 45
|
||||||
|
13 47 -4 47 -9 0 -14 -19 -15 -71z"/>
|
||||||
|
<path d="M1310 795 c-7 -8 -9 -23 -5 -36 6 -18 4 -21 -10 -15 -15 5 -16 4 -6
|
||||||
|
-8 9 -11 10 -24 2 -52 -5 -20 -7 -34 -4 -30 8 8 34 75 37 96 1 8 8 25 14 38
|
||||||
|
11 18 10 22 -1 22 -8 0 -20 -7 -27 -15z"/>
|
||||||
|
<path d="M624 778 c-11 -13 -30 -28 -44 -35 -20 -9 -21 -12 -6 -12 15 -1 16
|
||||||
|
-4 6 -16 -9 -10 -9 -15 -1 -15 10 0 15 13 12 33 0 4 6 7 14 7 15 0 51 47 42
|
||||||
|
56 -2 3 -13 -5 -23 -18z"/>
|
||||||
|
<path d="M409 788 c-12 -40 23 -62 42 -27 9 18 7 19 -17 13 -19 -5 -25 -4 -20
|
||||||
|
5 4 6 5 11 2 11 -3 0 -6 -1 -7 -2z"/>
|
||||||
|
<path d="M935 780 c-3 -5 -2 -10 4 -10 5 0 13 5 16 10 3 6 2 10 -4 10 -5 0
|
||||||
|
-13 -4 -16 -10z"/>
|
||||||
|
<path d="M660 770 c0 -5 5 -10 11 -10 5 0 7 5 4 10 -3 6 -8 10 -11 10 -2 0 -4
|
||||||
|
-4 -4 -10z"/>
|
||||||
|
<path d="M1222 760 c-8 -13 -8 -20 -2 -20 6 0 13 7 17 15 9 24 -1 27 -15 5z"/>
|
||||||
|
<path d="M827 763 c-4 -3 -7 -11 -7 -17 0 -6 5 -5 12 2 6 6 9 14 7 17 -3 3 -9
|
||||||
|
2 -12 -2z"/>
|
||||||
|
<path d="M1002 765 c5 -5 -1 -14 -12 -22 -11 -8 -20 -22 -19 -31 0 -14 2 -14
|
||||||
|
6 -2 4 8 15 22 26 31 17 13 18 17 6 24 -8 5 -11 5 -7 0z"/>
|
||||||
|
<path d="M355 750 c3 -6 1 -10 -5 -10 -6 0 -9 -4 -5 -10 6 -10 35 -2 35 10 0
|
||||||
|
4 -7 11 -16 14 -8 3 -12 2 -9 -4z"/>
|
||||||
|
<path d="M1185 730 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
|
||||||
|
-8 -4 -11 -10z"/>
|
||||||
|
<path d="M1469 733 c-1 -5 -1 -12 0 -17 1 -4 -6 -14 -16 -22 -16 -13 -16 -13
|
||||||
|
6 -6 26 9 41 32 21 32 -6 0 -8 5 -5 10 3 6 4 10 1 10 -3 0 -7 -3 -7 -7z"/>
|
||||||
|
<path d="M1147 701 c-4 -17 -2 -18 13 -8 10 6 16 15 14 19 -9 14 -22 9 -27
|
||||||
|
-11z"/>
|
||||||
|
<path d="M780 701 c0 -5 -12 -8 -27 -6 -26 3 -26 2 -8 -12 20 -15 20 -15 0
|
||||||
|
-11 -15 4 -21 0 -21 -13 0 -11 4 -19 8 -19 4 0 8 5 8 10 0 6 6 10 14 10 8 0
|
||||||
|
17 7 20 16 3 8 10 12 16 9 6 -3 7 1 4 9 -7 18 -14 21 -14 7z"/>
|
||||||
|
<path d="M810 706 c0 -2 7 -7 16 -10 8 -3 12 -2 9 4 -6 10 -25 14 -25 6z"/>
|
||||||
|
<path d="M1590 696 c7 -7 14 -6 21 1 9 9 7 12 -9 10 -14 -1 -18 -5 -12 -11z"/>
|
||||||
|
<path d="M89 666 c-17 -19 -37 -32 -43 -30 -6 2 -20 -10 -30 -26 -19 -34 -17
|
||||||
|
-50 4 -25 7 8 16 15 20 15 4 0 30 23 57 50 28 28 44 50 37 50 -7 0 -27 -15
|
||||||
|
-45 -34z"/>
|
||||||
|
<path d="M250 663 c-12 -21 -31 -43 -41 -51 -18 -13 -27 -42 -11 -42 4 0 18
|
||||||
|
16 32 35 29 39 46 45 35 13 -3 -13 -6 -30 -6 -38 3 -56 0 -83 -8 -99 -14 -26
|
||||||
|
-21 -71 -11 -71 7 0 20 38 25 70 0 3 4 10 8 16 10 17 17 204 7 204 -4 0 -18
|
||||||
|
-17 -30 -37z"/>
|
||||||
|
<path d="M375 690 c-3 -5 1 -10 9 -10 9 0 16 5 16 10 0 6 -4 10 -9 10 -6 0
|
||||||
|
-13 -4 -16 -10z"/>
|
||||||
|
<path d="M880 685 c-11 -13 -10 -14 4 -9 9 3 16 10 16 15 0 13 -6 11 -20 -6z"/>
|
||||||
|
<path d="M923 687 c-3 -6 -1 -7 5 -3 7 4 12 2 12 -4 0 -6 5 -8 10 -5 6 3 10
|
||||||
|
10 10 14 0 11 -30 9 -37 -2z"/>
|
||||||
|
<path d="M1063 685 c-3 -9 -3 -19 1 -22 3 -4 6 1 6 10 0 10 4 17 8 17 14 0 32
|
||||||
|
-23 32 -42 0 -16 3 -16 20 -6 18 12 19 13 2 19 -10 4 -24 14 -31 23 -17 20
|
||||||
|
-30 20 -38 1z"/>
|
||||||
|
<path d="M987 676 c-4 -10 0 -14 12 -13 11 1 17 7 14 14 -7 17 -20 17 -26 -1z"/>
|
||||||
|
<path d="M1576 663 c-6 -14 -5 -15 5 -6 7 7 10 15 7 18 -3 3 -9 -2 -12 -12z"/>
|
||||||
|
<path d="M1073 655 c-7 -8 -13 -19 -13 -25 0 -5 -4 -10 -10 -10 -5 0 -10 -6
|
||||||
|
-10 -12 1 -15 50 38 50 53 0 12 -2 11 -17 -6z"/>
|
||||||
|
<path d="M950 638 c0 -4 7 -8 15 -8 8 0 15 4 15 8 0 5 -7 9 -15 9 -8 0 -15 -4
|
||||||
|
-15 -9z"/>
|
||||||
|
<path d="M1215 640 c4 -6 11 -8 16 -5 14 9 11 15 -7 15 -8 0 -12 -5 -9 -10z"/>
|
||||||
|
<path d="M480 620 c-13 -8 -12 -10 3 -10 9 0 17 5 17 10 0 12 -1 12 -20 0z"/>
|
||||||
|
<path d="M894 622 c-6 -4 -14 -16 -17 -27 -7 -20 -7 -20 8 -1 8 11 21 23 27
|
||||||
|
28 7 4 9 8 3 8 -5 0 -15 -4 -21 -8z"/>
|
||||||
|
<path d="M728 608 c5 -5 16 -8 23 -6 8 3 3 7 -10 11 -17 4 -21 3 -13 -5z"/>
|
||||||
|
<path d="M1405 610 c-3 -6 1 -7 9 -4 18 7 21 14 7 14 -6 0 -13 -4 -16 -10z"/>
|
||||||
|
<path d="M1440 599 c0 -23 10 -28 35 -18 18 7 18 8 1 8 -11 1 -16 6 -13 14 3
|
||||||
|
8 -1 14 -9 14 -8 0 -14 -8 -14 -18z"/>
|
||||||
|
<path d="M813 595 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
|
||||||
|
<path d="M1271 573 c2 -24 2 -25 6 -5 2 12 9 20 14 17 5 -4 9 -1 9 4 0 6 -7
|
||||||
|
11 -15 11 -10 0 -15 -9 -14 -27z"/>
|
||||||
|
<path d="M1125 580 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
|
||||||
|
-8 -4 -11 -10z"/>
|
||||||
|
<path d="M1163 565 c0 -8 4 -12 9 -9 5 3 6 10 3 15 -9 13 -12 11 -12 -6z"/>
|
||||||
|
<path d="M1880 573 c0 -5 7 -19 16 -31 8 -12 12 -22 9 -22 -3 0 1 -10 9 -22
|
||||||
|
20 -28 20 -42 2 -35 -11 4 -11 1 -2 -18 6 -13 14 -21 18 -19 4 3 10 -8 13 -23
|
||||||
|
4 -15 11 -38 16 -51 7 -17 6 -21 -3 -16 -8 5 -9 2 -4 -12 5 -10 9 -26 10 -34
|
||||||
|
0 -8 5 -18 9 -21 5 -4 7 -16 5 -29 -3 -22 -3 -22 -25 -2 -13 10 -19 23 -15 27
|
||||||
|
4 5 1 5 -5 1 -10 -5 -9 -13 3 -32 9 -13 20 -24 25 -24 5 0 9 -12 9 -27 0 -23
|
||||||
|
-2 -25 -11 -13 -9 12 -10 11 -4 -5 4 -11 9 -53 12 -92 4 -55 10 -73 20 -73 13
|
||||||
|
0 14 25 10 162 -4 147 -8 172 -35 254 -16 50 -37 104 -46 122 -16 31 -36 50
|
||||||
|
-36 35z"/>
|
||||||
|
<path d="M30 548 c-12 -22 -12 -22 6 -6 10 10 15 20 12 24 -4 3 -12 -5 -18
|
||||||
|
-18z"/>
|
||||||
|
<path d="M430 559 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
|
||||||
|
-5 -10 -11z"/>
|
||||||
|
<path d="M1212 550 c-7 -11 -8 -20 -3 -20 9 0 24 30 18 36 -1 2 -8 -6 -15 -16z"/>
|
||||||
|
<path d="M165 544 c21 -16 19 -37 -6 -52 -12 -8 -18 -19 -15 -28 3 -8 1 -14
|
||||||
|
-5 -14 -6 0 -8 -9 -4 -19 4 -13 -5 -37 -24 -67 -16 -27 -27 -52 -24 -57 7 -12
|
||||||
|
47 59 67 121 19 56 38 78 58 66 10 -6 9 -3 -1 9 -8 11 -9 17 -3 17 6 0 16 10
|
||||||
|
22 23 9 18 9 20 -2 7 -18 -20 -42 -25 -35 -7 4 11 -2 14 -19 13 -20 -1 -22 -3
|
||||||
|
-9 -12z"/>
|
||||||
|
<path d="M998 553 c6 -2 9 -10 6 -15 -4 -7 -2 -8 5 -4 13 9 5 26 -12 25 -9 0
|
||||||
|
-8 -2 1 -6z"/>
|
||||||
|
<path d="M1463 540 c1 -21 12 -26 22 -10 3 6 1 10 -5 10 -6 0 -8 5 -5 10 3 6
|
||||||
|
2 10 -4 10 -5 0 -9 -9 -8 -20z"/>
|
||||||
|
<path d="M1045 518 c-4 -18 -17 -42 -29 -53 -12 -11 -17 -17 -11 -13 7 4 15 5
|
||||||
|
18 1 4 -3 9 1 13 10 3 9 11 15 17 13 7 -1 9 -1 6 1 -3 2 -4 19 -2 38 4 44 -3
|
||||||
|
45 -12 3z"/>
|
||||||
|
<path d="M130 530 c-13 -8 -12 -10 3 -10 9 0 17 5 17 10 0 12 -1 12 -20 0z"/>
|
||||||
|
<path d="M570 525 c-8 -9 -8 -15 -2 -15 12 0 26 19 19 26 -2 2 -10 -2 -17 -11z"/>
|
||||||
|
<path d="M712 501 c-16 -17 -33 -27 -36 -23 -4 3 -4 -1 -1 -10 6 -14 12 -11
|
||||||
|
41 19 19 20 33 37 31 40 -3 2 -18 -10 -35 -26z"/>
|
||||||
|
<path d="M850 516 c0 -8 4 -17 9 -20 5 -4 7 3 4 14 -6 23 -13 26 -13 6z"/>
|
||||||
|
<path d="M1436 516 c3 -9 1 -23 -6 -31 -7 -8 -8 -15 -4 -15 5 0 15 9 21 19 10
|
||||||
|
16 9 22 -3 32 -12 10 -13 9 -8 -5z"/>
|
||||||
|
<path d="M1260 510 c-8 -5 -10 -10 -5 -10 6 0 17 5 25 10 8 5 11 10 5 10 -5 0
|
||||||
|
-17 -5 -25 -10z"/>
|
||||||
|
<path d="M106 499 c10 -17 -56 -72 -71 -59 -8 7 -16 3 -25 -15 -7 -14 -10 -28
|
||||||
|
-6 -32 4 -4 9 3 13 16 3 14 11 20 20 17 8 -3 32 11 55 32 34 32 38 40 24 45
|
||||||
|
-11 5 -15 3 -10 -4z"/>
|
||||||
|
<path d="M1225 500 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
|
||||||
|
-8 -4 -11 -10z"/>
|
||||||
|
<path d="M635 474 c-9 -15 -12 -23 -6 -20 6 4 11 2 11 -3 0 -6 5 -11 11 -11 8
|
||||||
|
0 8 5 -1 15 -7 8 -8 15 -3 15 5 0 10 7 10 15 0 20 -2 19 -22 -11z"/>
|
||||||
|
<path d="M100 421 c0 -5 -6 -16 -12 -23 -51 -53 -40 -69 11 -17 34 34 39 49
|
||||||
|
16 49 -8 0 -15 -4 -15 -9z"/>
|
||||||
|
<path d="M176 388 c0 -23 6 -44 12 -46 14 -4 15 -55 2 -100 -8 -29 -7 -32 5
|
||||||
|
-22 17 14 30 93 20 117 -15 33 -15 56 -3 71 11 13 10 15 -3 10 -9 -4 -21 -2
|
||||||
|
-25 2 -6 6 -9 -8 -8 -32z"/>
|
||||||
|
<path d="M735 420 c-3 -6 1 -7 9 -4 18 7 21 14 7 14 -6 0 -13 -4 -16 -10z"/>
|
||||||
|
<path d="M1305 380 c-3 -5 1 -10 10 -10 9 0 13 5 10 10 -3 6 -8 10 -10 10 -2
|
||||||
|
0 -7 -4 -10 -10z"/>
|
||||||
|
<path d="M825 370 c-3 -5 -21 -10 -38 -10 -21 0 -28 -3 -19 -9 8 -4 13 -11 13
|
||||||
|
-15 -4 -29 13 -37 22 -12 4 11 1 13 -11 9 -14 -5 -15 -3 -5 7 9 10 15 10 26 1
|
||||||
|
15 -11 22 -7 41 27 9 14 -20 17 -29 2z"/>
|
||||||
|
<path d="M11 323 c-10 -26 -10 -27 4 -9 8 11 15 23 15 28 0 15 -7 8 -19 -19z"/>
|
||||||
|
<path d="M483 335 c-3 -9 -3 -18 -1 -21 3 -3 8 4 11 16 6 23 -1 27 -10 5z"/>
|
||||||
|
<path d="M1840 340 c0 -5 5 -10 11 -10 5 0 7 5 4 10 -3 6 -8 10 -11 10 -2 0
|
||||||
|
-4 -4 -4 -10z"/>
|
||||||
|
<path d="M570 320 c0 -5 5 -10 11 -10 5 0 7 5 4 10 -3 6 -8 10 -11 10 -2 0 -4
|
||||||
|
-4 -4 -10z"/>
|
||||||
|
<path d="M873 317 c0 -8 6 -14 14 -14 9 0 10 4 2 14 -6 7 -12 13 -14 13 -1 0
|
||||||
|
-2 -6 -2 -13z"/>
|
||||||
|
<path d="M1356 317 c3 -10 9 -15 12 -12 3 3 0 11 -7 18 -10 9 -11 8 -5 -6z"/>
|
||||||
|
<path d="M166 297 c3 -10 9 -15 12 -12 3 3 0 11 -7 18 -10 9 -11 8 -5 -6z"/>
|
||||||
|
<path d="M94 285 c-10 -8 -15 -15 -10 -15 5 0 4 -6 -1 -12 -5 -7 -10 -29 -11
|
||||||
|
-48 0 -33 0 -32 13 13 7 26 19 54 25 62 16 19 9 19 -16 0z"/>
|
||||||
|
<path d="M492 284 c-12 -8 -28 -12 -35 -8 -6 4 -5 -1 4 -11 13 -13 21 -15 37
|
||||||
|
-6 20 11 38 41 24 41 -4 0 -18 -7 -30 -16z"/>
|
||||||
|
<path d="M590 291 c0 -6 5 -11 11 -11 5 0 7 -4 4 -10 -3 -5 -1 -10 4 -10 16 0
|
||||||
|
13 16 -4 30 -10 9 -15 9 -15 1z"/>
|
||||||
|
<path d="M747 293 c-4 -3 -7 -13 -7 -22 1 -13 3 -13 11 2 11 19 8 33 -4 20z"/>
|
||||||
|
<path d="M127 283 c-11 -18 -4 -63 9 -63 16 0 19 16 4 25 -6 4 -8 15 -5 26 6
|
||||||
|
19 1 26 -8 12z"/>
|
||||||
|
<path d="M627 256 c-3 -8 5 -22 19 -33 13 -10 22 -24 19 -30 -9 -26 11 -12 28
|
||||||
|
19 13 23 21 28 26 19 7 -10 9 -9 14 2 3 9 -1 18 -9 21 -8 3 -17 0 -20 -7 -7
|
||||||
|
-19 -44 -14 -59 7 -11 14 -14 14 -18 2z"/>
|
||||||
|
<path d="M1075 250 c-10 -11 -14 -20 -9 -20 5 0 17 9 27 20 9 11 13 20 8 20
|
||||||
|
-4 0 -16 -9 -26 -20z"/>
|
||||||
|
<path d="M1155 261 c-3 -5 -2 -12 3 -15 5 -3 9 1 9 9 0 17 -3 19 -12 6z"/>
|
||||||
|
<path d="M1450 245 c-7 -8 -16 -12 -21 -9 -5 3 -9 -2 -9 -10 0 -9 7 -16 15
|
||||||
|
-16 8 0 15 7 15 15 0 7 8 19 18 24 9 6 12 11 6 11 -6 0 -17 -7 -24 -15z"/>
|
||||||
|
<path d="M1516 233 c10 -40 15 -42 12 -5 -1 18 -6 32 -10 32 -5 0 -5 -12 -2
|
||||||
|
-27z"/>
|
||||||
|
<path d="M40 219 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10 -5
|
||||||
|
-10 -11z"/>
|
||||||
|
<path d="M1010 189 c0 -5 5 -7 10 -4 6 3 10 8 10 11 0 2 -4 4 -10 4 -5 0 -10
|
||||||
|
-5 -10 -11z"/>
|
||||||
|
<path d="M1136 192 c-3 -5 1 -9 9 -9 8 0 12 4 9 9 -3 4 -7 8 -9 8 -2 0 -6 -4
|
||||||
|
-9 -8z"/>
|
||||||
|
<path d="M583 175 c-7 -8 -13 -18 -13 -22 0 -4 -11 -21 -25 -37 -30 -35 -31
|
||||||
|
-40 -5 -24 10 7 27 27 37 45 9 17 20 30 25 27 4 -3 5 2 2 10 -7 19 -6 19 -21
|
||||||
|
1z"/>
|
||||||
|
<path d="M920 181 c0 -6 4 -13 10 -16 6 -3 7 1 4 9 -7 18 -14 21 -14 7z"/>
|
||||||
|
<path d="M1531 170 c0 -8 4 -24 9 -35 9 -20 9 -20 9 0 0 11 -4 27 -9 35 -9 13
|
||||||
|
-10 13 -9 0z"/>
|
||||||
|
<path d="M1418 156 c-10 -7 -18 -19 -18 -26 0 -7 9 -3 20 10 28 33 27 39 -2
|
||||||
|
16z"/>
|
||||||
|
<path d="M520 145 c0 -8 2 -15 4 -15 2 0 6 7 10 15 3 8 1 15 -4 15 -6 0 -10
|
||||||
|
-7 -10 -15z"/>
|
||||||
|
<path d="M1602 140 c0 -14 2 -19 5 -12 2 6 2 18 0 25 -3 6 -5 1 -5 -13z"/>
|
||||||
|
<path d="M730 129 c0 -10 5 -21 12 -25 7 -4 8 -3 4 5 -4 6 -3 18 3 25 8 9 7
|
||||||
|
13 -4 13 -8 0 -15 -8 -15 -18z"/>
|
||||||
|
<path d="M666 131 c-4 -7 -5 -15 -2 -18 9 -9 19 4 14 18 -4 11 -6 11 -12 0z"/>
|
||||||
|
<path d="M496 121 c-5 -7 -18 -16 -30 -21 -20 -8 -20 -9 -1 -9 27 -1 48 14 43
|
||||||
|
30 -3 10 -6 10 -12 0z"/>
|
||||||
|
<path d="M923 114 c-15 -16 -16 -24 -4 -24 8 0 24 30 19 35 -2 2 -9 -3 -15
|
||||||
|
-11z"/>
|
||||||
|
<path d="M1497 103 c-3 -16 -3 -30 -1 -33 7 -7 24 23 24 43 0 27 -17 20 -23
|
||||||
|
-10z"/>
|
||||||
|
<path d="M832 105 c3 -3 -5 -18 -17 -31 -13 -14 -21 -32 -18 -40 6 -16 26 -8
|
||||||
|
20 9 -2 6 7 21 20 33 21 20 22 22 6 28 -10 4 -15 4 -11 1z"/>
|
||||||
|
<path d="M380 75 c-7 -9 -10 -18 -6 -22 3 -3 6 -1 6 6 0 7 3 10 8 8 4 -3 13 1
|
||||||
|
20 9 10 11 10 14 -1 14 -8 0 -20 -7 -27 -15z"/>
|
||||||
|
<path d="M1026 73 c-6 -14 -5 -15 5 -6 7 7 10 15 7 18 -3 3 -9 -2 -12 -12z"/>
|
||||||
|
<path d="M450 65 c-7 -8 -8 -15 -2 -15 5 0 15 7 22 15 7 8 8 15 2 15 -5 0 -15
|
||||||
|
-7 -22 -15z"/>
|
||||||
|
<path d="M1920 71 c0 -6 5 -13 11 -16 6 -4 8 -14 4 -22 -4 -13 -3 -14 9 -4 13
|
||||||
|
11 13 15 -2 32 -19 21 -22 23 -22 10z"/>
|
||||||
|
<path d="M66 61 c-6 -9 9 -61 18 -61 2 0 6 8 8 18 6 21 -17 58 -26 43z"/>
|
||||||
|
<path d="M1391 63 c-1 -17 16 -25 25 -12 4 7 3 9 -4 5 -5 -3 -13 0 -15 6 -4 9
|
||||||
|
-6 10 -6 1z"/>
|
||||||
|
<path d="M280 50 c-8 -5 -12 -11 -9 -14 2 -3 11 -1 19 4 8 5 12 11 9 14 -2 3
|
||||||
|
-11 1 -19 -4z"/>
|
||||||
|
<path d="M1252 40 c0 -14 2 -19 5 -12 2 6 2 18 0 25 -3 6 -5 1 -5 -13z"/>
|
||||||
|
<path d="M1465 50 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
|
||||||
|
-8 -4 -11 -10z"/>
|
||||||
|
<path d="M1626 48 c-7 -21 -3 -48 5 -43 4 3 6 13 5 23 -2 9 4 20 13 24 14 6
|
||||||
|
14 7 -2 7 -10 1 -19 -5 -21 -11z"/>
|
||||||
|
<path d="M7 25 c-4 -26 -4 -26 14 -6 23 24 24 31 5 31 -7 0 -16 -11 -19 -25z"/>
|
||||||
|
<path d="M1520 38 c0 -9 -8 -22 -17 -27 -16 -9 -16 -10 -2 -11 24 0 40 23 29
|
||||||
|
40 -8 13 -10 12 -10 -2z"/>
|
||||||
|
<path d="M930 31 c0 -6 4 -13 10 -16 6 -3 7 1 4 9 -7 18 -14 21 -14 7z"/>
|
||||||
|
<path d="M735 20 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0 -8
|
||||||
|
-4 -11 -10z"/>
|
||||||
|
</g>
|
||||||
|
<path d="M0 0 h 300 v 15 h -300 z"/>
|
||||||
|
<path d="M0 285 h 300 v 15 h -300 z"/>
|
||||||
|
<path d="M0 0 v 300 h 15 v -300 z"/>
|
||||||
|
<path d="M285 0 v 300 h 15 v -300 z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 54 KiB |
28
scryer-prolog.wxs
Normal file
28
scryer-prolog.wxs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Product Name="Scryer Prolog" Manufacturer="Scryer Prolog contributors" Id="*" UpgradeCode="cfb2dee4-5dd5-4d7d-b426-cd7340810559" Language="1033" Codepage="1252" Version="0.9.0">
|
||||||
|
<Package Description="An open source industrial strength production environment for ISO Prolog that is also a testbed for bleeding edge research in logic and constraint programming, which is itself written in a high-level language." Platform="x64" Keywords="prolog" Id="*" Compressed="yes" InstallScope="perMachine" InstallerVersion="300" Languages="1033" SummaryCodepage="1252" Manufacturer="Scryer Prolog contributors"/>
|
||||||
|
<Property Id="APPHELPLINK" Value="https://github.com/mthom/scryer-prolog"/>
|
||||||
|
<Media Id="1" Cabinet="scryer.cab" EmbedCab="yes" />
|
||||||
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
|
<Directory Id="ProgramFilesFolder" Name="PFiles">
|
||||||
|
<Directory Id="INSTALLDIR" Name="Scryer Prolog">
|
||||||
|
<Component Id="MainExecutable" Guid="1b41ceda-ba18-47f9-911b-ee41b4f20921">
|
||||||
|
<File Id="ScryerPrologEXE" Name="scryer-prolog.exe" DiskId="1" Source="target/release/scryer-prolog.exe" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="ProgramMenuFolder">
|
||||||
|
<Component Id="ApplicationShortcut" Guid="8c9b14a3-e7b1-4d30-a892-61d7371dcae2">
|
||||||
|
<Shortcut Id="ApplicationStarMenuShortcut" Name="Scryer Prolog" Description="Launch Scryer Prolog" Target="[#ScryerPrologEXE]" WorkingDirectory="INSTALLDIR"/>
|
||||||
|
<RemoveFolder Id="ApplicationShortcut" On="uninstall"/>
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\Microsoft\ScryerProlog" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
<Feature Id="Complete" Level="1" Display="expand" ConfigurableDirectory="INSTALLDIR">
|
||||||
|
<ComponentRef Id="MainExecutable"/>
|
||||||
|
<ComponentRef Id="ApplicationShortcut"/>
|
||||||
|
</Feature>
|
||||||
|
</Product>
|
||||||
|
</Wix>
|
||||||
57
src/allocator.rs
Normal file
57
src/allocator.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use crate::parser::ast::*;
|
||||||
|
|
||||||
|
use crate::forms::*;
|
||||||
|
use crate::instructions::*;
|
||||||
|
use crate::targets::*;
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
pub(crate) trait Allocator {
|
||||||
|
fn new() -> Self;
|
||||||
|
|
||||||
|
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
lvl: Level,
|
||||||
|
context: GenContext,
|
||||||
|
code: &mut CodeDeque,
|
||||||
|
) -> RegType;
|
||||||
|
|
||||||
|
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
lvl: Level,
|
||||||
|
context: GenContext,
|
||||||
|
cell: &'a Cell<RegType>,
|
||||||
|
code: &mut CodeDeque,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
var_num: usize,
|
||||||
|
lvl: Level,
|
||||||
|
cell: &Cell<VarReg>,
|
||||||
|
term_loc: GenContext,
|
||||||
|
code: &mut CodeDeque,
|
||||||
|
r: RegType,
|
||||||
|
is_new_var: bool,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType;
|
||||||
|
|
||||||
|
fn mark_var<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
var_num: usize,
|
||||||
|
lvl: Level,
|
||||||
|
cell: &Cell<VarReg>,
|
||||||
|
context: GenContext,
|
||||||
|
code: &mut CodeDeque,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn reset(&mut self);
|
||||||
|
fn reset_arg(&mut self, arg_num: usize);
|
||||||
|
fn reset_at_head(&mut self, args: &[Term]);
|
||||||
|
fn reset_contents(&mut self);
|
||||||
|
|
||||||
|
fn advance_arg(&mut self);
|
||||||
|
fn max_reg_allocated(&self) -> usize;
|
||||||
|
}
|
||||||
897
src/arena.rs
Normal file
897
src/arena.rs
Normal file
@@ -0,0 +1,897 @@
|
|||||||
|
#[cfg(feature = "http")]
|
||||||
|
use crate::http::{HttpListener, HttpResponse};
|
||||||
|
use crate::machine::heap::AllocError;
|
||||||
|
use crate::machine::loader::LiveLoadState;
|
||||||
|
use crate::machine::streams::*;
|
||||||
|
use crate::offset_table::*;
|
||||||
|
use crate::read::*;
|
||||||
|
use crate::types::UntypedArenaPtr;
|
||||||
|
|
||||||
|
use crate::parser::dashu::{Integer, Rational};
|
||||||
|
use ordered_float::OrderedFloat;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::io::PipeReader;
|
||||||
|
use std::io::PipeWriter;
|
||||||
|
use std::mem;
|
||||||
|
use std::mem::ManuallyDrop;
|
||||||
|
use std::net::TcpListener;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::process::Child;
|
||||||
|
use std::ptr;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
use std::ptr::addr_of_mut;
|
||||||
|
|
||||||
|
macro_rules! arena_alloc {
|
||||||
|
($e:expr, $arena:expr) => {{
|
||||||
|
let result = $e;
|
||||||
|
$crate::arena::AllocateInArena::arena_allocate(result, $arena)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! float_alloc {
|
||||||
|
($e:expr, $arena:expr) => {{ $arena.f64_tbl.build_with(OrderedFloat($e)) }};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header_offset_from_payload<T: ?Sized + ArenaAllocated>() -> usize
|
||||||
|
where
|
||||||
|
T::Payload: Sized,
|
||||||
|
{
|
||||||
|
let payload_offset = mem::offset_of!(TypedAllocSlab<T>, payload);
|
||||||
|
let slab_offset = mem::offset_of!(TypedAllocSlab<T>, slab);
|
||||||
|
let header_offset = slab_offset + mem::offset_of!(AllocSlab, header);
|
||||||
|
|
||||||
|
debug_assert!(payload_offset > header_offset);
|
||||||
|
payload_offset - header_offset
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Specifier, Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[bits = 7]
|
||||||
|
pub enum ArenaHeaderTag {
|
||||||
|
Integer = 0b10,
|
||||||
|
Rational = 0b11,
|
||||||
|
LiveLoadState = 0b0001000,
|
||||||
|
InactiveLoadState = 0b1011000,
|
||||||
|
InputFileStream = 0b10000,
|
||||||
|
OutputFileStream = 0b10100,
|
||||||
|
NamedTcpStream = 0b011100,
|
||||||
|
NamedTlsStream = 0b100000,
|
||||||
|
HttpReadStream = 0b100001,
|
||||||
|
HttpWriteStream = 0b100010,
|
||||||
|
ReadlineStream = 0b110000,
|
||||||
|
StaticStringStream = 0b110100,
|
||||||
|
ByteStream = 0b111000,
|
||||||
|
CallbackStream = 0b111001,
|
||||||
|
InputChannelStream = 0b111010,
|
||||||
|
StandardOutputStream = 0b1100,
|
||||||
|
StandardErrorStream = 0b11000,
|
||||||
|
NullStream = 0b111100,
|
||||||
|
TcpListener = 0b1000000,
|
||||||
|
HttpListener = 0b1000001,
|
||||||
|
HttpResponse = 0b1000010,
|
||||||
|
PipeWriter = 0b1000011,
|
||||||
|
Dropped = 0b1000100,
|
||||||
|
PipeReader = 0b1001001,
|
||||||
|
ChildProcess = 0b1001010,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitfield]
|
||||||
|
#[repr(align(8))]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct ArenaHeader {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
size: B56,
|
||||||
|
m: bool,
|
||||||
|
tag: ArenaHeaderTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
const_assert!(mem::size_of::<ArenaHeader>() == 8);
|
||||||
|
|
||||||
|
impl ArenaHeader {
|
||||||
|
#[inline]
|
||||||
|
pub fn build_with(size: u64, tag: ArenaHeaderTag) -> Self {
|
||||||
|
ArenaHeader::new()
|
||||||
|
.with_size(size)
|
||||||
|
.with_tag(tag)
|
||||||
|
.with_m(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_tag(self) -> ArenaHeaderTag {
|
||||||
|
self.tag()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TypedArenaPtr<T: ?Sized + ArenaAllocated>(ptr::NonNull<T::Payload>);
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> PartialOrd for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: PartialOrd,
|
||||||
|
{
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
(**self).partial_cmp(&**other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> PartialEq for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: PartialEq,
|
||||||
|
{
|
||||||
|
fn eq(&self, other: &TypedArenaPtr<T>) -> bool {
|
||||||
|
std::ptr::addr_eq(self.0.as_ptr(), other.0.as_ptr()) || **self == **other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> Eq for TypedArenaPtr<T> where T::Payload: Eq {}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> Ord for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: Ord,
|
||||||
|
{
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
(**self).cmp(&**other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> Hash for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: Hash,
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||||
|
(self as &T::Payload).hash(hasher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> Clone for TypedArenaPtr<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> Copy for TypedArenaPtr<T> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> Deref for TypedArenaPtr<T> {
|
||||||
|
type Target = T::Payload;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
unsafe { self.0.as_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> DerefMut for TypedArenaPtr<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
unsafe { self.0.as_mut() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ArenaAllocated> fmt::Display for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: fmt::Display,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", (self as &T::Payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> {
|
||||||
|
#[inline]
|
||||||
|
pub fn as_ptr(&self) -> *mut T::Payload {
|
||||||
|
self.0.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, T: ?Sized + ArenaAllocated<Payload = ManuallyDrop<P>>> TypedArenaPtr<T> {
|
||||||
|
pub fn drop_payload(&mut self) {
|
||||||
|
if self.get_tag() != ArenaHeaderTag::Dropped {
|
||||||
|
self.set_tag(ArenaHeaderTag::Dropped);
|
||||||
|
unsafe { ManuallyDrop::drop(&mut *self.as_ptr()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: Sized,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub fn header_ptr(&self) -> *const ArenaHeader {
|
||||||
|
unsafe { self.as_ptr().byte_sub(T::header_offset_from_payload()) as *const _ }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn header_ptr_mut(&mut self) -> *mut ArenaHeader {
|
||||||
|
unsafe { self.as_ptr().byte_sub(T::header_offset_from_payload()) as *mut _ }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_mark_bit(&self) -> bool {
|
||||||
|
unsafe { (*self.header_ptr()).m() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_tag(&mut self, tag: ArenaHeaderTag) {
|
||||||
|
unsafe {
|
||||||
|
(*self.header_ptr_mut()).set_tag(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_tag(&self) -> ArenaHeaderTag {
|
||||||
|
unsafe { (*self.header_ptr()).get_tag() }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn mark(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
(*self.header_ptr_mut()).set_m(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn unmark(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
(*self.header_ptr_mut()).set_m(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait AllocateInArena<AllocFor>
|
||||||
|
where
|
||||||
|
AllocFor: ArenaAllocated,
|
||||||
|
{
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<AllocFor>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, T: ArenaAllocated<Payload = P>> AllocateInArena<T> for P {
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<T> {
|
||||||
|
T::alloc(arena, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this isn't allowed due to https://github.com/rust-lang/rust/issues/20400 I think,
|
||||||
|
though P == ManuallyDrop<P> might also be a problem event though that shouldn't be possible
|
||||||
|
impl<P, T: ArenaAllocated<Payload = ManuallyDrop<P>>> AllocateInArena<T> for P {
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<T> {
|
||||||
|
T::alloc(arena, ManuallyDrop::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub trait ArenaAllocated {
|
||||||
|
type Payload: ?Sized;
|
||||||
|
|
||||||
|
fn tag() -> ArenaHeaderTag;
|
||||||
|
|
||||||
|
fn header_offset_from_payload() -> usize
|
||||||
|
where
|
||||||
|
Self::Payload: Sized,
|
||||||
|
{
|
||||||
|
header_offset_from_payload::<Self>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// - the caller must guarantee that the pointee type of UntypedArenaPtr is Self
|
||||||
|
/// - the pointer must be non-null
|
||||||
|
unsafe fn typed_ptr(ptr: UntypedArenaPtr) -> TypedArenaPtr<Self>
|
||||||
|
where
|
||||||
|
Self::Payload: Sized,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
TypedArenaPtr(NonNull::new_unchecked(
|
||||||
|
ptr.get_ptr()
|
||||||
|
.byte_add(Self::header_offset_from_payload())
|
||||||
|
.cast_mut()
|
||||||
|
.cast::<Self::Payload>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::missing_safety_doc)]
|
||||||
|
fn alloc(arena: &mut Arena, value: Self::Payload) -> TypedArenaPtr<Self>
|
||||||
|
where
|
||||||
|
Self::Payload: Sized,
|
||||||
|
{
|
||||||
|
let size = mem::size_of::<TypedAllocSlab<Self>>();
|
||||||
|
let slab = Box::new(TypedAllocSlab {
|
||||||
|
slab: AllocSlab {
|
||||||
|
next: arena.base.take(),
|
||||||
|
header: ArenaHeader::build_with(size as u64, Self::tag()),
|
||||||
|
},
|
||||||
|
payload: value,
|
||||||
|
});
|
||||||
|
|
||||||
|
let (allocated_ptr, untyped_slab) = slab.to_untyped();
|
||||||
|
|
||||||
|
arena.base = Some(untyped_slab);
|
||||||
|
|
||||||
|
allocated_ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// - ptr points to an allocated slab of the correct kind
|
||||||
|
unsafe fn dealloc(ptr: NonNull<TypedAllocSlab<Self>>) {
|
||||||
|
drop(unsafe { Box::from_raw(ptr.as_ptr()) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArenaAllocated for Integer {
|
||||||
|
type Payload = Self;
|
||||||
|
#[inline]
|
||||||
|
fn tag() -> ArenaHeaderTag {
|
||||||
|
ArenaHeaderTag::Integer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArenaAllocated for Rational {
|
||||||
|
type Payload = Self;
|
||||||
|
#[inline]
|
||||||
|
fn tag() -> ArenaHeaderTag {
|
||||||
|
ArenaHeaderTag::Rational
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllocateInArena<LiveLoadState> for LiveLoadState {
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<LiveLoadState> {
|
||||||
|
LiveLoadState::alloc(arena, ManuallyDrop::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArenaAllocated for LiveLoadState {
|
||||||
|
type Payload = ManuallyDrop<Self>;
|
||||||
|
#[inline]
|
||||||
|
fn tag() -> ArenaHeaderTag {
|
||||||
|
ArenaHeaderTag::LiveLoadState
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(ptr: NonNull<TypedAllocSlab<Self>>) {
|
||||||
|
let mut slab = unsafe { Box::from_raw(ptr.as_ptr()) };
|
||||||
|
|
||||||
|
match slab.tag() {
|
||||||
|
ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => {
|
||||||
|
unsafe { ManuallyDrop::drop(&mut slab.payload) };
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::Dropped => {}
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(slab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllocateInArena<TcpListener> for TcpListener {
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<TcpListener> {
|
||||||
|
TcpListener::alloc(arena, ManuallyDrop::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArenaAllocated for TcpListener {
|
||||||
|
type Payload = ManuallyDrop<Self>;
|
||||||
|
#[inline]
|
||||||
|
fn tag() -> ArenaHeaderTag {
|
||||||
|
ArenaHeaderTag::TcpListener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "http")]
|
||||||
|
impl ArenaAllocated for HttpListener {
|
||||||
|
type Payload = Self;
|
||||||
|
#[inline]
|
||||||
|
fn tag() -> ArenaHeaderTag {
|
||||||
|
ArenaHeaderTag::HttpListener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "http")]
|
||||||
|
impl ArenaAllocated for HttpResponse {
|
||||||
|
type Payload = Self;
|
||||||
|
#[inline]
|
||||||
|
fn tag() -> ArenaHeaderTag {
|
||||||
|
ArenaHeaderTag::HttpResponse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArenaAllocated for Child {
|
||||||
|
type Payload = ManuallyDrop<Self>;
|
||||||
|
#[inline]
|
||||||
|
fn tag() -> ArenaHeaderTag {
|
||||||
|
ArenaHeaderTag::ChildProcess
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AllocateInArena<Child> for Child {
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<Child> {
|
||||||
|
Child::alloc(arena, ManuallyDrop::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AllocSlab {
|
||||||
|
next: Option<UntypedArenaSlab>,
|
||||||
|
header: ArenaHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
if std::mem::align_of::<AllocSlab>() < std::mem::align_of::<*const ()>() {
|
||||||
|
panic!("alignment of AllocSlab is too low");
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::mem::offset_of!(AllocSlab, header) % std::mem::align_of::<*const ()>() != 0 {
|
||||||
|
panic!("alignment of header not a multiple of pointers alignment");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TypedAllocSlab<T: ?Sized + ArenaAllocated> {
|
||||||
|
slab: AllocSlab,
|
||||||
|
payload: T::Payload,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> TypedAllocSlab<T> {
|
||||||
|
pub fn tag(&self) -> ArenaHeaderTag {
|
||||||
|
self.slab.header.tag()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payload(&mut self) -> &mut T::Payload {
|
||||||
|
&mut self.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_untyped(self: Box<Self>) -> (TypedArenaPtr<T>, UntypedArenaSlab) {
|
||||||
|
let raw_box = Box::into_raw(self);
|
||||||
|
|
||||||
|
// safety: the pointer from Box::into_raw fulfills addr_of_mut's safety requirements
|
||||||
|
let payload_ptr = unsafe { addr_of_mut!((*raw_box).payload) };
|
||||||
|
|
||||||
|
(
|
||||||
|
TypedArenaPtr(unsafe {
|
||||||
|
// safety: the pointer points into a valid allocation so it is non null
|
||||||
|
ptr::NonNull::new_unchecked(payload_ptr)
|
||||||
|
}),
|
||||||
|
UntypedArenaSlab {
|
||||||
|
// safety: pointer from Box::into_raw is never null
|
||||||
|
slab: unsafe { NonNull::new_unchecked(raw_box.cast::<AllocSlab>()) },
|
||||||
|
tag: T::tag(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UntypedArenaSlab {
|
||||||
|
slab: NonNull<AllocSlab>,
|
||||||
|
tag: ArenaHeaderTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for UntypedArenaSlab {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { drop_slab_in_place(self.slab, self.tag) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Arena {
|
||||||
|
base: Option<UntypedArenaSlab>,
|
||||||
|
pub f64_tbl: F64Table,
|
||||||
|
pub code_index_tbl: CodeIndexTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Arena {}
|
||||||
|
unsafe impl Sync for Arena {}
|
||||||
|
|
||||||
|
#[allow(clippy::new_without_default)]
|
||||||
|
impl Arena {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Result<Self, AllocError> {
|
||||||
|
Ok(Arena {
|
||||||
|
base: None,
|
||||||
|
f64_tbl: F64Table::new()?,
|
||||||
|
code_index_tbl: CodeIndexTable::new()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn drop_slab_in_place(value: NonNull<AllocSlab>, tag: ArenaHeaderTag) {
|
||||||
|
macro_rules! drop_typed_slab_in_place {
|
||||||
|
($payload: ty, $value: expr) => {
|
||||||
|
<$payload as ArenaAllocated>::dealloc($value.cast::<TypedAllocSlab<$payload>>())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
match tag {
|
||||||
|
ArenaHeaderTag::Integer => {
|
||||||
|
drop_typed_slab_in_place!(Integer, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::Rational => {
|
||||||
|
drop_typed_slab_in_place!(Rational, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::InputFileStream => {
|
||||||
|
drop_typed_slab_in_place!(InputFileStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::OutputFileStream => {
|
||||||
|
drop_typed_slab_in_place!(OutputFileStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::NamedTcpStream => {
|
||||||
|
drop_typed_slab_in_place!(NamedTcpStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::NamedTlsStream => {
|
||||||
|
#[cfg(feature = "tls")]
|
||||||
|
drop_typed_slab_in_place!(NamedTlsStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::HttpReadStream => {
|
||||||
|
#[cfg(feature = "http")]
|
||||||
|
drop_typed_slab_in_place!(HttpReadStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::HttpWriteStream => {
|
||||||
|
#[cfg(feature = "http")]
|
||||||
|
drop_typed_slab_in_place!(HttpWriteStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::ReadlineStream => {
|
||||||
|
drop_typed_slab_in_place!(ReadlineStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::StaticStringStream => {
|
||||||
|
drop_typed_slab_in_place!(StaticStringStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::ByteStream => {
|
||||||
|
drop_typed_slab_in_place!(ByteStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::CallbackStream => {
|
||||||
|
drop_typed_slab_in_place!(CallbackStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::InputChannelStream => {
|
||||||
|
drop_typed_slab_in_place!(InputChannelStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => {
|
||||||
|
drop_typed_slab_in_place!(LiveLoadState, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::Dropped => {}
|
||||||
|
ArenaHeaderTag::TcpListener => {
|
||||||
|
drop_typed_slab_in_place!(TcpListener, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::HttpListener => {
|
||||||
|
#[cfg(feature = "http")]
|
||||||
|
drop_typed_slab_in_place!(HttpListener, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::HttpResponse => {
|
||||||
|
#[cfg(feature = "http")]
|
||||||
|
drop_typed_slab_in_place!(HttpResponse, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::StandardOutputStream => {
|
||||||
|
drop_typed_slab_in_place!(StandardOutputStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::StandardErrorStream => {
|
||||||
|
drop_typed_slab_in_place!(StandardErrorStream, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::PipeReader => {
|
||||||
|
drop_typed_slab_in_place!(PipeReader, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::PipeWriter => {
|
||||||
|
drop_typed_slab_in_place!(PipeWriter, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::ChildProcess => {
|
||||||
|
drop_typed_slab_in_place!(Child, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::NullStream => {
|
||||||
|
unreachable!("NullStream is never arena allocated!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Arena {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// we un-nest UntypedArenaSlab to prevent stackoverflow due to the recursive drop
|
||||||
|
|
||||||
|
let mut ptr = self.base.take();
|
||||||
|
|
||||||
|
while let Some(mut slab) = ptr {
|
||||||
|
ptr = unsafe { slab.slab.as_mut() }.next.take();
|
||||||
|
drop(slab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const_assert!(mem::size_of::<AllocSlab>() <= 24);
|
||||||
|
const_assert!(mem::size_of::<OrderedFloat<f64>>() == 8);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::arena::*;
|
||||||
|
use crate::atom_table::*;
|
||||||
|
use crate::machine::mock_wam::*;
|
||||||
|
use crate::types::*;
|
||||||
|
|
||||||
|
use crate::parser::dashu::{Integer, Rational};
|
||||||
|
use ordered_float::OrderedFloat;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_ptr_cast() {
|
||||||
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
let f = 0f64;
|
||||||
|
let fp = float_alloc!(f, wam.machine_st.arena);
|
||||||
|
let mut cell = HeapCellValue::from(fp);
|
||||||
|
|
||||||
|
assert_eq!(cell.get_tag(), HeapCellValueTag::F64Offset);
|
||||||
|
assert!(!cell.get_mark_bit());
|
||||||
|
assert_eq!(wam.machine_st.arena.f64_tbl.get_entry(fp), OrderedFloat(f));
|
||||||
|
|
||||||
|
cell.set_mark_bit(true);
|
||||||
|
|
||||||
|
assert!(cell.get_mark_bit());
|
||||||
|
|
||||||
|
read_heap_cell!(cell,
|
||||||
|
(HeapCellValueTag::F64Offset, offset) => {
|
||||||
|
let fp = wam.machine_st.arena.f64_tbl.get_entry(offset);
|
||||||
|
assert_eq!(fp, OrderedFloat(0f64))
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn heap_cell_value_const_cast() {
|
||||||
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
assert_eq!(ConsPtr::NICHE_SHIFT, 0);
|
||||||
|
|
||||||
|
#[cfg(not(target_pointer_width = "32"))]
|
||||||
|
assert_eq!(ConsPtr::NICHE_SHIFT, 3);
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
let dummy_ptr: *const ArenaHeader = std::ptr::without_provenance(0x0000_0438);
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
let dummy_ptr: *const ArenaHeader = std::ptr::without_provenance(0x0000_5555_ff00_0438);
|
||||||
|
|
||||||
|
assert!(dummy_ptr.is_aligned());
|
||||||
|
|
||||||
|
let const_value = HeapCellValue::from_arena_header_ptr(dummy_ptr);
|
||||||
|
|
||||||
|
match const_value.to_untyped_arena_ptr() {
|
||||||
|
Some(arena_ptr) => {
|
||||||
|
assert_eq!(
|
||||||
|
arena_ptr.into_bytes(),
|
||||||
|
const_value.to_untyped_arena_ptr_bytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream = Stream::from_static_string("test", &mut wam.machine_st.arena);
|
||||||
|
let stream_cell = HeapCellValue::from_arena_header_ptr(stream.as_ptr());
|
||||||
|
|
||||||
|
match stream_cell.to_untyped_arena_ptr() {
|
||||||
|
Some(arena_ptr) => {
|
||||||
|
assert_eq!(
|
||||||
|
arena_ptr.into_bytes(),
|
||||||
|
stream_cell.to_untyped_arena_ptr_bytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn heap_put_literal_tests() {
|
||||||
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
// integer
|
||||||
|
|
||||||
|
let big_int: Integer = 2 * Integer::from(1u64 << 63);
|
||||||
|
let big_int_ptr: TypedArenaPtr<Integer> = arena_alloc!(big_int, &mut wam.machine_st.arena);
|
||||||
|
|
||||||
|
assert!(!big_int_ptr.as_ptr().is_null());
|
||||||
|
|
||||||
|
let cell = HeapCellValue::from(big_int_ptr);
|
||||||
|
assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
|
||||||
|
|
||||||
|
let untyped_arena_ptr = match cell.to_untyped_arena_ptr() {
|
||||||
|
Some(ptr) => ptr,
|
||||||
|
None => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match_untyped_arena_ptr!(untyped_arena_ptr,
|
||||||
|
(ArenaHeaderTag::Integer, n) => {
|
||||||
|
assert_eq!(&*n, &(2 * Integer::from(1u64 << 63)))
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
);
|
||||||
|
|
||||||
|
read_heap_cell!(cell,
|
||||||
|
(HeapCellValueTag::Cons, cons_ptr) => {
|
||||||
|
match_untyped_arena_ptr!(cons_ptr,
|
||||||
|
(ArenaHeaderTag::Integer, n) => {
|
||||||
|
assert_eq!(&*n, &(2 * Integer::from(1u64 << 63)))
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
);
|
||||||
|
|
||||||
|
// rational
|
||||||
|
|
||||||
|
let big_rat = Rational::from(2) * Rational::from(1u64 << 63);
|
||||||
|
let big_rat_ptr: TypedArenaPtr<Rational> = arena_alloc!(big_rat, &mut wam.machine_st.arena);
|
||||||
|
|
||||||
|
assert!(!big_rat_ptr.as_ptr().is_null());
|
||||||
|
|
||||||
|
let rat_cell = typed_arena_ptr_as_cell!(big_rat_ptr);
|
||||||
|
assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
|
||||||
|
|
||||||
|
match rat_cell.to_untyped_arena_ptr() {
|
||||||
|
Some(untyped_arena_ptr) => {
|
||||||
|
assert_eq!(
|
||||||
|
Some(big_rat_ptr.header_ptr()),
|
||||||
|
Some(untyped_arena_ptr.get_ptr()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert_eq!(wam.machine_st.heap[1usize].get_tag(), HeapCellValueTag::Cons);
|
||||||
|
|
||||||
|
read_heap_cell!(rat_cell,
|
||||||
|
(HeapCellValueTag::Cons, cons_ptr) => {
|
||||||
|
match_untyped_arena_ptr!(cons_ptr,
|
||||||
|
(ArenaHeaderTag::Rational, n) => {
|
||||||
|
assert_eq!(&*n, &(Rational::from(2) * Rational::from(1u64 << 63)));
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
);
|
||||||
|
|
||||||
|
// atom
|
||||||
|
|
||||||
|
let f_atom = atom!("f");
|
||||||
|
let g_atom = atom!("g");
|
||||||
|
|
||||||
|
assert_eq!(&*f_atom.as_str(), "f");
|
||||||
|
assert_eq!(&*g_atom.as_str(), "g");
|
||||||
|
|
||||||
|
let f_atom_cell = atom_as_cell!(f_atom);
|
||||||
|
let g_atom_cell = atom_as_cell!(g_atom);
|
||||||
|
|
||||||
|
assert_eq!(f_atom_cell.get_tag(), HeapCellValueTag::Atom);
|
||||||
|
|
||||||
|
match f_atom_cell.to_atom() {
|
||||||
|
Some(atom) => {
|
||||||
|
assert_eq!(f_atom, atom);
|
||||||
|
assert_eq!(&*atom.as_str(), "f");
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
read_heap_cell!(f_atom_cell,
|
||||||
|
(HeapCellValueTag::Atom, (atom, arity)) => {
|
||||||
|
assert_eq!(f_atom, atom);
|
||||||
|
assert_eq!(arity, 0);
|
||||||
|
assert_eq!(&*atom.as_str(), "f");
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
);
|
||||||
|
|
||||||
|
read_heap_cell!(g_atom_cell,
|
||||||
|
(HeapCellValueTag::Atom, (atom, arity)) => {
|
||||||
|
assert_eq!(g_atom, atom);
|
||||||
|
assert_eq!(arity, 0);
|
||||||
|
assert_eq!(&*atom.as_str(), "g");
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
);
|
||||||
|
|
||||||
|
// fixnum
|
||||||
|
|
||||||
|
let fixnum_cell = fixnum_as_cell!(Fixnum::build_with(3));
|
||||||
|
|
||||||
|
assert_eq!(fixnum_cell.get_tag(), HeapCellValueTag::Fixnum);
|
||||||
|
|
||||||
|
match fixnum_cell.to_fixnum() {
|
||||||
|
Some(n) => assert_eq!(n.get_num(), 3),
|
||||||
|
None => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
read_heap_cell!(fixnum_cell,
|
||||||
|
(HeapCellValueTag::Fixnum, n) => {
|
||||||
|
assert_eq!(n.get_num(), 3);
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
);
|
||||||
|
|
||||||
|
let fixnum_b_cell = fixnum_as_cell!(
|
||||||
|
Fixnum::build_with_checked(1i64 << 54).expect("1 << 54 fits in Fixnum")
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(fixnum_b_cell.get_tag(), HeapCellValueTag::Fixnum);
|
||||||
|
|
||||||
|
match fixnum_b_cell.to_fixnum() {
|
||||||
|
Some(n) => assert_eq!(n.get_num(), 1 << 54),
|
||||||
|
None => unreachable!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
Fixnum::build_with_checked(1i64 << 56).expect_err("1 << 56 is too large for fixnum");
|
||||||
|
|
||||||
|
Fixnum::build_with_checked(i64::MAX).expect_err("i64::MAX is too large for Fixnum");
|
||||||
|
Fixnum::build_with_checked(i64::MIN).expect_err("i64::MIN is too small for Fixnum");
|
||||||
|
assert_eq!(
|
||||||
|
Fixnum::build_with_checked(-1i64)
|
||||||
|
.expect("-1 fits in fixnum")
|
||||||
|
.get_num(),
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
|
||||||
|
Fixnum::build_with_checked((1i64 << 55) - 1)
|
||||||
|
.expect("(1 << 55) - 1 is the largest value that fits in Fixnum");
|
||||||
|
|
||||||
|
Fixnum::build_with_checked(-(1i64 << 55))
|
||||||
|
.expect("-(1 << 55) is the smallest value that fits in fixnum");
|
||||||
|
Fixnum::build_with_checked(-(1i64 << 55) - 1)
|
||||||
|
.expect_err("-(1<<55) - 1 is too small for Fixnum");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
-Fixnum::build_with_checked(-1i64).expect("-1 fits in Fixnum"),
|
||||||
|
Fixnum::build_with(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// float
|
||||||
|
|
||||||
|
let float = std::f64::consts::PI;
|
||||||
|
let float_ptr = float_alloc!(float, wam.machine_st.arena);
|
||||||
|
let cell = HeapCellValue::from(float_ptr);
|
||||||
|
|
||||||
|
assert_eq!(cell.get_tag(), HeapCellValueTag::F64Offset);
|
||||||
|
|
||||||
|
// char
|
||||||
|
|
||||||
|
let c = 'c';
|
||||||
|
let char_cell = char_as_cell!(c);
|
||||||
|
|
||||||
|
read_heap_cell!(char_cell,
|
||||||
|
(HeapCellValueTag::Atom, (c, _arity)) => {
|
||||||
|
assert_eq!(&*c.as_str(), "c");
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
);
|
||||||
|
|
||||||
|
let c = 'Ћ';
|
||||||
|
let cyrillic_char_cell = char_as_cell!(c);
|
||||||
|
|
||||||
|
read_heap_cell!(cyrillic_char_cell,
|
||||||
|
(HeapCellValueTag::Atom, (c, _arity)) => {
|
||||||
|
assert_eq!(&*c.as_str(), "Ћ");
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
);
|
||||||
|
|
||||||
|
// empty list
|
||||||
|
|
||||||
|
let cell = empty_list_as_cell!();
|
||||||
|
|
||||||
|
read_heap_cell!(cell,
|
||||||
|
(HeapCellValueTag::Atom, (el, _arity)) => {
|
||||||
|
assert_eq!(el.flat_index(), empty_list_as_cell!().get_value());
|
||||||
|
assert_eq!(&*el.as_str(), "[]");
|
||||||
|
}
|
||||||
|
_ => { unreachable!() }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
672
src/arithmetic.rs
Normal file
672
src/arithmetic.rs
Normal file
@@ -0,0 +1,672 @@
|
|||||||
|
use crate::allocator::*;
|
||||||
|
use crate::arena::*;
|
||||||
|
use crate::atom_table::*;
|
||||||
|
use crate::debray_allocator::*;
|
||||||
|
use crate::forms::*;
|
||||||
|
use crate::instructions::*;
|
||||||
|
use crate::iterators::*;
|
||||||
|
use crate::offset_table::*;
|
||||||
|
use crate::targets::QueryInstruction;
|
||||||
|
use crate::types::*;
|
||||||
|
|
||||||
|
use crate::parser::ast::*;
|
||||||
|
use crate::parser::dashu::{Integer, Rational};
|
||||||
|
|
||||||
|
use crate::machine::machine_errors::*;
|
||||||
|
|
||||||
|
use dashu::base::Abs;
|
||||||
|
use dashu::base::BitTest;
|
||||||
|
use num_order::NumOrd;
|
||||||
|
use ordered_float::{Float, OrderedFloat};
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::f64;
|
||||||
|
use std::num::FpCategory;
|
||||||
|
use std::ops::Div;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ArithmeticTerm {
|
||||||
|
IntermReg(usize),
|
||||||
|
Reg(RegType),
|
||||||
|
Number(Number),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ArithmeticTerm {
|
||||||
|
fn default() -> Self {
|
||||||
|
ArithmeticTerm::Number(Number::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ArithInstructionIterator<'a> {
|
||||||
|
state_stack: Vec<TermIterState<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) type ArithCont = (CodeDeque, ArithmeticTerm);
|
||||||
|
|
||||||
|
impl<'a> ArithInstructionIterator<'a> {
|
||||||
|
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||||
|
self.state_stack
|
||||||
|
.push(TermIterState::subterm_to_state(lvl, term));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from(term: &'a Term) -> Result<Self, ArithmeticError> {
|
||||||
|
let state = match term {
|
||||||
|
Term::AnonVar => return Err(ArithmeticError::UninstantiatedVar),
|
||||||
|
Term::Clause(cell, name, terms) => {
|
||||||
|
TermIterState::Clause(Level::Shallow, 0, cell, *name, terms)
|
||||||
|
}
|
||||||
|
Term::Literal(cell, cons) => TermIterState::Literal(Level::Shallow, cell, cons),
|
||||||
|
Term::Cons(..) | Term::PartialString(..) | Term::CompleteString(..) => {
|
||||||
|
return Err(ArithmeticError::NonEvaluableFunctor(
|
||||||
|
Literal::Atom(atom!(".")),
|
||||||
|
2,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Shallow, cell, var_ptr.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ArithInstructionIterator {
|
||||||
|
state_stack: vec![state],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum ArithTermRef<'a> {
|
||||||
|
Literal(Literal),
|
||||||
|
Op(Level, &'a Cell<RegType>, Atom, usize), // name, arity.
|
||||||
|
Var(Level, &'a Cell<VarReg>, VarPtr),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for ArithInstructionIterator<'a> {
|
||||||
|
type Item = Result<ArithTermRef<'a>, ArithmeticError>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
while let Some(iter_state) = self.state_stack.pop() {
|
||||||
|
match iter_state {
|
||||||
|
TermIterState::AnonVar(_) => return Some(Err(ArithmeticError::UninstantiatedVar)),
|
||||||
|
TermIterState::Clause(lvl, child_num, cell, name, subterms) => {
|
||||||
|
let arity = subterms.len();
|
||||||
|
|
||||||
|
if child_num == arity {
|
||||||
|
return Some(Ok(ArithTermRef::Op(lvl, cell, name, arity)));
|
||||||
|
} else {
|
||||||
|
self.state_stack.push(TermIterState::Clause(
|
||||||
|
lvl,
|
||||||
|
child_num + 1,
|
||||||
|
cell,
|
||||||
|
name,
|
||||||
|
subterms,
|
||||||
|
));
|
||||||
|
|
||||||
|
self.push_subterm(lvl.child_level(), &subterms[child_num]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(*c))),
|
||||||
|
TermIterState::Var(lvl, cell, var_ptr) => {
|
||||||
|
return Some(Ok(ArithTermRef::Var(lvl, cell, var_ptr)));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Some(Err(ArithmeticError::NonEvaluableFunctor(
|
||||||
|
Literal::Atom(atom!(".")),
|
||||||
|
2,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ArithmeticEvaluator<'a> {
|
||||||
|
marker: &'a mut DebrayAllocator,
|
||||||
|
interm: Vec<ArithmeticTerm>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait ArithmeticTermIter<'a> {
|
||||||
|
type Iter: Iterator<Item = Result<ArithTermRef<'a>, ArithmeticError>>;
|
||||||
|
|
||||||
|
fn iter(self) -> Result<Self::Iter, ArithmeticError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArithmeticTermIter<'a> for &'a Term {
|
||||||
|
type Iter = ArithInstructionIterator<'a>;
|
||||||
|
|
||||||
|
fn iter(self) -> Result<Self::Iter, ArithmeticError> {
|
||||||
|
ArithInstructionIterator::from(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), ArithmeticError> {
|
||||||
|
match c {
|
||||||
|
Literal::Fixnum(n) => interm.push(ArithmeticTerm::Number(Number::Fixnum(*n))),
|
||||||
|
Literal::Integer(n) => interm.push(ArithmeticTerm::Number(Number::Integer(*n))),
|
||||||
|
&Literal::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)),
|
||||||
|
)),
|
||||||
|
Literal::Atom(name) if name == &atom!("pi") => interm.push(ArithmeticTerm::Number(
|
||||||
|
Number::Float(OrderedFloat(std::f64::consts::PI)),
|
||||||
|
)),
|
||||||
|
Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
|
||||||
|
Number::Float(OrderedFloat(f64::EPSILON)),
|
||||||
|
)),
|
||||||
|
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArithmeticEvaluator<'a> {
|
||||||
|
pub(crate) fn new(marker: &'a mut DebrayAllocator) -> Self {
|
||||||
|
ArithmeticEvaluator {
|
||||||
|
marker,
|
||||||
|
interm: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_unary_instr(
|
||||||
|
&self,
|
||||||
|
name: Atom,
|
||||||
|
a1: ArithmeticTerm,
|
||||||
|
t: usize,
|
||||||
|
) -> Result<Instruction, ArithmeticError> {
|
||||||
|
match name {
|
||||||
|
atom!("abs") => Ok(Instruction::Abs(a1, t)),
|
||||||
|
atom!("-") => Ok(Instruction::Neg(a1, t)),
|
||||||
|
atom!("+") => Ok(Instruction::Plus(a1, t)),
|
||||||
|
atom!("cos") => Ok(Instruction::Cos(a1, t)),
|
||||||
|
atom!("sin") => Ok(Instruction::Sin(a1, t)),
|
||||||
|
atom!("tan") => Ok(Instruction::Tan(a1, t)),
|
||||||
|
atom!("log") => Ok(Instruction::Log(a1, t)),
|
||||||
|
atom!("exp") => Ok(Instruction::Exp(a1, t)),
|
||||||
|
atom!("sqrt") => Ok(Instruction::Sqrt(a1, t)),
|
||||||
|
atom!("acos") => Ok(Instruction::ACos(a1, t)),
|
||||||
|
atom!("asin") => Ok(Instruction::ASin(a1, t)),
|
||||||
|
atom!("atan") => Ok(Instruction::ATan(a1, t)),
|
||||||
|
atom!("float") => Ok(Instruction::Float(a1, t)),
|
||||||
|
atom!("truncate") => Ok(Instruction::Truncate(a1, t)),
|
||||||
|
atom!("round") => Ok(Instruction::Round(a1, t)),
|
||||||
|
atom!("ceiling") => Ok(Instruction::Ceiling(a1, t)),
|
||||||
|
atom!("floor") => Ok(Instruction::Floor(a1, t)),
|
||||||
|
atom!("float_integer_part") => Ok(Instruction::FloatIntegerPart(a1, t)),
|
||||||
|
atom!("float_fractional_part") => Ok(Instruction::FloatFractionalPart(a1, t)),
|
||||||
|
atom!("sign") => Ok(Instruction::Sign(a1, t)),
|
||||||
|
atom!("\\") => Ok(Instruction::BitwiseComplement(a1, t)),
|
||||||
|
_ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 1)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_binary_instr(
|
||||||
|
&self,
|
||||||
|
name: Atom,
|
||||||
|
a1: ArithmeticTerm,
|
||||||
|
a2: ArithmeticTerm,
|
||||||
|
t: usize,
|
||||||
|
) -> Result<Instruction, ArithmeticError> {
|
||||||
|
match name {
|
||||||
|
atom!("+") => Ok(Instruction::Add(a1, a2, t)),
|
||||||
|
atom!("-") => Ok(Instruction::Sub(a1, a2, t)),
|
||||||
|
atom!("/") => Ok(Instruction::Div(a1, a2, t)),
|
||||||
|
atom!("//") => Ok(Instruction::IDiv(a1, a2, t)),
|
||||||
|
atom!("max") => Ok(Instruction::Max(a1, a2, t)),
|
||||||
|
atom!("min") => Ok(Instruction::Min(a1, a2, t)),
|
||||||
|
atom!("div") => Ok(Instruction::IntFloorDiv(a1, a2, t)),
|
||||||
|
atom!("rdiv") => Ok(Instruction::RDiv(a1, a2, t)),
|
||||||
|
atom!("*") => Ok(Instruction::Mul(a1, a2, t)),
|
||||||
|
atom!("**") => Ok(Instruction::Pow(a1, a2, t)),
|
||||||
|
atom!("^") => Ok(Instruction::IntPow(a1, a2, t)),
|
||||||
|
atom!(">>") => Ok(Instruction::Shr(a1, a2, t)),
|
||||||
|
atom!("<<") => Ok(Instruction::Shl(a1, a2, t)),
|
||||||
|
atom!("/\\") => Ok(Instruction::And(a1, a2, t)),
|
||||||
|
atom!("\\/") => Ok(Instruction::Or(a1, a2, t)),
|
||||||
|
atom!("xor") => Ok(Instruction::Xor(a1, a2, t)),
|
||||||
|
atom!("mod") => Ok(Instruction::Mod(a1, a2, t)),
|
||||||
|
atom!("rem") => Ok(Instruction::Rem(a1, a2, t)),
|
||||||
|
atom!("gcd") => Ok(Instruction::Gcd(a1, a2, t)),
|
||||||
|
atom!("atan2") => Ok(Instruction::ATan2(a1, a2, t)),
|
||||||
|
_ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 2)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_add_to_free_list(&mut self, a1: ArithmeticTerm) {
|
||||||
|
if let ArithmeticTerm::IntermReg(t) = a1 {
|
||||||
|
self.marker.add_reg_to_free_list(RegType::Temp(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn instr_from_clause(
|
||||||
|
&mut self,
|
||||||
|
name: Atom,
|
||||||
|
arity: usize,
|
||||||
|
arg: usize,
|
||||||
|
) -> Result<Instruction, ArithmeticError> {
|
||||||
|
match arity {
|
||||||
|
1 => {
|
||||||
|
let a1 = self.interm.pop().unwrap();
|
||||||
|
|
||||||
|
self.interm.push(ArithmeticTerm::IntermReg(arg));
|
||||||
|
self.try_add_to_free_list(a1);
|
||||||
|
|
||||||
|
self.get_unary_instr(name, a1, arg)
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
let a2 = self.interm.pop().unwrap();
|
||||||
|
let a1 = self.interm.pop().unwrap();
|
||||||
|
|
||||||
|
self.interm.push(ArithmeticTerm::IntermReg(arg));
|
||||||
|
|
||||||
|
self.try_add_to_free_list(a1);
|
||||||
|
self.try_add_to_free_list(a2);
|
||||||
|
|
||||||
|
self.get_binary_instr(name, a1, a2, arg)
|
||||||
|
}
|
||||||
|
_ => Err(ArithmeticError::NonEvaluableFunctor(
|
||||||
|
Literal::Atom(name),
|
||||||
|
arity,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compile_is(
|
||||||
|
&mut self,
|
||||||
|
src: &'a Term,
|
||||||
|
term_loc: GenContext,
|
||||||
|
arg: usize,
|
||||||
|
) -> Result<ArithCont, ArithmeticError> {
|
||||||
|
let mut code = CodeDeque::new();
|
||||||
|
|
||||||
|
for term_ref in src.iter()? {
|
||||||
|
match term_ref? {
|
||||||
|
ArithTermRef::Literal(c) => push_literal(&mut self.interm, &c)?,
|
||||||
|
ArithTermRef::Var(lvl, cell, name) => {
|
||||||
|
let var_num = name.to_var_num().unwrap();
|
||||||
|
|
||||||
|
let r = if lvl == Level::Shallow {
|
||||||
|
self.marker
|
||||||
|
.mark_non_callable(var_num, arg, term_loc, cell, &mut code)
|
||||||
|
} else if term_loc.is_last() || cell.get().norm().reg_num() == 0 {
|
||||||
|
let r = self.marker.get_binding(var_num);
|
||||||
|
|
||||||
|
if r.reg_num() == 0 {
|
||||||
|
self.marker.mark_var::<QueryInstruction>(
|
||||||
|
var_num, lvl, cell, term_loc, &mut code,
|
||||||
|
);
|
||||||
|
cell.get().norm()
|
||||||
|
} else {
|
||||||
|
self.marker.increment_running_count(var_num);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.marker.increment_running_count(var_num);
|
||||||
|
cell.get().norm()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.interm.push(ArithmeticTerm::Reg(r));
|
||||||
|
}
|
||||||
|
ArithTermRef::Op(lvl, cell, name, arity) => {
|
||||||
|
self.marker
|
||||||
|
.mark_non_var::<QueryInstruction>(lvl, term_loc, cell, &mut code);
|
||||||
|
|
||||||
|
if let RegType::Temp(t) = cell.get() {
|
||||||
|
code.push_back(self.instr_from_clause(name, arity, t)?);
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((code, self.interm.pop().unwrap()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// integer division rounding function -- 9.1.3.1.
|
||||||
|
pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result<Number, EvalError> {
|
||||||
|
match n {
|
||||||
|
&Number::Integer(i) => {
|
||||||
|
if let Ok(value) = Fixnum::build_with_checked(&*i) {
|
||||||
|
Ok(Number::Fixnum(value))
|
||||||
|
} else {
|
||||||
|
Ok(*n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Number::Fixnum(_) => Ok(*n),
|
||||||
|
&Number::Float(f) => {
|
||||||
|
let f = f.floor();
|
||||||
|
|
||||||
|
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 (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 {
|
||||||
|
Ok(Number::Integer(arena_alloc!(
|
||||||
|
Integer::try_from(classify_float(f.0)?).unwrap_or_else(|_| {
|
||||||
|
unreachable!();
|
||||||
|
}),
|
||||||
|
arena
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Number::Rational(r) => {
|
||||||
|
let floor = r.floor();
|
||||||
|
|
||||||
|
if let Ok(value) = Fixnum::build_with_checked(&floor) {
|
||||||
|
Ok(Number::Fixnum(value))
|
||||||
|
} else {
|
||||||
|
Ok(Number::Integer(arena_alloc!(floor, arena)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Fixnum> for Integer {
|
||||||
|
#[inline]
|
||||||
|
fn from(n: Fixnum) -> Integer {
|
||||||
|
Integer::from(n.get_num())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// floating point rounding function -- 9.1.4.1.
|
||||||
|
pub(crate) fn rnd_f(n: &Number) -> f64 {
|
||||||
|
match n {
|
||||||
|
&Number::Fixnum(n) => n.get_num() as f64,
|
||||||
|
Number::Integer(n) => n.to_f64().value(),
|
||||||
|
&Number::Float(OrderedFloat(f)) => f,
|
||||||
|
Number::Rational(r) => r.to_f64().value(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// floating point result function -- 9.1.4.2.
|
||||||
|
pub(crate) fn result_f(n: &Number) -> Result<f64, EvalError> {
|
||||||
|
classify_float(rnd_f(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn classify_float(f: f64) -> Result<f64, EvalError> {
|
||||||
|
match f.classify() {
|
||||||
|
FpCategory::Normal | FpCategory::Zero => Ok(f),
|
||||||
|
FpCategory::Infinite => {
|
||||||
|
if OrderedFloat(f) == OrderedFloat(f64::MAX) {
|
||||||
|
Ok(f)
|
||||||
|
} else {
|
||||||
|
Err(EvalError::FloatOverflow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FpCategory::Nan => Err(EvalError::Undefined),
|
||||||
|
_ => Ok(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn float_fn_to_f(n: i64) -> Result<f64, EvalError> {
|
||||||
|
classify_float(n as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn float_i_to_f(n: &Integer) -> Result<f64, EvalError> {
|
||||||
|
classify_float(n.to_f64().value())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn float_r_to_f(r: &Rational) -> Result<f64, EvalError> {
|
||||||
|
classify_float(r.to_f64().value())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn add_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||||
|
Ok(OrderedFloat(classify_float(f1 + f2)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn mul_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||||
|
Ok(OrderedFloat(classify_float(f1 * f2)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn div_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||||
|
if FpCategory::Zero == f2.classify() {
|
||||||
|
Err(EvalError::ZeroDivisor)
|
||||||
|
} else {
|
||||||
|
Ok(OrderedFloat(classify_float(f1 / f2)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Number> for Number {
|
||||||
|
type Output = Result<Number, EvalError>;
|
||||||
|
|
||||||
|
fn div(self, rhs: Number) -> Self::Output {
|
||||||
|
match (self, rhs) {
|
||||||
|
(Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_fn_to_f(n1.get_num())?,
|
||||||
|
float_fn_to_f(n2.get_num())?,
|
||||||
|
)?)),
|
||||||
|
(Number::Fixnum(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_fn_to_f(n1.get_num())?,
|
||||||
|
float_i_to_f(&n2)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_i_to_f(&n1)?,
|
||||||
|
float_fn_to_f(n2.get_num())?,
|
||||||
|
)?)),
|
||||||
|
(Number::Fixnum(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_fn_to_f(n1.get_num())?,
|
||||||
|
float_r_to_f(&n2)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Rational(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_r_to_f(&n1)?,
|
||||||
|
float_fn_to_f(n2.get_num())?,
|
||||||
|
)?)),
|
||||||
|
(Number::Fixnum(n1), Number::Float(OrderedFloat(n2))) => {
|
||||||
|
Ok(Number::Float(div_f(float_fn_to_f(n1.get_num())?, n2)?))
|
||||||
|
}
|
||||||
|
(Number::Float(OrderedFloat(n1)), Number::Fixnum(n2)) => {
|
||||||
|
Ok(Number::Float(div_f(n1, float_fn_to_f(n2.get_num())?)?))
|
||||||
|
}
|
||||||
|
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_i_to_f(&n1)?,
|
||||||
|
float_i_to_f(&n2)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Integer(n1), Number::Float(OrderedFloat(n2))) => {
|
||||||
|
Ok(Number::Float(div_f(float_i_to_f(&n1)?, n2)?))
|
||||||
|
}
|
||||||
|
(Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
|
||||||
|
Ok(Number::Float(div_f(n2, float_i_to_f(&n1)?)?))
|
||||||
|
}
|
||||||
|
(Number::Integer(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_i_to_f(&n1)?,
|
||||||
|
float_r_to_f(&n2)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Rational(n2), Number::Integer(n1)) => Ok(Number::Float(div_f(
|
||||||
|
float_r_to_f(&n2)?,
|
||||||
|
float_i_to_f(&n1)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Rational(n1), Number::Float(OrderedFloat(n2))) => {
|
||||||
|
Ok(Number::Float(div_f(float_r_to_f(&n1)?, n2)?))
|
||||||
|
}
|
||||||
|
(Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
|
||||||
|
Ok(Number::Float(div_f(n2, float_r_to_f(&n1)?)?))
|
||||||
|
}
|
||||||
|
(Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
|
||||||
|
Ok(Number::Float(div_f(f1, f2)?))
|
||||||
|
}
|
||||||
|
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Float(div_f(
|
||||||
|
float_r_to_f(&r1)?,
|
||||||
|
float_r_to_f(&r2)?,
|
||||||
|
)?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Number {
|
||||||
|
fn eq(&self, rhs: &Self) -> bool {
|
||||||
|
match (self, rhs) {
|
||||||
|
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.eq(&n2),
|
||||||
|
(&Number::Fixnum(n1), Number::Integer(n2)) => n1.get_num().num_eq(&**n2),
|
||||||
|
(Number::Integer(n1), &Number::Fixnum(n2)) => n1.num_eq(&n2.get_num()),
|
||||||
|
(&Number::Fixnum(n1), Number::Rational(n2)) => {
|
||||||
|
Integer::from(n1.get_num()).num_eq(&**n2)
|
||||||
|
}
|
||||||
|
(Number::Rational(n1), &Number::Fixnum(n2)) => n1.num_eq(&Integer::from(n2.get_num())),
|
||||||
|
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).eq(&n2),
|
||||||
|
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2.get_num() as f64)),
|
||||||
|
(Number::Integer(n1), Number::Integer(n2)) => n1.eq(n2),
|
||||||
|
(Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(n2),
|
||||||
|
(&Number::Float(n1), Number::Integer(n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())),
|
||||||
|
(Number::Integer(n1), Number::Rational(n2)) => n1.num_eq(&**n2),
|
||||||
|
(Number::Rational(n1), Number::Integer(n2)) => n1.num_eq(&**n2),
|
||||||
|
(Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(&n2),
|
||||||
|
(&Number::Float(n1), Number::Rational(n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())),
|
||||||
|
(&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2),
|
||||||
|
(Number::Rational(r1), Number::Rational(r2)) => r1.eq(r2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Number {}
|
||||||
|
|
||||||
|
impl PartialOrd<usize> for Number {
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, rhs: &usize) -> Option<Ordering> {
|
||||||
|
match self {
|
||||||
|
Number::Fixnum(n) => {
|
||||||
|
let n = n.get_num();
|
||||||
|
|
||||||
|
if n < 0i64 {
|
||||||
|
Some(Ordering::Less)
|
||||||
|
} else {
|
||||||
|
(n as usize).partial_cmp(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Number::Integer(n) => Some((n).num_cmp(rhs)),
|
||||||
|
Number::Rational(r) => Some((r).num_cmp(&Integer::from(*rhs))),
|
||||||
|
Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<usize> for Number {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, rhs: &usize) -> bool {
|
||||||
|
match self {
|
||||||
|
Number::Fixnum(n) => {
|
||||||
|
let n = n.get_num();
|
||||||
|
|
||||||
|
if n < 0i64 {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
(n as usize).eq(rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Number::Integer(n) => (n).num_eq(rhs),
|
||||||
|
Number::Rational(r) => (r).num_eq(&Integer::from(*rhs)),
|
||||||
|
Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Number {
|
||||||
|
fn partial_cmp(&self, rhs: &Number) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(rhs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Number {
|
||||||
|
fn cmp(&self, rhs: &Number) -> Ordering {
|
||||||
|
match (self, rhs) {
|
||||||
|
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.get_num().cmp(&n2.get_num()),
|
||||||
|
(&Number::Fixnum(n1), Number::Integer(n2)) => Integer::from(n1.get_num()).cmp(n2),
|
||||||
|
(Number::Integer(n1), &Number::Fixnum(n2)) => (**n1).cmp(&Integer::from(n2.get_num())),
|
||||||
|
(&Number::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1.get_num()).cmp(n2),
|
||||||
|
(Number::Rational(n1), &Number::Fixnum(n2)) => {
|
||||||
|
(**n1).cmp(&Rational::from(n2.get_num()))
|
||||||
|
}
|
||||||
|
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).cmp(&n2),
|
||||||
|
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2.get_num() as f64)),
|
||||||
|
(&Number::Integer(n1), &Number::Integer(n2)) => (*n1).cmp(&*n2),
|
||||||
|
(&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(n2),
|
||||||
|
(&Number::Float(n1), Number::Integer(n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())),
|
||||||
|
(&Number::Integer(n1), &Number::Rational(n2)) => {
|
||||||
|
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||||
|
}
|
||||||
|
(&Number::Rational(n1), &Number::Integer(n2)) => {
|
||||||
|
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||||
|
}
|
||||||
|
(&Number::Rational(n1), &Number::Float(n2)) => {
|
||||||
|
OrderedFloat(n1.to_f64().value()).cmp(&n2)
|
||||||
|
}
|
||||||
|
(&Number::Float(n1), &Number::Rational(n2)) => {
|
||||||
|
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
||||||
|
}
|
||||||
|
(&Number::Float(f1), &Number::Float(f2)) => f1.cmp(&f2),
|
||||||
|
(&Number::Rational(r1), &Number::Rational(r2)) => (*r1).cmp(&*r2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<(HeapCellValue, &'_ F64Table)> for Number {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_from((value, f64_tbl): (HeapCellValue, &F64Table)) -> Result<Number, Self::Error> {
|
||||||
|
read_heap_cell!(value,
|
||||||
|
(HeapCellValueTag::Cons, c) => {
|
||||||
|
match_untyped_arena_ptr!(c,
|
||||||
|
(ArenaHeaderTag::Integer, n) => {
|
||||||
|
Ok(Number::Integer(n))
|
||||||
|
}
|
||||||
|
(ArenaHeaderTag::Rational, n) => {
|
||||||
|
Ok(Number::Rational(n))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(HeapCellValueTag::F64Offset, offset) => {
|
||||||
|
let n = f64_tbl.get_entry(offset);
|
||||||
|
Ok(Number::Float(n))
|
||||||
|
}
|
||||||
|
(HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => {
|
||||||
|
Ok(Number::Fixnum(n))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes n ^ power. Ignores the sign of power.
|
||||||
|
pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer {
|
||||||
|
let mut power = power.abs();
|
||||||
|
|
||||||
|
if power.is_zero() {
|
||||||
|
return Integer::ONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut oddand = Integer::ONE;
|
||||||
|
|
||||||
|
while power.num_gt(&1) {
|
||||||
|
if power.bit(0) {
|
||||||
|
oddand *= &n;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = n.pow(2);
|
||||||
|
power >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n * oddand
|
||||||
|
}
|
||||||
548
src/atom_table.rs
Normal file
548
src/atom_table.rs
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
use crate::machine::heap::AllocError;
|
||||||
|
use crate::parser::ast::MAX_ARITY;
|
||||||
|
use crate::raw_block::*;
|
||||||
|
use crate::types::*;
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::mem;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::ptr;
|
||||||
|
use std::str;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
use std::sync::Weak;
|
||||||
|
|
||||||
|
use arcu::Rcu;
|
||||||
|
use arcu::atomic::Arcu;
|
||||||
|
use arcu::epoch_counters::GlobalEpochCounterPool;
|
||||||
|
use arcu::rcu_ref::RcuRef;
|
||||||
|
use indexmap::IndexSet;
|
||||||
|
|
||||||
|
use 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
*atom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>> {
|
||||||
|
static GLOBAL_ATOM_TABLE: RwLock<Weak<AtomTable>> = RwLock::new(Weak::new());
|
||||||
|
&GLOBAL_ATOM_TABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn arc_atom_table() -> Option<Arc<AtomTable>> {
|
||||||
|
global_atom_table().read().unwrap().upgrade()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawBlockTraits for AtomTable {
|
||||||
|
#[inline]
|
||||||
|
fn init_size() -> usize {
|
||||||
|
ATOM_TABLE_INIT_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn align() -> usize {
|
||||||
|
ATOM_TABLE_ALIGN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitfield]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
struct AtomHeader {
|
||||||
|
#[allow(unused)]
|
||||||
|
m: bool,
|
||||||
|
len: B50,
|
||||||
|
#[allow(unused)]
|
||||||
|
padding: B13,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Atom {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||||
|
self.as_str().hash(hasher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AtomString<'a> {
|
||||||
|
Static(&'a str),
|
||||||
|
Inlined([u8; 8]),
|
||||||
|
Dynamic(AtomTableRef<str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
std::fmt::Debug::fmt(self.deref(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for AtomString<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
std::fmt::Display::fmt(self.deref(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for AtomString<'_> {
|
||||||
|
type Target = str;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
match self {
|
||||||
|
Self::Static(reference) => reference,
|
||||||
|
Self::Inlined(inlined) => inlined_to_str(inlined),
|
||||||
|
Self::Dynamic(guard) => guard.deref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "repl")]
|
||||||
|
impl rustyline::completion::Candidate for AtomString<'_> {
|
||||||
|
fn display(&self) -> &str {
|
||||||
|
self.deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replacement(&self) -> &str {
|
||||||
|
self.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Atom {
|
||||||
|
#[inline]
|
||||||
|
fn new_inlined(string: &str) -> Self {
|
||||||
|
AtomCell::new_inlined(string, 0).get_name()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
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");
|
||||||
|
|
||||||
|
AtomTableRef::try_map(atom_table.inner.read(), |buf| unsafe {
|
||||||
|
let ptr = buf
|
||||||
|
.block
|
||||||
|
.get_unchecked(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))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn from(index: u64) -> Self {
|
||||||
|
Self { index }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn len(self) -> usize {
|
||||||
|
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 len: u64 = self.as_ptr().unwrap().header.len();
|
||||||
|
len as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(self) -> bool {
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_char(self) -> Option<char> {
|
||||||
|
let s = self.as_str();
|
||||||
|
let mut it = s.chars();
|
||||||
|
|
||||||
|
let c1 = it.next();
|
||||||
|
let c2 = it.next();
|
||||||
|
|
||||||
|
if c2.is_none() { c1 } else { None }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn inlined_str<'a>(&self) -> Option<AtomString<'a>> {
|
||||||
|
if self.is_inlined() {
|
||||||
|
Some(AtomString::Inlined(self.flat_index().to_le_bytes()))
|
||||||
|
} else {
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn defrock_brackets(&self, atom_tbl: &AtomTable) -> Self {
|
||||||
|
let s = self.as_str();
|
||||||
|
|
||||||
|
let sub_str = if s.starts_with('(') && s.ends_with(')') {
|
||||||
|
&s['('.len_utf8()..s.len() - ')'.len_utf8()]
|
||||||
|
} else {
|
||||||
|
return *self;
|
||||||
|
};
|
||||||
|
|
||||||
|
AtomTable::build_with(atom_tbl, sub_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_to_ptr(string: &str, ptr: *mut u8) {
|
||||||
|
unsafe {
|
||||||
|
ptr::write(ptr as *mut _, AtomHeader::build_with(string.len() as u64));
|
||||||
|
let str_ptr = ptr.add(mem::size_of::<AtomHeader>());
|
||||||
|
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr, string.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Atom {
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, other: &Atom) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Atom {
|
||||||
|
#[inline]
|
||||||
|
fn cmp(&self, other: &Atom) -> Ordering {
|
||||||
|
self.as_str().cmp(&*other.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InnerAtomTable {
|
||||||
|
block: RawBlock<AtomTable>,
|
||||||
|
pub table: Arcu<IndexSet<Atom>, GlobalEpochCounterPool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AtomTable {
|
||||||
|
inner: Arcu<InnerAtomTable, GlobalEpochCounterPool>,
|
||||||
|
// this lock is taking during resizing
|
||||||
|
update: Mutex<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type AtomTableRef<M> = arcu::rcu_ref::RcuRef<InnerAtomTable, M>;
|
||||||
|
|
||||||
|
impl InnerAtomTable {
|
||||||
|
#[inline(always)]
|
||||||
|
fn lookup_str(self: &InnerAtomTable, string: &str) -> Option<Atom> {
|
||||||
|
STATIC_ATOMS_MAP
|
||||||
|
.get(string)
|
||||||
|
.cloned()
|
||||||
|
.or_else(|| self.table.read().get(string).cloned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtomTable {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Result<Arc<Self>, AllocError> {
|
||||||
|
let upgraded = global_atom_table().read().unwrap().upgrade();
|
||||||
|
// don't inline upgraded, otherwise temporary will be dropped too late in case of None
|
||||||
|
if let Some(atom_table) = upgraded {
|
||||||
|
Ok(atom_table)
|
||||||
|
} else {
|
||||||
|
let mut guard = global_atom_table().write().unwrap();
|
||||||
|
// try to upgrade again in case we lost the race on the write lock
|
||||||
|
if let Some(atom_table) = guard.upgrade() {
|
||||||
|
Ok(atom_table)
|
||||||
|
} else {
|
||||||
|
let atom_table = Arc::new(Self {
|
||||||
|
inner: Arcu::new(
|
||||||
|
InnerAtomTable {
|
||||||
|
block: RawBlock::new()?,
|
||||||
|
table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool),
|
||||||
|
},
|
||||||
|
GlobalEpochCounterPool,
|
||||||
|
),
|
||||||
|
update: Mutex::new(()),
|
||||||
|
});
|
||||||
|
*guard = Arc::downgrade(&atom_table);
|
||||||
|
Ok(atom_table)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn retrieve() -> Arc<Self> {
|
||||||
|
global_atom_table().read().unwrap().upgrade().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
|
||||||
|
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.read();
|
||||||
|
let mut table_epoch = block_epoch.table.read();
|
||||||
|
|
||||||
|
if let Some(atom) = block_epoch.lookup_str(string) {
|
||||||
|
return atom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take a lock to prevent concurrent updates
|
||||||
|
let update_guard = atom_table.update.lock().unwrap();
|
||||||
|
|
||||||
|
let is_same_allocation = RcuRef::same_epoch(&block_epoch, &atom_table.inner.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
|
||||||
|
// us aquring the update lock,
|
||||||
|
// try again
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = mem::size_of::<AtomHeader>() + string.len();
|
||||||
|
let size = size.next_multiple_of(AtomTable::align());
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let len_ptr = loop {
|
||||||
|
let ptr = block_epoch.block.alloc(size);
|
||||||
|
|
||||||
|
if ptr.is_null() {
|
||||||
|
// garbage collection would go here
|
||||||
|
let new_block = block_epoch.block.grow_new().unwrap();
|
||||||
|
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.read();
|
||||||
|
table_epoch = block_epoch.table.read();
|
||||||
|
} else {
|
||||||
|
break ptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: `len_ptr` was obtained from `block_epoch.block.alloc()`
|
||||||
|
let len_offset = block_epoch.block.get_offset(len_ptr);
|
||||||
|
|
||||||
|
write_to_ptr(string, len_ptr);
|
||||||
|
|
||||||
|
let atom = AtomCell::new()
|
||||||
|
.with_name((STRINGS.len() + len_offset) 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);
|
||||||
|
block_epoch.table.replace(table);
|
||||||
|
|
||||||
|
// explicit drop to ensure we don't accidentally drop it early
|
||||||
|
drop(update_guard);
|
||||||
|
|
||||||
|
return atom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for AtomTable {}
|
||||||
|
unsafe impl Sync for AtomTable {}
|
||||||
7
src/bin/scryer-prolog.rs
Normal file
7
src/bin/scryer-prolog.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
fn main() -> std::process::ExitCode {
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
return std::process::ExitCode::SUCCESS;
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
return scryer_prolog::run_binary();
|
||||||
|
}
|
||||||
1321
src/codegen.rs
Normal file
1321
src/codegen.rs
Normal file
File diff suppressed because it is too large
Load Diff
889
src/debray_allocator.rs
Normal file
889
src/debray_allocator.rs
Normal file
@@ -0,0 +1,889 @@
|
|||||||
|
use crate::allocator::*;
|
||||||
|
use crate::codegen::SubsumedBranchHits;
|
||||||
|
use crate::forms::{BranchNumber, GenContext, Level};
|
||||||
|
use crate::instructions::*;
|
||||||
|
use crate::machine::disjuncts::VarData;
|
||||||
|
use crate::parser::ast::*;
|
||||||
|
use crate::targets::*;
|
||||||
|
use crate::variable_records::*;
|
||||||
|
|
||||||
|
use bit_set::*;
|
||||||
|
use bitvec::prelude::*;
|
||||||
|
use fxhash::FxBuildHasher;
|
||||||
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub type BranchHits = IndexMap<usize, BitVec, FxBuildHasher>; // key: var_num, value: branch arm occurrences.
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct BranchOccurrences {
|
||||||
|
pub hits: BranchHits,
|
||||||
|
pub shallow_safety: BitSet<usize>, // unset means safe, set means unsafe (after the branch merge)
|
||||||
|
pub deep_safety: BitSet<usize>,
|
||||||
|
pub num_branches: usize,
|
||||||
|
pub current_branch_idx: usize,
|
||||||
|
pub current_branch_num: Arc<BranchNumber>,
|
||||||
|
pub subsumed_hits: SubsumedBranchHits,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BranchOccurrences {
|
||||||
|
fn new(current_branch_num: Arc<BranchNumber>, num_branches: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
hits: BranchHits::with_hasher(FxBuildHasher::default()),
|
||||||
|
shallow_safety: BitSet::default(),
|
||||||
|
deep_safety: BitSet::default(),
|
||||||
|
num_branches,
|
||||||
|
current_branch_idx: 0,
|
||||||
|
current_branch_num,
|
||||||
|
subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
|
||||||
|
debug_assert!(self.current_branch_idx < self.num_branches);
|
||||||
|
let num_branches = self.num_branches;
|
||||||
|
|
||||||
|
let entry = self
|
||||||
|
.hits
|
||||||
|
.entry(var_num)
|
||||||
|
.or_insert_with(|| BitVec::repeat(false, num_branches));
|
||||||
|
|
||||||
|
entry.set(self.current_branch_idx, true);
|
||||||
|
self.subsumed_hits.insert(var_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct BranchStack {
|
||||||
|
stack: Vec<BranchOccurrences>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for BranchStack {
|
||||||
|
type Target = Vec<BranchOccurrences>;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.stack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for BranchStack {
|
||||||
|
#[inline]
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.stack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BranchStack {
|
||||||
|
fn safety_unneeded_in_branch(
|
||||||
|
&self,
|
||||||
|
safety: &VarSafetyStatus,
|
||||||
|
branch: &BranchDesignator,
|
||||||
|
) -> bool {
|
||||||
|
match safety {
|
||||||
|
VarSafetyStatus::Needed => false,
|
||||||
|
VarSafetyStatus::LocallyUnneeded(planter_branch) => planter_branch
|
||||||
|
.branch_num
|
||||||
|
.has_as_subbranch(&branch.branch_num),
|
||||||
|
VarSafetyStatus::GloballyUnneeded => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
|
||||||
|
if let Some(occurrences) = self.last_mut() {
|
||||||
|
occurrences.add_branch_occurrence(var_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_branch_stack(&mut self, branch_num: Arc<BranchNumber>, num_branches: usize) {
|
||||||
|
self.push(BranchOccurrences::new(branch_num, num_branches));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn current_branch_designator(&self) -> BranchDesignator {
|
||||||
|
let branch_num = self
|
||||||
|
.last()
|
||||||
|
.map(|occurrences| occurrences.current_branch_num.clone())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
BranchDesignator { branch_num }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn incr_current_branch(&mut self, branch_num: Arc<BranchNumber>) {
|
||||||
|
let branch_occurrences = self.last_mut().unwrap();
|
||||||
|
branch_occurrences.current_branch_idx += 1;
|
||||||
|
branch_occurrences.current_branch_num = branch_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn drain_branches(
|
||||||
|
&mut self,
|
||||||
|
depth: usize,
|
||||||
|
) -> std::vec::Drain<'_, BranchOccurrences> {
|
||||||
|
let start_idx = self.len() - depth;
|
||||||
|
self.drain(start_idx..)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct DebrayAllocator {
|
||||||
|
pub(crate) var_data: VarData, // var_data replaces bindings.
|
||||||
|
pub(crate) branch_stack: BranchStack,
|
||||||
|
pub(crate) in_tail_position: bool,
|
||||||
|
arg_c: usize,
|
||||||
|
temp_lb: usize,
|
||||||
|
perm_lb: usize,
|
||||||
|
arity: usize, // 0 if not at head.
|
||||||
|
shallow_temp_mappings: IndexMap<usize, usize, FxBuildHasher>,
|
||||||
|
in_use: BitSet<usize>, // deep and non-var allocations
|
||||||
|
temp_free_list: Vec<usize>,
|
||||||
|
perm_free_list: VecDeque<(usize, usize)>, // chunk_num, var_num
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DebrayAllocator {
|
||||||
|
pub(crate) fn add_branch(&mut self) {
|
||||||
|
let branch_designator = self.branch_stack.current_branch_designator();
|
||||||
|
let subsumed_hits = {
|
||||||
|
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||||
|
|
||||||
|
std::mem::replace(
|
||||||
|
&mut branch_occurrences.subsumed_hits,
|
||||||
|
SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
for var_num in subsumed_hits {
|
||||||
|
match &mut self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Perm(_, allocation) => {
|
||||||
|
if let PermVarAllocation::Done {
|
||||||
|
shallow_safety,
|
||||||
|
deep_safety,
|
||||||
|
..
|
||||||
|
} = allocation
|
||||||
|
{
|
||||||
|
if !self
|
||||||
|
.branch_stack
|
||||||
|
.safety_unneeded_in_branch(shallow_safety, &branch_designator)
|
||||||
|
{
|
||||||
|
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||||
|
branch_occurrences.shallow_safety.insert(var_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.branch_stack
|
||||||
|
.safety_unneeded_in_branch(deep_safety, &branch_designator)
|
||||||
|
{
|
||||||
|
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||||
|
branch_occurrences.deep_safety.insert(var_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*allocation = PermVarAllocation::Pending;
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn pop_branch(&mut self, depth: usize, subsumed_hits: SubsumedBranchHits) {
|
||||||
|
let removed_branches = self.branch_stack.drain_branches(depth);
|
||||||
|
|
||||||
|
let (deep_safety, shallow_safety) = removed_branches.into_iter().fold(
|
||||||
|
(BitSet::default(), BitSet::default()),
|
||||||
|
|(mut deep_safety, mut shallow_safety), branch_occurrences| {
|
||||||
|
deep_safety.union_with(&branch_occurrences.deep_safety);
|
||||||
|
shallow_safety.union_with(&branch_occurrences.shallow_safety);
|
||||||
|
|
||||||
|
(deep_safety, shallow_safety)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||||
|
|
||||||
|
let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() {
|
||||||
|
Some(latest_branch) => {
|
||||||
|
latest_branch.deep_safety.union_with(&deep_safety);
|
||||||
|
latest_branch.shallow_safety.union_with(&shallow_safety);
|
||||||
|
|
||||||
|
(&latest_branch.deep_safety, &latest_branch.shallow_safety)
|
||||||
|
}
|
||||||
|
None => (&deep_safety, &shallow_safety),
|
||||||
|
};
|
||||||
|
|
||||||
|
for var_num in subsumed_hits.iter().cloned() {
|
||||||
|
let running_count = self.var_data.records[var_num].running_count;
|
||||||
|
let num_occurrences = self.var_data.records[var_num].num_occurrences;
|
||||||
|
|
||||||
|
match &mut self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Perm(_, allocation) => {
|
||||||
|
let shallow_safety = VarSafetyStatus::needed_if(
|
||||||
|
shallow_safety.contains(var_num),
|
||||||
|
&branch_designator,
|
||||||
|
);
|
||||||
|
|
||||||
|
let deep_safety = VarSafetyStatus::needed_if(
|
||||||
|
deep_safety.contains(var_num),
|
||||||
|
&branch_designator,
|
||||||
|
);
|
||||||
|
|
||||||
|
if running_count < num_occurrences {
|
||||||
|
*allocation = PermVarAllocation::Done {
|
||||||
|
shallow_safety,
|
||||||
|
deep_safety,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.branch_stack.is_empty() {
|
||||||
|
for var_num in subsumed_hits {
|
||||||
|
self.branch_stack.add_branch_occurrence(var_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_curr_arg_distinct_from(&self, var_num: usize) -> bool {
|
||||||
|
match self.shallow_temp_mappings.get(&self.arg_c).cloned() {
|
||||||
|
Some(t_var) => t_var != var_num,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn occurs_shallowly_in_head(&self, var_num: usize, r: usize) -> bool {
|
||||||
|
match &self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Temp {
|
||||||
|
temp_var_data,
|
||||||
|
term_loc: GenContext::Head,
|
||||||
|
..
|
||||||
|
} => temp_var_data.use_set.contains(&(GenContext::Head, r)),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_in_use(&self, r: usize) -> bool {
|
||||||
|
let in_use_range = r <= self.arity && r >= self.arg_c;
|
||||||
|
in_use_range || self.in_use.contains(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_with_cr(&self, var_num: usize) -> usize {
|
||||||
|
match &self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Temp { temp_var_data, .. } => {
|
||||||
|
for &(_, reg) in temp_var_data.use_set.iter() {
|
||||||
|
if !self.is_in_use(reg) {
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = 0;
|
||||||
|
|
||||||
|
for reg in self.temp_lb.. {
|
||||||
|
if !self.is_in_use(reg) && !temp_var_data.no_use_set.contains(reg) {
|
||||||
|
result = reg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_with_ca(&self, var_num: usize) -> usize {
|
||||||
|
match &self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Temp { temp_var_data, .. } => {
|
||||||
|
for &(_, reg) in temp_var_data.use_set.iter() {
|
||||||
|
if !self.is_in_use(reg) {
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = 0;
|
||||||
|
|
||||||
|
for reg in self.temp_lb.. {
|
||||||
|
if !self.is_in_use(reg)
|
||||||
|
&& !temp_var_data.no_use_set.contains(reg)
|
||||||
|
&& !temp_var_data.conflict_set.contains(reg)
|
||||||
|
{
|
||||||
|
result = reg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(usize, usize)> {
|
||||||
|
// we want to allocate a register to the k^{th} parameter, par_k.
|
||||||
|
// par_k may not be a temporary variable.
|
||||||
|
let k = self.arg_c;
|
||||||
|
|
||||||
|
match self.shallow_temp_mappings.get(&k).cloned() {
|
||||||
|
Some(t_var) => {
|
||||||
|
// suppose this branch fires. then t_var is a
|
||||||
|
// temp. var. belonging to the current chunk.
|
||||||
|
// consider its use set. T == par_k iff
|
||||||
|
// (GenContext::Last(_), k) is in t_var.use_set.
|
||||||
|
|
||||||
|
if let VarAlloc::Temp { temp_var_data, .. } =
|
||||||
|
&self.var_data.records[t_var].allocation
|
||||||
|
{
|
||||||
|
if !temp_var_data
|
||||||
|
.use_set
|
||||||
|
.contains(&(GenContext::Last(chunk_num), k))
|
||||||
|
{
|
||||||
|
return Some((t_var, self.alloc_with_ca(t_var)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evacuate_arg<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
chunk_num: usize,
|
||||||
|
code: &mut CodeDeque,
|
||||||
|
) {
|
||||||
|
if let Some((var_num, r)) = self.alloc_in_last_goal_hint(chunk_num) {
|
||||||
|
let k = self.arg_c;
|
||||||
|
|
||||||
|
if r != k {
|
||||||
|
let r = RegType::Temp(r);
|
||||||
|
|
||||||
|
code.push_back(Target::move_to_register(r, k));
|
||||||
|
|
||||||
|
self.shallow_temp_mappings.swap_remove(&k);
|
||||||
|
self.shallow_temp_mappings.insert(r.reg_num(), var_num);
|
||||||
|
|
||||||
|
self.var_data.records[var_num]
|
||||||
|
.allocation
|
||||||
|
.set_register(r.reg_num());
|
||||||
|
self.in_use.insert(r.reg_num());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_reg_to_var<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
var_num: usize,
|
||||||
|
lvl: Level,
|
||||||
|
term_loc: GenContext,
|
||||||
|
target: &mut CodeDeque,
|
||||||
|
) -> usize {
|
||||||
|
match term_loc {
|
||||||
|
GenContext::Head => {
|
||||||
|
if let Level::Shallow = lvl {
|
||||||
|
self.evacuate_arg::<Target>(0, target);
|
||||||
|
self.alloc_with_cr(var_num)
|
||||||
|
} else {
|
||||||
|
self.alloc_with_ca(var_num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GenContext::Mid(_) => self.alloc_with_ca(var_num),
|
||||||
|
GenContext::Last(chunk_num) => {
|
||||||
|
if let Level::Shallow = lvl {
|
||||||
|
self.evacuate_arg::<Target>(chunk_num, target);
|
||||||
|
self.alloc_with_cr(var_num)
|
||||||
|
} else {
|
||||||
|
self.alloc_with_ca(var_num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_reg_to_non_var(&mut self) -> usize {
|
||||||
|
let mut final_index = 0;
|
||||||
|
|
||||||
|
while let Some(r) = self.temp_free_list.pop() {
|
||||||
|
if !self.is_in_use(r) {
|
||||||
|
self.in_use.insert(r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for index in self.temp_lb.. {
|
||||||
|
if !self.in_use.contains(index) {
|
||||||
|
final_index = index;
|
||||||
|
self.in_use.insert(final_index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.temp_lb = final_index + 1;
|
||||||
|
final_index
|
||||||
|
}
|
||||||
|
|
||||||
|
fn in_place(&self, var_num: usize, term_loc: GenContext, r: RegType, k: usize) -> bool {
|
||||||
|
match term_loc {
|
||||||
|
GenContext::Head if !r.is_perm() => r.reg_num() == k,
|
||||||
|
_ => match &self.var_data.records[var_num].allocation {
|
||||||
|
&VarAlloc::Temp { temp_reg, .. } if r.reg_num() == k => temp_reg == k,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_perm_var(&mut self, var_num: usize, chunk_num: usize) -> usize {
|
||||||
|
let p = if let Some(p) = self.pop_free_perm(chunk_num) {
|
||||||
|
p
|
||||||
|
} else {
|
||||||
|
let p = self.perm_lb;
|
||||||
|
self.perm_lb += 1;
|
||||||
|
|
||||||
|
p
|
||||||
|
};
|
||||||
|
|
||||||
|
self.var_data.records[var_num].allocation = VarAlloc::Perm(p, PermVarAllocation::done());
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_reg_to_free_list(&mut self, r: RegType) {
|
||||||
|
if let RegType::Temp(r) = r {
|
||||||
|
self.in_use.remove(r);
|
||||||
|
self.temp_free_list.push(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_free_list(&mut self) {
|
||||||
|
self.temp_free_list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn get_binding(&self, var_num: usize) -> RegType {
|
||||||
|
self.var_data.records[var_num].allocation.as_reg_type()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_perm_vars(&self) -> usize {
|
||||||
|
self.perm_lb - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn increment_running_count(&mut self, var_num: usize) {
|
||||||
|
self.var_data.records[var_num].running_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_perm_to_free_list(&mut self, chunk_num: usize, var_num: usize) {
|
||||||
|
if let VarAlloc::Perm(..) = &self.var_data.records[var_num].allocation {
|
||||||
|
self.perm_free_list.push_back((chunk_num, var_num));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_free_perm(&mut self, chunk_num: usize) -> Option<usize> {
|
||||||
|
while let Some((perm_chunk_num, var_num)) = self.perm_free_list.front().cloned() {
|
||||||
|
if chunk_num > perm_chunk_num {
|
||||||
|
self.perm_free_list.pop_front();
|
||||||
|
|
||||||
|
match &mut self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Perm(p, PermVarAllocation::Pending) if *p > 0 => {
|
||||||
|
return Some(std::mem::replace(p, 0));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn free_var(&mut self, chunk_num: usize, var_num: usize) {
|
||||||
|
if let VarAlloc::Perm(_, allocation) = &mut self.var_data.records[var_num].allocation {
|
||||||
|
*allocation = PermVarAllocation::Pending;
|
||||||
|
self.add_perm_to_free_list(chunk_num, var_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mark_safe_var_unconditionally(&mut self, var_num: usize) {
|
||||||
|
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||||
|
|
||||||
|
match &mut self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Perm(
|
||||||
|
_,
|
||||||
|
PermVarAllocation::Done {
|
||||||
|
deep_safety,
|
||||||
|
shallow_safety,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
*deep_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||||
|
*shallow_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||||
|
}
|
||||||
|
VarAlloc::Temp { safety, .. } => {
|
||||||
|
*safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) {
|
||||||
|
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||||
|
|
||||||
|
match &mut self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Perm(
|
||||||
|
_,
|
||||||
|
PermVarAllocation::Done {
|
||||||
|
deep_safety,
|
||||||
|
shallow_safety,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
// GetVariable in head chunk is considered safe.
|
||||||
|
if lvl == Level::Deep {
|
||||||
|
*deep_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||||
|
*shallow_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||||
|
} else if term_loc == GenContext::Head {
|
||||||
|
*shallow_safety = VarSafetyStatus::GloballyUnneeded;
|
||||||
|
} else if let Some(&temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c) {
|
||||||
|
match &mut self.var_data.records[temp_var_num].allocation {
|
||||||
|
VarAlloc::Temp {
|
||||||
|
to_perm_var_num, ..
|
||||||
|
} => {
|
||||||
|
*to_perm_var_num = Some(var_num);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VarAlloc::Temp { safety, .. } => {
|
||||||
|
*safety = VarSafetyStatus::GloballyUnneeded;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn argument_to_value<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
var_num: usize,
|
||||||
|
r: RegType,
|
||||||
|
arg_c: usize,
|
||||||
|
) -> Instruction {
|
||||||
|
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||||
|
|
||||||
|
match &mut self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Perm(_, PermVarAllocation::Done { shallow_safety, .. }) => {
|
||||||
|
if !self.in_tail_position
|
||||||
|
|| self
|
||||||
|
.branch_stack
|
||||||
|
.safety_unneeded_in_branch(shallow_safety, &branch_designator)
|
||||||
|
{
|
||||||
|
Target::argument_to_value(r, arg_c)
|
||||||
|
} else {
|
||||||
|
*shallow_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||||
|
Target::unsafe_argument_to_value(r, arg_c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VarAlloc::Temp { .. } => {
|
||||||
|
debug_assert!(matches!(r, RegType::Temp(_)));
|
||||||
|
Target::argument_to_value(r, arg_c)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn subterm_to_value<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
var_num: usize,
|
||||||
|
r: RegType,
|
||||||
|
) -> Instruction {
|
||||||
|
let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
|
||||||
|
|
||||||
|
match &mut self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, .. }) => {
|
||||||
|
if self
|
||||||
|
.branch_stack
|
||||||
|
.safety_unneeded_in_branch(deep_safety, &branch_designator)
|
||||||
|
{
|
||||||
|
Target::subterm_to_value(r)
|
||||||
|
} else {
|
||||||
|
*deep_safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||||
|
Target::unsafe_subterm_to_value(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VarAlloc::Temp { safety, .. } => {
|
||||||
|
if self
|
||||||
|
.branch_stack
|
||||||
|
.safety_unneeded_in_branch(safety, &branch_designator)
|
||||||
|
{
|
||||||
|
Target::subterm_to_value(r)
|
||||||
|
} else {
|
||||||
|
*safety = VarSafetyStatus::unneeded(&branch_designator);
|
||||||
|
Target::unsafe_subterm_to_value(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Allocator for DebrayAllocator {
|
||||||
|
fn new() -> DebrayAllocator {
|
||||||
|
Self {
|
||||||
|
var_data: VarData::default(),
|
||||||
|
in_tail_position: false,
|
||||||
|
arity: 0,
|
||||||
|
arg_c: 1,
|
||||||
|
temp_lb: 1,
|
||||||
|
perm_lb: 1,
|
||||||
|
shallow_temp_mappings: IndexMap::with_hasher(FxBuildHasher::default()),
|
||||||
|
in_use: BitSet::default(),
|
||||||
|
temp_free_list: vec![],
|
||||||
|
perm_free_list: VecDeque::new(),
|
||||||
|
branch_stack: BranchStack { stack: vec![] },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
lvl: Level,
|
||||||
|
term_loc: GenContext,
|
||||||
|
code: &mut CodeDeque,
|
||||||
|
) -> RegType {
|
||||||
|
let r = RegType::Temp(self.alloc_reg_to_non_var());
|
||||||
|
|
||||||
|
match lvl {
|
||||||
|
Level::Deep => code.push_back(Target::subterm_to_variable(r)),
|
||||||
|
Level::Root | Level::Shallow => {
|
||||||
|
let k = self.arg_c;
|
||||||
|
|
||||||
|
if let GenContext::Last(chunk_num) = term_loc {
|
||||||
|
self.evacuate_arg::<Target>(chunk_num, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.arg_c += 1;
|
||||||
|
|
||||||
|
code.push_back(Target::argument_to_variable(r, k));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
lvl: Level,
|
||||||
|
term_loc: GenContext,
|
||||||
|
cell: &'a Cell<RegType>,
|
||||||
|
code: &mut CodeDeque,
|
||||||
|
) {
|
||||||
|
let r = cell.get();
|
||||||
|
|
||||||
|
let r = match lvl {
|
||||||
|
Level::Shallow => {
|
||||||
|
let k = self.arg_c;
|
||||||
|
|
||||||
|
if let GenContext::Last(chunk_num) = term_loc {
|
||||||
|
self.evacuate_arg::<Target>(chunk_num, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.arg_c += 1;
|
||||||
|
RegType::Temp(k)
|
||||||
|
}
|
||||||
|
_ if r.reg_num() == 0 => RegType::Temp(self.alloc_reg_to_non_var()),
|
||||||
|
_ => {
|
||||||
|
self.in_use.insert(r.reg_num());
|
||||||
|
r
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cell.set(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_var<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
var_num: usize,
|
||||||
|
lvl: Level,
|
||||||
|
cell: &Cell<VarReg>,
|
||||||
|
term_loc: GenContext,
|
||||||
|
code: &mut CodeDeque,
|
||||||
|
) {
|
||||||
|
let (r, is_new_var) = match self.get_binding(var_num) {
|
||||||
|
RegType::Temp(0) => {
|
||||||
|
let o = self.alloc_reg_to_var::<Target>(var_num, lvl, term_loc, code);
|
||||||
|
cell.set(VarReg::Norm(RegType::Temp(o)));
|
||||||
|
(RegType::Temp(o), true)
|
||||||
|
}
|
||||||
|
RegType::Perm(0) => {
|
||||||
|
let p = self.alloc_perm_var(var_num, term_loc.chunk_num());
|
||||||
|
cell.set(VarReg::Norm(RegType::Perm(p)));
|
||||||
|
(RegType::Perm(p), true)
|
||||||
|
}
|
||||||
|
r @ RegType::Perm(_) => {
|
||||||
|
let is_new_var = match &mut self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Perm(_, allocation) => {
|
||||||
|
if allocation.pending() {
|
||||||
|
*allocation = PermVarAllocation::done();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
(r, is_new_var)
|
||||||
|
}
|
||||||
|
r => (r, false),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.mark_reserved_var::<Target>(var_num, lvl, cell, term_loc, code, r, is_new_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
|
||||||
|
&mut self,
|
||||||
|
var_num: usize,
|
||||||
|
lvl: Level,
|
||||||
|
cell: &Cell<VarReg>,
|
||||||
|
term_loc: GenContext,
|
||||||
|
code: &mut CodeDeque,
|
||||||
|
r: RegType,
|
||||||
|
is_new_var: bool,
|
||||||
|
) {
|
||||||
|
match lvl {
|
||||||
|
Level::Root | Level::Shallow => {
|
||||||
|
let k = self.arg_c;
|
||||||
|
|
||||||
|
if self.is_curr_arg_distinct_from(var_num) {
|
||||||
|
self.evacuate_arg::<Target>(term_loc.chunk_num(), code);
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.set(VarReg::ArgAndNorm(r, k));
|
||||||
|
|
||||||
|
if !self.in_place(var_num, term_loc, r, k) {
|
||||||
|
if is_new_var {
|
||||||
|
self.mark_safe_var(var_num, lvl, term_loc);
|
||||||
|
code.push_back(Target::argument_to_variable(r, k));
|
||||||
|
} else {
|
||||||
|
code.push_back(self.argument_to_value::<Target>(var_num, r, k));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.arg_c += 1;
|
||||||
|
}
|
||||||
|
Level::Deep if is_new_var => {
|
||||||
|
if let GenContext::Head = term_loc {
|
||||||
|
if self.occurs_shallowly_in_head(var_num, r.reg_num()) {
|
||||||
|
code.push_back(self.subterm_to_value::<Target>(var_num, r));
|
||||||
|
} else {
|
||||||
|
self.mark_safe_var(var_num, lvl, term_loc);
|
||||||
|
code.push_back(Target::subterm_to_variable(r));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.mark_safe_var(var_num, lvl, term_loc);
|
||||||
|
code.push_back(Target::subterm_to_variable(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Level::Deep => code.push_back(self.subterm_to_value::<Target>(var_num, r)),
|
||||||
|
}
|
||||||
|
|
||||||
|
let o = r.reg_num();
|
||||||
|
|
||||||
|
if !r.is_perm() {
|
||||||
|
self.shallow_temp_mappings.insert(o, var_num);
|
||||||
|
} else if r.is_perm() && is_new_var {
|
||||||
|
self.branch_stack.add_branch_occurrence(var_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
let record = &mut self.var_data.records[var_num];
|
||||||
|
|
||||||
|
record.allocation.set_register(o);
|
||||||
|
|
||||||
|
if record.running_count < record.num_occurrences {
|
||||||
|
record.running_count += 1;
|
||||||
|
} else {
|
||||||
|
self.free_var(term_loc.chunk_num(), var_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.in_use.insert(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType {
|
||||||
|
match self.get_binding(var_num) {
|
||||||
|
RegType::Perm(0) => RegType::Perm(self.alloc_perm_var(var_num, chunk_num)),
|
||||||
|
RegType::Temp(0) => {
|
||||||
|
let t = self.alloc_reg_to_non_var();
|
||||||
|
|
||||||
|
match &mut self.var_data.records[var_num].allocation {
|
||||||
|
VarAlloc::Temp {
|
||||||
|
temp_reg, safety, ..
|
||||||
|
} => {
|
||||||
|
*temp_reg = t;
|
||||||
|
*safety = VarSafetyStatus::GloballyUnneeded;
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
RegType::Temp(t)
|
||||||
|
}
|
||||||
|
r => r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.perm_lb = 1;
|
||||||
|
self.shallow_temp_mappings.clear();
|
||||||
|
self.in_use.clear();
|
||||||
|
self.temp_free_list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_contents(&mut self) {
|
||||||
|
self.in_use.clear();
|
||||||
|
self.shallow_temp_mappings.clear();
|
||||||
|
self.temp_free_list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance_arg(&mut self) {
|
||||||
|
self.arg_c += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_at_head(&mut self, args: &[Term]) {
|
||||||
|
self.reset_arg(args.len());
|
||||||
|
self.arity = args.len();
|
||||||
|
|
||||||
|
for (idx, arg) in args.iter().enumerate() {
|
||||||
|
if let Term::Var(_, var) = arg {
|
||||||
|
let var_num = var.to_var_num().unwrap();
|
||||||
|
let r = self.get_binding(var_num);
|
||||||
|
|
||||||
|
if !r.is_perm() && r.reg_num() == 0 {
|
||||||
|
self.in_use.insert(idx + 1);
|
||||||
|
self.shallow_temp_mappings.insert(idx + 1, var_num);
|
||||||
|
self.var_data.records[var_num]
|
||||||
|
.allocation
|
||||||
|
.set_register(idx + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_arg(&mut self, arity: usize) {
|
||||||
|
self.arity = 0;
|
||||||
|
self.arg_c = 1;
|
||||||
|
self.temp_lb = arity + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn max_reg_allocated(&self) -> usize {
|
||||||
|
std::cmp::max(self.temp_lb, self.arg_c)
|
||||||
|
}
|
||||||
|
}
|
||||||
219
src/examples/bimetatrans/README.md
Normal file
219
src/examples/bimetatrans/README.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
### BiMetaTrans 1.0
|
||||||
|
|
||||||
|
BiMetaTrans(Prolog, RuleML) is a bidirectional translator capable of
|
||||||
|
parsing and generating a well-formed sublanguage of RuleML/XML to/from
|
||||||
|
a metalogical encoding of our own design called Prolog/'$V'. For later
|
||||||
|
ease of reference:
|
||||||
|
|
||||||
|
* Prolog/'$V' stands for "Metalogic Prolog with variable-as-'$V'-term encoding"
|
||||||
|
* RuleML/XML stands for "RuleML in XML with any kind of rendering"
|
||||||
|
* RuleML/xmL stands for "RuleML in minified XML"
|
||||||
|
* RuleML/Xml stands for "RuleML in indented XML"
|
||||||
|
|
||||||
|
"Minified XML" refers to XML with only necessary whitespace, between
|
||||||
|
element names and attributes, and pairs of attributes, with all
|
||||||
|
indentation stripped out. "Indented XML" contains indentation and
|
||||||
|
newlines, and is usually formatted for human readers.
|
||||||
|
|
||||||
|
The specification of Prolog/'$V', and of the NafHornlogEq sublanguage
|
||||||
|
of RuleML/XML targeted by BiMetaTrans, is contained in the preprint
|
||||||
|
[Invertible Bidirectional Metalogical Translation Between Prolog and
|
||||||
|
RuleML/XML for Knowledge Representation and
|
||||||
|
Querying](http://ruleml.org/papers/RuleMLXMLBiDirTransScryer.pdf) and
|
||||||
|
its accompanying talk
|
||||||
|
[slides](http://ruleml.org/talks/RuleMLXMLBiDirTransScryer-talk.pdf). The
|
||||||
|
preprint and talk also describe the implementation of BiMetaTrans
|
||||||
|
found here, and outlines a strategy for proving the invertibility of
|
||||||
|
BiMetaTrans.
|
||||||
|
|
||||||
|
BiMetaTrans exports a single public predicate subsuming its user
|
||||||
|
API, `parse_ruleml/3`. It has two modes, each corresponding to a single
|
||||||
|
direction of translation:
|
||||||
|
|
||||||
|
```
|
||||||
|
parse_ruleml(+AssertItems, +QueryItems, ?XML) (Prolog->RuleML)
|
||||||
|
parse_ruleml(?AssertItems, ?QueryItems, +XML) (RuleML->Prolog)
|
||||||
|
```
|
||||||
|
|
||||||
|
The modes constrain the inputs to fit one of two patterns, the
|
||||||
|
first where AssertItems and QueryItems are instantiated and XML is
|
||||||
|
possibly a variable, and conversely for the second. Instantiated
|
||||||
|
inputs are expected to be ground, meaning they should not contain
|
||||||
|
free variables.
|
||||||
|
|
||||||
|
We explore several examples of its use in Scryer Prolog.
|
||||||
|
|
||||||
|
Loading BiMetaTrans from the Scryer REPL:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module('src/examples/bimetatrans/bimetatrans').
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `write/1` to print the string to standard output, capturing the
|
||||||
|
`Prolog->RuleML` direction (`write/1` is used because it does not
|
||||||
|
print strings with escape characters, ie., `\"` for double quote):
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml([people('Alex',male),people('Alex',female),people('Siri',female)], [], XML),
|
||||||
|
write(XML).
|
||||||
|
"<Assert mapClosure="universal"><Atom><Rel>people</Rel><Ind>Alex</Ind><Data iso:type="symbol">male</Data></Atom><Atom><Rel>people</Rel><Ind>Alex</Ind><Data iso:type="symbol">female</Data></Atom><Atom><Rel>people</Rel><Ind>Siri</Ind><Data iso:type="symbol">female</Data></Atom></Assert>" XML = "<Assert mapClosure= ...".
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the XML input can contain extraneous whitespace and
|
||||||
|
indentation while generated XML never does (ie., the XML generated by
|
||||||
|
BiMetaTrans is always Ruleml/xmL). `\` is used to continue ISO Prolog
|
||||||
|
strings to the next line but is never stored to the string by the
|
||||||
|
Prolog reader.
|
||||||
|
|
||||||
|
Performing the inverse translation of the previous example
|
||||||
|
(`RuleML->Prolog`), in RuleML/Xml:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml(AssertItems, QueryItems,
|
||||||
|
"<Assert mapClosure=\"universal\">\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>people</Rel>\
|
||||||
|
<Ind>Alex</Ind>\
|
||||||
|
<Data iso:type=\"symbol\">male</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>people</Rel>\
|
||||||
|
<Ind>Alex</Ind>\
|
||||||
|
<Data iso:type=\"symbol\">female</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>people</Rel>\
|
||||||
|
<Ind>Siri</Ind>\
|
||||||
|
<Data iso:type=\"symbol\">female</Data>\
|
||||||
|
</Atom>\
|
||||||
|
</Assert>").
|
||||||
|
AssertItems = [people('Alex',male),people('Alex',female),people('Siri',female)], QueryItems = [].
|
||||||
|
```
|
||||||
|
|
||||||
|
Double quote characters within strings must be escaped as in
|
||||||
|
`\"`. Non-empty AssertItems and QueryItems lists generated to
|
||||||
|
RuleML/xmL simultaneously:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml([a(item), b(item), c(item)], [(?- p, q, r(1), s(-2.222342432), t("attached")), (?- u, v('$V'(q)))], XML),
|
||||||
|
write(XML).
|
||||||
|
"<Assert mapClosure="universal"><Atom><Rel>a</Rel><Data iso:type="symbol">item</Data></Atom><Atom><Rel>b</Rel><Data iso:type="symbol">item</Data></Atom><Atom><Rel>c</Rel><Data iso:type="symbol">item</Data></Atom></Assert><Query closure="existential"><And><Atom><Rel>p</Rel></Atom><Atom><Rel>q</Rel></Atom><Atom><Rel>r</Rel><Data iso:type="number">1</Data></Atom><Atom><Rel>s</Rel><Data iso:type="number">-2.222342432</Data></Atom><Atom><Rel>t</Rel><Data iso:type="string">"attached"</Data></Atom></And></Query><Query closure="existential"><And><Atom><Rel>u</Rel></Atom><Atom><Rel>v</Rel><Var>q</Var></Atom></And></Query>" XML = "<Assert mapClosure= ...".
|
||||||
|
```
|
||||||
|
|
||||||
|
To insert escape characters into the printed RuleML/xmL, we use
|
||||||
|
`writeq/1` in place of `write/1`:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml([a(item), b(item), c(item)], [(?- p, q, r(1), s(-2.222342432), t("attached")), (?- u, v('$V'(q)))], XML),
|
||||||
|
writeq(XML).
|
||||||
|
"<Assert mapClosure=\"universal\"><Atom><Rel>a</Rel><Data iso:type=\"symbol\">item</Data></Atom><Atom><Rel>b</Rel><Data iso:type=\"symbol\">item</Data></Atom><Atom><Rel>c</Rel><Data iso:type=\"symbol\">item</Data></Atom></Assert><Query closure=\"existential\"><And><Atom><Rel>p</Rel></Atom><Atom><Rel>q</Rel></Atom><Atom><Rel>r</Rel><Data iso:type=\"number\">1</Data></Atom><Atom><Rel>s</Rel><Data iso:type=\"number\">-2.222342432</Data></Atom><Atom><Rel>t</Rel><Data iso:type=\"string\">\"attached\"</Data></Atom></And></Query><Query closure=\"existential\"><And><Atom><Rel>u</Rel></Atom><Atom><Rel>v</Rel><Var>q</Var></Atom></And></Query>" XML = "<Assert mapClosure= ...".
|
||||||
|
```
|
||||||
|
|
||||||
|
XML can be pretty printed using a [free online XML pretty
|
||||||
|
printer](https://codebeautify.org/xmlviewer), as here:
|
||||||
|
|
||||||
|
```
|
||||||
|
<Assert mapClosure=\"universal\">
|
||||||
|
<Atom>
|
||||||
|
<Rel>a</Rel>
|
||||||
|
<Data iso:type=\"symbol\">item</Data>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>b</Rel>
|
||||||
|
<Data iso:type=\"symbol\">item</Data>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>c</Rel>
|
||||||
|
<Data iso:type=\"symbol\">item</Data>
|
||||||
|
</Atom>
|
||||||
|
</Assert>
|
||||||
|
<Query closure=\"existential\">
|
||||||
|
<And>
|
||||||
|
<Atom>
|
||||||
|
<Rel>p</Rel>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>q</Rel>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>r</Rel>
|
||||||
|
<Data iso:type=\"number\">1</Data>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>s</Rel>
|
||||||
|
<Data iso:type=\"number\">-2.222342432</Data>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>t</Rel>
|
||||||
|
<Data iso:type=\"string\">\"attached\"</Data>
|
||||||
|
</Atom>
|
||||||
|
</And>
|
||||||
|
</Query>
|
||||||
|
<Query closure=\"existential\">
|
||||||
|
<And>
|
||||||
|
<Atom>
|
||||||
|
<Rel>u</Rel>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>v</Rel>
|
||||||
|
<Var>q</Var>
|
||||||
|
</Atom>
|
||||||
|
</And>
|
||||||
|
</Query>
|
||||||
|
```
|
||||||
|
|
||||||
|
The AssertItems and QueryItems lists can then be recovered using this
|
||||||
|
RuleML/Xml string, with the `\` character added to the end of each
|
||||||
|
line:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml(AssertItems, QueryItems,
|
||||||
|
"<Assert mapClosure=\"universal\">\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>a</Rel>\
|
||||||
|
<Data iso:type=\"symbol\">item</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>b</Rel>\
|
||||||
|
<Data iso:type=\"symbol\">item</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>c</Rel>\
|
||||||
|
<Data iso:type=\"symbol\">item</Data>\
|
||||||
|
</Atom>\
|
||||||
|
</Assert>\
|
||||||
|
<Query closure=\"existential\">\
|
||||||
|
<And>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>p</Rel>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>q</Rel>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>r</Rel>\
|
||||||
|
<Data iso:type=\"number\">1</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>s</Rel>\
|
||||||
|
<Data iso:type=\"number\">-2.222342432</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>t</Rel>\
|
||||||
|
<Data iso:type=\"string\">\"attached\"</Data>\
|
||||||
|
</Atom>\
|
||||||
|
</And>\
|
||||||
|
</Query>\
|
||||||
|
<Query closure=\"existential\">\
|
||||||
|
<And>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>u</Rel>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>v</Rel>\
|
||||||
|
<Var>q</Var>\
|
||||||
|
</Atom>\
|
||||||
|
</And>\
|
||||||
|
</Query>").
|
||||||
|
AssertItems = [a(item),b(item),c(item)], QueryItems = [(?-p,q,r(1),s(-2.222342432),t("attached")),(?-u,v('$V'(q)))].
|
||||||
|
```
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
:- module(ruleml_xml_parser, [parse_ruleml/3]).
|
:- module(bimetatrans, [parse_ruleml/3]).
|
||||||
|
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(iso_ext)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
:- set_prolog_flag(double_quotes, chars).
|
:- set_prolog_flag(double_quotes, chars).
|
||||||
|
|
||||||
@@ -17,28 +19,34 @@
|
|||||||
* all indentation stripped out. "Indented XML" contains indentation
|
* all indentation stripped out. "Indented XML" contains indentation
|
||||||
* and newlines, and is usually formatted for human readers.
|
* and newlines, and is usually formatted for human readers.
|
||||||
*
|
*
|
||||||
* parse_ruleml/3 is the sole public predicate of BiMetaTrans(RuleML,
|
* parse_ruleml/3 is the sole public predicate of BiMetaTrans(Prolog,
|
||||||
* Prolog), a bidirectional translator capable of parsing well-formed
|
* RuleML), a bidirectional translator capable of parsing well-formed
|
||||||
* RuleML/XML (valid w.r.t. a proposed anchor schema nafhornlogeq
|
* RuleML/XML (valid w.r.t. a proposed anchor schema nafhornlogeq
|
||||||
* defining a sublanguage of the existing anchor schema naffologeq) to
|
* defining a sublanguage of the existing anchor schema naffologeq) to
|
||||||
* an equivalent Prolog/'$V' term and back to RuleML/xmL. It has the
|
* an equivalent Prolog/'$V' term and back to RuleML/xmL. It has the
|
||||||
* modes
|
* modes
|
||||||
*
|
*
|
||||||
* +AssertItem, +QueryItems, ?XML (Prolog->RuleML)
|
* +AssertItems, +QueryItems, ?XML (Prolog->RuleML)
|
||||||
* ?AssertItem, ?QueryItems, +XML (RuleML->Prolog)
|
* ?AssertItems, ?QueryItems, +XML (RuleML->Prolog)
|
||||||
*
|
*
|
||||||
* The modes constrain the inputs to fit one of two patterns, the
|
* The modes constrain the inputs to fit one of two patterns, the
|
||||||
* first where AssertItem and QueryItem are instantiated and XML is
|
* first where AssertItems and QueryItems are instantiated and XML is
|
||||||
* possibly a variable, and conversely for the second. Instantiated
|
* possibly a variable, and conversely for the second. Instantiated
|
||||||
* inputs are expected to be ground, meaning they should not contain
|
* inputs are expected to be ground, meaning they should not contain
|
||||||
* free variables.
|
* free variables.
|
||||||
*
|
*
|
||||||
|
* A RuleML/XML document is assumed to contain an optional Assert
|
||||||
|
* element (with zero or more children) and zero or more Query
|
||||||
|
* elements. The children of the Assert element are the Items of
|
||||||
|
* AssertItems, while the Items of QueryItems each correspond to a
|
||||||
|
* single Query element.
|
||||||
|
*
|
||||||
* parse_ruleml/3 is meant to subsume an invertible function in the
|
* parse_ruleml/3 is meant to subsume an invertible function in the
|
||||||
* sense that the top-level query
|
* sense that the top-level query
|
||||||
*
|
*
|
||||||
* ?- parse_ruleml(AssertItem, QueryItems, XML0), % Prolog->RuleML
|
* ?- parse_ruleml(AssertItems, QueryItems, XML0), % Prolog->RuleML
|
||||||
* parse_ruleml(AssertItem0, QueryItems0, XML), % RuleML->Prolog
|
* parse_ruleml(AssertItems0, QueryItems0, XML), % RuleML->Prolog
|
||||||
* AssertItem0 == AssertItem, % Test for expected round-trip results.
|
* AssertItems0 == AssertItems, % Test for expected round-trip results.
|
||||||
* QueryItems0 == QueryItems,
|
* QueryItems0 == QueryItems,
|
||||||
* XML0 == XML.
|
* XML0 == XML.
|
||||||
*
|
*
|
||||||
@@ -46,10 +54,11 @@
|
|||||||
* not 0-appended are instantiated and therefore ground.
|
* not 0-appended are instantiated and therefore ground.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
parse_ruleml(AssertItem, QueryItem, XML) :-
|
parse_ruleml(AssertItems, QueryItems, XML) :-
|
||||||
( ( var(AssertItem) ; var(QueryItem) ), var(XML) ->
|
( ( var(AssertItems) ; var(QueryItems) ), var(XML) ->
|
||||||
throw(error(instantiation_error, parse_ruleml/2))
|
throw(error(instantiation_error, parse_ruleml/2))
|
||||||
; phrase(ruleml_top_level_items(AssertItem, QueryItem), XML)
|
; phrase(ruleml_top_level_items(AssertItems, QueryItems), XML),
|
||||||
|
!
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
@@ -80,24 +89,15 @@ parse_header -->
|
|||||||
*
|
*
|
||||||
* The two arguments are (un)instantiated depending on the direction
|
* The two arguments are (un)instantiated depending on the direction
|
||||||
* in which parse_ruleml/2 operates. In either direction, once
|
* in which parse_ruleml/2 operates. In either direction, once
|
||||||
* ruleml_query_item(QueryItems) or ruleml_assert(AssertItem)
|
* ruleml_query_item(QueryItems) or ruleml_assert(AssertItems)
|
||||||
* succeeds:
|
* succeeds, AssertItems and QueryItems are ground lists of terms,
|
||||||
*
|
* corresponding to Assert/Query performative(s) of the RuleML/XML KB.
|
||||||
* 1) AssertItem/QueryItems is a fully ground list of terms,
|
|
||||||
* corresponding to an Assert/Query performative of the RuleML/XML KB.
|
|
||||||
*
|
|
||||||
* 2) It would not be meaningful to backtrack after the last success,
|
|
||||||
* thus undoing its effects; that is because we have already refuted
|
|
||||||
* the possibility of there being further items for the grammar to
|
|
||||||
* consume. Hence we see that the cut (!) does not restrict the
|
|
||||||
* generality of the grammar.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ruleml_top_level_items(AssertItem, QueryItems) -->
|
ruleml_top_level_items(AssertItems, QueryItems) -->
|
||||||
( ruleml_assert(AssertItem),
|
( ruleml_assert(AssertItems),
|
||||||
ruleml_query_items(QueryItems),
|
ruleml_query_items(QueryItems)
|
||||||
!
|
; { AssertItems = [] },
|
||||||
; { AssertItem = [] },
|
|
||||||
ruleml_query_items(QueryItems)
|
ruleml_query_items(QueryItems)
|
||||||
).
|
).
|
||||||
|
|
||||||
@@ -167,22 +167,16 @@ ruleml_top_level_items(AssertItem, QueryItems) -->
|
|||||||
* outset of ruleml_assert//1 will cause it to fail, meaning that
|
* outset of ruleml_assert//1 will cause it to fail, meaning that
|
||||||
* an empty <Assert></Assert> element is never emitted to the output
|
* an empty <Assert></Assert> element is never emitted to the output
|
||||||
* RuleML/xmL.
|
* RuleML/xmL.
|
||||||
*
|
|
||||||
* The two cuts are green cuts, meaning they do not change the meaning
|
|
||||||
* or output of the program, they simply prevent needless
|
|
||||||
* backtracking.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ruleml_assert(Items) -->
|
ruleml_assert(Items) -->
|
||||||
( { var(Items) } ->
|
( { var(Items) } ->
|
||||||
list_ws("<Assert mapClosure=\"universal\">"),
|
list_ws("<Assert mapClosure=\"universal\">"),
|
||||||
ruleml_assert_items(Items),
|
ruleml_assert_items(Items),
|
||||||
!,
|
|
||||||
list_ws("</Assert>")
|
list_ws("</Assert>")
|
||||||
; "<Assert mapClosure=\"universal\">",
|
; "<Assert mapClosure=\"universal\">",
|
||||||
{ Items \== [] },
|
{ Items \== [] },
|
||||||
ruleml_assert_items(Items),
|
ruleml_assert_items(Items),
|
||||||
!,
|
|
||||||
"</Assert>"
|
"</Assert>"
|
||||||
).
|
).
|
||||||
|
|
||||||
@@ -204,7 +198,6 @@ ruleml_assert(Items) -->
|
|||||||
|
|
||||||
ruleml_assert_items([Item | Items]) -->
|
ruleml_assert_items([Item | Items]) -->
|
||||||
ruleml_assert_item(Item),
|
ruleml_assert_item(Item),
|
||||||
!,
|
|
||||||
ruleml_assert_items(Items).
|
ruleml_assert_items(Items).
|
||||||
ruleml_assert_items([]) --> [].
|
ruleml_assert_items([]) --> [].
|
||||||
|
|
||||||
@@ -212,7 +205,6 @@ ruleml_assert_items([]) --> [].
|
|||||||
ruleml_query_items(Items) -->
|
ruleml_query_items(Items) -->
|
||||||
( { var(Items) } ->
|
( { var(Items) } ->
|
||||||
ruleml_query_item(Item),
|
ruleml_query_item(Item),
|
||||||
!,
|
|
||||||
( { var(Item) } ->
|
( { var(Item) } ->
|
||||||
ruleml_query_items(Items)
|
ruleml_query_items(Items)
|
||||||
; { Items = [Item | Items0] },
|
; { Items = [Item | Items0] },
|
||||||
@@ -220,7 +212,6 @@ ruleml_query_items(Items) -->
|
|||||||
)
|
)
|
||||||
; { Items = [Item | Items0] },
|
; { Items = [Item | Items0] },
|
||||||
ruleml_query_item(Item),
|
ruleml_query_item(Item),
|
||||||
!,
|
|
||||||
ruleml_query_items(Items0)
|
ruleml_query_items(Items0)
|
||||||
).
|
).
|
||||||
ruleml_query_items([]) --> [].
|
ruleml_query_items([]) --> [].
|
||||||
@@ -277,7 +268,7 @@ space(' ') --> " ".
|
|||||||
|
|
||||||
decimal_point('.') --> ".".
|
decimal_point('.') --> ".".
|
||||||
|
|
||||||
sign('-') --> "-", !.
|
sign('-') --> "-".
|
||||||
sign('+') --> "+".
|
sign('+') --> "+".
|
||||||
|
|
||||||
double_quote('"') --> "\"".
|
double_quote('"') --> "\"".
|
||||||
@@ -330,7 +321,6 @@ ruleml_condition(Item) -->
|
|||||||
|
|
||||||
ruleml_item_conjunction([Item | Items]) -->
|
ruleml_item_conjunction([Item | Items]) -->
|
||||||
ruleml_condition(Item),
|
ruleml_condition(Item),
|
||||||
!,
|
|
||||||
ruleml_item_conjunction(Items).
|
ruleml_item_conjunction(Items).
|
||||||
ruleml_item_conjunction([]) --> [].
|
ruleml_item_conjunction([]) --> [].
|
||||||
|
|
||||||
@@ -402,7 +392,6 @@ ruleml_conjunction_of_items(Items) -->
|
|||||||
|
|
||||||
ruleml_item_disjunction([Item | Items]) -->
|
ruleml_item_disjunction([Item | Items]) -->
|
||||||
ruleml_condition(Item),
|
ruleml_condition(Item),
|
||||||
!,
|
|
||||||
ruleml_item_disjunction(Items).
|
ruleml_item_disjunction(Items).
|
||||||
ruleml_item_disjunction([]) --> [].
|
ruleml_item_disjunction([]) --> [].
|
||||||
|
|
||||||
@@ -498,7 +487,7 @@ ruleml_plex(Plex) -->
|
|||||||
list_ws("</Plex>")
|
list_ws("</Plex>")
|
||||||
; list_ws("<Plex/>")
|
; list_ws("<Plex/>")
|
||||||
)
|
)
|
||||||
; { \+ string(Plex),
|
; { ( \+ partial_string(Plex) ; Plex == [] ),
|
||||||
acyclic_term(Plex) },
|
acyclic_term(Plex) },
|
||||||
( { functor(Plex, ('.'), 2) } ->
|
( { functor(Plex, ('.'), 2) } ->
|
||||||
{ split_plex(Plex, PlexItems, RepoVar) },
|
{ split_plex(Plex, PlexItems, RepoVar) },
|
||||||
@@ -547,7 +536,6 @@ ruleml_naf(Item) -->
|
|||||||
|
|
||||||
ruleml_atoms([Item|Items]) -->
|
ruleml_atoms([Item|Items]) -->
|
||||||
ruleml_atom(Item),
|
ruleml_atom(Item),
|
||||||
!,
|
|
||||||
ruleml_atoms(Items).
|
ruleml_atoms(Items).
|
||||||
ruleml_atoms([]) --> [].
|
ruleml_atoms([]) --> [].
|
||||||
|
|
||||||
@@ -582,13 +570,11 @@ ruleml_atom(Item) -->
|
|||||||
list_ws("<Atom>"),
|
list_ws("<Atom>"),
|
||||||
list_ws("<Rel>"),
|
list_ws("<Rel>"),
|
||||||
prolog_symbol(Name),
|
prolog_symbol(Name),
|
||||||
{ Name \== (','), Name \== (';') },
|
|
||||||
list_ws("</Rel>"),
|
list_ws("</Rel>"),
|
||||||
ruleml_items(Args),
|
ruleml_items(Args),
|
||||||
list_ws("</Atom>"),
|
list_ws("</Atom>"),
|
||||||
{ Item =.. [Name | Args] }
|
{ Item =.. [Name | Args] }
|
||||||
; { Item =.. [Name | Args] },
|
; { Item =.. [Name | Args] },
|
||||||
{ Name \== (','), Name \== (';') },
|
|
||||||
"<Atom>",
|
"<Atom>",
|
||||||
"<Rel>",
|
"<Rel>",
|
||||||
prolog_symbol(Name),
|
prolog_symbol(Name),
|
||||||
@@ -652,7 +638,6 @@ ruleml_expr(Item) -->
|
|||||||
( list_ws("<repo>"),
|
( list_ws("<repo>"),
|
||||||
ruleml_item(RepoItem),
|
ruleml_item(RepoItem),
|
||||||
list_ws("</repo>"),
|
list_ws("</repo>"),
|
||||||
!,
|
|
||||||
{ fold_commas(Args, ArgsCommas) },
|
{ fold_commas(Args, ArgsCommas) },
|
||||||
{ Item =.. [Name, (ArgsCommas | RepoItem)] }
|
{ Item =.. [Name, (ArgsCommas | RepoItem)] }
|
||||||
; { Item =.. [Name | Args] }
|
; { Item =.. [Name | Args] }
|
||||||
@@ -789,7 +774,7 @@ constant_chars(symbol, Constant, Chars) :-
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* ruleml_data//1 delegates to ruleml_data_contents//2 to determine
|
* ruleml_data//1 delegates to ruleml_data_contents//2 to determine
|
||||||
* the contents of <Data> nodes, with adjoining xsi:type elements.
|
* the contents of <Data> nodes, with adjoining iso:type elements.
|
||||||
*
|
*
|
||||||
* constant_chars/3 performs type-driven conversion between
|
* constant_chars/3 performs type-driven conversion between
|
||||||
* Prolog/'$V' and RuleML/XML in both directions;
|
* Prolog/'$V' and RuleML/XML in both directions;
|
||||||
@@ -799,13 +784,13 @@ constant_chars(symbol, Constant, Chars) :-
|
|||||||
|
|
||||||
ruleml_data(Name) -->
|
ruleml_data(Name) -->
|
||||||
( { var(Name) } ->
|
( { var(Name) } ->
|
||||||
list_ws("<Data xsi:type=\""),
|
list_ws("<Data iso:type=\""),
|
||||||
prolog_symbol(Type),
|
prolog_symbol(Type),
|
||||||
list_ws("\">"),
|
list_ws("\">"),
|
||||||
ruleml_data_contents(Type, Cs),
|
ruleml_data_contents(Type, Cs),
|
||||||
{ constant_chars(Type, Name, Cs) },
|
{ constant_chars(Type, Name, Cs) },
|
||||||
list_ws("</Data>")
|
list_ws("</Data>")
|
||||||
; "<Data xsi:type=\"",
|
; "<Data iso:type=\"",
|
||||||
{ constant_chars(Type, Name, Cs) },
|
{ constant_chars(Type, Name, Cs) },
|
||||||
prolog_symbol(Type),
|
prolog_symbol(Type),
|
||||||
"\">",
|
"\">",
|
||||||
@@ -823,15 +808,12 @@ ruleml_data(Name) -->
|
|||||||
* converting from lists of characters (here denoted as Cs) to the
|
* converting from lists of characters (here denoted as Cs) to the
|
||||||
* named data type in ISO Prolog. The type information is given
|
* named data type in ISO Prolog. The type information is given
|
||||||
* in the first argument.
|
* in the first argument.
|
||||||
*
|
|
||||||
* Grammatically, ruleml_data_contents//3 is a disjoint union of its
|
|
||||||
* subgrammars, justifying the cut at the end of the first two rules.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ruleml_data_contents(number, Cs) -->
|
ruleml_data_contents(number, Cs) -->
|
||||||
ruleml_number(Cs), !.
|
ruleml_number(Cs).
|
||||||
ruleml_data_contents(symbol, Cs) -->
|
ruleml_data_contents(symbol, Cs) -->
|
||||||
ruleml_symbol(Cs), !.
|
ruleml_symbol(Cs).
|
||||||
ruleml_data_contents(string, Cs) -->
|
ruleml_data_contents(string, Cs) -->
|
||||||
ruleml_string(Cs).
|
ruleml_string(Cs).
|
||||||
|
|
||||||
@@ -1195,8 +1177,7 @@ fold_semicolons(List, Output) :-
|
|||||||
* complicated by Scryer's current lack of multi-argument indexing.
|
* complicated by Scryer's current lack of multi-argument indexing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fold_list([Item], _, Item) :-
|
fold_list([Item], _, Item).
|
||||||
!.
|
|
||||||
fold_list([Item|Items], F, Form) :-
|
fold_list([Item|Items], F, Form) :-
|
||||||
Form =.. [F, Item, Fs],
|
Form =.. [F, Item, Fs],
|
||||||
fold_list(Items, F, Fs).
|
fold_list(Items, F, Fs).
|
||||||
4061
src/examples/bimetatrans/bimetatrans_tests.pl
Normal file
4061
src/examples/bimetatrans/bimetatrans_tests.pl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,6 @@ verify_attributes(Var, Other, Goals) :-
|
|||||||
( Els = [] -> % exactly one element
|
( Els = [] -> % exactly one element
|
||||||
Goals = [Other=El] % implied binding
|
Goals = [Other=El] % implied binding
|
||||||
; Goals = [],
|
; Goals = [],
|
||||||
put_atts(Other, -dom(_)),
|
|
||||||
put_atts(Other, dom(Dc))% rescue intersection
|
put_atts(Other, dom(Dc))% rescue intersection
|
||||||
)
|
)
|
||||||
; Goals = [],
|
; Goals = [],
|
||||||
31
src/examples/echo_server.pl
Normal file
31
src/examples/echo_server.pl
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
:- module(echo_server, [echo_server/0,
|
||||||
|
echo_server/1]).
|
||||||
|
|
||||||
|
:- use_module(library(format)).
|
||||||
|
:- use_module(library(sockets)).
|
||||||
|
|
||||||
|
|
||||||
|
echo_server :-
|
||||||
|
echo_server('127.0.0.1').
|
||||||
|
|
||||||
|
|
||||||
|
echo_server(Addr) :-
|
||||||
|
socket_server_open(Addr:Port, ServerSocket),
|
||||||
|
format("echo_server: connection opened at ~w:~d~n", [Addr, Port]),
|
||||||
|
socket_server_accept(ServerSocket, Client, Stream, [eof_action(eof_code)]),
|
||||||
|
format("echo_server: connection accepted from ~a~n", [Client]),
|
||||||
|
!,
|
||||||
|
echo_loop(Stream),
|
||||||
|
socket_server_close(ServerSocket).
|
||||||
|
|
||||||
|
|
||||||
|
echo_loop(Stream) :-
|
||||||
|
read_term(Stream, Term, []),
|
||||||
|
( Term == end_of_file ->
|
||||||
|
true
|
||||||
|
;
|
||||||
|
format("received: ~w~n", [Term]),
|
||||||
|
!,
|
||||||
|
echo_loop(Stream)
|
||||||
|
).
|
||||||
|
|
||||||
@@ -16,15 +16,10 @@
|
|||||||
|
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
:- use_module(library(format)).
|
:- use_module(library(format)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
:- use_module(library(reif)).
|
:- use_module(library(reif)).
|
||||||
|
|
||||||
|
|
||||||
permutation([], []).
|
|
||||||
permutation([X|Xs], Ys) :-
|
|
||||||
permutation(Xs, Yss),
|
|
||||||
select(X, Ys, Yss).
|
|
||||||
|
|
||||||
|
|
||||||
valid_time([H1,H2,M1,M2], T) :-
|
valid_time([H1,H2,M1,M2], T) :-
|
||||||
memberd_t(H1, [0,1,2], TH1),
|
memberd_t(H1, [0,1,2], TH1),
|
||||||
memberd_t(H2, [0,1,2,3,4,5,6,7,8,9], TH2),
|
memberd_t(H2, [0,1,2,3,4,5,6,7,8,9], TH2),
|
||||||
@@ -49,15 +44,7 @@ permuted_times(Time, PermutedTimes) :-
|
|||||||
|
|
||||||
find_min_time(Time, Min) :-
|
find_min_time(Time, Min) :-
|
||||||
valid_time(Time, true),
|
valid_time(Time, true),
|
||||||
permuted_times(Time, PermutedTimes),
|
permuted_times(Time, [Min|_]).
|
||||||
find_min_time_(PermutedTimes, Time, Min).
|
|
||||||
|
|
||||||
find_min_time_([], Min, Min).
|
|
||||||
find_min_time_([Time|Times], MinSoFar, Min) :-
|
|
||||||
( Time @< MinSoFar ->
|
|
||||||
find_min_time_(Times, Time, Min)
|
|
||||||
; find_min_time_(Times, MinSoFar, Min)
|
|
||||||
).
|
|
||||||
|
|
||||||
|
|
||||||
write_time_nl(Time) :-
|
write_time_nl(Time) :-
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
:- use_module(library(dif)).
|
:- use_module(library(dif)).
|
||||||
|
:- use_module(library(format)).
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
pl_resolution(Clauses0, Chain) :-
|
pl_resolution(Clauses0, Chain) :-
|
||||||
23
src/examples/utf8.pl
Normal file
23
src/examples/utf8.pl
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
:- use_module(library(charsio)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(pio)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
|
||||||
|
:- initialization(unit_test).
|
||||||
|
|
||||||
|
unit_test :-
|
||||||
|
chars_utf8bytes("a£\x2124\", Bs),
|
||||||
|
Bs = [97, 194, 163, 226, 132, 164],
|
||||||
|
chars_utf8bytes(Cs, Bs),
|
||||||
|
Cs = "a£\x2124\".
|
||||||
|
|
||||||
|
write_f :-
|
||||||
|
chars_utf8bytes("£\x2124\\x2764\\x1F496\\n", Bs),
|
||||||
|
maplist(char_code, Cs, Bs),
|
||||||
|
phrase_to_file(Cs, "x.txt", [type(binary)]).
|
||||||
|
|
||||||
|
read_f :-
|
||||||
|
phrase_from_file(seq(Cs), "x.txt", [type(binary)]),
|
||||||
|
maplist(char_code, Cs, Bs),
|
||||||
|
chars_utf8bytes(Chars, Bs),
|
||||||
|
write(Chars).
|
||||||
1004
src/ffi.rs
Normal file
1004
src/ffi.rs
Normal file
File diff suppressed because it is too large
Load Diff
1087
src/forms.rs
Normal file
1087
src/forms.rs
Normal file
File diff suppressed because it is too large
Load Diff
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 FunctorElement::*;
|
||||||
|
use indexmap::indexmap;
|
||||||
|
use std::string::String;
|
||||||
|
|
||||||
|
#[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().unwrap();
|
||||||
|
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!());
|
||||||
|
}
|
||||||
|
}
|
||||||
3457
src/heap_iter.rs
Normal file
3457
src/heap_iter.rs
Normal file
File diff suppressed because it is too large
Load Diff
2232
src/heap_print.rs
Normal file
2232
src/heap_print.rs
Normal file
File diff suppressed because it is too large
Load Diff
25
src/http.rs
Normal file
25
src/http.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use bytes::{Bytes, buf::Reader};
|
||||||
|
use std::sync::{Arc, Condvar, Mutex};
|
||||||
|
use tokio::sync::Notify;
|
||||||
|
|
||||||
|
use warp::http;
|
||||||
|
|
||||||
|
pub struct HttpListener {
|
||||||
|
pub incoming: std::sync::mpsc::Receiver<HttpRequest>,
|
||||||
|
pub warp_shutdown: Arc<Notify>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HttpRequest {
|
||||||
|
pub request_data: HttpRequestData,
|
||||||
|
pub response: HttpResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type HttpResponse = Arc<(Mutex<bool>, Mutex<Option<warp::reply::Response>>, Condvar)>;
|
||||||
|
|
||||||
|
pub struct HttpRequestData {
|
||||||
|
pub method: http::Method,
|
||||||
|
pub headers: http::HeaderMap,
|
||||||
|
pub path: String,
|
||||||
|
pub query: String,
|
||||||
|
pub body: Reader<Bytes>,
|
||||||
|
}
|
||||||
1529
src/indexing.rs
Normal file
1529
src/indexing.rs
Normal file
File diff suppressed because it is too large
Load Diff
1386
src/instructions.rs
Normal file
1386
src/instructions.rs
Normal file
File diff suppressed because it is too large
Load Diff
469
src/iterators.rs
Normal file
469
src/iterators.rs
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
use crate::atom_table::*;
|
||||||
|
use crate::forms::*;
|
||||||
|
use crate::instructions::*;
|
||||||
|
use crate::parser::ast::*;
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::iter::*;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
#[allow(clippy::borrowed_box)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum TermRef<'a> {
|
||||||
|
AnonVar(Level),
|
||||||
|
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>, Rc<String>, &'a Box<Term>),
|
||||||
|
CompleteString(Level, &'a Cell<RegType>, Rc<String>),
|
||||||
|
Var(Level, &'a Cell<VarReg>, VarPtr),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::borrowed_box)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum TermIterState<'a> {
|
||||||
|
AnonVar(Level),
|
||||||
|
Clause(Level, usize, &'a Cell<RegType>, Atom, &'a Vec<Term>),
|
||||||
|
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>, 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),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TermIterState<'a> {
|
||||||
|
pub(crate) fn subterm_to_state(lvl: Level, term: &'a Term) -> TermIterState<'a> {
|
||||||
|
match term {
|
||||||
|
Term::AnonVar => TermIterState::AnonVar(lvl),
|
||||||
|
Term::Clause(cell, name, subterms) => {
|
||||||
|
TermIterState::Clause(lvl, 0, cell, *name, subterms)
|
||||||
|
}
|
||||||
|
Term::Cons(cell, head, tail) => {
|
||||||
|
TermIterState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref())
|
||||||
|
}
|
||||||
|
Term::Literal(cell, constant) => TermIterState::Literal(lvl, cell, constant),
|
||||||
|
Term::PartialString(cell, string_buf, tail) => {
|
||||||
|
TermIterState::InitialPartialString(lvl, cell, string_buf.clone(), tail)
|
||||||
|
}
|
||||||
|
Term::CompleteString(cell, string) => {
|
||||||
|
TermIterState::CompleteString(lvl, cell, string.clone())
|
||||||
|
}
|
||||||
|
Term::Var(cell, var_ptr) => TermIterState::Var(lvl, cell, var_ptr.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct QueryIterator<'a> {
|
||||||
|
state_stack: Vec<TermIterState<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> QueryIterator<'a> {
|
||||||
|
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||||
|
self.state_stack
|
||||||
|
.push(TermIterState::subterm_to_state(lvl, term));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
|
||||||
|
let state_stack = terms
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
QueryIterator { state_stack }
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn from_term(term: &'a Term) -> Self {
|
||||||
|
let state = match term {
|
||||||
|
Term::AnonVar
|
||||||
|
| Term::Cons(..)
|
||||||
|
| Term::Literal(..)
|
||||||
|
| Term::PartialString(..)
|
||||||
|
| Term::CompleteString(..) => {
|
||||||
|
return QueryIterator {
|
||||||
|
state_stack: vec![],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Term::Clause(r, name, terms) => TermIterState::Clause(Level::Root, 0, r, *name, terms),
|
||||||
|
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Root, cell, var_ptr.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
QueryIterator {
|
||||||
|
state_stack: vec![state],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extend_state(&mut self, lvl: Level, term: &'a QueryTerm) {
|
||||||
|
match term {
|
||||||
|
QueryTerm::Clause(cell, ClauseType::CallN(_), terms, _) => {
|
||||||
|
self.state_stack
|
||||||
|
.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms));
|
||||||
|
}
|
||||||
|
QueryTerm::Clause(cell, ct, terms, _) => {
|
||||||
|
self.state_stack
|
||||||
|
.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(term: &'a QueryTerm) -> Self {
|
||||||
|
let mut iter = QueryIterator {
|
||||||
|
state_stack: vec![],
|
||||||
|
};
|
||||||
|
iter.extend_state(Level::Root, term);
|
||||||
|
iter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for QueryIterator<'a> {
|
||||||
|
type Item = TermRef<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
while let Some(iter_state) = self.state_stack.pop() {
|
||||||
|
match iter_state {
|
||||||
|
TermIterState::AnonVar(lvl) => {
|
||||||
|
return Some(TermRef::AnonVar(lvl));
|
||||||
|
}
|
||||||
|
TermIterState::Clause(lvl, child_num, cell, name, child_terms) => {
|
||||||
|
if child_num == child_terms.len() {
|
||||||
|
match name {
|
||||||
|
atom!("$call") if lvl == Level::Root => {
|
||||||
|
self.push_subterm(Level::Shallow, &child_terms[0]);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return match lvl {
|
||||||
|
Level::Root => None,
|
||||||
|
lvl => Some(TermRef::Clause(lvl, cell, name, child_terms)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
self.state_stack.push(TermIterState::Clause(
|
||||||
|
lvl,
|
||||||
|
child_num + 1,
|
||||||
|
cell,
|
||||||
|
name,
|
||||||
|
child_terms,
|
||||||
|
));
|
||||||
|
|
||||||
|
self.push_subterm(lvl.child_level(), &child_terms[child_num]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
||||||
|
self.state_stack
|
||||||
|
.push(TermIterState::FinalCons(lvl, cell, head, tail));
|
||||||
|
|
||||||
|
self.push_subterm(lvl.child_level(), tail);
|
||||||
|
self.push_subterm(lvl.child_level(), head);
|
||||||
|
}
|
||||||
|
TermIterState::InitialPartialString(lvl, cell, string, tail) => {
|
||||||
|
self.state_stack
|
||||||
|
.push(TermIterState::FinalPartialString(lvl, cell, string, tail));
|
||||||
|
self.push_subterm(lvl.child_level(), tail);
|
||||||
|
}
|
||||||
|
TermIterState::FinalPartialString(lvl, cell, string, tail) => {
|
||||||
|
return Some(TermRef::PartialString(lvl, cell, string, tail));
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
TermIterState::Literal(lvl, cell, constant) => {
|
||||||
|
return Some(TermRef::Literal(lvl, cell, constant));
|
||||||
|
}
|
||||||
|
TermIterState::Var(lvl, cell, var_ptr) => {
|
||||||
|
return Some(TermRef::Var(lvl, cell, var_ptr));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct FactIterator<'a> {
|
||||||
|
state_queue: VecDeque<TermIterState<'a>>,
|
||||||
|
iterable_root: RootIterationPolicy,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FactIterator<'a> {
|
||||||
|
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||||
|
self.state_queue
|
||||||
|
.push_back(TermIterState::subterm_to_state(lvl, term));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_rule_head_clause(terms: &'a [Term]) -> Self {
|
||||||
|
let state_queue = terms
|
||||||
|
.iter()
|
||||||
|
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
FactIterator {
|
||||||
|
state_queue,
|
||||||
|
iterable_root: RootIterationPolicy::NotIterated,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(term: &'a Term, iterable_root: RootIterationPolicy) -> Self {
|
||||||
|
let states = match term {
|
||||||
|
Term::AnonVar => {
|
||||||
|
vec![TermIterState::AnonVar(Level::Root)]
|
||||||
|
}
|
||||||
|
Term::Clause(cell, name, terms) => {
|
||||||
|
vec![TermIterState::Clause(Level::Root, 0, cell, *name, terms)]
|
||||||
|
}
|
||||||
|
Term::Cons(cell, head, tail) => vec![TermIterState::InitialCons(
|
||||||
|
Level::Root,
|
||||||
|
cell,
|
||||||
|
head.as_ref(),
|
||||||
|
tail.as_ref(),
|
||||||
|
)],
|
||||||
|
Term::PartialString(cell, string, tail) => {
|
||||||
|
vec![TermIterState::InitialPartialString(
|
||||||
|
Level::Root,
|
||||||
|
cell,
|
||||||
|
string.clone(),
|
||||||
|
tail,
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
Term::CompleteString(cell, string) => {
|
||||||
|
vec![TermIterState::CompleteString(
|
||||||
|
Level::Root,
|
||||||
|
cell,
|
||||||
|
string.clone(),
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
Term::Literal(cell, constant) => {
|
||||||
|
vec![TermIterState::Literal(Level::Root, cell, constant)]
|
||||||
|
}
|
||||||
|
Term::Var(cell, var_ptr) => {
|
||||||
|
vec![TermIterState::Var(Level::Root, cell, var_ptr.clone())]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FactIterator {
|
||||||
|
state_queue: VecDeque::from(states),
|
||||||
|
iterable_root,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for FactIterator<'a> {
|
||||||
|
type Item = TermRef<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
while let Some(state) = self.state_queue.pop_front() {
|
||||||
|
match state {
|
||||||
|
TermIterState::AnonVar(lvl) => {
|
||||||
|
return Some(TermRef::AnonVar(lvl));
|
||||||
|
}
|
||||||
|
TermIterState::Clause(lvl, _, cell, name, child_terms) => {
|
||||||
|
for child_term in child_terms {
|
||||||
|
self.push_subterm(lvl.child_level(), child_term);
|
||||||
|
}
|
||||||
|
|
||||||
|
match lvl {
|
||||||
|
Level::Root if !self.iterable_root.iterable() => continue,
|
||||||
|
_ => return Some(TermRef::Clause(lvl, cell, name, child_terms)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
||||||
|
self.push_subterm(Level::Deep, head);
|
||||||
|
self.push_subterm(Level::Deep, tail);
|
||||||
|
|
||||||
|
return Some(TermRef::Cons(lvl, cell, head, tail));
|
||||||
|
}
|
||||||
|
TermIterState::InitialPartialString(lvl, cell, string_buf, tail) => {
|
||||||
|
self.push_subterm(Level::Deep, tail);
|
||||||
|
return Some(TermRef::PartialString(lvl, cell, string_buf, tail));
|
||||||
|
}
|
||||||
|
TermIterState::CompleteString(lvl, cell, atom) => {
|
||||||
|
return Some(TermRef::CompleteString(lvl, cell, atom));
|
||||||
|
}
|
||||||
|
TermIterState::Literal(lvl, cell, constant) => {
|
||||||
|
return Some(TermRef::Literal(lvl, cell, constant));
|
||||||
|
}
|
||||||
|
TermIterState::Var(lvl, cell, var_ptr) => {
|
||||||
|
return Some(TermRef::Var(lvl, cell, var_ptr));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn post_order_iter(term: &Term) -> QueryIterator<'_> {
|
||||||
|
QueryIterator::from_term(term)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn breadth_first_iter(
|
||||||
|
term: &Term,
|
||||||
|
iterable_root: RootIterationPolicy,
|
||||||
|
) -> FactIterator<'_> {
|
||||||
|
FactIterator::new(term, iterable_root)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
enum ClauseIteratorState<'a> {
|
||||||
|
RemainingChunks(&'a VecDeque<ChunkedTerms>, usize),
|
||||||
|
RemainingBranches(
|
||||||
|
&'a Vec<Arc<BranchNumber>>,
|
||||||
|
&'a Vec<VecDeque<ChunkedTerms>>,
|
||||||
|
usize,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum ClauseItem<'a> {
|
||||||
|
FirstBranch {
|
||||||
|
branch_num: &'a Arc<BranchNumber>,
|
||||||
|
num_branches: usize,
|
||||||
|
},
|
||||||
|
NextBranch {
|
||||||
|
branch_num: &'a Arc<BranchNumber>,
|
||||||
|
},
|
||||||
|
BranchEnd {
|
||||||
|
depth: usize,
|
||||||
|
},
|
||||||
|
Chunk {
|
||||||
|
terms: &'a VecDeque<QueryTerm>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ClauseIterator<'a> {
|
||||||
|
state_stack: Vec<ClauseIteratorState<'a>>,
|
||||||
|
remaining_chunks_on_stack: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_from_chunked_terms(chunk_vec: &VecDeque<ChunkedTerms>) -> ClauseIteratorState<'_> {
|
||||||
|
if chunk_vec.len() == 1 {
|
||||||
|
if let Some(ChunkedTerms::Branch { branch_nums, arms }) = chunk_vec.front() {
|
||||||
|
return ClauseIteratorState::RemainingBranches(branch_nums, arms, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClauseIteratorState::RemainingChunks(chunk_vec, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ClauseIterator<'a> {
|
||||||
|
pub fn new(clauses: &'a ChunkedTermVec) -> Self {
|
||||||
|
match state_from_chunked_terms(&clauses.chunk_vec) {
|
||||||
|
state @ ClauseIteratorState::RemainingBranches(..) => Self {
|
||||||
|
state_stack: vec![state],
|
||||||
|
remaining_chunks_on_stack: 0,
|
||||||
|
},
|
||||||
|
state @ ClauseIteratorState::RemainingChunks(..) => Self {
|
||||||
|
state_stack: vec![state],
|
||||||
|
remaining_chunks_on_stack: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn in_tail_position(&self) -> bool {
|
||||||
|
self.remaining_chunks_on_stack == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn branch_end_depth(&mut self) -> usize {
|
||||||
|
let mut depth = 1;
|
||||||
|
|
||||||
|
while let Some(state) = self.state_stack.pop() {
|
||||||
|
match state {
|
||||||
|
ClauseIteratorState::RemainingBranches(_branch_nums, terms, focus)
|
||||||
|
if terms.len() == focus =>
|
||||||
|
{
|
||||||
|
depth += 1;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.state_stack.push(state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
depth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for ClauseIterator<'a> {
|
||||||
|
type Item = ClauseItem<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
while let Some(state) = self.state_stack.pop() {
|
||||||
|
match state {
|
||||||
|
ClauseIteratorState::RemainingChunks(chunks, focus) if focus < chunks.len() => {
|
||||||
|
if focus + 1 < chunks.len() {
|
||||||
|
self.state_stack
|
||||||
|
.push(ClauseIteratorState::RemainingChunks(chunks, focus + 1));
|
||||||
|
} else {
|
||||||
|
self.remaining_chunks_on_stack -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &chunks[focus] {
|
||||||
|
ChunkedTerms::Branch { branch_nums, arms } => {
|
||||||
|
self.state_stack
|
||||||
|
.push(ClauseIteratorState::RemainingBranches(branch_nums, arms, 0));
|
||||||
|
}
|
||||||
|
ChunkedTerms::Chunk { terms } => {
|
||||||
|
return Some(ClauseItem::Chunk { terms });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClauseIteratorState::RemainingChunks(chunks, focus) => {
|
||||||
|
debug_assert_eq!(chunks.len(), focus);
|
||||||
|
}
|
||||||
|
ClauseIteratorState::RemainingBranches(branch_nums, branches, focus)
|
||||||
|
if focus < branches.len() =>
|
||||||
|
{
|
||||||
|
self.state_stack
|
||||||
|
.push(ClauseIteratorState::RemainingBranches(
|
||||||
|
branch_nums,
|
||||||
|
branches,
|
||||||
|
focus + 1,
|
||||||
|
));
|
||||||
|
let state = state_from_chunked_terms(&branches[focus]);
|
||||||
|
|
||||||
|
if let ClauseIteratorState::RemainingChunks(..) = &state {
|
||||||
|
self.remaining_chunks_on_stack += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state_stack.push(state);
|
||||||
|
|
||||||
|
return if focus == 0 {
|
||||||
|
Some(ClauseItem::FirstBranch {
|
||||||
|
branch_num: &branch_nums[0],
|
||||||
|
num_branches: branches.len(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Some(ClauseItem::NextBranch {
|
||||||
|
branch_num: &branch_nums[focus],
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ClauseIteratorState::RemainingBranches(_branch_nums, branches, focus) => {
|
||||||
|
debug_assert_eq!(branches.len(), focus);
|
||||||
|
return Some(ClauseItem::BranchEnd {
|
||||||
|
depth: self.branch_end_depth(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/lib.rs
Normal file
85
src/lib.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
//! A free software ISO Prolog system.
|
||||||
|
#![recursion_limit = "4112"]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate static_assertions;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub(crate) mod macros;
|
||||||
|
#[macro_use]
|
||||||
|
pub(crate) mod atom_table;
|
||||||
|
#[macro_use]
|
||||||
|
pub(crate) mod arena;
|
||||||
|
pub(crate) mod offset_table;
|
||||||
|
#[macro_use]
|
||||||
|
pub(crate) mod parser;
|
||||||
|
#[macro_use]
|
||||||
|
pub(crate) mod functor_macro;
|
||||||
|
mod allocator;
|
||||||
|
mod arithmetic;
|
||||||
|
pub(crate) mod codegen;
|
||||||
|
mod debray_allocator;
|
||||||
|
#[cfg(feature = "ffi")]
|
||||||
|
mod ffi;
|
||||||
|
mod forms;
|
||||||
|
mod heap_iter;
|
||||||
|
pub(crate) mod heap_print;
|
||||||
|
#[cfg(feature = "http")]
|
||||||
|
mod http;
|
||||||
|
mod indexing;
|
||||||
|
mod variable_records;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
pub(crate) mod instructions;
|
||||||
|
|
||||||
|
mod iterators;
|
||||||
|
pub(crate) mod machine;
|
||||||
|
mod raw_block;
|
||||||
|
pub(crate) mod read;
|
||||||
|
#[cfg(feature = "repl")]
|
||||||
|
mod repl_helper;
|
||||||
|
mod targets;
|
||||||
|
pub(crate) mod types;
|
||||||
|
|
||||||
|
// Re-exports
|
||||||
|
pub use machine::Machine;
|
||||||
|
pub use machine::config::*;
|
||||||
|
pub use machine::lib_machine::*;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub mod wasm;
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
|
||||||
|
#[cfg(feature = "repl")]
|
||||||
|
use crate::machine::INTERRUPT;
|
||||||
|
|
||||||
|
#[cfg(feature = "repl")]
|
||||||
|
ctrlc::set_handler(move || {
|
||||||
|
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();
|
||||||
|
|
||||||
|
#[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))
|
||||||
|
})
|
||||||
|
}
|
||||||
163
src/lib/arithmetic.pl
Normal file
163
src/lib/arithmetic.pl
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/** Arithmetic predicates
|
||||||
|
|
||||||
|
These predicates are additions to standard the arithmetic functions provided by `is/2`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(arithmetic, [expmod/4, lcm/3, lsb/2, msb/2, number_to_rational/2,
|
||||||
|
number_to_rational/3, popcount/2,
|
||||||
|
rational_numerator_denominator/3]).
|
||||||
|
|
||||||
|
:- use_module(library(charsio), [write_term_to_chars/3]).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(lists), [append/3, member/2]).
|
||||||
|
|
||||||
|
|
||||||
|
%% expmod(+Base, +Expo, +Mod, -R).
|
||||||
|
%
|
||||||
|
% Modular exponentiation. Base, Expo and Mod must be integers.
|
||||||
|
expmod(Base, Expo, Mod, R) :-
|
||||||
|
( member(N, [Base, Expo, Mod]), var(N) -> instantiation_error(expmod/4)
|
||||||
|
; member(N, [Base, Expo, Mod]), \+ integer(N) ->
|
||||||
|
type_error(integer, N, expmod/4)
|
||||||
|
; Expo < 0 -> domain_error(not_less_than_zero, Expo, expmod/4)
|
||||||
|
; expmod_(Base, Expo, Mod, 1, R)
|
||||||
|
).
|
||||||
|
|
||||||
|
expmod_(_, _, 1, _, 0) :- !.
|
||||||
|
expmod_(_, 0, _, R, R) :- !.
|
||||||
|
expmod_(Base0, Expo0, Mod, C0, R) :-
|
||||||
|
Expo0 /\ 1 =:= 1,
|
||||||
|
C is (C0 * Base0) mod Mod,
|
||||||
|
!,
|
||||||
|
Expo is Expo0 >> 1,
|
||||||
|
Base is (Base0 * Base0) mod Mod,
|
||||||
|
expmod_(Base, Expo, Mod, C, R).
|
||||||
|
expmod_(Base0, Expo0, Mod, C, R) :-
|
||||||
|
Expo is Expo0 >> 1,
|
||||||
|
Base is (Base0 * Base0) mod Mod,
|
||||||
|
expmod_(Base, Expo, Mod, C, R).
|
||||||
|
|
||||||
|
%% lcm(+A, +B, -Lcm) is det.
|
||||||
|
%
|
||||||
|
% Calculates the Least common multiple for A and B: the smallest positive integer
|
||||||
|
% that is divisible by both A and B.
|
||||||
|
%
|
||||||
|
% A and B need to be integers.
|
||||||
|
lcm(A, B, X) :-
|
||||||
|
builtins:must_be_number(A, lcm/2),
|
||||||
|
builtins:must_be_number(B, lcm/2),
|
||||||
|
( \+ integer(A) -> type_error(integer, A, lcm/2)
|
||||||
|
; \+ integer(B) -> type_error(integer, B, lcm/2)
|
||||||
|
; (A = 0, B = 0) -> X = 0
|
||||||
|
; builtins:can_be_number(X, lcm/2),
|
||||||
|
X is abs(B) // gcd(A,B) * abs(A)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% lsb(+X, -N).
|
||||||
|
%
|
||||||
|
% True iff N is the least significat bit of integer X
|
||||||
|
lsb(X, N) :-
|
||||||
|
builtins:must_be_number(X, lsb/2),
|
||||||
|
( \+ integer(X) -> type_error(integer, X, lsb/2)
|
||||||
|
; X < 1 -> domain_error(not_less_than_one, X, lsb/2)
|
||||||
|
; builtins:can_be_number(N, lsb/2),
|
||||||
|
X1 is X /\ (-X),
|
||||||
|
msb_(X1, -1, N)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% msb(+X, -N).
|
||||||
|
%
|
||||||
|
% True iff N is the most significant bit of integer X
|
||||||
|
msb(X, N) :-
|
||||||
|
builtins:must_be_number(X, msb/2),
|
||||||
|
( \+ integer(X) -> type_error(integer, X, msb/2)
|
||||||
|
; X < 1 -> domain_error(not_less_than_one, X, msb/2)
|
||||||
|
; builtins:can_be_number(N, msb/2),
|
||||||
|
X1 is X >> 1,
|
||||||
|
msb_(X1, 0, N)
|
||||||
|
).
|
||||||
|
|
||||||
|
msb_(0, N, N) :- !.
|
||||||
|
msb_(X, M, N) :-
|
||||||
|
X1 is X >> 1,
|
||||||
|
M1 is M + 1,
|
||||||
|
msb_(X1, M1, N).
|
||||||
|
|
||||||
|
%% number_to_rational(+Real, -Fraction).
|
||||||
|
%
|
||||||
|
% True iff given a number Real, Fraction is the same number represented as a fraction.
|
||||||
|
number_to_rational(Real, Fraction) :-
|
||||||
|
( var(Real) -> instantiation_error(number_to_rational/2)
|
||||||
|
; integer(Real) -> Fraction is Real rdiv 1
|
||||||
|
; (rational(Real) ; float(Real)) ->
|
||||||
|
number_to_rational(1.0e-6, Real, Fraction)
|
||||||
|
; type_error(number, Real, number_to_rational/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
% If 0 <= Eps0 <= 1e-16 then the search is for "infinite" precision.
|
||||||
|
number_to_rational(Eps0, Real0, Fraction) :-
|
||||||
|
( var(Eps0) -> instantiation_error(number_to_rational/3)
|
||||||
|
; \+ number(Eps0) -> type_error(number, Eps0, number_to_rational/3)
|
||||||
|
; Eps0 < 0 -> domain_error(not_less_than_zero, Eps0, number_to_rational/3)
|
||||||
|
; Eps_ is Eps0 rdiv 1,
|
||||||
|
rational_numerator_denominator(Eps_, EpsN, EpsD),
|
||||||
|
Eps = EpsN/EpsD
|
||||||
|
),
|
||||||
|
( var(Real0) -> instantiation_error(number_to_rational/3)
|
||||||
|
; \+ number(Real0) -> type_error(number, Eps0, number_to_rational/3)
|
||||||
|
; Real_ is Real0 rdiv 1,
|
||||||
|
rational_numerator_denominator(Real_, RealN, RealD),
|
||||||
|
Real = RealN/RealD
|
||||||
|
),
|
||||||
|
E0/E1 = Eps,
|
||||||
|
P0/Q0 = Real,
|
||||||
|
( P0 < 0 -> I1 is -1 + P0 // Q0
|
||||||
|
; I1 is P0 // Q0
|
||||||
|
),
|
||||||
|
P1 is P0 mod Q0,
|
||||||
|
Q1 = Q0,
|
||||||
|
( P1 =:= 0 -> Fraction is I1 + 0 rdiv 1
|
||||||
|
; Qn1n is max(P1 * E1 - Q1 * E0, 0),
|
||||||
|
Qn1d is Q1 * E1,
|
||||||
|
Qn1 = Qn1n/Qn1d,
|
||||||
|
Qp1n is P1 * E1 + Q1 * E0,
|
||||||
|
Qp1d = Qn1d,
|
||||||
|
Qp1 = Qp1n/Qp1d,
|
||||||
|
stern_brocot_(Qn1, Qp1, 0/1, 1/0, P2/Q2),
|
||||||
|
Fraction is I1 + P2 rdiv Q2
|
||||||
|
),
|
||||||
|
!.
|
||||||
|
|
||||||
|
stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, C/D, Fraction) :-
|
||||||
|
Fn1 is A + C,
|
||||||
|
Fd1 is B + D,
|
||||||
|
simplify_fraction(Fn1/Fd1, Fn/Fd),
|
||||||
|
S1 is sign(Fn * Qnd - Fd * Qnn),
|
||||||
|
S2 is sign(Fn * Qpd - Fd * Qpn),
|
||||||
|
( S1 < 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, Fn/Fd, C/D, Fraction)
|
||||||
|
; S2 > 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, Fn/Fd, Fraction)
|
||||||
|
; Fraction = Fn/Fd
|
||||||
|
).
|
||||||
|
|
||||||
|
simplify_fraction(A0/B0, A/B) :-
|
||||||
|
G is gcd(A0, B0),
|
||||||
|
A is A0 // G,
|
||||||
|
B is B0 // G.
|
||||||
|
|
||||||
|
%% rational_numerator_denominator(+Fraction, -Numerator, -Denominator).
|
||||||
|
%
|
||||||
|
% True iff given a fraction Fraction, Numerator is the numerator of that fraction
|
||||||
|
% and Denominator the denominator.
|
||||||
|
rational_numerator_denominator(R, N, D) :-
|
||||||
|
write_term_to_chars(R, [], Cs),
|
||||||
|
append(Ns, [' ', r, d, i, v, ' '|Ds], Cs),
|
||||||
|
number_chars(N, Ns),
|
||||||
|
number_chars(D, Ds).
|
||||||
|
|
||||||
|
%% popcount(+Number, -Bits1).
|
||||||
|
%
|
||||||
|
% True iff given an integer Number, Bits1 is the amount of 1 bits the binary representation
|
||||||
|
% of that number has.
|
||||||
|
popcount(X, N) :-
|
||||||
|
must_be(integer, X),
|
||||||
|
'$popcount'(X, N).
|
||||||
@@ -54,28 +54,24 @@
|
|||||||
|
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
/** <module> Binary associations
|
/** Binary associations
|
||||||
|
|
||||||
Assocs are Key-Value associations implemented as a balanced binary tree
|
Assocs are Key-Value associations implemented as a balanced binary tree
|
||||||
(AVL tree).
|
(AVL tree).
|
||||||
|
|
||||||
@see library(pairs), library(rbtrees)
|
Authors: R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
|
||||||
@author R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
:- meta_predicate(map_assoc(1, ?)).
|
||||||
:- meta_predicate
|
:- meta_predicate(map_assoc(2, ?, ?)).
|
||||||
map_assoc(1, ?),
|
|
||||||
map_assoc(2, ?, ?).
|
|
||||||
*/
|
|
||||||
|
|
||||||
%! empty_assoc(?Assoc) is semidet.
|
%% empty_assoc(?Assoc) is semidet.
|
||||||
%
|
%
|
||||||
% Is true if Assoc is the empty association list.
|
% Is true if Assoc is the empty association list.
|
||||||
|
|
||||||
empty_assoc(t).
|
empty_assoc(t).
|
||||||
|
|
||||||
%! assoc_to_list(+Assoc, -Pairs) is det.
|
%% assoc_to_list(+Assoc, -Pairs) is det.
|
||||||
%
|
%
|
||||||
% Translate Assoc to a list Pairs of Key-Value pairs. The keys
|
% Translate Assoc to a list Pairs of Key-Value pairs. The keys
|
||||||
% in Pairs are sorted in ascending order.
|
% in Pairs are sorted in ascending order.
|
||||||
@@ -89,7 +85,7 @@ assoc_to_list(t(Key,Val,_,L,R), List, Rest) :-
|
|||||||
assoc_to_list(t, List, List).
|
assoc_to_list(t, List, List).
|
||||||
|
|
||||||
|
|
||||||
%! assoc_to_keys(+Assoc, -Keys) is det.
|
%% assoc_to_keys(+Assoc, -Keys) is det.
|
||||||
%
|
%
|
||||||
% True if Keys is the list of keys in Assoc. The keys are sorted
|
% True if Keys is the list of keys in Assoc. The keys are sorted
|
||||||
% in ascending order.
|
% in ascending order.
|
||||||
@@ -103,7 +99,7 @@ assoc_to_keys(t(Key,_,_,L,R), List, Rest) :-
|
|||||||
assoc_to_keys(t, List, List).
|
assoc_to_keys(t, List, List).
|
||||||
|
|
||||||
|
|
||||||
%! assoc_to_values(+Assoc, -Values) is det.
|
%% assoc_to_values(+Assoc, -Values) is det.
|
||||||
%
|
%
|
||||||
% True if Values is the list of values in Assoc. Values are
|
% True if Values is the list of values in Assoc. Values are
|
||||||
% ordered in ascending order of the key to which they were
|
% ordered in ascending order of the key to which they were
|
||||||
@@ -117,7 +113,7 @@ assoc_to_values(t(_,Value,_,L,R), List, Rest) :-
|
|||||||
assoc_to_values(R, More, Rest).
|
assoc_to_values(R, More, Rest).
|
||||||
assoc_to_values(t, List, List).
|
assoc_to_values(t, List, List).
|
||||||
|
|
||||||
%! is_assoc(+Assoc) is semidet.
|
%% is_assoc(+Assoc) is semidet.
|
||||||
%
|
%
|
||||||
% True if Assoc is an association list. This predicate checks
|
% True if Assoc is an association list. This predicate checks
|
||||||
% that the structure is valid, elements are in order, and tree
|
% that the structure is valid, elements are in order, and tree
|
||||||
@@ -154,12 +150,10 @@ balance(=,-).
|
|||||||
balance(<,<).
|
balance(<,<).
|
||||||
balance(>,>).
|
balance(>,>).
|
||||||
|
|
||||||
%! gen_assoc(?Key, +Assoc, ?Value) is nondet.
|
%% gen_assoc(?Key, +Assoc, ?Value) is nondet.
|
||||||
%
|
%
|
||||||
% True if Key-Value is an association in Assoc. Enumerates keys in
|
% True if Key-Value is an association in Assoc. Enumerates keys in
|
||||||
% ascending order on backtracking.
|
% ascending order on backtracking.
|
||||||
%
|
|
||||||
% @see get_assoc/3.
|
|
||||||
|
|
||||||
gen_assoc(Key, Assoc, Value) :-
|
gen_assoc(Key, Assoc, Value) :-
|
||||||
( ground(Key)
|
( ground(Key)
|
||||||
@@ -174,11 +168,11 @@ gen_assoc_(Key, t(_,_,_,_,R), Val) :-
|
|||||||
gen_assoc_(Key, R, Val).
|
gen_assoc_(Key, R, Val).
|
||||||
|
|
||||||
|
|
||||||
%! get_assoc(+Key, +Assoc, -Value) is semidet.
|
%% get_assoc(+Key, +Assoc, -Value) is semidet.
|
||||||
%
|
%
|
||||||
% True if Key-Value is an association in Assoc.
|
% True if Key-Value is an association in Assoc.
|
||||||
%
|
%
|
||||||
% @error type_error(assoc, Assoc) if Assoc is not an association list.
|
% Throws error: `type_error(assoc, Assoc)` if Assoc is not an association list.
|
||||||
|
|
||||||
get_assoc(Key, Assoc, Val) :-
|
get_assoc(Key, Assoc, Val) :-
|
||||||
must_be(assoc, Assoc),
|
must_be(assoc, Assoc),
|
||||||
@@ -204,7 +198,7 @@ get_assoc(>, Key, _, _, Tree, Val) :-
|
|||||||
% :- endif.
|
% :- endif.
|
||||||
|
|
||||||
|
|
||||||
%! get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet.
|
%% get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet.
|
||||||
%
|
%
|
||||||
% True if Key-Val0 is in Assoc0 and Key-Val is in Assoc.
|
% True if Key-Val0 is in Assoc0 and Key-Val is in Assoc.
|
||||||
|
|
||||||
@@ -219,12 +213,12 @@ get_assoc(>, Key, V, L, R, Val, V, L, NR, NVal) :-
|
|||||||
get_assoc(Key, R, Val, NR, NVal).
|
get_assoc(Key, R, Val, NR, NVal).
|
||||||
|
|
||||||
|
|
||||||
%! list_to_assoc(+Pairs, -Assoc) is det.
|
%% list_to_assoc(+Pairs, -Assoc) is det.
|
||||||
%
|
%
|
||||||
% Create an association from a list Pairs of Key-Value pairs. List
|
% Create an association from a list Pairs of Key-Value pairs. List
|
||||||
% must not contain duplicate keys.
|
% must not contain duplicate keys.
|
||||||
%
|
%
|
||||||
% @error domain_error(unique_key_pairs, List) if List contains duplicate keys
|
% Throws error: `domain_error(unique_key_pairs, List)` if List contains duplicate keys
|
||||||
|
|
||||||
list_to_assoc(List, Assoc) :-
|
list_to_assoc(List, Assoc) :-
|
||||||
( List = [] -> Assoc = t
|
( List = [] -> Assoc = t
|
||||||
@@ -249,13 +243,13 @@ list_to_assoc(N, List, More, Depth, t(K,V,Balance,L,R)) :-
|
|||||||
compare(B, RDepth, LDepth),
|
compare(B, RDepth, LDepth),
|
||||||
balance(B, Balance).
|
balance(B, Balance).
|
||||||
|
|
||||||
%! ord_list_to_assoc(+Pairs, -Assoc) is det.
|
%% ord_list_to_assoc(+Pairs, -Assoc) is det.
|
||||||
%
|
%
|
||||||
% Assoc is created from an ordered list Pairs of Key-Value
|
% Assoc is created from an ordered list Pairs of Key-Value
|
||||||
% pairs. The pairs must occur in strictly ascending order of
|
% pairs. The pairs must occur in strictly ascending order of
|
||||||
% their keys.
|
% their keys.
|
||||||
%
|
%
|
||||||
% @error domain_error(key_ordered_pairs, List) if pairs are not ordered.
|
% Throws error: `domain_error(key_ordered_pairs, List)` if pairs are not ordered.
|
||||||
|
|
||||||
ord_list_to_assoc(Sorted, Assoc) :-
|
ord_list_to_assoc(Sorted, Assoc) :-
|
||||||
( Sorted = [] -> Assoc = t
|
( Sorted = [] -> Assoc = t
|
||||||
@@ -266,7 +260,7 @@ ord_list_to_assoc(Sorted, Assoc) :-
|
|||||||
)
|
)
|
||||||
).
|
).
|
||||||
|
|
||||||
%! ord_pairs(+Pairs) is semidet
|
%% ord_pairs(+Pairs) is semidet
|
||||||
%
|
%
|
||||||
% True if Pairs is a list of Key-Val pairs strictly ordered by key.
|
% True if Pairs is a list of Key-Val pairs strictly ordered by key.
|
||||||
|
|
||||||
@@ -277,7 +271,7 @@ ord_pairs([K-_V|Rest], K0) :-
|
|||||||
K0 @< K,
|
K0 @< K,
|
||||||
ord_pairs(Rest, K).
|
ord_pairs(Rest, K).
|
||||||
|
|
||||||
%! map_assoc(:Pred, +Assoc) is semidet.
|
%% map_assoc(:Pred, +Assoc) is semidet.
|
||||||
%
|
%
|
||||||
% True if Pred(Value) is true for all values in Assoc.
|
% True if Pred(Value) is true for all values in Assoc.
|
||||||
|
|
||||||
@@ -290,7 +284,7 @@ map_assoc_(t(_,Val,_,L,R), Pred) :-
|
|||||||
call(Pred, Val),
|
call(Pred, Val),
|
||||||
map_assoc_(R, Pred).
|
map_assoc_(R, Pred).
|
||||||
|
|
||||||
%! map_assoc(:Pred, +Assoc0, ?Assoc) is semidet.
|
%% map_assoc(:Pred, +Assoc0, ?Assoc) is semidet.
|
||||||
%
|
%
|
||||||
% Map corresponding values. True if Assoc is Assoc0 with Pred
|
% Map corresponding values. True if Assoc is Assoc0 with Pred
|
||||||
% applied to all corresponding pairs of of values.
|
% applied to all corresponding pairs of of values.
|
||||||
@@ -305,7 +299,7 @@ map_assoc_(t(Key,Val,B,L0,R0), Pred, t(Key,Ans,B,L1,R1)) :-
|
|||||||
map_assoc_(R0, Pred, 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.
|
% True if Key-Value is in Assoc and Key is the largest key.
|
||||||
|
|
||||||
@@ -317,7 +311,7 @@ max_assoc(t(K,V,_,_,R), _, _, Key, Val) :-
|
|||||||
max_assoc(R, K, V, Key, Val).
|
max_assoc(R, K, V, Key, Val).
|
||||||
|
|
||||||
|
|
||||||
%! min_assoc(+Assoc, -Key, -Value) is semidet.
|
%% min_assoc(+Assoc, -Key, -Value) is semidet.
|
||||||
%
|
%
|
||||||
% True if Key-Value is in assoc and Key is the smallest key.
|
% True if Key-Value is in assoc and Key is the smallest key.
|
||||||
|
|
||||||
@@ -329,7 +323,7 @@ min_assoc(t(K,V,_,L,_), _, _, Key, Val) :-
|
|||||||
min_assoc(L, K, V, Key, Val).
|
min_assoc(L, K, V, Key, Val).
|
||||||
|
|
||||||
|
|
||||||
%! put_assoc(+Key, +Assoc0, +Value, -Assoc) is det.
|
%% put_assoc(+Key, +Assoc0, +Value, -Assoc) is det.
|
||||||
%
|
%
|
||||||
% Assoc is Assoc0, except that Key is associated with
|
% Assoc is Assoc0, except that Key is associated with
|
||||||
% Value. This can be used to insert and change associations.
|
% Value. This can be used to insert and change associations.
|
||||||
@@ -364,7 +358,7 @@ table(< , right , - , no , no ) :- !.
|
|||||||
table(> , left , - , no , no ) :- !.
|
table(> , left , - , no , no ) :- !.
|
||||||
table(> , right , - , no , yes ) :- !.
|
table(> , right , - , no , yes ) :- !.
|
||||||
|
|
||||||
%! del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
|
%% del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
|
||||||
%
|
%
|
||||||
% True if Key-Value is in Assoc0 and Key is the smallest key.
|
% True if Key-Value is in Assoc0 and Key is the smallest key.
|
||||||
% Assoc is Assoc0 with Key-Value removed. Warning: This will
|
% Assoc is Assoc0 with Key-Value removed. Warning: This will
|
||||||
@@ -378,7 +372,7 @@ del_min_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :-
|
|||||||
del_min_assoc(L, Key, Val, NewL, LeftChanged),
|
del_min_assoc(L, Key, Val, NewL, LeftChanged),
|
||||||
deladjust(LeftChanged, t(K,V,B,NewL,R), left, NewTree, Changed).
|
deladjust(LeftChanged, t(K,V,B,NewL,R), left, NewTree, Changed).
|
||||||
|
|
||||||
%! del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
|
%% del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
|
||||||
%
|
%
|
||||||
% True if Key-Value is in Assoc0 and Key is the greatest key.
|
% True if Key-Value is in Assoc0 and Key is the greatest key.
|
||||||
% Assoc is Assoc0 with Key-Value removed. Warning: This will
|
% Assoc is Assoc0 with Key-Value removed. Warning: This will
|
||||||
@@ -392,7 +386,7 @@ del_max_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :-
|
|||||||
del_max_assoc(R, Key, Val, NewR, RightChanged),
|
del_max_assoc(R, Key, Val, NewR, RightChanged),
|
||||||
deladjust(RightChanged, t(K,V,B,L,NewR), right, NewTree, Changed).
|
deladjust(RightChanged, t(K,V,B,L,NewR), right, NewTree, Changed).
|
||||||
|
|
||||||
%! del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet.
|
%% del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet.
|
||||||
%
|
%
|
||||||
% True if Key-Value is in Assoc0. Assoc is Assoc0 with
|
% True if Key-Value is in Assoc0. Assoc is Assoc0 with
|
||||||
% Key-Value removed.
|
% Key-Value removed.
|
||||||
114
src/lib/atts.pl
Normal file
114
src/lib/atts.pl
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
:- module(atts, [op(1199, fx, attribute),
|
||||||
|
term_attributed_variables/2]).
|
||||||
|
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(terms)).
|
||||||
|
|
||||||
|
/* represent the list of attributes belonging to a variable,
|
||||||
|
of a particular module, as a list of terms of the form
|
||||||
|
Module:put_atts(V, ListOfAtts). */
|
||||||
|
'$default_attr_list'(Module, V) -->
|
||||||
|
( { Module:get_atts(V, Attributes) } ->
|
||||||
|
'$default_attr_list'(Attributes, Module, V)
|
||||||
|
; []
|
||||||
|
).
|
||||||
|
|
||||||
|
'$default_attr_list'([PG | PGs], Module, AttrVar) -->
|
||||||
|
[Module:put_atts(AttrVar, PG)],
|
||||||
|
'$default_attr_list'(PGs, Module, AttrVar).
|
||||||
|
'$default_attr_list'([], _, _) --> [].
|
||||||
|
|
||||||
|
'$absent_attr'(V, Module, Attr) :-
|
||||||
|
( '$get_from_attr_list'(V, Module, Attr) ->
|
||||||
|
false
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
'$copy_attr_list'(L, _Module, []) :- var(L), !.
|
||||||
|
'$copy_attr_list'([Module0:Att|Atts], Module, CopiedAtts) :-
|
||||||
|
( Module0 == Module ->
|
||||||
|
CopiedAtts = [Att|CopiedAtts0],
|
||||||
|
'$copy_attr_list'(Atts, Module, CopiedAtts0)
|
||||||
|
; '$copy_attr_list'(Atts, Module, CopiedAtts)
|
||||||
|
).
|
||||||
|
|
||||||
|
user:term_expansion(Term0, Terms) :-
|
||||||
|
nonvar(Term0),
|
||||||
|
Term0 = (:- attribute Atts),
|
||||||
|
nonvar(Atts),
|
||||||
|
prolog_load_context(module, Module),
|
||||||
|
phrase(expand_terms(Atts, Module), Terms).
|
||||||
|
|
||||||
|
expand_terms(Atts, Module) -->
|
||||||
|
put_attrs_var_check,
|
||||||
|
put_attrs(Atts, Module),
|
||||||
|
get_attrs_var_check(Module),
|
||||||
|
get_attrs(Atts, Module).
|
||||||
|
|
||||||
|
put_attrs_var_check -->
|
||||||
|
[(put_atts(Var, Attr) :- nonvar(Var),
|
||||||
|
throw(error(uninstantiation_error(Var), put_atts/2))),
|
||||||
|
(put_atts(Var, Attr) :- var(Attr),
|
||||||
|
throw(error(instantiation_error, put_atts/2)))].
|
||||||
|
|
||||||
|
get_attrs_var_check(Module) -->
|
||||||
|
[(get_atts(Var, Attr) :- nonvar(Var),
|
||||||
|
throw(error(uninstantiation_error(Var), get_atts/2))),
|
||||||
|
(get_atts(Var, Attr) :- var(Attr),
|
||||||
|
!,
|
||||||
|
'$get_attr_list'(Var, Ls),
|
||||||
|
nonvar(Ls),
|
||||||
|
atts:'$copy_attr_list'(Ls, Module, Attr))].
|
||||||
|
|
||||||
|
put_attrs(Name/Arity, Module) -->
|
||||||
|
put_attr(Name, Arity, Module),
|
||||||
|
[(put_atts(Var, Attr) :- lists:maplist(Module:put_atts(Var), Attr), !)].
|
||||||
|
put_attrs((Name/Arity, Atts), Module) -->
|
||||||
|
{ nonvar(Atts) },
|
||||||
|
put_attr(Name, Arity, Module),
|
||||||
|
put_attrs(Atts, Module).
|
||||||
|
|
||||||
|
get_attrs(Name/Arity, Module) -->
|
||||||
|
get_attr(Name, Arity, Module).
|
||||||
|
get_attrs((Name/Arity, Atts), Module) -->
|
||||||
|
{ nonvar(Atts) },
|
||||||
|
get_attr(Name, Arity, Module),
|
||||||
|
get_attrs(Atts, Module).
|
||||||
|
|
||||||
|
put_attr(Name, Arity, Module) -->
|
||||||
|
{ functor(Attr, Name, Arity) },
|
||||||
|
[(put_atts(V, +Attr) :-
|
||||||
|
!,
|
||||||
|
'$put_to_attr_list'(V, Module, Attr)),
|
||||||
|
(put_atts(V, Attr) :-
|
||||||
|
!,
|
||||||
|
'$put_to_attr_list'(V, Module, Attr)),
|
||||||
|
(put_atts(V, -Attr) :-
|
||||||
|
!,
|
||||||
|
'$del_from_attr_list'(V, Module, Attr))].
|
||||||
|
|
||||||
|
get_attr(Name, Arity, Module) -->
|
||||||
|
{ functor(Attr, Name, Arity) },
|
||||||
|
[(get_atts(V, +Attr) :-
|
||||||
|
!,
|
||||||
|
functor(Attr, _, _),
|
||||||
|
atts:'$get_from_attr_list'(V, Module, Attr)),
|
||||||
|
(get_atts(V, Attr) :-
|
||||||
|
!,
|
||||||
|
functor(Attr, _, _),
|
||||||
|
atts:'$get_from_attr_list'(V, Module, Attr)),
|
||||||
|
(get_atts(V, -Attr) :-
|
||||||
|
!,
|
||||||
|
functor(Attr, _, _),
|
||||||
|
atts:'$absent_attr'(V, Module, Attr))].
|
||||||
|
|
||||||
|
user:goal_expansion(Term, M:put_atts(Var, Attr)) :-
|
||||||
|
nonvar(Term),
|
||||||
|
Term = put_atts(Var, M, Attr).
|
||||||
|
user:goal_expansion(Term, M:get_atts(Var, Attr)) :-
|
||||||
|
nonvar(Term),
|
||||||
|
Term = get_atts(Var, M, Attr).
|
||||||
|
|
||||||
|
term_attributed_variables(Term, Vars) :-
|
||||||
|
'$term_attributed_variables'(Term, Vars).
|
||||||
@@ -1,3 +1,10 @@
|
|||||||
|
/** Predicates that generate integers
|
||||||
|
|
||||||
|
These predicates can be used to reason about integers in a reduced domain that
|
||||||
|
follow some property. `library(clpz)` provides another way of reasoning about
|
||||||
|
integers that may also be interesting.
|
||||||
|
*/
|
||||||
|
|
||||||
:- module(between, [between/3, gen_int/1, gen_nat/1, numlist/2, numlist/3, repeat/1]).
|
:- module(between, [between/3, gen_int/1, gen_nat/1, numlist/2, numlist/3, repeat/1]).
|
||||||
|
|
||||||
%% TODO: numlist/5.
|
%% TODO: numlist/5.
|
||||||
@@ -5,24 +12,52 @@
|
|||||||
:- use_module(library(lists), [length/2]).
|
:- use_module(library(lists), [length/2]).
|
||||||
:- use_module(library(error)).
|
:- use_module(library(error)).
|
||||||
|
|
||||||
|
%% between(+Lower, +Upper, ?X).
|
||||||
|
%
|
||||||
|
% Given Lower and Upper are both integer numbers, true iff X is an integer so that _Lower =< X =< Upper_.
|
||||||
|
% Can be used both to check if X is between Lower and Upper or to generate an integer between
|
||||||
|
% Lower and Upper.
|
||||||
|
%
|
||||||
|
% Examples:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- between(10, 20, 15).
|
||||||
|
% true.
|
||||||
|
% ?- between(10, 20, 25).
|
||||||
|
% false.
|
||||||
|
% ?- between(3, 5, X).
|
||||||
|
% X = 3
|
||||||
|
% ; X = 4
|
||||||
|
% ; X = 5.
|
||||||
|
% ```
|
||||||
between(Lower, Upper, X) :-
|
between(Lower, Upper, X) :-
|
||||||
must_be(integer, Lower),
|
must_be(integer, Lower),
|
||||||
must_be(integer, Upper),
|
must_be(integer, Upper),
|
||||||
can_be(integer, X),
|
can_be(integer, X),
|
||||||
between_(Lower, Upper, X).
|
( nonvar(X) ->
|
||||||
|
Lower =< X,
|
||||||
|
X =< Upper
|
||||||
|
; Lower =< Upper,
|
||||||
|
between_(Lower, Upper, X)
|
||||||
|
).
|
||||||
|
|
||||||
between_(Lower, Upper, Lower) :-
|
between_(Lower, Upper, Lower1) :-
|
||||||
Lower =< Upper.
|
Lower < Upper,
|
||||||
between_(Lower1, Upper, X) :-
|
!,
|
||||||
Lower1 < Upper,
|
( Lower1 = Lower
|
||||||
Lower2 is Lower1 + 1,
|
; Lower0 is Lower + 1,
|
||||||
between_(Lower2, Upper, X).
|
between_(Lower0, Upper, Lower1)
|
||||||
|
).
|
||||||
|
between_(Lower, Lower, Lower).
|
||||||
|
|
||||||
enumerate_nats(I, I).
|
enumerate_nats(I, I).
|
||||||
enumerate_nats(I0, N) :-
|
enumerate_nats(I0, N) :-
|
||||||
I1 is I0 + 1,
|
I1 is I0 + 1,
|
||||||
enumerate_nats(I1, N).
|
enumerate_nats(I1, N).
|
||||||
|
|
||||||
|
%% gen_nat(?N)
|
||||||
|
%
|
||||||
|
% True iff N is a natural number.
|
||||||
gen_nat(N) :-
|
gen_nat(N) :-
|
||||||
can_be(integer, N),
|
can_be(integer, N),
|
||||||
( var(N) -> enumerate_nats(0, N)
|
( var(N) -> enumerate_nats(0, N)
|
||||||
@@ -37,6 +72,9 @@ enumerate_ints(I0, N) :-
|
|||||||
I1 is I0 + 1,
|
I1 is I0 + 1,
|
||||||
enumerate_ints(I1, N).
|
enumerate_ints(I1, N).
|
||||||
|
|
||||||
|
%% gen_int(?N)
|
||||||
|
%
|
||||||
|
% True iff N is an integer.
|
||||||
gen_int(N) :-
|
gen_int(N) :-
|
||||||
can_be(integer, N),
|
can_be(integer, N),
|
||||||
( var(N) -> enumerate_ints(0, N)
|
( var(N) -> enumerate_ints(0, N)
|
||||||
@@ -48,9 +86,24 @@ repeat_integer(N) :-
|
|||||||
repeat_integer(N0) :-
|
repeat_integer(N0) :-
|
||||||
N0 > 0, N1 is N0 - 1, repeat_integer(N1).
|
N0 > 0, N1 is N0 - 1, repeat_integer(N1).
|
||||||
|
|
||||||
|
%% repeat(+N)
|
||||||
|
%
|
||||||
|
% Succeeds N times. This predicate is only included for compatibility and *should not be used*
|
||||||
|
% because it lacks a declarative interpretation.
|
||||||
repeat(N) :-
|
repeat(N) :-
|
||||||
must_be(integer, N), repeat_integer(N).
|
must_be(integer, N), repeat_integer(N).
|
||||||
|
|
||||||
|
%% numlist(?Upper, ?List)
|
||||||
|
%
|
||||||
|
% True iff List is the list of integers _[1, ..., Upper]_. Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- numlist(X, Y).
|
||||||
|
% X = 1, Y = [1],
|
||||||
|
% ; X = 2, Y = [1,2]
|
||||||
|
% ; X = 3, Y = [1,2,3]
|
||||||
|
% ; ... .
|
||||||
|
% ```
|
||||||
numlist(Upper, List) :-
|
numlist(Upper, List) :-
|
||||||
( integer(Upper) -> findall(X, between(1, Upper, X), List)
|
( integer(Upper) -> findall(X, between(1, Upper, X), List)
|
||||||
; List = [_|_], length(List, Upper), findall(X, between(1, Upper, X), List)
|
; List = [_|_], length(List, Upper), findall(X, between(1, Upper, X), List)
|
||||||
@@ -99,5 +152,14 @@ gen_ints(L, U) :-
|
|||||||
),
|
),
|
||||||
L =< U.
|
L =< U.
|
||||||
|
|
||||||
|
%% numlist(?Lower, ?Upper, ?List).
|
||||||
|
%
|
||||||
|
% True iff List is a list of the form _[Lower, ..., Upper]_.
|
||||||
|
% Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- numlist(5, 10, X).
|
||||||
|
% X = [5,6,7,8,9,10].
|
||||||
|
% ```
|
||||||
numlist(Lower, Upper, List) :-
|
numlist(Lower, Upper, List) :-
|
||||||
gen_ints(Lower, Upper), findall(X, between(Lower, Upper, X), List).
|
gen_ints(Lower, Upper), findall(X, between(Lower, Upper, X), List).
|
||||||
2214
src/lib/builtins.pl
Normal file
2214
src/lib/builtins.pl
Normal file
File diff suppressed because it is too large
Load Diff
395
src/lib/charsio.pl
Normal file
395
src/lib/charsio.pl
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
/** High-level predicates to work with chars and strings
|
||||||
|
|
||||||
|
This module contains predicates that relates strings of chars
|
||||||
|
to other representations, as well as high-level predicates to
|
||||||
|
read and write chars.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(charsio, [char_type/2,
|
||||||
|
chars_utf8bytes/2,
|
||||||
|
get_single_char/1,
|
||||||
|
get_n_chars/3,
|
||||||
|
get_line_to_chars/3,
|
||||||
|
read_from_chars/2,
|
||||||
|
read_term_from_chars/3,
|
||||||
|
write_term_to_chars/3,
|
||||||
|
chars_base64/3]).
|
||||||
|
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(iso_ext)).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(between)).
|
||||||
|
:- use_module(library(iso_ext), [partial_string/1,partial_string/3]).
|
||||||
|
|
||||||
|
fabricate_var_name(VarType, VarName, N) :-
|
||||||
|
char_code('A', AC),
|
||||||
|
LN is N mod 26 + AC,
|
||||||
|
char_code(LC, LN),
|
||||||
|
NN is N // 26,
|
||||||
|
( NN =:= 0 ->
|
||||||
|
( VarType == fabricated ->
|
||||||
|
atom_chars(VarName, ['_', LC])
|
||||||
|
; VarType == numbervars ->
|
||||||
|
atom_chars(VarName, [LC])
|
||||||
|
)
|
||||||
|
; number_chars(NN, NNChars),
|
||||||
|
( VarType == fabricated ->
|
||||||
|
atom_chars(VarName, ['_', LC | NNChars])
|
||||||
|
; VarType == numbervars ->
|
||||||
|
atom_chars(VarName, [LC | NNChars])
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
|
var_list_contains_name([VarName = _ | VarList], VarName0) :-
|
||||||
|
( VarName == VarName0 -> true
|
||||||
|
; var_list_contains_name(VarList, VarName0)
|
||||||
|
).
|
||||||
|
|
||||||
|
var_list_contains_variable([_ = Var | VarList], Var0) :-
|
||||||
|
( Var == Var0 -> true
|
||||||
|
; var_list_contains_variable(VarList, Var0)
|
||||||
|
).
|
||||||
|
|
||||||
|
make_new_var_name(VarType, V, VarName, N, N1, VarList) :-
|
||||||
|
fabricate_var_name(VarType, VarName0, N),
|
||||||
|
( var_list_contains_name(VarList, VarName0) ->
|
||||||
|
N0 is N + 1,
|
||||||
|
make_new_var_name(VarType, V, VarName, N0, N1, VarList)
|
||||||
|
; VarName = VarName0,
|
||||||
|
N1 is N + 1
|
||||||
|
).
|
||||||
|
|
||||||
|
extend_var_list(Vars, VarList, NewVarList, VarType) :-
|
||||||
|
extend_var_list_(Vars, 0, VarList, NewVarList0, VarType),
|
||||||
|
append(VarList, NewVarList0, NewVarList).
|
||||||
|
|
||||||
|
extend_var_list_([], _, _, [], _).
|
||||||
|
extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
|
||||||
|
( var_list_contains_variable(VarList, V) ->
|
||||||
|
extend_var_list_(Vs, N, VarList, NewVarList, VarType)
|
||||||
|
; make_new_var_name(VarType, V, VarName, N, N1, VarList),
|
||||||
|
NewVarList = [VarName = V | NewVarList0],
|
||||||
|
extend_var_list_(Vs, N1, VarList, NewVarList0, VarType)
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
%% char_type(?Char, ?Type).
|
||||||
|
%
|
||||||
|
% Type is one of the categories that Char fits in.
|
||||||
|
% At least one of the arguments must be ground.
|
||||||
|
% Possible categories are:
|
||||||
|
%
|
||||||
|
% - `alnum`
|
||||||
|
% - `alpha`
|
||||||
|
% - `alphabetic`
|
||||||
|
% - `alphanumeric`
|
||||||
|
% - `ascii`
|
||||||
|
% - `ascii_graphic`
|
||||||
|
% - `ascii_punctuation`
|
||||||
|
% - `binary_digit`
|
||||||
|
% - `control`
|
||||||
|
% - `decimal_digit`
|
||||||
|
% - `exponent`
|
||||||
|
% - `graphic`
|
||||||
|
% - `graphic_token`
|
||||||
|
% - `hexadecimal_digit`
|
||||||
|
% - `layout`
|
||||||
|
% - `lower`
|
||||||
|
% - `meta`
|
||||||
|
% - `numeric`
|
||||||
|
% - `octal_digit`
|
||||||
|
% - `octet`
|
||||||
|
% - `prolog`
|
||||||
|
% - `sign`
|
||||||
|
% - `solo`
|
||||||
|
% - `symbolic_control`
|
||||||
|
% - `symbolic_hexadecimal`
|
||||||
|
% - `upper`
|
||||||
|
% - `lower(Lower)`
|
||||||
|
% - `upper(Upper)`
|
||||||
|
% - `whitespace`
|
||||||
|
%
|
||||||
|
% An example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- char_type(a, Type).
|
||||||
|
% Type = alnum
|
||||||
|
% ; Type = alpha
|
||||||
|
% ; Type = alphabetic
|
||||||
|
% ; Type = alphanumeric
|
||||||
|
% ; Type = ascii
|
||||||
|
% ; Type = ascii_graphic
|
||||||
|
% ; Type = hexadecimal_digit
|
||||||
|
% ; Type = lower
|
||||||
|
% ; Type = octet
|
||||||
|
% ; Type = prolog
|
||||||
|
% ; Type = symbolic_control
|
||||||
|
% ; Type = lower("a")
|
||||||
|
% ; Type = upper("A")
|
||||||
|
% ; false.
|
||||||
|
% ```
|
||||||
|
%
|
||||||
|
% Note that uppercase and lowercase transformations use a string. This is because
|
||||||
|
% some characters do not map 1:1 between lowercase and uppercase.
|
||||||
|
char_type(Char, Type) :-
|
||||||
|
can_be(character, Char),
|
||||||
|
( \+ ctype(Type) ->
|
||||||
|
domain_error(char_type, Type, char_type/2)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
( ground(Char) ->
|
||||||
|
ctype(Type),
|
||||||
|
'$char_type'(Char, Type)
|
||||||
|
; ground(Type) ->
|
||||||
|
ccode(Code),
|
||||||
|
char_code(Char, Code),
|
||||||
|
'$char_type'(Char, Type)
|
||||||
|
; must_be(character, Char)
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
% 0xD800 to 0xDFFF are surrogate code points used by UTF-16.
|
||||||
|
|
||||||
|
ccode(Code) :- between(0, 0xD7FF, Code).
|
||||||
|
ccode(Code) :- between(0xE000, 0x10FFFF, Code).
|
||||||
|
|
||||||
|
ctype(alnum).
|
||||||
|
ctype(alpha).
|
||||||
|
ctype(alphabetic).
|
||||||
|
ctype(alphanumeric).
|
||||||
|
ctype(ascii).
|
||||||
|
ctype(ascii_graphic).
|
||||||
|
ctype(ascii_punctuation).
|
||||||
|
ctype(binary_digit).
|
||||||
|
ctype(control).
|
||||||
|
ctype(decimal_digit).
|
||||||
|
ctype(exponent).
|
||||||
|
ctype(graphic).
|
||||||
|
ctype(graphic_token).
|
||||||
|
ctype(hexadecimal_digit).
|
||||||
|
ctype(layout).
|
||||||
|
ctype(lower).
|
||||||
|
ctype(meta).
|
||||||
|
ctype(numeric).
|
||||||
|
ctype(octal_digit).
|
||||||
|
ctype(octet).
|
||||||
|
ctype(prolog).
|
||||||
|
ctype(sign).
|
||||||
|
ctype(solo).
|
||||||
|
ctype(symbolic_control).
|
||||||
|
ctype(symbolic_hexadecimal).
|
||||||
|
ctype(lower(_)).
|
||||||
|
ctype(upper(_)).
|
||||||
|
ctype(upper).
|
||||||
|
ctype(whitespace).
|
||||||
|
|
||||||
|
|
||||||
|
%% get_single_char(-Char).
|
||||||
|
%
|
||||||
|
% Gets a single char from the current input stream.
|
||||||
|
get_single_char(C) :-
|
||||||
|
( var(C) -> '$get_single_char'(C)
|
||||||
|
; atom_length(C, 1) -> '$get_single_char'(C)
|
||||||
|
; type_error(in_character, C, get_single_char/1)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% 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:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- read_from_chars("f(x,y).", X).
|
||||||
|
% X = f(x,y).
|
||||||
|
% ```
|
||||||
|
read_from_chars(Chars, Term) :-
|
||||||
|
must_be(chars, Chars),
|
||||||
|
'$read_from_chars'(Chars, Term0),
|
||||||
|
Term = Term0.
|
||||||
|
|
||||||
|
%% 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`.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- read_term_from_chars("f(X,y).", T, [variable_names(['X'=X])]).
|
||||||
|
% T = f(X,y).
|
||||||
|
% ```
|
||||||
|
read_term_from_chars(Chars, Term, Options) :-
|
||||||
|
must_be(chars, Chars),
|
||||||
|
builtins:parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term_from_chars/3),
|
||||||
|
'$read_term_from_chars'(Chars, Term0, Singletons, Variables, VariableNames),
|
||||||
|
Term = Term0.
|
||||||
|
|
||||||
|
%% write_term_to_chars(+Term, +Options, -Chars).
|
||||||
|
%
|
||||||
|
% Given a Term which is a Prolog term and a set of options, Chars is
|
||||||
|
% string representation of that term. 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.
|
||||||
|
% * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses.
|
||||||
|
% If N = 0 (default), there's no limit.
|
||||||
|
% * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false.
|
||||||
|
% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog syntax, are quoted. Default is false.
|
||||||
|
% * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`.
|
||||||
|
% * `double_quotes(+Boolean)` if true, strings are printed in double quotes rather than with list notation. Default is false.
|
||||||
|
write_term_to_chars(_, Options, _) :-
|
||||||
|
var(Options), instantiation_error(write_term_to_chars/3).
|
||||||
|
write_term_to_chars(Term, Options, Chars) :-
|
||||||
|
builtins:parse_write_options(Options,
|
||||||
|
[DoubleQuotes, IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames],
|
||||||
|
write_term_to_chars/3),
|
||||||
|
( nonvar(Chars) ->
|
||||||
|
throw(error(uninstantiation_error(Chars), write_term_to_chars/3))
|
||||||
|
;
|
||||||
|
true
|
||||||
|
),
|
||||||
|
term_variables(Term, Vars),
|
||||||
|
extend_var_list(Vars, VNNames, NewVarNames, numbervars),
|
||||||
|
'$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth, DoubleQuotes).
|
||||||
|
|
||||||
|
% Encodes Ch character to list of Bytes.
|
||||||
|
char_utf8bytes(Ch, Bytes) :-
|
||||||
|
char_code(Ch, Code),
|
||||||
|
phrase(code_to_utf8(Code), Bytes).
|
||||||
|
|
||||||
|
code_to_utf8(Code) --> {Code @< 0x80}, [Code], !.
|
||||||
|
code_to_utf8(Code) --> {Code @< 0x800}, encode(Code, 0xC0, 2), !.
|
||||||
|
code_to_utf8(Code) --> {Code @< 0x10000}, encode(Code, 0xE0, 3), !.
|
||||||
|
code_to_utf8(Code) --> {Code @< 0x110000}, encode(Code, 0xF0, 4), !.
|
||||||
|
|
||||||
|
encode(_, _, 0) --> !.
|
||||||
|
encode(Code, Prefix, Nb) -->
|
||||||
|
{ Nb1 is Nb - 1, Byte is Prefix \/ ((Code >> (6 * Nb1)) /\ 0x3F) },
|
||||||
|
[Byte], encode(Code, 0x80, Nb1).
|
||||||
|
|
||||||
|
% Maps characters and UTF-8 bytes.
|
||||||
|
% If Cs is a variable, parses Bs as a list of UTF-8 bytes.
|
||||||
|
% Otherwise, transform the list of characters Cs to UTF-8 bytes.
|
||||||
|
|
||||||
|
%% chars_utf8bytes(?Chars, ?Bytes).
|
||||||
|
%
|
||||||
|
% Maps a string made of chars with a list of UTF-8 bytes. Some examples:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- chars_utf8bytes("Prolog", X).
|
||||||
|
% X = [80,114,111,108,111,103].
|
||||||
|
% ?- chars_utf8bytes(X, [226, 136, 145]).
|
||||||
|
% X = "∑".
|
||||||
|
% ```
|
||||||
|
chars_utf8bytes(Cs, Bs) :-
|
||||||
|
var(Cs), must_be(list, Bs) ->
|
||||||
|
once(phrase(decode_utf8(Cs), Bs))
|
||||||
|
; (must_be(list, Cs),
|
||||||
|
maplist(must_be(atom), Cs),
|
||||||
|
maplist(char_utf8bytes, Cs, Bss),
|
||||||
|
append(Bss, Bs)).
|
||||||
|
|
||||||
|
decode_utf8([]) --> [].
|
||||||
|
decode_utf8(Chars) --> leading(Nb, Code), continuation(Code, Chars, Nb).
|
||||||
|
|
||||||
|
leading(1, Byte) --> [Byte], {Byte /\ 0x80 =:= 0}.
|
||||||
|
leading(2, Code) --> [Byte], {Byte /\ 0xE0 =:= 0xC0, Code is Byte - 0xC0}.
|
||||||
|
leading(3, Code) --> [Byte], {Byte /\ 0xF0 =:= 0xE0, Code is Byte - 0xE0}.
|
||||||
|
leading(4, Code) --> [Byte], {Byte /\ 0xF8 =:= 0xF0, Code is Byte - 0xF0}.
|
||||||
|
leading(1, 0xFFFD) --> [_]. % invalid first byte
|
||||||
|
|
||||||
|
continuation(Code, [H|T], 1) --> {char_code(H, Code)}, decode_utf8(T).
|
||||||
|
continuation(Code, Chars, Nb) --> [Byte],
|
||||||
|
{Nb1 is Nb - 1, Byte /\ 0xC0 =:= 0x80, NextCode is (Code << 6) \/ (Byte - 0x80)},
|
||||||
|
continuation(NextCode, Chars, Nb1).
|
||||||
|
|
||||||
|
% invalid continuation byte
|
||||||
|
% each remaining continuation byte (if any) will raise 0xFFFD too
|
||||||
|
continuation(_, ['\xFFFD\'|T], _) --> [_], decode_utf8(T).
|
||||||
|
|
||||||
|
%% get_line_to_chars(+Stream, -Chars, +InitialChars).
|
||||||
|
%
|
||||||
|
% Reads chars from stream Stream until it finds a `\n` character.
|
||||||
|
% InitialChars will be appended at the end of Chars
|
||||||
|
get_line_to_chars(Stream, Cs0, Cs) :-
|
||||||
|
'$get_n_chars'(Stream, 1, Char), % this also works for binary streams
|
||||||
|
( Char == [] -> Cs0 = Cs
|
||||||
|
; Char = [C],
|
||||||
|
Cs0 = [C|Rest],
|
||||||
|
( C == '\n' -> Rest = Cs
|
||||||
|
; get_line_to_chars(Stream, Rest, Cs)
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% get_n_chars(+Stream, ?N, -Chars).
|
||||||
|
%
|
||||||
|
% Read N chars from stream Stream. N can be an integer, in that case
|
||||||
|
% only N chars are read, or a variable, unifying N with the number of chars
|
||||||
|
% read until it found EOF.
|
||||||
|
get_n_chars(Stream, N, Cs) :-
|
||||||
|
can_be(integer, N),
|
||||||
|
( var(N) ->
|
||||||
|
get_to_eof(Stream, Cs),
|
||||||
|
length(Cs, N)
|
||||||
|
; N >= 0,
|
||||||
|
'$get_n_chars'(Stream, N, Cs)
|
||||||
|
).
|
||||||
|
|
||||||
|
get_n_chars_wrapper(Stream, N, Cs) :-
|
||||||
|
'$get_n_chars'(Stream, N, Cs).
|
||||||
|
|
||||||
|
get_to_eof(Stream, Cs) :-
|
||||||
|
catch(get_n_chars_wrapper(Stream, 512, Cs0),
|
||||||
|
error(syntax_error(unexpected_end_of_file), _),
|
||||||
|
Cs0 = []),
|
||||||
|
( Cs0 == [] -> Cs = []
|
||||||
|
; partial_string(Cs0, Cs, Rest),
|
||||||
|
get_to_eof(Stream, Rest)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% chars_base64(?Chars, ?Base64, +Options).
|
||||||
|
%
|
||||||
|
% Relation between a list of characters Cs and its Base64 encoding Bs,
|
||||||
|
% also a list of characters.
|
||||||
|
%
|
||||||
|
% At least one of the arguments must be instantiated.
|
||||||
|
%
|
||||||
|
% Options are:
|
||||||
|
%
|
||||||
|
% - `padding(Boolean)`
|
||||||
|
% Whether to use padding: true (the default) or false.
|
||||||
|
% - `charset(C)`
|
||||||
|
% Either 'standard' (RFC 4648 §4, the default) or 'url' (RFC 4648 §5).
|
||||||
|
%
|
||||||
|
% Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- chars_base64("hello", Bs, []).
|
||||||
|
% Bs = "aGVsbG8=".
|
||||||
|
% ```
|
||||||
|
|
||||||
|
chars_base64(Cs, Bs, Options) :-
|
||||||
|
must_be(list, Options),
|
||||||
|
( member(O, Options), var(O) ->
|
||||||
|
instantiation_error(chars_base64/3)
|
||||||
|
; ( member(padding(Padding), Options) -> true
|
||||||
|
; Padding = true
|
||||||
|
),
|
||||||
|
( member(charset(Charset), Options) -> true
|
||||||
|
; Charset = standard
|
||||||
|
)
|
||||||
|
),
|
||||||
|
must_be(boolean, Padding),
|
||||||
|
must_be(atom, Charset),
|
||||||
|
( member(Charset, [standard,url]) -> true
|
||||||
|
; domain_error(charset, Charset, chars_base64/3)
|
||||||
|
),
|
||||||
|
( var(Cs) ->
|
||||||
|
must_be(chars, Bs),
|
||||||
|
'$chars_base64'(Cs, Bs, Padding, Charset)
|
||||||
|
; must_be(chars, Cs),
|
||||||
|
( '$first_non_octet'(Cs, N) ->
|
||||||
|
domain_error(octet_character, N, chars_base64/3)
|
||||||
|
; '$chars_base64'(Cs, Bs, Padding, Charset)
|
||||||
|
)
|
||||||
|
).
|
||||||
@@ -1,10 +1,29 @@
|
|||||||
/* CLP(B): Constraint Logic Programming over Boolean Variables
|
/* CLP(B): Constraint Logic Programming over Boolean Variables
|
||||||
|
|
||||||
Copyright (C): 2019 Markus Triska
|
Author: Markus Triska
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
E-mail: triska@metalevel.at
|
E-mail: triska@metalevel.at
|
||||||
WWW: http://www.metalevel.at
|
WWW: https://www.metalevel.at
|
||||||
|
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
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use, copy,
|
||||||
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -31,9 +50,10 @@
|
|||||||
:- use_module(library(atts)).
|
:- use_module(library(atts)).
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
:- use_module(library(iso_ext)).
|
:- use_module(library(iso_ext)).
|
||||||
|
:- use_module(library(random)).
|
||||||
:- use_module(library(pairs)).
|
:- use_module(library(pairs)).
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
:- use_module(library(error), []).
|
:- use_module(library(error), [domain_error/3, type_error/3]).
|
||||||
|
|
||||||
:- attribute
|
:- attribute
|
||||||
clpb/1,
|
clpb/1,
|
||||||
@@ -48,17 +68,6 @@
|
|||||||
Compatibility predicates.
|
Compatibility predicates.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
group_pairs_by_key([], []).
|
|
||||||
group_pairs_by_key([M-N|T0], [M-[N|TN]|T]) :-
|
|
||||||
same_key(M, T0, TN, T1),
|
|
||||||
group_pairs_by_key(T1, T).
|
|
||||||
|
|
||||||
same_key(M0, [M-N|T0], [N|TN], T) :-
|
|
||||||
M0 == M,
|
|
||||||
!,
|
|
||||||
same_key(M, T0, TN, T).
|
|
||||||
same_key(_, L, [], L).
|
|
||||||
|
|
||||||
must_be(What, Term) :- must_be(What, unknown(Term)-1, Term).
|
must_be(What, Term) :- must_be(What, unknown(Term)-1, Term).
|
||||||
|
|
||||||
must_be(acyclic, Where, Term) :- !,
|
must_be(acyclic, Where, Term) :- !,
|
||||||
@@ -97,15 +106,49 @@ instantiation_error(_, Goal-Arg) :-
|
|||||||
domain_error(Expectation, Term) :-
|
domain_error(Expectation, Term) :-
|
||||||
domain_error(Expectation, Term, unknown(Term)-1).
|
domain_error(Expectation, Term, unknown(Term)-1).
|
||||||
|
|
||||||
domain_error(Expectation, Term, Goal-Arg) :-
|
|
||||||
throw(error(domain_error(Expectation, Term), domain_error(Goal, Arg, Expectation, Term))).
|
|
||||||
|
|
||||||
|
|
||||||
type_error(Expectation, Term) :-
|
type_error(Expectation, Term) :-
|
||||||
type_error(Expectation, Term, unknown(Term)-1).
|
type_error(Expectation, Term, unknown(Term)-1).
|
||||||
|
|
||||||
type_error(Expectation, Term, Goal-Arg) :-
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
throw(error(type_error(Expectation, Term), type_error(Goal, Arg, Expectation, Term))).
|
Compatibility predicates.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
:- meta_predicate(include(1, ?, ?)).
|
||||||
|
|
||||||
|
include(_, [], []).
|
||||||
|
include(Goal, [L|Ls0], Ls) :-
|
||||||
|
( call(Goal, L) ->
|
||||||
|
Ls = [L|Rest]
|
||||||
|
; Ls = Rest
|
||||||
|
),
|
||||||
|
include(Goal, Ls0, Rest).
|
||||||
|
|
||||||
|
:- meta_predicate(exclude(1, ?, ?)).
|
||||||
|
|
||||||
|
exclude(_, [], []).
|
||||||
|
exclude(Goal, [L|Ls0], Ls) :-
|
||||||
|
( call(Goal, L) ->
|
||||||
|
Ls = Rest
|
||||||
|
; Ls = [L|Rest]
|
||||||
|
),
|
||||||
|
exclude(Goal, Ls0, Rest).
|
||||||
|
|
||||||
|
:- meta_predicate(partition(2,?,?,?,?)).
|
||||||
|
|
||||||
|
partition(_, [], [], [], []).
|
||||||
|
partition(Pred, [H|T], L, E, G) :-
|
||||||
|
call(Pred, H, Diff),
|
||||||
|
partition_(Diff, H, Pred, T, L, E, G).
|
||||||
|
|
||||||
|
partition_(<, H, Pred, T, [H|Rest], E, G) :-
|
||||||
|
partition(Pred, T, Rest, E, G).
|
||||||
|
partition_(=, H, Pred, T, L, [H|Rest], G) :-
|
||||||
|
partition(Pred, T, L, Rest, G).
|
||||||
|
partition_(>, H, Pred, T, L, E, [H|Rest]) :-
|
||||||
|
partition(Pred, T, L, E, Rest).
|
||||||
|
|
||||||
|
:- meta_predicate(partition(1,?,?,?)).
|
||||||
|
|
||||||
partition(Pred, Ls0, As, Bs) :-
|
partition(Pred, Ls0, As, Bs) :-
|
||||||
include(Pred, Ls0, As),
|
include(Pred, Ls0, As),
|
||||||
@@ -121,6 +164,262 @@ goal_expansion(del_attr(Var, Module), (var(Var) -> put_atts(Var, -Access);true))
|
|||||||
Access =.. [Module,_].
|
Access =.. [Module,_].
|
||||||
|
|
||||||
|
|
||||||
|
/** Constraint Logic Programming over Boolean variables
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
This library provides CLP(B), Constraint Logic Programming over
|
||||||
|
Boolean variables. It can be used to model and solve combinatorial
|
||||||
|
problems such as verification, allocation and covering tasks.
|
||||||
|
|
||||||
|
CLP(B) is an instance of the general CLP(_X_) scheme,
|
||||||
|
extending logic programming with reasoning over specialised domains.
|
||||||
|
|
||||||
|
The implementation is based on reduced and ordered Binary Decision
|
||||||
|
Diagrams (BDDs).
|
||||||
|
|
||||||
|
Benchmarks and usage examples of this library are available from:
|
||||||
|
[*https://www.metalevel.at/clpb/*](https://www.metalevel.at/clpb/)
|
||||||
|
|
||||||
|
## Boolean expressions
|
||||||
|
|
||||||
|
A _Boolean expression_ is one of:
|
||||||
|
|
||||||
|
| `0` | false |
|
||||||
|
| `1` | true |
|
||||||
|
| _variable_ | unknown truth value |
|
||||||
|
| _atom_ | universally quantified variable |
|
||||||
|
| `~` _Expr_ | logical NOT |
|
||||||
|
| _Expr_ `+` _Expr_ | logical OR |
|
||||||
|
| _Expr_ `*` _Expr_ | logical AND |
|
||||||
|
| _Expr_ `#` _Expr_ | exclusive OR |
|
||||||
|
| _Var_ `^` _Expr_ | existential quantification |
|
||||||
|
| _Expr_ `=:=` _Expr_ | equality |
|
||||||
|
| _Expr_ `=\=` _Expr_ | disequality (same as #) |
|
||||||
|
| _Expr_ `=<` _Expr_ | less or equal (implication) |
|
||||||
|
| _Expr_ `>=` _Expr_ | greater or equal |
|
||||||
|
| _Expr_ `<` _Expr_ | less than |
|
||||||
|
| _Expr_ `>` _Expr_ | greater than |
|
||||||
|
| `card(Is,Exprs)` | cardinality constraint (_see below_) |
|
||||||
|
| `+(Exprs)` | n-fold disjunction (_see below_) |
|
||||||
|
| `*(Exprs)` | n-fold conjunction (_see below_) |
|
||||||
|
|
||||||
|
where _Expr_ again denotes a Boolean expression.
|
||||||
|
|
||||||
|
The Boolean expression `card(Is,Exprs)` is true iff the number of true
|
||||||
|
expressions in the list `Exprs` is a member of the list `Is` of
|
||||||
|
integers and integer ranges of the form `From-To`. For example, to
|
||||||
|
state that precisely two of the three variables `X`, `Y` and `Z` are
|
||||||
|
`true`, you can use `sat(card([2],[X,Y,Z]))`.
|
||||||
|
|
||||||
|
`+(Exprs)` and `*(Exprs)` denote, respectively, the disjunction and
|
||||||
|
conjunction of all elements in the list `Exprs` of Boolean
|
||||||
|
expressions.
|
||||||
|
|
||||||
|
Atoms denote parametric values that are universally quantified. All
|
||||||
|
universal quantifiers appear implicitly in front of the entire
|
||||||
|
expression. In residual goals, universally quantified variables always
|
||||||
|
appear on the right-hand side of equations. Therefore, they can be
|
||||||
|
used to express functional dependencies on input variables.
|
||||||
|
|
||||||
|
## Interface predicates
|
||||||
|
|
||||||
|
The most frequently used CLP(B) predicates are:
|
||||||
|
|
||||||
|
* `sat(+Expr)`
|
||||||
|
True iff the Boolean expression Expr is satisfiable.
|
||||||
|
|
||||||
|
* `taut(+Expr, -T)`
|
||||||
|
If Expr is a tautology with respect to the posted constraints, succeeds
|
||||||
|
with *T = 1*. If Expr cannot be satisfied, succeeds with *T = 0*.
|
||||||
|
Otherwise, it fails.
|
||||||
|
|
||||||
|
* `labeling(+Vs)`
|
||||||
|
Assigns truth values to the variables Vs such that all constraints
|
||||||
|
are satisfied.
|
||||||
|
|
||||||
|
The unification of a CLP(B) variable _X_ with a term _T_ is equivalent
|
||||||
|
to posting the constraint sat(X=:=T).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Here is an example session with a few queries and their answers:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module(library(clpb)).
|
||||||
|
true.
|
||||||
|
|
||||||
|
?- sat(X*Y).
|
||||||
|
X = 1, Y = 1.
|
||||||
|
|
||||||
|
?- sat(X * ~X).
|
||||||
|
false.
|
||||||
|
|
||||||
|
?- taut(X * ~X, T).
|
||||||
|
T = 0, clpb:sat(X=:=X).
|
||||||
|
|
||||||
|
?- sat(X^Y^(X+Y)).
|
||||||
|
clpb:sat(X=:=X), clpb:sat(Y=:=Y).
|
||||||
|
|
||||||
|
?- sat(X*Y + X*Z), labeling([X,Y,Z]).
|
||||||
|
X = 1, Y = 0, Z = 1
|
||||||
|
; X = 1, Y = 1, Z = 0
|
||||||
|
; X = 1, Y = 1, Z = 1.
|
||||||
|
|
||||||
|
?- sat(X =< Y), sat(Y =< Z), taut(X =< Z, T).
|
||||||
|
T = 1, clpb:sat(X=:=X*Y), clpb:sat(Y=:=Y*Z).
|
||||||
|
|
||||||
|
?- sat(1#X#a#b).
|
||||||
|
clpb:sat(X=:=a#b).
|
||||||
|
```
|
||||||
|
|
||||||
|
The pending residual goals constrain remaining variables to Boolean
|
||||||
|
expressions and are declaratively equivalent to the original query.
|
||||||
|
The last example illustrates that when applicable, remaining variables
|
||||||
|
are expressed as functions of universally quantified variables.
|
||||||
|
|
||||||
|
## Obtaining BDDs
|
||||||
|
|
||||||
|
By default, CLP(B) residual goals appear in (approximately) algebraic
|
||||||
|
normal form (ANF). This projection is often computationally expensive.
|
||||||
|
We can assert `clpb:clpb_residuals(bdd)` to see the BDD representation
|
||||||
|
of all constraints. This results in faster projection to residual
|
||||||
|
goals, and is also useful for learning more about BDDs. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- asserta(clpb:clpb_residuals(bdd)).
|
||||||
|
true.
|
||||||
|
|
||||||
|
?- sat(X#Y).
|
||||||
|
node(3)- (v(X, 0)->node(2);node(1)),
|
||||||
|
node(1)- (v(Y, 1)->true;false),
|
||||||
|
node(2)- (v(Y, 1)->false;true).
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this representation cannot be pasted back on the toplevel,
|
||||||
|
and its details are subject to change. Use copy_term/3 to obtain
|
||||||
|
such answers as Prolog terms.
|
||||||
|
|
||||||
|
The variable order of the BDD is determined by the order in which the
|
||||||
|
variables first appear in constraints. To obtain different orders,
|
||||||
|
we can for example use:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- sat(+[1,Y,X]), sat(X#Y).
|
||||||
|
node(3)- (v(Y, 0)->node(2);node(1)),
|
||||||
|
node(1)- (v(X, 1)->true;false),
|
||||||
|
node(2)- (v(X, 1)->false;true).
|
||||||
|
```
|
||||||
|
|
||||||
|
## Enabling monotonic CLP(B)
|
||||||
|
|
||||||
|
In the default execution mode, CLP(B) constraints are _not_ monotonic.
|
||||||
|
This means that _adding_ constraints can yield new solutions. For
|
||||||
|
example:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- sat(X=:=1), X = 1+0.
|
||||||
|
false.
|
||||||
|
|
||||||
|
?- X = 1+0, sat(X=:=1), X = 1+0.
|
||||||
|
X = 1+0.
|
||||||
|
```
|
||||||
|
|
||||||
|
This behaviour is highly problematic from a logical point of view, and
|
||||||
|
it may render [*declarative
|
||||||
|
debugging*](https://www.metalevel.at/prolog/debugging)
|
||||||
|
techniques inapplicable.
|
||||||
|
|
||||||
|
Assert `clpb:monotonic` to make CLP(B) *monotonic*. If this mode is
|
||||||
|
enabled, then you must wrap CLP(B) variables with the functor
|
||||||
|
`v/1`. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- asserta(clpb:monotonic).
|
||||||
|
true.
|
||||||
|
|
||||||
|
?- sat(v(X)=:=1#1).
|
||||||
|
X = 0.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example: Pigeons
|
||||||
|
|
||||||
|
In this example, we are attempting to place _I_ pigeons into _J_ holes
|
||||||
|
in such a way that each hole contains at most one pigeon. One
|
||||||
|
interesting property of this task is that it can be formulated using
|
||||||
|
only _cardinality constraints_ (`card/2`). Another interesting aspect
|
||||||
|
is that this task has no short resolution refutations in general.
|
||||||
|
|
||||||
|
In the following, we use [*Prolog DCG
|
||||||
|
notation*](https://www.metalevel.at/prolog/dcg) to describe a
|
||||||
|
list `Cs` of CLP(B) constraints that must all be satisfied.
|
||||||
|
|
||||||
|
```
|
||||||
|
:- use_module(library(clpb)).
|
||||||
|
:- use_module(library(clpz)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
|
||||||
|
pigeon(I, J, Rows, Cs) :-
|
||||||
|
length(Rows, I), length(Row, J),
|
||||||
|
maplist(same_length(Row), Rows),
|
||||||
|
transpose(Rows, TRows),
|
||||||
|
phrase((all_cards(Rows,[1]),all_cards(TRows,[0,1])), Cs).
|
||||||
|
|
||||||
|
all_cards([], _) --> [].
|
||||||
|
all_cards([Ls|Lss], Cs) --> [card(Cs,Ls)], all_cards(Lss, Cs).
|
||||||
|
```
|
||||||
|
|
||||||
|
Example queries:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- pigeon(9, 8, Rows, Cs), sat(*(Cs)).
|
||||||
|
false.
|
||||||
|
|
||||||
|
?- pigeon(2, 3, Rows, Cs), sat(*(Cs)),
|
||||||
|
append(Rows, Vs), labeling(Vs),
|
||||||
|
maplist(portray_clause, Rows).
|
||||||
|
[0,0,1].
|
||||||
|
[0,1,0].
|
||||||
|
etc.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example: Boolean circuit
|
||||||
|
|
||||||
|
Consider a Boolean circuit that express the Boolean function =|XOR|=
|
||||||
|
with 4 =|NAND|= gates. We can model such a circuit with CLP(B)
|
||||||
|
constraints as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
:- use_module(library(clpb)).
|
||||||
|
|
||||||
|
nand_gate(X, Y, Z) :- sat(Z =:= ~(X*Y)).
|
||||||
|
|
||||||
|
xor(X, Y, Z) :-
|
||||||
|
nand_gate(X, Y, T1),
|
||||||
|
nand_gate(X, T1, T2),
|
||||||
|
nand_gate(Y, T1, T3),
|
||||||
|
nand_gate(T2, T3, Z).
|
||||||
|
```
|
||||||
|
|
||||||
|
Using universally quantified variables, we can show that the circuit
|
||||||
|
does compute =|XOR|= as intended:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- xor(x, y, Z).
|
||||||
|
clpb:sat(Z=:=x#y).
|
||||||
|
```
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
The interface predicates of this library follow the example of
|
||||||
|
[*SICStus Prolog*](https://sicstus.sics.se).
|
||||||
|
|
||||||
|
Use SICStus Prolog for higher performance in many cases.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Each CLP(B) variable belongs to exactly one BDD. Each CLP(B)
|
Each CLP(B) variable belongs to exactly one BDD. Each CLP(B)
|
||||||
variable gets an attribute (in module "clpb") of the form:
|
variable gets an attribute (in module "clpb") of the form:
|
||||||
@@ -207,6 +506,10 @@ non_monotonic(X) :-
|
|||||||
; true
|
; true
|
||||||
).
|
).
|
||||||
|
|
||||||
|
:- meta_predicate(bdd_nodes(1, ?, ?)).
|
||||||
|
:- meta_predicate(bdd_nodes_(1, ?, ?, ?)).
|
||||||
|
:- meta_predicate(with_aux(1, ?)).
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Rewriting to canonical expressions.
|
Rewriting to canonical expressions.
|
||||||
Atoms are converted to variables with a special attribute.
|
Atoms are converted to variables with a special attribute.
|
||||||
@@ -949,7 +1252,7 @@ bdd_restriction_(Node, VI, Value, Res) -->
|
|||||||
node_id(Node, ID) },
|
node_id(Node, ID) },
|
||||||
( { I0 =:= VI } ->
|
( { I0 =:= VI } ->
|
||||||
( { Value =:= 0 } -> { Res = Low }
|
( { Value =:= 0 } -> { Res = Low }
|
||||||
; { Value =:= 1 } -> { Res = High }
|
; { Res = High }
|
||||||
)
|
)
|
||||||
; { I0 > VI } -> { Res = Node }
|
; { I0 > VI } -> { Res = Node }
|
||||||
; state(G0), { get_assoc(ID, G0, Res) } -> []
|
; state(G0), { get_assoc(ID, G0, Res) } -> []
|
||||||
@@ -1125,11 +1428,9 @@ indomain(1).
|
|||||||
%
|
%
|
||||||
% Examples:
|
% Examples:
|
||||||
%
|
%
|
||||||
% ==
|
% ```
|
||||||
% ?- sat(A =< B), Vs = [A,B], sat_count(+[1|Vs], Count).
|
% ?- sat(A =< B), Vs = [A,B], sat_count(+[1|Vs], Count).
|
||||||
% Vs = [A, B],
|
% Vs = [A,B], Count = 3, clpb:sat(A=:=A*B).
|
||||||
% Count = 3,
|
|
||||||
% sat(A=:=A*B).
|
|
||||||
%
|
%
|
||||||
% ?- length(Vs, 120),
|
% ?- length(Vs, 120),
|
||||||
% sat_count(+Vs, CountOr),
|
% sat_count(+Vs, CountOr),
|
||||||
@@ -1137,7 +1438,9 @@ indomain(1).
|
|||||||
% Vs = [...],
|
% Vs = [...],
|
||||||
% CountOr = 1329227995784915872903807060280344575,
|
% CountOr = 1329227995784915872903807060280344575,
|
||||||
% CountAnd = 1.
|
% CountAnd = 1.
|
||||||
% ==
|
% ```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sat_count(Sat0, N) :-
|
sat_count(Sat0, N) :-
|
||||||
catch((parse_sat(Sat0, Sat),
|
catch((parse_sat(Sat0, Sat),
|
||||||
@@ -1248,11 +1551,15 @@ random_bindings(VNum, Node) -->
|
|||||||
{ node_var_low_high(Node, Var, Low, High),
|
{ node_var_low_high(Node, Var, Low, High),
|
||||||
bdd_count(Node, VNum, Total),
|
bdd_count(Node, VNum, Total),
|
||||||
bdd_count(Low, VNum, LCount) },
|
bdd_count(Low, VNum, LCount) },
|
||||||
( { maybe(LCount, Total) } ->
|
( { weighted_maybe(LCount, Total) } ->
|
||||||
[Var=0], random_bindings(VNum, Low)
|
[Var=0], random_bindings(VNum, Low)
|
||||||
; [Var=1], random_bindings(VNum, High)
|
; [Var=1], random_bindings(VNum, High)
|
||||||
).
|
).
|
||||||
|
|
||||||
|
weighted_maybe(K, N) :-
|
||||||
|
random_integer(0, N, X),
|
||||||
|
X < K.
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Find solutions with maximum weight.
|
Find solutions with maximum weight.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
@@ -1263,7 +1570,7 @@ random_bindings(VNum, Node) -->
|
|||||||
% linear objective function over Boolean variables Vs with integer
|
% linear objective function over Boolean variables Vs with integer
|
||||||
% coefficients Weights. This predicate assigns 0 and 1 to the
|
% coefficients Weights. This predicate assigns 0 and 1 to the
|
||||||
% variables in Vs such that all stated constraints are satisfied, and
|
% variables in Vs such that all stated constraints are satisfied, and
|
||||||
% Maximum is the maximum of sum(Weight_i*V_i) over all admissible
|
% Maximum is the maximum of `sum(Weight_i*V_i)` over all admissible
|
||||||
% assignments. On backtracking, all admissible assignments that
|
% assignments. On backtracking, all admissible assignments that
|
||||||
% attain the optimum are generated.
|
% attain the optimum are generated.
|
||||||
%
|
%
|
||||||
@@ -1272,10 +1579,10 @@ random_bindings(VNum, Node) -->
|
|||||||
%
|
%
|
||||||
% Example:
|
% Example:
|
||||||
%
|
%
|
||||||
% ==
|
% ```
|
||||||
% ?- sat(A#B), weighted_maximum([1,2,1], [A,B,C], Maximum).
|
% ?- sat(A#B), weighted_maximum([1,2,1], [A,B,C], Maximum).
|
||||||
% A = 0, B = 1, C = 1, Maximum = 3.
|
% A = 0, B = 1, C = 1, Maximum = 3.
|
||||||
% ==
|
% ```
|
||||||
|
|
||||||
weighted_maximum(Ws, Vars, Max) :-
|
weighted_maximum(Ws, Vars, Max) :-
|
||||||
must_be(list(integer), Ws),
|
must_be(list(integer), Ws),
|
||||||
@@ -1293,13 +1600,18 @@ weighted_maximum(Ws, Vars, Max) :-
|
|||||||
maplist(var_with_index, Vars, IVs),
|
maplist(var_with_index, Vars, IVs),
|
||||||
pairs_keys_values(Pairs0, IVs, Ws),
|
pairs_keys_values(Pairs0, IVs, Ws),
|
||||||
keysort(Pairs0, Pairs1),
|
keysort(Pairs0, Pairs1),
|
||||||
pairs_keys_values(Pairs1, IVs1, WeightsIndexOrder),
|
% sum linear combinations of repeated variables
|
||||||
|
group_pairs_by_key(Pairs1, Groups),
|
||||||
|
maplist(group_sumweights_pair, Groups, Pairs2),
|
||||||
|
pairs_keys_values(Pairs2, IVs1, WeightsIndexOrder),
|
||||||
pairs_values(IVs1, VarsIndexOrder),
|
pairs_values(IVs1, VarsIndexOrder),
|
||||||
% Pairs is a list of Var-Weight terms, in index order of Vars
|
% Pairs is a list of Var-Weight terms, in index order of Vars
|
||||||
pairs_keys_values(Pairs, VarsIndexOrder, WeightsIndexOrder),
|
pairs_keys_values(Pairs, VarsIndexOrder, WeightsIndexOrder),
|
||||||
bdd_maximum(BDD, Pairs, Max),
|
bdd_maximum(BDD, Pairs, Max),
|
||||||
max_labeling(BDD, Pairs).
|
max_labeling(BDD, Pairs).
|
||||||
|
|
||||||
|
group_sumweights_pair((I-V)-Ws, (I-V)-W) :- sum_list(Ws, W).
|
||||||
|
|
||||||
max_labeling(1, Pairs) :- max_upto(Pairs, _, _).
|
max_labeling(1, Pairs) :- max_upto(Pairs, _, _).
|
||||||
max_labeling(node(_,Var,Low,High,Aux), Pairs0) :-
|
max_labeling(node(_,Var,Low,High,Aux), Pairs0) :-
|
||||||
max_upto(Pairs0, Var, Pairs),
|
max_upto(Pairs0, Var, Pairs),
|
||||||
@@ -1383,14 +1695,14 @@ skip_to_var_(Var, Weight, [Var0-Weight0|VWs0], VWs) -->
|
|||||||
|
|
||||||
attribute_goals(Var) -->
|
attribute_goals(Var) -->
|
||||||
{ var_index_root(Var, _, Root) },
|
{ var_index_root(Var, _, Root) },
|
||||||
|
!,
|
||||||
( { root_get_formula_bdd(Root, Formula, BDD) } ->
|
( { root_get_formula_bdd(Root, Formula, BDD) } ->
|
||||||
{ del_bdd(Root) },
|
{ del_bdd(Root) },
|
||||||
( { clpb_residuals(bdd) } ->
|
( { clpb_residuals(bdd) } ->
|
||||||
{ bdd_nodes(BDD, Nodes),
|
{ bdd_nodes(BDD, Nodes),
|
||||||
phrase(nodes(Nodes), Ns) },
|
phrase(nodes(Nodes), Ns) },
|
||||||
[clpb:'$clpb_bdd'(Ns)]
|
[clpb:'$clpb_bdd'(Ns)]
|
||||||
; { prepare_global_variables(BDD),
|
; { phrase(sat_ands(Formula), Ands0),
|
||||||
phrase(sat_ands(Formula), Ands0),
|
|
||||||
ands_fusion(Ands0, Ands),
|
ands_fusion(Ands0, Ands),
|
||||||
maplist(formula_anf, Ands, ANFs0),
|
maplist(formula_anf, Ands, ANFs0),
|
||||||
sort(ANFs0, ANFs1),
|
sort(ANFs0, ANFs1),
|
||||||
@@ -1410,39 +1722,24 @@ attribute_goals(Var) -->
|
|||||||
booleans(RestVs)
|
booleans(RestVs)
|
||||||
; boolean(Var) % the variable may have occurred only in taut/2
|
; boolean(Var) % the variable may have occurred only in taut/2
|
||||||
).
|
).
|
||||||
|
attribute_goals(Var) -->
|
||||||
|
{ get_atts(Var, clpb_max(_)),
|
||||||
|
!,
|
||||||
|
put_atts(Var, -clpb_max(_)) }.
|
||||||
|
attribute_goals(Var) -->
|
||||||
|
{ get_atts(Var, clpb_bdd(BDD)),
|
||||||
|
ground(BDD),
|
||||||
|
put_atts(Var, -clpb_bdd(_)) }.
|
||||||
|
|
||||||
del_clpb(Var) :-
|
del_clpb(Var) :-
|
||||||
del_attr(Var, clpb),
|
del_attr(Var, clpb),
|
||||||
del_attr(Var, clpb_hash).
|
del_attr(Var, clpb_hash),
|
||||||
|
del_attr(Var, clpb_atom).
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
To make residual projection work with recorded constraints, the
|
|
||||||
global counters must be adjusted so that new variables and nodes
|
|
||||||
also get new IDs. Also, clpb_next_id/2 is used to actually create
|
|
||||||
these counters, because creating them with b_setval/2 would make
|
|
||||||
them [] on backtracking, which is quite unfortunate in itself.
|
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
||||||
|
|
||||||
b_setval(K, T) :- bb_b_put(K, T).
|
b_setval(K, T) :- bb_b_put(K, T).
|
||||||
nb_setval(K, T) :- bb_put(K, T).
|
nb_setval(K, T) :- bb_put(K, T).
|
||||||
b_getval(K, T) :- bb_get(K, T).
|
b_getval(K, T) :- bb_get(K, T).
|
||||||
|
|
||||||
prepare_global_variables(BDD) :-
|
|
||||||
clpb_next_id('$clpb_next_var', V0),
|
|
||||||
clpb_next_id('$clpb_next_node', N0),
|
|
||||||
bdd_nodes(BDD, Nodes),
|
|
||||||
foldl(max_variable_node, Nodes, V0-N0, MaxV0-MaxN0),
|
|
||||||
MaxV is MaxV0 + 1,
|
|
||||||
MaxN is MaxN0 + 1,
|
|
||||||
b_setval('$clpb_next_var', MaxV),
|
|
||||||
b_setval('$clpb_next_node', MaxN).
|
|
||||||
|
|
||||||
max_variable_node(Node, V0-N0, V-N) :-
|
|
||||||
node_id(Node, N1),
|
|
||||||
node_varindex(Node, V1),
|
|
||||||
N is max(N0,N1),
|
|
||||||
V is max(V0,V1).
|
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Fuse formulas that share the same variables into single conjunctions.
|
Fuse formulas that share the same variables into single conjunctions.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
@@ -1534,8 +1831,8 @@ pairs_([], _) --> [].
|
|||||||
pairs_([B|Bs], A) --> [A-B], pairs_(Bs, A).
|
pairs_([B|Bs], A) --> [A-B], pairs_(Bs, A).
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Set the Prolog flag clpb_residuals to bdd to obtain the BDD nodes
|
Assert clpb:clpb_residuals(bdd) to obtain the BDD nodes as
|
||||||
as residuals. Note that they cannot be used as regular goals.
|
residuals. Note that they cannot be used as regular goals.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
nodes([]) --> [].
|
nodes([]) --> [].
|
||||||
@@ -1563,10 +1860,12 @@ sats([]) --> [].
|
|||||||
sats([A|As]) --> [clpb:sat(A)], sats(As).
|
sats([A|As]) --> [clpb:sat(A)], sats(As).
|
||||||
|
|
||||||
booleans([]) --> [].
|
booleans([]) --> [].
|
||||||
booleans([B|Bs]) --> boolean(B), { del_clpb(B) }, booleans(Bs).
|
booleans([B|Bs]) --> boolean(B), booleans(Bs).
|
||||||
|
|
||||||
boolean(Var) -->
|
boolean(Var) -->
|
||||||
( { get_attr(Var, clpb_omit_boolean, true) } -> []
|
{ del_clpb(Var) },
|
||||||
|
( { get_attr(Var, clpb_omit_boolean, true) } ->
|
||||||
|
{ put_atts(Var, -clpb_omit_boolean(_)) }
|
||||||
; [clpb:sat(Var =:= Var)]
|
; [clpb:sat(Var =:= Var)]
|
||||||
).
|
).
|
||||||
|
|
||||||
@@ -1668,49 +1967,3 @@ clpb_atom_var(Atom, Var) :-
|
|||||||
put_assoc(Atom, A0, Var, A),
|
put_assoc(Atom, A0, Var, A),
|
||||||
b_setval('$clpb_atoms', A)
|
b_setval('$clpb_atoms', A)
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
||||||
Compatibility predicates.
|
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
||||||
|
|
||||||
|
|
||||||
include(Goal, List, Is) :-
|
|
||||||
include_(List, Goal, Is).
|
|
||||||
|
|
||||||
include_([], _, []).
|
|
||||||
include_([X1|Xs1], P, Is) :-
|
|
||||||
( call(P, X1)
|
|
||||||
-> Is = [X1|Is1]
|
|
||||||
; Is = Is1
|
|
||||||
),
|
|
||||||
include_(Xs1, P, Is1).
|
|
||||||
|
|
||||||
|
|
||||||
exclude(Goal, List, Is) :-
|
|
||||||
exclude_(List, Goal, Is).
|
|
||||||
|
|
||||||
exclude_([], _, []).
|
|
||||||
exclude_([X1|Xs1], P, Is) :-
|
|
||||||
( call(P, X1)
|
|
||||||
-> Is = Is1
|
|
||||||
; Is = [X1|Is1]
|
|
||||||
),
|
|
||||||
exclude_(Xs1, P, Is1).
|
|
||||||
|
|
||||||
|
|
||||||
partition(Pred, List, Less, Equal, Greater) :-
|
|
||||||
partition_(List, Pred, Less, Equal, Greater).
|
|
||||||
|
|
||||||
partition_([], _, [], [], []).
|
|
||||||
partition_([H|T], Pred, L, E, G) :-
|
|
||||||
call(Pred, H, Diff),
|
|
||||||
partition_(Diff, H, Pred, T, L, E, G).
|
|
||||||
|
|
||||||
partition_(<, H, Pred, T, [H|Rest], E, G) :-
|
|
||||||
partition_(T, Pred, Rest, E, G).
|
|
||||||
partition_(=, H, Pred, T, L, [H|Rest], G) :-
|
|
||||||
partition_(T, Pred, L, Rest, G).
|
|
||||||
partition_(>, H, Pred, T, L, E, [H|Rest]) :-
|
|
||||||
partition_(T, Pred, L, E, Rest).
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
|||||||
:- module(cont, [reset/3, shift/1]).
|
:- module(cont, [reset/3, shift/1]).
|
||||||
|
|
||||||
|
:- meta_predicate(reset(0, ?, ?)).
|
||||||
|
|
||||||
reset(Goal, Ball, Cont) :-
|
reset(Goal, Ball, Cont) :-
|
||||||
call(Goal),
|
call(Goal),
|
||||||
'$reset_cont_marker',
|
'$reset_cont_marker',
|
||||||
@@ -10,8 +12,8 @@ shift(Ball) :-
|
|||||||
'$nextEP'(first, E, P),
|
'$nextEP'(first, E, P),
|
||||||
get_chunks(E, P, L),
|
get_chunks(E, P, L),
|
||||||
( L == [] ->
|
( L == [] ->
|
||||||
Cont = none
|
Cont = cont(true)
|
||||||
; Cont = cont(call_continuation(L))
|
; Cont = cont(cont:call_continuation(L))
|
||||||
),
|
),
|
||||||
'$write_cont_and_term'(_, _, Cont, Ball),
|
'$write_cont_and_term'(_, _, Cont, Ball),
|
||||||
'$unwind_environments'.
|
'$unwind_environments'.
|
||||||
1124
src/lib/crypto.pl
Normal file
1124
src/lib/crypto.pl
Normal file
File diff suppressed because it is too large
Load Diff
251
src/lib/csv.pl
Normal file
251
src/lib/csv.pl
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
/** Predicates for parsing CSV data
|
||||||
|
|
||||||
|
## Read CSV files.
|
||||||
|
|
||||||
|
Only two options with default values:
|
||||||
|
|
||||||
|
- `token_separator(',')`
|
||||||
|
- `with_header(true)`
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
|
||||||
|
Parsing a CSV string:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module(library(csv)).
|
||||||
|
?- use_module(library(dcgs)).
|
||||||
|
?- phrase(parse_csv(Data), "col1,col2,col3,col4\none,2,,three").
|
||||||
|
Data = frame(["col1","col2","col3","col4"],[["one",2,[],"three"]]).
|
||||||
|
```
|
||||||
|
|
||||||
|
With some options:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- phrase(parse_csv(Data, [with_header(false), token_separator(';')]), "one;2;;three").
|
||||||
|
Data = frame([],[["one",2,[],"three"]]).
|
||||||
|
```
|
||||||
|
|
||||||
|
Parsing a CSV file:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module(library(csv)).
|
||||||
|
?- use_module(library(pio)).
|
||||||
|
?- phrase_from_file(parse_csv(frame(Header, Rows)), './test.csv').
|
||||||
|
```
|
||||||
|
|
||||||
|
## Write CSV files
|
||||||
|
|
||||||
|
Four options with default values :
|
||||||
|
|
||||||
|
- `line_separator('\n')`
|
||||||
|
- `token_separator(',')`
|
||||||
|
- `with_header(true)`
|
||||||
|
- `null_value(empty)`
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
Writing a CSV file:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module(library(csv)).
|
||||||
|
?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]])).
|
||||||
|
```
|
||||||
|
|
||||||
|
With some options
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module(library(csv)).
|
||||||
|
?- write_csv('./test.csv', frame(
|
||||||
|
["col1","col2","col3","col4"],
|
||||||
|
[["one",2,[],"three"]]
|
||||||
|
),
|
||||||
|
[with_header(false), line_separator('\r\n'), token_separator(';'), null_value('\\N')]).
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(csv, [
|
||||||
|
parse_csv//1,
|
||||||
|
parse_csv//2,
|
||||||
|
write_csv/2,
|
||||||
|
write_csv/3
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- use_module(library(format)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(iso_ext)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
|
|
||||||
|
option(W, O) :-
|
||||||
|
( member(W, O) -> true
|
||||||
|
; throw(error(domain_error(csv_option, W), option/2))).
|
||||||
|
|
||||||
|
|
||||||
|
option_extends([], Opt, Opt).
|
||||||
|
option_extends([X | Y], Opt0, Opt) :-
|
||||||
|
functor(X, Name, 1),
|
||||||
|
F0 =.. [Name, _],
|
||||||
|
( select(F0, Opt0, R) ->
|
||||||
|
option_extends(Y, [X | R], Opt)
|
||||||
|
; option_extends(Y, [X | Opt0], Opt) ).
|
||||||
|
|
||||||
|
|
||||||
|
%% -- write --
|
||||||
|
|
||||||
|
|
||||||
|
escaped_field([], []).
|
||||||
|
escaped_field(['"' | Y], ['"', '"' | R]) :-
|
||||||
|
escaped_field(Y, R).
|
||||||
|
escaped_field([X | Y], [X | R]) :-
|
||||||
|
X \== '"',
|
||||||
|
escaped_field(Y, R).
|
||||||
|
|
||||||
|
|
||||||
|
ensure_escaped(Field, Field) :-
|
||||||
|
(atom(Field); integer(Field); float(Field)).
|
||||||
|
ensure_escaped([X | Y], Field) :-
|
||||||
|
escaped_field([X | Y], Field).
|
||||||
|
|
||||||
|
|
||||||
|
write_field(Out, Field, Opt) :-
|
||||||
|
( Field \== [] ->
|
||||||
|
ensure_escaped(Field, Field0),
|
||||||
|
format(Out, "~w", [Field0])
|
||||||
|
; option(null_value(Null_Value), Opt),
|
||||||
|
( Null_Value == empty -> true
|
||||||
|
; format(Out, "~w", [Null_Value]))).
|
||||||
|
|
||||||
|
|
||||||
|
write_row(Out, [Field], Opt) :-
|
||||||
|
write_field(Out, Field, Opt).
|
||||||
|
write_row(Out, [Field, X | Y], Opt) :-
|
||||||
|
write_field(Out, Field, Opt),
|
||||||
|
option(token_separator(Tk_Sep), Opt),
|
||||||
|
format(Out, "~w", [Tk_Sep]),
|
||||||
|
write_row(Out, [X | Y], Opt).
|
||||||
|
|
||||||
|
|
||||||
|
write_rows(Out, [Row], Opt) :-
|
||||||
|
write_row(Out, Row, Opt).
|
||||||
|
write_rows(Out, [Row, X | Y], Opt) :-
|
||||||
|
option(line_separator(Line_Sep), Opt),
|
||||||
|
write_row(Out, Row, Opt),
|
||||||
|
format(Out, "~w", [Line_Sep]),
|
||||||
|
write_rows(Out, [X | Y], Opt).
|
||||||
|
|
||||||
|
|
||||||
|
write_csv_(Out, frame(Header, Rows), Opt) :-
|
||||||
|
option(with_header(With_Header), Opt),
|
||||||
|
( With_Header == true ->
|
||||||
|
write_row(Out, Header, Opt),
|
||||||
|
option(line_separator(Line_Sep), Opt),
|
||||||
|
format(Out, "~w", [Line_Sep])
|
||||||
|
; true),
|
||||||
|
write_rows(Out, Rows, Opt).
|
||||||
|
|
||||||
|
|
||||||
|
write_csv(File_Name, Frm, Opt) :-
|
||||||
|
option_extends(Opt, [
|
||||||
|
null_value(empty),
|
||||||
|
token_separator(','),
|
||||||
|
with_header(true),
|
||||||
|
line_separator('\n')
|
||||||
|
], Opt0),
|
||||||
|
setup_call_cleanup(
|
||||||
|
open(File_Name, write, Out),
|
||||||
|
write_csv_(Out, Frm, Opt0),
|
||||||
|
close(Out)).
|
||||||
|
write_csv(File_Name, Frm) :-
|
||||||
|
write_csv(File_Name, Frm, []).
|
||||||
|
|
||||||
|
|
||||||
|
%% -- read --
|
||||||
|
|
||||||
|
|
||||||
|
tokens([], Opt), [Tk_Sep] -->
|
||||||
|
{ option(token_separator(Tk_Sep), Opt) },
|
||||||
|
[Tk_Sep],
|
||||||
|
!.
|
||||||
|
tokens([], _), "\r\n" -->
|
||||||
|
"\r\n",
|
||||||
|
!.
|
||||||
|
tokens([], _), "\n" -->
|
||||||
|
"\n",
|
||||||
|
!.
|
||||||
|
tokens([], _), "\r" -->
|
||||||
|
"\r",
|
||||||
|
!.
|
||||||
|
tokens([X | Y], Opt) -->
|
||||||
|
[X],
|
||||||
|
!,
|
||||||
|
tokens(Y, Opt).
|
||||||
|
tokens([], _) --> [].
|
||||||
|
|
||||||
|
|
||||||
|
field(R, Opt) -->
|
||||||
|
"\"",
|
||||||
|
!,
|
||||||
|
string_tokens(R, Opt).
|
||||||
|
field(R, Opt) -->
|
||||||
|
tokens(R0, Opt),
|
||||||
|
{ R0 \== [],
|
||||||
|
catch(number_chars(R, R0), _, R = R0)
|
||||||
|
}.
|
||||||
|
field([], _) --> [].
|
||||||
|
|
||||||
|
|
||||||
|
string_tokens(R, Opt) -->
|
||||||
|
[X],
|
||||||
|
( { X == '"' } ->
|
||||||
|
( "\"" ->
|
||||||
|
{ R = [X | Y] },
|
||||||
|
string_tokens(Y, Opt)
|
||||||
|
; { R = [] })
|
||||||
|
; { R = [X | Y] },
|
||||||
|
string_tokens(Y, Opt)).
|
||||||
|
|
||||||
|
|
||||||
|
end_token --> "\r\n".
|
||||||
|
end_token --> "\n".
|
||||||
|
end_token --> "\r".
|
||||||
|
end_token --> [].
|
||||||
|
|
||||||
|
|
||||||
|
separator(Opt) -->
|
||||||
|
{ option(token_separator(Tk_Sep), Opt) },
|
||||||
|
[Tk_Sep].
|
||||||
|
|
||||||
|
|
||||||
|
row([X | Y], Opt) -->
|
||||||
|
field(X, Opt),
|
||||||
|
!,
|
||||||
|
( separator(Opt) ->
|
||||||
|
row(Y, Opt)
|
||||||
|
; end_token,
|
||||||
|
{ Y = [] }).
|
||||||
|
|
||||||
|
|
||||||
|
rows(R, Opt) -->
|
||||||
|
row(X, Opt),
|
||||||
|
!,
|
||||||
|
( { X \== [[]] } ->
|
||||||
|
rows(Y, Opt),
|
||||||
|
{ R = [X | Y] }
|
||||||
|
; { R = [] }).
|
||||||
|
|
||||||
|
|
||||||
|
parse_csv(frame(Header, Rows), Opt) -->
|
||||||
|
{ option_extends(Opt, [
|
||||||
|
with_header(true),
|
||||||
|
token_separator(',')
|
||||||
|
], Opt0)
|
||||||
|
},
|
||||||
|
( { option(with_header(With_Header), Opt0),
|
||||||
|
With_Header == true } ->
|
||||||
|
row(Header, Opt0),
|
||||||
|
{ Header \== [[]] },
|
||||||
|
end_token
|
||||||
|
; { Header = [] }),
|
||||||
|
rows(Rows, Opt0).
|
||||||
|
parse_csv(R) -->
|
||||||
|
parse_csv(R, []).
|
||||||
284
src/lib/dcgs.pl
Normal file
284
src/lib/dcgs.pl
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
/** Support for Definite Clause Grammars.
|
||||||
|
|
||||||
|
A Prolog definite clause grammar (DCG) describes a sequence. Operationally, DCGs
|
||||||
|
can be used to parse, generate, complete and check sequences manifested as lists.
|
||||||
|
|
||||||
|
Check [The Power of Prolog chapter on DCGs](https://www.metalevel.at/prolog/dcg)
|
||||||
|
to learn more about them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
:- module(dcgs,
|
||||||
|
[op(1105, xfy, '|'),
|
||||||
|
phrase/2,
|
||||||
|
phrase/3,
|
||||||
|
phrase//2,
|
||||||
|
phrase//3,
|
||||||
|
seq//1,
|
||||||
|
seqq//1,
|
||||||
|
... //0,
|
||||||
|
(-->)/2
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(iso_ext)).
|
||||||
|
:- 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(3, ?, ?, ?)).
|
||||||
|
|
||||||
|
:- meta_predicate(phrase(4, ?, ?, ?, ?)).
|
||||||
|
|
||||||
|
:- meta_predicate(','(2, 2, ?, ?)).
|
||||||
|
|
||||||
|
:- meta_predicate(;(2, 2, ?, ?)).
|
||||||
|
|
||||||
|
%% phrase(+Body, ?Ls).
|
||||||
|
%
|
||||||
|
% True iff Body describes the list Ls. Body must be a DCG body.
|
||||||
|
% It is equivalent to `phrase(Body, Ls, [])`.
|
||||||
|
%
|
||||||
|
% Examples:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% as --> [].
|
||||||
|
% as --> [a], as.
|
||||||
|
%
|
||||||
|
% ?- phrase(as, Ls).
|
||||||
|
% Ls = []
|
||||||
|
% ; Ls = "a"
|
||||||
|
% ; Ls = "aa"
|
||||||
|
% ; Ls = "aaa"
|
||||||
|
% ; ... .
|
||||||
|
%
|
||||||
|
% ?- phrase(as, "aaa").
|
||||||
|
% true.
|
||||||
|
% ```
|
||||||
|
|
||||||
|
phrase(GRBody, S0) :-
|
||||||
|
phrase(GRBody, S0, []).
|
||||||
|
|
||||||
|
%% phrase(+Body, ?Ls, ?Ls0).
|
||||||
|
%
|
||||||
|
% True iff Body describes part of the list Ls and the rest of Ls is Ls0.
|
||||||
|
%
|
||||||
|
% Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- phrase(seq(X), "aaa", Y).
|
||||||
|
% X = [], Y = "aaa"
|
||||||
|
% ; X = "a", Y = "aa"
|
||||||
|
% ; X = "aa", Y = "a"
|
||||||
|
% ; X = "aaa", Y = [].
|
||||||
|
% ```
|
||||||
|
phrase(GRBody, S0, S) :-
|
||||||
|
strip_module(GRBody, M, GRBody1),
|
||||||
|
( var(GRBody) ->
|
||||||
|
instantiation_error(phrase/3)
|
||||||
|
; nonvar(GRBody1),
|
||||||
|
dcg_constr(GRBody1),
|
||||||
|
dcg_body(GRBody1, S0, S, GRBody2) ->
|
||||||
|
call(M:GRBody2)
|
||||||
|
; call(M:GRBody1, S0, S)
|
||||||
|
).
|
||||||
|
|
||||||
|
phrase(GRBody, Arg, S0, S) :-
|
||||||
|
strip_module(GRBody, M, GRBody1),
|
||||||
|
( var(GRBody) ->
|
||||||
|
instantiation_error(phrase/4)
|
||||||
|
; nonvar(GRBody1),
|
||||||
|
GRBody1 =.. GRBodys1,
|
||||||
|
append(GRBodys1, [Arg], GRBodys2),
|
||||||
|
GRBody2 =.. GRBodys2,
|
||||||
|
dcg_constr(GRBody2),
|
||||||
|
dcg_body(GRBody2, S0, S, GRBody3) ->
|
||||||
|
call(M:GRBody3)
|
||||||
|
; call(M:GRBody1, Arg, S0, S)
|
||||||
|
).
|
||||||
|
|
||||||
|
phrase(GRBody, Arg1, Arg2, S0, S) :-
|
||||||
|
strip_module(GRBody, M, GRBody1),
|
||||||
|
( var(GRBody) ->
|
||||||
|
instantiation_error(phrase/5)
|
||||||
|
; nonvar(GRBody1),
|
||||||
|
GRBody1 =.. GRBodys1,
|
||||||
|
append(GRBodys1, [Arg1,Arg2], GRBodys2),
|
||||||
|
GRBody2 =.. GRBodys2,
|
||||||
|
dcg_constr(GRBody2),
|
||||||
|
dcg_body(GRBody2, S0, S, GRBody3) ->
|
||||||
|
call(M:GRBody3)
|
||||||
|
; call(M:GRBody1, Arg1, Arg2, S0, S)
|
||||||
|
).
|
||||||
|
|
||||||
|
% The same version of the below two dcg_rule clauses, but with module scoping.
|
||||||
|
dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :-
|
||||||
|
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||||
|
dcg_body(GRBody, S0, S1, Goal1),
|
||||||
|
dcg_terminals(Terminals, S, S1, Goal2),
|
||||||
|
Body = ( Goal1, Goal2 ).
|
||||||
|
dcg_rule(( M:NonTerminal --> GRBody ), ( M:Head :- Body )) :-
|
||||||
|
NonTerminal \= ( _, _ ),
|
||||||
|
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||||
|
dcg_body(GRBody, S0, S, Body).
|
||||||
|
|
||||||
|
% This program uses append/3 as defined in the Prolog prologue.
|
||||||
|
% Expands a DCG rule into a Prolog rule, when no error condition applies.
|
||||||
|
dcg_rule(( NonTerminal, Terminals --> GRBody ), ( Head :- Body )) :-
|
||||||
|
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||||
|
dcg_body(GRBody, S0, S1, Goal1),
|
||||||
|
dcg_terminals(Terminals, S, S1, Goal2),
|
||||||
|
Body = ( Goal1, Goal2 ).
|
||||||
|
dcg_rule(( NonTerminal --> GRBody ), ( Head :- Body )) :-
|
||||||
|
NonTerminal \= ( _, _ ),
|
||||||
|
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||||
|
dcg_body(GRBody, S0, S, Body).
|
||||||
|
|
||||||
|
dcg_non_terminal(NonTerminal, S0, S, Goal) :-
|
||||||
|
NonTerminal =.. NonTerminalUniv,
|
||||||
|
append(NonTerminalUniv, [S0, S], GoalUniv),
|
||||||
|
( callable(NonTerminal) ->
|
||||||
|
Goal =.. GoalUniv
|
||||||
|
; Goal = NonTerminal % let call/N throw an error instead of throwing one here.
|
||||||
|
).
|
||||||
|
|
||||||
|
dcg_terminals(Terminals, S0, S, S0 = List) :-
|
||||||
|
append(Terminals, S, List).
|
||||||
|
|
||||||
|
dcg_body(Var, S0, S, Body) :-
|
||||||
|
var(Var),
|
||||||
|
Body = phrase(Var, S0, S).
|
||||||
|
dcg_body(GRBody, S0, S, Body) :-
|
||||||
|
nonvar(GRBody),
|
||||||
|
dcg_constr(GRBody),
|
||||||
|
dcg_cbody(GRBody, S0, S, Body).
|
||||||
|
dcg_body(NonTerminal, S0, S, Goal1) :-
|
||||||
|
nonvar(NonTerminal),
|
||||||
|
\+ dcg_constr(NonTerminal),
|
||||||
|
loader:strip_module(NonTerminal, M, NonTerminal0),
|
||||||
|
dcg_non_terminal(NonTerminal0, S0, S, Goal0),
|
||||||
|
( functor(NonTerminal, (:), 2) ->
|
||||||
|
Goal1 = M:Goal0
|
||||||
|
; Goal1 = Goal0
|
||||||
|
).
|
||||||
|
|
||||||
|
% The following constructs in a grammar rule body
|
||||||
|
% are defined in the corresponding subclauses.
|
||||||
|
dcg_constr([]). % 7.14.1
|
||||||
|
dcg_constr([_|_]). % 7.14.2 - terminal sequence
|
||||||
|
dcg_constr(( _, _ )). % 7.14.3 - concatenation
|
||||||
|
dcg_constr(( _ ; _ )). % 7.14.4 - alternative
|
||||||
|
dcg_constr(( _'|'_ )). % 7.14.6 - alternative
|
||||||
|
dcg_constr({_}). % 7.14.7
|
||||||
|
dcg_constr(call(_)). % 7.14.8
|
||||||
|
dcg_constr(phrase(_)). % 7.14.9
|
||||||
|
dcg_constr(phrase(_,_)). % extension of 7.14.9
|
||||||
|
dcg_constr(phrase(_,_,_)). % extension of 7.14.9
|
||||||
|
dcg_constr(!). % 7.14.10
|
||||||
|
dcg_constr(\+ G_0) :- % 7.14.11 - not (existence implementation def.)
|
||||||
|
throw(error(representation_error(dcg_body), [culprit- (\+ G_0)])).
|
||||||
|
dcg_constr((If->Then)) :- % 7.14.12 - if-then (existence implementation def.)
|
||||||
|
throw(error(representation_error(dcg_body), [culprit- (If->Then)])).
|
||||||
|
|
||||||
|
% The principal functor of the first argument indicates
|
||||||
|
% the construct to be expanded.
|
||||||
|
dcg_cbody([], S0, S, S0 = S).
|
||||||
|
dcg_cbody([T|Ts], S0, S, Goal) :-
|
||||||
|
must_be(list, [T|Ts]),
|
||||||
|
dcg_terminals([T|Ts], S0, S, Goal).
|
||||||
|
dcg_cbody(( GRFirst, GRSecond ), S0, S, ( First, Second )) :-
|
||||||
|
dcg_body(GRFirst, S0, S1, First),
|
||||||
|
dcg_body(GRSecond, S1, S, Second).
|
||||||
|
dcg_cbody(( GREither ; GROr ), S0, S, ( Either ; Or )) :-
|
||||||
|
\+ subsumes_term(( _ -> _ ), GREither),
|
||||||
|
dcg_body(GREither, S0, S, Either),
|
||||||
|
dcg_body(GROr, S0, S, Or).
|
||||||
|
dcg_cbody(( GRCond ; GRElse ), S0, S, ( Cond ; Else )) :-
|
||||||
|
subsumes_term(( _GRIf -> _GRThen ), GRCond),
|
||||||
|
dcg_cbody(GRCond, S0, S, Cond),
|
||||||
|
dcg_body(GRElse, S0, S, Else).
|
||||||
|
dcg_cbody(( GREither '|' GROr ), S0, S, ( Either ; Or )) :-
|
||||||
|
dcg_body(GREither, S0, S, Either),
|
||||||
|
dcg_body(GROr, S0, S, Or).
|
||||||
|
dcg_cbody({Goal}, S0, S, ( Goal, S0 = S )).
|
||||||
|
dcg_cbody(call(Cont), S0, S, call(Cont, S0, S)).
|
||||||
|
dcg_cbody(phrase(Body), S0, S, phrase(Body, S0, S)).
|
||||||
|
dcg_cbody(phrase(Body, Arg), S0, S, phrase(Body, Arg, S0, S)).
|
||||||
|
dcg_cbody(phrase(Body, Arg1, Arg2), S0, S, phrase(Body, Arg1, Arg2, S0, S)).
|
||||||
|
dcg_cbody(!, S0, S, ( !, S0 = S )).
|
||||||
|
% dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )).
|
||||||
|
dcg_cbody(( 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),
|
||||||
|
catch(dcg_rule(Term0, Term), E, Term = throw_dcg_expansion_error(E)).
|
||||||
|
|
||||||
|
%% seq(Seq)//
|
||||||
|
%
|
||||||
|
% Describes a sequence
|
||||||
|
seq(Xs, Cs0,Cs) :-
|
||||||
|
var(Xs),
|
||||||
|
Cs0 == [],
|
||||||
|
!,
|
||||||
|
Xs = [],
|
||||||
|
Cs0 = Cs.
|
||||||
|
seq([]) --> [].
|
||||||
|
seq([E|Es]) --> [E], seq(Es).
|
||||||
|
|
||||||
|
%% seqq(SeqOfSeqs)//
|
||||||
|
%
|
||||||
|
% Describes a sequence of sequences
|
||||||
|
seqq([]) --> [].
|
||||||
|
seqq([Es|Ess]) --> seq(Es), seqq(Ess).
|
||||||
|
|
||||||
|
%% ...//
|
||||||
|
%
|
||||||
|
% Describes an arbitrary number of elements
|
||||||
|
...(Cs0,Cs) :-
|
||||||
|
Cs0 == [],
|
||||||
|
!,
|
||||||
|
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),
|
||||||
|
error(representation_error(dcg_body), Context)).
|
||||||
|
error_goal(E, _) :- throw(E).
|
||||||
|
|
||||||
|
user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
|
||||||
|
loader:strip_module(GRBody, M, GRBody0),
|
||||||
|
nonvar(GRBody0),
|
||||||
|
catch(dcgs:dcg_body(GRBody0, S, S0, GRBody1),
|
||||||
|
E,
|
||||||
|
dcgs:error_goal(E, GRBody1)
|
||||||
|
),
|
||||||
|
( 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
|
||||||
|
).
|
||||||
|
|
||||||
|
user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])).
|
||||||
|
|
||||||
|
|
||||||
|
% (-->)/2 behaves as if it didn't exist. We export (and define) it
|
||||||
|
% only so that clauses for (-->)/2 cannot be asserted when
|
||||||
|
% library(dcgs) is loaded.
|
||||||
|
|
||||||
|
(_-->_) :- throw(error(existence_error(procedure,(-->)/2),(-->)/2)).
|
||||||
56
src/lib/debug.pl
Normal file
56
src/lib/debug.pl
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/** Declarative debugging.
|
||||||
|
|
||||||
|
This library provides three predicates with associated operators.
|
||||||
|
The operators can be placed in front of goals to debug Prolog
|
||||||
|
programs.
|
||||||
|
|
||||||
|
Of these predicates, the most frequently used is `(*)/1`, with
|
||||||
|
associated prefix operator `*` (star). Placing `*` in front of a
|
||||||
|
goal means to _generalize away_ the goal. `* Goal` acts as if `Goal`
|
||||||
|
did not appear at all in the source code. It is declaratively
|
||||||
|
equivalent to _commenting out_ the goal, and easier to write,
|
||||||
|
because `*` can also be placed in front of the last goal in a clause
|
||||||
|
without any additional changes.
|
||||||
|
|
||||||
|
Source: [https://stackoverflow.com/a/30791637](https://stackoverflow.com/a/30791637)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:- module(debug, [
|
||||||
|
op(900, fx, $),
|
||||||
|
op(900, fx, $-),
|
||||||
|
op(950, fy, *),
|
||||||
|
(*)/1,
|
||||||
|
($)/1,
|
||||||
|
($-)/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- use_module(library(format), [portray_clause/1]).
|
||||||
|
|
||||||
|
:- meta_predicate(*(0)).
|
||||||
|
:- meta_predicate($(0)).
|
||||||
|
:- meta_predicate($-(0)).
|
||||||
|
|
||||||
|
%% $-(Goal)
|
||||||
|
%
|
||||||
|
% Portray exceptions thrown by Goal.
|
||||||
|
|
||||||
|
$-(G_0) :-
|
||||||
|
catch(G_0, Ex, ( portray_clause(exception:Ex:G_0), throw(Ex) ) ).
|
||||||
|
|
||||||
|
%% $(Goal)
|
||||||
|
%
|
||||||
|
% Provide a _trace_ for calls of Goal.
|
||||||
|
|
||||||
|
$(G_0) :-
|
||||||
|
portray_clause(call:G_0),
|
||||||
|
$-G_0,
|
||||||
|
portray_clause(exit:G_0).
|
||||||
|
|
||||||
|
%% *(Goal)
|
||||||
|
%
|
||||||
|
% Generalize away Goal.
|
||||||
|
|
||||||
|
*(_).
|
||||||
187
src/lib/diag.pl
Normal file
187
src/lib/diag.pl
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
:- module(diag, [wam_instructions/2, inlined_instructions/2]).
|
||||||
|
|
||||||
|
/** Diagnostics library
|
||||||
|
|
||||||
|
The predicate `wam_instructions/2` _decompiles_ a predicate so that
|
||||||
|
we can inspect its Warren Abstract Machine (WAM) instructions.
|
||||||
|
In this way, we can verify and reason about compiled programs,
|
||||||
|
and detect opportunities for optimization.
|
||||||
|
|
||||||
|
For example, we have:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module(library(lists)).
|
||||||
|
true.
|
||||||
|
?- use_module(library(diag)).
|
||||||
|
true.
|
||||||
|
?- use_module(library(format)).
|
||||||
|
true.
|
||||||
|
?- wam_instructions(append/3, Is),
|
||||||
|
maplist(portray_clause, Is).
|
||||||
|
switch_on_term(1,external(1),external(2),external(6),fail).
|
||||||
|
try_me_else(4).
|
||||||
|
get_constant(level(shallow),[],x(1)).
|
||||||
|
get_value(x(2),3).
|
||||||
|
proceed.
|
||||||
|
trust_me(0).
|
||||||
|
get_list(level(shallow),x(1)).
|
||||||
|
unify_variable(x(4)).
|
||||||
|
unify_variable(x(1)).
|
||||||
|
get_list(level(shallow),x(3)).
|
||||||
|
unify_value(x(4)).
|
||||||
|
unify_variable(x(3)).
|
||||||
|
execute(append,3).
|
||||||
|
Is = [switch_on_term(1,external(1),external(2),external(6),fail)|...].
|
||||||
|
```
|
||||||
|
|
||||||
|
`inlined_instructions/2` decompiles predicates at the code offset in
|
||||||
|
its first argument.
|
||||||
|
|
||||||
|
For example, given the program
|
||||||
|
|
||||||
|
```
|
||||||
|
?- [user].
|
||||||
|
:- use_module(library(clpz)).
|
||||||
|
|
||||||
|
all_eq(Vs, E) :- maplist(#=(E), Vs).
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
we inspect the code of `all_eqs/2` using `wam_instructions/2`,
|
||||||
|
revealing:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- wam_instructions(all_eq/2, Is),
|
||||||
|
maplist(portray_clause, Is).
|
||||||
|
put_structure('$aux',2,x(3)).
|
||||||
|
set_local_value(x(2)).
|
||||||
|
set_void(1).
|
||||||
|
set_constant('$index_ptr'(115334)).
|
||||||
|
get_variable(x(4),1).
|
||||||
|
put_structure(:,2,x(1)).
|
||||||
|
set_constant(user).
|
||||||
|
set_local_value(x(3)).
|
||||||
|
get_variable(x(5),2).
|
||||||
|
put_value(x(4),2).
|
||||||
|
execute(maplist,2).
|
||||||
|
Is = [put_structure('$aux',2,x(3)),set_local_value(x(2)),set_void(1),set_constant('$index_ptr'(115334)),get_variable(x(4),1),put_structure(:,2,x(1)),set_constant(user),set_local_value(x(3)),get_variable(x(5),2),put_value(x(4),2),execute(maplist,2)].
|
||||||
|
```
|
||||||
|
|
||||||
|
The `'$index_ptr(115334)` functor gives a code offset to an inlined
|
||||||
|
predicate compiled for the use of maplist/2. `inlined_instructions/2`
|
||||||
|
can be used to decompile its source code:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- inlined_instructions(115334, Is),
|
||||||
|
maplist(portray_clause, Is).
|
||||||
|
allocate(1).
|
||||||
|
get_level(y(1)).
|
||||||
|
get_variable(x(5),2).
|
||||||
|
put_value(x(3),2).
|
||||||
|
get_variable(x(6),3).
|
||||||
|
put_value(x(5),3).
|
||||||
|
put_unsafe_value(1,4).
|
||||||
|
deallocate.
|
||||||
|
jmp_by_execute(1).
|
||||||
|
try_me_else(8).
|
||||||
|
call(integer,1).
|
||||||
|
neck_cut.
|
||||||
|
get_variable(x(5),1).
|
||||||
|
put_value(x(2),1).
|
||||||
|
get_variable(x(6),2).
|
||||||
|
put_value(x(5),2).
|
||||||
|
jmp_by_execute(7).
|
||||||
|
try_me_else(12).
|
||||||
|
allocate(3).
|
||||||
|
get_level(y(1)).
|
||||||
|
get_variable(y(3),1).
|
||||||
|
get_variable(y(2),2).
|
||||||
|
call_default(true,0).
|
||||||
|
call(var,1).
|
||||||
|
cut(y(1)).
|
||||||
|
put_unsafe_value(3,1).
|
||||||
|
put_unsafe_value(2,2).
|
||||||
|
deallocate.
|
||||||
|
execute_default(is,2).
|
||||||
|
default_retry_me_else(4).
|
||||||
|
call(integer,1).
|
||||||
|
neck_cut.
|
||||||
|
execute(=:=,2).
|
||||||
|
default_trust_me(0).
|
||||||
|
allocate(2).
|
||||||
|
get_variable(y(1),1).
|
||||||
|
get_variable(y(2),3).
|
||||||
|
put_value(y(2),1).
|
||||||
|
call_default(is,2).
|
||||||
|
put_unsafe_value(2,1).
|
||||||
|
put_unsafe_value(1,2).
|
||||||
|
deallocate.
|
||||||
|
execute_default(clpz_equal,2).
|
||||||
|
default_retry_me_else(4).
|
||||||
|
call(integer,1).
|
||||||
|
neck_cut.
|
||||||
|
jmp_by_execute(29).
|
||||||
|
try_me_else(12).
|
||||||
|
allocate(3).
|
||||||
|
get_level(y(1)).
|
||||||
|
get_variable(y(3),1).
|
||||||
|
get_variable(y(2),2).
|
||||||
|
call_default(true,0).
|
||||||
|
call(var,1).
|
||||||
|
cut(y(1)).
|
||||||
|
put_unsafe_value(3,1).
|
||||||
|
put_unsafe_value(2,2).
|
||||||
|
deallocate.
|
||||||
|
execute_default(is,2).
|
||||||
|
default_trust_me(0).
|
||||||
|
allocate(2).
|
||||||
|
get_variable(y(2),1).
|
||||||
|
get_variable(y(1),3).
|
||||||
|
put_value(y(1),1).
|
||||||
|
call_default(is,2).
|
||||||
|
put_unsafe_value(2,1).
|
||||||
|
put_unsafe_value(1,2).
|
||||||
|
deallocate.
|
||||||
|
execute_default(clpz_equal,2).
|
||||||
|
default_trust_me(0).
|
||||||
|
execute_default(clpz_equal,2).
|
||||||
|
Is = [allocate(1),get_level(y(1)),get_variable(x(5),2),put_value(x(3),2),get_variable(x(6),3),put_value(x(5),3),put_unsafe_value(1,4),deallocate,jmp_by_execute(1),try_me_else(8),call(integer,1),neck_cut,get_variable(x(5),1),put_value(x(2),1),get_variable(x(6),2),put_value(x(5),2),jmp_by_execute(7),try_me_else(12),allocate(3),get_level(...),...].
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
|
||||||
|
%% wam_instructions(+PI, -Instrs)
|
||||||
|
%
|
||||||
|
% _Instrs_ are the WAM instructions corresponding to predicate indicator _PI_.
|
||||||
|
|
||||||
|
wam_instructions(Clause, Listing) :-
|
||||||
|
( nonvar(Clause) ->
|
||||||
|
( Clause = Name / Arity ->
|
||||||
|
fetch_instructions(user, Name, Arity, Listing)
|
||||||
|
; Clause = Module : (Name / Arity) ->
|
||||||
|
fetch_instructions(Module, Name, Arity, Listing)
|
||||||
|
)
|
||||||
|
; throw(error(instantiation_error, wam_instructions/2))
|
||||||
|
).
|
||||||
|
|
||||||
|
%% inlined_instructions(+IndexPtr, -Instrs)
|
||||||
|
%
|
||||||
|
% _Instrs_ are the WAM instructions corresponding to code offset _IndexPtr_.
|
||||||
|
|
||||||
|
inlined_instructions(IndexPtr, Listing) :-
|
||||||
|
must_be(integer, IndexPtr),
|
||||||
|
( IndexPtr >= 0 ->
|
||||||
|
'$inlined_instructions'(IndexPtr, Listing)
|
||||||
|
; throw(error(domain_error(not_less_than_zero, IndexPtr), inlined_instructions/2))
|
||||||
|
).
|
||||||
|
|
||||||
|
fetch_instructions(Module, Name, Arity, Listing) :-
|
||||||
|
must_be(atom, Module),
|
||||||
|
must_be(atom, Name),
|
||||||
|
must_be(integer, Arity),
|
||||||
|
( Arity >= 0 ->
|
||||||
|
'$wam_instructions'(Module, Name, Arity, Listing)
|
||||||
|
; throw(error(domain_error(not_less_than_zero, Arity), wam_instructions/2))
|
||||||
|
).
|
||||||
107
src/lib/dif.pl
Normal file
107
src/lib/dif.pl
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
Provides predicate `dif/2`. `dif/2` is a constraint that is true only if both of its
|
||||||
|
arguments are different terms.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(dif, [dif/2]).
|
||||||
|
|
||||||
|
:- use_module(library(atts)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(lists), [append/3, maplist/3]).
|
||||||
|
|
||||||
|
:- attribute dif/1.
|
||||||
|
|
||||||
|
put_dif_att(Var, X, Y) :-
|
||||||
|
( get_atts(Var, +dif(Z)) ->
|
||||||
|
sort([X \== Y | Z], NewZ),
|
||||||
|
put_atts(Var, +dif(NewZ))
|
||||||
|
; put_atts(Var, +dif([X \== Y]))
|
||||||
|
).
|
||||||
|
|
||||||
|
dif_set_variables([], _, _).
|
||||||
|
dif_set_variables([Var|Vars], X, Y) :-
|
||||||
|
put_dif_att(Var, X, Y),
|
||||||
|
dif_set_variables(Vars, X, Y).
|
||||||
|
|
||||||
|
remove_goal([], _, []).
|
||||||
|
remove_goal([G0|G0s], Goal0, Goals) :-
|
||||||
|
( G0 == Goal0 ->
|
||||||
|
remove_goal(G0s, Goal0, Goals)
|
||||||
|
; Goals = [G0|Goals1],
|
||||||
|
remove_goal(G0s, Goal0, Goals1)
|
||||||
|
).
|
||||||
|
|
||||||
|
vars_remove_goal([], _).
|
||||||
|
vars_remove_goal([Var|Vars], Goal0) :-
|
||||||
|
( get_atts(Var, +dif(Goals0)) ->
|
||||||
|
remove_goal(Goals0, Goal0, Goals),
|
||||||
|
( Goals = [] ->
|
||||||
|
put_atts(Var, -dif(_))
|
||||||
|
; put_atts(Var, +dif(Goals))
|
||||||
|
)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
vars_remove_goal(Vars, Goal0).
|
||||||
|
|
||||||
|
reinforce_goal(Goal0, Goal) :-
|
||||||
|
Goal = (
|
||||||
|
term_variables(Goal0, Vars),
|
||||||
|
dif:vars_remove_goal(Vars, Goal0),
|
||||||
|
Goal0 = (L \== R),
|
||||||
|
dif:dif(L, R)
|
||||||
|
).
|
||||||
|
|
||||||
|
append_goals([], _).
|
||||||
|
append_goals([Var|Vars], Goals) :-
|
||||||
|
( get_atts(Var, +dif(VarGoals)) ->
|
||||||
|
append(Goals, VarGoals, NewGoals0),
|
||||||
|
sort(NewGoals0, NewGoals)
|
||||||
|
; NewGoals = Goals
|
||||||
|
),
|
||||||
|
put_atts(Var, +dif(NewGoals)),
|
||||||
|
append_goals(Vars, Goals).
|
||||||
|
|
||||||
|
verify_attributes(Var, Value, Goals) :-
|
||||||
|
( get_atts(Var, +dif(Goals0)) ->
|
||||||
|
term_variables(Value, ValueVars),
|
||||||
|
append_goals(ValueVars, Goals0),
|
||||||
|
maplist(reinforce_goal, Goals0, Goals)
|
||||||
|
; Goals = []
|
||||||
|
).
|
||||||
|
|
||||||
|
%% dif(?X, ?Y).
|
||||||
|
%
|
||||||
|
% True iff X and Y are different terms. Unlike `\=/2`, `dif/2` is more declarative because if X and Y can
|
||||||
|
% unify but they're not yet equal, the decision is delayed, and prevents X and Y to become equal later.
|
||||||
|
% Examples:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- dif(a, a).
|
||||||
|
% false.
|
||||||
|
% ?- dif(a, b).
|
||||||
|
% true.
|
||||||
|
% ?- dif(X, b).
|
||||||
|
% dif:dif(X,b).
|
||||||
|
% ?- dif(X, b), X = b.
|
||||||
|
% false.
|
||||||
|
% ```
|
||||||
|
dif(X, Y) :-
|
||||||
|
X \== Y,
|
||||||
|
( X \= Y -> true
|
||||||
|
; term_variables(dif(X,Y), Vars),
|
||||||
|
dif_set_variables(Vars, X, Y)
|
||||||
|
).
|
||||||
|
|
||||||
|
gather_dif_goals(_, []) --> [].
|
||||||
|
gather_dif_goals(V, [(X \== Y) | Goals]) -->
|
||||||
|
( { term_variables(X-Y, [V0 | _]),
|
||||||
|
V == V0 } ->
|
||||||
|
[dif:dif(X, Y)]
|
||||||
|
; []
|
||||||
|
),
|
||||||
|
gather_dif_goals(V, Goals).
|
||||||
|
|
||||||
|
attribute_goals(X) -->
|
||||||
|
{ get_atts(X, +dif(Goals)) },
|
||||||
|
gather_dif_goals(X, Goals),
|
||||||
|
{ put_atts(X, -dif(_)) }.
|
||||||
273
src/lib/error.pl
Normal file
273
src/lib/error.pl
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
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,
|
||||||
|
call_with_error_context/2
|
||||||
|
]).
|
||||||
|
|
||||||
|
|
||||||
|
:- 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
|
||||||
|
% - pair
|
||||||
|
% - term
|
||||||
|
|
||||||
|
must_be(Type, Term) :-
|
||||||
|
must_be_(type, Type),
|
||||||
|
must_be_(Type, Term).
|
||||||
|
|
||||||
|
must_be_(Type, _) :-
|
||||||
|
var(Type),
|
||||||
|
instantiation_error(must_be/2).
|
||||||
|
must_be_(var, Term) :-
|
||||||
|
( var(Term) -> true
|
||||||
|
; throw(error(uninstantiation_error(Term), must_be/2))
|
||||||
|
).
|
||||||
|
must_be_(integer, Term) :- check_(integer, integer, Term).
|
||||||
|
must_be_(not_less_than_zero, N) :-
|
||||||
|
must_be(integer, N),
|
||||||
|
( N >= 0 -> true
|
||||||
|
; domain_error(not_less_than_zero, N, must_be/2)
|
||||||
|
).
|
||||||
|
must_be_(atom, Term) :- check_(atom, atom, Term).
|
||||||
|
must_be_(character, T) :- check_(error:character, character, T).
|
||||||
|
must_be_(in_character, T) :- check_(error:in_character, in_character, T).
|
||||||
|
must_be_(chars, Ls) :-
|
||||||
|
can_be(chars, Ls), % prioritize type errors over instantiation errors
|
||||||
|
must_be(list, Ls),
|
||||||
|
( '$is_partial_string'(Ls) ->
|
||||||
|
% The expected case (success) uses a very fast test.
|
||||||
|
% We cannot use partial_string/1 from library(iso_ext),
|
||||||
|
% because that library itself imports library(error).
|
||||||
|
true
|
||||||
|
; all_characters(Ls)
|
||||||
|
).
|
||||||
|
must_be_(octet_character, C) :-
|
||||||
|
must_be(character, C),
|
||||||
|
( octet_character(C) -> true
|
||||||
|
; domain_error(octet_character, C, must_be/2)
|
||||||
|
).
|
||||||
|
must_be_(octet_chars, Cs) :-
|
||||||
|
must_be(chars, Cs),
|
||||||
|
( '$first_non_octet'(Cs, C) ->
|
||||||
|
domain_error(octet_character, C, must_be/2)
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
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
|
||||||
|
; instantiation_error(must_be/2)
|
||||||
|
)
|
||||||
|
; type_error(term, Term, must_be/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
% We cannot use maplist(must_be(character), Cs), because library(lists)
|
||||||
|
% uses library(error), so importing it would create a cyclic dependency.
|
||||||
|
|
||||||
|
all_characters([]).
|
||||||
|
all_characters([C|Cs]) :-
|
||||||
|
must_be(character, C),
|
||||||
|
all_characters(Cs).
|
||||||
|
|
||||||
|
check_(Pred, Type, Term) :-
|
||||||
|
( var(Term) -> instantiation_error(must_be/2)
|
||||||
|
; call(Pred, Term) -> true
|
||||||
|
; type_error(Type, Term, must_be/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
pair(_-_).
|
||||||
|
|
||||||
|
boolean(B) :- ( B == true ; B == false ).
|
||||||
|
|
||||||
|
character(C) :-
|
||||||
|
atom(C),
|
||||||
|
atom_length(C, 1).
|
||||||
|
|
||||||
|
octet_character(C) :-
|
||||||
|
char_code(C, Code),
|
||||||
|
0 =< Code, Code =< 0xff.
|
||||||
|
|
||||||
|
in_character(C) :-
|
||||||
|
( character(C)
|
||||||
|
; C == end_of_file
|
||||||
|
).
|
||||||
|
|
||||||
|
ilist(Ls) :-
|
||||||
|
'$skip_max_list'(_, _, Ls, Rs),
|
||||||
|
( var(Rs) ->
|
||||||
|
instantiation_error(must_be/2)
|
||||||
|
; Rs == []
|
||||||
|
).
|
||||||
|
|
||||||
|
type(type).
|
||||||
|
type(integer).
|
||||||
|
type(atom).
|
||||||
|
type(character).
|
||||||
|
type(in_character).
|
||||||
|
type(octet_character).
|
||||||
|
type(octet_chars).
|
||||||
|
type(chars).
|
||||||
|
type(list).
|
||||||
|
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) :-
|
||||||
|
must_be(type, Type),
|
||||||
|
( var(Term) -> true
|
||||||
|
; can_(Type, Term) -> true
|
||||||
|
; type_error(Type, Term, can_be/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
can_(integer, Term) :- integer(Term).
|
||||||
|
can_(not_less_than_zero, N) :-
|
||||||
|
( integer(N) ->
|
||||||
|
( N >= 0 -> true
|
||||||
|
; domain_error(not_less_than_zero, N, can_be/2)
|
||||||
|
)
|
||||||
|
; type_error(integer, N, can_be/2)
|
||||||
|
).
|
||||||
|
can_(atom, Term) :- atom(Term).
|
||||||
|
can_(character, T) :- character(T).
|
||||||
|
can_(in_character, T) :- in_character(T).
|
||||||
|
can_(chars, Ls) :-
|
||||||
|
( '$is_partial_string'(Ls) -> true
|
||||||
|
; can_be(list, Ls),
|
||||||
|
can_be_chars(Ls)
|
||||||
|
).
|
||||||
|
can_(octet_character, C) :-
|
||||||
|
( octet_character(C) -> true
|
||||||
|
; domain_error(octet_character, C, can_be/2)
|
||||||
|
).
|
||||||
|
can_(octet_chars, Cs) :-
|
||||||
|
can_be(chars, Cs),
|
||||||
|
( '$skip_max_list'(_, _, Cs, []), % temporarily turn Cs into a list
|
||||||
|
'$first_non_octet'(Cs, C) ->
|
||||||
|
domain_error(octet_character, C, can_be/2)
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
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
|
||||||
|
; type_error(term, Term, can_be/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
can_be_chars(Var) :- var(Var), !.
|
||||||
|
can_be_chars([]).
|
||||||
|
can_be_chars([X|Xs]) :-
|
||||||
|
can_be(character, X),
|
||||||
|
can_be_chars(Xs).
|
||||||
|
|
||||||
|
list_or_partial_list(Ls) :-
|
||||||
|
'$skip_max_list'(_, _, Ls, Rs),
|
||||||
|
( var(Rs) -> true
|
||||||
|
; Rs == []
|
||||||
|
).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
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) :-
|
||||||
|
throw(error(instantiation_error, Context)).
|
||||||
|
|
||||||
|
domain_error(Type, Term, Context) :-
|
||||||
|
throw(error(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]))).
|
||||||
259
src/lib/ffi.pl
Normal file
259
src/lib/ffi.pl
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
:- 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
|
||||||
|
|
||||||
|
This module contains predicates used to call native code (exposed by the C ABI).
|
||||||
|
It uses [libffi](https://sourceware.org/libffi/) under the hood. The bridge is very simple
|
||||||
|
and is very unsafe and should be used with care. FFI isn't the only way to communicate with
|
||||||
|
the outside world in Prolog: sockets, pipes and HTTP may be good enough for your use case.
|
||||||
|
|
||||||
|
The main predicate is `use_foreign_module/2`. It takes a library name (which depending on the
|
||||||
|
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.
|
||||||
|
|
||||||
|
For each function in the list a predicate of the same name is generated in the ffi module which
|
||||||
|
can then be used to call the native code.
|
||||||
|
The predicates arguments are the input arguments of the foreign function and depending on the return type an extra argument for the return value.
|
||||||
|
Functions with return type `void` or `bool` don't have this extra argument.
|
||||||
|
Predicates for functions with return type `void` always succeed.
|
||||||
|
Predicates for functions with retun type `bool` succeed iff the return value is 1.
|
||||||
|
|
||||||
|
```
|
||||||
|
ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN, -ReturnArg). % for all return types except void and bool
|
||||||
|
ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN). % for void and bool
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available types are
|
||||||
|
|
||||||
|
### Basic C Types
|
||||||
|
|
||||||
|
[C Types Reference](https://en.cppreference.com/w/c/language/types.html)
|
||||||
|
|
||||||
|
- `void`,
|
||||||
|
- `char`, `uchar`, `schar`
|
||||||
|
- `short`, `ushort`
|
||||||
|
- `int`, `uint`
|
||||||
|
- `long`, `ulong`,
|
||||||
|
- `longlong`, `ulonglong`,
|
||||||
|
- `float`, `double`
|
||||||
|
|
||||||
|
### Fixed Width Integer Types
|
||||||
|
|
||||||
|
[C Fixed With Integer Types Reference](https://en.cppreference.com/w/c/types/integer.html)
|
||||||
|
|
||||||
|
- `sint8`/`i8`, `uint8`/`u8`,
|
||||||
|
- `sint16`/`i16`, `uint16`/`u16`,
|
||||||
|
- `sint32`/`i32`, `uint32`/`u32`,
|
||||||
|
- `sint64`/`i64`, `uint64`/`u64`,
|
||||||
|
|
||||||
|
### Fixed Width Floating-Point Types
|
||||||
|
|
||||||
|
[C++ Fixed Width Floating-Point Types Reference](https://en.cppreference.com/w/cpp/types/floating-point.html)
|
||||||
|
|
||||||
|
- `f32`, `f64`
|
||||||
|
|
||||||
|
### Other Types
|
||||||
|
|
||||||
|
- `cstr`,
|
||||||
|
- `ptr`,
|
||||||
|
- `bool` and,
|
||||||
|
- custom structs, which can be defined with `foreign_struct/2`.
|
||||||
|
|
||||||
|
### Notes regarding bool
|
||||||
|
|
||||||
|
- Not necessarily compatible with the fundamental C type bool.
|
||||||
|
- Same as `i8` but only values 0 and 1 are valid values.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
- When an ffi function returns bytes that are not a valid utf8-string the bytes will be turned into a list of `codes` (integers)
|
||||||
|
instead of a string (list of `chars`). Note: passing a list of `codes` is not accepted in argument position.
|
||||||
|
- In argument position you can also pass a pointer directly instead of a string,
|
||||||
|
e.g. to pass a null-pointer one can provide the integer 0 as the argument.
|
||||||
|
- In return position a null-pointer will be returned as the integer 0
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
For example, let's see how to define a function from the [raylib](https://www.raylib.com/) library.
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_foreign_module("./libraylib.so", ['InitWindow'([int, int, cstr], void)]).
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a `'InitWindow'` predicate under the ffi module. Now, we can call it:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- ffi:'InitWindow'(800, 600, "Scryer Prolog + Raylib").
|
||||||
|
```
|
||||||
|
|
||||||
|
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).
|
||||||
|
%
|
||||||
|
% Defines a new struct type with name Name, composed of the elements Elements, which is a list
|
||||||
|
% of other types.
|
||||||
|
%
|
||||||
|
% The name of the types doesn't matter, but the order of Elements must match the ones in the
|
||||||
|
% native code.
|
||||||
|
%
|
||||||
|
% Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- foreign_struct(color, [uint8, uint8, uint8, uint8]).
|
||||||
|
% ```
|
||||||
|
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).
|
||||||
|
|
||||||
|
assert_predicate(PredicateDefinition) :-
|
||||||
|
PredicateDefinition =.. [Name, Inputs, void],
|
||||||
|
length(Inputs, NumInputs),
|
||||||
|
functor(Head, Name, NumInputs),
|
||||||
|
term_variables(Head, TermList),
|
||||||
|
Body = (
|
||||||
|
'$foreign_call'(Name, TermList, _),!
|
||||||
|
),
|
||||||
|
Predicate = (Head:-Body),
|
||||||
|
assertz(ffi:Predicate).
|
||||||
|
|
||||||
|
assert_predicate(PredicateDefinition) :-
|
||||||
|
PredicateDefinition =.. [Name, Inputs, bool],
|
||||||
|
length(Inputs, NumInputs),
|
||||||
|
functor(Head, Name, NumInputs),
|
||||||
|
term_variables(Head, TermList),
|
||||||
|
Body = (
|
||||||
|
'$foreign_call'(Name, TermList, 1),!
|
||||||
|
),
|
||||||
|
Predicate = (Head:-Body),
|
||||||
|
assertz(ffi:Predicate).
|
||||||
|
|
||||||
|
assert_predicate(PredicateDefinition) :-
|
||||||
|
PredicateDefinition =.. [Name, Inputs, Return],
|
||||||
|
\+ member(Return, [void, bool]),
|
||||||
|
length(Inputs, NumInputs),
|
||||||
|
NumArgs is NumInputs + 1,
|
||||||
|
functor(Head, Name, NumArgs),
|
||||||
|
term_variables(Head, TermList),
|
||||||
|
Body = (
|
||||||
|
lists:append(TermListInputs, [TermListReturn], TermList),
|
||||||
|
'$foreign_call'(Name, TermListInputs, TermListReturn),!
|
||||||
|
),
|
||||||
|
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 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 length 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).
|
||||||
303
src/lib/files.pl
Normal file
303
src/lib/files.pl
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
/** Predicates for reasoning about files and directories.
|
||||||
|
|
||||||
|
In this library, directories and files are represented as
|
||||||
|
_lists of characters_. This is an ideal representation:
|
||||||
|
|
||||||
|
* Lists of characters can be conveniently reasoned about with DCGs
|
||||||
|
and built-in Prolog predicates from `library(lists)`. This alone
|
||||||
|
is already a very compelling argument to use them.
|
||||||
|
* Other Scryer libraries such as `library(http/http_open)` also already
|
||||||
|
use lists of characters to represent paths.
|
||||||
|
* File names are mostly ephemeral, so it is good for efficiency
|
||||||
|
that they can quickly allocated transiently on the heap, leaving the
|
||||||
|
atom table mostly unaffected. Indexing is almost never needed
|
||||||
|
for file names. If needed, it should be added to the engine.
|
||||||
|
* The previous point is also good for security, since the system
|
||||||
|
leaves little trace of which files were even accessed.
|
||||||
|
* Scryer Prolog represents lists of characters extremely compactly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Written 2020-2025 by Markus Triska (triska@metalevel.at)
|
||||||
|
Part of Scryer Prolog.
|
||||||
|
|
||||||
|
Predicates for reasoning about files and directories.
|
||||||
|
|
||||||
|
In this library, directories and files are represented as
|
||||||
|
*lists of characters*. This is an ideal representation:
|
||||||
|
|
||||||
|
-) Lists of characters can be conveniently reasoned about with DCGs
|
||||||
|
and built-in Prolog predicates from library(lists). This alone
|
||||||
|
is already a very compelling argument to use them.
|
||||||
|
-) Other Scryer libraries such as library(http/http_open) also already
|
||||||
|
use lists of characters to represent paths.
|
||||||
|
-) File names are mostly ephemeral, so it is good for efficiency
|
||||||
|
that they can quickly allocated transiently on the heap, leaving the
|
||||||
|
atom table mostly unaffected. Indexing is almost never needed
|
||||||
|
for file names. If needed, it should be added to the engine.
|
||||||
|
-) The previous point is also good for security, since the system
|
||||||
|
leaves little trace of which files were even accessed.
|
||||||
|
-) Scryer Prolog represents lists of characters extremely compactly.
|
||||||
|
|
||||||
|
Some Prolog programmers will likely find this representation quite
|
||||||
|
unusual, because files are represented as *atoms* in many systems.
|
||||||
|
|
||||||
|
However, the ISO standard only demands that sources and sinks be
|
||||||
|
*ground* terms, so lists of characters are completely admissible:
|
||||||
|
|
||||||
|
A source/sink is specified as an implementation defined
|
||||||
|
ground term in a call of open/4 (8.11.5). All subsequent
|
||||||
|
references to the source/sink are made by referring to a
|
||||||
|
stream-term (7.10.2) or alias (7.10.2.2).
|
||||||
|
|
||||||
|
I believe that with the advent of Scryer Prolog and its efficient
|
||||||
|
representation of strings as lists of characters, we should take
|
||||||
|
this opportunity for improvement, and in fact extend the use of
|
||||||
|
lists of characters also to other predicates like open/3.
|
||||||
|
|
||||||
|
Please note that we *cannot* simply accept *both* representations,
|
||||||
|
because that would invalidate the type errors raised by this library,
|
||||||
|
and make future extensions for type checking impossible.
|
||||||
|
|
||||||
|
So I ask you: Please try out this representation for a few months.
|
||||||
|
It simplifies working with files considerably. Once we have collected
|
||||||
|
more experience with this representations, please let us consider
|
||||||
|
how to proceed in such a way that we can call it an improvement.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
:- module(files, [directory_files/2,
|
||||||
|
file_size/2,
|
||||||
|
file_exists/1,
|
||||||
|
directory_exists/1,
|
||||||
|
delete_file/1,
|
||||||
|
rename_file/2,
|
||||||
|
file_copy/2,
|
||||||
|
delete_directory/1,
|
||||||
|
make_directory/1,
|
||||||
|
make_directory_path/1,
|
||||||
|
working_directory/2,
|
||||||
|
path_canonical/2,
|
||||||
|
path_segments/2,
|
||||||
|
file_modification_time/2,
|
||||||
|
file_creation_time/2,
|
||||||
|
file_access_time/2]).
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(charsio)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
|
||||||
|
%% directory_files(+Directory, -Files).
|
||||||
|
%
|
||||||
|
% 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).
|
||||||
|
%
|
||||||
|
% 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),
|
||||||
|
can_be(integer, Size),
|
||||||
|
'$file_size'(File, Size).
|
||||||
|
|
||||||
|
%% file_exists(+File).
|
||||||
|
%
|
||||||
|
% 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).
|
||||||
|
%
|
||||||
|
% 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).
|
||||||
|
%
|
||||||
|
% 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).
|
||||||
|
|
||||||
|
%% make_directory_path(+Directory).
|
||||||
|
%
|
||||||
|
% Similar to `make_directory/1` but recursively creates directories if they're missing.
|
||||||
|
% Equivalent to mkdir -p in Unix.
|
||||||
|
make_directory_path(Directory) :-
|
||||||
|
must_be(chars, Directory),
|
||||||
|
'$make_directory_path'(Directory).
|
||||||
|
|
||||||
|
%% delete_file(+File).
|
||||||
|
%
|
||||||
|
% Succeeds if deletes File from the current system.
|
||||||
|
delete_file(File) :-
|
||||||
|
file_must_exist(File, delete_file/1),
|
||||||
|
'$delete_file'(File).
|
||||||
|
|
||||||
|
%% rename_file(+File, +Renamed).
|
||||||
|
%
|
||||||
|
% Succeeds if File is renamed to Renamed
|
||||||
|
rename_file(File, Renamed) :-
|
||||||
|
file_must_exist(File, rename_file/2),
|
||||||
|
must_be(chars, Renamed),
|
||||||
|
'$rename_file'(File, Renamed).
|
||||||
|
|
||||||
|
%% file_copy(+File, +Copied).
|
||||||
|
%
|
||||||
|
% Succeeds if File is copied to Copied
|
||||||
|
file_copy(File, Copied) :-
|
||||||
|
file_must_exist(File, file_copy/2),
|
||||||
|
must_be(chars, Copied),
|
||||||
|
'$file_copy'(File, Copied).
|
||||||
|
|
||||||
|
%% delete_directory(+Directory).
|
||||||
|
%
|
||||||
|
% Succeeds if Directory is deleted from the current system.
|
||||||
|
% Directory must be empty.
|
||||||
|
delete_directory(Directory) :-
|
||||||
|
directory_must_exist(Directory, delete_directory/1),
|
||||||
|
must_be(chars, Directory),
|
||||||
|
'$delete_directory'(Directory).
|
||||||
|
|
||||||
|
file_must_exist(File, Context) :-
|
||||||
|
( file_exists(File) -> true
|
||||||
|
; throw(error(existence_error(file, File), Context))
|
||||||
|
).
|
||||||
|
|
||||||
|
directory_must_exist(Directory, Context) :-
|
||||||
|
( directory_exists(Directory) -> true
|
||||||
|
; throw(error(existence_error(directory, Directory), Context))
|
||||||
|
).
|
||||||
|
|
||||||
|
%% working_directory(Dir0, Dir).
|
||||||
|
%
|
||||||
|
% Dir0 is the current working directory, and the working directory
|
||||||
|
% is changed to Dir.
|
||||||
|
%
|
||||||
|
% Use `working_directory/2` to determine the current working directory,
|
||||||
|
% and leave it as is.
|
||||||
|
|
||||||
|
working_directory(Dir0, Dir) :-
|
||||||
|
can_be(list, Dir0),
|
||||||
|
can_be(list, Dir),
|
||||||
|
'$working_directory'(Dir0, Dir).
|
||||||
|
|
||||||
|
%% path_canonical(Ps, Cs).
|
||||||
|
%
|
||||||
|
% True iff Cs is the canonical, absolute path of Ps.
|
||||||
|
%
|
||||||
|
% All intermediate components are normalized, and all symbolic links
|
||||||
|
% are resolved.
|
||||||
|
%
|
||||||
|
% The predicate fails in the following situations, though not
|
||||||
|
% necessarily *only* in these cases:
|
||||||
|
%
|
||||||
|
% 1. Ps is a path that does not exist.
|
||||||
|
% 2. A non-final component in Ps is not a directory.
|
||||||
|
|
||||||
|
path_canonical(Ps, Cs) :-
|
||||||
|
must_be(chars, Ps),
|
||||||
|
can_be(list, Cs),
|
||||||
|
'$path_canonical'(Ps, Cs).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
T is, respectively, the modification, access or creation time of File.
|
||||||
|
T is a time stamp, suitable for use in format_time//2 in library(time).
|
||||||
|
|
||||||
|
For two time stamps A and B, if A precedes B, then A @< B holds.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
%% file_modification_time(+File, ?T).
|
||||||
|
%
|
||||||
|
% For a file `File` that must exist, `T` is the modification 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).
|
||||||
|
%
|
||||||
|
% 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).
|
||||||
|
%
|
||||||
|
% 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) :-
|
||||||
|
file_time_(File, creation, T).
|
||||||
|
|
||||||
|
file_time_(File, Which, T) :-
|
||||||
|
file_must_exist(File, file_time_/3),
|
||||||
|
'$file_time'(File, Which, T0),
|
||||||
|
read_from_chars(T0, T).
|
||||||
|
|
||||||
|
|
||||||
|
%% path_segments(?Ps, ?Segments).
|
||||||
|
%
|
||||||
|
% True iff Segments are the segments of Ps.
|
||||||
|
%
|
||||||
|
% Segments is the list of components of the path Ps that are
|
||||||
|
% separated by the platform-specific directory separator. Each
|
||||||
|
% segment is a list of characters.
|
||||||
|
%
|
||||||
|
% At least one of the arguments must be instantiated.
|
||||||
|
%
|
||||||
|
% Examples:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- path_segments("/hello/there", Segments).
|
||||||
|
% Segments = [[],"hello","there"].
|
||||||
|
% ?- path_segments(Path, ["hello","there"]).
|
||||||
|
% Path = "hello/there".
|
||||||
|
% ```
|
||||||
|
%
|
||||||
|
% To obtain the platform-specific directory separator, you can use:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- path_segments(Separator, ["",""]).
|
||||||
|
% Separator = "/".
|
||||||
|
% ```
|
||||||
|
|
||||||
|
path_segments(Path, Segments) :-
|
||||||
|
'$directory_separator'(Sep),
|
||||||
|
( var(Path) ->
|
||||||
|
must_be(list, Segments),
|
||||||
|
maplist(must_be(chars), Segments),
|
||||||
|
phrase(append_with_separator(Segments, Sep), Path)
|
||||||
|
; must_be(chars, Path),
|
||||||
|
path_to_segments(Path, Sep, Segments)
|
||||||
|
).
|
||||||
|
|
||||||
|
append_with_separator([], _) --> [].
|
||||||
|
append_with_separator([Segment|Segments], Sep) -->
|
||||||
|
append_with_separator_(Segments, Segment, Sep).
|
||||||
|
|
||||||
|
append_with_separator_([], Segment, _) --> seq(Segment).
|
||||||
|
append_with_separator_([Segment|Segments], Prev, Sep) -->
|
||||||
|
seq(Prev), [Sep],
|
||||||
|
append_with_separator_(Segments, Segment, Sep).
|
||||||
|
|
||||||
|
path_to_segments(Path, Sep, Segments) :-
|
||||||
|
( append(Front, [Sep|Ps], Path) ->
|
||||||
|
Segments = [Front|Rest],
|
||||||
|
path_to_segments(Ps, Sep, Rest)
|
||||||
|
; Segments = [Path]
|
||||||
|
).
|
||||||
719
src/lib/format.pl
Normal file
719
src/lib/format.pl
Normal file
@@ -0,0 +1,719 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
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.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
/** This library provides the nonterminal `format_//2` to describe
|
||||||
|
formatted strings. `format/[2,3]` are provided for _impure_ output.
|
||||||
|
|
||||||
|
The entire library only works if the Prolog flag `double_quotes`
|
||||||
|
is set to `chars`, the default value in Scryer Prolog. This should
|
||||||
|
also stay that way, to encourage a sensible environment.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(format, [format_//2,
|
||||||
|
format/2,
|
||||||
|
format/3,
|
||||||
|
portray_clause_//1,
|
||||||
|
portray_clause/1,
|
||||||
|
portray_clause/2,
|
||||||
|
listing/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(charsio)).
|
||||||
|
:- use_module(library(between)).
|
||||||
|
:- use_module(library(pio)).
|
||||||
|
|
||||||
|
%% format_(+FormatString, +Arguments)//
|
||||||
|
%
|
||||||
|
% Usage:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% phrase(format_(FormatString, Arguments), Ls)
|
||||||
|
% ```
|
||||||
|
%
|
||||||
|
% `format_//2` describes a list of characters Ls that are formatted
|
||||||
|
% according to FormatString. FormatString is a string (i.e., a list of
|
||||||
|
% characters) that specifies the layout of Ls. The characters in
|
||||||
|
% FormatString are used literally, except for the following tokens
|
||||||
|
% with special meaning:
|
||||||
|
%
|
||||||
|
% | `~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 |
|
||||||
|
% | `~d` | use the next argument here, which must be an integer |
|
||||||
|
% | `~f` | use the next argument here, a floating point number |
|
||||||
|
% | `~Nf` | where N is an integer: format the float argument |
|
||||||
|
% | | using N digits after the decimal point |
|
||||||
|
% | `~Nd` | like ~d, placing the last N digits after a decimal point; |
|
||||||
|
% | | if N is 0 or omitted, no decimal point is used. |
|
||||||
|
% | `~ND` | like ~Nd, separating digits to the left of the decimal point |
|
||||||
|
% | | in groups of three, using the character "," (comma) |
|
||||||
|
% | `~NU` | like ~ND, using "_" (underscore) to separate groups of digits |
|
||||||
|
% | `~NL` | format an integer so that at most N digits appear on a line. |
|
||||||
|
% | | If N is 0 or omitted, it defaults to 72. |
|
||||||
|
% | `~Nr` | where N is an integer between 2 and 36: format the |
|
||||||
|
% | | next argument, which must be an integer, in radix N. |
|
||||||
|
% | | The characters "a" to "z" are used for radices 10 to 36. |
|
||||||
|
% | | If N is omitted, it defaults to 8 (octal). |
|
||||||
|
% | `~NR` | like ~Nr, except that "A" to "Z" are used for radices > 9 |
|
||||||
|
% | `~|` | place a tab stop at this position |
|
||||||
|
% | `~N|` | where N is an integer: place a tab stop at text column N |
|
||||||
|
% | `~N+` | where N is an integer: place a tab stop N characters |
|
||||||
|
% | | after the previous tab stop (or start of line) |
|
||||||
|
% | `~t` | distribute spaces evenly between the two closest tab stops |
|
||||||
|
% | ``~`Ct`` | like ~t, use character C instead of spaces to fill the space |
|
||||||
|
% | `~n` | newline |
|
||||||
|
% | `~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.
|
||||||
|
%
|
||||||
|
% Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs).
|
||||||
|
% Cs = "hello\n......there!".
|
||||||
|
% ```
|
||||||
|
|
||||||
|
format_(Fs, Args) -->
|
||||||
|
{ 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),
|
||||||
|
format_cells(Cells).
|
||||||
|
|
||||||
|
format_cell(newline) --> "\n".
|
||||||
|
format_cell(cell(From,To,Es)) -->
|
||||||
|
% distribute the space between the glue elements
|
||||||
|
{ phrase(elements_gluevars(Es, 0, Length), Vs),
|
||||||
|
( Vs = [] -> true
|
||||||
|
; Space is To - From - Length,
|
||||||
|
( Space =< 0 -> maplist(=(0), Vs)
|
||||||
|
; length(Vs, NumGlue),
|
||||||
|
Distr is Space // NumGlue,
|
||||||
|
Delta is Space - Distr*NumGlue,
|
||||||
|
( Delta =:= 0 ->
|
||||||
|
maplist(=(Distr), Vs)
|
||||||
|
; BigGlue is Distr + Delta,
|
||||||
|
reverse(Vs, [BigGlue|Rest]),
|
||||||
|
maplist(=(Distr), Rest)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) },
|
||||||
|
format_elements(Es).
|
||||||
|
|
||||||
|
format_elements([]) --> [].
|
||||||
|
format_elements([E|Es]) -->
|
||||||
|
format_element(E),
|
||||||
|
format_elements(Es).
|
||||||
|
|
||||||
|
format_element(chars(Cs)) --> seq(Cs).
|
||||||
|
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) -->
|
||||||
|
element_gluevar(E, N0, N1),
|
||||||
|
elements_gluevars(Es, N1, N).
|
||||||
|
|
||||||
|
element_gluevar(chars(Cs), N0, N) -->
|
||||||
|
{ 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.
|
||||||
|
A cell has the shape cell(From,To,Elements), where
|
||||||
|
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), 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. 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
|
||||||
|
consume whitespace in the sense of format strings.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
cells([], Args, Tab, Es, _) --> !,
|
||||||
|
( { Args == [] } -> cell(Tab, Tab, Es)
|
||||||
|
; { domain_error(empty_list, Args, format_//2) }
|
||||||
|
).
|
||||||
|
cells([~,~|Fs], Args, Tab, Es, VNs) --> !,
|
||||||
|
cells(Fs, Args, Tab, [chars("~")|Es], VNs).
|
||||||
|
cells([~,w|Fs], [Arg|Args], Tab, 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) --> !,
|
||||||
|
{ 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) --> !,
|
||||||
|
{ 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]) },
|
||||||
|
!,
|
||||||
|
{ 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]) },
|
||||||
|
!,
|
||||||
|
{ 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]) },
|
||||||
|
!,
|
||||||
|
{ 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]) },
|
||||||
|
!,
|
||||||
|
{ 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) --> !,
|
||||||
|
cell(Tab, Tab, Es),
|
||||||
|
n_newlines(1),
|
||||||
|
cells(Fs, Args, 0, [], VNs).
|
||||||
|
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||||
|
{ numeric_argument(Fs0, Num, [n|Fs], Args0, Args) },
|
||||||
|
!,
|
||||||
|
cell(Tab, Tab, Es),
|
||||||
|
n_newlines(Num),
|
||||||
|
cells(Fs, Args, 0, [], VNs).
|
||||||
|
cells([~,s|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||||
|
cells(Fs, Args, Tab, [chars(Arg)|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]) },
|
||||||
|
!,
|
||||||
|
{ 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]) },
|
||||||
|
!,
|
||||||
|
{ 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]) },
|
||||||
|
!,
|
||||||
|
{ 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) --> !,
|
||||||
|
( { 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) },
|
||||||
|
!,
|
||||||
|
cell(Tab, Num, Es),
|
||||||
|
cells(Fs, Args, Num, [], VNs).
|
||||||
|
cells([~|Fs0], Args0, Tab0, Es, VNs) -->
|
||||||
|
{ numeric_argument(Fs0, Num, [+|Fs], Args0, Args) },
|
||||||
|
!,
|
||||||
|
( { 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 == [] } ->
|
||||||
|
{ domain_error(non_empty_list, [], format_//2) }
|
||||||
|
; { domain_error(format_string, [~|Cs], format_//2) }
|
||||||
|
).
|
||||||
|
cells(Fs0, Args, Tab, Es, VNs) -->
|
||||||
|
{ phrase(upto_what(Fs1, ~), Fs0, Fs),
|
||||||
|
Fs1 = [_|_] },
|
||||||
|
cells(Fs, Args, Tab, [chars(Fs1)|Es], VNs).
|
||||||
|
|
||||||
|
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) --> [].
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
?- phrase(format:upto_what(Cs, ~), "abc~test", Rest).
|
||||||
|
Cs = "abc", Rest = "~test".
|
||||||
|
?- phrase(format:upto_what(Cs, ~), "abc", Rest).
|
||||||
|
Cs = "abc", Rest = [].
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
separate_digits_fractional(Arg, Sep, Num, Cs) :-
|
||||||
|
number_chars(Num, NCs),
|
||||||
|
phrase(("~",seq(NCs),"d"), FStr),
|
||||||
|
phrase(format_(FStr, [Arg]), Cs0),
|
||||||
|
phrase(upto_what(Bs0, .), Cs0, Ds),
|
||||||
|
reverse(Bs0, Bs1),
|
||||||
|
phrase(groups_of_three(Bs1,Sep), Bs2),
|
||||||
|
reverse(Bs2, Bs),
|
||||||
|
append(Bs, Ds, Cs).
|
||||||
|
|
||||||
|
upto_what([], W), [W] --> [W], !.
|
||||||
|
upto_what([C|Cs], W) --> [C], !, upto_what(Cs, W).
|
||||||
|
upto_what([], _) --> [].
|
||||||
|
|
||||||
|
groups_of_three([A,B,C,D|Rs], Sep) --> !, [A,B,C,Sep], groups_of_three([D|Rs], Sep).
|
||||||
|
groups_of_three(Ls, _) --> seq(Ls).
|
||||||
|
|
||||||
|
split_lines_width(Cs, Num) -->
|
||||||
|
( { length(Prefix, Num),
|
||||||
|
append(Prefix, [R|Rs], Cs) } ->
|
||||||
|
seq(Prefix), "_\n",
|
||||||
|
split_lines_width([R|Rs], Num)
|
||||||
|
; seq(Cs)
|
||||||
|
).
|
||||||
|
|
||||||
|
cell(From, To, Es0) -->
|
||||||
|
( { Es0 == [] } -> []
|
||||||
|
; { reverse(Es0, Es) },
|
||||||
|
[cell(From,To,Es)]
|
||||||
|
).
|
||||||
|
|
||||||
|
%?- format:numeric_argument("2f", Num, [f|Fs], Args0, Args).
|
||||||
|
|
||||||
|
%?- format:numeric_argument("100b", Num, Rs, Args0, Args).
|
||||||
|
|
||||||
|
numeric_argument(Ds, Num, Rest, Args0, Args) :-
|
||||||
|
( Ds = [*|Rest] ->
|
||||||
|
Args0 = [Num|Args]
|
||||||
|
; phrase(numeric_argument_(Ds, Rest), Ns),
|
||||||
|
foldl(plus_times10, Ns, 0, Num),
|
||||||
|
Args0 = Args
|
||||||
|
).
|
||||||
|
|
||||||
|
numeric_argument_([D|Ds], Rest) -->
|
||||||
|
( { member(D, "0123456789") } ->
|
||||||
|
{ number_chars(N, [D]) },
|
||||||
|
[N],
|
||||||
|
numeric_argument_(Ds, Rest)
|
||||||
|
; { Rest = [D|Ds] }
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
plus_times10(D, N0, N) :- N is D + N0*10.
|
||||||
|
|
||||||
|
radix_error(lowercase, R) --> format_("~~~dr", [R]).
|
||||||
|
radix_error(uppercase, R) --> format_("~~~dR", [R]).
|
||||||
|
|
||||||
|
integer_to_radix(I0, R, Which, Cs) :-
|
||||||
|
I is I0, % evaluate compound expression
|
||||||
|
must_be(integer, I),
|
||||||
|
must_be(integer, R),
|
||||||
|
( \+ between(2, 36, R) ->
|
||||||
|
phrase(radix_error(Which,R), Es),
|
||||||
|
domain_error(format_string, Es, format_//2)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
digits(Which, Ds),
|
||||||
|
( I < 0 ->
|
||||||
|
Pos is abs(I),
|
||||||
|
phrase(integer_to_radix_(Pos, R, Ds), Cs0, "-")
|
||||||
|
; I =:= 0 -> Cs0 = "0"
|
||||||
|
; phrase(integer_to_radix_(I, R, Ds), Cs0)
|
||||||
|
),
|
||||||
|
reverse(Cs0, Cs).
|
||||||
|
|
||||||
|
integer_to_radix_(0, _, _) --> !.
|
||||||
|
integer_to_radix_(I0, R, Ds) -->
|
||||||
|
{ M is I0 mod R,
|
||||||
|
nth0(M, Ds, D),
|
||||||
|
I is I0 // R },
|
||||||
|
[D],
|
||||||
|
integer_to_radix_(I, R, Ds).
|
||||||
|
|
||||||
|
digits(lowercase, "0123456789abcdefghijklmnopqrstuvwxyz").
|
||||||
|
digits(uppercase, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Impure I/O, implemented as a small wrapper over format_//2.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
%% format(+Fs, +Args)
|
||||||
|
%
|
||||||
|
% The predicate `format/2` is like `format_//2`, except that it
|
||||||
|
% outputs the text on the terminal instead of describing it
|
||||||
|
% declaratively as a list of characters.
|
||||||
|
%
|
||||||
|
% If at all possible, `format_//2` should be used, to stress pure
|
||||||
|
% parts that enable easy testing etc. If necessary, you can emit the
|
||||||
|
% described list of characters `Ls` with `maplist(put_char, Ls)` or,
|
||||||
|
% much faster, with `format("~s", [Ls])`. Ideally, however, you use
|
||||||
|
% `phrase_to_file/[2,3]` or `phrase_to_stream/2` from `library(pio)`
|
||||||
|
% to write the described list directly to a file or stream,
|
||||||
|
% respectively: `phrase_to_stream(format_(..., [...]), S)`. The
|
||||||
|
% advantage of this is that an ideal implementation writes the
|
||||||
|
% characters as they become known, without manifesting the list.
|
||||||
|
|
||||||
|
format(_, _) :- not_used.
|
||||||
|
|
||||||
|
user:goal_expansion(format(Fs, Args),
|
||||||
|
( current_output(Stream),
|
||||||
|
format(Stream, Fs, Args))).
|
||||||
|
|
||||||
|
%% format(Stream, FormatString, Arguments)
|
||||||
|
%
|
||||||
|
% Output the described string to the given Stream. If Stream is a
|
||||||
|
% binary stream, then the code of each emitted character must be in
|
||||||
|
% 0..255.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
?- phrase(format:cells("hello~10|", [], 0, [], []), Cs).
|
||||||
|
?- phrase(format:cells("~ta~t~10|", [], 0, [], []), Cs).
|
||||||
|
|
||||||
|
?- phrase(format_("~`at~50|", []), Ls).
|
||||||
|
|
||||||
|
?- phrase(format:cells("~`at~50|", [], 0, [], []), Cs),
|
||||||
|
phrase(format:format_cells(Cs), Ls).
|
||||||
|
?- phrase(format:cells("~ta~t~tb~tc~21|", [], 0, [], []), Cs).
|
||||||
|
Cs = [cell(0,21,[glue(' ',_A),chars("a"),glue(' ',_B),glue(' ',_C),chars("b"),glue(' ',_D),chars("c")])].
|
||||||
|
?- phrase(format:cells("~ta~t~4|", [], 0, [], []), Cs).
|
||||||
|
Cs = [cell(0,4,[glue(' ',_A),chars("a"),glue(' ',_B)])].
|
||||||
|
|
||||||
|
?- phrase(format:format_cell(cell(0,1,[glue(a,_94)])), Ls).
|
||||||
|
|
||||||
|
?- phrase(format:format_cell(cell(0,50,[chars("hello")])), Ls).
|
||||||
|
|
||||||
|
?- phrase(format_("~`at~50|~n", []), Ls).
|
||||||
|
?- phrase(format_("hello~n~tthere~6|", []), Ls).
|
||||||
|
|
||||||
|
?- format("~ta~t~4|", []).
|
||||||
|
a true.
|
||||||
|
|
||||||
|
?- format("~ta~tb~tc~10|", []).
|
||||||
|
a b c true.
|
||||||
|
|
||||||
|
?- format("~tabc~3|", []).
|
||||||
|
|
||||||
|
?- format("~ta~t~4|", []).
|
||||||
|
|
||||||
|
?- format("~ta~t~tb~tc~20|", []).
|
||||||
|
a b c true.
|
||||||
|
|
||||||
|
?- format("~2f~n", [3]).
|
||||||
|
3.00
|
||||||
|
true.
|
||||||
|
|
||||||
|
?- format("~20f", [0.1]).
|
||||||
|
0.10000000000000000000 true.
|
||||||
|
|
||||||
|
?- X is atan(2), format("~7f~n", [X]).
|
||||||
|
1.1071487
|
||||||
|
X = 1.1071487177940906.
|
||||||
|
|
||||||
|
?- format("~`at~50|~n", []).
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
true
|
||||||
|
|
||||||
|
?- format("~t~N", []).
|
||||||
|
|
||||||
|
?- format("~q", [.]).
|
||||||
|
'.' true.
|
||||||
|
|
||||||
|
?- format("~12r", [300]).
|
||||||
|
210 true.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
We also provide rudimentary versions of portray_clause/1 and listing/1.
|
||||||
|
|
||||||
|
In the eventual library organization, portray_clause/1 and
|
||||||
|
related predicates may be placed in their own dedicated library.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
|
||||||
|
%% portray_clause(+Term)
|
||||||
|
%
|
||||||
|
% `portray_clause/1` is useful for printing solutions in such a way
|
||||||
|
% that they can be read back with `read/1`.
|
||||||
|
|
||||||
|
portray_clause(Term) :-
|
||||||
|
current_output(Out),
|
||||||
|
portray_clause(Out, Term).
|
||||||
|
|
||||||
|
portray_clause(Stream, Term) :-
|
||||||
|
phrase_to_stream(portray_clause_(Term), Stream),
|
||||||
|
flush_output(Stream).
|
||||||
|
|
||||||
|
portray_clause_(Term) -->
|
||||||
|
{ unique_variable_names(numbervars, Term, VNs) },
|
||||||
|
portray_(Term, VNs), ".\n".
|
||||||
|
|
||||||
|
literal(Lit, VNs) -->
|
||||||
|
{ write_term_to_chars(Lit, [quoted(true),variable_names(VNs),double_quotes(true)], 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) --> !,
|
||||||
|
literal(Head, VNs), " :-\n",
|
||||||
|
body_(Body, 0, 3, VNs).
|
||||||
|
portray_((Head --> Body), VNs) --> !,
|
||||||
|
literal(Head, VNs), " -->\n",
|
||||||
|
body_(Body, 0, 3, VNs).
|
||||||
|
portray_(Any, VNs) --> literal_(Any, VNs).
|
||||||
|
|
||||||
|
|
||||||
|
body_(Var, C, I, VNs) --> { var(Var) }, !,
|
||||||
|
indent_to(C, I),
|
||||||
|
literal(Var, VNs).
|
||||||
|
body_((A,B), C, I, VNs) --> !,
|
||||||
|
body_(A, C, I, VNs), ",\n",
|
||||||
|
body_(B, 0, I, VNs).
|
||||||
|
body_(Body, C, I, VNs) -->
|
||||||
|
{ body_if_then_else(Body, If, Then, Else) },
|
||||||
|
!,
|
||||||
|
indent_to(C, I),
|
||||||
|
"( ",
|
||||||
|
{ C1 is I + 3 },
|
||||||
|
body_(If, C1, C1, VNs), " ->\n",
|
||||||
|
body_(Then, 0, C1, VNs), "\n",
|
||||||
|
else_branch(Else, I, VNs).
|
||||||
|
body_((A;B), C, I, VNs) --> !,
|
||||||
|
indent_to(C, I),
|
||||||
|
"( ",
|
||||||
|
{ C1 is I + 3 },
|
||||||
|
body_(A, C1, C1, VNs), "\n",
|
||||||
|
else_branch(B, I, VNs).
|
||||||
|
body_(Goal, C, I, VNs) -->
|
||||||
|
indent_to(C, I), literal_(Goal, VNs).
|
||||||
|
|
||||||
|
|
||||||
|
% True iff Body has the shape ( If -> Then ; Else ).
|
||||||
|
body_if_then_else(Body, If, Then, Else) :-
|
||||||
|
nonvar(Body),
|
||||||
|
Body = (A ; Else),
|
||||||
|
nonvar(A),
|
||||||
|
A = (If -> Then).
|
||||||
|
|
||||||
|
else_branch(Else, I, VNs) -->
|
||||||
|
indent_to(0, I),
|
||||||
|
"; ",
|
||||||
|
{ C is I + 3 },
|
||||||
|
( { body_if_then_else(Else, If, Then, NextElse) } ->
|
||||||
|
body_(If, C, C, VNs), " ->\n",
|
||||||
|
body_(Then, 0, C, VNs), "\n",
|
||||||
|
else_branch(NextElse, I, VNs)
|
||||||
|
; { nonvar(Else), Else = ( A ; B ) } ->
|
||||||
|
body_(A, C, C, VNs), "\n",
|
||||||
|
else_branch(B, I, VNs)
|
||||||
|
; body_(Else, C, C, VNs), "\n",
|
||||||
|
indent_to(0, I),
|
||||||
|
")"
|
||||||
|
).
|
||||||
|
|
||||||
|
indent_to(CurrentColumn, Indent) -->
|
||||||
|
format_("~t~*|", [Indent-CurrentColumn]).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
?- portray_clause(a).
|
||||||
|
a.
|
||||||
|
|
||||||
|
?- portray_clause((a :- b)).
|
||||||
|
a :-
|
||||||
|
b.
|
||||||
|
|
||||||
|
?- portray_clause((a :- b, c, d)).
|
||||||
|
a :-
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
d.
|
||||||
|
true.
|
||||||
|
|
||||||
|
|
||||||
|
?- portray_clause([a,b,c,d]).
|
||||||
|
"abcd".
|
||||||
|
|
||||||
|
?- portray_clause(X).
|
||||||
|
?- portray_clause((f(X) :- X)).
|
||||||
|
|
||||||
|
?- portray_clause((h :- ( a -> b; c))).
|
||||||
|
|
||||||
|
?- portray_clause((h :- ( (a -> x ; y) -> b; c))).
|
||||||
|
|
||||||
|
?- portray_clause((h(X) :- ( (a(X) ; y(A,B)) -> b; c))).
|
||||||
|
|
||||||
|
?- portray_clause((h :- (a,d;b,c) ; (b,e;d))).
|
||||||
|
|
||||||
|
?- portray_clause((a :- b ; c ; d)).
|
||||||
|
|
||||||
|
?- portray_clause((h :- L = '.')).
|
||||||
|
|
||||||
|
?- portray_clause(-->(a, (b, {t}, d))).
|
||||||
|
|
||||||
|
?- portray_clause((A :- B)).
|
||||||
|
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
listing(PI) :-
|
||||||
|
nonvar(PI),
|
||||||
|
( PI = Name/Arity0 ->
|
||||||
|
Arity = Arity0
|
||||||
|
; PI = Name//Arity0 ->
|
||||||
|
Arity is Arity0 + 2
|
||||||
|
; type_error(predicate_indicator, PI, listing/1)
|
||||||
|
),
|
||||||
|
functor(Head, Name, Arity),
|
||||||
|
\+ \+ clause(Head, _), % only true if there is at least one clause
|
||||||
|
( clause(Head, Body),
|
||||||
|
( Body == true ->
|
||||||
|
portray_clause(Head)
|
||||||
|
; portray_clause((Head :- Body))
|
||||||
|
),
|
||||||
|
false
|
||||||
|
; true
|
||||||
|
).
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
:- module(freeze, [freeze/2]).
|
:- module(freeze, [freeze/2]).
|
||||||
|
|
||||||
|
/** Provides the constraint `freeze/2`.
|
||||||
|
*/
|
||||||
|
|
||||||
:- use_module(library(atts)).
|
:- use_module(library(atts)).
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
|
|
||||||
|
:- meta_predicate(freeze(-, 0)).
|
||||||
|
|
||||||
:- attribute frozen/1.
|
:- attribute frozen/1.
|
||||||
|
|
||||||
verify_attributes(Var, Other, Goals) :-
|
verify_attributes(Var, Other, Goals) :-
|
||||||
@@ -17,6 +22,15 @@ verify_attributes(Var, Other, Goals) :-
|
|||||||
).
|
).
|
||||||
verify_attributes(_, _, []).
|
verify_attributes(_, _, []).
|
||||||
|
|
||||||
|
%% freeze(Var, Goal)
|
||||||
|
%
|
||||||
|
% Schedules Goal to be executed when Var is instantiated. This can
|
||||||
|
% be useful to observe the exact moment a variable becomes bound to a
|
||||||
|
% more concrete term, for example when creating animations of search
|
||||||
|
% processes. Higher-level constructs such as `phrase_from_file/2` can
|
||||||
|
% also be implemented with `freeze/2`, by scheduling a goal that
|
||||||
|
% reads additional data from a file as soon as it is needed.
|
||||||
|
|
||||||
freeze(X, Goal) :-
|
freeze(X, Goal) :-
|
||||||
put_atts(Fresh, frozen(Goal)),
|
put_atts(Fresh, frozen(Goal)),
|
||||||
Fresh = X.
|
Fresh = X.
|
||||||
@@ -24,5 +38,5 @@ freeze(X, Goal) :-
|
|||||||
attribute_goals(Var) -->
|
attribute_goals(Var) -->
|
||||||
{ get_atts(Var, frozen(Goals)),
|
{ get_atts(Var, frozen(Goals)),
|
||||||
put_atts(Var, -frozen(_)) },
|
put_atts(Var, -frozen(_)) },
|
||||||
[freeze(Var, Goals)].
|
[freeze:freeze(Var, Goals)].
|
||||||
|
|
||||||
@@ -19,14 +19,14 @@ gensym(Base, Unique) :-
|
|||||||
must_be(var, Unique),
|
must_be(var, Unique),
|
||||||
atom_si(Base),
|
atom_si(Base),
|
||||||
gensym_key(Base, BaseKey),
|
gensym_key(Base, BaseKey),
|
||||||
( bb_get(BaseKey, UniqueID0) ->
|
( bb_get(BaseKey, UniqueID0) -> true
|
||||||
|
; UniqueID0 = 0
|
||||||
|
),
|
||||||
UniqueID is UniqueID0 + 1,
|
UniqueID is UniqueID0 + 1,
|
||||||
bb_put(BaseKey, UniqueID),
|
append_id(Base, UniqueID, Unique),
|
||||||
append_id(Base, UniqueID, Unique)
|
bb_put(BaseKey, UniqueID).
|
||||||
; bb_put(BaseKey, 1),
|
|
||||||
append_id(Base, 1, Unique)
|
|
||||||
).
|
|
||||||
|
|
||||||
reset_gensym(Base) :-
|
reset_gensym(Base) :-
|
||||||
atom_si(Base),
|
atom_si(Base),
|
||||||
bb_put(Base, 0).
|
gensym_key(Base, BaseKey),
|
||||||
|
bb_put(BaseKey, 0).
|
||||||
73
src/lib/http/http_open.pl
Normal file
73
src/lib/http/http_open.pl
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Written 2022 by Adrián Arroyo Calle (adrian.arroyocalle@gmail.com)
|
||||||
|
Part of Scryer Prolog.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Make HTTP requests.
|
||||||
|
|
||||||
|
This library contains the predicate `http_open/3` which allows you to perform HTTP(S) calls.
|
||||||
|
Useful for making API calls, or parsing websites. It uses Hyper underneath.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(http_open, [http_open/3]).
|
||||||
|
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
|
%% http_open(+Address, -Stream, +Options).
|
||||||
|
%
|
||||||
|
% Yields Stream to read the body of an HTTP reply from Address.
|
||||||
|
% Address is a list of characters, and includes the method. Both HTTP
|
||||||
|
% and HTTPS are supported.
|
||||||
|
%
|
||||||
|
% Options supported:
|
||||||
|
%
|
||||||
|
% * `method(+Method)`: Sets the HTTP method of the call. Method can be `get` (default), `head`, `delete`, `post`, `put` or `patch`.
|
||||||
|
% * `data(+Data)`: Data to be sent in the request. Useful for POST, PUT and PATCH operations.
|
||||||
|
% * `size(-Size)`: Unifies with the value of the Content-Length header
|
||||||
|
% * `request_headers(+RequestHeaders)`: Headers to be used in the request
|
||||||
|
% * `headers(-ListHeaders)`: Unifies with a list with all headers returned in the response
|
||||||
|
% * `status_code(-Code)`: Unifies with the status code of the request (200, 201, 404, ...)
|
||||||
|
%
|
||||||
|
% Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- http_open("https://www.example.com", S, []), get_n_chars(S, N, HTML).
|
||||||
|
% S = '$stream'(0x7fb548001be8), N = 1256, HTML = "<!doctype html>\n<ht ...".
|
||||||
|
% ```
|
||||||
|
http_open(Address, Response, Options) :-
|
||||||
|
parse_http_options(Options, OptionValues),
|
||||||
|
( member(method(Method), OptionValues) -> true; Method = get),
|
||||||
|
( member(data(Data), OptionValues) -> true; Data = []),
|
||||||
|
( member(request_headers(RequestHeaders), OptionValues) -> true; RequestHeaders = ['user-agent'("Scryer Prolog")]),
|
||||||
|
( member(status_code(Code), OptionValues) -> true; true),
|
||||||
|
( member(headers(Headers), OptionValues) -> true; true),
|
||||||
|
( member(size(Size), OptionValues) -> member('content-length'(Size), Headers); true),
|
||||||
|
'$http_open'(Address, Response, Method, Code, Data, Headers, RequestHeaders).
|
||||||
|
|
||||||
|
parse_http_options(Options, OptionValues) :-
|
||||||
|
maplist(parse_http_options_, Options, OptionValues).
|
||||||
|
|
||||||
|
parse_http_options_(method(Method), method(Method)) :-
|
||||||
|
( var(Method) ->
|
||||||
|
throw(error(instantiation_error, http_open/3))
|
||||||
|
;
|
||||||
|
member(Method, [get, post, put, delete, patch, head]) -> true
|
||||||
|
;
|
||||||
|
throw(error(domain_error(http_option, method(Method)), _))
|
||||||
|
).
|
||||||
|
|
||||||
|
parse_http_options_(data(Data), data(Data)) :-
|
||||||
|
( var(Data) ->
|
||||||
|
throw(error(instantiation_error, http_open/3))
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
parse_http_options_(request_headers(Headers), request_headers(Headers)) :-
|
||||||
|
( var(Headers) ->
|
||||||
|
throw(error(instantiation_error, http_open/3))
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
parse_http_options_(size(Size), size(Size)).
|
||||||
|
parse_http_options_(status_code(Code), status_code(Code)).
|
||||||
|
parse_http_options_(headers(Headers), headers(Headers)).
|
||||||
476
src/lib/http/http_server.pl
Normal file
476
src/lib/http/http_server.pl
Normal file
@@ -0,0 +1,476 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Written in December 2020 by Adrián Arroyo (adrian.arroyocalle@gmail.com)
|
||||||
|
Updated in March 2022 by Adrián Arroyo to use the Hyper backend
|
||||||
|
Part of Scryer Prolog.
|
||||||
|
I place this code in the public domain. Use it in any way you want.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** This library provides an starting point to build HTTP server based applications.
|
||||||
|
It is based on [Warp](https://github.com/seanmonstar/warp), which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However,
|
||||||
|
some advanced features that Warp provides are still not accesible.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The main predicate of the library is `http_listen/2`, which needs a port number
|
||||||
|
(usually 80) and a list of handlers. A handler is a compound term with the functor
|
||||||
|
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!")).
|
||||||
|
|
||||||
|
parameter_handler(User, Request, Response) :-
|
||||||
|
http_body(Response, text(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.
|
||||||
|
Although you can work directly with `http_request` and `http_response` terms, it is
|
||||||
|
recommeded to use the helper predicates, which are easier to understand and cleaner:
|
||||||
|
|
||||||
|
- `http_headers(Response/Request, Headers)`
|
||||||
|
- `http_status_code(Responde, StatusCode)`
|
||||||
|
- `http_body(Response/Request, text(Body))`
|
||||||
|
- `http_body(Response/Request, binary(Body))`
|
||||||
|
- `http_body(Request, form(Form))`
|
||||||
|
- `http_body(Response, file(Filename))`
|
||||||
|
- `http_redirect(Response, Url)`
|
||||||
|
- `http_query(Request, QueryName, QueryValue)`
|
||||||
|
|
||||||
|
Some things that are still missing:
|
||||||
|
|
||||||
|
- Read forms in multipart format
|
||||||
|
- Session handling via cookies
|
||||||
|
- HTML Templating (but you can use [Teruel](https://github.com/aarroyoc/teruel/), [Marquete](https://github.com/aarroyoc/marquete/) or [Djota](https://github.com/aarroyoc/djota) for that)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
:- module(http_server, [
|
||||||
|
http_listen/2,
|
||||||
|
http_listen/3,
|
||||||
|
http_headers/2,
|
||||||
|
http_status_code/2,
|
||||||
|
http_body/2,
|
||||||
|
http_redirect/2,
|
||||||
|
http_query/3,
|
||||||
|
http_basic_auth/4
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- meta_predicate(http_listen(?, :)).
|
||||||
|
:- meta_predicate(http_listen(?, :, ?)).
|
||||||
|
|
||||||
|
:- meta_predicate(http_basic_auth(:, :, ?, ?)).
|
||||||
|
|
||||||
|
:- use_module(library(charsio)).
|
||||||
|
:- use_module(library(crypto)).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(format)).
|
||||||
|
:- use_module(library(iso_ext)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(pio)).
|
||||||
|
:- use_module(library(time)).
|
||||||
|
|
||||||
|
%% http_listen(+Port, +Handlers).
|
||||||
|
%
|
||||||
|
% Equivalent to `http_listen(Port, Handlers, [])`.
|
||||||
|
http_listen(Port, Module:Handlers0) :-
|
||||||
|
must_be(integer, Port),
|
||||||
|
must_be(list, Handlers0),
|
||||||
|
maplist(module_qualification(Module), Handlers0, Handlers),
|
||||||
|
http_listen_(Port, Handlers, []).
|
||||||
|
|
||||||
|
%% http_listen(+Port, +Handlers, +Options).
|
||||||
|
%
|
||||||
|
% Listens for HTTP connections on port Port. Each handler on the list Handlers should be of the form: `HttpVerb(PathUnification, Predicate)`.
|
||||||
|
% For example: `get(user/User, get_info(User))` will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable)
|
||||||
|
% and it will call `get_info` with three arguments: an `http_request` term, an `http_response` term and User.
|
||||||
|
%
|
||||||
|
% The following options are supported:
|
||||||
|
%
|
||||||
|
% - `tls_key(+Key)` - a TLS key for HTTPS (string)
|
||||||
|
% - `tls_cert(+Cert)` - a TLS cert for HTTPS (string)
|
||||||
|
% - `content_length_limit(+Limit)` - maximum length (in bytes) for the incoming bodies. By default, 32KB.
|
||||||
|
%
|
||||||
|
% In order to have a HTTPS server (instead of plain HTTP), both `tls_key` and `tls_cert` options must be provided.
|
||||||
|
http_listen(Port, Module:Handlers0, Options) :-
|
||||||
|
must_be(integer, Port),
|
||||||
|
must_be(list, Handlers0),
|
||||||
|
must_be(list, Options),
|
||||||
|
maplist(module_qualification(Module), Handlers0, Handlers),
|
||||||
|
http_listen_(Port, Handlers, Options).
|
||||||
|
|
||||||
|
module_qualification(M, H0, H) :-
|
||||||
|
H0 =.. [Method, Path, Goal],
|
||||||
|
H =.. [Method, Path, M:Goal].
|
||||||
|
|
||||||
|
http_listen__(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit) :-
|
||||||
|
'$http_listen'(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit).
|
||||||
|
|
||||||
|
http_listen_stop_(HttpListener) :-
|
||||||
|
'$http_listen_stop'(HttpListener).
|
||||||
|
|
||||||
|
http_accept_(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle) :-
|
||||||
|
'$http_accept'(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle).
|
||||||
|
|
||||||
|
http_answer_(ResponseHandle, Code, Headers, ResponseStream) :-
|
||||||
|
'$http_answer'(ResponseHandle, Code, Headers, ResponseStream).
|
||||||
|
|
||||||
|
http_listen_(Port, Handlers, Options) :-
|
||||||
|
parse_options(Options, TLSKey, TLSCert, ContentLengthLimit),
|
||||||
|
phrase(format_("0.0.0.0:~d", [Port]), Addr),
|
||||||
|
setup_call_cleanup(
|
||||||
|
(
|
||||||
|
http_listen__(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit),
|
||||||
|
format("Listening at http://~s\n", [Addr])
|
||||||
|
),
|
||||||
|
http_loop(HttpListener, Handlers),
|
||||||
|
http_listen_stop_(HttpListener)
|
||||||
|
).
|
||||||
|
|
||||||
|
parse_options(Options, TLSKey, TLSCert, ContentLengthLimit) :-
|
||||||
|
member_option_default(tls_key, Options, "", TLSKey),
|
||||||
|
member_option_default(tls_cert, Options, "", TLSCert),
|
||||||
|
member_option_default(content_length_limit, Options, 32768, ContentLengthLimit),
|
||||||
|
must_be(integer, ContentLengthLimit).
|
||||||
|
|
||||||
|
member_option_default(Key, List, _Default, Value) :-
|
||||||
|
X =.. [Key, Value],
|
||||||
|
member(X, List).
|
||||||
|
member_option_default(Key, List, Default, Default) :-
|
||||||
|
X =.. [Key, _],
|
||||||
|
\+ member(X, List).
|
||||||
|
|
||||||
|
http_loop(HttpListener, Handlers) :-
|
||||||
|
time((
|
||||||
|
http_accept_(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle),
|
||||||
|
current_time(Time),
|
||||||
|
phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString),
|
||||||
|
format("~s ~w ~s", [TimeString, RequestMethod, RequestPath]),
|
||||||
|
maplist(map_header_kv, RequestHeaders, RequestHeadersKV),
|
||||||
|
phrase(parse_queries(RequestQueries), RequestQuery),
|
||||||
|
(
|
||||||
|
match_handler(Handlers, RequestMethod, RequestPath, Handler) ->
|
||||||
|
(
|
||||||
|
HttpRequest = http_request(RequestHeadersKV, stream(RequestStream), RequestQueries),
|
||||||
|
HttpResponse = http_response(_, _, _),
|
||||||
|
catch(
|
||||||
|
(call(Handler, HttpRequest, HttpResponse) ->
|
||||||
|
send_response(ResponseHandle, HttpResponse)
|
||||||
|
;
|
||||||
|
setup_call_cleanup(
|
||||||
|
http_answer_(ResponseHandle, 500, [], ResponseStream),
|
||||||
|
format(ResponseStream, "Internal Server Error", []),
|
||||||
|
close(ResponseStream)
|
||||||
|
),
|
||||||
|
throw(handler_not_available(Handler, RequestMethod, RequestPath, RequestQuery, RequestHeaders))
|
||||||
|
),
|
||||||
|
HandlerError,
|
||||||
|
(
|
||||||
|
setup_call_cleanup(
|
||||||
|
http_answer_(ResponseHandle, 500, [], ResponseStream),
|
||||||
|
format(ResponseStream, "Internal Server Error", []),
|
||||||
|
close(ResponseStream)
|
||||||
|
),
|
||||||
|
throw(HandlerError)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
setup_call_cleanup(
|
||||||
|
http_answer_(ResponseHandle, 404, [], ResponseStream),
|
||||||
|
format(ResponseStream, "Not Found", []),
|
||||||
|
close(ResponseStream)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
http_loop(HttpListener, Handlers).
|
||||||
|
|
||||||
|
send_response(ResponseHandle, http_response(StatusCode0, text(ResponseText), ResponseHeaders0)) :-
|
||||||
|
default(StatusCode0, 200, StatusCode),
|
||||||
|
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
|
||||||
|
http_answer_(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream0),
|
||||||
|
open(stream(ResponseStream0), write, ResponseStream, [type(text)]),
|
||||||
|
catch(
|
||||||
|
call_cleanup(format(ResponseStream, "~s", [ResponseText]),close(ResponseStream)),
|
||||||
|
error(existence_error(stream, _), _),
|
||||||
|
true
|
||||||
|
).
|
||||||
|
|
||||||
|
send_response(ResponseHandle, http_response(StatusCode0, bytes(ResponseBytes), ResponseHeaders0)) :-
|
||||||
|
default(StatusCode0, 200, StatusCode),
|
||||||
|
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
|
||||||
|
http_answer_(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
|
||||||
|
catch(
|
||||||
|
call_cleanup(format(ResponseStream, "~s", [ResponseBytes]),close(ResponseStream)),
|
||||||
|
error(existence_error(stream, _), _),
|
||||||
|
true
|
||||||
|
).
|
||||||
|
|
||||||
|
send_response(ResponseHandle, http_response(StatusCode0, file(Filename), ResponseHeaders0)) :-
|
||||||
|
default(StatusCode0, 200, StatusCode),
|
||||||
|
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
|
||||||
|
http_answer_(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
|
||||||
|
catch(
|
||||||
|
call_cleanup(
|
||||||
|
setup_call_cleanup(
|
||||||
|
open(Filename, read, FileStream, [type(binary)]),
|
||||||
|
(
|
||||||
|
get_n_chars(FileStream, _, FileCs),
|
||||||
|
format(ResponseStream, "~s", [FileCs])
|
||||||
|
),
|
||||||
|
close(FileStream)
|
||||||
|
),
|
||||||
|
close(ResponseStream)
|
||||||
|
),
|
||||||
|
error(existence_error(stream, _), _),
|
||||||
|
true
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
default(Var, Default, Out) :-
|
||||||
|
(var(Var) -> Out = Default
|
||||||
|
; Var = Out
|
||||||
|
).
|
||||||
|
|
||||||
|
map_header_kv(T, K-V) :-
|
||||||
|
T =.. [K0, V],
|
||||||
|
atom_chars(K0, K).
|
||||||
|
|
||||||
|
map_header_kv_2(T, K-V) :-
|
||||||
|
atom_chars(K0, K),
|
||||||
|
T =.. [K0, V].
|
||||||
|
|
||||||
|
match_handler(Handlers, Method, "/", Handler) :-
|
||||||
|
member(H, Handlers),
|
||||||
|
H =.. [Method, /, Handler].
|
||||||
|
match_handler(Handlers, Method, Path, Handler) :-
|
||||||
|
member(H, Handlers),
|
||||||
|
copy_term(H, H1),
|
||||||
|
H1 =.. [Method, Pattern, Handler],
|
||||||
|
\+ var(Pattern),
|
||||||
|
phrase(path(Pattern), Path).
|
||||||
|
match_handler(Handlers, Method, Path, Handler) :-
|
||||||
|
member(H, Handlers),
|
||||||
|
copy_term(H, H1),
|
||||||
|
H1 =.. [Method, Var, Handler],
|
||||||
|
var(Var),
|
||||||
|
Var = Path.
|
||||||
|
|
||||||
|
path(Pattern) -->
|
||||||
|
{
|
||||||
|
Pattern =.. Parts,
|
||||||
|
length(Parts, 3),
|
||||||
|
nth0(1, Parts, Pattern0),
|
||||||
|
nth0(2, Parts, PartAtom),
|
||||||
|
(var(PartAtom) -> Part = PartAtom; atom_chars(PartAtom, Part))
|
||||||
|
},
|
||||||
|
path(Pattern0),
|
||||||
|
"/",
|
||||||
|
string_without("/", Part).
|
||||||
|
|
||||||
|
path(Pattern) -->
|
||||||
|
{
|
||||||
|
Pattern =.. Parts,
|
||||||
|
Parts = [PartAtom],
|
||||||
|
(var(PartAtom) -> Part = PartAtom; atom_chars(PartAtom, Part))
|
||||||
|
},
|
||||||
|
"/",
|
||||||
|
string_without("/", Part).
|
||||||
|
|
||||||
|
path([]) --> [].
|
||||||
|
|
||||||
|
string_without(Not, [Char|String]) -->
|
||||||
|
[Char],
|
||||||
|
{
|
||||||
|
\+ member(Char, Not)
|
||||||
|
},
|
||||||
|
string_without(Not, String).
|
||||||
|
|
||||||
|
string_without(_, []) -->
|
||||||
|
[].
|
||||||
|
|
||||||
|
%% http_headers(?Request_Response, ?Headers).
|
||||||
|
%
|
||||||
|
% True iff `Request_Response` is a request or response with headers Headers. Can be used both to get headers (usually in from a request)
|
||||||
|
% and to add headers (usually in a response).
|
||||||
|
http_headers(http_request(Headers, _, _), Headers).
|
||||||
|
http_headers(http_response(_, _, Headers), Headers).
|
||||||
|
|
||||||
|
%% http_body(?Request_Response, ?Body).
|
||||||
|
%
|
||||||
|
% True iff Body is the body of the request or response. A body can be of the following types:
|
||||||
|
%
|
||||||
|
% * `bytes(Bytes)` for both requests and responses, interprets the body as bytes
|
||||||
|
% * `text(Bytes)` for both requests and responses, interprets the body as text
|
||||||
|
% * `form(Form)` only for requests, interprets the body as an `application/x-www-form-urlencoded` form.
|
||||||
|
% * `file(File)` only for responses, interprets the body as the content of a file (useful to send static files).
|
||||||
|
http_body(http_request(_, stream(StreamBody), _), bytes(BytesBody)) :- get_n_chars(StreamBody, _, BytesBody).
|
||||||
|
http_body(http_request(_, stream(StreamBody), _), text(TextBody)) :- get_n_chars(StreamBody, _, TextBody).
|
||||||
|
http_body(http_request(Headers, stream(StreamBody), _), form(FormBody)) :-
|
||||||
|
member("content-type"-"application/x-www-form-urlencoded", Headers),
|
||||||
|
get_n_chars(StreamBody, _, TextBody),
|
||||||
|
phrase(parse_queries(FormBody), TextBody).
|
||||||
|
http_body(http_request(_, Body, _), Body).
|
||||||
|
http_body(http_response(_, Body, _), Body).
|
||||||
|
|
||||||
|
%% http_status_code(?Response, ?StatusCode).
|
||||||
|
%
|
||||||
|
% True iff the status code of the response Response unifies with StatusCode.
|
||||||
|
http_status_code(http_response(StatusCode, _, _), StatusCode).
|
||||||
|
|
||||||
|
%% http_redirect(-Response, +Uri).
|
||||||
|
%
|
||||||
|
% True iff Response is a response that redirects the user to the uri Uri.
|
||||||
|
http_redirect(http_response(307, text("Moved Temporarily"), ["Location"-Uri]), Uri).
|
||||||
|
|
||||||
|
%% http_query(+Request, ?Key, ?Value).
|
||||||
|
%
|
||||||
|
% True iff there's a query in request Request with key Key and value Value.
|
||||||
|
http_query(http_request(_, _, Queries), Key, Value) :- member(Key-Value, Queries).
|
||||||
|
|
||||||
|
parse_queries([Key-Value|Queries]) -->
|
||||||
|
string_without("=", Key0),
|
||||||
|
"=",
|
||||||
|
string_without("&", Value0),
|
||||||
|
"&",
|
||||||
|
parse_queries(Queries),
|
||||||
|
{
|
||||||
|
phrase(url_decode(Key), Key0),
|
||||||
|
phrase(url_decode(Value), Value0)
|
||||||
|
}.
|
||||||
|
|
||||||
|
parse_queries([Key-Value]) -->
|
||||||
|
string_without("=", Key0),
|
||||||
|
"=",
|
||||||
|
string_without(" ", Value0),
|
||||||
|
{
|
||||||
|
phrase(url_decode(Key), Key0),
|
||||||
|
phrase(url_decode(Value), Value0)
|
||||||
|
}.
|
||||||
|
|
||||||
|
parse_queries([]) -->
|
||||||
|
[].
|
||||||
|
|
||||||
|
% Decodes a UTF-8 URL Encoded string: RFC-1738
|
||||||
|
url_decode([Char|Chars]) -->
|
||||||
|
[Char],
|
||||||
|
{
|
||||||
|
Char \= '%',
|
||||||
|
Char \= (+)
|
||||||
|
},
|
||||||
|
url_decode(Chars).
|
||||||
|
url_decode([' '|Chars]) -->
|
||||||
|
"+",
|
||||||
|
url_decode(Chars).
|
||||||
|
url_decode([Char|Chars]) -->
|
||||||
|
"%",
|
||||||
|
[A],
|
||||||
|
[B],
|
||||||
|
{
|
||||||
|
hex_bytes([A,B], Bytes),
|
||||||
|
Bytes = [FirstByte|_],
|
||||||
|
FirstByte < 128,
|
||||||
|
chars_utf8bytes(Chars0, Bytes),
|
||||||
|
Chars0 = [Char]
|
||||||
|
},
|
||||||
|
url_decode(Chars).
|
||||||
|
url_decode([Char|Chars]) -->
|
||||||
|
"%",
|
||||||
|
[A, B],
|
||||||
|
"%",
|
||||||
|
[C, D],
|
||||||
|
{
|
||||||
|
hex_bytes([A,B,C,D], Bytes),
|
||||||
|
Bytes = [FirstByte|_],
|
||||||
|
FirstByte < 224,
|
||||||
|
chars_utf8bytes(Chars0, Bytes),
|
||||||
|
Chars0 = [Char]
|
||||||
|
},
|
||||||
|
url_decode(Chars).
|
||||||
|
url_decode([Char|Chars]) -->
|
||||||
|
"%",
|
||||||
|
[A, B],
|
||||||
|
"%",
|
||||||
|
[C, D],
|
||||||
|
"%",
|
||||||
|
[E, F],
|
||||||
|
{
|
||||||
|
hex_bytes([A,B,C,D,E,F], Bytes),
|
||||||
|
Bytes = [FirstByte|_],
|
||||||
|
FirstByte < 240,
|
||||||
|
chars_utf8bytes(Chars0, Bytes),
|
||||||
|
Chars0 = [Char]
|
||||||
|
},
|
||||||
|
url_decode(Chars).
|
||||||
|
url_decode([Char|Chars]) -->
|
||||||
|
"%",
|
||||||
|
[A, B],
|
||||||
|
"%",
|
||||||
|
[C, D],
|
||||||
|
"%",
|
||||||
|
[E, F],
|
||||||
|
"%",
|
||||||
|
[H, I],
|
||||||
|
{
|
||||||
|
hex_bytes([A,B,C,D,E,F,H,I], Bytes),
|
||||||
|
chars_utf8bytes(Chars0, Bytes),
|
||||||
|
Chars0 = [Char]
|
||||||
|
},
|
||||||
|
url_decode(Chars).
|
||||||
|
|
||||||
|
url_decode([]) --> [].
|
||||||
|
|
||||||
|
%% http_basic_auth(+LoginPredicate, +Handler, +Request, -Response)
|
||||||
|
%
|
||||||
|
% Metapredicate that wraps an existing Handler with an HTTP Basic Auth flow.
|
||||||
|
% Checks if a given user + password is authorized to execute that handler, returning 401
|
||||||
|
% if it's not satisfied.
|
||||||
|
%
|
||||||
|
% `LoginPredicate` must be a predicate of arity 2 that takes a User and a Password.
|
||||||
|
% `Handler` will have, in addition to the Request and Response arguments, a User argument
|
||||||
|
% containing the User given in the authentication.
|
||||||
|
%
|
||||||
|
% Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% main :-
|
||||||
|
% http_listen(8800,[get('/', http_basic_auth(login, inside_handler("data")))]).
|
||||||
|
%
|
||||||
|
% login(User, Pass) :-
|
||||||
|
% User = "aarroyoc",
|
||||||
|
% Pass = "123456".
|
||||||
|
%
|
||||||
|
% inside_handler(Data, User, Request, Response) :-
|
||||||
|
% http_body(Response, text(User)).
|
||||||
|
% ```
|
||||||
|
http_basic_auth(LoginPredicate, Handler, Request, Response) :-
|
||||||
|
http_headers(Request, Headers),
|
||||||
|
member("authorization"-AuthorizationStr, Headers),
|
||||||
|
append("Basic ", Coded, AuthorizationStr),
|
||||||
|
chars_base64(UserPass, Coded, []),
|
||||||
|
append(User, [':'|Password], UserPass),
|
||||||
|
(
|
||||||
|
call(LoginPredicate, User, Password) ->
|
||||||
|
call(Handler, User, Request, Response)
|
||||||
|
; http_basic_auth_unauthorized_response(Response)
|
||||||
|
).
|
||||||
|
|
||||||
|
http_basic_auth(_LoginPredicate, _Handler, Request, Response) :-
|
||||||
|
http_headers(Request, Headers),
|
||||||
|
\+ member("authorization"-_, Headers),
|
||||||
|
http_basic_auth_unauthorized_response(Response).
|
||||||
|
|
||||||
|
http_basic_auth_unauthorized_response(Response) :-
|
||||||
|
http_status_code(Response, 401),
|
||||||
|
http_headers(Response, ["www-authenticate"-"Basic realm=\"Scryer Prolog\", charset=\"UTF-8\""]),
|
||||||
|
http_body(Response, text("Unauthorized")).
|
||||||
|
|
||||||
408
src/lib/iso_ext.pl
Normal file
408
src/lib/iso_ext.pl
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
/** Useful general predicates that are not ISO standard yet
|
||||||
|
|
||||||
|
Predicates available here are similar to the ones defined in builtin.pl,
|
||||||
|
but they're not part of the ISO Prolog standard at the moment.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(iso_ext, [bb_b_put/2,
|
||||||
|
bb_get/2,
|
||||||
|
bb_put/2,
|
||||||
|
call_cleanup/2,
|
||||||
|
call_with_inference_limit/3,
|
||||||
|
call_residue_vars/2,
|
||||||
|
forall/2,
|
||||||
|
partial_string/1,
|
||||||
|
partial_string/3,
|
||||||
|
partial_string_tail/2,
|
||||||
|
setup_call_cleanup/3,
|
||||||
|
succ/2,
|
||||||
|
call_nth/2,
|
||||||
|
countall/2,
|
||||||
|
copy_term_nat/2,
|
||||||
|
copy_term/3]).
|
||||||
|
|
||||||
|
:- use_module(library(error), [can_be/2,
|
||||||
|
must_be/2,
|
||||||
|
domain_error/3,
|
||||||
|
instantiation_error/1,
|
||||||
|
type_error/3]).
|
||||||
|
|
||||||
|
:- use_module(library(lists), [maplist/3]).
|
||||||
|
|
||||||
|
:- use_module(library('$project_atts')).
|
||||||
|
|
||||||
|
:- meta_predicate(forall(0, 0)).
|
||||||
|
|
||||||
|
%% forall(Generate, Test).
|
||||||
|
%
|
||||||
|
% For all bindings possible by Generate, Test must be true.
|
||||||
|
%
|
||||||
|
% In this example, it checks that all numbers are even:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- Ns = [2,4,6], forall(member(N, Ns), 0 is N mod 2).
|
||||||
|
% Ns = [2,4,6].
|
||||||
|
% ```
|
||||||
|
forall(Generate, Test) :-
|
||||||
|
\+ (Generate, \+ Test).
|
||||||
|
|
||||||
|
% (non-)backtrackable global variables.
|
||||||
|
|
||||||
|
%% bb_put(+Key, +Value).
|
||||||
|
%
|
||||||
|
% Sets a global variable named Key (must be an atom) with value Value.
|
||||||
|
% The global variable isn't backtrackable. Check `bb_b_put/2` for the
|
||||||
|
% backtrackable version.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- bb_put(city, "Valladolid").
|
||||||
|
% true.
|
||||||
|
% ?- bb_get(city, X).
|
||||||
|
% X = "Valladolid".
|
||||||
|
% ```
|
||||||
|
%
|
||||||
|
% In this example one can understand the difference between `bb_put/2` and
|
||||||
|
% `bb_b_put/2`:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)).
|
||||||
|
% X = "Salamanca".
|
||||||
|
% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)).
|
||||||
|
% X = "Valladolid".
|
||||||
|
% ```
|
||||||
|
bb_put(Key, Value) :-
|
||||||
|
( atom(Key) ->
|
||||||
|
'$store_global_var'(Key, Value)
|
||||||
|
; type_error(atom, Key, bb_put/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
% backtrackable global variables.
|
||||||
|
|
||||||
|
%% bb_b_put(+Key, +Value).
|
||||||
|
%
|
||||||
|
% Sets a global variable named Key (must be an atom) with value Value.
|
||||||
|
% The global variable is backtrackable. Check `bb_put/2` for the
|
||||||
|
% non-backtrackable version.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- bb_b_put(city, "Valladolid").
|
||||||
|
% true.
|
||||||
|
% ?- bb_get(city, X).
|
||||||
|
% X = "Valladolid".
|
||||||
|
% ```
|
||||||
|
%
|
||||||
|
% In this example one can understand the difference between `bb_put/2` and
|
||||||
|
% `bb_b_put/2`:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)).
|
||||||
|
% X = "Salamanca".
|
||||||
|
% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)).
|
||||||
|
% X = "Valladolid".
|
||||||
|
% ```
|
||||||
|
bb_b_put(Key, Value) :-
|
||||||
|
( atom(Key) ->
|
||||||
|
'$store_backtrackable_global_var'(Key, Value)
|
||||||
|
; type_error(atom, Key, bb_b_put/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% bb_get(+Key, -Value).
|
||||||
|
%
|
||||||
|
% Gets the value Value of a global variable named Key (must be an atom)
|
||||||
|
bb_get(Key, Value) :-
|
||||||
|
( atom(Key) ->
|
||||||
|
'$fetch_global_var'(Key, Value)
|
||||||
|
; type_error(atom, Key, bb_get/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
%% succ(?I, ?S).
|
||||||
|
%
|
||||||
|
% True iff S is the successor of the non-negative integer I.
|
||||||
|
% At least one of the arguments must be instantiated.
|
||||||
|
|
||||||
|
succ(I, S) :-
|
||||||
|
can_be(not_less_than_zero, I),
|
||||||
|
can_be(not_less_than_zero, S),
|
||||||
|
( integer(S) ->
|
||||||
|
S > 0,
|
||||||
|
I is S-1
|
||||||
|
; integer(I) ->
|
||||||
|
S is I+1
|
||||||
|
; instantiation_error(succ/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
% setup_call_cleanup.
|
||||||
|
|
||||||
|
:- meta_predicate(call_cleanup(0, 0)).
|
||||||
|
|
||||||
|
%% call_cleanup(Goal, Cleanup).
|
||||||
|
%
|
||||||
|
% Executes Goal and then, either on success or failure, executes Cleanup.
|
||||||
|
% The success or failure of Cleanup is ignored and choice points created inside are destroyed.
|
||||||
|
call_cleanup(G, C) :- setup_call_cleanup(true, G, C).
|
||||||
|
|
||||||
|
:- meta_predicate(setup_call_cleanup(0, 0, 0)).
|
||||||
|
|
||||||
|
:- non_counted_backtracking setup_call_cleanup/3.
|
||||||
|
|
||||||
|
%% setup_call_cleanup(Setup, Goal, Cleanup).
|
||||||
|
%
|
||||||
|
% If Setup succeeds, Cleanup will be called after the execution of Goal. Goal itself can succeed or not.
|
||||||
|
%
|
||||||
|
% In this example, we use the predicate to always close an open file:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- setup_call_cleanup(open(File, read, Stream), do_something_with_stream(Stream), close(Stream)).
|
||||||
|
% ```
|
||||||
|
setup_call_cleanup(S, G, C) :-
|
||||||
|
'$get_b_value'(B),
|
||||||
|
'$call_with_inference_counting'(call(S)),
|
||||||
|
'$set_cp_by_default'(B),
|
||||||
|
'$get_current_scc_block'(Bb),
|
||||||
|
( C = _:CC,
|
||||||
|
var(CC) ->
|
||||||
|
instantiation_error(setup_call_cleanup/3)
|
||||||
|
; scc_helper(C, G, Bb)
|
||||||
|
).
|
||||||
|
|
||||||
|
:- meta_predicate(scc_helper(?,0,?)).
|
||||||
|
|
||||||
|
:- non_counted_backtracking scc_helper/3.
|
||||||
|
|
||||||
|
scc_helper(C, G, Bb) :-
|
||||||
|
'$get_cp'(Cp),
|
||||||
|
'$install_scc_cleaner'(C),
|
||||||
|
'$call_with_inference_counting'(call(G)),
|
||||||
|
( '$check_cp'(Cp) ->
|
||||||
|
'$reset_scc_block'(Bb),
|
||||||
|
run_cleaners_without_handling(Cp)
|
||||||
|
; true
|
||||||
|
; '$fail'
|
||||||
|
).
|
||||||
|
scc_helper(_, _, Bb) :-
|
||||||
|
'$reset_scc_block'(Bb),
|
||||||
|
'$push_ball_stack',
|
||||||
|
run_cleaners_with_handling,
|
||||||
|
'$pop_from_ball_stack',
|
||||||
|
'$unwind_stack'.
|
||||||
|
scc_helper(_, _, _) :-
|
||||||
|
'$get_cp'(Cp),
|
||||||
|
run_cleaners_without_handling(Cp),
|
||||||
|
'$fail'.
|
||||||
|
|
||||||
|
:- non_counted_backtracking run_cleaners_with_handling/0.
|
||||||
|
|
||||||
|
run_cleaners_with_handling :-
|
||||||
|
'$get_scc_cleaner'(C),
|
||||||
|
'$get_cp'(B),
|
||||||
|
catch(C, _, true),
|
||||||
|
'$set_cp_by_default'(B),
|
||||||
|
run_cleaners_with_handling.
|
||||||
|
run_cleaners_with_handling :-
|
||||||
|
'$restore_cut_policy'.
|
||||||
|
|
||||||
|
:- non_counted_backtracking run_cleaners_without_handling/1.
|
||||||
|
|
||||||
|
run_cleaners_without_handling(Cp) :-
|
||||||
|
'$get_scc_cleaner'(C),
|
||||||
|
'$get_cp'(B),
|
||||||
|
call(C),
|
||||||
|
'$set_cp_by_default'(B),
|
||||||
|
run_cleaners_without_handling(Cp).
|
||||||
|
run_cleaners_without_handling(Cp) :-
|
||||||
|
'$set_cp_by_default'(Cp),
|
||||||
|
'$restore_cut_policy'.
|
||||||
|
|
||||||
|
% call_with_inference_limit
|
||||||
|
|
||||||
|
:- meta_predicate(call_with_inference_limit(0, ?, ?)).
|
||||||
|
|
||||||
|
:- non_counted_backtracking call_with_inference_limit/3.
|
||||||
|
|
||||||
|
%% 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 ->
|
||||||
|
domain_error(not_less_than_zero, L, call_with_inference_limit/3)
|
||||||
|
; true
|
||||||
|
)
|
||||||
|
; var(L) ->
|
||||||
|
instantiation_error(call_with_inference_limit/3)
|
||||||
|
; type_error(integer, L, call_with_inference_limit/3)
|
||||||
|
),
|
||||||
|
'$get_current_block'(Bb),
|
||||||
|
'$get_b_value'(B),
|
||||||
|
call_with_inference_limit(G, L, R, Bb, B),
|
||||||
|
'$remove_call_policy_check'(B).
|
||||||
|
|
||||||
|
|
||||||
|
:- meta_predicate(call_with_inference_limit(0,?,?,?,?)).
|
||||||
|
|
||||||
|
:- non_counted_backtracking call_with_inference_limit/5.
|
||||||
|
|
||||||
|
call_with_inference_limit(G, L, R, Bb, B) :-
|
||||||
|
'$install_new_block'(NBb),
|
||||||
|
'$install_inference_counter'(NBb, L, Count0),
|
||||||
|
'$call_with_inference_counting'(call(G)),
|
||||||
|
'$inference_level'(R, B),
|
||||||
|
'$remove_inference_counter'(NBb, Count1),
|
||||||
|
Diff is L - (Count1 - Count0),
|
||||||
|
( '$clean_up_block'(NBb),
|
||||||
|
'$reset_block'(Bb)
|
||||||
|
; '$install_inference_counter'(NBb, Diff, _),
|
||||||
|
'$reset_block'(NBb),
|
||||||
|
'$fail'
|
||||||
|
).
|
||||||
|
call_with_inference_limit(_, _, R, Bb, B) :-
|
||||||
|
( '$inference_limit_exceeded' ->
|
||||||
|
R = inference_limit_exceeded
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
'$get_current_block'(NBb),
|
||||||
|
'$remove_inference_counter'(NBb, _),
|
||||||
|
'$reset_block'(Bb),
|
||||||
|
'$remove_call_policy_check'(B),
|
||||||
|
( '$get_ball'(_),
|
||||||
|
'$push_ball_stack',
|
||||||
|
'$get_cp'(Cp),
|
||||||
|
'$set_cp_by_default'(Cp),
|
||||||
|
'$pop_from_ball_stack',
|
||||||
|
'$unwind_stack'
|
||||||
|
; nonvar(R)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% 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, Ls0, Ls) :-
|
||||||
|
must_be(chars, String),
|
||||||
|
( String == [] ->
|
||||||
|
Ls0 = Ls
|
||||||
|
; '$create_partial_string'(String, Ls0, Ls)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% partial_string(+String)
|
||||||
|
%
|
||||||
|
% Succeeds if String is a _partial string_. A partial string is a string composed of several smaller
|
||||||
|
% strings, even just one. That means all strings in Scryer are partial strings.
|
||||||
|
partial_string(String) :-
|
||||||
|
'$is_partial_string'(String).
|
||||||
|
|
||||||
|
%% partial_string_tail(+String, -Tail).
|
||||||
|
%
|
||||||
|
% Unifies Tail with the last section of the partial string.
|
||||||
|
% It's not recommended to use this predicate in application code.
|
||||||
|
partial_string_tail(String, Tail) :-
|
||||||
|
( partial_string(String) ->
|
||||||
|
'$partial_string_tail'(String, Tail)
|
||||||
|
; throw(error(type_error(partial_string, String), partial_string_tail/2))
|
||||||
|
).
|
||||||
|
|
||||||
|
:- dynamic(i_call_nth_nesting/2).
|
||||||
|
:- dynamic(i_call_nth_counter/1).
|
||||||
|
|
||||||
|
:- meta_predicate(call_nth(0, ?)).
|
||||||
|
|
||||||
|
%% call_nth(Goal, N).
|
||||||
|
%
|
||||||
|
% Succeeds when Goal succeeded for the Nth time (there are at least N solutions)
|
||||||
|
call_nth(Goal, N) :-
|
||||||
|
can_be(integer, N),
|
||||||
|
( integer(N) ->
|
||||||
|
( N < 0 ->
|
||||||
|
domain_error(not_less_than_zero, N, call_nth/2)
|
||||||
|
; N > 0
|
||||||
|
)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
setup_call_cleanup(call_nth_nesting(C, ID),
|
||||||
|
( Goal,
|
||||||
|
bb_get(ID, N0),
|
||||||
|
N1 is N0 + 1,
|
||||||
|
bb_put(ID, N1),
|
||||||
|
( integer(N) ->
|
||||||
|
N = N1,
|
||||||
|
!
|
||||||
|
; N = N1
|
||||||
|
)
|
||||||
|
),
|
||||||
|
( bb_get(i_call_nth_counter, C) ->
|
||||||
|
C1 is C - 1,
|
||||||
|
bb_put(i_call_nth_counter, C1)
|
||||||
|
; true
|
||||||
|
)).
|
||||||
|
|
||||||
|
call_nth_nesting(C, ID) :-
|
||||||
|
( bb_get(i_call_nth_counter, C0) ->
|
||||||
|
C is C0 + 1
|
||||||
|
; C = 0
|
||||||
|
),
|
||||||
|
number_chars(C, Cs),
|
||||||
|
atom_chars(Atom, Cs),
|
||||||
|
atom_concat(i_call_nth_nesting_, Atom, ID),
|
||||||
|
bb_put(ID, 0),
|
||||||
|
bb_put(i_call_nth_counter, C).
|
||||||
|
|
||||||
|
%% countall(G_0, N).
|
||||||
|
%
|
||||||
|
% countall(G_0, N) is true iff N unifies with the total number of
|
||||||
|
% answers of call(G_0).
|
||||||
|
|
||||||
|
:- meta_predicate(countall(0, ?)).
|
||||||
|
|
||||||
|
countall(Goal, N) :-
|
||||||
|
can_be(integer, N),
|
||||||
|
( integer(N) ->
|
||||||
|
( N < 0 ->
|
||||||
|
domain_error(not_less_than_zero, N, countall/2)
|
||||||
|
; true
|
||||||
|
)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
setup_call_cleanup(call_nth_nesting(C, ID),
|
||||||
|
( ( Goal,
|
||||||
|
bb_get(ID, N0),
|
||||||
|
N1 is N0 + 1,
|
||||||
|
bb_put(ID, N1),
|
||||||
|
false
|
||||||
|
; bb_get(ID, N)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
( bb_get(i_call_nth_counter, C) ->
|
||||||
|
C1 is C - 1,
|
||||||
|
bb_put(i_call_nth_counter, C1)
|
||||||
|
; true
|
||||||
|
)).
|
||||||
|
|
||||||
|
%% copy_term_nat(Source, Dest)
|
||||||
|
%
|
||||||
|
% Similar to `copy_term/2` but without attribute variables
|
||||||
|
copy_term_nat(Source, Dest) :-
|
||||||
|
'$copy_term_without_attr_vars'(Source, Dest).
|
||||||
|
|
||||||
|
%% copy_term(+Term, -Copy, -Gs).
|
||||||
|
%
|
||||||
|
% Produce a deep copy of Term and unify it to Copy, without attributes.
|
||||||
|
% Unify Gs with a list of goals that represent the attributes of Term.
|
||||||
|
% Similar to `copy_term/2` but splitting the attributes.
|
||||||
|
copy_term(Term, Copy, Gs) :-
|
||||||
|
can_be(list, Gs),
|
||||||
|
findall(Term-Rs, '$project_atts':term_residual_goals(Term,Rs), [Copy-Gs]),
|
||||||
|
( var(Gs) ->
|
||||||
|
Gs = []
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
:- meta_predicate(call_residue_vars(0, ?)).
|
||||||
|
|
||||||
|
call_residue_vars(Goal, Vars) :-
|
||||||
|
can_be(list, Vars),
|
||||||
|
'$get_attr_var_queue_delim'(B),
|
||||||
|
call(Goal),
|
||||||
|
'$get_attr_var_queue_beyond'(B, Vars).
|
||||||
233
src/lib/lambda.pl
Normal file
233
src/lib/lambda.pl
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
Author: Ulrich Neumerkel
|
||||||
|
E-mail: ulrich@complang.tuwien.ac.at
|
||||||
|
Copyright (C): 2009 Ulrich Neumerkel. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY Ulrich Neumerkel ``AS IS'' AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Ulrich Neumerkel OR
|
||||||
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
The views and conclusions contained in the software and documentation
|
||||||
|
are those of the authors and should not be interpreted as representing
|
||||||
|
official policies, either expressed or implied, of Ulrich Neumerkel.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(lambda, [
|
||||||
|
(^)/3, (^)/4, (^)/5, (^)/6, (^)/7, (^)/8, (^)/9, (^)/10,
|
||||||
|
(\)/1, (\)/2, (\)/3, (\)/4, (\)/5, (\)/6, (\)/7, (\)/8,
|
||||||
|
(+\)/2, (+\)/3, (+\)/4, (+\)/5, (+\)/6, (+\)/7, (+\)/8,
|
||||||
|
(+\)/9, op(201,xfx,+\)]).
|
||||||
|
|
||||||
|
:- use_module(library(iso_ext)).
|
||||||
|
|
||||||
|
/** <module> Lambda expressions
|
||||||
|
|
||||||
|
This library provides lambda expressions to simplify higher order
|
||||||
|
programming based on call/N.
|
||||||
|
|
||||||
|
Lambda expressions are represented by ordinary Prolog terms.
|
||||||
|
There are two kinds of lambda expressions:
|
||||||
|
|
||||||
|
```
|
||||||
|
Free+\X1^X2^ ..^XN^Goal
|
||||||
|
|
||||||
|
\X1^X2^ ..^XN^Goal
|
||||||
|
```
|
||||||
|
|
||||||
|
The second is a shorthand for `t+\X1^X2^..^XN^Goal`.
|
||||||
|
|
||||||
|
Xi are the parameters.
|
||||||
|
|
||||||
|
Goal is a goal or continuation. Syntax note: Operators within Goal
|
||||||
|
require parentheses due to the low precedence of the ^ operator.
|
||||||
|
|
||||||
|
Free contains variables that are valid outside the scope of the lambda
|
||||||
|
expression. They are thus free variables within.
|
||||||
|
|
||||||
|
All other variables of Goal are considered local variables. They must
|
||||||
|
not appear outside the lambda expression. This restriction is
|
||||||
|
currently not checked. Violations may lead to unexpected bindings.
|
||||||
|
|
||||||
|
In the following example the parentheses around X>3 are necessary.
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module(library(lambda)).
|
||||||
|
?- use_module(library(lists)).
|
||||||
|
|
||||||
|
?- maplist(\X^(X>3),[4,5,9]).
|
||||||
|
true.
|
||||||
|
```
|
||||||
|
|
||||||
|
In the following X is a variable that is shared by both instances of
|
||||||
|
the lambda expression. The second query illustrates the cooperation of
|
||||||
|
continuations and lambdas. The lambda expression is in this case a
|
||||||
|
continuation expecting a further argument.
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module(library(dif)).
|
||||||
|
true.
|
||||||
|
|
||||||
|
?- Xs = [A,B], maplist(X+\Y^dif(X,Y), Xs).
|
||||||
|
Xs = [A,B], dif:dif(X,A), dif:dif(X,B).
|
||||||
|
|
||||||
|
?- Xs = [A,B], maplist(X+\dif(X), Xs).
|
||||||
|
Xs = [A,B], dif:dif(X,A), dif:dif(X,B).
|
||||||
|
```
|
||||||
|
|
||||||
|
The following queries are all equivalent. To see this, use
|
||||||
|
the fact `f(x,y)`.
|
||||||
|
|
||||||
|
```
|
||||||
|
?- call(f,A1,A2).
|
||||||
|
?- call(\X^f(X),A1,A2).
|
||||||
|
?- call(\X^Y^f(X,Y), A1,A2).
|
||||||
|
?- call(\X^(X+\Y^f(X,Y)), A1,A2).
|
||||||
|
?- call(call(f, A1),A2).
|
||||||
|
?- call(f(A1),A2).
|
||||||
|
?- f(A1,A2).
|
||||||
|
A1 = x, A2 = y.
|
||||||
|
```
|
||||||
|
|
||||||
|
Further discussions
|
||||||
|
[http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord](http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord)
|
||||||
|
|
||||||
|
@tbd Static expansion similar to apply_macros.
|
||||||
|
@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(no_hat_call(0)).
|
||||||
|
|
||||||
|
^(V1,C_0,V1) :-
|
||||||
|
no_hat_call(C_0).
|
||||||
|
^(V1,C_1,V1,V2) :-
|
||||||
|
call(C_1,V2).
|
||||||
|
^(V1,C_2,V1,V2,V3) :-
|
||||||
|
call(C_2,V2,V3).
|
||||||
|
^(V1,C_3,V1,V2,V3,V4) :-
|
||||||
|
call(C_3,V2,V3,V4).
|
||||||
|
^(V1,C_4,V1,V2,V3,V4,V5) :-
|
||||||
|
call(C_4,V2,V3,V4,V5).
|
||||||
|
^(V1,C_5,V1,V2,V3,V4,V5,V6) :-
|
||||||
|
call(C_5,V2,V3,V4,V5,V6).
|
||||||
|
^(V1,C_6,V1,V2,V3,V4,V5,V6,V7) :-
|
||||||
|
call(C_6,V2,V3,V4,V5,V6,V7).
|
||||||
|
^(V1,C_7,V1,V2,V3,V4,V5,V6,V7,V8) :-
|
||||||
|
call(C_7,V2,V3,V4,V5,V6,V7,V8).
|
||||||
|
|
||||||
|
\(FC_0) :-
|
||||||
|
copy_term_nat(FC_0,C_0),
|
||||||
|
no_hat_call(C_0).
|
||||||
|
\(FC_1,V1) :-
|
||||||
|
copy_term_nat(FC_1,C_1),
|
||||||
|
call(C_1,V1).
|
||||||
|
\(FC_2,V1,V2) :-
|
||||||
|
copy_term_nat(FC_2,C_2),
|
||||||
|
call(C_2,V1,V2).
|
||||||
|
\(FC_3,V1,V2,V3) :-
|
||||||
|
copy_term_nat(FC_3,C_3),
|
||||||
|
call(C_3,V1,V2,V3).
|
||||||
|
\(FC_4,V1,V2,V3,V4) :-
|
||||||
|
copy_term_nat(FC_4,C_4),
|
||||||
|
call(C_4,V1,V2,V3,V4).
|
||||||
|
\(FC_5,V1,V2,V3,V4,V5) :-
|
||||||
|
copy_term_nat(FC_5,C_5),
|
||||||
|
call(C_5,V1,V2,V3,V4,V5).
|
||||||
|
\(FC_6,V1,V2,V3,V4,V5,V6) :-
|
||||||
|
copy_term_nat(FC_6,C_6),
|
||||||
|
call(C_6,V1,V2,V3,V4,V5,V6).
|
||||||
|
\(FC_7,V1,V2,V3,V4,V5,V6,V7) :-
|
||||||
|
copy_term_nat(FC_7,C_7),
|
||||||
|
call(C_7,V1,V2,V3,V4,V5,V6,V7).
|
||||||
|
|
||||||
|
|
||||||
|
+\(GV,FC_0) :-
|
||||||
|
copy_term_nat(GV+FC_0,GV+C_0),
|
||||||
|
no_hat_call(C_0).
|
||||||
|
+\(GV,FC_1,V1) :-
|
||||||
|
copy_term_nat(GV+FC_1,GV+C_1),
|
||||||
|
call(C_1,V1).
|
||||||
|
+\(GV,FC_2,V1,V2) :-
|
||||||
|
copy_term_nat(GV+FC_2,GV+C_2),
|
||||||
|
call(C_2,V1,V2).
|
||||||
|
+\(GV,FC_3,V1,V2,V3) :-
|
||||||
|
copy_term_nat(GV+FC_3,GV+C_3),
|
||||||
|
call(C_3,V1,V2,V3).
|
||||||
|
+\(GV,FC_4,V1,V2,V3,V4) :-
|
||||||
|
copy_term_nat(GV+FC_4,GV+C_4),
|
||||||
|
call(C_4,V1,V2,V3,V4).
|
||||||
|
+\(GV,FC_5,V1,V2,V3,V4,V5) :-
|
||||||
|
copy_term_nat(GV+FC_5,GV+C_5),
|
||||||
|
call(C_5,V1,V2,V3,V4,V5).
|
||||||
|
+\(GV,FC_6,V1,V2,V3,V4,V5,V6) :-
|
||||||
|
copy_term_nat(GV+FC_6,GV+C_6),
|
||||||
|
call(C_6,V1,V2,V3,V4,V5,V6).
|
||||||
|
+\(GV,FC_7,V1,V2,V3,V4,V5,V6,V7) :-
|
||||||
|
copy_term_nat(GV+FC_7,GV+C_7),
|
||||||
|
call(C_7,V1,V2,V3,V4,V5,V6,V7).
|
||||||
|
|
||||||
|
|
||||||
|
%% no_hat_call(:Goal_0)
|
||||||
|
%
|
||||||
|
% Like call, but issues an error for a goal (^)/2. Such goals are
|
||||||
|
% likely the result of an insufficient number of arguments.
|
||||||
|
|
||||||
|
no_hat_call(MGoal_0) :-
|
||||||
|
strip_module(MGoal_0, _, Goal_0),
|
||||||
|
( nonvar(Goal_0),
|
||||||
|
Goal_0 = (_^_)
|
||||||
|
-> throw(
|
||||||
|
error(
|
||||||
|
existence_error(lambda_parameter,MGoal_0),
|
||||||
|
_))
|
||||||
|
; call(MGoal_0)
|
||||||
|
).
|
||||||
|
|
||||||
|
% I would like to replace this by:
|
||||||
|
% V1^Goal :- throw(error(existence_error(lambda_parameter,V1^Goal),_)).
|
||||||
550
src/lib/lists.pl
Normal file
550
src/lib/lists.pl
Normal file
@@ -0,0 +1,550 @@
|
|||||||
|
/**
|
||||||
|
List manipulation predicates
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5, foldl/6,
|
||||||
|
memberchk/2, reverse/2, length/2, maplist/2,
|
||||||
|
maplist/3, maplist/4, maplist/5, maplist/6,
|
||||||
|
maplist/7, maplist/8, maplist/9, same_length/2, nth0/3, nth0/4, nth1/3, nth1/4,
|
||||||
|
sum_list/2, transpose/2, list_to_set/2, list_max/2,
|
||||||
|
list_min/2, permutation/2]).
|
||||||
|
|
||||||
|
/* Author: Mark Thom, Jan Wielemaker, and Richard O'Keefe
|
||||||
|
Copyright (c) 2018-2021, Mark Thom
|
||||||
|
Copyright (c) 2002-2020, University of Amsterdam
|
||||||
|
VU University Amsterdam
|
||||||
|
SWI-Prolog Solutions b.v.
|
||||||
|
All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
:- 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(foldl(3, ?, ?, ?)).
|
||||||
|
:- meta_predicate(foldl(4, ?, ?, ?, ?)).
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
|
||||||
|
:- meta_predicate(resource_error(+,:)).
|
||||||
|
|
||||||
|
resource_error(Resource, Context) :-
|
||||||
|
throw(error(resource_error(Resource), Context)).
|
||||||
|
|
||||||
|
%% length(?Xs, ?N).
|
||||||
|
%
|
||||||
|
% Relates a list to its length (number of elements). It can be used to count the elements of a current list or
|
||||||
|
% to create a list full of free variables with N length.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- length("abc", 3).
|
||||||
|
% true.
|
||||||
|
% ?- length("abc", N).
|
||||||
|
% N = 3.
|
||||||
|
% ?- length(Xs, 3).
|
||||||
|
% Xs = [_A,_B,_C].
|
||||||
|
% ```
|
||||||
|
|
||||||
|
length(Xs0, N) :-
|
||||||
|
'$skip_max_list'(M, N, Xs0,Xs),
|
||||||
|
!,
|
||||||
|
( Xs == [] -> N = M
|
||||||
|
; nonvar(Xs) -> var(N), Xs = [_|_], resource_error(finite_memory,length/2)
|
||||||
|
; nonvar(N) -> R is N-M, length_rundown(Xs, R)
|
||||||
|
; N == Xs -> failingvarskip(Xs), resource_error(finite_memory,length/2)
|
||||||
|
; length_addendum(Xs, N, M)
|
||||||
|
).
|
||||||
|
length(_, N) :-
|
||||||
|
integer(N), !,
|
||||||
|
domain_error(not_less_than_zero, N, length/2).
|
||||||
|
length(_, N) :-
|
||||||
|
type_error(integer, N, length/2).
|
||||||
|
|
||||||
|
length_rundown(Xs, 0) :- !, Xs = [].
|
||||||
|
length_rundown(Vs, N) :-
|
||||||
|
'$unattributed_var'(Vs), % unconstrained
|
||||||
|
!,
|
||||||
|
'$det_length_rundown'(Vs, N).
|
||||||
|
length_rundown([_|Xs], N) :- % force unification
|
||||||
|
N1 is N-1,
|
||||||
|
length(Xs, N1). % maybe some new info on Xs
|
||||||
|
|
||||||
|
failingvarskip(Xs) :-
|
||||||
|
'$unattributed_var'(Xs), % unconstrained
|
||||||
|
!.
|
||||||
|
failingvarskip([_|Xs0]) :- % force unification
|
||||||
|
'$skip_max_list'(_, _, Xs0,Xs),
|
||||||
|
( nonvar(Xs) -> Xs = [_|_]
|
||||||
|
; failingvarskip(Xs)
|
||||||
|
).
|
||||||
|
|
||||||
|
length_addendum([], N, N).
|
||||||
|
length_addendum([_|Xs], N, M) :-
|
||||||
|
M1 is M + 1,
|
||||||
|
length_addendum(Xs, N, M1).
|
||||||
|
|
||||||
|
%% member(?X, ?Xs).
|
||||||
|
%
|
||||||
|
% Succeeds when X unifies with an item of the list Xs, which can be at any position.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- member(X, "hello world").
|
||||||
|
% X = h
|
||||||
|
% ; ... .
|
||||||
|
% ```
|
||||||
|
|
||||||
|
member(X, [L|Ls]) :-
|
||||||
|
member_(Ls, L, X).
|
||||||
|
|
||||||
|
member_(_, X, X).
|
||||||
|
member_([L|Ls], _, X) :-
|
||||||
|
member_(Ls, L, X).
|
||||||
|
|
||||||
|
%% select(X, Xs0, Xs1).
|
||||||
|
%
|
||||||
|
% Succeeds when the list Xs1 is the list Xs0 without the item X
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- select(c, "abcd", X).
|
||||||
|
% X = "abd"
|
||||||
|
% ; false.
|
||||||
|
% ```
|
||||||
|
select(X, [X|Xs], Xs).
|
||||||
|
select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys).
|
||||||
|
|
||||||
|
%% append(+XsXs, ?Xs).
|
||||||
|
%
|
||||||
|
% Concatenates a list of lists
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- append([[1, 2], [3]], Xs).
|
||||||
|
% Xs = [1,2,3].
|
||||||
|
% ```
|
||||||
|
append([], []).
|
||||||
|
append([L0|Ls0], Ls) :-
|
||||||
|
append(L0, Rest, Ls),
|
||||||
|
append(Ls0, Rest).
|
||||||
|
|
||||||
|
%% append(Xs0, Xs1, Xs).
|
||||||
|
%
|
||||||
|
% List Xs is the concatenation of Xs0 and Xs1
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- append([1,2,3], [4,5,6], Xs).
|
||||||
|
% Xs = [1,2,3,4,5,6].
|
||||||
|
% ```
|
||||||
|
append([], R, R).
|
||||||
|
append([X|L], R, [X|S]) :- append(L, R, S).
|
||||||
|
|
||||||
|
%% memberchk(?X, +Xs).
|
||||||
|
%
|
||||||
|
% This predicate is similar to `member/2`, but it only provides a single answer
|
||||||
|
memberchk(X, Xs) :- member(X, Xs), !.
|
||||||
|
|
||||||
|
%% reverse(?Xs, ?Ys).
|
||||||
|
%
|
||||||
|
% Xs is the Ys list in reverse order
|
||||||
|
%
|
||||||
|
% ?- reverse([1,2,3], [3,2,1]).
|
||||||
|
% true.
|
||||||
|
%
|
||||||
|
reverse(Xs, Ys) :-
|
||||||
|
( nonvar(Xs) -> reverse(Xs, Ys, [], Xs)
|
||||||
|
; reverse(Ys, Xs, [], Ys)
|
||||||
|
).
|
||||||
|
|
||||||
|
reverse([], [], YsRev, YsRev).
|
||||||
|
reverse([_|Xs], [Y1|Ys], YsPreludeRev, Xss) :-
|
||||||
|
reverse(Xs, Ys, [Y1|YsPreludeRev], Xss).
|
||||||
|
|
||||||
|
%% maplist(+Predicate, ?Xs0).
|
||||||
|
%
|
||||||
|
% This is a metapredicate that applies predicate to each element of the list Xs0
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- maplist(write, [1,2,3]).
|
||||||
|
% 123 true.
|
||||||
|
% ```
|
||||||
|
maplist(_, []).
|
||||||
|
maplist(Cont1, [E1|E1s]) :-
|
||||||
|
call(Cont1, E1),
|
||||||
|
maplist(Cont1, E1s).
|
||||||
|
|
||||||
|
%% maplist(+Predicate, ?Xs0, ?Xs1).
|
||||||
|
%
|
||||||
|
% This is a metapredicate that applies predicate to each element of the lists Xs0 and Xs1.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- maplist(length, ["hello", "prolog", "marseille"], Xs1).
|
||||||
|
% Xs1 = [5,6,9].
|
||||||
|
% ```
|
||||||
|
maplist(_, [], []).
|
||||||
|
maplist(Cont2, [E1|E1s], [E2|E2s]) :-
|
||||||
|
call(Cont2, E1, E2),
|
||||||
|
maplist(Cont2, E1s, E2s).
|
||||||
|
|
||||||
|
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2).
|
||||||
|
%
|
||||||
|
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1 and Xs2.
|
||||||
|
maplist(_, [], [], []).
|
||||||
|
maplist(Cont3, [E1|E1s], [E2|E2s], [E3|E3s]) :-
|
||||||
|
call(Cont3, E1, E2, E3),
|
||||||
|
maplist(Cont3, E1s, E2s, E3s).
|
||||||
|
|
||||||
|
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3).
|
||||||
|
%
|
||||||
|
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2 and Xs3.
|
||||||
|
maplist(_, [], [], [], []).
|
||||||
|
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s]) :-
|
||||||
|
call(Cont, E1, E2, E3, E4),
|
||||||
|
maplist(Cont, E1s, E2s, E3s, E4s).
|
||||||
|
|
||||||
|
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4).
|
||||||
|
%
|
||||||
|
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3 and Xs4.
|
||||||
|
maplist(_, [], [], [], [], []).
|
||||||
|
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s]) :-
|
||||||
|
call(Cont, E1, E2, E3, E4, E5),
|
||||||
|
maplist(Cont, E1s, E2s, E3s, E4s, E5s).
|
||||||
|
|
||||||
|
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5).
|
||||||
|
%
|
||||||
|
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4 and Xs5.
|
||||||
|
maplist(_, [], [], [], [], [], []).
|
||||||
|
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s]) :-
|
||||||
|
call(Cont, E1, E2, E3, E4, E5, E6),
|
||||||
|
maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s).
|
||||||
|
|
||||||
|
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5, ?Xs6).
|
||||||
|
%
|
||||||
|
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4, Xs5 and Xs6.
|
||||||
|
maplist(_, [], [], [], [], [], [], []).
|
||||||
|
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s]) :-
|
||||||
|
call(Cont, E1, E2, E3, E4, E5, E6, E7),
|
||||||
|
maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s, E7s).
|
||||||
|
|
||||||
|
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5, ?Xs6, ?Xs7).
|
||||||
|
%
|
||||||
|
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4, Xs5, Xs6 and Xs7.
|
||||||
|
maplist(_, [], [], [], [], [], [], [], []).
|
||||||
|
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s], [E8|E8s]) :-
|
||||||
|
call(Cont, E1, E2, E3, E4, E5, E6, E7, E8),
|
||||||
|
maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s, E7s, E8s).
|
||||||
|
|
||||||
|
%% sum_list(+Xs, -Sum).
|
||||||
|
%
|
||||||
|
% Takes a lists of numbers and unifies Sum with the result of summing all the elements of the list.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- sum_list([2,2,2], 6).
|
||||||
|
% true.
|
||||||
|
% ```
|
||||||
|
sum_list(Ls, S) :-
|
||||||
|
foldl(lists:sum_, Ls, 0, S).
|
||||||
|
|
||||||
|
sum_(L, S0, S) :- S is S0 + L.
|
||||||
|
|
||||||
|
|
||||||
|
%% same_length(?Xs, ?Ys).
|
||||||
|
%
|
||||||
|
% Succeeds if Xs and Ys are lists of the same length
|
||||||
|
same_length([], []).
|
||||||
|
same_length([_|As], [_|Bs]) :-
|
||||||
|
same_length(As, Bs).
|
||||||
|
|
||||||
|
%% foldl(+Predicate, ?Ls, +A0, ?A).
|
||||||
|
%
|
||||||
|
% foldl, sometimes called reduce, is a metapredicate that takes a predicate, a list of items
|
||||||
|
% and a starting value, and outputs a single value. The predicate _Predicate_ must be able to take the current
|
||||||
|
% element of the list, the previous value of the computation and the next value of the computation.
|
||||||
|
%
|
||||||
|
% For example, if we define sum_ as:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% sum_(L, S0, S) :- S is S0 + L.
|
||||||
|
% ```
|
||||||
|
%
|
||||||
|
% Then we can define `sum_list/2` as the following:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% sum_list(Ls, S) :- foldl(sum_, Ls, 0, S).
|
||||||
|
% ```
|
||||||
|
|
||||||
|
foldl(_, [], A, A).
|
||||||
|
foldl(G_3, [L|Ls], A0, A) :-
|
||||||
|
call(G_3, L, A0, A1),
|
||||||
|
foldl(G_3, Ls, A1, A).
|
||||||
|
|
||||||
|
%% foldl(+Predicate, ?Ls0, ?Ls1, +A0, ?A).
|
||||||
|
%
|
||||||
|
% Same as `foldl/4` but with an extra list
|
||||||
|
|
||||||
|
foldl(_, [], [], A, A).
|
||||||
|
foldl(G_4, [X|Xs], [Y|Ys], A0, A) :-
|
||||||
|
call(G_4, X, Y, A0, A1),
|
||||||
|
foldl(G_4, Xs, Ys, A1, A).
|
||||||
|
|
||||||
|
|
||||||
|
%% foldl(+Goal, ?Ls0, ?Ls1, ?Ls2, +A0, ?A).
|
||||||
|
%
|
||||||
|
% Like `foldl/4`, with 2 additional lists.
|
||||||
|
|
||||||
|
foldl(_, [], [], [], A, A).
|
||||||
|
foldl(G_5, [X|Xs], [Y|Ys], [Z|Zs], A0, A) :-
|
||||||
|
call(G_5, X, Y, Z, A0, A1),
|
||||||
|
foldl(G_5, Xs, Ys, Zs, A1, A).
|
||||||
|
|
||||||
|
%% transpose(?Ls, ?Ts).
|
||||||
|
%
|
||||||
|
% If Ls is a list of lists, Ts contains the transposition
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- transpose([[1,1],[2,2]], Ts).
|
||||||
|
% Ts = [[1,2],[1,2]].
|
||||||
|
% ```
|
||||||
|
transpose(Ls, Ts) :-
|
||||||
|
lists_transpose(Ls, Ts).
|
||||||
|
|
||||||
|
lists_transpose([], []).
|
||||||
|
lists_transpose([L|Ls], Ts) :-
|
||||||
|
maplist(lists:same_length(L), Ls),
|
||||||
|
foldl(lists:transpose_, L, Ts, [L|Ls], _).
|
||||||
|
|
||||||
|
transpose_(_, Fs, Lists0, Lists) :-
|
||||||
|
maplist(lists:list_first_rest, Lists0, Fs, Lists).
|
||||||
|
|
||||||
|
list_first_rest([L|Ls], L, Ls).
|
||||||
|
|
||||||
|
%% list_to_set(+Ls0, -Set).
|
||||||
|
%
|
||||||
|
% Takes a list Ls0 and returns a list Set that doesn't contain any repeated element
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- list_to_set([2,3,4,4,1,2], Set).
|
||||||
|
% Set = [2,3,4,1].
|
||||||
|
% ```
|
||||||
|
list_to_set(Ls0, Ls) :-
|
||||||
|
maplist(lists:with_var, Ls0, LVs0),
|
||||||
|
keysort(LVs0, LVs),
|
||||||
|
same_elements(LVs),
|
||||||
|
pick_firsts(LVs0, Ls).
|
||||||
|
|
||||||
|
pick_firsts([], []).
|
||||||
|
pick_firsts([E-V|EVs], Fs0) :-
|
||||||
|
( V == visited ->
|
||||||
|
Fs0 = Fs
|
||||||
|
; V = visited,
|
||||||
|
Fs0 = [E|Fs]
|
||||||
|
),
|
||||||
|
pick_firsts(EVs, Fs).
|
||||||
|
|
||||||
|
with_var(E, E-_).
|
||||||
|
|
||||||
|
same_elements([]).
|
||||||
|
same_elements([EV|EVs]) :-
|
||||||
|
foldl(lists:unify_same, EVs, EV, _).
|
||||||
|
|
||||||
|
unify_same(E-V, Prev-Var, E-V) :-
|
||||||
|
( Prev == E ->
|
||||||
|
Var = V
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
%% nth0(?N, ?Ls, ?E).
|
||||||
|
%
|
||||||
|
% Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from zero.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- nth0(2, [1,2,3,4], 3).
|
||||||
|
% true.
|
||||||
|
% ```
|
||||||
|
nth0(N, Es0, E) :-
|
||||||
|
nonvar(N),
|
||||||
|
'$skip_max_list'(Skip, N, Es0,Es1),
|
||||||
|
!,
|
||||||
|
( Skip == N
|
||||||
|
-> Es1 = [E|_]
|
||||||
|
; ( var(Es1) ; Es1 = [_|_] ) % a partial or infinite list
|
||||||
|
-> R is N-Skip,
|
||||||
|
skipn(R,Es1,Es2),
|
||||||
|
Es2 = [E|_]
|
||||||
|
).
|
||||||
|
nth0(N, Es0, E) :-
|
||||||
|
can_be(not_less_than_zero, N),
|
||||||
|
Es0 = [E0|Es1],
|
||||||
|
nth0_el(0,N, E0,E, Es1).
|
||||||
|
|
||||||
|
skipn(N0, Es0,Es) :-
|
||||||
|
N0>0,
|
||||||
|
N1 is N0-1,
|
||||||
|
Es0 = [_|Es1],
|
||||||
|
skipn(N1, Es1,Es).
|
||||||
|
skipn(0, Es,Es).
|
||||||
|
|
||||||
|
nth0_el(N0,N, E0,E, Es0) :-
|
||||||
|
Es0 == [],
|
||||||
|
!, % indexing
|
||||||
|
N0 = N,
|
||||||
|
E0 = E.
|
||||||
|
nth0_el(N,N, E,E, _).
|
||||||
|
nth0_el(N0,N, _,E, [E0|Es0]) :-
|
||||||
|
N1 is N0+1,
|
||||||
|
nth0_el(N1,N, E0,E, Es0).
|
||||||
|
|
||||||
|
%% nth1(?N, ?Ls, ?E).
|
||||||
|
%
|
||||||
|
% Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from one.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- nth1(2, [1,2,3,4], 2).
|
||||||
|
% true.
|
||||||
|
% ```
|
||||||
|
nth1(N, Es0, E) :-
|
||||||
|
N \== 0,
|
||||||
|
nth0(N, [_|Es0], E),
|
||||||
|
N \== 0.
|
||||||
|
|
||||||
|
skipn(N0, Es0,Es, Xs0,Xs) :-
|
||||||
|
N0>0,
|
||||||
|
N1 is N0-1,
|
||||||
|
Es0 = [E|Es1],
|
||||||
|
Xs0 = [E|Xs1],
|
||||||
|
skipn(N1, Es1,Es, Xs1,Xs).
|
||||||
|
skipn(0, Es,Es, Xs,Xs).
|
||||||
|
|
||||||
|
%% nth0(?N, ?Ls, ?E, ?Rs).
|
||||||
|
%
|
||||||
|
% Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from zero.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- nth0(2, [1,2,3,4], 3, [1,2,4]).
|
||||||
|
% true.
|
||||||
|
% ```
|
||||||
|
nth0(N, Es0, E, Es) :-
|
||||||
|
integer(N),
|
||||||
|
N >= 0,
|
||||||
|
!,
|
||||||
|
skipn(N, Es0,Es1, Es,Es2),
|
||||||
|
Es1 = [E|Es2].
|
||||||
|
nth0(N, Es0, E, Es) :-
|
||||||
|
can_be(not_less_than_zero, N),
|
||||||
|
Es0 = [E0|Es1],
|
||||||
|
nth0_elx(0,N, E0,E, Es1, Es).
|
||||||
|
|
||||||
|
nth0_elx(N0,N, E0,E, Es0, Es) :-
|
||||||
|
Es0 == [],
|
||||||
|
!,
|
||||||
|
N0 = N,
|
||||||
|
E0 = E,
|
||||||
|
Es0 = Es.
|
||||||
|
nth0_elx(N,N, E,E, Es, Es).
|
||||||
|
nth0_elx(N0,N, E0,E, [E1|Es0], [E0|Es]) :-
|
||||||
|
N1 is N0+1,
|
||||||
|
nth0_elx(N1,N, E1,E, Es0, Es).
|
||||||
|
|
||||||
|
% p.p.8.5
|
||||||
|
|
||||||
|
%% nth1(?N, ?Ls, ?E, ?Rs).
|
||||||
|
%
|
||||||
|
% Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from one.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- nth1(2, [1,2,3,4], 2, [1,3,4]).
|
||||||
|
% true.
|
||||||
|
% ```
|
||||||
|
nth1(N, Es0, E, Es) :-
|
||||||
|
N \== 0,
|
||||||
|
nth0(N, [_|Es0], E, [_|Es]),
|
||||||
|
N \== 0.
|
||||||
|
|
||||||
|
%% list_max(+Xs, -Max).
|
||||||
|
%
|
||||||
|
% Takes a list Xs and unifies with the maximum value of the list
|
||||||
|
list_max([N|Ns], Max) :-
|
||||||
|
foldl(lists:list_max_, Ns, N, Max).
|
||||||
|
|
||||||
|
list_max_(N, Max0, Max) :-
|
||||||
|
Max is max(N, Max0).
|
||||||
|
|
||||||
|
%% list_min(+Xs, -Min).
|
||||||
|
%
|
||||||
|
% Takes a list Xs and unifies with the minimum value of the list
|
||||||
|
list_min([N|Ns], Min) :-
|
||||||
|
foldl(lists:list_min_, Ns, N, Min).
|
||||||
|
|
||||||
|
list_min_(N, Min0, Min) :-
|
||||||
|
Min is min(N, Min0).
|
||||||
|
|
||||||
|
%% permutation(?Xs, ?Ys) is nondet.
|
||||||
|
%
|
||||||
|
% True when Xs is a permutation of Ys. This can solve for Ys given
|
||||||
|
% Xs or Xs given Ys, or even enumerate Xs and Ys together. The
|
||||||
|
% predicate `permutation/2` is primarily intended to generate
|
||||||
|
% permutations. Note that a list of length N has N! permutations,
|
||||||
|
% and unbounded permutation generation becomes prohibitively
|
||||||
|
% expensive, even for rather short lists (10! = 3,628,800).
|
||||||
|
%
|
||||||
|
% The example below illustrates that Xs and Ys being proper lists
|
||||||
|
% is not a sufficient condition to use the above replacement.
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- permutation([1,2], [X,Y]).
|
||||||
|
% X = 1, Y = 2
|
||||||
|
% ; X = 2, Y = 1
|
||||||
|
% ; false.
|
||||||
|
% ```
|
||||||
|
%
|
||||||
|
% Throws `type_error(list, Arg)` if either argument is not a proper
|
||||||
|
% or partial list.
|
||||||
|
|
||||||
|
permutation(Xs, Ys) :-
|
||||||
|
'$skip_max_list'(Xlen, _, Xs, XTail),
|
||||||
|
'$skip_max_list'(Ylen, _, Ys, YTail),
|
||||||
|
( XTail == [], YTail == [] % both proper lists
|
||||||
|
-> Xlen == Ylen
|
||||||
|
; var(XTail), YTail == [] % partial, proper
|
||||||
|
-> length(Xs, Ylen)
|
||||||
|
; XTail == [], var(YTail) % proper, partial
|
||||||
|
-> length(Ys, Xlen)
|
||||||
|
; var(XTail), var(YTail) % partial, partial
|
||||||
|
-> length(Xs, Len),
|
||||||
|
length(Ys, Len)
|
||||||
|
; must_be(list, Xs), % either is not a list
|
||||||
|
must_be(list, Ys)
|
||||||
|
),
|
||||||
|
perm(Xs, Ys).
|
||||||
|
|
||||||
|
perm([], []).
|
||||||
|
perm(List, [First|Perm]) :-
|
||||||
|
select(First, List, Rest),
|
||||||
|
perm(Rest, Perm).
|
||||||
190
src/lib/numerics/quadtests.pl
Normal file
190
src/lib/numerics/quadtests.pl
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
% Efforts toward literate tests with quads
|
||||||
|
|
||||||
|
:- module(quadtests, [check_module_quads/2]).
|
||||||
|
|
||||||
|
:- 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(library('numerics/testutils')).
|
||||||
|
:- use_module(library('numerics/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(Module), 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 = (_ = _) -> 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(Module, Q-QVN, A-AVN) :-
|
||||||
|
Q = ?-(G),
|
||||||
|
phrase(portray_clause_(Q), LitQ), % NB: LitQ terminates w/ newline
|
||||||
|
format("% CHECKING.. ",[]),
|
||||||
|
( A == true -> call(Module:G)
|
||||||
|
; A == false -> ( call(Module:G) -> false
|
||||||
|
; true
|
||||||
|
)
|
||||||
|
; phrase(unconj(A), As) ->
|
||||||
|
( length(As, N),
|
||||||
|
n_answers(N, A, AVN, ADs),
|
||||||
|
n_answers(N, Module:G, QVN, Answers),
|
||||||
|
maplist(contains, ADs, Answers)
|
||||||
|
)
|
||||||
|
; % Otherwise, we have the ',' case of a solitary answer
|
||||||
|
call(Module: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(quadtests, (?-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]).
|
||||||
133
src/lib/ops_and_meta_predicates.pl
Normal file
133
src/lib/ops_and_meta_predicates.pl
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
:- op(400, yfx, /).
|
||||||
|
|
||||||
|
% module resolution operator.
|
||||||
|
:- op(600, xfy, :).
|
||||||
|
|
||||||
|
% 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.
|
||||||
|
:- op(700, xfx, is).
|
||||||
|
:- op(500, yfx, +).
|
||||||
|
:- op(500, yfx, -).
|
||||||
|
:- op(400, yfx, *).
|
||||||
|
:- op(200, xfx, **).
|
||||||
|
:- op(200, xfy, ^).
|
||||||
|
:- op(500, yfx, /\).
|
||||||
|
:- op(500, yfx, \/).
|
||||||
|
:- op(400, yfx, div).
|
||||||
|
:- op(400, yfx, //).
|
||||||
|
:- op(400, yfx, rdiv).
|
||||||
|
:- op(400, yfx, <<).
|
||||||
|
:- op(400, yfx, >>).
|
||||||
|
:- op(400, yfx, mod).
|
||||||
|
:- op(400, yfx, rem).
|
||||||
|
:- op(200, fy, +).
|
||||||
|
:- op(200, fy, -).
|
||||||
|
:- op(200, fy, \).
|
||||||
|
|
||||||
|
% arithmetic comparison operators.
|
||||||
|
:- op(700, xfx, >).
|
||||||
|
:- op(700, xfx, <).
|
||||||
|
:- op(700, xfx, =\=).
|
||||||
|
:- op(700, xfx, =:=).
|
||||||
|
:- op(700, xfx, >=).
|
||||||
|
:- op(700, xfx, =<).
|
||||||
|
|
||||||
|
% term comparison.
|
||||||
|
:- op(700, xfx, ==).
|
||||||
|
:- op(700, xfx, \==).
|
||||||
|
:- op(700, xfx, @=<).
|
||||||
|
:- op(700, xfx, @>=).
|
||||||
|
:- op(700, xfx, @<).
|
||||||
|
:- op(700, xfx, @>).
|
||||||
|
|
||||||
|
% conditional operators.
|
||||||
|
:- op(1050, xfy, ->).
|
||||||
|
:- op(1100, xfy, ;).
|
||||||
|
|
||||||
|
% control.
|
||||||
|
:- op(700, xfx, =).
|
||||||
|
:- op(700, xfx, =..).
|
||||||
|
:- op(700, xfx, \=).
|
||||||
|
:- op(900, fy, \+).
|
||||||
|
|
||||||
|
:- 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, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)).
|
||||||
@@ -54,42 +54,41 @@
|
|||||||
|
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
/** <module> Ordered set manipulation
|
/** Ordered set manipulation
|
||||||
|
|
||||||
Ordered sets are lists with unique elements sorted to the standard order
|
Ordered sets are lists with unique elements sorted to the standard order
|
||||||
of terms (see sort/2). Exploiting ordering, many of the set operations
|
of terms (see `sort/2`). Exploiting ordering, many of the set operations
|
||||||
can be expressed in order N rather than N^2 when dealing with unordered
|
can be expressed in order N rather than N^2 when dealing with unordered
|
||||||
sets that may contain duplicates. The library(ordsets) is available in a
|
sets that may contain duplicates. The library(ordsets) is available in a
|
||||||
number of Prolog implementations. Our predicates are designed to be
|
number of Prolog implementations. Our predicates are designed to be
|
||||||
compatible with common practice in the Prolog community. The
|
compatible with common practice in the Prolog community.
|
||||||
implementation is incomplete and relies partly on library(oset), an
|
|
||||||
older ordered set library distributed with SWI-Prolog. New applications
|
|
||||||
are advised to use library(ordsets).
|
|
||||||
Some of these predicates match directly to corresponding list
|
Some of these predicates match directly to corresponding list
|
||||||
operations. It is advised to use the versions from this library to make
|
operations. It is advised to use the versions from this library to make
|
||||||
clear you are operating on ordered sets. An exception is member/2. See
|
clear you are operating on ordered sets. An exception is `member/2`. See
|
||||||
ord_memberchk/2.
|
`ord_memberchk/2`.
|
||||||
|
|
||||||
The ordsets library is based on the standard order of terms. This
|
The ordsets library is based on the standard order of terms. This
|
||||||
implies it can handle all Prolog terms, including variables. Note
|
implies it can handle all Prolog terms, including variables. Note
|
||||||
however, that the ordering is not stable if a term inside the set is
|
however, that the ordering is not stable if a term inside the set is
|
||||||
further instantiated. Also note that variable ordering changes if
|
further instantiated. Also note that variable ordering changes if
|
||||||
variables in the set are unified with each other or a variable in the
|
variables in the set are unified with each other or a variable in the
|
||||||
set is unified with a variable that is `older' than the newest variable
|
set is unified with a variable that is _older_ than the newest variable
|
||||||
in the set. In practice, this implies that it is allowed to use
|
in the set. In practice, this implies that it is allowed to use
|
||||||
member(X, OrdSet) on an ordered set that holds variables only if X is a
|
member(X, OrdSet) on an ordered set that holds variables only if X is a
|
||||||
fresh variable. In other cases one should cease using it as an ordset
|
fresh variable. In other cases one should cease using it as an ordset
|
||||||
because the order it relies on may have been changed.
|
because the order it relies on may have been changed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
%! is_ordset(@Term) is semidet.
|
%% is_ordset(@Term) is semidet.
|
||||||
%
|
%
|
||||||
% True if Term is an ordered set. All predicates in this library
|
% True if Term is an ordered set. All predicates in this library
|
||||||
% expect ordered sets as input arguments. Failing to fullfil this
|
% expect ordered sets as input arguments. Failing to fullfil this
|
||||||
% assumption results in undefined behaviour. Typically, ordered
|
% assumption results in undefined behaviour. Typically, ordered
|
||||||
% sets are created by predicates from this library, sort/2 or
|
% sets are created by predicates from this library, `sort/2` or
|
||||||
% setof/3.
|
% `setof/3`.
|
||||||
|
|
||||||
is_ordset(Term) :-
|
is_ordset(Term) :-
|
||||||
'$skip_max_list'(_, -1, Term, Tail), Tail == [], %% is_list(Term),
|
'$skip_max_list'(_, _, Term, Tail), Tail == [], %% is_list(Term),
|
||||||
is_ordset2(Term).
|
is_ordset2(Term).
|
||||||
|
|
||||||
is_ordset2([]).
|
is_ordset2([]).
|
||||||
@@ -102,7 +101,7 @@ is_ordset3([H2|T], H) :-
|
|||||||
is_ordset3(T, H2).
|
is_ordset3(T, H2).
|
||||||
|
|
||||||
|
|
||||||
%! ord_empty(?List) is semidet.
|
%% ord_empty(?List) is semidet.
|
||||||
%
|
%
|
||||||
% True when List is the empty ordered set. Simply unifies list
|
% True when List is the empty ordered set. Simply unifies list
|
||||||
% with the empty list. Not part of Quintus.
|
% with the empty list. Not part of Quintus.
|
||||||
@@ -110,18 +109,16 @@ is_ordset3([H2|T], H) :-
|
|||||||
ord_empty([]).
|
ord_empty([]).
|
||||||
|
|
||||||
|
|
||||||
%! ord_seteq(+Set1, +Set2) is semidet.
|
%% ord_seteq(+Set1, +Set2) is semidet.
|
||||||
%
|
%
|
||||||
% True if Set1 and Set2 have the same elements. As both are
|
% True if Set1 and Set2 have the same elements. As both are
|
||||||
% canonical sorted lists, this is the same as ==/2.
|
% canonical sorted lists, this is the same as `==/2`.
|
||||||
%
|
|
||||||
% @compat sicstus
|
|
||||||
|
|
||||||
ord_seteq(Set1, Set2) :-
|
ord_seteq(Set1, Set2) :-
|
||||||
Set1 == Set2.
|
Set1 == Set2.
|
||||||
|
|
||||||
|
|
||||||
%! list_to_ord_set(+List, -OrdSet) is det.
|
%% list_to_ord_set(+List, -OrdSet) is det.
|
||||||
%
|
%
|
||||||
% Transform a list into an ordered set. This is the same as
|
% Transform a list into an ordered set. This is the same as
|
||||||
% sorting the list.
|
% sorting the list.
|
||||||
@@ -130,7 +127,7 @@ list_to_ord_set(List, Set) :-
|
|||||||
sort(List, Set).
|
sort(List, Set).
|
||||||
|
|
||||||
|
|
||||||
%! ord_intersect(+Set1, +Set2) is semidet.
|
%% ord_intersect(+Set1, +Set2) is semidet.
|
||||||
%
|
%
|
||||||
% True if both ordered sets have a non-empty intersection.
|
% True if both ordered sets have a non-empty intersection.
|
||||||
|
|
||||||
@@ -148,31 +145,29 @@ ord_intersect__(>, H1, T1, _H2, T2) :-
|
|||||||
ord_intersect_(T2, H1, T1).
|
ord_intersect_(T2, H1, T1).
|
||||||
|
|
||||||
|
|
||||||
%! ord_disjoint(+Set1, +Set2) is semidet.
|
%% ord_disjoint(+Set1, +Set2) is semidet.
|
||||||
%
|
%
|
||||||
% True if Set1 and Set2 have no common elements. This is the
|
% True if Set1 and Set2 have no common elements. This is the
|
||||||
% negation of ord_intersect/2.
|
% negation of `ord_intersect/2`.
|
||||||
|
|
||||||
ord_disjoint(Set1, Set2) :-
|
ord_disjoint(Set1, Set2) :-
|
||||||
\+ ord_intersect(Set1, Set2).
|
\+ ord_intersect(Set1, Set2).
|
||||||
|
|
||||||
|
|
||||||
%! ord_intersect(+Set1, +Set2, -Intersection)
|
%% ord_intersect(+Set1, +Set2, -Intersection)
|
||||||
%
|
%
|
||||||
% Intersection holds the common elements of Set1 and Set2.
|
% Intersection holds the common elements of Set1 and Set2.
|
||||||
%
|
%
|
||||||
% @deprecated Use ord_intersection/3
|
% This predicate is *deprecated*. Use `ord_intersection/3`
|
||||||
|
|
||||||
ord_intersect(Set1, Set2, Intersection) :-
|
ord_intersect(Set1, Set2, Intersection) :-
|
||||||
oset_int(Set1, Set2, Intersection).
|
oset_int(Set1, Set2, Intersection).
|
||||||
|
|
||||||
|
|
||||||
%! ord_intersection(+PowerSet, -Intersection)
|
%% ord_intersection(+PowerSet, -Intersection)
|
||||||
%
|
%
|
||||||
% Intersection of a powerset. True when Intersection is an ordered
|
% Intersection of a powerset. True when Intersection is an ordered
|
||||||
% set holding all elements common to all sets in PowerSet.
|
% set holding all elements common to all sets in PowerSet.
|
||||||
%
|
|
||||||
% @compat sicstus
|
|
||||||
|
|
||||||
ord_intersection(PowerSet, Intersection) :-
|
ord_intersection(PowerSet, Intersection) :-
|
||||||
key_by_length(PowerSet, Pairs),
|
key_by_length(PowerSet, Pairs),
|
||||||
@@ -190,10 +185,10 @@ l_int([_-H|T], S0, S) :-
|
|||||||
l_int(T, S1, S).
|
l_int(T, S1, S).
|
||||||
|
|
||||||
|
|
||||||
%! ord_intersection(+Set1, +Set2, -Intersection) is det.
|
%% ord_intersection(+Set1, +Set2, -Intersection) is det.
|
||||||
%
|
%
|
||||||
% Intersection holds the common elements of Set1 and Set2. Uses
|
% Intersection holds the common elements of Set1 and Set2. Uses
|
||||||
% ord_disjoint/2 if Intersection is bound to `[]` on entry.
|
% `ord_disjoint/2` if Intersection is bound to `[]` on entry.
|
||||||
|
|
||||||
ord_intersection(Set1, Set2, Intersection) :-
|
ord_intersection(Set1, Set2, Intersection) :-
|
||||||
( Intersection == []
|
( Intersection == []
|
||||||
@@ -202,13 +197,11 @@ ord_intersection(Set1, Set2, Intersection) :-
|
|||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
%! ord_intersection(+Set1, +Set2, ?Intersection, ?Difference) is det.
|
%% ord_intersection(+Set1, +Set2, ?Intersection, ?Difference) is det.
|
||||||
%
|
%
|
||||||
% Intersection and difference between two ordered sets.
|
% Intersection and difference between two ordered sets.
|
||||||
% Intersection is the intersection between Set1 and Set2, while
|
% Intersection is the intersection between Set1 and Set2, while
|
||||||
% Difference is defined by ord_subtract(Set2, Set1, Difference).
|
% Difference is defined by `ord_subtract(Set2, Set1, Difference)`.
|
||||||
%
|
|
||||||
% @see ord_intersection/3 and ord_subtract/3.
|
|
||||||
|
|
||||||
ord_intersection([], L, [], L) :- !.
|
ord_intersection([], L, [], L) :- !.
|
||||||
ord_intersection([_|_], [], [], []) :- !.
|
ord_intersection([_|_], [], [], []) :- !.
|
||||||
@@ -224,35 +217,35 @@ ord_intersection2(>, H1, T1, H2, T2, Intersection, [H2|HDiff]) :-
|
|||||||
ord_intersection([H1|T1], T2, Intersection, HDiff).
|
ord_intersection([H1|T1], T2, Intersection, HDiff).
|
||||||
|
|
||||||
|
|
||||||
%! ord_add_element(+Set1, +Element, ?Set2) is det.
|
%% ord_add_element(+Set1, +Element, ?Set2) is det.
|
||||||
%
|
%
|
||||||
% Insert an element into the set. This is the same as
|
% Insert an element into the set. This is the same as
|
||||||
% ord_union(Set1, [Element], Set2).
|
% `ord_union(Set1, [Element], Set2)`.
|
||||||
|
|
||||||
ord_add_element(Set1, Element, Set2) :-
|
ord_add_element(Set1, Element, Set2) :-
|
||||||
oset_addel(Set1, Element, Set2).
|
oset_addel(Set1, Element, Set2).
|
||||||
|
|
||||||
|
|
||||||
%! ord_del_element(+Set, +Element, -NewSet) is det.
|
%% ord_del_element(+Set, +Element, -NewSet) is det.
|
||||||
%
|
%
|
||||||
% Delete an element from an ordered set. This is the same as
|
% Delete an element from an ordered set. This is the same as
|
||||||
% ord_subtract(Set, [Element], NewSet).
|
% `ord_subtract(Set, [Element], NewSet)`.
|
||||||
|
|
||||||
ord_del_element(Set, Element, NewSet) :-
|
ord_del_element(Set, Element, NewSet) :-
|
||||||
oset_delel(Set, Element, NewSet).
|
oset_delel(Set, Element, NewSet).
|
||||||
|
|
||||||
|
|
||||||
%! ord_selectchk(+Item, ?Set1, ?Set2) is semidet.
|
%% ord_selectchk(+Item, ?Set1, ?Set2) is semidet.
|
||||||
%
|
%
|
||||||
% Selectchk/3, specialised for ordered sets. Is true when
|
% `selectchk/3`, specialised for ordered sets. Is true when
|
||||||
% select(Item, Set1, Set2) and Set1, Set2 are both sorted lists
|
% select(Item, Set1, Set2) and Set1, Set2 are both sorted lists
|
||||||
% without duplicates. This implementation is only expected to work
|
% without duplicates. This implementation is only expected to work
|
||||||
% for Item ground and either Set1 or Set2 ground. The "chk" suffix
|
% for Item ground and either Set1 or Set2 ground. The "chk" suffix
|
||||||
% is meant to remind you of memberchk/2, which also expects its
|
% is meant to remind you of `memberchk/2`, which also expects its
|
||||||
% first argument to be ground. ord_selectchk(X, S, T) =>
|
% first argument to be ground. `ord_selectchk(X, S, T) =>
|
||||||
% ord_memberchk(X, S) & \+ ord_memberchk(X, T).
|
% ord_memberchk(X, S) & \+ ord_memberchk(X, T).`
|
||||||
%
|
%
|
||||||
% @author Richard O'Keefe
|
% Author: Richard O'Keefe
|
||||||
|
|
||||||
ord_selectchk(Item, [X|Set1], [X|Set2]) :-
|
ord_selectchk(Item, [X|Set1], [X|Set2]) :-
|
||||||
X @< Item,
|
X @< Item,
|
||||||
@@ -266,19 +259,19 @@ ord_selectchk(Item, [Item|Set1], Set1) :-
|
|||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
%! ord_memberchk(+Element, +OrdSet) is semidet.
|
%% ord_memberchk(+Element, +OrdSet) is semidet.
|
||||||
%
|
%
|
||||||
% True if Element is a member of OrdSet, compared using ==. Note
|
% True if Element is a member of OrdSet, compared using ==. Note
|
||||||
% that _enumerating_ elements of an ordered set can be done using
|
% that _enumerating_ elements of an ordered set can be done using
|
||||||
% member/2.
|
% `member/2`.
|
||||||
%
|
%
|
||||||
% Some Prolog implementations also provide ord_member/2, with the
|
% Some Prolog implementations also provide `ord_member/2`, with the
|
||||||
% same semantics as ord_memberchk/2. We believe that having a
|
% same semantics as `ord_memberchk/2`. We believe that having a
|
||||||
% semidet ord_member/2 is unacceptably inconsistent with the *_chk
|
% semidet `ord_member/2` is unacceptably inconsistent with the \*\_chk
|
||||||
% convention. Portable code should use ord_memberchk/2 or
|
% convention. Portable code should use `ord_memberchk/2` or
|
||||||
% member/2.
|
% `member/2`.
|
||||||
%
|
%
|
||||||
% @author Richard O'Keefe
|
% Author: Richard O'Keefe
|
||||||
|
|
||||||
ord_memberchk(Item, [X1,X2,X3,X4|Xs]) :-
|
ord_memberchk(Item, [X1,X2,X3,X4|Xs]) :-
|
||||||
!,
|
!,
|
||||||
@@ -303,7 +296,7 @@ ord_memberchk(Item, [X1]) :-
|
|||||||
Item == X1.
|
Item == X1.
|
||||||
|
|
||||||
|
|
||||||
%! ord_subset(+Sub, +Super) is semidet.
|
%% ord_subset(+Sub, +Super) is semidet.
|
||||||
%
|
%
|
||||||
% Is true if all elements of Sub are in Super
|
% Is true if all elements of Sub are in Super
|
||||||
|
|
||||||
@@ -319,7 +312,7 @@ ord_subset_(=, _, T1, T2) :-
|
|||||||
ord_subset(T1, T2).
|
ord_subset(T1, T2).
|
||||||
|
|
||||||
|
|
||||||
%! ord_subtract(+InOSet, +NotInOSet, -Diff) is det.
|
%% ord_subtract(+InOSet, +NotInOSet, -Diff) is det.
|
||||||
%
|
%
|
||||||
% Diff is the set holding all elements of InOSet that are not in
|
% Diff is the set holding all elements of InOSet that are not in
|
||||||
% NotInOSet.
|
% NotInOSet.
|
||||||
@@ -328,13 +321,11 @@ ord_subtract(InOSet, NotInOSet, Diff) :-
|
|||||||
oset_diff(InOSet, NotInOSet, Diff).
|
oset_diff(InOSet, NotInOSet, Diff).
|
||||||
|
|
||||||
|
|
||||||
%! ord_union(+SetOfSets, -Union) is det.
|
%% ord_union(+SetOfSets, -Union) is det.
|
||||||
%
|
%
|
||||||
% True if Union is the union of all elements in the superset
|
% True if Union is the union of all elements in the superset
|
||||||
% SetOfSets. Each member of SetOfSets must be an ordered set, the
|
% SetOfSets. Each member of SetOfSets must be an ordered set, the
|
||||||
% sets need not be ordered in any way.
|
% sets need not be ordered in any way.
|
||||||
%
|
|
||||||
% @author Copied from YAP, probably originally by Richard O'Keefe.
|
|
||||||
|
|
||||||
ord_union([], []).
|
ord_union([], []).
|
||||||
ord_union([Set|Sets], Union) :-
|
ord_union([Set|Sets], Union) :-
|
||||||
@@ -355,7 +346,7 @@ ord_union_all(N, Sets0, Union, Sets) :-
|
|||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
%! ord_union(+Set1, +Set2, ?Union) is det.
|
%% ord_union(+Set1, +Set2, ?Union) is det.
|
||||||
%
|
%
|
||||||
% Union is the union of Set1 and Set2
|
% Union is the union of Set1 and Set2
|
||||||
|
|
||||||
@@ -363,10 +354,10 @@ ord_union(Set1, Set2, Union) :-
|
|||||||
oset_union(Set1, Set2, Union).
|
oset_union(Set1, Set2, Union).
|
||||||
|
|
||||||
|
|
||||||
%! ord_union(+Set1, +Set2, -Union, -New) is det.
|
%% ord_union(+Set1, +Set2, -Union, -New) is det.
|
||||||
%
|
%
|
||||||
% True iff ord_union(Set1, Set2, Union) and
|
% True iff `ord_union(Set1, Set2, Union)` and
|
||||||
% ord_subtract(Set2, Set1, New).
|
% `ord_subtract(Set2, Set1, New)`.
|
||||||
|
|
||||||
ord_union([], Set2, Set2, Set2).
|
ord_union([], Set2, Set2, Set2).
|
||||||
ord_union([H|T], Set2, Union, New) :-
|
ord_union([H|T], Set2, Union, New) :-
|
||||||
@@ -390,7 +381,7 @@ ord_union_2([H|T], H2, T2, Union, New) :-
|
|||||||
ord_union(Order, H, T, H2, T2, Union, New).
|
ord_union(Order, H, T, H2, T2, Union, New).
|
||||||
|
|
||||||
|
|
||||||
%! ord_symdiff(+Set1, +Set2, ?Difference) is det.
|
%% ord_symdiff(+Set1, +Set2, ?Difference) is det.
|
||||||
%
|
%
|
||||||
% Is true when Difference is the symmetric difference of Set1 and
|
% Is true when Difference is the symmetric difference of Set1 and
|
||||||
% Set2. I.e., Difference contains all elements that are not in the
|
% Set2. I.e., Difference contains all elements that are not in the
|
||||||
@@ -398,18 +389,18 @@ ord_union_2([H|T], H2, T2, Union, New) :-
|
|||||||
% sequence below (but the actual implementation requires only a
|
% sequence below (but the actual implementation requires only a
|
||||||
% single scan).
|
% single scan).
|
||||||
%
|
%
|
||||||
% ==
|
% ```
|
||||||
% ord_union(Set1, Set2, Union),
|
% ord_union(Set1, Set2, Union),
|
||||||
% ord_intersection(Set1, Set2, Intersection),
|
% ord_intersection(Set1, Set2, Intersection),
|
||||||
% ord_subtract(Union, Intersection, Difference).
|
% ord_subtract(Union, Intersection, Difference).
|
||||||
% ==
|
% ```
|
||||||
%
|
%
|
||||||
% For example:
|
% For example:
|
||||||
%
|
%
|
||||||
% ==
|
% ```
|
||||||
% ?- ord_symdiff([1,2], [2,3], X).
|
% ?- ord_symdiff([1,2], [2,3], X).
|
||||||
% X = [1,3].
|
% X = [1,3].
|
||||||
% ==
|
% ```
|
||||||
|
|
||||||
ord_symdiff([], Set2, Set2).
|
ord_symdiff([], Set2, Set2).
|
||||||
ord_symdiff([H1|T1], Set2, Difference) :-
|
ord_symdiff([H1|T1], Set2, Difference) :-
|
||||||
@@ -457,7 +448,7 @@ ord_symdiff(>, H1, T1, H2, Set2, [H2|Difference]) :-
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/** <module> Ordered set manipulation
|
/* Ordered set manipulation
|
||||||
|
|
||||||
This library defines set operations on sets represented as ordered
|
This library defines set operations on sets represented as ordered
|
||||||
lists.
|
lists.
|
||||||
145
src/lib/os.pl
Normal file
145
src/lib/os.pl
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Predicates for reasoning about the operating system (OS) environment.
|
||||||
|
Written July 2020 by Markus Triska (triska@metalevel.at).
|
||||||
|
|
||||||
|
Lists of characters are used throughout to represent keys and values.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
?- getenv("LANG", Ls).
|
||||||
|
Ls = "en_US.UTF-8".
|
||||||
|
|
||||||
|
Public domain code.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
/** Predicates for reasoning about the operating system (OS) environment.
|
||||||
|
|
||||||
|
This includes predicates about environment variables, calls to shell and
|
||||||
|
finding out the PID of the running system.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(os, [getenv/2,
|
||||||
|
setenv/2,
|
||||||
|
unsetenv/1,
|
||||||
|
shell/1,
|
||||||
|
shell/2,
|
||||||
|
pid/1,
|
||||||
|
raw_argv/1,
|
||||||
|
argv/1]).
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(charsio)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(si)).
|
||||||
|
|
||||||
|
%% getenv(+Key, -Value).
|
||||||
|
%
|
||||||
|
% True iff Value contains the value of the environment variable Key.
|
||||||
|
% Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- getenv("LANG", Ls).
|
||||||
|
% Ls = "en_US.UTF-8".
|
||||||
|
% ```
|
||||||
|
getenv(Key, Value) :-
|
||||||
|
must_be_env_var(Key),
|
||||||
|
'$getenv'(Key, Value).
|
||||||
|
|
||||||
|
%% setenv(+Key, +Value).
|
||||||
|
%
|
||||||
|
% Sets the environment variable Key to Value
|
||||||
|
setenv(Key, Value) :-
|
||||||
|
must_be_env_var(Key),
|
||||||
|
must_be_chars(Value),
|
||||||
|
'$setenv'(Key, Value).
|
||||||
|
|
||||||
|
%% unsetenv(+Key).
|
||||||
|
%
|
||||||
|
% Unsets the environment variable Key
|
||||||
|
unsetenv(Key) :-
|
||||||
|
must_be_env_var(Key),
|
||||||
|
'$unsetenv'(Key).
|
||||||
|
|
||||||
|
%% shell(+Command)
|
||||||
|
%
|
||||||
|
% Equivalent to `shell(Command, 0)`.
|
||||||
|
shell(Command) :- shell(Command, 0).
|
||||||
|
|
||||||
|
%% shell(+Command, -Status).
|
||||||
|
%
|
||||||
|
% True iff executes Command in a shell of the operating system and the exit code is Status.
|
||||||
|
% Keep in mind the shell syntax is dependant on the operating system, so it should be
|
||||||
|
% used very carefully.
|
||||||
|
%
|
||||||
|
% Example (using Linux and fish shell):
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% ?- shell("echo $SHELL", Status).
|
||||||
|
% /bin/fish
|
||||||
|
% Status = 0.
|
||||||
|
% ```
|
||||||
|
shell(Command, Status) :-
|
||||||
|
must_be_chars(Command),
|
||||||
|
can_be(integer, Status),
|
||||||
|
'$shell'(Command, Status).
|
||||||
|
|
||||||
|
%% pid(-PID).
|
||||||
|
%
|
||||||
|
% True iff PID is the process identification number of current Scryer Prolog instance.
|
||||||
|
pid(PID) :-
|
||||||
|
can_be(integer, PID),
|
||||||
|
'$pid'(PID).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
For now, we only support a restricted subset of variable names.
|
||||||
|
|
||||||
|
The reason is that Rust may panic if a key is empty, contains an
|
||||||
|
ASCII equals sign '=' or the NUL character '\0', or when the value
|
||||||
|
contains the NUL character.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
must_be_env_var(Cs) :-
|
||||||
|
must_be_chars(Cs),
|
||||||
|
Cs = [_|_],
|
||||||
|
( maplist(permitted, Cs) -> true
|
||||||
|
; domain_error(env_var, Cs, os)
|
||||||
|
).
|
||||||
|
|
||||||
|
permitted(C) :- char_type(C, alnum).
|
||||||
|
permitted(C) :- char_type(C, ascii_punctuation).
|
||||||
|
permitted('_').
|
||||||
|
|
||||||
|
must_be_chars(Cs) :-
|
||||||
|
must_be(list, Cs),
|
||||||
|
maplist(must_be(character), Cs).
|
||||||
|
|
||||||
|
%% raw_argv(-Argv)
|
||||||
|
%
|
||||||
|
% True iff Argv is the list of arguments that this program was started with (usually passed via command line).
|
||||||
|
% In contrast to `argv/1`, this version includes every argument, without any postprocessing, just as the operating
|
||||||
|
% system reports it to the system. This includes-flags of Scryer itself, which are not needed in general.
|
||||||
|
raw_argv(Argv) :-
|
||||||
|
can_be(list, Argv),
|
||||||
|
'$argv'(Argv).
|
||||||
|
|
||||||
|
%% argv(-Argv)
|
||||||
|
%
|
||||||
|
% True if Argv is the list of arguments that this program was started with (usually passed via command line).
|
||||||
|
% In this version, only arguments specific to the program are passed. To differentiate between the system
|
||||||
|
% arguments and the program arguments, we use `--` as a separator.
|
||||||
|
%
|
||||||
|
% Example:
|
||||||
|
%
|
||||||
|
% ```
|
||||||
|
% % Call with scryer-prolog -f -- -t hello
|
||||||
|
% ?- argv(X).
|
||||||
|
% X = ["-t", "hello"].
|
||||||
|
% ```
|
||||||
|
argv(Argv) :-
|
||||||
|
can_be(list, Argv),
|
||||||
|
'$argv'(Argv0),
|
||||||
|
( append(_, ["--"|Argv1], Argv0) ->
|
||||||
|
Argv = Argv1
|
||||||
|
;
|
||||||
|
Argv = []
|
||||||
|
).
|
||||||
55
src/lib/pairs.pl
Normal file
55
src/lib/pairs.pl
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/** Reasoning about pairs.
|
||||||
|
|
||||||
|
Pairs are Prolog terms with principal functor `(-)/2`. A pair
|
||||||
|
often has the form `Key-Value`. The predicates of this library
|
||||||
|
relate pairs to keys and values.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(pairs, [pairs_keys_values/3,
|
||||||
|
pairs_keys/2,
|
||||||
|
pairs_values/2,
|
||||||
|
group_pairs_by_key/2,
|
||||||
|
map_list_to_pairs/3]).
|
||||||
|
|
||||||
|
|
||||||
|
:- meta_predicate(map_list_to_pairs(2, ?, ?)).
|
||||||
|
|
||||||
|
%% pairs_keys_values(?Pairs, ?Keys, ?Values)
|
||||||
|
%
|
||||||
|
% The first argument is a list of Pairs, the second the corresponding
|
||||||
|
% Keys, and the third argument the corresponding values.
|
||||||
|
|
||||||
|
pairs_keys_values([], [], []).
|
||||||
|
pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :-
|
||||||
|
pairs_keys_values(ABs, As, Bs).
|
||||||
|
|
||||||
|
%% pairs_keys(?Pairs, ?Keys)
|
||||||
|
%
|
||||||
|
% Same as `pairs_keys_values(Pairs, Keys, _)`.
|
||||||
|
|
||||||
|
pairs_keys(Ps, Ks) :- pairs_keys_values(Ps, Ks, _).
|
||||||
|
|
||||||
|
%% pairs_values(?Pairs, ?Values)
|
||||||
|
%
|
||||||
|
% Same as `pairs_keys_values(Pairs, _, Values)`.
|
||||||
|
|
||||||
|
pairs_values(Ps, Vs) :- pairs_keys_values(Ps, _, Vs).
|
||||||
|
|
||||||
|
map_list_to_pairs(Pred, Ls, Ps) :-
|
||||||
|
map_list_to_pairs2(Ls, Pred, Ps).
|
||||||
|
|
||||||
|
map_list_to_pairs2([], _, []).
|
||||||
|
map_list_to_pairs2([H|T0], Pred, [K-H|T]) :-
|
||||||
|
call(Pred, H, K),
|
||||||
|
map_list_to_pairs2(T0, Pred, T).
|
||||||
|
|
||||||
|
|
||||||
|
group_pairs_by_key([], []).
|
||||||
|
group_pairs_by_key([K-V|KVs0], [K-[V|Vs]|KVs]) :-
|
||||||
|
same_key(K, KVs0, Vs, KVs1),
|
||||||
|
group_pairs_by_key(KVs1, KVs).
|
||||||
|
|
||||||
|
same_key(K0, [K1-V|KVs0], [V|Vs], KVs) :-
|
||||||
|
K0 == K1, !,
|
||||||
|
same_key(K0, KVs0, Vs, KVs).
|
||||||
|
same_key(_, KVs, [], KVs).
|
||||||
237
src/lib/pio.pl
Normal file
237
src/lib/pio.pl
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
/** Pure I/O.
|
||||||
|
|
||||||
|
Our goal is to encourage the use of definite clause grammars (DCGs)
|
||||||
|
for describing strings. The predicates `phrase_from_file/[2,3]`,
|
||||||
|
`phrase_to_file/[2,3]` and `phrase_to_stream/2` let us apply DCGs
|
||||||
|
transparently to files and streams, and therefore decouple side-effects
|
||||||
|
from declarative descriptions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:- module(pio, [phrase_from_file/2,
|
||||||
|
phrase_from_file/3,
|
||||||
|
phrase_from_stream/2,
|
||||||
|
phrase_to_file/2,
|
||||||
|
phrase_to_file/3,
|
||||||
|
phrase_to_stream/2
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(freeze)).
|
||||||
|
:- use_module(library(gensym)).
|
||||||
|
:- use_module(library(iso_ext), [
|
||||||
|
bb_get/2, bb_put/2, setup_call_cleanup/3, partial_string/3, partial_string_tail/2
|
||||||
|
]).
|
||||||
|
:- use_module(library(lists), [append/3, length/2, member/2, maplist/2]).
|
||||||
|
:- use_module(library(charsio), [get_n_chars/3]).
|
||||||
|
|
||||||
|
:- meta_predicate(phrase_from_file(2, ?)).
|
||||||
|
:- meta_predicate(phrase_from_file(2, ?, ?)).
|
||||||
|
:- meta_predicate(phrase_from_stream(2, ?)).
|
||||||
|
:- meta_predicate(phrase_to_file(2, ?)).
|
||||||
|
:- meta_predicate(phrase_to_file(2, ?, ?)).
|
||||||
|
:- meta_predicate(phrase_to_stream(2, ?)).
|
||||||
|
|
||||||
|
|
||||||
|
%% phrase_from_stream(+GRBody, +Stream)
|
||||||
|
%
|
||||||
|
% True if grammar rule body GRBody covers the contents of the stream,
|
||||||
|
% represented as a list of characters.
|
||||||
|
|
||||||
|
phrase_from_stream(GRBody, Stream) :-
|
||||||
|
stream_property(Stream, reposition(Reposition)),
|
||||||
|
stream_to_lazy_list(Reposition, Stream, Ls),
|
||||||
|
phrase(GRBody, Ls).
|
||||||
|
|
||||||
|
%% phrase_from_file(+GRBody, +File)
|
||||||
|
%
|
||||||
|
% True if grammar rule body GRBody covers the contents of File,
|
||||||
|
% represented as a list of characters.
|
||||||
|
|
||||||
|
phrase_from_file(NT, File) :-
|
||||||
|
phrase_from_file(NT, File, []).
|
||||||
|
|
||||||
|
%% phrase_from_file(+GRBody, +File, +Options)
|
||||||
|
%
|
||||||
|
% Like `phrase_from_file/2`, using Options to open the file.
|
||||||
|
|
||||||
|
phrase_from_file(NT, File, Options) :-
|
||||||
|
( var(File) -> instantiation_error(phrase_from_file/3)
|
||||||
|
; must_be(list, Options),
|
||||||
|
( member(Var, Options), var(Var) -> instantiation_error(phrase_from_file/3)
|
||||||
|
; member(type(Type), Options) ->
|
||||||
|
must_be(atom, Type),
|
||||||
|
member(Type, [text,binary])
|
||||||
|
; Type = text
|
||||||
|
),
|
||||||
|
setup_call_cleanup(
|
||||||
|
open(File, read, Stream, [reposition(true)|Options]),
|
||||||
|
phrase_from_stream(NT, Stream),
|
||||||
|
close(Stream)
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
|
% How many chars to read from stream and buffer in each step
|
||||||
|
chars_to_read(4096).
|
||||||
|
|
||||||
|
stream_to_lazy_list(Reposition, Stream, Ls) :-
|
||||||
|
get_stream_buffer_position(Reposition, Stream, Pos),
|
||||||
|
freeze(Ls, render_step(Reposition, Stream, Pos, Ls)).
|
||||||
|
|
||||||
|
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(Reposition, Stream, CharsToRead, Chars),
|
||||||
|
partial_string(Chars, Ls, Ls0),
|
||||||
|
stream_to_lazy_list(Reposition, Stream, Ls0)
|
||||||
|
).
|
||||||
|
|
||||||
|
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(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(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(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),
|
||||||
|
bb_get(BufferPosId, BufferPos),
|
||||||
|
( BufferPos = eof ->
|
||||||
|
Chars = []
|
||||||
|
; string_get_n_chars(Buffer, BufferPos, N, Chars),
|
||||||
|
length(Chars, NChars),
|
||||||
|
( NChars = 0 ->
|
||||||
|
BufferPos1 = eof
|
||||||
|
; BufferPos1 is BufferPos + NChars
|
||||||
|
),
|
||||||
|
bb_put(BufferPosId, BufferPos1)
|
||||||
|
).
|
||||||
|
|
||||||
|
buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N) :-
|
||||||
|
bb_get(BufferPosId, BufferPos),
|
||||||
|
bb_get(BufferLenId, BufferLen),
|
||||||
|
( BufferLen < BufferPos + N ->
|
||||||
|
bb_get(BufferId, Buffer),
|
||||||
|
(
|
||||||
|
( var(Buffer) ->
|
||||||
|
BufferTail = Buffer
|
||||||
|
; partial_string_last_tail(Buffer, BufferTail)
|
||||||
|
) ->
|
||||||
|
( at_end_of_stream(Stream) ->
|
||||||
|
BufferTail = [],
|
||||||
|
bb_put(BufferId, Buffer)
|
||||||
|
; chars_to_read(CharsToRead),
|
||||||
|
get_n_chars(Stream, CharsToRead, Chars),
|
||||||
|
length(Chars, NChars),
|
||||||
|
partial_string(Chars, BufferTail, _),
|
||||||
|
bb_put(BufferId, Buffer),
|
||||||
|
BufferLen1 is BufferLen + NChars,
|
||||||
|
bb_put(BufferLenId, BufferLen1),
|
||||||
|
buffer_prepare_for_n(Stream, BufferId, BufferPosId, BufferLenId, N)
|
||||||
|
)
|
||||||
|
; true
|
||||||
|
)
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
partial_string_last_tail(PartialString, PartialStringTail) :-
|
||||||
|
partial_string_tail(PartialString, PartialStringTail0),
|
||||||
|
( var(PartialStringTail0) ->
|
||||||
|
PartialStringTail = PartialStringTail0
|
||||||
|
; partial_string_last_tail(PartialStringTail0, PartialStringTail)
|
||||||
|
).
|
||||||
|
|
||||||
|
string_get_n_chars(String, Pos, N, Chars) :-
|
||||||
|
'$skip_max_list'(_, Pos, String, String1),
|
||||||
|
'$skip_max_list'(N1, N, String1, _),
|
||||||
|
length(Chars, N1),
|
||||||
|
append(Chars, _, String1).
|
||||||
|
|
||||||
|
stream_bufferids(Stream, BufferId, BufferPosId, BufferLenId) :-
|
||||||
|
( bb_get(streams_buffers, _) ->
|
||||||
|
true
|
||||||
|
; bb_put(streams_buffers, [])
|
||||||
|
),
|
||||||
|
bb_get(streams_buffers, StreamsBuffers),
|
||||||
|
( member(
|
||||||
|
stream_buffer(Stream, BufferId, BufferPosId, BufferLenId),
|
||||||
|
StreamsBuffers
|
||||||
|
) ->
|
||||||
|
true
|
||||||
|
; gensym(buffer, BufferId),
|
||||||
|
gensym(buffer_pos, BufferPosId),
|
||||||
|
gensym(buffer_len, BufferLenId),
|
||||||
|
bb_put(
|
||||||
|
streams_buffers,
|
||||||
|
[stream_buffer(Stream, BufferId, BufferPosId, BufferLenId)|StreamsBuffers]
|
||||||
|
),
|
||||||
|
bb_put(BufferId, _),
|
||||||
|
bb_put(BufferPosId, 0),
|
||||||
|
bb_put(BufferLenId, 0)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% phrase_to_stream(+GRBody, +Stream)
|
||||||
|
%
|
||||||
|
% Emit the list of characters described by the grammar rule body
|
||||||
|
% GRBody to Stream.
|
||||||
|
%
|
||||||
|
% An ideal implementation of `phrase_to_stream/2` writes each
|
||||||
|
% character as soon as it becomes known and no choice-points remain,
|
||||||
|
% and thus avoids the manifestation of the entire string in memory.
|
||||||
|
% See [#691](https://github.com/mthom/scryer-prolog/issues/691) for
|
||||||
|
% more information.
|
||||||
|
%
|
||||||
|
% The current preliminary implementation is provided so that Prolog
|
||||||
|
% programmers can already get used to describing output with DCGs,
|
||||||
|
% and then writing it to a file when necessary. This simple
|
||||||
|
% implementation suffices as long as the entire contents can be
|
||||||
|
% represented in memory, and thus covers a large number of use cases.
|
||||||
|
|
||||||
|
phrase_to_stream(GRBody, Stream) :-
|
||||||
|
phrase(GRBody, Cs),
|
||||||
|
must_be(chars, Cs),
|
||||||
|
( stream_property(Stream, type(binary)) ->
|
||||||
|
( '$first_non_octet'(Cs, N) ->
|
||||||
|
domain_error(octet_character, N, phrase_to_stream/2)
|
||||||
|
; true
|
||||||
|
)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
% we use a specialised internal predicate that uses only a
|
||||||
|
% single "write" operation for efficiency. It is equivalent to
|
||||||
|
% maplist(put_char(Stream), Cs). It also works for binary streams.
|
||||||
|
'$put_chars'(Stream, Cs).
|
||||||
|
|
||||||
|
%% phrase_to_file(+GRBody, +File)
|
||||||
|
%
|
||||||
|
% Write the string described by GRBody to File.
|
||||||
|
|
||||||
|
phrase_to_file(GRBody, File) :-
|
||||||
|
phrase_to_file(GRBody, File, []).
|
||||||
|
|
||||||
|
|
||||||
|
%% phrase_to_file(+GRBody, +File, +Options)
|
||||||
|
%
|
||||||
|
% Like `phrase_to_file/2`, using Options to open the file.
|
||||||
|
|
||||||
|
phrase_to_file(GRBody, File, Options) :-
|
||||||
|
setup_call_cleanup(open(File, write, Stream, Options),
|
||||||
|
phrase_to_stream(GRBody, Stream),
|
||||||
|
close(Stream)).
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user