Compare commits
914 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d19243b3b | ||
|
|
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 | ||
|
|
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 | ||
|
|
cc9d6587a3 |
40
.github/workflows/test.yml
vendored
Normal file
40
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: Test
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, macos-10.15]
|
||||
rust-version: [stable, beta]
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust-version }}
|
||||
override: true
|
||||
- name: Build lib
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: rustc
|
||||
args: --verbose --lib -- -D warnings
|
||||
- name: Build bin
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: rustc
|
||||
args: --verbose --bin scryer-prolog -- -D warnings
|
||||
- name: Test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --all
|
||||
- name: Num tests
|
||||
uses: actions-rs/cargo@v1
|
||||
continue-on-error: true
|
||||
with:
|
||||
command: test
|
||||
args: --verbose --all --no-default-features --features num
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
src/static_atoms.rs
|
||||
target/
|
||||
|
||||
|
||||
|
||||
|
||||
31
.travis.yml
31
.travis.yml
@@ -1,31 +0,0 @@
|
||||
language: rust
|
||||
cache: cargo
|
||||
os: linux
|
||||
dist: xenial
|
||||
|
||||
before_script:
|
||||
- cargo fetch
|
||||
|
||||
jobs:
|
||||
allow_failures:
|
||||
env:
|
||||
- CAN_FAIL=true
|
||||
include:
|
||||
- stage: "Stable: Build"
|
||||
rust: stable
|
||||
script: cargo rustc --verbose -- -D warnings
|
||||
name: "Build Stable"
|
||||
- stage: "Stable: Tests"
|
||||
rust: stable
|
||||
script: cargo test --verbose --all
|
||||
name: "Tests Stable"
|
||||
- stage: "Features"
|
||||
rust: stable
|
||||
script: cargo test --verbose --all --no-default-features --features num
|
||||
name: "num Tests"
|
||||
env: CAN_FAIL=true
|
||||
- stage: "Beta: Build"
|
||||
# - #
|
||||
rust: beta
|
||||
script: cargo rustc --verbose -- -D warnings
|
||||
name: "Build Beta"
|
||||
1604
Cargo.lock
generated
1604
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
62
Cargo.toml
62
Cargo.toml
@@ -1,43 +1,75 @@
|
||||
[package]
|
||||
name = "scryer-prolog"
|
||||
version = "0.8.123"
|
||||
version = "0.9.0"
|
||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
description = "A modern Prolog implementation written mostly in Rust."
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/mthom/scryer-prolog"
|
||||
license = "BSD-3-Clause"
|
||||
keywords = ["prolog", "prolog-interpreter", "prolog-implementation"]
|
||||
keywords = ["prolog", "prolog-interpreter", "prolog-system"]
|
||||
categories = ["command-line-utilities"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
indexmap = "1.0.2"
|
||||
[workspace]
|
||||
members = ["crates/num-rug-adapter",
|
||||
"crates/static-string-indexing",
|
||||
"crates/instructions-template",
|
||||
"crates/to-syn-value",
|
||||
"crates/to-syn-value_derive"]
|
||||
|
||||
[features]
|
||||
default = ["rug", "prolog_parser/rug"]
|
||||
num = ["num-rug-adapter", "prolog_parser/num"]
|
||||
num = ["num-rug-adapter"]
|
||||
# no default features to make num tests work
|
||||
# workaround for --no-default-features and --features not working intuitively for workspaces with a root package
|
||||
# see rust-lang/cargo#7160
|
||||
default = ["rug"]
|
||||
|
||||
[build-dependencies]
|
||||
indexmap = "1.0.2"
|
||||
static-string-indexing = { path = "./crates/static-string-indexing" }
|
||||
instructions-template = { path = "./crates/instructions-template" }
|
||||
proc-macro2 = "*"
|
||||
|
||||
[dependencies]
|
||||
cpu-time = "1.0.0"
|
||||
crossterm = "0.16.0"
|
||||
dirs = "2.0.2"
|
||||
dirs-next = "2.0.0"
|
||||
divrem = "0.1.0"
|
||||
downcast = "0.10.0"
|
||||
fxhash = "0.2.1"
|
||||
git-version = "0.3.4"
|
||||
hostname = "0.3.1"
|
||||
indexmap = "1.0.2"
|
||||
lazy_static = "1.4.0"
|
||||
lexical = "5.2.2"
|
||||
libc = "0.2.62"
|
||||
modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" } # modular-bitfield = "0.11.2"
|
||||
nix = "0.15.0"
|
||||
num-rug-adapter = { optional = true, version = "0.1.3" }
|
||||
ordered-float = "0.5.0"
|
||||
prolog_parser = { version = "0.8.59", default-features = false }
|
||||
num-rug-adapter = { optional = true, path = "./crates/num-rug-adapter" }
|
||||
ordered-float = "2.1.1"
|
||||
phf = { version = "0.9", features = ["macros"] }
|
||||
ref_thread_local = "0.0.0"
|
||||
rug = { version = "1.4.0", optional = true }
|
||||
rustyline = "6.0.0"
|
||||
unicode_reader = "1.0.0"
|
||||
rug = { version = "1.12.0", optional = true }
|
||||
rustyline = "9.0.0"
|
||||
ring = "0.16.13"
|
||||
ripemd160 = "0.8.0"
|
||||
sha3 = "0.8.2"
|
||||
blake2 = "0.8.1"
|
||||
openssl = { version = "0.10.29", features = ["vendored"] }
|
||||
native-tls = "0.2.4"
|
||||
chrono = "0.4.11"
|
||||
select = "0.4.3"
|
||||
roxmltree = "0.11.0"
|
||||
base64 = "0.12.3"
|
||||
smallvec = "*"
|
||||
sodiumoxide = "0.2.6"
|
||||
static_assertions = "1.1.0"
|
||||
slice-deque = "0.3.0"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "1.0.3"
|
||||
predicates-core = "1.0.2"
|
||||
serial_test = "0.5.1"
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
48
Dockerfile
48
Dockerfile
@@ -1,30 +1,26 @@
|
||||
# Based on https://hub.docker.com/_/rust?tab=description and https://blog.sedrik.se/posts/my-docker-setup-for-rust/
|
||||
|
||||
# The first container is for build purposes only.
|
||||
FROM rust as builder
|
||||
|
||||
WORKDIR /usr/src/scryer-prolog
|
||||
|
||||
# Using a dummy build.rs and src/main.rs with your Cargo.toml lets Docker cache your Rust dependencies and not rebuild
|
||||
# them every time.
|
||||
COPY Cargo.toml .
|
||||
COPY Cargo.lock .
|
||||
RUN mkdir -p src
|
||||
RUN echo "fn main() {}" > src/main.rs
|
||||
RUN echo "fn main() {}" > build.rs
|
||||
RUN cargo build --release
|
||||
|
||||
# We need to touch our real main.rs and build.rs files or else
|
||||
# docker will use the cached ones.
|
||||
# See https://github.com/LukeMathWalker/cargo-chef
|
||||
ARG RUST_VERSION=1.52.1-buster
|
||||
FROM rust:${RUST_VERSION} as planner
|
||||
WORKDIR /scryer-prolog
|
||||
RUN cargo install cargo-chef
|
||||
COPY . .
|
||||
RUN touch src/main.rs
|
||||
RUN touch build.rs
|
||||
RUN cargo chef prepare --recipe-path recipe.json
|
||||
|
||||
RUN cargo build --release
|
||||
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
|
||||
|
||||
RUN ls ./target/release
|
||||
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
|
||||
|
||||
# Finally, copy the scryer-prolog executable to a slimmer container.
|
||||
FROM debian:buster-slim
|
||||
COPY --from=builder /usr/src/scryer-prolog/target/release/scryer-prolog /usr/local/bin/scryer-prolog
|
||||
CMD ["scryer-prolog"]
|
||||
FROM debian:stable-slim
|
||||
COPY --from=builder /scryer-prolog/target/release/scryer-prolog /usr/local/bin
|
||||
ENV RUST_BACKTRACE=1
|
||||
ENTRYPOINT ["/usr/local/bin/scryer-prolog"]
|
||||
|
||||
298
README.md
298
README.md
@@ -5,6 +5,8 @@ source industrial strength production environment that is also a
|
||||
testbed for bleeding edge research in logic and constraint
|
||||
programming, which is itself written in a high-level language.
|
||||
|
||||

|
||||
|
||||
## Phase 1
|
||||
|
||||
Produce an implementation of the Warren Abstract Machine in Rust, done
|
||||
@@ -57,10 +59,16 @@ Extend Scryer Prolog to include the following, among other features:
|
||||
- [x] clp(B) and clp(ℤ) as builtin libraries.
|
||||
- [x] Streams and predicates for stream control.
|
||||
- [x] A simple sockets library representing TCP connections as streams.
|
||||
- [ ] Incremental compilation and loading process, newly written,
|
||||
primarily in Prolog. (_in progress_)
|
||||
- [ ] A compacting garbage collector satisfying the five
|
||||
properties of "Precise Garbage Collection in Prolog."
|
||||
- [x] Incremental compilation and loading process, newly written,
|
||||
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.
|
||||
- [ ] Greatly reducing the number of instructions used to compile disjunctives.
|
||||
- [ ] Storing short atoms to heap cells without writing them to the atom table.
|
||||
- [ ] A compacting garbage collector satisfying the five properties of
|
||||
"Precise Garbage Collection in Prolog." (_in progress_)
|
||||
- [ ] Mode declarations.
|
||||
|
||||
## Phase 3
|
||||
@@ -128,6 +136,8 @@ $> cargo run [--release]
|
||||
The optional `--release` flag will perform various optimizations,
|
||||
producing a faster executable.
|
||||
|
||||
Scryer Prolog must be built with **Rust 1.57 and up**.
|
||||
|
||||
### Docker Install (All Platforms)
|
||||
|
||||
First, install [Docker](https://docs.docker.com/get-docker/) on Linux,
|
||||
@@ -201,9 +211,10 @@ predicates it defines. For example, with the program shown above:
|
||||
; 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 `h` to show a help message.
|
||||
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 the next 5 answers, and
|
||||
`a` to see all answers. Press `h` to show a help message.
|
||||
|
||||
To quit Scryer Prolog, use the standard predicate `halt/0`:
|
||||
|
||||
@@ -224,6 +235,36 @@ arithmetic operators with the usual precedences,
|
||||
|
||||
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
|
||||
|
||||
In Scryer Prolog, the default value of the Prolog flag `double_quotes`
|
||||
@@ -269,8 +310,60 @@ the above example, posting <tt>Ls0 = [a,b,c|Ls]</tt> yields
|
||||
the exact same internal representation, and has the advantage that
|
||||
only the standard predicate `(=)/2` is used.
|
||||
|
||||
Definite clause grammars as provided by `library(dcgs)` are ideally
|
||||
suited for reasoning about strings.
|
||||
Definite clause grammars as provided by
|
||||
[`library(dcgs)`](src/lib/dcgs.pl), and the predicates from
|
||||
[`library(lists)`](src/lib/lists.pl), are ideally suited for reasoning
|
||||
about strings.
|
||||
|
||||
Partial strings were first proposed by Ulrich Neumerkel in issue
|
||||
[#95](https://github.com/mthom/scryer-prolog/issues/95).
|
||||
|
||||
### 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, performing 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)
|
||||
|
||||
@@ -287,7 +380,7 @@ 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/prolog/lib/tabling.pl) and add a `(table)/1`
|
||||
[`library(tabling)`](src/lib/tabling.pl) and add a `(table)/1`
|
||||
directive for the desired predicate indicator. For example, if we
|
||||
write:
|
||||
|
||||
@@ -312,14 +405,14 @@ 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/prolog/lib/dif.pl),
|
||||
[`freeze/2`](src/prolog/lib/freeze.pl),
|
||||
[CLP(B)](src/prolog/lib/clpb.pl) and [CLP(ℤ)](src/prolog/lib/clpz.pl),
|
||||
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/prolog/lib/atts.pl) as in SICStus Prolog,
|
||||
[`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/prolog/lib/iso_ext.pl),
|
||||
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.
|
||||
@@ -332,7 +425,7 @@ learning and developing portable CLP applications.
|
||||
Scryer has a simple predicate-based module system. It provides a
|
||||
way to separate units of code into distinct namespaces, for both
|
||||
predicates and operators. See the files
|
||||
[`src/prolog/lib/*.pl`](src/prolog/lib) for
|
||||
[`src/lib/*.pl`](src/lib) for
|
||||
examples.
|
||||
|
||||
At the time of this writing, many predicates reside in their own
|
||||
@@ -340,31 +433,31 @@ modules that need to be imported before they can be used.
|
||||
The modules that ship with Scryer Prolog are also called
|
||||
*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]`,
|
||||
`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
|
||||
that uses the operator `(-->)/2` to define grammar rules,
|
||||
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:
|
||||
It is true if and only if its arguments are different, and
|
||||
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
|
||||
as described in *Indexing dif/2*.
|
||||
* [`clpz`](src/prolog/lib/clpz.pl)
|
||||
* [`clpz`](src/lib/clpz.pl)
|
||||
CLP(ℤ): Constraint Logic Programming over Integers,
|
||||
providing declarative integer arithmetic via `(#=)/2`, `(#\=)/2`,
|
||||
`(#>=)/2` etc., and various global constraints and
|
||||
enumeration predicates for solving combinatorial tasks.
|
||||
* [`pairs`](src/prolog/lib/pairs.pl)
|
||||
* [`pairs`](src/lib/pairs.pl)
|
||||
By convention, *pairs* are Prolog terms with
|
||||
principal functor `(-)/2`, written as `Key-Value`.
|
||||
This library provides `pairs_keys_values/3`,
|
||||
`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`
|
||||
and `list_si/1` implement sound type checks. They raise
|
||||
instantiation errors if no decision can be made.
|
||||
@@ -373,51 +466,102 @@ The modules that ship with Scryer Prolog are also called
|
||||
write `integer_si(X)` to ensure soundness of your programs.
|
||||
"si" stands for *sufficiently instantiated*, and also for
|
||||
*sound inference*.
|
||||
* [`error`](src/prolog/lib/error.pl)
|
||||
`must_be/2` and `can_be/2` complement the type checks provided
|
||||
by `library(si)`, and are especially useful for Prolog library
|
||||
authors.
|
||||
* [`tabling`](src/prolog/lib/tabling.pl)
|
||||
* [`debug`](src/lib/debug.pl)
|
||||
Various predicates that allow for declarative debugging.
|
||||
* [`pio`](src/lib/pio.pl)
|
||||
`phrase_from_file/2` applies a DCG nonterminal to the contents of a
|
||||
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
|
||||
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,
|
||||
arranging arguments according to a given format string.
|
||||
The predicates `format/[2,3]`, `portray_clause/1` and `listing/1`
|
||||
The predicates `format/[2,3]`, `portray_clause/[1,2]` and `listing/1`
|
||||
provide formatted *impure* output.
|
||||
* [`assoc`](src/prolog/lib/assoc.pl)
|
||||
* [`assoc`](src/lib/assoc.pl)
|
||||
providing `empty_assoc/1`, `get_assoc/3`, `put_assoc/4` etc.
|
||||
to manage elements in AVL trees which ensure
|
||||
*O*(log(*N*)) access.
|
||||
* [`ordsets`](src/prolog/lib/ordsets.pl)
|
||||
* [`ordsets`](src/lib/ordsets.pl)
|
||||
represents ordered sets as lists.
|
||||
* [`clpb`](src/prolog/lib/clpb.pl)
|
||||
* [`clpb`](src/lib/clpb.pl)
|
||||
CLP(B): Constraint Logic Programming over Boolean variables,
|
||||
a BDD-based SAT solver provided via the predicates
|
||||
`sat/1`, `taut/2`, `labeling/1` etc.
|
||||
* [`arithmetic`](src/prolog/lib/arithmetic.pl)
|
||||
* [`arithmetic`](src/lib/arithmetic.pl)
|
||||
Arithmetic predicates such as `lsb/2`, `msb/2` and
|
||||
`number_to_rational/2`.
|
||||
* [`time`](src/prolog/lib/time.pl)
|
||||
`time/1` reports the CPU time of a goal. It is useful
|
||||
for measuring the performance of your code.
|
||||
* [`cont`](src/prolog/lib/cont.pl)
|
||||
* [`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/prolog/lib/random.pl)
|
||||
* [`random`](src/lib/random.pl)
|
||||
Probabilistic predicates and random number generators.
|
||||
* [`sockets`](src/prolog/lib/sockets.pl)
|
||||
* [`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.0 web server.
|
||||
* [`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.
|
||||
* [`crypto`](src/prolog/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,
|
||||
authenticated encryption, and reasoning about elliptic curves.
|
||||
|
||||
To read contents of external files, use `phrase_from_file/2` from
|
||||
[`library(pio)`](src/prolog/lib/pio.pl) to apply a DCG to
|
||||
file contents. The predicates in
|
||||
[`library(charsio)`](src/prolog/lib/charsio.pl) are also useful for
|
||||
parsing.
|
||||
* [`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.
|
||||
* [`uuid`](src/lib/uuid.pl) UUIDv4 generation and hex representation
|
||||
* [`tls`](src/lib/tls.pl)
|
||||
Predicates for negotiating TLS connections explicitly.
|
||||
* [`ugraphs`](src/lib/ugraphs.pl) Graph manipulation library
|
||||
|
||||
To use predicates provided by the `lists` library, write:
|
||||
|
||||
@@ -462,3 +606,55 @@ local_member(X, Xs) :- member(X, Xs).
|
||||
|
||||
The user listing can also be terminated by placing `end_of_file.` at
|
||||
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.
|
||||
|
||||
## 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)!
|
||||
|
||||
108
build.rs
108
build.rs
@@ -1,60 +1,88 @@
|
||||
extern crate indexmap;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
use static_string_indexing::index_static_strings;
|
||||
use instructions_template::generate_instructions_rs;
|
||||
|
||||
use std::env;
|
||||
use std::fs::{File, copy, read_dir};
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
fn main()
|
||||
{
|
||||
fn find_prolog_files(libraries: &mut File, prefix: &str, current_dir: &Path) {
|
||||
let entries = match current_dir.read_dir() {
|
||||
Ok(entries) => entries,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
for entry in entries.filter_map(Result::ok).map(|e| e.path()) {
|
||||
if entry.is_dir() {
|
||||
if let Some(file_name) = entry.file_name() {
|
||||
let new_prefix = prefix.to_owned() + file_name.to_str().unwrap() + "/";
|
||||
find_prolog_files(libraries, &new_prefix, &entry);
|
||||
}
|
||||
} else if entry.is_file() {
|
||||
let ext = std::ffi::OsStr::new("pl");
|
||||
if entry.extension() == Some(ext) {
|
||||
let contain = String::from_utf8(fs::read(&entry).unwrap()).unwrap();
|
||||
let name = entry.file_stem().unwrap().to_str().unwrap();
|
||||
|
||||
let line = format!(
|
||||
" m.insert(\"{}\",\n{:?});\n",
|
||||
prefix.to_owned() + name,
|
||||
contain
|
||||
);
|
||||
|
||||
libraries.write_all(line.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 lib_path = Path::new("src/lib");
|
||||
|
||||
let paths = read_dir("./src/prolog/lib").unwrap();
|
||||
libraries
|
||||
.write_all(
|
||||
b"ref_thread_local::ref_thread_local! {
|
||||
pub(crate) static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
|
||||
let mut m = IndexMap::new();\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for item in paths {
|
||||
let item = item.unwrap().path();
|
||||
find_prolog_files(&mut libraries, "", &lib_path);
|
||||
libraries.write_all(b"\n m\n };\n}\n").unwrap();
|
||||
|
||||
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);
|
||||
let instructions_path = Path::new(&out_dir).join("instructions.rs");
|
||||
let mut instructions_file = File::create(&instructions_path).unwrap();
|
||||
|
||||
match copy(&item, dest) {
|
||||
Ok(_) => {},
|
||||
Err(e) => panic!("die: {:?}", e)
|
||||
};
|
||||
let quoted_output = generate_instructions_rs();
|
||||
|
||||
let include_line = format!("static {}: &str = include_str!(\"{}.pl\");\n",
|
||||
file_str, file_stem.to_string_lossy());
|
||||
instructions_file
|
||||
.write_all(quoted_output.to_string().as_bytes())
|
||||
.unwrap();
|
||||
|
||||
libraries.write_all(include_line.as_bytes()).unwrap();
|
||||
library_index.insert(file_stem.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::new("rustfmt")
|
||||
.arg(instructions_path.as_os_str())
|
||||
.spawn().unwrap()
|
||||
.wait().unwrap();
|
||||
|
||||
libraries.write_all(b"\nref_thread_local! {
|
||||
pub static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
|
||||
let mut m = IndexMap::new();\n").unwrap();
|
||||
let static_atoms_path = Path::new(&out_dir).join("static_atoms.rs");
|
||||
let mut static_atoms_file = File::create(&static_atoms_path).unwrap();
|
||||
|
||||
for item in library_index {
|
||||
let line = format!("\n m.insert(\"{}\", {});", item, item.to_uppercase());
|
||||
libraries.write_all(line.as_bytes()).unwrap();
|
||||
}
|
||||
let quoted_output = index_static_strings(&instructions_path);
|
||||
|
||||
libraries.write_all(b"\n\n m\n };
|
||||
}\n").unwrap();
|
||||
static_atoms_file
|
||||
.write_all(quoted_output.to_string().as_bytes())
|
||||
.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();
|
||||
Command::new("rustfmt")
|
||||
.arg(static_atoms_path.as_os_str())
|
||||
.spawn().unwrap()
|
||||
.wait().unwrap();
|
||||
|
||||
println!("cargo:rerun-if-changed=src/");
|
||||
}
|
||||
|
||||
129
crates/instructions-template/Cargo.lock
generated
Normal file
129
crates/instructions-template/Cargo.lock
generated
Normal file
@@ -0,0 +1,129 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instructions-template"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "392a54546fda6b7cc663379d0e6ce8b324cf88aecc5a499838e1be9781bdce2e"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "to-syn-value"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"syn",
|
||||
"to-syn-value_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "to-syn-value_derive"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
14
crates/instructions-template/Cargo.toml
Normal file
14
crates/instructions-template/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "instructions-template"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
indexmap = "*"
|
||||
proc-macro2 = "*"
|
||||
quote = "*"
|
||||
strum = "0.23"
|
||||
strum_macros = "0.23"
|
||||
syn = { version = "*", features = ['full', 'visit', 'extra-traits'] }
|
||||
to-syn-value = { path = "../to-syn-value" }
|
||||
to-syn-value_derive = { path = "../to-syn-value_derive" }
|
||||
3290
crates/instructions-template/src/lib.rs
Normal file
3290
crates/instructions-template/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
18
crates/num-rug-adapter/Cargo.toml
Normal file
18
crates/num-rug-adapter/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "num-rug-adapter"
|
||||
version = "0.1.5"
|
||||
authors = ["Marco A L Barbosa <malbarbo@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "An adapter to use num crate where rug is needed."
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/malbarbo/num-rug-adapter"
|
||||
keywords = ["mathematics", "numerics", "bignum"]
|
||||
categories = ["api-bindings", "science"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
num-bigint = "0.2"
|
||||
num-integer = "0.1.41"
|
||||
num-rational = "0.2"
|
||||
num-traits = "0.2"
|
||||
888
crates/num-rug-adapter/src/lib.rs
Normal file
888
crates/num-rug-adapter/src/lib.rs
Normal file
@@ -0,0 +1,888 @@
|
||||
use num_bigint::{BigInt, ParseBigIntError};
|
||||
use num_integer::Integer as _;
|
||||
use num_rational::BigRational;
|
||||
use num_traits::{FromPrimitive, Num, Signed, ToPrimitive};
|
||||
use num_traits::identities::One;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct Integer(BigInt);
|
||||
|
||||
impl Integer {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Integer(BigInt::default())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_str_radix(s: &str, radix: u32) -> Result<Self, ParseBigIntError> {
|
||||
BigInt::from_str_radix(s, radix).map(Integer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_u8(&self) -> Option<u8> {
|
||||
self.0.to_u8()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_u32(&self) -> Option<u32> {
|
||||
self.0.to_u32()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_u64(&self) -> Option<u64> {
|
||||
self.0.to_u64()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_usize(&self) -> Option<usize> {
|
||||
self.0.to_usize()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_i32(&self) -> Option<i32> {
|
||||
self.0.to_i32()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_isize(&self) -> Option<isize> {
|
||||
self.0.to_isize()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> f64 {
|
||||
self.0.to_f64().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn abs(&self) -> Self {
|
||||
Integer(self.0.abs())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn abs_ref(&self) -> Self {
|
||||
Integer(self.0.abs())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn div_rem(&self, other: Self) -> (Self, Self) {
|
||||
let (a, b) = num_integer::Integer::div_rem(&self.0, &other.0);
|
||||
(Integer(a), Integer(b))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn div_rem_ref(&self, other: &Self) -> (Self, Self) {
|
||||
let (a, b) = num_integer::Integer::div_rem(&self.0, &other.0);
|
||||
(Integer(a), Integer(b))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn div_rem_floor(&self, other: Self) -> (Self, Self) {
|
||||
let (a, b) = num_integer::Integer::div_mod_floor(&self.0, &other.0);
|
||||
(Integer(a), Integer(b))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn div_rem_floor_ref(&self, other: &Self) -> (Self, Self) {
|
||||
let (a, b) = num_integer::Integer::div_mod_floor(&self.0, &other.0);
|
||||
(Integer(a), Integer(b))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mod_u(&self, modulo: u32) -> u32 {
|
||||
(self.0.abs() % modulo).to_u32().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_odd(&self) -> bool {
|
||||
num_integer::Integer::is_odd(&self.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_f64(v: f64) -> Option<Self> {
|
||||
BigInt::from_f64(v).map(Integer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn gcd_ref(&self, other: &Self) -> Self {
|
||||
Integer(num_integer::Integer::gcd(&self.0, &other.0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn gcd(&self, other: &Self) -> Self {
|
||||
Integer(num_integer::Integer::gcd(&self.0, &other.0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn count_ones(&self) -> Option<u32> {
|
||||
Some(self.0.to_u32_digits().1.iter().map(|&d| d.count_ones()).sum())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Integer> for Integer {
|
||||
#[inline]
|
||||
fn from(s: &Integer) -> Self {
|
||||
s.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Integer {
|
||||
#[inline]
|
||||
fn from(s: i32) -> Self {
|
||||
Integer(BigInt::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<isize> for Integer {
|
||||
#[inline]
|
||||
fn from(s: isize) -> Self {
|
||||
Integer(BigInt::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Integer {
|
||||
#[inline]
|
||||
fn from(s: u8) -> Self {
|
||||
Integer(BigInt::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Integer {
|
||||
#[inline]
|
||||
fn from(s: u32) -> Self {
|
||||
Integer(BigInt::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Integer {
|
||||
#[inline]
|
||||
fn from(s: u64) -> Self {
|
||||
Integer(BigInt::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Integer {
|
||||
#[inline]
|
||||
fn from(s: usize) -> Self {
|
||||
Integer(BigInt::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: Integer) -> Self::Output {
|
||||
Integer(self.0 * other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<u32> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: u32) -> Self::Output {
|
||||
Integer(self.0 * other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<&Integer> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
fn mul(self, other: &Integer) -> Self::Output {
|
||||
Integer(self.0 * &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<&Integer> for Integer {
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: &Integer) {
|
||||
self.0 *= &other.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Integer) -> Self::Output {
|
||||
Integer(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Integer> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Integer) -> Self::Output {
|
||||
Integer(&self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Integer> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: &Integer) -> Self::Output {
|
||||
Integer(self.0 + &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Integer> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: &Integer) -> Self::Output {
|
||||
Integer(&self.0 + &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<i64> for Integer {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: i64) {
|
||||
self.0 += other;
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&Integer> for Integer {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: &Integer) {
|
||||
self.0 += &other.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Integer) -> Integer {
|
||||
Integer(self.0 / other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<&Integer> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: &Integer) -> Integer {
|
||||
Integer(self.0 / &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<Integer> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Integer) -> Integer {
|
||||
Integer(&self.0 / &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr<u32> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn shr(self, rhs: u32) -> Self::Output {
|
||||
Integer(self.0 >> rhs as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr<u32> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn shr(self, rhs: u32) -> Self::Output {
|
||||
Integer(&self.0 >> rhs as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl ShrAssign<u32> for Integer {
|
||||
#[inline]
|
||||
fn shr_assign(&mut self, rhs: u32) {
|
||||
self.0 >>= rhs as usize;
|
||||
}
|
||||
}
|
||||
|
||||
impl Shl<u32> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn shl(self, rhs: u32) -> Self::Output {
|
||||
Integer(self.0 << rhs as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Shl<u32> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn shl(self, rhs: u32) -> Self::Output {
|
||||
Integer(&self.0 << rhs as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn not(self) -> Self::Output {
|
||||
Integer(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn not(self) -> Self::Output {
|
||||
Integer(!&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn rem(self, other: Integer) -> Self::Output {
|
||||
Integer(self.0.mod_floor(&other.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem<&Integer> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn rem(self, other: &Integer) -> Self::Output {
|
||||
Integer(self.0.mod_floor(&other.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem<&Integer> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn rem(self, other: &Integer) -> Self::Output {
|
||||
Integer(self.0.mod_floor(&other.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem<Integer> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn rem(self, other: Integer) -> Self::Output {
|
||||
Integer(self.0.mod_floor(&other.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitand(self, other: Integer) -> Self::Output {
|
||||
Integer(self.0 & &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd<&Integer> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitand(self, other: &Integer) -> Self::Output {
|
||||
Integer(self.0 & &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd<Integer> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitand(self, other: Integer) -> Self::Output {
|
||||
Integer(&self.0 & other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitand(self, other: &Integer) -> Self::Output {
|
||||
Integer(&self.0 & &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, other: Integer) -> Self::Output {
|
||||
Integer(self.0 | other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr<&Integer> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, other: &Integer) -> Self::Output {
|
||||
Integer(self.0 | &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr<Integer> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, other: Integer) -> Self::Output {
|
||||
Integer(&self.0 | other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitor(self, other: &Integer) -> Self::Output {
|
||||
Integer(&self.0 | &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitxor(self, other: Integer) -> Self::Output {
|
||||
Integer(self.0 ^ other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor<&Integer> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitxor(self, other: &Integer) -> Self::Output {
|
||||
Integer(self.0 ^ &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor<Integer> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitxor(self, other: Integer) -> Self::Output {
|
||||
Integer(&self.0 ^ other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor<&Integer> for &Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn bitxor(self, other: &Integer) -> Self::Output {
|
||||
Integer(&self.0 ^ &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<i32> for Integer {
|
||||
#[inline]
|
||||
fn eq(&self, other: &i32) -> bool {
|
||||
self.0 == BigInt::from(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<i64> for Integer {
|
||||
#[inline]
|
||||
fn eq(&self, other: &i64) -> bool {
|
||||
self.0 == BigInt::from(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<isize> for Integer {
|
||||
#[inline]
|
||||
fn eq(&self, other: &isize) -> bool {
|
||||
self.0 == BigInt::from(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<usize> for Integer {
|
||||
#[inline]
|
||||
fn eq(&self, other: &usize) -> bool {
|
||||
self.0 == BigInt::from(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Integer> for isize {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Integer) -> bool {
|
||||
other.0 == BigInt::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<i32> for Integer {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &i32) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&BigInt::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<i64> for Integer {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &i64) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&BigInt::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<isize> for Integer {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &isize) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&BigInt::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<usize> for Integer {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &usize) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&BigInt::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Integer {
|
||||
type Err = <BigInt as FromStr>::Err;
|
||||
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Integer(s.parse()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
Integer(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Integer {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Rational
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct Rational(BigRational);
|
||||
|
||||
impl Rational {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Rational(BigRational::from(BigInt::default()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_f64(v: f64) -> Option<Self> {
|
||||
BigRational::from_f64(v).map(Rational)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_f64(&self) -> f64 {
|
||||
self.0.numer().to_f64().unwrap() / self.0.denom().to_f64().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn numer(&self) -> &Integer {
|
||||
unsafe { ::std::mem::transmute(self.0.numer()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn denom(&self) -> &Integer {
|
||||
unsafe { ::std::mem::transmute(self.0.denom()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn abs(self) -> Self {
|
||||
Rational(self.0.abs())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn abs_ref(&self) -> Self {
|
||||
Rational(self.0.abs())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fract_floor_ref(&self) -> &Self {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<isize> for Rational {
|
||||
#[inline]
|
||||
fn from(s: isize) -> Self {
|
||||
Rational(BigRational::new_raw(BigInt::from(s), One::one()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Integer> for Rational {
|
||||
#[inline]
|
||||
fn from(s: &Integer) -> Self {
|
||||
Rational::from(s.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Rational> for Rational {
|
||||
#[inline]
|
||||
fn from(s: &Rational) -> Self {
|
||||
s.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Integer> for Rational {
|
||||
#[inline]
|
||||
fn from(i: Integer) -> Self {
|
||||
Rational(BigRational::from(i.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Rational {
|
||||
type Output = Rational;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Rational) -> Self::Output {
|
||||
Rational(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&Rational> for Rational {
|
||||
type Output = Rational;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: &Rational) -> Self::Output {
|
||||
Rational(self.0 + &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<i32> for Rational {
|
||||
#[inline]
|
||||
fn eq(&self, other: &i32) -> bool {
|
||||
self.0 == BigRational::from(BigInt::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<i64> for Rational {
|
||||
#[inline]
|
||||
fn eq(&self, other: &i64) -> bool {
|
||||
self.0 == BigRational::from(BigInt::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<isize> for Rational {
|
||||
#[inline]
|
||||
fn eq(&self, other: &isize) -> bool {
|
||||
self == &Rational::from(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Rational> for isize {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Rational) -> bool {
|
||||
other == &Rational::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<isize> for Rational {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &isize) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&BigRational::from(BigInt::from(*other)))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<i64> for Rational {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &i64) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&BigRational::from(BigInt::from(*other)))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<i32> for Rational {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &i32) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&BigRational::from(BigInt::from(*other)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Rational {
|
||||
type Output = Rational;
|
||||
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
Rational(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Rational {
|
||||
type Output = Rational;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, other: Rational) -> Self::Output {
|
||||
Rational(self.0 * other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<&Rational> for Rational {
|
||||
type Output = Rational;
|
||||
|
||||
fn mul(self, other: &Rational) -> Self::Output {
|
||||
Rational(self.0 * &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for Rational {
|
||||
type Output = Rational;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Rational) -> Self::Output {
|
||||
Rational(self.0 / other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<&Rational> for &Rational {
|
||||
type Output = Rational;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: &Rational) -> Self::Output {
|
||||
Rational(&self.0 / &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Rational {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Assign<Src = Self> {
|
||||
fn assign(&mut self, src: Src);
|
||||
}
|
||||
|
||||
impl Assign<&Rational> for (&mut Rational, &mut Integer) {
|
||||
fn assign(&mut self, _src: &Rational) {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ops {
|
||||
use super::{Integer, Rational};
|
||||
|
||||
pub trait Pow<Rhs> {
|
||||
type Output;
|
||||
fn pow(self, rhs: Rhs) -> Self::Output;
|
||||
}
|
||||
|
||||
impl Pow<u32> for Integer {
|
||||
type Output = Integer;
|
||||
|
||||
fn pow(self, rhs: u32) -> Self::Output {
|
||||
Integer(num_traits::Pow::pow(&self.0, rhs))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PowAssign<Rhs> {
|
||||
fn pow_assign(&mut self, rhs: Rhs);
|
||||
}
|
||||
|
||||
impl PowAssign<u32> for Integer {
|
||||
fn pow_assign(&mut self, rhs: u32) {
|
||||
// FIXME: make it efficient
|
||||
self.0 = num_traits::Pow::pow(&self.0, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NegAssign {
|
||||
fn neg_assign(&mut self);
|
||||
}
|
||||
|
||||
impl NegAssign for Integer {
|
||||
fn neg_assign(&mut self) {
|
||||
self.0 = -std::mem::replace(self, Integer::new()).0;
|
||||
}
|
||||
}
|
||||
|
||||
impl NegAssign for Rational {
|
||||
#[inline]
|
||||
fn neg_assign(&mut self) {
|
||||
self.0 = -std::mem::replace(self, Rational::new()).0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod rand {
|
||||
use super::Integer;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct RandState<'a>{
|
||||
_marker: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> RandState<'a> {
|
||||
pub fn new() -> Self {
|
||||
unsafe { libc::srand(libc::time(std::ptr::null_mut()) as _) };
|
||||
RandState { _marker: PhantomData }
|
||||
}
|
||||
|
||||
pub fn borrow_mut(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bits(&mut self, bits: u32) -> u32 {
|
||||
assert!(bits <= 32);
|
||||
(unsafe { libc::rand() } as u32) & (u32::max_value() >> (32 - bits))
|
||||
}
|
||||
|
||||
pub fn seed(&mut self, seed: &Integer) {
|
||||
unsafe { libc::srand(seed.to_f64() as _)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::ops::NegAssign;
|
||||
|
||||
#[test]
|
||||
fn bits() {
|
||||
let mut rand = rand::RandState::new();
|
||||
for bits in 1..32 {
|
||||
for _ in 0..100 {
|
||||
let r = rand.bits(bits);
|
||||
let max = 1 << bits;
|
||||
assert!(max > r, "{} > {}", max, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn neg_rational() {
|
||||
let mut x = Rational::from_f64(5.0).unwrap();
|
||||
let x_neg = Rational::from_f64(-5.0).unwrap();
|
||||
x.neg_assign();
|
||||
assert_eq!(x, x_neg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn neg_integer() {
|
||||
let mut x = Integer::from(5);
|
||||
let x_neg = Integer::from(-5);
|
||||
x.neg_assign();
|
||||
assert_eq!(x, x_neg);
|
||||
}
|
||||
}
|
||||
11
crates/static-string-indexing/Cargo.toml
Normal file
11
crates/static-string-indexing/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "static-string-indexing"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "*"
|
||||
syn = { version = "*", features = ['full', 'visit', 'extra-traits'] }
|
||||
indexmap = "*"
|
||||
walkdir = "2"
|
||||
quote = "*"
|
||||
177
crates/static-string-indexing/src/lib.rs
Normal file
177
crates/static-string-indexing/src/lib.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::*;
|
||||
use syn::parse::*;
|
||||
use syn::visit::*;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
struct StaticStrVisitor {
|
||||
static_strs: IndexSet<String>,
|
||||
}
|
||||
|
||||
impl StaticStrVisitor {
|
||||
fn new() -> Self {
|
||||
Self { static_strs: IndexSet::new() }
|
||||
}
|
||||
}
|
||||
|
||||
struct MacroFnArgs {
|
||||
args: Vec<Expr>,
|
||||
}
|
||||
|
||||
struct ReadHeapCellExprAndArms {
|
||||
expr: Expr,
|
||||
arms: Vec<Arm>,
|
||||
}
|
||||
|
||||
impl Parse for ReadHeapCellExprAndArms {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut arms = vec![];
|
||||
let expr = input.parse()?;
|
||||
|
||||
input.parse::<Token![,]>()?;
|
||||
arms.push(input.parse()?);
|
||||
|
||||
while !input.is_empty() {
|
||||
if let Ok(_) = input.parse::<Token![,]>() {}
|
||||
arms.push(input.parse()?);
|
||||
}
|
||||
|
||||
Ok(ReadHeapCellExprAndArms { expr, arms })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for MacroFnArgs {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut args = vec![];
|
||||
|
||||
if !input.is_empty() {
|
||||
args.push(input.parse()?);
|
||||
}
|
||||
|
||||
while !input.is_empty() {
|
||||
if let Ok(_) = input.parse::<Token![,]>() {}
|
||||
args.push(input.parse()?);
|
||||
}
|
||||
|
||||
Ok(MacroFnArgs { args })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> Visit<'ast> for StaticStrVisitor {
|
||||
fn visit_macro(&mut self, m: &'ast Macro) {
|
||||
let Macro { path, .. } = m;
|
||||
|
||||
if path.is_ident("atom") {
|
||||
if let Some(Lit::Str(string)) = m.parse_body::<Lit>().ok() {
|
||||
self.static_strs.insert(string.value());
|
||||
}
|
||||
} else if path.is_ident("read_heap_cell") {
|
||||
if let Some(m) = m.parse_body::<ReadHeapCellExprAndArms>().ok() {
|
||||
self.visit_expr(&m.expr);
|
||||
|
||||
for e in m.arms {
|
||||
self.visit_arm(&e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(m) = m.parse_body::<MacroFnArgs>().ok() {
|
||||
for e in m.args {
|
||||
self.visit_expr(&e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index_static_strings(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) => {
|
||||
panic!("parse error: {} in file {:?}", e, path);
|
||||
}
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
match process_filepath(instruction_rs_path) {
|
||||
Ok(syntax) => visitor.visit_file(&syntax),
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
let indices = (0..visitor.static_strs.len()).map(|i| i << 3);
|
||||
let indices_iter = indices.clone();
|
||||
|
||||
let static_strs_len = visitor.static_strs.len();
|
||||
let static_strs: &Vec<_> = &visitor.static_strs.into_iter().collect();
|
||||
|
||||
quote! {
|
||||
use phf;
|
||||
|
||||
static STRINGS: [&'static str; #static_strs_len] = [
|
||||
#(
|
||||
#static_strs,
|
||||
)*
|
||||
];
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! atom {
|
||||
#((#static_strs) => { Atom { index: #indices_iter } };)*
|
||||
}
|
||||
|
||||
static STATIC_ATOMS_MAP: phf::Map<&'static str, Atom> = phf::phf_map! {
|
||||
#(#static_strs => { Atom { index: #indices } },)*
|
||||
};
|
||||
}
|
||||
}
|
||||
10
crates/to-syn-value/Cargo.toml
Normal file
10
crates/to-syn-value/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "to-syn-value"
|
||||
version = "0.1.0"
|
||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "*", features = ['full', 'visit', 'extra-traits'] }
|
||||
to-syn-value_derive = { path = "../to-syn-value_derive" }
|
||||
3
crates/to-syn-value/src/lib.rs
Normal file
3
crates/to-syn-value/src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub trait ToDeriveInput {
|
||||
fn to_derive_input() -> syn::DeriveInput;
|
||||
}
|
||||
14
crates/to-syn-value_derive/Cargo.toml
Normal file
14
crates/to-syn-value_derive/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "to-syn-value_derive"
|
||||
version = "0.1.0"
|
||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "*"
|
||||
syn = { version = "*", features = ['full', 'visit', 'extra-traits'] }
|
||||
quote = "*"
|
||||
20
crates/to-syn-value_derive/src/lib.rs
Normal file
20
crates/to-syn-value_derive/src/lib.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use syn::*;
|
||||
use quote::*;
|
||||
|
||||
#[proc_macro_derive(ToDeriveInput)]
|
||||
pub fn derive_to_derive_input(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let derive_input = parse_macro_input!(input as DeriveInput);
|
||||
let ty_name = derive_input.ident.clone();
|
||||
|
||||
quote! {
|
||||
use to_syn_value::*;
|
||||
|
||||
impl ToDeriveInput for #ty_name {
|
||||
fn to_derive_input() -> syn::DeriveInput {
|
||||
syn::parse_quote! {
|
||||
#derive_input
|
||||
}
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
}
|
||||
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 |
@@ -1,41 +1,57 @@
|
||||
use prolog_parser::ast::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::temp_v;
|
||||
|
||||
use crate::prolog::fixtures::*;
|
||||
use crate::prolog::forms::*;
|
||||
use crate::prolog::machine::machine_indices::*;
|
||||
use crate::prolog::targets::*;
|
||||
use crate::fixtures::*;
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::targets::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub trait Allocator<'a> {
|
||||
pub(crate) trait Allocator {
|
||||
fn new() -> Self;
|
||||
|
||||
fn mark_anon_var<Target>(&mut self, _: Level, _: GenContext, _: &mut Vec<Target>)
|
||||
where
|
||||
Target: CompilationTarget<'a>;
|
||||
fn mark_non_var<Target>(&mut self, _: Level, _: GenContext, _: &'a Cell<RegType>, _: &mut Vec<Target>)
|
||||
where
|
||||
Target: CompilationTarget<'a>;
|
||||
fn mark_reserved_var<Target>(
|
||||
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
_: Rc<Var>,
|
||||
_: Level,
|
||||
_: &'a Cell<VarReg>,
|
||||
_: GenContext,
|
||||
_: &mut Vec<Target>,
|
||||
_: RegType,
|
||||
_: bool,
|
||||
) where
|
||||
Target: CompilationTarget<'a>;
|
||||
fn mark_var<Target>(&mut self, _: Rc<Var>, _: Level, _: &'a Cell<VarReg>, _: GenContext, _: &mut Vec<Target>)
|
||||
where
|
||||
Target: CompilationTarget<'a>;
|
||||
lvl: Level,
|
||||
context: GenContext,
|
||||
code: &mut Code,
|
||||
);
|
||||
|
||||
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
lvl: Level,
|
||||
context: GenContext,
|
||||
cell: &'a Cell<RegType>,
|
||||
code: &mut Code,
|
||||
);
|
||||
|
||||
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_name: Rc<String>,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut Code,
|
||||
r: RegType,
|
||||
is_new_var: bool,
|
||||
);
|
||||
|
||||
fn mark_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_name: Rc<String>,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
context: GenContext,
|
||||
code: &mut Code,
|
||||
);
|
||||
|
||||
fn reset(&mut self);
|
||||
fn reset_contents(&mut self) {}
|
||||
fn reset_arg(&mut self, _: usize);
|
||||
fn reset_at_head(&mut self, _: &Vec<Box<Term>>);
|
||||
fn reset_arg(&mut self, arg_num: usize);
|
||||
fn reset_at_head(&mut self, args: &Vec<Term>);
|
||||
|
||||
fn advance_arg(&mut self);
|
||||
|
||||
@@ -44,10 +60,10 @@ pub trait Allocator<'a> {
|
||||
|
||||
fn take_bindings(self) -> AllocVarDict;
|
||||
|
||||
fn drain_var_data(
|
||||
fn drain_var_data<'a>(
|
||||
&mut self,
|
||||
vs: VariableFixtures<'a>,
|
||||
num_of_chunks: usize
|
||||
num_of_chunks: usize,
|
||||
) -> VariableFixtures<'a> {
|
||||
let mut perm_vs = VariableFixtures::new();
|
||||
|
||||
@@ -71,17 +87,17 @@ pub trait Allocator<'a> {
|
||||
perm_vs
|
||||
}
|
||||
|
||||
fn get(&self, var: Rc<Var>) -> RegType {
|
||||
fn get(&self, var: Rc<String>) -> RegType {
|
||||
self.bindings()
|
||||
.get(&var)
|
||||
.map_or(temp_v!(0), |v| v.as_reg_type())
|
||||
}
|
||||
|
||||
fn is_unbound(&self, var: Rc<Var>) -> bool {
|
||||
fn is_unbound(&self, var: Rc<String>) -> bool {
|
||||
self.get(var).reg_num() == 0
|
||||
}
|
||||
|
||||
fn record_register(&mut self, var: Rc<Var>, r: RegType) {
|
||||
fn record_register(&mut self, var: Rc<String>, r: RegType) {
|
||||
match self.bindings_mut().get_mut(&var).unwrap() {
|
||||
&mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(),
|
||||
&mut VarData::Perm(ref mut s) => *s = r.reg_num(),
|
||||
801
src/arena.rs
Normal file
801
src/arena.rs
Normal file
@@ -0,0 +1,801 @@
|
||||
use crate::machine::loader::LiveLoadState;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::machine::streams::*;
|
||||
use crate::read::*;
|
||||
|
||||
use modular_bitfield::prelude::*;
|
||||
use ordered_float::OrderedFloat;
|
||||
use rug::{Integer, Rational};
|
||||
|
||||
use std::alloc;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
use std::net::TcpListener;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ptr;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! arena_alloc {
|
||||
($e:expr, $arena:expr) => {{
|
||||
let result = $e;
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { $arena.alloc(result) }
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq)]
|
||||
#[bits = 7]
|
||||
pub enum ArenaHeaderTag {
|
||||
F64 = 0b01,
|
||||
Integer = 0b10,
|
||||
Rational = 0b11,
|
||||
OssifiedOpDir = 0b0000100,
|
||||
LiveLoadState = 0b0001000,
|
||||
InactiveLoadState = 0b1011000,
|
||||
InputFileStream = 0b10000,
|
||||
OutputFileStream = 0b10100,
|
||||
NamedTcpStream = 0b011100,
|
||||
NamedTlsStream = 0b100000,
|
||||
ReadlineStream = 0b110000,
|
||||
StaticStringStream = 0b110100,
|
||||
ByteStream = 0b111000,
|
||||
StandardOutputStream = 0b1100,
|
||||
StandardErrorStream = 0b11000,
|
||||
NullStream = 0b111100,
|
||||
TcpListener = 0b1000000,
|
||||
Dropped = 0b1000100,
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ArenaHeader {
|
||||
size: B56,
|
||||
m: bool,
|
||||
tag: ArenaHeaderTag,
|
||||
}
|
||||
|
||||
const_assert!(mem::size_of::<ArenaHeader>() == 8);
|
||||
|
||||
impl ArenaHeader {
|
||||
#[inline]
|
||||
pub fn build_with(size: u64, tag: ArenaHeaderTag) -> Self {
|
||||
ArenaHeader::new()
|
||||
.with_size(size)
|
||||
.with_tag(tag)
|
||||
.with_m(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_tag(self) -> ArenaHeaderTag {
|
||||
self.tag()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord)]
|
||||
pub struct TypedArenaPtr<T: ?Sized>(ptr::NonNull<T>);
|
||||
|
||||
impl<T: ?Sized + PartialEq> PartialEq for TypedArenaPtr<T> {
|
||||
fn eq(&self, other: &TypedArenaPtr<T>) -> bool {
|
||||
self.0 == other.0 || &**self == &**other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + PartialEq> Eq for TypedArenaPtr<T> {}
|
||||
|
||||
impl<T: ?Sized + Hash> Hash for TypedArenaPtr<T> {
|
||||
#[inline(always)]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
(&*self as &T).hash(hasher)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Clone for TypedArenaPtr<T> {
|
||||
fn clone(&self) -> Self {
|
||||
TypedArenaPtr(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Copy for TypedArenaPtr<T> {}
|
||||
|
||||
impl<T: ?Sized> Deref for TypedArenaPtr<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { self.0.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for TypedArenaPtr<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { self.0.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for TypedArenaPtr<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", **self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> TypedArenaPtr<T> {
|
||||
#[inline]
|
||||
pub const fn new(data: *mut T) -> Self {
|
||||
unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *mut T {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn header_ptr(&self) -> *const ArenaHeader {
|
||||
let mut ptr = self.as_ptr() as *const u8 as usize;
|
||||
ptr -= mem::size_of::<*const ArenaHeader>();
|
||||
ptr as *const ArenaHeader
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn header_ptr_mut(&mut self) -> *mut ArenaHeader {
|
||||
let mut ptr = self.as_ptr() as *const u8 as usize;
|
||||
ptr -= mem::size_of::<*const ArenaHeader>();
|
||||
ptr as *mut ArenaHeader
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mark_bit(&self) -> bool {
|
||||
unsafe { (*self.header_ptr()).m() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_tag(&mut self, tag: ArenaHeaderTag) {
|
||||
unsafe { (*self.header_ptr_mut()).set_tag(tag); }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_tag(&self) -> ArenaHeaderTag {
|
||||
unsafe { (*self.header_ptr()).get_tag() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mark(&mut self) {
|
||||
unsafe {
|
||||
(*self.header_ptr_mut()).set_m(true);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unmark(&mut self) {
|
||||
unsafe {
|
||||
(*self.header_ptr_mut()).set_m(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ArenaAllocated {
|
||||
type PtrToAllocated;
|
||||
|
||||
fn tag() -> ArenaHeaderTag;
|
||||
fn size(&self) -> usize;
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct F64Ptr(pub TypedArenaPtr<OrderedFloat<f64>>);
|
||||
|
||||
impl fmt::Display for F64Ptr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", *self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for F64Ptr {
|
||||
type Target = TypedArenaPtr<OrderedFloat<f64>>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for F64Ptr {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaAllocated for OrderedFloat<f64> {
|
||||
type PtrToAllocated = F64Ptr;
|
||||
|
||||
#[inline]
|
||||
fn tag() -> ArenaHeaderTag {
|
||||
ArenaHeaderTag::F64
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
F64Ptr(TypedArenaPtr::new(dst as *mut Self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaAllocated for Integer {
|
||||
type PtrToAllocated = TypedArenaPtr<Integer>;
|
||||
|
||||
#[inline]
|
||||
fn tag() -> ArenaHeaderTag {
|
||||
ArenaHeaderTag::Integer
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaAllocated for Rational {
|
||||
type PtrToAllocated = TypedArenaPtr<Rational>;
|
||||
|
||||
#[inline]
|
||||
fn tag() -> ArenaHeaderTag {
|
||||
ArenaHeaderTag::Rational
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaAllocated for OssifiedOpDir {
|
||||
type PtrToAllocated = TypedArenaPtr<OssifiedOpDir>;
|
||||
|
||||
#[inline]
|
||||
fn tag() -> ArenaHeaderTag {
|
||||
ArenaHeaderTag::OssifiedOpDir
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaAllocated for LiveLoadState {
|
||||
type PtrToAllocated = TypedArenaPtr<LiveLoadState>;
|
||||
|
||||
#[inline]
|
||||
fn tag() -> ArenaHeaderTag {
|
||||
ArenaHeaderTag::LiveLoadState
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaAllocated for TcpListener {
|
||||
type PtrToAllocated = TypedArenaPtr<TcpListener>;
|
||||
|
||||
#[inline]
|
||||
fn tag() -> ArenaHeaderTag {
|
||||
ArenaHeaderTag::TcpListener
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<Self>()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
|
||||
unsafe {
|
||||
ptr::write(dst, self);
|
||||
TypedArenaPtr::new(dst as *mut Self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct AllocSlab {
|
||||
next: *mut AllocSlab,
|
||||
header: ArenaHeader,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Arena(*mut AllocSlab);
|
||||
|
||||
unsafe impl Send for Arena {}
|
||||
unsafe impl Sync for Arena {}
|
||||
|
||||
impl Arena {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Arena(ptr::null_mut())
|
||||
}
|
||||
|
||||
pub unsafe fn alloc<T: ArenaAllocated>(&mut self, value: T) -> T::PtrToAllocated {
|
||||
let size = value.size() + mem::size_of::<AllocSlab>();
|
||||
|
||||
let align = mem::align_of::<AllocSlab>();
|
||||
let layout = alloc::Layout::from_size_align_unchecked(size, align);
|
||||
|
||||
let slab = alloc::alloc(layout) as *mut AllocSlab;
|
||||
|
||||
(*slab).next = self.0;
|
||||
(*slab).header = ArenaHeader::build_with(value.size() as u64, T::tag());
|
||||
|
||||
let offset = (*slab).payload_offset();
|
||||
let result = value.copy_to_arena(offset as *mut T);
|
||||
|
||||
self.0 = slab;
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn drop_slab_in_place(value: &mut AllocSlab) {
|
||||
use crate::parser::char_reader::CharReader;
|
||||
|
||||
match value.header.tag() {
|
||||
ArenaHeaderTag::Integer => {
|
||||
ptr::drop_in_place(value.payload_offset::<Integer>());
|
||||
}
|
||||
ArenaHeaderTag::Rational => {
|
||||
ptr::drop_in_place(value.payload_offset::<Rational>());
|
||||
}
|
||||
ArenaHeaderTag::InputFileStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<InputFileStream>>>());
|
||||
}
|
||||
ArenaHeaderTag::OutputFileStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<OutputFileStream>>());
|
||||
}
|
||||
ArenaHeaderTag::NamedTcpStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<NamedTcpStream>>>());
|
||||
}
|
||||
ArenaHeaderTag::NamedTlsStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<NamedTlsStream>>>());
|
||||
}
|
||||
ArenaHeaderTag::ReadlineStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<ReadlineStream>>());
|
||||
}
|
||||
ArenaHeaderTag::StaticStringStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<StaticStringStream>>());
|
||||
}
|
||||
ArenaHeaderTag::ByteStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<ByteStream>>>());
|
||||
}
|
||||
ArenaHeaderTag::OssifiedOpDir => {
|
||||
ptr::drop_in_place(value.payload_offset::<OssifiedOpDir>());
|
||||
}
|
||||
ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => {
|
||||
ptr::drop_in_place(value.payload_offset::<LiveLoadState>());
|
||||
}
|
||||
ArenaHeaderTag::Dropped => {
|
||||
}
|
||||
ArenaHeaderTag::TcpListener => {
|
||||
ptr::drop_in_place(value.payload_offset::<TcpListener>());
|
||||
}
|
||||
ArenaHeaderTag::StandardOutputStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<StandardOutputStream>>());
|
||||
}
|
||||
ArenaHeaderTag::StandardErrorStream => {
|
||||
ptr::drop_in_place(value.payload_offset::<StreamLayout<StandardErrorStream>>());
|
||||
}
|
||||
ArenaHeaderTag::F64 | ArenaHeaderTag::NullStream => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Arena {
|
||||
fn drop(&mut self) {
|
||||
let mut ptr = self.0;
|
||||
|
||||
while !ptr.is_null() {
|
||||
unsafe {
|
||||
let ptr_r = &*ptr;
|
||||
|
||||
let layout = alloc::Layout::from_size_align_unchecked(
|
||||
ptr_r.slab_size(),
|
||||
mem::align_of::<AllocSlab>(),
|
||||
);
|
||||
|
||||
drop_slab_in_place(&mut *ptr);
|
||||
|
||||
let next_ptr = ptr_r.next;
|
||||
alloc::dealloc(ptr as *mut u8, layout);
|
||||
ptr = next_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
self.0 = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
|
||||
const_assert!(mem::size_of::<AllocSlab>() == 16);
|
||||
|
||||
impl AllocSlab {
|
||||
#[inline]
|
||||
fn slab_size(&self) -> usize {
|
||||
self.header.size() as usize + mem::size_of::<AllocSlab>()
|
||||
}
|
||||
|
||||
fn payload_offset<T>(&self) -> *mut T {
|
||||
let mut ptr = (self as *const AllocSlab) as usize;
|
||||
ptr += mem::size_of::<AllocSlab>();
|
||||
ptr as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
const_assert!(mem::size_of::<OrderedFloat<f64>>() == 8);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::machine::mock_wam::*;
|
||||
use crate::machine::partial_string::*;
|
||||
|
||||
use ordered_float::OrderedFloat;
|
||||
use rug::{Integer, Rational};
|
||||
|
||||
#[test]
|
||||
fn float_ptr_cast() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
let f = OrderedFloat(0f64);
|
||||
let mut fp = arena_alloc!(f, &mut wam.machine_st.arena);
|
||||
let cell = HeapCellValue::from(fp);
|
||||
|
||||
assert_eq!(cell.get_tag(), HeapCellValueTag::F64);
|
||||
assert_eq!(fp.get_mark_bit(), false);
|
||||
assert_eq!(**fp, f);
|
||||
|
||||
fp.mark();
|
||||
|
||||
assert_eq!(fp.get_mark_bit(), true);
|
||||
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::F64, ptr) => {
|
||||
assert_eq!(**ptr, f)
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn heap_cell_value_const_cast() {
|
||||
let mut wam = MockWAM::new();
|
||||
let const_value = HeapCellValue::from(ConsPtr::build_with(
|
||||
0x0000_5555_ff00_0431 as *const _,
|
||||
ConsPtrMaskTag::Cons,
|
||||
));
|
||||
|
||||
match const_value.to_untyped_arena_ptr() {
|
||||
Some(arena_ptr) => {
|
||||
assert_eq!(arena_ptr.into_bytes(), const_value.into_bytes());
|
||||
}
|
||||
None => {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
let stream = Stream::from_static_string("test", &mut wam.machine_st.arena);
|
||||
let stream_cell =
|
||||
HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons));
|
||||
|
||||
match stream_cell.to_untyped_arena_ptr() {
|
||||
Some(arena_ptr) => {
|
||||
assert_eq!(arena_ptr.into_bytes(), stream_cell.into_bytes());
|
||||
}
|
||||
None => {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn heap_put_literal_tests() {
|
||||
let mut wam = MockWAM::new();
|
||||
|
||||
// integer
|
||||
|
||||
let big_int = 2 * Integer::from(1u64 << 63);
|
||||
let big_int_ptr: TypedArenaPtr<Integer> = arena_alloc!(big_int, &mut wam.machine_st.arena);
|
||||
|
||||
assert!(!big_int_ptr.as_ptr().is_null());
|
||||
|
||||
let cell = HeapCellValue::from(Literal::Integer(big_int_ptr));
|
||||
assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
|
||||
|
||||
let untyped_arena_ptr = match cell.to_untyped_arena_ptr() {
|
||||
Some(ptr) => ptr,
|
||||
None => {
|
||||
assert!(false);
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
match_untyped_arena_ptr!(untyped_arena_ptr,
|
||||
(ArenaHeaderTag::Integer, n) => {
|
||||
assert_eq!(&*n, &(2 * Integer::from(1u64 << 63)))
|
||||
}
|
||||
_ => unreachable!()
|
||||
);
|
||||
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::Cons, cons_ptr) => {
|
||||
match_untyped_arena_ptr!(cons_ptr,
|
||||
(ArenaHeaderTag::Integer, n) => {
|
||||
assert_eq!(&*n, &(2 * Integer::from(1u64 << 63)))
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
)
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
|
||||
// rational
|
||||
|
||||
let big_rat = 2 * Rational::from(1u64 << 63);
|
||||
let big_rat_ptr: TypedArenaPtr<Rational> = arena_alloc!(big_rat, &mut wam.machine_st.arena);
|
||||
|
||||
assert!(!big_rat_ptr.as_ptr().is_null());
|
||||
|
||||
let rat_cell = typed_arena_ptr_as_cell!(big_rat_ptr);
|
||||
assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
|
||||
|
||||
match rat_cell.to_untyped_arena_ptr() {
|
||||
Some(untyped_arena_ptr) => {
|
||||
assert_eq!(
|
||||
Some(big_rat_ptr.header_ptr()),
|
||||
Some(untyped_arena_ptr.into()),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
assert!(false); // we fail.
|
||||
}
|
||||
}
|
||||
|
||||
// assert_eq!(wam.machine_st.heap[1usize].get_tag(), HeapCellValueTag::Cons);
|
||||
|
||||
read_heap_cell!(rat_cell,
|
||||
(HeapCellValueTag::Cons, cons_ptr) => {
|
||||
match_untyped_arena_ptr!(cons_ptr,
|
||||
(ArenaHeaderTag::Rational, n) => {
|
||||
assert_eq!(&*n, &(2 * Rational::from(1u64 << 63)));
|
||||
}
|
||||
_ => unreachable!()
|
||||
)
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
|
||||
// atom
|
||||
|
||||
let f_atom = atom!("f");
|
||||
let g_atom = atom!("g");
|
||||
|
||||
assert_eq!(f_atom.as_str(), "f");
|
||||
assert_eq!(g_atom.as_str(), "g");
|
||||
|
||||
let f_atom_cell = atom_as_cell!(f_atom);
|
||||
let g_atom_cell = atom_as_cell!(g_atom);
|
||||
|
||||
assert_eq!(f_atom_cell.get_tag(), HeapCellValueTag::Atom);
|
||||
|
||||
match f_atom_cell.to_atom() {
|
||||
Some(atom) => {
|
||||
assert_eq!(f_atom, atom);
|
||||
assert_eq!(atom.as_str(), "f");
|
||||
}
|
||||
None => {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
read_heap_cell!(f_atom_cell,
|
||||
(HeapCellValueTag::Atom, (atom, arity)) => {
|
||||
assert_eq!(f_atom, atom);
|
||||
assert_eq!(arity, 0);
|
||||
assert_eq!(atom.as_str(), "f");
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
|
||||
read_heap_cell!(g_atom_cell,
|
||||
(HeapCellValueTag::Atom, (atom, arity)) => {
|
||||
assert_eq!(g_atom, atom);
|
||||
assert_eq!(arity, 0);
|
||||
assert_eq!(atom.as_str(), "g");
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
|
||||
// complete string
|
||||
|
||||
let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "ronan", &mut wam.machine_st.atom_tbl);
|
||||
let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
|
||||
|
||||
assert_eq!(pstr_cell.get_tag(), HeapCellValueTag::PStr);
|
||||
|
||||
match pstr_cell.to_pstr() {
|
||||
Some(pstr) => {
|
||||
assert_eq!(pstr.as_str_from(0), "ronan");
|
||||
}
|
||||
None => {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
read_heap_cell!(pstr_cell,
|
||||
(HeapCellValueTag::PStr, pstr_atom) => {
|
||||
let pstr = PartialString::from(pstr_atom);
|
||||
assert_eq!(pstr.as_str_from(0), "ronan");
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
|
||||
// fixnum
|
||||
|
||||
let fixnum_cell = fixnum_as_cell!(Fixnum::build_with(3));
|
||||
|
||||
assert_eq!(fixnum_cell.get_tag(), HeapCellValueTag::Fixnum);
|
||||
|
||||
match fixnum_cell.to_fixnum() {
|
||||
Some(n) => assert_eq!(n.get_num(), 3),
|
||||
None => assert!(false),
|
||||
}
|
||||
|
||||
read_heap_cell!(fixnum_cell,
|
||||
(HeapCellValueTag::Fixnum, n) => {
|
||||
assert_eq!(n.get_num(), 3);
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
|
||||
let fixnum_b_cell = fixnum_as_cell!(Fixnum::build_with(1 << 55));
|
||||
|
||||
assert_eq!(fixnum_b_cell.get_tag(), HeapCellValueTag::Fixnum);
|
||||
|
||||
match fixnum_b_cell.to_fixnum() {
|
||||
Some(n) => assert_eq!(n.get_num(), 1 << 55),
|
||||
None => assert!(false),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(1 << 57) {
|
||||
Ok(_) => assert!(false),
|
||||
_ => assert!(true),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(i64::MAX) {
|
||||
Ok(_) => assert!(false),
|
||||
_ => assert!(true),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(i64::MIN) {
|
||||
Ok(_) => assert!(false),
|
||||
_ => assert!(true),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(-1) {
|
||||
Ok(n) => assert_eq!(n.get_num(), -1),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked((1 << 56) - 1) {
|
||||
Ok(n) => assert_eq!(n.get_num(), (1 << 56) - 1),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(-(1 << 56)) {
|
||||
Ok(n) => assert_eq!(n.get_num(), -(1 << 56)),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(-(1 << 56) - 1) {
|
||||
Ok(_n) => assert!(false),
|
||||
_ => assert!(true),
|
||||
}
|
||||
|
||||
match Fixnum::build_with_checked(-1) {
|
||||
Ok(n) => assert_eq!(-n, Fixnum::build_with(1)),
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
||||
// float
|
||||
|
||||
let float = OrderedFloat(3.1415926f64);
|
||||
let float_ptr = arena_alloc!(float, &mut wam.machine_st.arena);
|
||||
|
||||
assert!(!float_ptr.as_ptr().is_null());
|
||||
|
||||
let float_cell = typed_arena_ptr_as_cell!(float_ptr);
|
||||
assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
|
||||
|
||||
match float_cell.to_untyped_arena_ptr() {
|
||||
Some(untyped_arena_ptr) => {
|
||||
assert_eq!(Some(float_ptr.header_ptr()), Some(untyped_arena_ptr.into()),);
|
||||
}
|
||||
None => {
|
||||
assert!(false); // we fail.
|
||||
}
|
||||
}
|
||||
|
||||
// char
|
||||
|
||||
let c = 'c';
|
||||
let char_cell = char_as_cell!(c);
|
||||
|
||||
read_heap_cell!(char_cell,
|
||||
(HeapCellValueTag::Char, c) => {
|
||||
assert_eq!(c, 'c');
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
|
||||
let c = 'Ћ';
|
||||
let cyrillic_char_cell = char_as_cell!(c);
|
||||
|
||||
read_heap_cell!(cyrillic_char_cell,
|
||||
(HeapCellValueTag::Char, c) => {
|
||||
assert_eq!(c, 'Ћ');
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
|
||||
// empty list
|
||||
|
||||
let cell = empty_list_as_cell!();
|
||||
|
||||
read_heap_cell!(cell,
|
||||
(HeapCellValueTag::Atom, (el, _arity)) => {
|
||||
assert_eq!(el.flat_index() as usize, empty_list_as_cell!().get_value());
|
||||
assert_eq!(el.as_str(), "[]");
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
);
|
||||
}
|
||||
}
|
||||
722
src/arithmetic.rs
Normal file
722
src/arithmetic.rs
Normal file
@@ -0,0 +1,722 @@
|
||||
use crate::allocator::*;
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::fixtures::*;
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::iterators::*;
|
||||
use crate::types::*;
|
||||
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::rug::ops::PowAssign;
|
||||
use crate::parser::rug::{Assign, Integer, Rational};
|
||||
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
|
||||
use ordered_float::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::{max, min, Ordering};
|
||||
use std::convert::TryFrom;
|
||||
use std::f64;
|
||||
use std::num::FpCategory;
|
||||
use std::ops::Div;
|
||||
use std::rc::Rc;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ArithmeticTerm {
|
||||
Reg(RegType),
|
||||
Interm(usize),
|
||||
Number(Number),
|
||||
}
|
||||
|
||||
impl ArithmeticTerm {
|
||||
pub(crate) fn interm_or(&self, interm: usize) -> usize {
|
||||
if let &ArithmeticTerm::Interm(interm) = self {
|
||||
interm
|
||||
} else {
|
||||
interm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = (Code, Option<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) => match ClauseType::from(*name, terms.len()) {
|
||||
ct @ ClauseType::Named(..) => {
|
||||
Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms))
|
||||
}
|
||||
ClauseType::Inlined(InlinedClauseType::IsFloat(_)) => {
|
||||
let ct = ClauseType::Named(1, atom!("float"), CodeIndex::default());
|
||||
Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms))
|
||||
}
|
||||
_ => Err(ArithmeticError::NonEvaluableFunctor(
|
||||
Literal::Atom(*name),
|
||||
terms.len(),
|
||||
)),
|
||||
}?,
|
||||
Term::Literal(cell, cons) => TermIterState::Literal(Level::Shallow, cell, cons),
|
||||
Term::Cons(..) | Term::PartialString(..) => {
|
||||
return Err(ArithmeticError::NonEvaluableFunctor(
|
||||
Literal::Atom(atom!(".")),
|
||||
2,
|
||||
))
|
||||
}
|
||||
Term::Var(cell, var) => TermIterState::Var(Level::Shallow, cell, var.clone()),
|
||||
};
|
||||
|
||||
Ok(ArithInstructionIterator {
|
||||
state_stack: vec![state],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ArithTermRef<'a> {
|
||||
Literal(&'a Literal),
|
||||
Op(Atom, usize), // name, arity.
|
||||
Var(Level, &'a Cell<VarReg>, Rc<String>),
|
||||
}
|
||||
|
||||
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, ct, subterms) => {
|
||||
let arity = subterms.len();
|
||||
|
||||
if child_num == arity {
|
||||
return Some(Ok(ArithTermRef::Op(ct.name(), arity)));
|
||||
} else {
|
||||
self.state_stack.push(TermIterState::Clause(
|
||||
lvl,
|
||||
child_num + 1,
|
||||
cell,
|
||||
ct,
|
||||
subterms,
|
||||
));
|
||||
|
||||
self.push_subterm(lvl, &subterms[child_num]);
|
||||
}
|
||||
}
|
||||
TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))),
|
||||
TermIterState::Var(lvl, cell, var) => {
|
||||
// the expression is the second argument of an
|
||||
// is/2 but the iterator can't see that, so the
|
||||
// level needs to be demoted manually.
|
||||
return Some(Ok(ArithTermRef::Var(lvl.child_level(), cell, var.clone())));
|
||||
}
|
||||
_ => {
|
||||
return Some(Err(ArithmeticError::NonEvaluableFunctor(
|
||||
Literal::Atom(atom!(".")),
|
||||
2,
|
||||
)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ArithmeticEvaluator<'a, TermMarker> {
|
||||
marker: &'a mut TermMarker,
|
||||
interm: Vec<ArithmeticTerm>,
|
||||
interm_c: usize,
|
||||
}
|
||||
|
||||
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::Float(n) => interm.push(ArithmeticTerm::Number(Number::Float(***n))),
|
||||
Literal::Rational(n) => interm.push(ArithmeticTerm::Number(Number::Rational(*n))),
|
||||
Literal::Atom(name) if name == &atom!("e") => interm.push(ArithmeticTerm::Number(
|
||||
Number::Float(OrderedFloat(f64::consts::E)),
|
||||
)),
|
||||
Literal::Atom(name) if name == &atom!("pi") => interm.push(ArithmeticTerm::Number(
|
||||
Number::Float(OrderedFloat(f64::consts::PI)),
|
||||
)),
|
||||
Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
|
||||
Number::Float(OrderedFloat(f64::EPSILON)),
|
||||
)),
|
||||
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'a, TermMarker: Allocator> ArithmeticEvaluator<'a, TermMarker> {
|
||||
pub(crate) fn new(marker: &'a mut TermMarker, target_int: usize) -> Self {
|
||||
ArithmeticEvaluator {
|
||||
marker,
|
||||
interm: Vec::new(),
|
||||
interm_c: target_int,
|
||||
}
|
||||
}
|
||||
|
||||
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!("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 incr_interm(&mut self) -> usize {
|
||||
let temp = self.interm_c;
|
||||
|
||||
self.interm.push(ArithmeticTerm::Interm(temp));
|
||||
self.interm_c += 1;
|
||||
|
||||
temp
|
||||
}
|
||||
|
||||
fn instr_from_clause(
|
||||
&mut self,
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
) -> Result<Instruction, ArithmeticError> {
|
||||
match arity {
|
||||
1 => {
|
||||
let a1 = self.interm.pop().unwrap();
|
||||
|
||||
let ninterm = if a1.interm_or(0) == 0 {
|
||||
self.incr_interm()
|
||||
} else {
|
||||
self.interm.push(a1.clone());
|
||||
a1.interm_or(0)
|
||||
};
|
||||
|
||||
self.get_unary_instr(name, a1, ninterm)
|
||||
}
|
||||
2 => {
|
||||
let a2 = self.interm.pop().unwrap();
|
||||
let a1 = self.interm.pop().unwrap();
|
||||
|
||||
let min_interm = min(a1.interm_or(0), a2.interm_or(0));
|
||||
|
||||
let ninterm = if min_interm == 0 {
|
||||
let max_interm = max(a1.interm_or(0), a2.interm_or(0));
|
||||
|
||||
if max_interm == 0 {
|
||||
self.incr_interm()
|
||||
} else {
|
||||
self.interm.push(ArithmeticTerm::Interm(max_interm));
|
||||
self.interm_c = max_interm + 1;
|
||||
max_interm
|
||||
}
|
||||
} else {
|
||||
self.interm.push(ArithmeticTerm::Interm(min_interm));
|
||||
self.interm_c = min_interm + 1;
|
||||
min_interm
|
||||
};
|
||||
|
||||
self.get_binary_instr(name, a1, a2, ninterm)
|
||||
}
|
||||
_ => Err(ArithmeticError::NonEvaluableFunctor(
|
||||
Literal::Atom(name),
|
||||
arity,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn eval(
|
||||
&mut self,
|
||||
src: &'a Term,
|
||||
term_loc: GenContext,
|
||||
) -> Result<ArithCont, ArithmeticError>
|
||||
{
|
||||
let mut code = vec![];
|
||||
let mut iter = src.iter()?;
|
||||
|
||||
while let Some(term_ref) = iter.next() {
|
||||
match term_ref? {
|
||||
ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?,
|
||||
ArithTermRef::Var(lvl, cell, name) => {
|
||||
let r = if cell.get().norm().reg_num() == 0 {
|
||||
let mut getter = || {
|
||||
use crate::targets::QueryInstruction;
|
||||
|
||||
loop {
|
||||
match self.marker.bindings().get(&name) {
|
||||
Some(&VarData::Temp(_, t, _)) if t != 0 =>
|
||||
return RegType::Temp(t),
|
||||
Some(&VarData::Perm(p)) if p != 0 =>
|
||||
return RegType::Perm(p),
|
||||
_ => {
|
||||
self.marker.mark_var::<QueryInstruction>(
|
||||
name.clone(),
|
||||
lvl,
|
||||
cell,
|
||||
term_loc,
|
||||
&mut code,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getter()
|
||||
/*
|
||||
_ => return Err(ArithmeticError::UninstantiatedVar),
|
||||
*/
|
||||
} else {
|
||||
cell.get().norm()
|
||||
};
|
||||
|
||||
self.interm.push(ArithmeticTerm::Reg(r));
|
||||
}
|
||||
ArithTermRef::Op(name, arity) => {
|
||||
code.push(self.instr_from_clause(name, arity)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((code, self.interm.pop()))
|
||||
}
|
||||
}
|
||||
|
||||
// integer division rounding function -- 9.1.3.1.
|
||||
pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number {
|
||||
match n {
|
||||
&Number::Integer(_) | &Number::Fixnum(_) => *n,
|
||||
&Number::Float(OrderedFloat(f)) => fixnum!(Number, f.floor() as i64, arena),
|
||||
&Number::Rational(ref r) => {
|
||||
let r_ref = r.fract_floor_ref();
|
||||
let (mut fract, mut floor) = (Rational::new(), Integer::new());
|
||||
(&mut fract, &mut floor).assign(r_ref);
|
||||
|
||||
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(ref n) => n.to_f64(),
|
||||
&Number::Float(OrderedFloat(f)) => f,
|
||||
&Number::Rational(ref r) => r.to_f64(),
|
||||
}
|
||||
}
|
||||
|
||||
// floating point result function -- 9.1.4.2.
|
||||
pub(crate) fn result_f<Round>(n: &Number, round: Round) -> Result<f64, EvalError>
|
||||
where
|
||||
Round: Fn(&Number) -> f64,
|
||||
{
|
||||
let f = rnd_f(n);
|
||||
classify_float(f, round)
|
||||
}
|
||||
|
||||
fn classify_float<Round>(f: f64, round: Round) -> Result<f64, EvalError>
|
||||
where
|
||||
Round: Fn(&Number) -> f64,
|
||||
{
|
||||
match f.classify() {
|
||||
FpCategory::Normal | FpCategory::Zero => Ok(round(&Number::Float(OrderedFloat(f)))),
|
||||
FpCategory::Infinite => {
|
||||
let f = round(&Number::Float(OrderedFloat(f)));
|
||||
|
||||
if OrderedFloat(f) == OrderedFloat(f64::MAX) {
|
||||
Ok(f)
|
||||
} else {
|
||||
Err(EvalError::FloatOverflow)
|
||||
}
|
||||
}
|
||||
FpCategory::Nan => Err(EvalError::Undefined),
|
||||
_ => Ok(round(&Number::Float(OrderedFloat(f)))),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn float_fn_to_f(n: i64) -> Result<f64, EvalError> {
|
||||
classify_float(n as f64, rnd_f)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn float_i_to_f(n: &Integer) -> Result<f64, EvalError> {
|
||||
classify_float(n.to_f64(), rnd_f)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn float_r_to_f(r: &Rational) -> Result<f64, EvalError> {
|
||||
classify_float(r.to_f64(), rnd_f)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn add_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||
Ok(OrderedFloat(classify_float(f1 + f2, rnd_f)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn mul_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||
Ok(OrderedFloat(classify_float(f1 * f2, rnd_f)?))
|
||||
}
|
||||
|
||||
#[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, rnd_f)?))
|
||||
}
|
||||
}
|
||||
|
||||
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(ref n2)) => n1.get_num().eq(&**n2),
|
||||
(&Number::Integer(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2.get_num()),
|
||||
(&Number::Fixnum(n1), &Number::Rational(ref n2)) => n1.get_num().eq(&**n2),
|
||||
(&Number::Rational(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2.get_num()),
|
||||
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).eq(&n2),
|
||||
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2.get_num() as f64)),
|
||||
(&Number::Integer(ref n1), &Number::Integer(ref n2)) => n1.eq(n2),
|
||||
(&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(n2),
|
||||
(&Number::Float(n1), &Number::Integer(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64())),
|
||||
(&Number::Integer(ref n1), &Number::Rational(ref n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
&Rational::from(&**n1) == &**n2
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
&**n1 == &**n2
|
||||
}
|
||||
}
|
||||
(&Number::Rational(ref n1), &Number::Integer(ref n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
&**n1 == &Rational::from(&**n2)
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
&**n1 == &**n2
|
||||
}
|
||||
}
|
||||
(&Number::Rational(ref n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(&n2),
|
||||
(&Number::Float(n1), &Number::Rational(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64())),
|
||||
(&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2),
|
||||
(&Number::Rational(ref r1), &Number::Rational(ref 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) => (&**n).partial_cmp(rhs),
|
||||
Number::Rational(r) => (&**r).partial_cmp(rhs),
|
||||
Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<usize> for Number {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &usize) -> bool {
|
||||
match self {
|
||||
Number::Fixnum(n) => {
|
||||
let n = n.get_num();
|
||||
|
||||
if n < 0i64 {
|
||||
false
|
||||
} else {
|
||||
(n as usize).eq(rhs)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => (&**n).eq(rhs),
|
||||
Number::Rational(r) => (&**r).eq(rhs),
|
||||
Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Number {
|
||||
fn partial_cmp(&self, rhs: &Number) -> Option<Ordering> {
|
||||
Some(self.cmp(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
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()).cmp(n2),
|
||||
(&Number::Float(n1), &Number::Integer(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64())),
|
||||
(&Number::Integer(n1), &Number::Rational(n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
Rational::from(&**n1).cmp(n2)
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(n1), &Number::Integer(n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
(&**n1).cmp(&Rational::from(&**n2))
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(&n2),
|
||||
(&Number::Float(n1), &Number::Rational(n2)) => n1.cmp(&OrderedFloat(n2.to_f64())),
|
||||
(&Number::Float(f1), &Number::Float(f2)) => f1.cmp(&f2),
|
||||
(&Number::Rational(r1), &Number::Rational(r2)) => (*r1).cmp(&*r2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<HeapCellValue> for Number {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: HeapCellValue) -> Result<Number, Self::Error> {
|
||||
read_heap_cell!(value,
|
||||
(HeapCellValueTag::Cons, c) => {
|
||||
match_untyped_arena_ptr!(c,
|
||||
(ArenaHeaderTag::F64, n) => {
|
||||
Ok(Number::Float(*n))
|
||||
}
|
||||
(ArenaHeaderTag::Integer, n) => {
|
||||
Ok(Number::Integer(n))
|
||||
}
|
||||
(ArenaHeaderTag::Rational, n) => {
|
||||
Ok(Number::Rational(n))
|
||||
}
|
||||
_ => {
|
||||
Err(())
|
||||
}
|
||||
)
|
||||
}
|
||||
(HeapCellValueTag::F64, n) => {
|
||||
Ok(Number::Float(**n))
|
||||
}
|
||||
(HeapCellValueTag::Fixnum, n) => {
|
||||
Ok(Number::Fixnum(n))
|
||||
}
|
||||
_ => {
|
||||
Err(())
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Computes n ^ power. Ignores the sign of power.
|
||||
pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer {
|
||||
let mut power = Integer::from(power.abs_ref());
|
||||
|
||||
if power == 0 {
|
||||
return Integer::from(1);
|
||||
}
|
||||
|
||||
let mut oddand = Integer::from(1);
|
||||
|
||||
while power > 1 {
|
||||
if power.is_odd() {
|
||||
oddand *= &n;
|
||||
}
|
||||
|
||||
n.pow_assign(2);
|
||||
power >>= 1;
|
||||
}
|
||||
|
||||
n * oddand
|
||||
}
|
||||
366
src/atom_table.rs
Normal file
366
src/atom_table.rs
Normal file
@@ -0,0 +1,366 @@
|
||||
use crate::parser::ast::MAX_ARITY;
|
||||
use crate::raw_block::*;
|
||||
use crate::types::*;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::str;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use modular_bitfield::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Atom {
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
const_assert!(mem::size_of::<Atom>() == 8);
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/static_atoms.rs"));
|
||||
|
||||
impl<'a> From<&'a Atom> for Atom {
|
||||
#[inline]
|
||||
fn from(atom: &'a Atom) -> Self {
|
||||
*atom
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Atom {
|
||||
#[inline]
|
||||
fn from(value: bool) -> Self {
|
||||
if value { atom!("true") } else { atom!("false") }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use std::cell::RefCell;
|
||||
|
||||
const ATOM_TABLE_INIT_SIZE: usize = 1 << 16;
|
||||
const ATOM_TABLE_ALIGN: usize = 8;
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
static ATOM_TABLE_BUF_BASE: RefCell<*const u8> = RefCell::new(ptr::null_mut());
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
static mut ATOM_TABLE_BUF_BASE: *const u8 = ptr::null_mut();
|
||||
|
||||
#[cfg(test)]
|
||||
fn set_atom_tbl_buf_base(ptr: *const u8) {
|
||||
ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| {
|
||||
*atom_table_buf_base.borrow_mut() = ptr;
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn get_atom_tbl_buf_base() -> *const u8 {
|
||||
ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow())
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn set_atom_tbl_buf_base(ptr: *const u8) {
|
||||
unsafe {
|
||||
ATOM_TABLE_BUF_BASE = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub(crate) fn get_atom_tbl_buf_base() -> *const u8 {
|
||||
unsafe { ATOM_TABLE_BUF_BASE }
|
||||
}
|
||||
|
||||
impl RawBlockTraits for AtomTable {
|
||||
#[inline]
|
||||
fn init_size() -> usize {
|
||||
ATOM_TABLE_INIT_SIZE
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn align() -> usize {
|
||||
ATOM_TABLE_ALIGN
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct AtomHeader {
|
||||
#[allow(unused)] m: bool,
|
||||
len: B50,
|
||||
#[allow(unused)] padding: B13,
|
||||
}
|
||||
|
||||
impl AtomHeader {
|
||||
fn build_with(len: u64) -> Self {
|
||||
AtomHeader::new().with_len(len).with_m(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for Atom {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Atom {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.as_str().hash(hasher)
|
||||
// hasher.write_usize(self.index)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_char {
|
||||
($s:expr) => {
|
||||
!$s.is_empty() && $s.chars().nth(1).is_none()
|
||||
};
|
||||
}
|
||||
|
||||
impl Atom {
|
||||
#[inline]
|
||||
pub fn buf(self) -> *const u8 {
|
||||
let ptr = self.as_ptr();
|
||||
|
||||
if ptr.is_null() {
|
||||
return ptr::null();
|
||||
}
|
||||
|
||||
(ptr as usize + mem::size_of::<AtomHeader>()) as *const u8
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_static(self) -> bool {
|
||||
self.index < STRINGS.len() << 3
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(self) -> *const u8 {
|
||||
if self.is_static() {
|
||||
ptr::null()
|
||||
} else {
|
||||
(get_atom_tbl_buf_base() as usize + self.index - (STRINGS.len() << 3)) as *const u8
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from(index: usize) -> Self {
|
||||
Self { index }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(self) -> usize {
|
||||
if self.is_static() {
|
||||
STRINGS[self.index >> 3].len()
|
||||
} else {
|
||||
unsafe { ptr::read(self.as_ptr() as *const AtomHeader).len() as _ }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn flat_index(self) -> u64 {
|
||||
(self.index >> 3) as u64
|
||||
}
|
||||
|
||||
pub fn as_char(self) -> Option<char> {
|
||||
let s = self.as_str();
|
||||
let mut it = s.chars();
|
||||
|
||||
let c1 = it.next();
|
||||
let c2 = it.next();
|
||||
|
||||
if c2.is_none() { c1 } else { None }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn chars(&self) -> str::Chars {
|
||||
self.as_str().chars()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
unsafe {
|
||||
let ptr = self.as_ptr();
|
||||
|
||||
if ptr.is_null() {
|
||||
return STRINGS[self.index >> 3];
|
||||
}
|
||||
|
||||
let header = ptr::read::<AtomHeader>(ptr as *const _);
|
||||
let len = header.len() as usize;
|
||||
let buf = (ptr as usize + mem::size_of::<AtomHeader>()) as *mut u8;
|
||||
|
||||
str::from_utf8_unchecked(slice::from_raw_parts(buf, len))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn defrock_brackets(&self, atom_tbl: &mut AtomTable) -> Self {
|
||||
let s = self.as_str();
|
||||
|
||||
let s = if s.starts_with('(') && s.ends_with(')') {
|
||||
&s['('.len_utf8()..s.len() - ')'.len_utf8()]
|
||||
} else {
|
||||
return *self;
|
||||
};
|
||||
|
||||
atom_tbl.build_with(s)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn write_to_ptr(string: &str, ptr: *mut u8) {
|
||||
ptr::write(ptr as *mut _, AtomHeader::build_with(string.len() as u64));
|
||||
let str_ptr = (ptr as usize + mem::size_of::<AtomHeader>()) as *mut u8;
|
||||
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr as *mut u8, string.len());
|
||||
}
|
||||
|
||||
impl PartialOrd for Atom {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Atom) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Atom {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Atom) -> Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AtomTable {
|
||||
block: RawBlock<AtomTable>,
|
||||
pub table: IndexSet<Atom>,
|
||||
}
|
||||
|
||||
impl Drop for AtomTable {
|
||||
fn drop(&mut self) {
|
||||
self.block.deallocate();
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomTable {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
let table = Self {
|
||||
block: RawBlock::new(),
|
||||
table: IndexSet::new(),
|
||||
};
|
||||
|
||||
set_atom_tbl_buf_base(table.block.base);
|
||||
table
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn buf(&self) -> *const u8 {
|
||||
self.block.base as *const u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn top(&self) -> *const u8 {
|
||||
self.block.top
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn lookup_str(&self, string: &str) -> Option<Atom> {
|
||||
STATIC_ATOMS_MAP.get(string).or_else(|| self.table.get(string)).cloned()
|
||||
}
|
||||
|
||||
pub fn build_with(&mut self, string: &str) -> Atom {
|
||||
if let Some(atom) = self.lookup_str(string) {
|
||||
return atom;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let size = mem::size_of::<AtomHeader>() + string.len();
|
||||
let align_offset = 8 * mem::align_of::<AtomHeader>();
|
||||
let size = (size & !(align_offset - 1)) + align_offset;
|
||||
|
||||
let len_ptr = {
|
||||
let mut ptr;
|
||||
|
||||
loop {
|
||||
ptr = self.block.alloc(size);
|
||||
|
||||
if ptr.is_null() {
|
||||
self.block.grow();
|
||||
set_atom_tbl_buf_base(self.block.base);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ptr
|
||||
};
|
||||
|
||||
let ptr_base = self.block.base as usize;
|
||||
|
||||
write_to_ptr(string, len_ptr);
|
||||
|
||||
let atom = Atom {
|
||||
index: (STRINGS.len() << 3) + len_ptr as usize - ptr_base,
|
||||
};
|
||||
|
||||
self.table.insert(atom);
|
||||
|
||||
atom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct AtomCell {
|
||||
name: B46,
|
||||
arity: B10,
|
||||
#[allow(unused)] f: bool,
|
||||
#[allow(unused)] m: bool,
|
||||
#[allow(unused)] tag: B6,
|
||||
}
|
||||
|
||||
impl AtomCell {
|
||||
#[inline]
|
||||
pub fn build_with(name: u64, arity: u16, tag: HeapCellValueTag) -> Self {
|
||||
if arity > 0 {
|
||||
debug_assert!(arity as usize <= MAX_ARITY);
|
||||
|
||||
AtomCell::new()
|
||||
.with_name(name)
|
||||
.with_arity(arity)
|
||||
.with_f(false)
|
||||
.with_tag(tag as u8)
|
||||
} else {
|
||||
AtomCell::new()
|
||||
.with_name(name)
|
||||
.with_f(false)
|
||||
.with_tag(tag as u8)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_index(self) -> usize {
|
||||
self.name() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name(self) -> Atom {
|
||||
Atom::from(self.get_index() << 3)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_arity(self) -> usize {
|
||||
self.arity() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name_and_arity(self) -> (Atom, usize) {
|
||||
(Atom::from(self.get_index() << 3), self.get_arity())
|
||||
}
|
||||
}
|
||||
20
src/bin/scryer-prolog.rs
Normal file
20
src/bin/scryer-prolog.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
fn main() {
|
||||
use nix::sys::signal;
|
||||
use scryer_prolog::*;
|
||||
|
||||
let handler = signal::SigHandler::Handler(handle_sigint);
|
||||
unsafe { signal::signal(signal::Signal::SIGINT, handler) }.unwrap();
|
||||
|
||||
let mut wam = machine::Machine::new();
|
||||
|
||||
wam.run_top_level();
|
||||
}
|
||||
|
||||
pub extern "C" fn handle_sigint(signal: libc::c_int) {
|
||||
use nix::sys::signal;
|
||||
use std::sync::atomic::Ordering;
|
||||
let signal = signal::Signal::from_c_int(signal).unwrap();
|
||||
if signal == signal::Signal::SIGINT {
|
||||
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
1165
src/codegen.rs
Normal file
1165
src/codegen.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,36 +1,40 @@
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use prolog_parser::ast::*;
|
||||
use crate::allocator::*;
|
||||
use crate::fixtures::*;
|
||||
use crate::forms::Level;
|
||||
use crate::instructions::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::targets::CompilationTarget;
|
||||
|
||||
use crate::prolog::allocator::*;
|
||||
use crate::prolog::fixtures::*;
|
||||
use crate::prolog::forms::*;
|
||||
use crate::prolog::machine::machine_indices::*;
|
||||
use crate::prolog::targets::*;
|
||||
use crate::temp_v;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::BTreeSet;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DebrayAllocator {
|
||||
bindings: IndexMap<Rc<Var>, VarData>,
|
||||
pub(crate) struct DebrayAllocator {
|
||||
bindings: IndexMap<Rc<String>, VarData, FxBuildHasher>,
|
||||
arg_c: usize,
|
||||
temp_lb: usize,
|
||||
arity: usize, // 0 if not at head.
|
||||
contents: IndexMap<usize, Rc<Var>>,
|
||||
contents: IndexMap<usize, Rc<String>, FxBuildHasher>,
|
||||
in_use: BTreeSet<usize>,
|
||||
}
|
||||
|
||||
impl DebrayAllocator {
|
||||
fn is_curr_arg_distinct_from(&self, var: &Var) -> bool {
|
||||
fn is_curr_arg_distinct_from(&self, var: &String) -> bool {
|
||||
match self.contents.get(&self.arg_c) {
|
||||
Some(t_var) if **t_var != *var => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn occurs_shallowly_in_head(&self, var: &Var, r: usize) -> bool {
|
||||
fn occurs_shallowly_in_head(&self, var: &String, r: usize) -> bool {
|
||||
match self.bindings.get(var).unwrap() {
|
||||
&VarData::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)),
|
||||
_ => false,
|
||||
@@ -43,7 +47,7 @@ impl DebrayAllocator {
|
||||
in_use_range || self.in_use.contains(&r)
|
||||
}
|
||||
|
||||
fn alloc_with_cr(&self, var: &Var) -> usize {
|
||||
fn alloc_with_cr(&self, var: &String) -> usize {
|
||||
match self.bindings.get(var) {
|
||||
Some(&VarData::Temp(_, _, ref tvd)) => {
|
||||
for &(_, reg) in tvd.use_set.iter() {
|
||||
@@ -69,7 +73,7 @@ impl DebrayAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_with_ca(&self, var: &Var) -> usize {
|
||||
fn alloc_with_ca(&self, var: &String) -> usize {
|
||||
match self.bindings.get(var) {
|
||||
Some(&VarData::Temp(_, _, ref tvd)) => {
|
||||
for &(_, reg) in tvd.use_set.iter() {
|
||||
@@ -97,7 +101,7 @@ impl DebrayAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Rc<Var>, usize)> {
|
||||
fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Rc<String>, usize)> {
|
||||
// we want to allocate a register to the k^{th} parameter, par_k.
|
||||
// par_k may not be a temporary variable.
|
||||
let k = self.arg_c;
|
||||
@@ -122,10 +126,11 @@ impl DebrayAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
fn evacuate_arg<'a, Target>(&mut self, chunk_num: usize, target: &mut Vec<Target>)
|
||||
where
|
||||
Target: CompilationTarget<'a>,
|
||||
{
|
||||
fn evacuate_arg<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
chunk_num: usize,
|
||||
code: &mut Code,
|
||||
) {
|
||||
match self.alloc_in_last_goal_hint(chunk_num) {
|
||||
Some((var, r)) => {
|
||||
let k = self.arg_c;
|
||||
@@ -133,7 +138,7 @@ impl DebrayAllocator {
|
||||
if r != k {
|
||||
let r = RegType::Temp(r);
|
||||
|
||||
target.push(Target::move_to_register(r, k));
|
||||
code.push(Target::move_to_register(r, k));
|
||||
|
||||
self.contents.swap_remove(&k);
|
||||
self.contents.insert(r.reg_num(), var.clone());
|
||||
@@ -148,10 +153,10 @@ impl DebrayAllocator {
|
||||
|
||||
fn alloc_reg_to_var<'a, Target>(
|
||||
&mut self,
|
||||
var: &Var,
|
||||
var: &String,
|
||||
lvl: Level,
|
||||
term_loc: GenContext,
|
||||
target: &mut Vec<Target>,
|
||||
target: &mut Vec<Instruction>,
|
||||
) -> usize
|
||||
where
|
||||
Target: CompilationTarget<'a>,
|
||||
@@ -159,7 +164,7 @@ impl DebrayAllocator {
|
||||
match term_loc {
|
||||
GenContext::Head => {
|
||||
if let Level::Shallow = lvl {
|
||||
self.evacuate_arg(0, target);
|
||||
self.evacuate_arg::<Target>(0, target);
|
||||
self.alloc_with_cr(var)
|
||||
} else {
|
||||
self.alloc_with_ca(var)
|
||||
@@ -168,7 +173,7 @@ impl DebrayAllocator {
|
||||
GenContext::Mid(_) => self.alloc_with_ca(var),
|
||||
GenContext::Last(chunk_num) => {
|
||||
if let Level::Shallow = lvl {
|
||||
self.evacuate_arg(chunk_num, target);
|
||||
self.evacuate_arg::<Target>(chunk_num, target);
|
||||
self.alloc_with_cr(var)
|
||||
} else {
|
||||
self.alloc_with_ca(var)
|
||||
@@ -192,7 +197,7 @@ impl DebrayAllocator {
|
||||
final_index
|
||||
}
|
||||
|
||||
fn in_place(&self, var: &Var, term_loc: GenContext, r: RegType, k: usize) -> bool {
|
||||
fn in_place(&self, var: &String, term_loc: GenContext, r: RegType, k: usize) -> bool {
|
||||
match term_loc {
|
||||
GenContext::Head if !r.is_perm() => r.reg_num() == k,
|
||||
_ => match self.bindings().get(var).unwrap() {
|
||||
@@ -203,49 +208,49 @@ impl DebrayAllocator {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Allocator<'a> for DebrayAllocator {
|
||||
impl Allocator for DebrayAllocator {
|
||||
fn new() -> DebrayAllocator {
|
||||
DebrayAllocator {
|
||||
arity: 0,
|
||||
arg_c: 1,
|
||||
temp_lb: 1,
|
||||
bindings: IndexMap::new(),
|
||||
contents: IndexMap::new(),
|
||||
bindings: IndexMap::with_hasher(FxBuildHasher::default()),
|
||||
contents: IndexMap::with_hasher(FxBuildHasher::default()),
|
||||
in_use: BTreeSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_anon_var<Target>(&mut self, lvl: Level, term_loc: GenContext, target: &mut Vec<Target>)
|
||||
where
|
||||
Target: CompilationTarget<'a>,
|
||||
{
|
||||
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
lvl: Level,
|
||||
term_loc: GenContext,
|
||||
code: &mut Code,
|
||||
) {
|
||||
let r = RegType::Temp(self.alloc_reg_to_non_var());
|
||||
|
||||
match lvl {
|
||||
Level::Deep => target.push(Target::subterm_to_variable(r)),
|
||||
Level::Deep => code.push(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(chunk_num, target);
|
||||
self.evacuate_arg::<Target>(chunk_num, code);
|
||||
}
|
||||
|
||||
self.arg_c += 1;
|
||||
|
||||
target.push(Target::argument_to_variable(r, k));
|
||||
code.push(Target::argument_to_variable(r, k));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn mark_non_var<Target>(
|
||||
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
lvl: Level,
|
||||
term_loc: GenContext,
|
||||
cell: &Cell<RegType>,
|
||||
target: &mut Vec<Target>,
|
||||
) where
|
||||
Target: CompilationTarget<'a>,
|
||||
{
|
||||
cell: &'a Cell<RegType>,
|
||||
code: &mut Code,
|
||||
) {
|
||||
let r = cell.get();
|
||||
|
||||
let r = match lvl {
|
||||
@@ -253,7 +258,7 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
||||
let k = self.arg_c;
|
||||
|
||||
if let GenContext::Last(chunk_num) = term_loc {
|
||||
self.evacuate_arg(chunk_num, target);
|
||||
self.evacuate_arg::<Target>(chunk_num, code);
|
||||
}
|
||||
|
||||
self.arg_c += 1;
|
||||
@@ -269,20 +274,18 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
||||
cell.set(r);
|
||||
}
|
||||
|
||||
fn mark_var<Target>(
|
||||
fn mark_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var: Rc<Var>,
|
||||
var: Rc<String>,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
target: &mut Vec<Target>,
|
||||
) where
|
||||
Target: CompilationTarget<'a>,
|
||||
{
|
||||
code: &mut Code,
|
||||
) {
|
||||
let (r, is_new_var) = match self.get(var.clone()) {
|
||||
RegType::Temp(0) => {
|
||||
// here, r is temporary *and* unassigned.
|
||||
let o = self.alloc_reg_to_var(&var, lvl, term_loc, target);
|
||||
let o = self.alloc_reg_to_var::<Target>(&var, lvl, term_loc, code);
|
||||
cell.set(VarReg::Norm(RegType::Temp(o)));
|
||||
|
||||
(RegType::Temp(o), true)
|
||||
@@ -293,32 +296,28 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
||||
|
||||
(pr, true)
|
||||
}
|
||||
r => {
|
||||
(r, false)
|
||||
}
|
||||
r => (r, false),
|
||||
};
|
||||
|
||||
self.mark_reserved_var(var, lvl, cell, term_loc, target, r, is_new_var);
|
||||
self.mark_reserved_var::<Target>(var, lvl, cell, term_loc, code, r, is_new_var);
|
||||
}
|
||||
|
||||
fn mark_reserved_var<Target>(
|
||||
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var: Rc<Var>,
|
||||
var: Rc<String>,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
target: &mut Vec<Target>,
|
||||
code: &mut Code,
|
||||
r: RegType,
|
||||
is_new_var: bool,
|
||||
) where
|
||||
Target: CompilationTarget<'a>,
|
||||
{
|
||||
) {
|
||||
match lvl {
|
||||
Level::Root | Level::Shallow => {
|
||||
let k = self.arg_c;
|
||||
|
||||
if self.is_curr_arg_distinct_from(&var) {
|
||||
self.evacuate_arg(term_loc.chunk_num(), target);
|
||||
self.evacuate_arg::<Target>(term_loc.chunk_num(), code);
|
||||
}
|
||||
|
||||
self.arg_c += 1;
|
||||
@@ -327,24 +326,24 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
||||
|
||||
if !self.in_place(&var, term_loc, r, k) {
|
||||
if is_new_var {
|
||||
target.push(Target::argument_to_variable(r, k));
|
||||
code.push(Target::argument_to_variable(r, k));
|
||||
} else {
|
||||
target.push(Target::argument_to_value(r, k));
|
||||
code.push(Target::argument_to_value(r, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
Level::Deep if is_new_var => {
|
||||
if let GenContext::Head = term_loc {
|
||||
if self.occurs_shallowly_in_head(&var, r.reg_num()) {
|
||||
target.push(Target::subterm_to_value(r));
|
||||
code.push(Target::subterm_to_value(r));
|
||||
} else {
|
||||
target.push(Target::subterm_to_variable(r));
|
||||
code.push(Target::subterm_to_variable(r));
|
||||
}
|
||||
} else {
|
||||
target.push(Target::subterm_to_variable(r));
|
||||
code.push(Target::subterm_to_variable(r));
|
||||
}
|
||||
}
|
||||
Level::Deep => target.push(Target::subterm_to_value(r)),
|
||||
Level::Deep => code.push(Target::subterm_to_value(r)),
|
||||
};
|
||||
|
||||
if !r.is_perm() {
|
||||
@@ -383,12 +382,12 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
||||
self.bindings
|
||||
}
|
||||
|
||||
fn reset_at_head(&mut self, args: &Vec<Box<Term>>) {
|
||||
fn reset_at_head(&mut self, args: &Vec<Term>) {
|
||||
self.reset_arg(args.len());
|
||||
self.arity = args.len();
|
||||
|
||||
for (idx, arg) in args.iter().enumerate() {
|
||||
if let &Term::Var(_, ref var) = arg.as_ref() {
|
||||
if let &Term::Var(_, ref var) = arg {
|
||||
let r = self.get(var.clone());
|
||||
|
||||
if !r.is_perm() && r.reg_num() == 0 {
|
||||
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,4 +1,4 @@
|
||||
:- module(bimetatrans_ruleml, [parse_ruleml/3]).
|
||||
:- module(bimetatrans, [parse_ruleml/3]).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(iso_ext)).
|
||||
@@ -19,28 +19,34 @@
|
||||
* all indentation stripped out. "Indented XML" contains indentation
|
||||
* and newlines, and is usually formatted for human readers.
|
||||
*
|
||||
* parse_ruleml/3 is the sole public predicate of BiMetaTrans(RuleML,
|
||||
* Prolog), a bidirectional translator capable of parsing well-formed
|
||||
* parse_ruleml/3 is the sole public predicate of BiMetaTrans(Prolog,
|
||||
* RuleML), a bidirectional translator capable of parsing well-formed
|
||||
* RuleML/XML (valid w.r.t. a proposed anchor schema nafhornlogeq
|
||||
* defining a sublanguage of the existing anchor schema naffologeq) to
|
||||
* an equivalent Prolog/'$V' term and back to RuleML/xmL. It has the
|
||||
* modes
|
||||
*
|
||||
* +AssertItem, +QueryItems, ?XML (Prolog->RuleML)
|
||||
* ?AssertItem, ?QueryItems, +XML (RuleML->Prolog)
|
||||
* +AssertItems, +QueryItems, ?XML (Prolog->RuleML)
|
||||
* ?AssertItems, ?QueryItems, +XML (RuleML->Prolog)
|
||||
*
|
||||
* 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
|
||||
* inputs are expected to be ground, meaning they should not contain
|
||||
* 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
|
||||
* sense that the top-level query
|
||||
*
|
||||
* ?- parse_ruleml(AssertItem, QueryItems, XML0), % Prolog->RuleML
|
||||
* parse_ruleml(AssertItem0, QueryItems0, XML), % RuleML->Prolog
|
||||
* AssertItem0 == AssertItem, % Test for expected round-trip results.
|
||||
* ?- parse_ruleml(AssertItems, QueryItems, XML0), % Prolog->RuleML
|
||||
* parse_ruleml(AssertItems0, QueryItems0, XML), % RuleML->Prolog
|
||||
* AssertItems0 == AssertItems, % Test for expected round-trip results.
|
||||
* QueryItems0 == QueryItems,
|
||||
* XML0 == XML.
|
||||
*
|
||||
@@ -48,10 +54,11 @@
|
||||
* not 0-appended are instantiated and therefore ground.
|
||||
*/
|
||||
|
||||
parse_ruleml(AssertItem, QueryItem, XML) :-
|
||||
( ( var(AssertItem) ; var(QueryItem) ), var(XML) ->
|
||||
parse_ruleml(AssertItems, QueryItems, XML) :-
|
||||
( ( var(AssertItems) ; var(QueryItems) ), var(XML) ->
|
||||
throw(error(instantiation_error, parse_ruleml/2))
|
||||
; phrase(ruleml_top_level_items(AssertItem, QueryItem), XML)
|
||||
; phrase(ruleml_top_level_items(AssertItems, QueryItems), XML),
|
||||
!
|
||||
).
|
||||
|
||||
|
||||
@@ -82,24 +89,15 @@ parse_header -->
|
||||
*
|
||||
* The two arguments are (un)instantiated depending on the direction
|
||||
* in which parse_ruleml/2 operates. In either direction, once
|
||||
* ruleml_query_item(QueryItems) or ruleml_assert(AssertItem)
|
||||
* succeeds:
|
||||
*
|
||||
* 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_query_item(QueryItems) or ruleml_assert(AssertItems)
|
||||
* succeeds, AssertItems and QueryItems are ground lists of terms,
|
||||
* corresponding to Assert/Query performative(s) of the RuleML/XML KB.
|
||||
*/
|
||||
|
||||
ruleml_top_level_items(AssertItem, QueryItems) -->
|
||||
( ruleml_assert(AssertItem),
|
||||
ruleml_query_items(QueryItems),
|
||||
!
|
||||
; { AssertItem = [] },
|
||||
ruleml_top_level_items(AssertItems, QueryItems) -->
|
||||
( ruleml_assert(AssertItems),
|
||||
ruleml_query_items(QueryItems)
|
||||
; { AssertItems = [] },
|
||||
ruleml_query_items(QueryItems)
|
||||
).
|
||||
|
||||
@@ -169,22 +167,16 @@ ruleml_top_level_items(AssertItem, QueryItems) -->
|
||||
* outset of ruleml_assert//1 will cause it to fail, meaning that
|
||||
* an empty <Assert></Assert> element is never emitted to the output
|
||||
* 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) -->
|
||||
( { var(Items) } ->
|
||||
list_ws("<Assert mapClosure=\"universal\">"),
|
||||
ruleml_assert_items(Items),
|
||||
!,
|
||||
list_ws("</Assert>")
|
||||
; "<Assert mapClosure=\"universal\">",
|
||||
{ Items \== [] },
|
||||
ruleml_assert_items(Items),
|
||||
!,
|
||||
"</Assert>"
|
||||
).
|
||||
|
||||
@@ -206,7 +198,6 @@ ruleml_assert(Items) -->
|
||||
|
||||
ruleml_assert_items([Item | Items]) -->
|
||||
ruleml_assert_item(Item),
|
||||
!,
|
||||
ruleml_assert_items(Items).
|
||||
ruleml_assert_items([]) --> [].
|
||||
|
||||
@@ -214,7 +205,6 @@ ruleml_assert_items([]) --> [].
|
||||
ruleml_query_items(Items) -->
|
||||
( { var(Items) } ->
|
||||
ruleml_query_item(Item),
|
||||
!,
|
||||
( { var(Item) } ->
|
||||
ruleml_query_items(Items)
|
||||
; { Items = [Item | Items0] },
|
||||
@@ -222,7 +212,6 @@ ruleml_query_items(Items) -->
|
||||
)
|
||||
; { Items = [Item | Items0] },
|
||||
ruleml_query_item(Item),
|
||||
!,
|
||||
ruleml_query_items(Items0)
|
||||
).
|
||||
ruleml_query_items([]) --> [].
|
||||
@@ -279,7 +268,7 @@ space(' ') --> " ".
|
||||
|
||||
decimal_point('.') --> ".".
|
||||
|
||||
sign('-') --> "-", !.
|
||||
sign('-') --> "-".
|
||||
sign('+') --> "+".
|
||||
|
||||
double_quote('"') --> "\"".
|
||||
@@ -332,7 +321,6 @@ ruleml_condition(Item) -->
|
||||
|
||||
ruleml_item_conjunction([Item | Items]) -->
|
||||
ruleml_condition(Item),
|
||||
!,
|
||||
ruleml_item_conjunction(Items).
|
||||
ruleml_item_conjunction([]) --> [].
|
||||
|
||||
@@ -404,7 +392,6 @@ ruleml_conjunction_of_items(Items) -->
|
||||
|
||||
ruleml_item_disjunction([Item | Items]) -->
|
||||
ruleml_condition(Item),
|
||||
!,
|
||||
ruleml_item_disjunction(Items).
|
||||
ruleml_item_disjunction([]) --> [].
|
||||
|
||||
@@ -549,7 +536,6 @@ ruleml_naf(Item) -->
|
||||
|
||||
ruleml_atoms([Item|Items]) -->
|
||||
ruleml_atom(Item),
|
||||
!,
|
||||
ruleml_atoms(Items).
|
||||
ruleml_atoms([]) --> [].
|
||||
|
||||
@@ -652,7 +638,6 @@ ruleml_expr(Item) -->
|
||||
( list_ws("<repo>"),
|
||||
ruleml_item(RepoItem),
|
||||
list_ws("</repo>"),
|
||||
!,
|
||||
{ fold_commas(Args, ArgsCommas) },
|
||||
{ Item =.. [Name, (ArgsCommas | RepoItem)] }
|
||||
; { Item =.. [Name | Args] }
|
||||
@@ -823,15 +808,12 @@ ruleml_data(Name) -->
|
||||
* converting from lists of characters (here denoted as Cs) to the
|
||||
* named data type in ISO Prolog. The type information is given
|
||||
* 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_number(Cs), !.
|
||||
ruleml_number(Cs).
|
||||
ruleml_data_contents(symbol, Cs) -->
|
||||
ruleml_symbol(Cs), !.
|
||||
ruleml_symbol(Cs).
|
||||
ruleml_data_contents(string, Cs) -->
|
||||
ruleml_string(Cs).
|
||||
|
||||
@@ -1195,8 +1177,7 @@ fold_semicolons(List, Output) :-
|
||||
* complicated by Scryer's current lack of multi-argument indexing.
|
||||
*/
|
||||
|
||||
fold_list([Item], _, Item) :-
|
||||
!.
|
||||
fold_list([Item], _, Item).
|
||||
fold_list([Item|Items], F, Form) :-
|
||||
Form =.. [F, Item, Fs],
|
||||
fold_list(Items, F, Fs).
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,20 +11,15 @@
|
||||
*/
|
||||
|
||||
:- module(least_time, [find_min_time/2,
|
||||
write_time_nl/1]).
|
||||
write_time_nl/1]).
|
||||
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(reif)).
|
||||
|
||||
|
||||
permutation([], []).
|
||||
permutation([X|Xs], Ys) :-
|
||||
permutation(Xs, Yss),
|
||||
select(X, Ys, Yss).
|
||||
|
||||
|
||||
valid_time([H1,H2,M1,M2], T) :-
|
||||
memberd_t(H1, [0,1,2], TH1),
|
||||
memberd_t(H2, [0,1,2,3,4,5,6,7,8,9], TH2),
|
||||
@@ -32,10 +27,10 @@ valid_time([H1,H2,M1,M2], T) :-
|
||||
memberd_t(M2, [0,1,2,3,4,5,6,7,8,9], TM2),
|
||||
( maplist(=(true), [TH1, TH2, TM1, TM2]) ->
|
||||
( H1 =:= 2 ->
|
||||
( H2 =< 3 ->
|
||||
T = true
|
||||
; T = false
|
||||
)
|
||||
( H2 =< 3 ->
|
||||
T = true
|
||||
; T = false
|
||||
)
|
||||
; T = true
|
||||
)
|
||||
; T = false
|
||||
@@ -49,15 +44,7 @@ permuted_times(Time, PermutedTimes) :-
|
||||
|
||||
find_min_time(Time, Min) :-
|
||||
valid_time(Time, true),
|
||||
permuted_times(Time, PermutedTimes),
|
||||
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)
|
||||
).
|
||||
permuted_times(Time, [Min|_]).
|
||||
|
||||
|
||||
write_time_nl(Time) :-
|
||||
31
src/examples/utf8.pl
Normal file
31
src/examples/utf8.pl
Normal file
@@ -0,0 +1,31 @@
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
:- 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 :-
|
||||
open('x.txt', write, Stream, [type(binary)]),
|
||||
F = put_byte(Stream),
|
||||
chars_utf8bytes("£\x2124\\x2764\\x1F496\\n", Bs),
|
||||
maplist(F, Bs),
|
||||
close(Stream).
|
||||
|
||||
get_bytes(Stream, Res) :- get_bytes(Stream, [], Res).
|
||||
get_bytes(Stream, Acc, Res) :-
|
||||
get_byte(Stream, B),
|
||||
(B =:= -1 ->
|
||||
reverse(Acc, Res)
|
||||
; get_bytes(Stream, [B|Acc], Res)).
|
||||
|
||||
read_f :-
|
||||
open('x.txt', read, Stream, [type(binary)]),
|
||||
get_bytes(Stream, Bs),
|
||||
chars_utf8bytes(Cs, Bs),
|
||||
write(Cs),
|
||||
close(Stream).
|
||||
@@ -1,8 +1,8 @@
|
||||
use prolog_parser::ast::*;
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use crate::prolog::forms::*;
|
||||
use crate::prolog::instructions::*;
|
||||
use crate::prolog::iterators::*;
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::iterators::*;
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
|
||||
@@ -14,23 +14,23 @@ use std::vec::Vec;
|
||||
|
||||
// labeled with chunk numbers.
|
||||
#[derive(Debug)]
|
||||
pub enum VarStatus {
|
||||
pub(crate) enum VarStatus {
|
||||
Perm(usize),
|
||||
Temp(usize, TempVarData), // Perm(chunk_num) | Temp(chunk_num, _)
|
||||
}
|
||||
|
||||
pub type OccurrenceSet = BTreeSet<(GenContext, usize)>;
|
||||
pub(crate) type OccurrenceSet = BTreeSet<(GenContext, usize)>;
|
||||
|
||||
// Perm: 0 initially, a stack register once processed.
|
||||
// Temp: labeled with chunk_num and temp offset (unassigned if 0).
|
||||
#[derive(Debug)]
|
||||
pub enum VarData {
|
||||
pub(crate) enum VarData {
|
||||
Perm(usize),
|
||||
Temp(usize, usize, TempVarData),
|
||||
}
|
||||
|
||||
impl VarData {
|
||||
pub fn as_reg_type(&self) -> RegType {
|
||||
pub(crate) fn as_reg_type(&self) -> RegType {
|
||||
match self {
|
||||
&VarData::Temp(_, r, _) => RegType::Temp(r),
|
||||
&VarData::Perm(r) => RegType::Perm(r),
|
||||
@@ -39,15 +39,15 @@ impl VarData {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TempVarData {
|
||||
pub last_term_arity: usize,
|
||||
pub use_set: OccurrenceSet,
|
||||
pub no_use_set: BTreeSet<usize>,
|
||||
pub conflict_set: BTreeSet<usize>,
|
||||
pub(crate) struct TempVarData {
|
||||
pub(crate) last_term_arity: usize,
|
||||
pub(crate) use_set: OccurrenceSet,
|
||||
pub(crate) no_use_set: BTreeSet<usize>,
|
||||
pub(crate) conflict_set: BTreeSet<usize>,
|
||||
}
|
||||
|
||||
impl TempVarData {
|
||||
pub fn new(last_term_arity: usize) -> Self {
|
||||
pub(crate) fn new(last_term_arity: usize) -> Self {
|
||||
TempVarData {
|
||||
last_term_arity: last_term_arity,
|
||||
use_set: BTreeSet::new(),
|
||||
@@ -56,7 +56,7 @@ impl TempVarData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uses_reg(&self, reg: usize) -> bool {
|
||||
pub(crate) fn uses_reg(&self, reg: usize) -> bool {
|
||||
for &(_, nreg) in self.use_set.iter() {
|
||||
if reg == nreg {
|
||||
return true;
|
||||
@@ -66,7 +66,7 @@ impl TempVarData {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn populate_conflict_set(&mut self) {
|
||||
pub(crate) fn populate_conflict_set(&mut self) {
|
||||
if self.last_term_arity > 0 {
|
||||
let arity = self.last_term_arity;
|
||||
let mut conflict_set: BTreeSet<usize> = (1..arity).collect();
|
||||
@@ -83,30 +83,29 @@ impl TempVarData {
|
||||
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VariableFixtures<'a>{
|
||||
perm_vars: IndexMap<Rc<Var>, VariableFixture<'a>>,
|
||||
last_chunk_temp_vars: IndexSet<Rc<Var>>
|
||||
pub(crate) struct VariableFixtures<'a> {
|
||||
perm_vars: IndexMap<Rc<String>, VariableFixture<'a>>,
|
||||
last_chunk_temp_vars: IndexSet<Rc<String>>,
|
||||
}
|
||||
|
||||
impl<'a> VariableFixtures<'a> {
|
||||
pub fn new() -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
VariableFixtures {
|
||||
perm_vars: IndexMap::new(),
|
||||
last_chunk_temp_vars: IndexSet::new()
|
||||
last_chunk_temp_vars: IndexSet::new(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, var: Rc<Var>, vs: VariableFixture<'a>) {
|
||||
pub(crate) fn insert(&mut self, var: Rc<String>, vs: VariableFixture<'a>) {
|
||||
self.perm_vars.insert(var, vs);
|
||||
}
|
||||
|
||||
pub fn insert_last_chunk_temp_var(&mut self, var: Rc<Var>) {
|
||||
pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Rc<String>) {
|
||||
self.last_chunk_temp_vars.insert(var);
|
||||
}
|
||||
|
||||
// computes no_use and conflict sets for all temp vars.
|
||||
pub fn populate_restricting_sets(&mut self) {
|
||||
pub(crate) fn populate_restricting_sets(&mut self) {
|
||||
// three stages:
|
||||
// 1. move the use sets of each variable to a local IndexMap, use_set
|
||||
// (iterate mutably, swap mutable refs).
|
||||
@@ -116,7 +115,7 @@ impl<'a> VariableFixtures<'a> {
|
||||
// Compute the conflict set of u.
|
||||
|
||||
// 1.
|
||||
let mut use_sets: IndexMap<Rc<Var>, OccurrenceSet> = IndexMap::new();
|
||||
let mut use_sets: IndexMap<Rc<String>, OccurrenceSet> = IndexMap::new();
|
||||
|
||||
for (var, &mut (ref mut var_status, _)) in self.iter_mut() {
|
||||
if let &mut VarStatus::Temp(_, ref mut var_data) = var_status {
|
||||
@@ -154,11 +153,11 @@ impl<'a> VariableFixtures<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mut(&mut self, u: Rc<Var>) -> Option<&mut VariableFixture<'a>> {
|
||||
fn get_mut(&mut self, u: Rc<String>) -> Option<&mut VariableFixture<'a>> {
|
||||
self.perm_vars.get_mut(&u)
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> indexmap::map::IterMut<Rc<Var>, VariableFixture<'a>> {
|
||||
fn iter_mut(&mut self) -> indexmap::map::IterMut<Rc<String>, VariableFixture<'a>> {
|
||||
self.perm_vars.iter_mut()
|
||||
}
|
||||
|
||||
@@ -171,7 +170,7 @@ impl<'a> VariableFixtures<'a> {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vars_above_threshold(&self, index: usize) -> usize {
|
||||
pub(crate) fn vars_above_threshold(&self, index: usize) -> usize {
|
||||
let mut var_count = 0;
|
||||
|
||||
for &(ref var_status, _) in self.values() {
|
||||
@@ -185,7 +184,7 @@ impl<'a> VariableFixtures<'a> {
|
||||
var_count
|
||||
}
|
||||
|
||||
pub fn mark_vars_in_chunk<I>(&mut self, iter: I, lt_arity: usize, term_loc: GenContext)
|
||||
pub(crate) fn mark_vars_in_chunk<I>(&mut self, iter: I, lt_arity: usize, term_loc: GenContext)
|
||||
where
|
||||
I: Iterator<Item = TermRef<'a>>,
|
||||
{
|
||||
@@ -219,19 +218,19 @@ impl<'a> VariableFixtures<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> indexmap::map::IntoIter<Rc<Var>, VariableFixture<'a>> {
|
||||
pub(crate) fn into_iter(self) -> indexmap::map::IntoIter<Rc<String>, VariableFixture<'a>> {
|
||||
self.perm_vars.into_iter()
|
||||
}
|
||||
|
||||
fn values(&self) -> indexmap::map::Values<Rc<Var>, VariableFixture<'a>> {
|
||||
fn values(&self) -> indexmap::map::Values<Rc<String>, VariableFixture<'a>> {
|
||||
self.perm_vars.values()
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
pub(crate) fn size(&self) -> usize {
|
||||
self.perm_vars.len()
|
||||
}
|
||||
|
||||
pub fn set_perm_vals(&self, has_deep_cuts: bool) {
|
||||
pub(crate) fn set_perm_vals(&self, has_deep_cuts: bool) {
|
||||
let mut values_vec: Vec<_> = self
|
||||
.values()
|
||||
.filter_map(|ref v| match &v.0 {
|
||||
@@ -253,43 +252,41 @@ impl<'a> VariableFixtures<'a> {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnsafeVarMarker {
|
||||
pub unsafe_vars: IndexMap<RegType, usize>,
|
||||
pub safe_vars: IndexSet<RegType>,
|
||||
pub(crate) struct UnsafeVarMarker {
|
||||
pub(crate) unsafe_vars: IndexMap<RegType, usize>,
|
||||
pub(crate) safe_vars: IndexSet<RegType>,
|
||||
}
|
||||
|
||||
impl UnsafeVarMarker {
|
||||
pub fn new() -> Self {
|
||||
pub(crate) fn new() -> Self {
|
||||
UnsafeVarMarker {
|
||||
unsafe_vars: IndexMap::new(),
|
||||
safe_vars: IndexSet::new()
|
||||
safe_vars: IndexSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_safe_vars(safe_vars: IndexSet<RegType>) -> Self {
|
||||
pub(crate) fn from_safe_vars(safe_vars: IndexSet<RegType>) -> Self {
|
||||
UnsafeVarMarker {
|
||||
unsafe_vars: IndexMap::new(),
|
||||
safe_vars
|
||||
safe_vars,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mark_safe_vars(&mut self, query_instr: &QueryInstruction) -> bool {
|
||||
pub(crate) fn mark_safe_vars(&mut self, query_instr: &Instruction) -> bool {
|
||||
match query_instr {
|
||||
&QueryInstruction::PutVariable(r @ RegType::Temp(_), _)
|
||||
| &QueryInstruction::SetVariable(r) => {
|
||||
&Instruction::PutVariable(r @ RegType::Temp(_), _) |
|
||||
&Instruction::SetVariable(r) => {
|
||||
self.safe_vars.insert(r);
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mark_phase(&mut self, query_instr: &QueryInstruction, phase: usize) {
|
||||
pub(crate) fn mark_phase(&mut self, query_instr: &Instruction, phase: usize) {
|
||||
match query_instr {
|
||||
&QueryInstruction::PutValue(r @ RegType::Perm(_), _)
|
||||
| &QueryInstruction::SetValue(r) => {
|
||||
&Instruction::PutValue(r @ RegType::Perm(_), _) |
|
||||
&Instruction::SetValue(r) => {
|
||||
let p = self.unsafe_vars.entry(r).or_insert(0);
|
||||
*p = phase;
|
||||
}
|
||||
@@ -297,21 +294,21 @@ impl UnsafeVarMarker {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mark_unsafe_vars(&mut self, query_instr: &mut QueryInstruction, phase: usize) {
|
||||
pub(crate) fn mark_unsafe_vars(&mut self, query_instr: &mut Instruction, phase: usize) {
|
||||
match query_instr {
|
||||
&mut QueryInstruction::PutValue(RegType::Perm(i), arg) => {
|
||||
&mut Instruction::PutValue(RegType::Perm(i), arg) => {
|
||||
if let Some(p) = self.unsafe_vars.swap_remove(&RegType::Perm(i)) {
|
||||
if p == phase {
|
||||
*query_instr = QueryInstruction::PutUnsafeValue(i, arg);
|
||||
*query_instr = Instruction::PutUnsafeValue(i, arg);
|
||||
self.safe_vars.insert(RegType::Perm(i));
|
||||
} else {
|
||||
self.unsafe_vars.insert(RegType::Perm(i), p);
|
||||
}
|
||||
}
|
||||
}
|
||||
&mut QueryInstruction::SetValue(r) => {
|
||||
&mut Instruction::SetValue(r) => {
|
||||
if !self.safe_vars.contains(&r) {
|
||||
*query_instr = QueryInstruction::SetLocalValue(r);
|
||||
*query_instr = Instruction::SetLocalValue(r);
|
||||
|
||||
self.safe_vars.insert(r);
|
||||
self.unsafe_vars.remove(&r);
|
||||
885
src/forms.rs
Normal file
885
src/forms.rs
Normal file
@@ -0,0 +1,885 @@
|
||||
use crate::arena::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::instructions::*;
|
||||
use crate::machine::heap::*;
|
||||
use crate::machine::loader::PredicateQueue;
|
||||
use crate::machine::machine_errors::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::parser::ast::*;
|
||||
use crate::parser::parser::CompositeOpDesc;
|
||||
use crate::parser::rug::{Integer, Rational};
|
||||
use crate::types::*;
|
||||
|
||||
use fxhash::FxBuildHasher;
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use ordered_float::OrderedFloat;
|
||||
|
||||
use slice_deque::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::ops::AddAssign;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{is_infix, is_postfix};
|
||||
|
||||
pub type PredicateKey = (Atom, usize); // name, arity.
|
||||
|
||||
pub type Predicate = Vec<PredicateClause>;
|
||||
|
||||
// vars of predicate, toplevel offset. Vec<Term> is always a vector
|
||||
// of vars (we get their adjoining cells this way).
|
||||
pub type JumpStub = Vec<Term>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TopLevel {
|
||||
Fact(Term), // Term, line_num, col_num
|
||||
Predicate(Predicate),
|
||||
Query(Vec<QueryTerm>),
|
||||
Rule(Rule), // Rule, line_num, col_num
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AppendOrPrepend {
|
||||
Append,
|
||||
Prepend,
|
||||
}
|
||||
|
||||
impl AppendOrPrepend {
|
||||
#[inline]
|
||||
pub(crate) fn is_append(self) -> bool {
|
||||
match self {
|
||||
AppendOrPrepend::Append => true,
|
||||
AppendOrPrepend::Prepend => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Level {
|
||||
Deep,
|
||||
Root,
|
||||
Shallow,
|
||||
}
|
||||
|
||||
impl Level {
|
||||
pub(crate) fn child_level(self) -> Level {
|
||||
match self {
|
||||
Level::Root => Level::Shallow,
|
||||
_ => Level::Deep,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum QueryTerm {
|
||||
// register, clause type, subterms, use default call policy.
|
||||
Clause(Cell<RegType>, ClauseType, Vec<Term>, bool),
|
||||
BlockedCut, // a cut which is 'blocked by letters', like the P term in P -> Q.
|
||||
UnblockedCut(Cell<VarReg>),
|
||||
GetLevelAndUnify(Cell<VarReg>, Rc<String>),
|
||||
Jump(JumpStub),
|
||||
}
|
||||
|
||||
impl QueryTerm {
|
||||
pub(crate) fn set_default_caller(&mut self) {
|
||||
match self {
|
||||
&mut QueryTerm::Clause(_, _, _, ref mut use_default_cp) => *use_default_cp = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn arity(&self) -> usize {
|
||||
match self {
|
||||
&QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(),
|
||||
&QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => 0,
|
||||
&QueryTerm::Jump(ref vars) => vars.len(),
|
||||
&QueryTerm::GetLevelAndUnify(..) => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Rule {
|
||||
pub(crate) head: (Atom, Vec<Term>, QueryTerm),
|
||||
pub(crate) clauses: Vec<QueryTerm>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash)]
|
||||
pub enum ListingSource {
|
||||
DynamicallyGenerated,
|
||||
File(Atom, PathBuf), // filename, path
|
||||
User,
|
||||
}
|
||||
|
||||
impl ListingSource {
|
||||
pub(crate) fn from_file_and_path(filename: Atom, path_buf: PathBuf) -> Self {
|
||||
ListingSource::File(filename, path_buf)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ClauseInfo {
|
||||
fn is_consistent(&self, clauses: &PredicateQueue) -> bool {
|
||||
match clauses.first() {
|
||||
Some(cl) => {
|
||||
self.name() == ClauseInfo::name(cl) && self.arity() == ClauseInfo::arity(cl)
|
||||
}
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> Option<Atom>;
|
||||
fn arity(&self) -> usize;
|
||||
}
|
||||
|
||||
impl ClauseInfo for PredicateKey {
|
||||
#[inline]
|
||||
fn name(&self) -> Option<Atom> {
|
||||
Some(self.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn arity(&self) -> usize {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
impl ClauseInfo for Term {
|
||||
fn name(&self) -> Option<Atom> {
|
||||
//, atom_tbl: &AtomTable) -> Option<StringBuffer> {
|
||||
match self {
|
||||
Term::Clause(_, name, terms) => {
|
||||
// let str_buf = StringBuffer::from(*name, atom_tbl);
|
||||
|
||||
match name.as_str() {
|
||||
// str_buf.as_str() {
|
||||
":-" => {
|
||||
match terms.len() {
|
||||
1 => None, // a declaration.
|
||||
2 => terms[0].name(), //.map(|name| StringBuffer::from(name, atom_tbl)),
|
||||
_ => Some(*name),
|
||||
}
|
||||
}
|
||||
_ => Some(*name), //str_buf),
|
||||
}
|
||||
}
|
||||
Term::Literal(_, Literal::Atom(name)) => Some(*name), //Some(StringBuffer::from(*name, atom_tbl)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn arity(&self) -> usize {
|
||||
match self {
|
||||
Term::Clause(_, name, terms) => match name.as_str() {
|
||||
":-" => match terms.len() {
|
||||
1 => 0,
|
||||
2 => terms[0].arity(),
|
||||
_ => terms.len(),
|
||||
},
|
||||
_ => terms.len(),
|
||||
},
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClauseInfo for Rule {
|
||||
fn name(&self) -> Option<Atom> {
|
||||
Some(self.head.0)
|
||||
}
|
||||
|
||||
fn arity(&self) -> usize {
|
||||
self.head.1.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl ClauseInfo for PredicateClause {
|
||||
fn name(&self) -> Option<Atom> {
|
||||
match self {
|
||||
&PredicateClause::Fact(ref term, ..) => term.name(),
|
||||
&PredicateClause::Rule(ref rule, ..) => rule.name(),
|
||||
}
|
||||
}
|
||||
|
||||
fn arity(&self) -> usize {
|
||||
match self {
|
||||
&PredicateClause::Fact(ref term, ..) => term.arity(),
|
||||
&PredicateClause::Rule(ref rule, ..) => rule.arity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PredicateClause {
|
||||
Fact(Term),
|
||||
Rule(Rule),
|
||||
}
|
||||
|
||||
impl PredicateClause {
|
||||
pub(crate) fn args(&self) -> Option<&[Term]> {
|
||||
match self {
|
||||
PredicateClause::Fact(term, ..) => match term {
|
||||
Term::Clause(_, _, args) => Some(&args),
|
||||
_ => None,
|
||||
},
|
||||
PredicateClause::Rule(rule, ..) => {
|
||||
if rule.head.1.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&rule.head.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClauseSpan {
|
||||
pub left: usize,
|
||||
pub right: usize,
|
||||
pub instantiated_arg_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ModuleSource {
|
||||
Library(Atom),
|
||||
File(Atom),
|
||||
}
|
||||
|
||||
impl ModuleSource {
|
||||
pub(crate) fn as_functor_stub(&self) -> MachineStub {
|
||||
match self {
|
||||
ModuleSource::Library(name) => {
|
||||
functor!(atom!("library"), [atom(name)])
|
||||
}
|
||||
ModuleSource::File(name) => {
|
||||
functor!(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Hash, Debug)]
|
||||
pub enum MetaSpec {
|
||||
Minus,
|
||||
Plus,
|
||||
Either,
|
||||
RequiresExpansionWithArgument(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Declaration {
|
||||
Dynamic(Atom, usize),
|
||||
MetaPredicate(Atom, Atom, Vec<MetaSpec>), // module name, name, meta-specs
|
||||
Module(ModuleDecl),
|
||||
NonCountedBacktracking(Atom, usize), // name, arity
|
||||
Op(OpDecl),
|
||||
UseModule(ModuleSource),
|
||||
UseQualifiedModule(ModuleSource, IndexSet<ModuleExport>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
|
||||
pub struct OpDecl {
|
||||
pub(crate) op_desc: OpDesc,
|
||||
pub(crate) name: Atom,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn fixity(spec: u32) -> Fixity {
|
||||
match spec {
|
||||
XFY | XFX | YFX => Fixity::In,
|
||||
XF | YF => Fixity::Post,
|
||||
FX | FY => Fixity::Pre,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl OpDecl {
|
||||
#[inline]
|
||||
pub(crate) fn new(op_desc: OpDesc, name: Atom) -> Self {
|
||||
Self { op_desc, name }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn remove(&mut self, op_dir: &mut OpDir) {
|
||||
let prec = self.op_desc.get_prec();
|
||||
self.op_desc.set(0, self.op_desc.get_spec());
|
||||
|
||||
self.insert_into_op_dir(op_dir);
|
||||
self.op_desc.set(prec, self.op_desc.get_spec());
|
||||
}
|
||||
|
||||
pub(crate) fn insert_into_op_dir(&self, op_dir: &mut OpDir) -> Option<OpDesc> {
|
||||
let key = (self.name, fixity(self.op_desc.get_spec() as u32));
|
||||
|
||||
match op_dir.get_mut(&key) {
|
||||
Some(cell) => {
|
||||
let (old_prec, old_spec) = cell.get();
|
||||
cell.set(self.op_desc.get_prec(), self.op_desc.get_spec());
|
||||
return Some(OpDesc::build_with(old_prec, old_spec));
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
op_dir.insert(key, self.op_desc)
|
||||
}
|
||||
|
||||
pub(crate) fn submit(
|
||||
&self,
|
||||
existing_desc: Option<CompositeOpDesc>,
|
||||
op_dir: &mut OpDir,
|
||||
) -> Result<(), SessionError> {
|
||||
let (spec, name) = (self.op_desc.get_spec(), self.name.clone());
|
||||
|
||||
if is_infix!(spec as u32) {
|
||||
if let Some(desc) = existing_desc {
|
||||
if desc.post > 0 {
|
||||
return Err(SessionError::OpIsInfixAndPostFix(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if is_postfix!(spec as u32) {
|
||||
if let Some(desc) = existing_desc {
|
||||
if desc.inf > 0 {
|
||||
return Err(SessionError::OpIsInfixAndPostFix(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.insert_into_op_dir(op_dir);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AtomOrString {
|
||||
Atom(Atom),
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl AtomOrString {
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
AtomOrString::Atom(atom) if atom == &atom!("[]") => "",
|
||||
AtomOrString::Atom(atom) => atom.as_str(),
|
||||
AtomOrString::String(string) => string.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_string(self) -> String {
|
||||
match self {
|
||||
AtomOrString::Atom(atom) => {
|
||||
atom.as_str().to_owned()
|
||||
}
|
||||
AtomOrString::String(string) => {
|
||||
string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_atom_op_spec(
|
||||
name: Atom,
|
||||
spec: Option<OpDesc>,
|
||||
op_dir: &OpDir,
|
||||
) -> Option<OpDesc> {
|
||||
fetch_op_spec_from_existing(name, 2, spec, op_dir)
|
||||
.or_else(|| fetch_op_spec_from_existing(name, 1, spec, op_dir))
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_op_spec_from_existing(
|
||||
name: Atom,
|
||||
arity: usize,
|
||||
op_desc: Option<OpDesc>,
|
||||
op_dir: &OpDir,
|
||||
) -> Option<OpDesc> {
|
||||
if let Some(ref op_desc) = &op_desc {
|
||||
if op_desc.arity() != arity {
|
||||
/* it's possible to extend operator functors with
|
||||
* additional terms. When that happens,
|
||||
* void the op_spec by returning None. */
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
op_desc.or_else(|| fetch_op_spec(name, arity, op_dir))
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_op_spec(name: Atom, arity: usize, op_dir: &OpDir) -> Option<OpDesc> {
|
||||
match arity {
|
||||
2 => op_dir.get(&(name, Fixity::In)).and_then(|op_desc| {
|
||||
if op_desc.get_prec() > 0 {
|
||||
Some(*op_desc)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
1 => {
|
||||
if let Some(op_desc) = op_dir.get(&(name.clone(), Fixity::Pre)) {
|
||||
if op_desc.get_prec() > 0 {
|
||||
return Some(*op_desc);
|
||||
}
|
||||
}
|
||||
|
||||
op_dir.get(&(name, Fixity::Post)).and_then(|op_desc| {
|
||||
if op_desc.get_prec() > 0 {
|
||||
Some(*op_desc)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
0 => {
|
||||
fetch_atom_op_spec(name, None, op_dir)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ModuleDir = IndexMap<Atom, Module, FxBuildHasher>;
|
||||
|
||||
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
|
||||
pub enum ModuleExport {
|
||||
OpDecl(OpDecl),
|
||||
PredicateKey(PredicateKey),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleDecl {
|
||||
pub(crate) name: Atom,
|
||||
pub(crate) exports: Vec<ModuleExport>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Module {
|
||||
pub(crate) module_decl: ModuleDecl,
|
||||
pub(crate) code_dir: CodeDir,
|
||||
pub(crate) op_dir: OpDir,
|
||||
pub(crate) meta_predicates: MetaPredicateDir,
|
||||
pub(crate) extensible_predicates: ExtensiblePredicates,
|
||||
pub(crate) local_extensible_predicates: LocalExtensiblePredicates,
|
||||
pub(crate) listing_src: ListingSource,
|
||||
}
|
||||
|
||||
// Module's and related types are defined in forms.
|
||||
impl Module {
|
||||
pub(crate) fn new(
|
||||
module_decl: ModuleDecl,
|
||||
listing_src: ListingSource,
|
||||
) -> Self {
|
||||
Module {
|
||||
module_decl,
|
||||
code_dir: CodeDir::with_hasher(FxBuildHasher::default()),
|
||||
op_dir: default_op_dir(),
|
||||
meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()),
|
||||
extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()),
|
||||
local_extensible_predicates: LocalExtensiblePredicates::with_hasher(
|
||||
FxBuildHasher::default(),
|
||||
),
|
||||
listing_src,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_in_situ(module_decl: ModuleDecl) -> Self {
|
||||
Module {
|
||||
module_decl,
|
||||
code_dir: CodeDir::with_hasher(FxBuildHasher::default()),
|
||||
op_dir: OpDir::with_hasher(FxBuildHasher::default()),
|
||||
meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()),
|
||||
extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()),
|
||||
local_extensible_predicates: LocalExtensiblePredicates::with_hasher(
|
||||
FxBuildHasher::default()
|
||||
),
|
||||
listing_src: ListingSource::DynamicallyGenerated,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Number {
|
||||
Float(OrderedFloat<f64>),
|
||||
Integer(TypedArenaPtr<Integer>),
|
||||
Rational(TypedArenaPtr<Rational>),
|
||||
Fixnum(Fixnum),
|
||||
}
|
||||
|
||||
impl Default for Number {
|
||||
fn default() -> Self {
|
||||
Number::Fixnum(Fixnum::build_with(0))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Number::Float(fl) => write!(f, "{}", fl),
|
||||
Number::Integer(n) => write!(f, "{}", n),
|
||||
Number::Rational(r) => write!(f, "{}", r),
|
||||
Number::Fixnum(n) => write!(f, "{}", n.get_num()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ArenaFrom<T> {
|
||||
fn arena_from(value: T, arena: &mut Arena) -> Self;
|
||||
}
|
||||
|
||||
impl ArenaFrom<Integer> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: Integer, arena: &mut Arena) -> Number {
|
||||
Number::Integer(arena_alloc!(value, arena))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<Rational> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: Rational, arena: &mut Arena) -> Number {
|
||||
Number::Rational(arena_alloc!(value, arena))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<usize> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: usize, arena: &mut Arena) -> Number {
|
||||
match i64::try_from(value) {
|
||||
Ok(value) => Fixnum::build_with_checked(value)
|
||||
.map(Number::Fixnum)
|
||||
.unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena))),
|
||||
Err(_) => Number::Integer(arena_alloc!(Integer::from(value), arena)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<u64> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: u64, arena: &mut Arena) -> Number {
|
||||
match i64::try_from(value) {
|
||||
Ok(value) => Fixnum::build_with_checked(value)
|
||||
.map(Number::Fixnum)
|
||||
.unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena))),
|
||||
Err(_) => Number::Integer(arena_alloc!(Integer::from(value), arena)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<i64> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: i64, arena: &mut Arena) -> Number {
|
||||
Fixnum::build_with_checked(value)
|
||||
.map(Number::Fixnum)
|
||||
.unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<isize> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: isize, arena: &mut Arena) -> Number {
|
||||
Fixnum::build_with_checked(value as i64)
|
||||
.map(Number::Fixnum)
|
||||
.unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<u32> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: u32, _arena: &mut Arena) -> Number {
|
||||
Number::Fixnum(Fixnum::build_with(value as i64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<i32> for Number {
|
||||
#[inline]
|
||||
fn arena_from(value: i32, _arena: &mut Arena) -> Number {
|
||||
Number::Fixnum(Fixnum::build_with(value as i64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<Number> for Literal {
|
||||
#[inline]
|
||||
fn arena_from(value: Number, arena: &mut Arena) -> Literal {
|
||||
match value {
|
||||
Number::Fixnum(n) => Literal::Fixnum(n),
|
||||
Number::Integer(n) => Literal::Integer(n),
|
||||
Number::Float(f) => Literal::Float(arena_alloc!(f, arena)),
|
||||
Number::Rational(r) => Literal::Rational(r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ArenaFrom<Number> for HeapCellValue {
|
||||
#[inline]
|
||||
fn arena_from(value: Number, arena: &mut Arena) -> HeapCellValue {
|
||||
match value {
|
||||
Number::Fixnum(n) => fixnum_as_cell!(n),
|
||||
Number::Integer(n) => typed_arena_ptr_as_cell!(n),
|
||||
Number::Float(n) => typed_arena_ptr_as_cell!(arena_alloc!(n, arena)),
|
||||
Number::Rational(n) => typed_arena_ptr_as_cell!(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Number {
|
||||
#[inline]
|
||||
pub(crate) fn is_positive(&self) -> bool {
|
||||
match self {
|
||||
&Number::Fixnum(n) => n.get_num() > 0,
|
||||
&Number::Integer(ref n) => &**n > &0,
|
||||
&Number::Float(f) => f.is_sign_positive(),
|
||||
&Number::Rational(ref r) => &**r > &0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_negative(&self) -> bool {
|
||||
match self {
|
||||
&Number::Fixnum(n) => n.get_num() < 0,
|
||||
&Number::Integer(ref n) => &**n < &0,
|
||||
&Number::Float(OrderedFloat(f)) => f.is_sign_negative() && OrderedFloat(f) != -0f64,
|
||||
&Number::Rational(ref r) => &**r < &0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_zero(&self) -> bool {
|
||||
match self {
|
||||
&Number::Fixnum(n) => n.get_num() == 0,
|
||||
&Number::Integer(ref n) => &**n == &0,
|
||||
&Number::Float(f) => f == OrderedFloat(0f64) || f == OrderedFloat(-0f64),
|
||||
&Number::Rational(ref r) => &**r == &0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_integer(&self) -> bool {
|
||||
match self {
|
||||
Number::Fixnum(_) | Number::Integer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum OptArgIndexKey {
|
||||
Literal(usize, usize, Literal, Vec<Literal>), // index, IndexingCode location, opt arg, alternatives
|
||||
List(usize, usize), // index, IndexingCode location
|
||||
None,
|
||||
Structure(usize, usize, Atom, usize), // index, IndexingCode location, name, arity
|
||||
}
|
||||
|
||||
impl OptArgIndexKey {
|
||||
#[inline]
|
||||
pub(crate) fn take(&mut self) -> OptArgIndexKey {
|
||||
std::mem::replace(self, OptArgIndexKey::None)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn arg_num(&self) -> usize {
|
||||
match &self {
|
||||
OptArgIndexKey::Literal(arg_num, ..)
|
||||
| OptArgIndexKey::Structure(arg_num, ..)
|
||||
| OptArgIndexKey::List(arg_num, _) => {
|
||||
// these are always at least 1.
|
||||
*arg_num
|
||||
}
|
||||
OptArgIndexKey::None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_some(&self) -> bool {
|
||||
self.switch_on_term_loc().is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn switch_on_term_loc(&self) -> Option<usize> {
|
||||
match &self {
|
||||
OptArgIndexKey::Literal(_, loc, ..)
|
||||
| OptArgIndexKey::Structure(_, loc, ..)
|
||||
| OptArgIndexKey::List(_, loc) => Some(*loc),
|
||||
OptArgIndexKey::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn set_switch_on_term_loc(&mut self, value: usize) {
|
||||
match self {
|
||||
OptArgIndexKey::Literal(_, ref mut loc, ..)
|
||||
| OptArgIndexKey::Structure(_, ref mut loc, ..)
|
||||
| OptArgIndexKey::List(_, ref mut loc) => {
|
||||
*loc = value;
|
||||
}
|
||||
OptArgIndexKey::None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<usize> for OptArgIndexKey {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, n: usize) {
|
||||
match self {
|
||||
OptArgIndexKey::Literal(_, ref mut o, ..)
|
||||
| OptArgIndexKey::List(_, ref mut o)
|
||||
| OptArgIndexKey::Structure(_, ref mut o, ..) => {
|
||||
*o += n;
|
||||
}
|
||||
OptArgIndexKey::None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ClauseIndexInfo {
|
||||
pub(crate) clause_start: usize,
|
||||
pub(crate) opt_arg_index_key: OptArgIndexKey,
|
||||
}
|
||||
|
||||
impl ClauseIndexInfo {
|
||||
#[inline]
|
||||
pub(crate) fn new(clause_start: usize) -> Self {
|
||||
Self {
|
||||
clause_start,
|
||||
opt_arg_index_key: OptArgIndexKey::None,
|
||||
// index_locs: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct PredicateInfo {
|
||||
pub(crate) is_extensible: bool,
|
||||
pub(crate) is_discontiguous: bool,
|
||||
pub(crate) is_dynamic: bool,
|
||||
pub(crate) is_multifile: bool,
|
||||
pub(crate) has_clauses: bool,
|
||||
}
|
||||
|
||||
impl Default for PredicateInfo {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
PredicateInfo {
|
||||
is_extensible: false,
|
||||
is_discontiguous: false,
|
||||
is_dynamic: false,
|
||||
is_multifile: false,
|
||||
has_clauses: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PredicateInfo {
|
||||
#[inline]
|
||||
pub(crate) fn compile_incrementally(&self) -> bool {
|
||||
let base = self.is_extensible && self.has_clauses;
|
||||
base && (self.is_discontiguous || self.is_multifile)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn must_retract_local_clauses(&self) -> bool {
|
||||
self.is_extensible && self.has_clauses && !self.is_discontiguous
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct LocalPredicateSkeleton {
|
||||
pub(crate) is_discontiguous: bool,
|
||||
pub(crate) is_dynamic: bool,
|
||||
pub(crate) is_multifile: bool,
|
||||
pub(crate) clause_clause_locs: SliceDeque<usize>,
|
||||
pub(crate) clause_assert_margin: usize,
|
||||
pub(crate) retracted_dynamic_clauses: Option<Vec<ClauseIndexInfo>>, // always None if non-dynamic.
|
||||
}
|
||||
|
||||
impl LocalPredicateSkeleton {
|
||||
#[inline]
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
is_discontiguous: false,
|
||||
is_dynamic: false,
|
||||
is_multifile: false,
|
||||
clause_clause_locs: sdeq![],
|
||||
clause_assert_margin: 0,
|
||||
retracted_dynamic_clauses: Some(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn predicate_info(&self) -> PredicateInfo {
|
||||
PredicateInfo {
|
||||
is_extensible: true,
|
||||
is_discontiguous: self.is_discontiguous,
|
||||
is_dynamic: self.is_dynamic,
|
||||
is_multifile: self.is_multifile,
|
||||
has_clauses: !self.clause_clause_locs.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn reset(&mut self) {
|
||||
self.clause_clause_locs.clear();
|
||||
self.clause_assert_margin = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn add_retracted_dynamic_clause_info(&mut self, clause_info: ClauseIndexInfo) {
|
||||
debug_assert_eq!(self.is_dynamic, true);
|
||||
|
||||
if self.retracted_dynamic_clauses.is_none() {
|
||||
self.retracted_dynamic_clauses = Some(vec![]);
|
||||
}
|
||||
|
||||
self.retracted_dynamic_clauses
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.push(clause_info);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct PredicateSkeleton {
|
||||
pub(crate) core: LocalPredicateSkeleton,
|
||||
pub(crate) clauses: SliceDeque<ClauseIndexInfo>,
|
||||
}
|
||||
|
||||
impl PredicateSkeleton {
|
||||
#[inline]
|
||||
pub(crate) fn new() -> Self {
|
||||
PredicateSkeleton {
|
||||
core: LocalPredicateSkeleton::new(),
|
||||
clauses: sdeq![],
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn predicate_info(&self) -> PredicateInfo {
|
||||
PredicateInfo {
|
||||
is_extensible: true,
|
||||
is_discontiguous: self.core.is_discontiguous,
|
||||
is_dynamic: self.core.is_dynamic,
|
||||
is_multifile: self.core.is_multifile,
|
||||
has_clauses: !self.clauses.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn target_pos_of_clause_clause_loc(
|
||||
&self,
|
||||
clause_clause_loc: usize,
|
||||
) -> Option<usize> {
|
||||
let search_result = self.core.clause_clause_locs[0..self.core.clause_assert_margin]
|
||||
.binary_search_by(|loc| clause_clause_loc.cmp(&loc));
|
||||
|
||||
match search_result {
|
||||
Ok(loc) => Some(loc),
|
||||
Err(_) => self.core.clause_clause_locs[self.core.clause_assert_margin..]
|
||||
.binary_search_by(|loc| loc.cmp(&clause_clause_loc))
|
||||
.map(|loc| loc + self.core.clause_assert_margin)
|
||||
.ok(),
|
||||
}
|
||||
}
|
||||
}
|
||||
2593
src/heap_iter.rs
Normal file
2593
src/heap_iter.rs
Normal file
File diff suppressed because it is too large
Load Diff
1837
src/heap_print.rs
Normal file
1837
src/heap_print.rs
Normal file
File diff suppressed because it is too large
Load Diff
1564
src/indexing.rs
Normal file
1564
src/indexing.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
use prolog_parser::ast::*;
|
||||
|
||||
use crate::prolog::clause_types::*;
|
||||
use crate::prolog::forms::*;
|
||||
use crate::prolog::machine::machine_indices::*;
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::machine::machine_indices::*;
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
@@ -12,126 +12,62 @@ use std::rc::Rc;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TermRef<'a> {
|
||||
pub(crate) enum TermRef<'a> {
|
||||
AnonVar(Level),
|
||||
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||
Constant(Level, &'a Cell<RegType>, &'a Constant),
|
||||
Clause(Level, &'a Cell<RegType>, ClauseType, &'a Vec<Box<Term>>),
|
||||
PartialString(Level, &'a Cell<RegType>, String, Option<&'a Term>),
|
||||
Var(Level, &'a Cell<VarReg>, Rc<Var>),
|
||||
Literal(Level, &'a Cell<RegType>, &'a Literal),
|
||||
Clause(Level, &'a Cell<RegType>, ClauseType, &'a Vec<Term>),
|
||||
PartialString(Level, &'a Cell<RegType>, Atom, &'a Option<Box<Term>>),
|
||||
Var(Level, &'a Cell<VarReg>, Rc<String>),
|
||||
}
|
||||
|
||||
impl<'a> TermRef<'a> {
|
||||
pub fn level(self) -> Level {
|
||||
pub(crate) fn level(self) -> Level {
|
||||
match self {
|
||||
TermRef::AnonVar(lvl)
|
||||
| TermRef::Cons(lvl, ..)
|
||||
| TermRef::Constant(lvl, ..)
|
||||
| TermRef::Var(lvl, ..)
|
||||
| TermRef::Clause(lvl, ..) => lvl,
|
||||
| TermRef::PartialString(lvl, ..) => lvl,
|
||||
| TermRef::Cons(lvl, ..)
|
||||
| TermRef::Literal(lvl, ..)
|
||||
| TermRef::Var(lvl, ..)
|
||||
| TermRef::Clause(lvl, ..) => lvl,
|
||||
TermRef::PartialString(lvl, ..) => lvl,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TermIterState<'a> {
|
||||
pub(crate) enum TermIterState<'a> {
|
||||
AnonVar(Level),
|
||||
Constant(Level, &'a Cell<RegType>, &'a Constant),
|
||||
Clause(
|
||||
Level,
|
||||
usize,
|
||||
&'a Cell<RegType>,
|
||||
ClauseType,
|
||||
&'a Vec<Box<Term>>,
|
||||
),
|
||||
Literal(Level, &'a Cell<RegType>, &'a Literal),
|
||||
Clause(Level, usize, &'a Cell<RegType>, ClauseType, &'a Vec<Term>),
|
||||
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||
FinalCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||
PartialString(Level, &'a Cell<RegType>, String, Option<&'a Term>),
|
||||
Var(Level, &'a Cell<VarReg>, Rc<Var>),
|
||||
}
|
||||
|
||||
fn is_partial_string<'a>(
|
||||
head: &'a Term,
|
||||
mut tail: &'a Term,
|
||||
) -> Option<(String, Option<&'a Term>)>
|
||||
{
|
||||
let mut string =
|
||||
match head {
|
||||
&Term::Constant(_, Constant::Atom(ref atom, _)) if atom.is_char() => {
|
||||
atom.as_str().chars().next().unwrap().to_string()
|
||||
}
|
||||
&Term::Constant(_, Constant::Char(c)) => {
|
||||
c.to_string()
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
while let Term::Cons(_, ref head, ref succ) = tail {
|
||||
match head.as_ref() {
|
||||
&Term::Constant(_, Constant::Atom(ref atom, _)) if atom.is_char() => {
|
||||
string.push(atom.as_str().chars().next().unwrap());
|
||||
}
|
||||
&Term::Constant(_, Constant::Char(c)) => {
|
||||
string.push(c);
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
tail = succ.as_ref();
|
||||
}
|
||||
|
||||
match tail {
|
||||
Term::AnonVar | Term::Var(..) => {
|
||||
return Some((string, Some(tail)));
|
||||
}
|
||||
Term::Constant(_, Constant::EmptyList) => {
|
||||
return Some((string, None));
|
||||
}
|
||||
Term::Constant(_, Constant::String(tail)) => {
|
||||
string += &tail;
|
||||
return Some((string, None));
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
InitialPartialString(Level, &'a Cell<RegType>, Atom, &'a Option<Box<Term>>),
|
||||
FinalPartialString(Level, &'a Cell<RegType>, Atom, &'a Option<Box<Term>>),
|
||||
Var(Level, &'a Cell<VarReg>, Rc<String>),
|
||||
}
|
||||
|
||||
impl<'a> TermIterState<'a> {
|
||||
pub fn subterm_to_state(lvl: Level, term: &'a Term) -> TermIterState<'a> {
|
||||
pub(crate) fn subterm_to_state(lvl: Level, term: &'a Term) -> TermIterState<'a> {
|
||||
match term {
|
||||
&Term::AnonVar => {
|
||||
TermIterState::AnonVar(lvl)
|
||||
}
|
||||
&Term::Clause(ref cell, ref name, ref subterms, ref spec) => {
|
||||
let ct = if let Some(spec) = spec {
|
||||
ClauseType::Op(name.clone(), spec.clone(), CodeIndex::default())
|
||||
} else {
|
||||
ClauseType::Named(name.clone(), subterms.len(), CodeIndex::default())
|
||||
};
|
||||
|
||||
Term::AnonVar => TermIterState::AnonVar(lvl),
|
||||
Term::Clause(cell, name, subterms) => {
|
||||
let ct = ClauseType::Named(subterms.len(), *name, CodeIndex::default());
|
||||
TermIterState::Clause(lvl, 0, cell, ct, subterms)
|
||||
}
|
||||
&Term::Cons(ref cell, ref head, ref tail) => {
|
||||
Term::Cons(cell, head, tail) => {
|
||||
TermIterState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref())
|
||||
}
|
||||
&Term::Constant(ref cell, ref constant) => {
|
||||
TermIterState::Constant(lvl, cell, constant)
|
||||
}
|
||||
&Term::Var(ref cell, ref var) => {
|
||||
TermIterState::Var(lvl, cell, var.clone())
|
||||
Term::Literal(cell, constant) => TermIterState::Literal(lvl, cell, constant),
|
||||
Term::PartialString(cell, string_buf, tail) => {
|
||||
TermIterState::InitialPartialString(lvl, cell, *string_buf, tail)
|
||||
}
|
||||
Term::Var(cell, var) => TermIterState::Var(lvl, cell, var.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QueryIterator<'a> {
|
||||
pub(crate) struct QueryIterator<'a> {
|
||||
state_stack: Vec<TermIterState<'a>>,
|
||||
}
|
||||
|
||||
@@ -141,11 +77,11 @@ impl<'a> QueryIterator<'a> {
|
||||
.push(TermIterState::subterm_to_state(lvl, term));
|
||||
}
|
||||
|
||||
fn from_rule_head_clause(terms: &'a Vec<Box<Term>>) -> Self {
|
||||
fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
|
||||
let state_stack = terms
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt.as_ref()))
|
||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||
.collect();
|
||||
|
||||
QueryIterator { state_stack }
|
||||
@@ -153,30 +89,19 @@ impl<'a> QueryIterator<'a> {
|
||||
|
||||
fn from_term(term: &'a Term) -> Self {
|
||||
let state = match term {
|
||||
&Term::AnonVar => {
|
||||
Term::AnonVar | Term::Cons(..) | Term::Literal(..) | Term::PartialString(..) => {
|
||||
return QueryIterator {
|
||||
state_stack: vec![],
|
||||
}
|
||||
}
|
||||
&Term::Clause(ref r, ref name, ref terms, ref fixity) => TermIterState::Clause(
|
||||
Term::Clause(r, name, terms) => TermIterState::Clause(
|
||||
Level::Root,
|
||||
0,
|
||||
r,
|
||||
ClauseType::from(name.clone(), terms.len(), fixity.clone()),
|
||||
ClauseType::from(*name, terms.len()),
|
||||
terms,
|
||||
),
|
||||
&Term::Cons(..) => {
|
||||
return QueryIterator {
|
||||
state_stack: vec![],
|
||||
}
|
||||
}
|
||||
&Term::Constant(_, _) => {
|
||||
return QueryIterator {
|
||||
state_stack: vec![],
|
||||
}
|
||||
}
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
TermIterState::Var(Level::Root, cell, (*var).clone()),
|
||||
Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, var.clone()),
|
||||
};
|
||||
|
||||
QueryIterator {
|
||||
@@ -186,8 +111,8 @@ impl<'a> QueryIterator<'a> {
|
||||
|
||||
fn new(term: &'a QueryTerm) -> Self {
|
||||
match term {
|
||||
&QueryTerm::Clause(ref cell, ClauseType::CallN, ref terms, _) => {
|
||||
let state = TermIterState::Clause(Level::Root, 1, cell, ClauseType::CallN, terms);
|
||||
&QueryTerm::Clause(ref cell, ClauseType::CallN(arity), ref terms, _) => {
|
||||
let state = TermIterState::Clause(Level::Root, 1, cell, ClauseType::CallN(arity), terms);
|
||||
QueryIterator {
|
||||
state_stack: vec![state],
|
||||
}
|
||||
@@ -199,7 +124,7 @@ impl<'a> QueryIterator<'a> {
|
||||
}
|
||||
}
|
||||
&QueryTerm::UnblockedCut(ref cell) => {
|
||||
let state = TermIterState::Var(Level::Root, cell, rc_atom!("!"));
|
||||
let state = TermIterState::Var(Level::Root, cell, Rc::new("!".to_string()));
|
||||
QueryIterator {
|
||||
state_stack: vec![state],
|
||||
}
|
||||
@@ -238,10 +163,10 @@ impl<'a> Iterator for QueryIterator<'a> {
|
||||
TermIterState::Clause(lvl, child_num, cell, ct, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
match ct {
|
||||
ClauseType::CallN => {
|
||||
self.push_subterm(Level::Shallow, child_terms[0].as_ref())
|
||||
ClauseType::CallN(_) => {
|
||||
self.push_subterm(Level::Shallow, &child_terms[0]);
|
||||
}
|
||||
ClauseType::Named(..) | ClauseType::Op(..) => {
|
||||
ClauseType::Named(..) => {
|
||||
return match lvl {
|
||||
Level::Root => None,
|
||||
lvl => Some(TermRef::Clause(lvl, cell, ct, child_terms)),
|
||||
@@ -260,36 +185,30 @@ impl<'a> Iterator for QueryIterator<'a> {
|
||||
child_terms,
|
||||
));
|
||||
|
||||
self.push_subterm(lvl.child_level(), child_terms[child_num].as_ref());
|
||||
self.push_subterm(lvl.child_level(), &child_terms[child_num]);
|
||||
}
|
||||
}
|
||||
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
||||
if let Some((string, tail)) = is_partial_string(head, tail) {
|
||||
self.state_stack.push(TermIterState::PartialString(
|
||||
lvl,
|
||||
cell,
|
||||
string,
|
||||
tail,
|
||||
));
|
||||
self.state_stack.push(TermIterState::FinalCons(lvl, cell, head, tail));
|
||||
|
||||
if let Some(tail) = tail {
|
||||
self.push_subterm(lvl.child_level(), tail);
|
||||
}
|
||||
} else {
|
||||
self.state_stack.push(TermIterState::FinalCons(lvl, cell, head, tail));
|
||||
self.push_subterm(lvl.child_level(), tail);
|
||||
self.push_subterm(lvl.child_level(), head);
|
||||
}
|
||||
TermIterState::InitialPartialString(lvl, cell, string, tail) => {
|
||||
self.state_stack.push(TermIterState::FinalPartialString(lvl, cell, string, tail));
|
||||
|
||||
if let Some(tail) = tail {
|
||||
self.push_subterm(lvl.child_level(), tail);
|
||||
self.push_subterm(lvl.child_level(), head);
|
||||
}
|
||||
}
|
||||
TermIterState::PartialString(lvl, cell, string, tail) => {
|
||||
TermIterState::FinalPartialString(lvl, cell, string, tail) => {
|
||||
return Some(TermRef::PartialString(lvl, cell, string, tail));
|
||||
}
|
||||
TermIterState::FinalCons(lvl, cell, head, tail) => {
|
||||
return Some(TermRef::Cons(lvl, cell, head, tail));
|
||||
}
|
||||
TermIterState::Constant(lvl, cell, constant) => {
|
||||
return Some(TermRef::Constant(lvl, cell, constant));
|
||||
TermIterState::Literal(lvl, cell, constant) => {
|
||||
return Some(TermRef::Literal(lvl, cell, constant));
|
||||
}
|
||||
TermIterState::Var(lvl, cell, var) => {
|
||||
return Some(TermRef::Var(lvl, cell, var));
|
||||
@@ -302,20 +221,21 @@ impl<'a> Iterator for QueryIterator<'a> {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FactIterator<'a> {
|
||||
pub(crate) struct FactIterator<'a> {
|
||||
state_queue: VecDeque<TermIterState<'a>>,
|
||||
iterable_root: bool,
|
||||
}
|
||||
|
||||
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));
|
||||
self.state_queue
|
||||
.push_back(TermIterState::subterm_to_state(lvl, term));
|
||||
}
|
||||
|
||||
pub fn from_rule_head_clause(terms: &'a Vec<Box<Term>>) -> Self {
|
||||
pub(crate) fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
|
||||
let state_queue = terms
|
||||
.iter()
|
||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt.as_ref()))
|
||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||
.collect();
|
||||
|
||||
FactIterator {
|
||||
@@ -326,23 +246,31 @@ impl<'a> FactIterator<'a> {
|
||||
|
||||
fn new(term: &'a Term, iterable_root: bool) -> Self {
|
||||
let states = match term {
|
||||
&Term::AnonVar => {
|
||||
Term::AnonVar => {
|
||||
vec![TermIterState::AnonVar(Level::Root)]
|
||||
}
|
||||
&Term::Clause(ref cell, ref name, ref terms, ref fixity) => {
|
||||
let ct = ClauseType::from(name.clone(), terms.len(), fixity.clone());
|
||||
Term::Clause(cell, name, terms) => {
|
||||
let ct = ClauseType::from(*name, terms.len());
|
||||
vec![TermIterState::Clause(Level::Root, 0, cell, ct, terms)]
|
||||
}
|
||||
&Term::Cons(ref cell, ref head, ref tail) => vec![TermIterState::InitialCons(
|
||||
Term::Cons(cell, head, tail) => vec![TermIterState::InitialCons(
|
||||
Level::Root,
|
||||
cell,
|
||||
head.as_ref(),
|
||||
tail.as_ref(),
|
||||
)],
|
||||
&Term::Constant(ref cell, ref constant) => {
|
||||
vec![TermIterState::Constant(Level::Root, cell, constant)]
|
||||
Term::PartialString(cell, string_buf, tail_opt) => {
|
||||
vec![TermIterState::InitialPartialString(
|
||||
Level::Root,
|
||||
cell,
|
||||
*string_buf,
|
||||
tail_opt,
|
||||
)]
|
||||
}
|
||||
&Term::Var(ref cell, ref var) => {
|
||||
Term::Literal(cell, constant) => {
|
||||
vec![TermIterState::Literal(Level::Root, cell, constant)]
|
||||
}
|
||||
Term::Var(cell, var) => {
|
||||
vec![TermIterState::Var(Level::Root, cell, var.clone())]
|
||||
}
|
||||
};
|
||||
@@ -374,27 +302,25 @@ impl<'a> Iterator for FactIterator<'a> {
|
||||
};
|
||||
}
|
||||
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
||||
if let Some((string, tail)) = is_partial_string(head, tail) {
|
||||
if let Some(tail) = tail {
|
||||
self.push_subterm(Level::Deep, tail);
|
||||
}
|
||||
self.push_subterm(Level::Deep, head);
|
||||
self.push_subterm(Level::Deep, tail);
|
||||
|
||||
return Some(TermRef::PartialString(lvl, cell, string, tail));
|
||||
} else {
|
||||
self.push_subterm(Level::Deep, head);
|
||||
self.push_subterm(Level::Deep, tail);
|
||||
|
||||
return Some(TermRef::Cons(lvl, cell, head, tail));
|
||||
}
|
||||
return Some(TermRef::Cons(lvl, cell, head, tail));
|
||||
}
|
||||
TermIterState::Constant(lvl, cell, constant) => {
|
||||
return Some(TermRef::Constant(lvl, cell, constant))
|
||||
TermIterState::InitialPartialString(lvl, cell, string_buf, tail_opt) => {
|
||||
if let Some(tail) = tail_opt {
|
||||
self.push_subterm(Level::Deep, tail);
|
||||
}
|
||||
|
||||
return Some(TermRef::PartialString(lvl, cell, string_buf, tail_opt));
|
||||
}
|
||||
TermIterState::Literal(lvl, cell, constant) => {
|
||||
return Some(TermRef::Literal(lvl, cell, constant))
|
||||
}
|
||||
TermIterState::Var(lvl, cell, var) => {
|
||||
return Some(TermRef::Var(lvl, cell, var));
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,28 +328,28 @@ impl<'a> Iterator for FactIterator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn post_order_iter(term: &Term) -> QueryIterator {
|
||||
pub(crate) fn post_order_iter<'a>(term: &'a Term) -> QueryIterator<'a> {
|
||||
QueryIterator::from_term(term)
|
||||
}
|
||||
|
||||
pub fn breadth_first_iter(term: &Term, iterable_root: bool) -> FactIterator {
|
||||
pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: bool) -> FactIterator<'a> {
|
||||
FactIterator::new(term, iterable_root)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ChunkedTerm<'a> {
|
||||
HeadClause(ClauseName, &'a Vec<Box<Term>>),
|
||||
pub(crate) enum ChunkedTerm<'a> {
|
||||
HeadClause(Atom, &'a Vec<Term>),
|
||||
BodyTerm(&'a QueryTerm),
|
||||
}
|
||||
|
||||
pub fn query_term_post_order_iter<'a>(query_term: &'a QueryTerm) -> QueryIterator<'a> {
|
||||
pub(crate) fn query_term_post_order_iter<'a>(query_term: &'a QueryTerm) -> QueryIterator<'a> {
|
||||
QueryIterator::new(query_term)
|
||||
}
|
||||
|
||||
impl<'a> ChunkedTerm<'a> {
|
||||
pub fn post_order_iter(&self) -> QueryIterator<'a> {
|
||||
pub(crate) fn post_order_iter(&self) -> QueryIterator<'a> {
|
||||
match self {
|
||||
&ChunkedTerm::BodyTerm(ref qt) => QueryIterator::new(qt),
|
||||
&ChunkedTerm::BodyTerm(qt) => QueryIterator::new(qt),
|
||||
&ChunkedTerm::HeadClause(_, terms) => QueryIterator::from_rule_head_clause(terms),
|
||||
}
|
||||
}
|
||||
@@ -441,8 +367,8 @@ fn contains_cut_var<'a, Iter: Iterator<Item = &'a Term>>(terms: Iter) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub struct ChunkedIterator<'a> {
|
||||
pub chunk_num: usize,
|
||||
pub(crate) struct ChunkedIterator<'a> {
|
||||
pub(crate) chunk_num: usize,
|
||||
iter: Box<dyn Iterator<Item = ChunkedTerm<'a>> + 'a>,
|
||||
deep_cut_encountered: bool,
|
||||
cut_var_in_head: bool,
|
||||
@@ -464,7 +390,7 @@ type ChunkedIteratorItem<'a> = (usize, usize, Vec<ChunkedTerm<'a>>);
|
||||
type RuleBodyIteratorItem<'a> = (usize, usize, Vec<&'a QueryTerm>);
|
||||
|
||||
impl<'a> ChunkedIterator<'a> {
|
||||
pub fn rule_body_iter(self) -> Box<dyn Iterator<Item = RuleBodyIteratorItem<'a>> + 'a> {
|
||||
pub(crate) fn rule_body_iter(self) -> Box<dyn Iterator<Item = RuleBodyIteratorItem<'a>> + 'a> {
|
||||
Box::new(self.filter_map(|(cn, lt_arity, terms)| {
|
||||
let filtered_terms: Vec<_> = terms
|
||||
.into_iter()
|
||||
@@ -481,17 +407,17 @@ impl<'a> ChunkedIterator<'a> {
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn from_term_sequence(terms: &'a [QueryTerm]) -> Self {
|
||||
ChunkedIterator {
|
||||
chunk_num: 0,
|
||||
iter: Box::new(terms.iter().map(|t| ChunkedTerm::BodyTerm(t))),
|
||||
deep_cut_encountered: false,
|
||||
cut_var_in_head: false,
|
||||
/*
|
||||
pub(crate) fn from_term_sequence(terms: &'a [QueryTerm]) -> Self {
|
||||
ChunkedIterator {
|
||||
chunk_num: 0,
|
||||
iter: Box::new(terms.iter().map(|t| ChunkedTerm::BodyTerm(t))),
|
||||
deep_cut_encountered: false,
|
||||
cut_var_in_head: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_rule_body(p1: &'a QueryTerm, clauses: &'a Vec<QueryTerm>) -> Self {
|
||||
*/
|
||||
pub(crate) fn from_rule_body(p1: &'a QueryTerm, clauses: &'a Vec<QueryTerm>) -> Self {
|
||||
let inner_iter = Box::new(once(ChunkedTerm::BodyTerm(p1)));
|
||||
let iter = inner_iter.chain(clauses.iter().map(|t| ChunkedTerm::BodyTerm(t)));
|
||||
|
||||
@@ -503,7 +429,7 @@ impl<'a> ChunkedIterator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_rule(rule: &'a Rule) -> Self {
|
||||
pub(crate) fn from_rule(rule: &'a Rule) -> Self {
|
||||
let &Rule {
|
||||
head: (ref name, ref args, ref p1),
|
||||
ref clauses,
|
||||
@@ -521,7 +447,7 @@ impl<'a> ChunkedIterator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encountered_deep_cut(&self) -> bool {
|
||||
pub(crate) fn encountered_deep_cut(&self) -> bool {
|
||||
self.deep_cut_encountered
|
||||
}
|
||||
|
||||
@@ -533,7 +459,7 @@ impl<'a> ChunkedIterator<'a> {
|
||||
while let Some(term) = item {
|
||||
match term {
|
||||
ChunkedTerm::HeadClause(_, terms) => {
|
||||
if contains_cut_var(terms.iter().map(|t| t.as_ref())) {
|
||||
if contains_cut_var(terms.iter()) {
|
||||
self.cut_var_in_head = true;
|
||||
}
|
||||
|
||||
@@ -563,13 +489,16 @@ impl<'a> ChunkedIterator<'a> {
|
||||
arity = 1;
|
||||
break;
|
||||
}
|
||||
ChunkedTerm::BodyTerm(&QueryTerm::UnblockedCut(..)) => result.push(term),
|
||||
ChunkedTerm::BodyTerm(&QueryTerm::UnblockedCut(..)) => {
|
||||
self.deep_cut_encountered = true;
|
||||
result.push(term);
|
||||
}
|
||||
ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => {
|
||||
result.push(term)
|
||||
}
|
||||
ChunkedTerm::BodyTerm(&QueryTerm::Clause(
|
||||
_,
|
||||
ClauseType::CallN,
|
||||
ClauseType::CallN(_),
|
||||
ref subterms,
|
||||
_,
|
||||
)) => {
|
||||
34
src/lib.rs
Normal file
34
src/lib.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
#![recursion_limit = "4112"]
|
||||
|
||||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
#[macro_use]
|
||||
pub mod atom_table;
|
||||
#[macro_use]
|
||||
pub mod arena;
|
||||
#[macro_use]
|
||||
pub mod parser;
|
||||
mod allocator;
|
||||
mod arithmetic;
|
||||
pub mod codegen;
|
||||
mod debray_allocator;
|
||||
mod fixtures;
|
||||
mod forms;
|
||||
mod heap_iter;
|
||||
pub mod heap_print;
|
||||
mod indexing;
|
||||
#[macro_use]
|
||||
pub mod instructions {
|
||||
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
|
||||
}
|
||||
mod iterators;
|
||||
pub mod machine;
|
||||
mod raw_block;
|
||||
pub mod read;
|
||||
mod targets;
|
||||
pub mod types;
|
||||
|
||||
use instructions::instr;
|
||||
@@ -1,5 +1,5 @@
|
||||
:- module(arithmetic, [expmod/4, lsb/2, msb/2, number_to_rational/2,
|
||||
number_to_rational/3,
|
||||
number_to_rational/3, popcount/2,
|
||||
rational_numerator_denominator/3]).
|
||||
|
||||
:- use_module(library(charsio), [write_term_to_chars/3]).
|
||||
@@ -94,12 +94,6 @@ number_to_rational(Eps0, Real0, Fraction) :-
|
||||
),
|
||||
!.
|
||||
|
||||
number(X) :-
|
||||
( integer(X)
|
||||
; float(X)
|
||||
; rational(X)
|
||||
).
|
||||
|
||||
stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, C/D, Fraction) :-
|
||||
Fn1 is A + C,
|
||||
Fd1 is B + D,
|
||||
@@ -121,3 +115,7 @@ rational_numerator_denominator(R, N, D) :-
|
||||
append(Ns, [' ', r, d, i, v, ' '|Ds], Cs),
|
||||
number_chars(N, Ns),
|
||||
number_chars(D, Ds).
|
||||
|
||||
popcount(X, N) :-
|
||||
must_be(integer, X),
|
||||
'$popcount'(X, N).
|
||||
@@ -63,11 +63,8 @@ Assocs are Key-Value associations implemented as a balanced binary tree
|
||||
@author R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
|
||||
*/
|
||||
|
||||
/*
|
||||
:- meta_predicate
|
||||
map_assoc(1, ?),
|
||||
map_assoc(2, ?, ?).
|
||||
*/
|
||||
:- meta_predicate map_assoc(1, ?).
|
||||
:- meta_predicate map_assoc(2, ?, ?).
|
||||
|
||||
%! empty_assoc(?Assoc) is semidet.
|
||||
%
|
||||
196
src/lib/atts.pl
Normal file
196
src/lib/atts.pl
Normal file
@@ -0,0 +1,196 @@
|
||||
:- module(atts, [op(1199, fx, attribute),
|
||||
call_residue_vars/2,
|
||||
term_attributed_variables/2]).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- 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, Attr) :-
|
||||
'$get_attr_list'(V, Ls),
|
||||
'$absent_from_list'(Ls, Attr).
|
||||
|
||||
'$absent_from_list'(X, Attr) :-
|
||||
( var(X) ->
|
||||
true
|
||||
; X = [L|Ls],
|
||||
L \= Attr ->
|
||||
'$absent_from_list'(Ls, Attr)
|
||||
).
|
||||
|
||||
'$get_attr'(V, Attr) :-
|
||||
'$get_attr_list'(V, Ls),
|
||||
nonvar(Ls),
|
||||
'$get_from_list'(Ls, V, Attr).
|
||||
|
||||
'$get_from_list'([L|Ls], V, Attr) :-
|
||||
nonvar(L),
|
||||
( L \= Attr ->
|
||||
nonvar(Ls),
|
||||
'$get_from_list'(Ls, V, Attr)
|
||||
; L = Attr,
|
||||
'$enqueue_attr_var'(V)
|
||||
).
|
||||
|
||||
'$put_attr'(V, Attr) :-
|
||||
'$get_attr_list'(V, Ls),
|
||||
'$add_to_list'(Ls, V, Attr).
|
||||
|
||||
'$add_to_list'(Ls, V, Attr) :-
|
||||
( var(Ls) ->
|
||||
Ls = [Attr | _],
|
||||
'$enqueue_attr_var'(V)
|
||||
; Ls = [_ | Ls0],
|
||||
'$add_to_list'(Ls0, V, Attr)
|
||||
).
|
||||
|
||||
'$del_attr'(Ls0, _, _) :-
|
||||
var(Ls0),
|
||||
!.
|
||||
'$del_attr'(Ls0, V, Attr) :-
|
||||
Ls0 = [Att | Ls1],
|
||||
nonvar(Att),
|
||||
( Att \= Attr ->
|
||||
'$del_attr_buried'(Ls0, Ls1, V, Attr)
|
||||
; '$enqueue_attr_var'(V),
|
||||
'$del_attr_head'(V),
|
||||
'$del_attr'(Ls1, V, Attr)
|
||||
).
|
||||
|
||||
'$del_attr_step'(Ls1, V, Attr) :-
|
||||
( nonvar(Ls1) ->
|
||||
Ls1 = [_ | Ls2],
|
||||
'$del_attr_buried'(Ls1, Ls2, V, Attr)
|
||||
; true
|
||||
).
|
||||
|
||||
%% assumptions: Ls0 is a list, Ls1 is its tail;
|
||||
%% the head of Ls0 can be ignored.
|
||||
'$del_attr_buried'(Ls0, Ls1, V, Attr) :-
|
||||
( var(Ls1) -> true
|
||||
; Ls1 = [Att | Ls2] ->
|
||||
( Att \= Attr ->
|
||||
'$del_attr_buried'(Ls1, Ls2, V, Attr)
|
||||
; '$enqueue_attr_var'(V),
|
||||
'$del_attr_non_head'(Ls0), %% set tail of Ls0 = tail of Ls1. can be undone by backtracking.
|
||||
'$del_attr_step'(Ls1, V, Attr)
|
||||
)
|
||||
).
|
||||
|
||||
'$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) :-
|
||||
!,
|
||||
functor(Attr, Head, Arity),
|
||||
functor(AttrForm, Head, Arity),
|
||||
'$get_attr_list'(V, Ls),
|
||||
atts:'$del_attr'(Ls, V, Module:AttrForm),
|
||||
atts:'$put_attr'(V, Module:Attr)),
|
||||
(put_atts(V, Attr) :-
|
||||
!,
|
||||
functor(Attr, Head, Arity),
|
||||
functor(AttrForm, Head, Arity),
|
||||
'$get_attr_list'(V, Ls),
|
||||
atts:'$del_attr'(Ls, V, Module:AttrForm),
|
||||
atts:'$put_attr'(V, Module:Attr)),
|
||||
(put_atts(V, -Attr) :-
|
||||
!,
|
||||
functor(Attr, _, _),
|
||||
'$get_attr_list'(V, Ls),
|
||||
atts:'$del_attr'(Ls, V, Module:Attr))].
|
||||
|
||||
get_attr(Name, Arity, Module) -->
|
||||
{ functor(Attr, Name, Arity) },
|
||||
[(get_atts(V, +Attr) :-
|
||||
!,
|
||||
functor(Attr, _, _),
|
||||
atts:'$get_attr'(V, Module:Attr)),
|
||||
(get_atts(V, Attr) :-
|
||||
!,
|
||||
functor(Attr, _, _),
|
||||
atts:'$get_attr'(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).
|
||||
|
||||
:- meta_predicate call_residue_vars(0, ?).
|
||||
|
||||
call_residue_vars(Goal, Vars) :-
|
||||
'$get_attr_var_queue_delim'(B),
|
||||
call(Goal),
|
||||
'$get_attr_var_queue_beyond'(B, Vars).
|
||||
|
||||
term_attributed_variables(Term, Vars) :-
|
||||
'$term_attributed_variables'(Term, Vars).
|
||||
@@ -12,15 +12,18 @@ between(Lower, Upper, X) :-
|
||||
( nonvar(X) ->
|
||||
Lower =< X,
|
||||
X =< Upper
|
||||
; between_(Lower, Upper, X)
|
||||
; Lower =< Upper,
|
||||
between_(Lower, Upper, X)
|
||||
).
|
||||
|
||||
between_(Lower, Upper, Lower) :-
|
||||
Lower =< Upper.
|
||||
between_(Lower1, Upper, X) :-
|
||||
Lower1 < Upper,
|
||||
Lower2 is Lower1 + 1,
|
||||
between_(Lower2, Upper, X).
|
||||
between_(Lower, Upper, Lower1) :-
|
||||
Lower < Upper,
|
||||
!,
|
||||
( Lower1 = Lower
|
||||
; Lower0 is Lower + 1,
|
||||
between_(Lower0, Upper, Lower1)
|
||||
).
|
||||
between_(Lower, Lower, Lower).
|
||||
|
||||
enumerate_nats(I, I).
|
||||
enumerate_nats(I0, N) :-
|
||||
1699
src/lib/builtins.pl
Normal file
1699
src/lib/builtins.pl
Normal file
File diff suppressed because it is too large
Load Diff
257
src/lib/charsio.pl
Normal file
257
src/lib/charsio.pl
Normal file
@@ -0,0 +1,257 @@
|
||||
:- module(charsio, [char_type/2,
|
||||
chars_utf8bytes/2,
|
||||
get_single_char/1,
|
||||
get_n_chars/3,
|
||||
read_line_to_chars/3,
|
||||
read_from_chars/2,
|
||||
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(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) :-
|
||||
must_be(character, Char),
|
||||
( ground(Type) ->
|
||||
( ctype(Type) ->
|
||||
'$char_type'(Char, Type)
|
||||
; domain_error(char_type, Type, char_type/2)
|
||||
)
|
||||
; ctype(Type),
|
||||
'$char_type'(Char, Type)
|
||||
).
|
||||
|
||||
|
||||
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(upper).
|
||||
ctype(whitespace).
|
||||
|
||||
|
||||
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) :-
|
||||
must_be(chars, Chars),
|
||||
'$read_term_from_chars'(Chars, Term).
|
||||
|
||||
|
||||
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,
|
||||
[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).
|
||||
|
||||
% 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(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).
|
||||
|
||||
|
||||
read_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
|
||||
; read_line_to_chars(Stream, Rest, Cs)
|
||||
)
|
||||
).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Read N characters from Stream.
|
||||
|
||||
If N is a variable, read until EOF, unifying N with the number of
|
||||
characters read.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
get_n_chars(Stream, N, Cs) :-
|
||||
can_be(integer, N),
|
||||
( var(N) ->
|
||||
read_to_eof(Stream, Cs),
|
||||
length(Cs, N)
|
||||
; N >= 0,
|
||||
'$get_n_chars'(Stream, N, Cs)
|
||||
).
|
||||
|
||||
read_to_eof(Stream, Cs) :-
|
||||
'$get_n_chars'(Stream, 512, Cs0),
|
||||
( Cs0 == [] -> Cs = []
|
||||
; partial_string(Cs0, Cs, Rest),
|
||||
read_to_eof(Stream, Rest)
|
||||
).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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(byte_char, N, chars_base64/3)
|
||||
; '$chars_base64'(Cs, Bs, Padding, Charset)
|
||||
)
|
||||
).
|
||||
@@ -17,8 +17,8 @@
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(clpb, [op(300, fy, ~),
|
||||
op(500, yfx, #),
|
||||
sat/1,
|
||||
op(500, yfx, #),
|
||||
sat/1,
|
||||
taut/2,
|
||||
labeling/1,
|
||||
sat_count/2,
|
||||
@@ -49,17 +49,6 @@
|
||||
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(acyclic, Where, Term) :- !,
|
||||
@@ -842,9 +831,8 @@ verify_attributes(Var, Other, Gs) :-
|
||||
( integer(Other) ->
|
||||
( between(0, 1, Other) ->
|
||||
root_get_formula_bdd(Root, Sat, BDD0),
|
||||
bdd_restriction(BDD0, I, Other, BDD),
|
||||
root_put_formula_bdd(Root, Sat, BDD),
|
||||
Gs = [satisfiable_bdd(BDD)]
|
||||
Gs = [bdd_restriction(BDD0,I,Other,BDD),satisfiable_bdd(BDD)]
|
||||
; no_truth_value(Other)
|
||||
)
|
||||
; atom(Other) ->
|
||||
@@ -1288,13 +1276,18 @@ weighted_maximum(Ws, Vars, Max) :-
|
||||
maplist(var_with_index, Vars, IVs),
|
||||
pairs_keys_values(Pairs0, IVs, Ws),
|
||||
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 is a list of Var-Weight terms, in index order of Vars
|
||||
pairs_keys_values(Pairs, VarsIndexOrder, WeightsIndexOrder),
|
||||
bdd_maximum(BDD, Pairs, Max),
|
||||
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(node(_,Var,Low,High,Aux), Pairs0) :-
|
||||
max_upto(Pairs0, Var, Pairs),
|
||||
@@ -1529,8 +1522,8 @@ pairs_([], _) --> [].
|
||||
pairs_([B|Bs], A) --> [A-B], pairs_(Bs, A).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Set the Prolog flag clpb_residuals to bdd to obtain the BDD nodes
|
||||
as residuals. Note that they cannot be used as regular goals.
|
||||
Assert clpb:clpb_residuals(bdd) to obtain the BDD nodes as
|
||||
residuals. Note that they cannot be used as regular goals.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
nodes([]) --> [].
|
||||
@@ -1558,9 +1551,10 @@ sats([]) --> [].
|
||||
sats([A|As]) --> [clpb:sat(A)], sats(As).
|
||||
|
||||
booleans([]) --> [].
|
||||
booleans([B|Bs]) --> boolean(B), { del_clpb(B) }, booleans(Bs).
|
||||
booleans([B|Bs]) --> boolean(B), booleans(Bs).
|
||||
|
||||
boolean(Var) -->
|
||||
{ del_clpb(Var) },
|
||||
( { get_attr(Var, clpb_omit_boolean, true) } -> []
|
||||
; [clpb:sat(Var =:= Var)]
|
||||
).
|
||||
@@ -3,7 +3,7 @@
|
||||
Author: Markus Triska
|
||||
E-mail: triska@metalevel.at
|
||||
WWW: https://www.metalevel.at
|
||||
Copyright (C): 2016-2020 Markus Triska
|
||||
Copyright (C): 2016-2021 Markus Triska
|
||||
|
||||
This library provides CLP(ℤ):
|
||||
|
||||
@@ -99,7 +99,11 @@
|
||||
fd_inf/2,
|
||||
fd_sup/2,
|
||||
fd_size/2,
|
||||
fd_dom/2
|
||||
fd_dom/2,
|
||||
|
||||
% for use in predicates from library(reif)
|
||||
(#=)/3,
|
||||
(#<)/3
|
||||
|
||||
% called from goal_expansion
|
||||
% clpz_equal/2,
|
||||
@@ -118,6 +122,7 @@
|
||||
:- use_module(library(error), [domain_error/3, type_error/3]).
|
||||
:- use_module(library(si)).
|
||||
:- use_module(library(freeze)).
|
||||
:- use_module(library(arithmetic)).
|
||||
|
||||
% :- use_module(library(types)).
|
||||
|
||||
@@ -310,7 +315,7 @@ possible.
|
||||
Almost all Prolog programs also reason about integers. Therefore, it
|
||||
is highly advisable that you make CLP(ℤ) constraints available in all
|
||||
your programs. One way to do this is to put the following directive in
|
||||
your =|~/.swiplrc|= initialisation file:
|
||||
your =|~/.scryerrc|= initialisation file:
|
||||
|
||||
==
|
||||
:- use_module(library(clpz)).
|
||||
@@ -400,7 +405,7 @@ The [_arithmetic constraints_](<#clpz-arith-constraints>) #=/2, #>/2
|
||||
etc. are meant to be used _instead_ of the primitives `(is)/2`,
|
||||
`(=:=)/2`, `(>)/2` etc. over integers. Almost all Prolog programs also
|
||||
reason about integers. Therefore, it is recommended that you put the
|
||||
following directive in your =|~/.swiplrc|= initialisation file to make
|
||||
following directive in your =|~/.scryerrc|= initialisation file to make
|
||||
CLP(ℤ) constraints available in all your programs:
|
||||
|
||||
==
|
||||
@@ -1898,12 +1903,6 @@ label([], _, Selection, Order, Choice, Optim0, Consistency, Vars) :-
|
||||
retractall(extremum(_)))
|
||||
).
|
||||
|
||||
retractall(What) :-
|
||||
( \+ \+ retract(What) ->
|
||||
retractall(What)
|
||||
; true
|
||||
).
|
||||
|
||||
% Introduce new variables for each min/max expression to avoid
|
||||
% reparsing expressions during optimisation.
|
||||
|
||||
@@ -2569,12 +2568,12 @@ parse_clpz(E, R,
|
||||
m(\A) => [p(pfunction(\, A, R))],
|
||||
m(msb(A)) => [p(pfunction(msb, A, R))],
|
||||
m(lsb(A)) => [p(pfunction(lsb, A, R))],
|
||||
m(popcount(A)) => [p(pfunction(popcount, A, R))],
|
||||
m(popcount(A)) => [p(ppopcount(A, R))],
|
||||
m(A<<B) => [p(pfunction(<<, A, B, R))],
|
||||
m(A>>B) => [p(pfunction(>>, A, B, R))],
|
||||
m(A/\B) => [p(pfunction(/\, A, B, R))],
|
||||
m(A\/B) => [p(pfunction(\/, A, B, R))],
|
||||
m(xor(A,B)) => [p(pfunction(xor, A, B, R))],
|
||||
m(xor(A, B)) => [p(pxor(A, B, R))],
|
||||
g(true) => [g(domain_error(clpz_expression, E))]
|
||||
]).
|
||||
|
||||
@@ -2956,7 +2955,15 @@ expr_conds(A0\/B0, A\/B) --> expr_conds(A0, A), expr_conds(B0, B).
|
||||
expr_conds(xor(A0,B0), xor(A,B)) --> expr_conds(A0, A), expr_conds(B0, B).
|
||||
expr_conds(lsb(A0), lsb(A)) --> expr_conds(A0, A).
|
||||
expr_conds(msb(A0), msb(A)) --> expr_conds(A0, A).
|
||||
expr_conds(popcount(A0), popcount(A)) --> expr_conds(A0, A).
|
||||
expr_conds(popcount(A0), Count) -->
|
||||
expr_conds(A0, A),
|
||||
[I is A, arithmetic:popcount(I, Count)].
|
||||
|
||||
no_popcount_t(Gs, T) :-
|
||||
( member(arithmetic:popcount(_, _), Gs) ->
|
||||
T = false
|
||||
; T = true
|
||||
).
|
||||
|
||||
clpz_expandable(_ in _).
|
||||
clpz_expandable(_ #= _).
|
||||
@@ -2965,29 +2972,48 @@ clpz_expandable(_ #=< _).
|
||||
clpz_expandable(_ #> _).
|
||||
clpz_expandable(_ #< _).
|
||||
clpz_expandable(_ #\= _).
|
||||
clpz_expandable(_ #<==> _).
|
||||
|
||||
clpz_expansion(Var in Dom, In) :-
|
||||
( ground(Dom), Dom = L..U, integer(L), integer(U) ->
|
||||
expansion_simpler(
|
||||
( integer(Var) ->
|
||||
between(L, U, Var)
|
||||
between:between(L, U, Var)
|
||||
; clpz:clpz_in(Var, Dom)
|
||||
), In)
|
||||
; In = clpz:clpz_in(Var, Dom)
|
||||
).
|
||||
clpz_expansion(A #<==> B, Reif) :-
|
||||
nonvar(A),
|
||||
A =.. [F0,X0,Y0],
|
||||
clpz_builtin(F0, F),
|
||||
phrase(expr_conds(X0, X), Cs0, Cs),
|
||||
phrase(expr_conds(Y0, Y), Cs),
|
||||
list_goal(Cs0, Cond),
|
||||
Expr =.. [F,X,Y],
|
||||
expansion_simpler(( Cond, ( var(B) ; integer(B), clpz:between(0, 1, B) ) ->
|
||||
( Expr ->
|
||||
B = 1
|
||||
; B = 0
|
||||
)
|
||||
; clpz:reify(A, RA),
|
||||
clpz:reify(B, RA)
|
||||
), Reif).
|
||||
clpz_expansion(X0 #= Y0, Equal) :-
|
||||
phrase(expr_conds(X0, X), CsX),
|
||||
phrase(expr_conds(Y0, Y), CsY),
|
||||
list_goal(CsX, CondX),
|
||||
list_goal(CsY, CondY),
|
||||
no_popcount_t(CsY, YT),
|
||||
no_popcount_t(CsX, XT),
|
||||
expansion_simpler(
|
||||
( CondX ->
|
||||
( var(Y) -> Y is X
|
||||
( YT, var(Y) -> Y is X
|
||||
; CondY -> X =:= Y
|
||||
; T is X, clpz:clpz_equal(T, Y0)
|
||||
)
|
||||
; CondY ->
|
||||
( var(X) -> X is Y
|
||||
( XT, var(X) -> X is Y
|
||||
; T is Y, clpz:clpz_equal(X0, T)
|
||||
)
|
||||
; clpz:clpz_equal(X0, Y0)
|
||||
@@ -3023,6 +3049,13 @@ clpz_expansion(X0 #\= Y0, Neq) :-
|
||||
), Neq).
|
||||
|
||||
|
||||
clpz_builtin(#=, =:=).
|
||||
clpz_builtin(#\=, =\=).
|
||||
clpz_builtin(#>, >).
|
||||
clpz_builtin(#<, <).
|
||||
clpz_builtin(#>=, >=).
|
||||
clpz_builtin(#=<, =<).
|
||||
|
||||
expansion_simpler((True->Then0;_), Then) :-
|
||||
is_true(True), !,
|
||||
expansion_simpler(Then0, Then).
|
||||
@@ -3047,7 +3080,9 @@ expansion_simpler(Var =:= Expr0, Goal) :-
|
||||
( maplist(call, Gs) -> Value is Expr, Goal = (Var =:= Value)
|
||||
; Goal = false
|
||||
).
|
||||
expansion_simpler(between(L,U,V), Goal) :- maplist(integer, [L,U,V]), !,
|
||||
expansion_simpler(between:between(L,U,V), Goal) :-
|
||||
maplist(integer, [L,U,V]),
|
||||
!,
|
||||
( between(L,U,V) -> Goal = true
|
||||
; Goal = false
|
||||
).
|
||||
@@ -3064,12 +3099,10 @@ is_false(var(X)) :- nonvar(X).
|
||||
|
||||
:- dynamic(goal_expansion/1).
|
||||
|
||||
% goal expansion is disabled for now, until #445 is resolved
|
||||
%
|
||||
% user:goal_expansion(Goal0, Goal) :-
|
||||
% \+ goal_expansion(false),
|
||||
% clpz_expandable(Goal0),
|
||||
% clpz_expansion(Goal0, Goal).
|
||||
user:goal_expansion(Goal0, Goal) :-
|
||||
\+ goal_expansion(false),
|
||||
clpz_expandable(Goal0),
|
||||
clpz_expansion(Goal0, Goal).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
@@ -3501,7 +3534,7 @@ parse_reified(E, R, D,
|
||||
m(A>>B) => [function(D,>>,A,B,R)],
|
||||
m(A/\B) => [function(D,/\,A,B,R)],
|
||||
m(A\/B) => [function(D,\/,A,B,R)],
|
||||
m(xor(A, B)) => [function(D,xor,A,B,R)],
|
||||
m(xor(A, B)) => [skeleton(A,B,D,R,pxor)],
|
||||
g(true) => [g(domain_error(clpz_expression, E))]]
|
||||
).
|
||||
|
||||
@@ -4285,13 +4318,13 @@ tuples_in(Tuples, Relation) :-
|
||||
must_be(list(list), Tuples),
|
||||
maplist(maplist(fd_variable), Tuples),
|
||||
must_be(list(list(integer)), Relation),
|
||||
maplist(relation_tuple(Relation), Tuples),
|
||||
do_queue.
|
||||
maplist(relation_tuple(Relation), Tuples).
|
||||
|
||||
relation_tuple(Relation, Tuple) :-
|
||||
relation_unifiable(Relation, Tuple, Us, _, _),
|
||||
( ground(Tuple) -> memberchk(Tuple, Relation)
|
||||
; phrase(tuple_domain(Tuple, Us), _),
|
||||
; new_queue(Q),
|
||||
phrase((tuple_domain(Tuple, Us),do_queue), [Q], _),
|
||||
( Tuple = [_,_|_] -> tuple_freeze(Tuple, Us)
|
||||
; true
|
||||
)
|
||||
@@ -4925,97 +4958,234 @@ run_propagator(ptzdiv(X,Y,Z), MState) -->
|
||||
%% % Z = X mod Y
|
||||
|
||||
run_propagator(pmod(X,Y,Z), MState) -->
|
||||
( nonvar(X) ->
|
||||
( nonvar(Y) -> kill(MState), Y =\= 0, Z is X mod Y
|
||||
( Y == 0 -> { false }
|
||||
; Y == Z -> { false }
|
||||
% ; nonvar(Y), Z == X -> true
|
||||
; X == Y -> kill(MState), queue_goal(Z = 0)
|
||||
; true
|
||||
),
|
||||
( nonvar(X), nonvar(Y) ->
|
||||
kill(MState),
|
||||
Z is X mod Y
|
||||
; nonvar(Y), nonvar(Z) ->
|
||||
( Y > 0 -> Z >= 0, Z < Y
|
||||
; Y < 0 -> Z =< 0, Z > Y
|
||||
),
|
||||
( { fd_get(X, _, n(XL), _, _) } ->
|
||||
( (XL - Z) mod Y =\= 0 ->
|
||||
XMin is Z + Y * ((XL - Z) div Y + 1)
|
||||
; XMin is XL
|
||||
),
|
||||
{ fd_get(X, XD0, XPs),
|
||||
domain_remove_smaller_than(XD0, XMin, XD2) },
|
||||
fd_put(X, XD2, XPs)
|
||||
% queue_goal(X #>= XMin)
|
||||
; true
|
||||
),
|
||||
( { fd_get(X, _, _, n(XU), _) } ->
|
||||
XMax is Z + Y * ((XU - Z) div Y),
|
||||
{ fd_get(X, XD1, XPs),
|
||||
domain_remove_greater_than(XD1, XMax, XD3) },
|
||||
fd_put(X, XD3, XPs)
|
||||
% queue_goal(X #=< XMax)
|
||||
; true
|
||||
)
|
||||
; nonvar(Y) ->
|
||||
Y =\= 0,
|
||||
( abs(Y) =:= 1 -> kill(MState), Z = 0
|
||||
; var(Z) ->
|
||||
YP is abs(Y) - 1,
|
||||
( Y > 0, { fd_get(X, _, n(XL), n(XU), _) } ->
|
||||
( XL >= 0, XU < Y ->
|
||||
kill(MState), Z = X, ZL = XL, ZU = XU
|
||||
; ZL = 0, ZU = YP
|
||||
)
|
||||
; Y > 0 -> ZL = 0, ZU = YP
|
||||
; YN is -YP, ZL = YN, ZU = 0
|
||||
),
|
||||
( { fd_get(Z, ZD, ZPs) } ->
|
||||
{ domains_intersection(ZD, from_to(n(ZL), n(ZU)), ZD1),
|
||||
domain_infimum(ZD1, n(ZMin)),
|
||||
domain_supremum(ZD1, n(ZMax)) },
|
||||
fd_put(Z, ZD1, ZPs)
|
||||
; ZMin = Z, ZMax = Z
|
||||
),
|
||||
( { fd_get(X, XD, XPs), domain_infimum(XD, n(XMin)) } ->
|
||||
Z1 is XMin mod Y,
|
||||
( { between(ZMin, ZMax, Z1) } -> true
|
||||
; Y > 0 ->
|
||||
Next is ((XMin - ZMin + Y - 1) div Y)*Y + ZMin,
|
||||
{ domain_remove_smaller_than(XD, Next, XD1) },
|
||||
fd_put(X, XD1, XPs)
|
||||
; neq_num(X, XMin)
|
||||
)
|
||||
; true
|
||||
),
|
||||
( { fd_get(X, XD2, XPs2), domain_supremum(XD2, n(XMax)) } ->
|
||||
Z2 is XMax mod Y,
|
||||
( { between(ZMin, ZMax, Z2) } -> true
|
||||
; Y > 0 ->
|
||||
Prev is ((XMax - ZMin) div Y)*Y + ZMax,
|
||||
{ domain_remove_greater_than(XD2, Prev, XD3) },
|
||||
fd_put(X, XD3, XPs2)
|
||||
; neq_num(X, XMax)
|
||||
)
|
||||
; true
|
||||
% kill(MState),
|
||||
% queue_goal(X #= Z + Y * _) % Add a variable to be efficient.
|
||||
; nonvar(Z), nonvar(X) ->
|
||||
( Z > 0 ->
|
||||
( X < 0 -> true
|
||||
; X >= Z
|
||||
)
|
||||
; { fd_get(X, XD, XPs) },
|
||||
% if possible, propagate at the boundaries
|
||||
( { domain_infimum(XD, n(Min)) } ->
|
||||
( Min mod Y =:= Z -> true
|
||||
; Y > 0 ->
|
||||
Next is ((Min - Z + Y - 1) div Y)*Y + Z,
|
||||
{ domain_remove_smaller_than(XD, Next, XD1) },
|
||||
fd_put(X, XD1, XPs)
|
||||
; neq_num(X, Min)
|
||||
)
|
||||
; true
|
||||
),
|
||||
( { fd_get(X, XD2, XPs2) } ->
|
||||
( { domain_supremum(XD2, n(Max)) } ->
|
||||
( Max mod Y =:= Z -> true
|
||||
; Y > 0 ->
|
||||
Prev is ((Max - Z) div Y)*Y + Z,
|
||||
{ domain_remove_greater_than(XD2, Prev, XD3) },
|
||||
fd_put(X, XD3, XPs2)
|
||||
; neq_num(X, Max)
|
||||
)
|
||||
; true
|
||||
)
|
||||
; Z < 0 ->
|
||||
( X > 0 -> true
|
||||
; X =< Z
|
||||
)
|
||||
; Z =:= 0 % Multiple solutions so do nothing special.
|
||||
),
|
||||
( { fd_get(Y, _, _, n(YU), _),
|
||||
YU < X, X =< 0 } -> kill(MState), Z =:= X
|
||||
; { fd_get(Y, _, n(YL), _, _),
|
||||
YL > X, X >= 0 } -> kill(MState), Z =:= X
|
||||
; ( Z > 0 ->
|
||||
{ fd_get(Y, YD, YPs),
|
||||
YMin is Z + 1,
|
||||
domain_remove_smaller_than(YD, YMin, YD1) },
|
||||
fd_put(Y, YD1, YPs)
|
||||
% queue_goal(Y #> Z)
|
||||
; Z < 0 ->
|
||||
{ fd_get(Y, YD, YPs),
|
||||
YMax is Z - 1,
|
||||
domain_remove_greater_than(YD, YMax, YD1) },
|
||||
fd_put(Y, YD1, YPs)
|
||||
% queue_goal(Y #< Z)
|
||||
; true
|
||||
)
|
||||
)
|
||||
; X == Y -> kill(MState), Z = 0
|
||||
; { fd_get(X, XD, XPs),
|
||||
fd_get(Y, YD, _),
|
||||
fd_get(Z, ZD, ZPs) },
|
||||
( { domain_infimum(XD, n(XMin)), XMin >= 0,
|
||||
domain_infimum(YD, n(YMin)), YMin > 0 } ->
|
||||
{ domain_remove_smaller_than(ZD, 0, ZD1) }
|
||||
; ZD1 = ZD
|
||||
),
|
||||
( { domain_supremum(YD, n(YMax)), YMax > 0 } ->
|
||||
{ Max is YMax - 1, Min is -Max,
|
||||
domain_remove_smaller_than(ZD1, Min, ZD2),
|
||||
domain_remove_greater_than(ZD2, Max, ZD3) }
|
||||
; ZD3 = ZD1
|
||||
),
|
||||
fd_put(Z, ZD3, ZPs)
|
||||
% TODO: propagate more
|
||||
; run_propagator(pmodz(X,Y,Z), MState),
|
||||
run_propagator(pmody(X,Y,Z), MState),
|
||||
true
|
||||
).
|
||||
|
||||
run_propagator(pmodz(X,Y,Z), MState) -->
|
||||
( nonvar(Z) -> true % Nothing to do.
|
||||
; nonvar(X) ->
|
||||
( X =:= 0 -> kill(MState), queue_goal(Z = X)
|
||||
; ( X > 0 ->
|
||||
( { fd_get(Y, _, n(YL), _, _), YL > X } ->
|
||||
kill(MState),
|
||||
queue_goal(Z = X)
|
||||
; { fd_get(Z, ZD0, ZPs),
|
||||
domain_remove_greater_than(ZD0, X, ZD2) },
|
||||
fd_put(Z, ZD2, ZPs)
|
||||
% queue_goal(Z #=< X)
|
||||
)
|
||||
; X < 0 ->
|
||||
( { fd_get(Y, _, _, n(YU), _), YU < X } ->
|
||||
kill(MState),
|
||||
queue_goal(Z = X)
|
||||
; { fd_get(Z, ZD0, ZPs),
|
||||
domain_remove_smaller_than(ZD0, X, ZD2) },
|
||||
fd_put(Z, ZD2, ZPs)
|
||||
% queue_goal(Z #>= X)
|
||||
)
|
||||
),
|
||||
( { fd_get(Y, _, n(YL), n(YU), _), YL > 0 } ->
|
||||
ZMax is YU - 1,
|
||||
{ fd_get(Z, ZD1, ZPs),
|
||||
domain_remove_smaller_than(ZD1, 0, ZD3),
|
||||
domain_remove_greater_than(ZD3, ZMax, ZD5) },
|
||||
fd_put(Z, ZD5, ZPs)
|
||||
% queue_goal(Z in 0..ZMax)
|
||||
; { fd_get(Y, _, n(YL), n(YU), _), YU < 0 } ->
|
||||
ZMin is YL + 1,
|
||||
{ fd_get(Z, ZD1, ZPs),
|
||||
domain_remove_greater_than(ZD1, 0, ZD3),
|
||||
domain_remove_smaller_than(ZD3, ZMin, ZD5) },
|
||||
fd_put(Z, ZD5, ZPs)
|
||||
% queue_goal(Z in ZMin..0)
|
||||
; true
|
||||
)
|
||||
)
|
||||
; nonvar(Y) ->
|
||||
( abs(Y) =:= 1 -> kill(MState), queue_goal(Z = 0)
|
||||
; Y < 0 ->
|
||||
( { fd_get(X, _, n(XL), n(XU), _), XU =< 0, Y < XL } ->
|
||||
kill(MState),
|
||||
queue_goal(Z = X)
|
||||
; ZMin is Y + 1,
|
||||
{ fd_get(Z, ZD1, ZPs),
|
||||
domain_remove_greater_than(ZD1, 0, ZD3),
|
||||
domain_remove_smaller_than(ZD3, ZMin, ZD5) },
|
||||
fd_put(Z, ZD5, ZPs)
|
||||
% queue_goal(Z in ZMin..0)
|
||||
)
|
||||
; Y > 0 ->
|
||||
( { fd_get(X, _, n(XL), n(XU), _), XL >= 0, Y > XU } ->
|
||||
kill(MState),
|
||||
queue_goal(Z = X)
|
||||
; ZMax is Y - 1,
|
||||
{ fd_get(Z, ZD1, ZPs),
|
||||
domain_remove_smaller_than(ZD1, 0, ZD3),
|
||||
domain_remove_greater_than(ZD3, ZMax, ZD5) },
|
||||
fd_put(Z, ZD5, ZPs)
|
||||
% queue_goal(Z in 0..ZMax)
|
||||
)
|
||||
)
|
||||
; ( { fd_get(X, _, n(XL), n(XU), _), XL >= 0,
|
||||
fd_get(Y, _, n(YL), _, _), XU < YL } ->
|
||||
kill(MState),
|
||||
queue_goal(Z = X)
|
||||
; { fd_get(X, _, n(XL), n(XU), _), XU =< 0,
|
||||
fd_get(Y, _, _, n(YU), _), XL > YU } ->
|
||||
kill(MState),
|
||||
queue_goal(Z = X)
|
||||
; ( { fd_get(X, _, n(XL), n(XU), _), XL >= 0 } ->
|
||||
{ fd_get(Z, ZD0, ZPs),
|
||||
domain_remove_greater_than(ZD0, XU, ZD2) },
|
||||
fd_put(Z, ZD2, ZPs)
|
||||
% queue_goal(Z #=< XU)
|
||||
; { fd_get(X, _, n(XL), n(XU), _), XU =< 0 } ->
|
||||
{ fd_get(Z, ZD0, ZPs),
|
||||
domain_remove_smaller_than(ZD0, XL, ZD2) },
|
||||
fd_put(Z, ZD2, ZPs)
|
||||
% queue_goal(Z #>= XL)
|
||||
; true
|
||||
),
|
||||
( { fd_get(Y, _, n(YL), n(YU), _), YL > 0 } ->
|
||||
ZMax is YU - 1,
|
||||
{ fd_get(Z, ZD1, ZPs),
|
||||
domain_remove_smaller_than(ZD1, 0, ZD3),
|
||||
domain_remove_greater_than(ZD3, ZMax, ZD5) },
|
||||
fd_put(Z, ZD5, ZPs)
|
||||
% queue_goal(Z in 0..ZMax)
|
||||
; { fd_get(Y, _, n(YL), n(YU), _), YU < 0 } ->
|
||||
ZMin is YL + 1,
|
||||
{ fd_get(Z, ZD1, ZPs),
|
||||
domain_remove_greater_than(ZD1, 0, ZD3),
|
||||
domain_remove_smaller_than(ZD3, ZMin, ZD5) },
|
||||
fd_put(Z, ZD5, ZPs)
|
||||
% queue_goal(Z in ZMin..0)
|
||||
; { fd_get(Y, _, n(YL), n(YU), _) } ->
|
||||
ZMin is YL + 1,
|
||||
ZMax is YU - 1,
|
||||
{ fd_get(Z, ZD1, ZPs),
|
||||
domain_remove_greater_than(ZD1, ZMax, ZD3),
|
||||
domain_remove_smaller_than(ZD3, ZMin, ZD5) },
|
||||
fd_put(Z, ZD5, ZPs)
|
||||
% queue_goal(Z in ZMin..ZMax)
|
||||
%/* This doesn't work very well.
|
||||
; { fd_get(Y, _, _, n(YU), _), YU > 0 } ->
|
||||
{ fd_get(Z, ZD1, ZPs),
|
||||
ZMax is YU - 1,
|
||||
domain_remove_greater_than(ZD1, ZMax, ZD3) },
|
||||
fd_put(Z, ZD3, ZPs)
|
||||
% queue_goal(Z #< YU)
|
||||
; { fd_get(Y, _, n(YL), _, _), YL < 0 } ->
|
||||
{ fd_get(Z, ZD1, ZPs),
|
||||
ZMin is YL + 1,
|
||||
domain_remove_smaller_than(ZD1, ZMin, ZD3) },
|
||||
fd_put(Z, ZD3, ZPs)
|
||||
% queue_goal(Z #> YL)
|
||||
% * /
|
||||
; true
|
||||
)
|
||||
)
|
||||
).
|
||||
|
||||
run_propagator(pmody(X,Y,Z), MState) -->
|
||||
( nonvar(Y) -> true % Nothing to do.
|
||||
% ; nonvar(X) -> true
|
||||
; nonvar(Z) ->
|
||||
( Z > 0 -> % queue_goal(Y #> Z)
|
||||
{ fd_get(Y, YD, YPs),
|
||||
YMin is Z + 1,
|
||||
domain_remove_smaller_than(YD, YMin, YD1) },
|
||||
fd_put(Y, YD1, YPs)
|
||||
; Z < 0 -> % queue_goal(Y #< Z)
|
||||
{ fd_get(Y, YD, YPs),
|
||||
YMax is Z - 1,
|
||||
domain_remove_greater_than(YD, YMax, YD1) },
|
||||
fd_put(Y, YD1, YPs)
|
||||
; Z =:= 0 -> kill(MState), queue_goal(X / Y #= _)
|
||||
)
|
||||
; ( { fd_get(Z, _, n(ZL), _, _), ZL > 0 } ->
|
||||
{ fd_get(Y, YD, YPs),
|
||||
YMin is ZL + 1,
|
||||
domain_remove_smaller_than(YD, YMin, YD1) },
|
||||
fd_put(Y, YD1, YPs)
|
||||
% queue_goal(Y #> ZL)
|
||||
; { fd_get(Z, _, _, n(ZU), _), ZU < 0 } ->
|
||||
{ fd_get(Y, YD, YPs),
|
||||
YMax is ZU - 1,
|
||||
domain_remove_greater_than(YD, YMax, YD1) },
|
||||
fd_put(Y, YD1, YPs)
|
||||
% queue_goal(Y #< ZU)
|
||||
; true
|
||||
)
|
||||
).
|
||||
|
||||
|
||||
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% % Z = X rem Y
|
||||
|
||||
@@ -5314,6 +5484,51 @@ run_propagator(pexp(X,Y,Z), MState) -->
|
||||
; true
|
||||
).
|
||||
|
||||
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% % Y = popcount(X)
|
||||
|
||||
run_propagator(ppopcount(X,Y), MState) -->
|
||||
( nonvar(X) ->
|
||||
kill(MState),
|
||||
queue_goal(popcount(X, Y))
|
||||
; true
|
||||
).
|
||||
|
||||
|
||||
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%% % Z = X xor Y
|
||||
|
||||
run_propagator(pxor(X,Y,Z), MState) -->
|
||||
( nonvar(X), nonvar(Y) ->
|
||||
kill(MState),
|
||||
Z is xor(X, Y)
|
||||
; nonvar(Y), nonvar(Z) ->
|
||||
kill(MState),
|
||||
X is xor(Y, Z)
|
||||
; nonvar(Z), nonvar(X) ->
|
||||
kill(MState),
|
||||
Y is xor(Z, X)
|
||||
; X == Y ->
|
||||
kill(MState),
|
||||
queue_goal(Z = 0)
|
||||
; Y == Z ->
|
||||
kill(MState),
|
||||
queue_goal(X = 0)
|
||||
; Z == X ->
|
||||
kill(MState),
|
||||
queue_goal(Y = 0)
|
||||
; X == 0 ->
|
||||
kill(MState),
|
||||
queue_goal(Y = Z)
|
||||
; Y == 0 ->
|
||||
kill(MState),
|
||||
queue_goal(Z = X)
|
||||
; Z == 0 ->
|
||||
kill(MState),
|
||||
queue_goal(X = Y)
|
||||
; true
|
||||
).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
run_propagator(pzcompare(Order, A, B), MState) -->
|
||||
( A == B -> kill(MState), Order = (=)
|
||||
@@ -5748,10 +5963,12 @@ difference_arcs([V|Vs], FL0) -->
|
||||
|
||||
writeln(T) :- write(T), nl.
|
||||
|
||||
:- meta_predicate must_succeed(0).
|
||||
|
||||
must_succeed(G) :-
|
||||
(G -> true
|
||||
;write(failed-G), halt
|
||||
).
|
||||
( G -> true
|
||||
; throw(failed-G)
|
||||
).
|
||||
|
||||
enumerate([], _) --> [].
|
||||
enumerate([N|Ns], V) -->
|
||||
@@ -5911,6 +6128,8 @@ remove_attr(Var, Attr) :-
|
||||
functor(Term, Attr, 1),
|
||||
put_atts(Var, -Term).
|
||||
|
||||
:- meta_predicate with_local_attributes(?, 0, ?).
|
||||
|
||||
with_local_attributes(Vars, Goal, Result) :-
|
||||
catch((Goal,
|
||||
maplist(del_all_attrs, Vars),
|
||||
@@ -5950,7 +6169,7 @@ distinct_goals_([flow_to(F,To)|Es], V) -->
|
||||
get_attr(To, lowlink, L2),
|
||||
L1 =\= L2 } ->
|
||||
{ get_attr(To, value, N) },
|
||||
[neq_num(V, N)]
|
||||
[clpz:neq_num(V, N)]
|
||||
; []
|
||||
),
|
||||
distinct_goals_(Es, V).
|
||||
@@ -6471,7 +6690,7 @@ gcc_edge_goal(arc_to(_,_,V,F), Val) -->
|
||||
get_attr(Val, lowlink, L2),
|
||||
L1 =\= L2,
|
||||
get_attr(Val, value, Value) } ->
|
||||
[neq_num(V, Value)]
|
||||
[clpz:neq_num(V, Value)]
|
||||
; []
|
||||
).
|
||||
|
||||
@@ -6847,10 +7066,6 @@ cumulative(Tasks, Options) :-
|
||||
resource_limit(Start, End, Tasks, Bss, L)
|
||||
).
|
||||
|
||||
min_(E, M0, M) :- M is min(E,M0).
|
||||
|
||||
max_(E, M0, M) :- M is max(E,M0).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Trivial lower and upper bounds, assuming no gaps and not necessarily
|
||||
retaining the rectangular shape of each task.
|
||||
@@ -7442,20 +7657,6 @@ attribute_goals(X) -->
|
||||
attributes_goals(Ps),
|
||||
{ del_attr(X, clpz) }.
|
||||
|
||||
clpz_aux:attribute_goals(_) --> [].
|
||||
|
||||
clpz_gcc_vs:attribute_goals(_) --> [].
|
||||
|
||||
clpz_gcc_num:attribute_goals(_) --> [].
|
||||
|
||||
clpz_gcc_occurred:attribute_goals(_) --> [].
|
||||
|
||||
clpz_relation:attribute_goals(_) --> [].
|
||||
|
||||
attribute_goal(Var, Goal) :-
|
||||
phrase(attribute_goals(Var), Goals),
|
||||
list_goal(Goals, Goal).
|
||||
|
||||
attributes_goals([]) --> [].
|
||||
attributes_goals([propagator(P, State)|As]) -->
|
||||
( { ground(State) } -> []
|
||||
@@ -7500,6 +7701,8 @@ attribute_goal_(pmod(X,M,K)) --> [?(X) mod ?(M) #= ?(K)].
|
||||
attribute_goal_(prem(X,Y,Z)) --> [?(X) rem ?(Y) #= ?(Z)].
|
||||
attribute_goal_(pmax(X,Y,Z)) --> [?(Z) #= max(?(X),?(Y))].
|
||||
attribute_goal_(pmin(X,Y,Z)) --> [?(Z) #= min(?(X),?(Y))].
|
||||
attribute_goal_(pxor(X,Y,Z)) --> [?(Z) #= xor(?(X), ?(Y))].
|
||||
attribute_goal_(ppopcount(X,Y)) --> [?(Y) #= popcount(?(X))].
|
||||
attribute_goal_(scalar_product_neq(Cs,Vs,C)) -->
|
||||
[Left #\= Right],
|
||||
{ scalar_product_left_right([-1|Cs], [C|Vs], Left, Right) }.
|
||||
@@ -7518,7 +7721,9 @@ attribute_goal_(pelement(N,Is,V)) --> [element(N, Is, V)].
|
||||
attribute_goal_(pgcc(Vs, Pairs, _)) --> [global_cardinality(Vs, Pairs)].
|
||||
attribute_goal_(pgcc_single(_,_)) --> [].
|
||||
attribute_goal_(pgcc_check_single(_)) --> [].
|
||||
attribute_goal_(pgcc_check(_)) --> [].
|
||||
attribute_goal_(pgcc_check(Pairs)) -->
|
||||
{ pairs_values(Pairs, Nums),
|
||||
maplist(gcc_done, Nums) }.
|
||||
attribute_goal_(pcircuit(Vs)) --> [circuit(Vs)].
|
||||
attribute_goal_(pserialized(_,_,_,_,O)) --> original_goal(O).
|
||||
attribute_goal_(rel_tuple(R, Tuple)) -->
|
||||
@@ -7608,15 +7813,29 @@ plusterm_(CV, T0, T0+T) :- coeff_var_term(CV, T).
|
||||
|
||||
coeff_var_term(C-V, T) :- ( C =:= 1 -> T = ?(V) ; T = C * ?(V) ).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Reified predicates for use with predicates from library(reif).
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
#=(X, Y, T) :-
|
||||
X #= Y #<==> B,
|
||||
zo_t(B, T).
|
||||
|
||||
#<(X, Y, T) :-
|
||||
X #< Y #<==> B,
|
||||
zo_t(B, T).
|
||||
|
||||
zo_t(0, false).
|
||||
zo_t(1, true).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Generated predicates
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
generated_clauses(Cs) :-
|
||||
make_parse_clpz(Cs1),
|
||||
make_parse_reified(Cs2),
|
||||
make_matches(Cs3),
|
||||
append([Cs1,Cs2,Cs3], Cs).
|
||||
term_expansion(make_parse_clpz, Clauses) :- make_parse_clpz(Clauses).
|
||||
term_expansion(make_parse_reified, Clauses) :- make_parse_reified(Clauses).
|
||||
term_expansion(make_matches, Clauses) :- make_matches(Clauses).
|
||||
|
||||
:- initialization((generated_clauses(Cs),
|
||||
maplist(assertz, Cs))).
|
||||
make_parse_clpz.
|
||||
make_parse_reified.
|
||||
make_matches.
|
||||
@@ -1,5 +1,7 @@
|
||||
:- module(cont, [reset/3, shift/1]).
|
||||
|
||||
:- meta_predicate reset(0, ?, ?).
|
||||
|
||||
reset(Goal, Ball, Cont) :-
|
||||
call(Goal),
|
||||
'$reset_cont_marker',
|
||||
@@ -11,7 +13,7 @@ shift(Ball) :-
|
||||
get_chunks(E, P, L),
|
||||
( L == [] ->
|
||||
Cont = cont(true)
|
||||
; Cont = cont(call_continuation(L))
|
||||
; Cont = cont(cont:call_continuation(L))
|
||||
),
|
||||
'$write_cont_and_term'(_, _, Cont, Ball),
|
||||
'$unwind_environments'.
|
||||
@@ -1,5 +1,5 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written May 2020 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020, 2021, 2022 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
Predicates for cryptographic applications.
|
||||
@@ -29,6 +29,8 @@
|
||||
ed25519_keypair_public_key/2, % +KeyPair, +PublicKey
|
||||
ed25519_sign/4, % +KeyPair, +Data, -Signature, +Options
|
||||
ed25519_verify/4, % +PublicKey, +Data, +Signature, +Options
|
||||
curve25519_generator/1, % -Generator
|
||||
curve25519_scalar_mult/3, % +Scalar, +Point, -Result
|
||||
crypto_name_curve/2, % +Name, -Curve
|
||||
crypto_curve_order/2, % +Curve, -Order
|
||||
crypto_curve_generator/2, % +Curve, -Generator
|
||||
@@ -43,6 +45,8 @@
|
||||
:- use_module(library(arithmetic)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(si)).
|
||||
:- use_module(library(iso_ext), [partial_string/3]).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
hex_bytes(?Hex, ?Bytes) is det.
|
||||
@@ -55,8 +59,7 @@
|
||||
Example:
|
||||
|
||||
?- hex_bytes("501ACE", Bs).
|
||||
Bs = [80,26,206]
|
||||
; false.
|
||||
Bs = [80,26,206].
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
|
||||
@@ -76,13 +79,13 @@ hex_bytes([]) --> [].
|
||||
hex_bytes([H1,H2|Hs]) --> [Byte],
|
||||
{ char_hexval(H1, High),
|
||||
char_hexval(H2, Low),
|
||||
Byte is High*16 + Low },
|
||||
Byte #= High*16 + Low },
|
||||
hex_bytes(Hs).
|
||||
|
||||
bytes_hex([]) --> [].
|
||||
bytes_hex([B|Bs]) --> [C0,C1],
|
||||
{ High is B>>4,
|
||||
Low is B /\ 0xf,
|
||||
{ High #= B>>4,
|
||||
Low #= B /\ 0xf,
|
||||
char_hexval(C0, High),
|
||||
char_hexval(C1, Low)
|
||||
},
|
||||
@@ -101,6 +104,13 @@ must_be_bytes(Bytes, Context) :-
|
||||
).
|
||||
|
||||
|
||||
must_be_byte_chars(Chars, Context) :-
|
||||
must_be(chars, Chars),
|
||||
( '$first_non_octet'(Chars, F) ->
|
||||
domain_error(byte_char, F, Context)
|
||||
; true
|
||||
).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Cryptographically secure random numbers
|
||||
=======================================
|
||||
@@ -133,7 +143,7 @@ must_be_bytes(Bytes, Context) :-
|
||||
?- crypto_n_random_bytes(32, Bs),
|
||||
bytes_integer(Bs, I).
|
||||
Bs = [146,166,162,210,242,7,25,132,64,94|...],
|
||||
I = 337420085690608915485...(56 digits omitted)
|
||||
I = 337420085690608915485...(56 digits omitted).
|
||||
|
||||
The above relation also works in the other direction, letting you
|
||||
translate an integer _to_ a list of bytes. In addition, you can
|
||||
@@ -142,7 +152,7 @@ must_be_bytes(Bytes, Context) :-
|
||||
|
||||
?- crypto_n_random_bytes(12, Bs),
|
||||
hex_bytes(Hex, Bs).
|
||||
Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ..."
|
||||
Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ...".
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
|
||||
@@ -177,8 +187,7 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
|
||||
Example:
|
||||
|
||||
?- crypto_data_hash("abc", Hs, [algorithm(sha256)]).
|
||||
Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
|
||||
; false.
|
||||
Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad".
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@@ -191,18 +200,18 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
|
||||
|
||||
crypto_data_hash(Data0, Hash, Options0) :-
|
||||
must_be(list, Options0),
|
||||
options_data_bytes(Options0, Data0, Data),
|
||||
options_data_chars(Options0, Data0, Data, Encoding),
|
||||
functor_hash_options(algorithm, A, Options0, _),
|
||||
( hash_algorithm(A) -> true
|
||||
; domain_error(hash_algorithm, A, crypto_data_hash/3)
|
||||
),
|
||||
'$crypto_data_hash'(Data, HashBytes, A),
|
||||
'$crypto_data_hash'(Data, Encoding, HashBytes, A),
|
||||
hex_bytes(Hash, HashBytes).
|
||||
|
||||
options_data_bytes(Options, Data, Bytes) :-
|
||||
options_data_chars(Options, Data, Chars, Encoding) :-
|
||||
option(encoding(Encoding), Options, utf8),
|
||||
must_be(atom, Encoding),
|
||||
encoding_bytes(Encoding, Data, Bytes).
|
||||
encoding_chars(Encoding, Data, Chars).
|
||||
|
||||
default_hash(sha256).
|
||||
|
||||
@@ -269,13 +278,13 @@ crypto_data_hkdf(Data0, L, Bytes, Options0) :-
|
||||
; domain_error(hkdf_algorithm, Algorithm, crypto_data_hkdf/4)
|
||||
),
|
||||
must_be(integer, L),
|
||||
L >= 0,
|
||||
options_data_bytes(Options, Data0, Data),
|
||||
L #>= 0,
|
||||
options_data_chars(Options, Data0, Data, Encoding),
|
||||
option(salt(SaltBytes), Options, []),
|
||||
must_be_bytes(SaltBytes, crypto_data_hkdf/4),
|
||||
option(info(Info0), Options, []),
|
||||
chars_bytes_(Info0, Info, crypto_data_hkdf/4),
|
||||
'$crypto_data_hkdf'(Data, SaltBytes, Info, Algorithm, L, Bytes).
|
||||
'$crypto_data_hkdf'(Data, Encoding, SaltBytes, Info, Algorithm, L, Bytes).
|
||||
|
||||
hkdf_algorithm(sha256).
|
||||
hkdf_algorithm(sha384).
|
||||
@@ -402,7 +411,7 @@ crypto_password_hash(Password0, Hash, Options) :-
|
||||
chars_bytes_(Password0, Password, crypto_password_hash/3),
|
||||
must_be(list, Options),
|
||||
option(cost(C), Options, 17),
|
||||
Iterations is 2^C,
|
||||
Iterations #= 2^C,
|
||||
Algorithm = 'pbkdf2-sha512', % current default and only option
|
||||
option(algorithm(Algorithm), Options, Algorithm),
|
||||
( member(salt(SaltBytes), Options) ->
|
||||
@@ -416,61 +425,17 @@ crypto_password_hash(Password0, Hash, Options) :-
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Bidirectional Bytes <-> Base64 conversion
|
||||
=========================================
|
||||
|
||||
This implements Base64 conversion *without padding*.
|
||||
Bidirectional Bytes <-> Base64 conversion *without padding*.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
n_base64(0 , 'A'). n_base64(1 , 'B'). n_base64(2 , 'C'). n_base64(3 , 'D').
|
||||
n_base64(4 , 'E'). n_base64(5 , 'F'). n_base64(6 , 'G'). n_base64(7 , 'H').
|
||||
n_base64(8 , 'I'). n_base64(9 , 'J'). n_base64(10, 'K'). n_base64(11, 'L').
|
||||
n_base64(12, 'M'). n_base64(13, 'N'). n_base64(14, 'O'). n_base64(15, 'P').
|
||||
n_base64(16, 'Q'). n_base64(17, 'R'). n_base64(18, 'S'). n_base64(19, 'T').
|
||||
n_base64(20, 'U'). n_base64(21, 'V'). n_base64(22, 'W'). n_base64(23, 'X').
|
||||
n_base64(24, 'Y'). n_base64(25, 'Z'). n_base64(26, 'a'). n_base64(27, 'b').
|
||||
n_base64(28, 'c'). n_base64(29, 'd'). n_base64(30, 'e'). n_base64(31, 'f').
|
||||
n_base64(32, 'g'). n_base64(33, 'h'). n_base64(34, 'i'). n_base64(35, 'j').
|
||||
n_base64(36, 'k'). n_base64(37, 'l'). n_base64(38, 'm'). n_base64(39, 'n').
|
||||
n_base64(40, 'o'). n_base64(41, 'p'). n_base64(42, 'q'). n_base64(43, 'r').
|
||||
n_base64(44, 's'). n_base64(45, 't'). n_base64(46, 'u'). n_base64(47, 'v').
|
||||
n_base64(48, 'w'). n_base64(49, 'x'). n_base64(50, 'y'). n_base64(51, 'z').
|
||||
n_base64(52, '0'). n_base64(53, '1'). n_base64(54, '2'). n_base64(55, '3').
|
||||
n_base64(56, '4'). n_base64(57, '5'). n_base64(58, '6'). n_base64(59, '7').
|
||||
n_base64(60, '8'). n_base64(61, '9'). n_base64(62, '+'). n_base64(63, '/').
|
||||
|
||||
bytes_base64(Ls, Bs) :-
|
||||
( list(Bs), maplist(atom, Bs) ->
|
||||
maplist(n_base64, Is, Bs),
|
||||
phrase(bytes_base64_(Ls), Is),
|
||||
Ls ins 0..255
|
||||
; phrase(bytes_base64_(Ls), Is),
|
||||
Is ins 0..63,
|
||||
maplist(n_base64, Is, Bs)
|
||||
bytes_base64(Bytes, Base64) :-
|
||||
( var(Bytes) ->
|
||||
chars_base64(Chars, Base64, [padding(false)]),
|
||||
maplist(char_code, Chars, Bytes)
|
||||
; maplist(char_code, Chars, Bytes),
|
||||
chars_base64(Chars, Base64, [padding(false)])
|
||||
).
|
||||
|
||||
list(Ls) :-
|
||||
nonvar(Ls),
|
||||
( Ls = [] -> true
|
||||
; Ls = [_|Rest],
|
||||
list(Rest)
|
||||
).
|
||||
|
||||
bytes_base64_([]) --> [].
|
||||
bytes_base64_([A]) --> [W,X],
|
||||
{ A #= W*4 + X//16,
|
||||
X #= 16*_ }.
|
||||
bytes_base64_([A,B]) --> [W,X,Y],
|
||||
{ A #= W*4 + X//16,
|
||||
B #= (X mod 16)*16 + Y//4,
|
||||
Y #= 4*_ }.
|
||||
bytes_base64_([A,B,C|Ls]) --> [W,X,Y,Z],
|
||||
{ A #= W*4 + X//16,
|
||||
B #= (X mod 16)*16 + Y//4,
|
||||
C #= (Y mod 4)*64 + Z },
|
||||
bytes_base64_(Ls).
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
crypto_data_encrypt(+PlainText,
|
||||
+Algorithm,
|
||||
@@ -523,6 +488,12 @@ bytes_base64_([A,B,C|Ls]) --> [W,X,Y,Z],
|
||||
list of _bytes_ holding the tag. This tag must be provided for
|
||||
decryption.
|
||||
|
||||
- aad(+Data)
|
||||
Data is additional authenticated data (AAD), a list of
|
||||
characters. It is authenticated in that it influences the tag,
|
||||
but it is not encrypted. The encoding/1 option also specifies
|
||||
the encoding of Data.
|
||||
|
||||
Here is an example encryption and decryption, using the ChaCha20
|
||||
stream cipher with the Poly1305 authenticator. This cipher uses a
|
||||
256-bit key and a 96-bit nonce, i.e., 32 and 12 _bytes_,
|
||||
@@ -558,19 +529,26 @@ bytes_base64_([A,B,C|Ls]) --> [W,X,Y,Z],
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :-
|
||||
options_data_bytes(Options, PlainText0, PlainText),
|
||||
options_data_chars(Options, PlainText0, PlainText, Encoding),
|
||||
option(tag(Tag), Options, _),
|
||||
( nonvar(Tag) ->
|
||||
must_be_bytes(Tag, crypto_data_encrypt/6)
|
||||
; true
|
||||
),
|
||||
option(aad(AAD0), Options, []),
|
||||
encoding_chars(Encoding, AAD0, AAD),
|
||||
must_be_bytes(Key, crypto_data_encrypt/6),
|
||||
must_be_bytes(IV, crypto_data_encrypt/6),
|
||||
must_be(atom, Algorithm),
|
||||
( Algorithm = 'chacha20-poly1305' -> true
|
||||
; domain_error('chacha20-poly1305', Algorithm, crypto_data_encrypt/6)
|
||||
),
|
||||
'$crypto_data_encrypt'(PlainText, Key, IV, Tag, CipherText).
|
||||
algorithm_key_iv(Algorithm, Key, IV),
|
||||
'$crypto_data_encrypt'(PlainText, AAD, Encoding, Key, IV, Tag, CipherText).
|
||||
|
||||
algorithm_key_iv('chacha20-poly1305', Key, IV) :-
|
||||
length(Key, 32),
|
||||
length(IV, 12).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
crypto_data_decrypt(+CipherText,
|
||||
@@ -598,6 +576,10 @@ crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :-
|
||||
- tag(+Tag)
|
||||
For authenticated encryption schemes, the tag must be specified as
|
||||
a list of bytes exactly as they were generated upon encryption.
|
||||
|
||||
- aad(+Data)
|
||||
Any additional authenticated data (AAD) must be specified. The
|
||||
encoding/1 option also specifies the encoding of Data.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :-
|
||||
@@ -607,29 +589,31 @@ crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :-
|
||||
must_be_bytes(IV, crypto_data_decrypt/6),
|
||||
must_be(atom, Algorithm),
|
||||
option(encoding(Encoding), Options, utf8),
|
||||
option(aad(AAD0), Options, []),
|
||||
encoding_chars(Encoding, AAD0, AAD),
|
||||
must_be(atom, Encoding),
|
||||
member(Encoding, [utf8,octet]),
|
||||
must_be(list, CipherText0),
|
||||
encoding_bytes(octet, CipherText0, CipherText1),
|
||||
append(CipherText1, Tag, CipherText),
|
||||
encoding_chars(octet, CipherText0, CipherText1),
|
||||
maplist(char_code, TagChars, Tag),
|
||||
% we append the tag very efficiently, retaining a compact
|
||||
% internal string representation of the ciphertext
|
||||
partial_string(CipherText1, CipherText, TagChars),
|
||||
( Algorithm = 'chacha20-poly1305' -> true
|
||||
; domain_error('chacha20-poly1305', Algorithm, crypto_data_decrypt/6)
|
||||
),
|
||||
'$crypto_data_decrypt'(CipherText, Key, IV, Encoding, PlainText).
|
||||
algorithm_key_iv(Algorithm, Key, IV),
|
||||
'$crypto_data_decrypt'(CipherText, AAD, Key, IV, Encoding, PlainText).
|
||||
|
||||
encoding_bytes(octet, Bs0, Bs) :-
|
||||
must_be(list, Bs0),
|
||||
( maplist(integer, Bs0) ->
|
||||
Bs0 = Bs
|
||||
; maplist(char_code, Bs0, Bs)
|
||||
|
||||
encoding_chars(octet, Bs, Cs) :-
|
||||
must_be(list, Bs),
|
||||
( maplist(integer, Bs) ->
|
||||
maplist(char_code, Cs, Bs)
|
||||
; Bs = Cs
|
||||
),
|
||||
must_be_bytes(Bs, crypto_encoding).
|
||||
encoding_bytes(utf8, Cs, Bs) :-
|
||||
must_be(list, Cs),
|
||||
( maplist(atom, Cs) ->
|
||||
chars_bytes_(Cs, Bs, crypto_encoding)
|
||||
; domain_error(encryption_encoding, Cs, crypto)
|
||||
).
|
||||
must_be_byte_chars(Cs, crypto_encoding).
|
||||
encoding_chars(utf8, Cs, Cs) :-
|
||||
must_be(chars, Cs).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Digital signatures with Ed25519
|
||||
@@ -667,60 +651,75 @@ encoding_bytes(utf8, Cs, Bs) :-
|
||||
ed25519_new_keypair(Pair) :-
|
||||
'$ed25519_new_keypair'(Pair).
|
||||
|
||||
ed25519_keypair_public_key(Pair0, PublicKey) :-
|
||||
encoding_bytes(octet, Pair0, Pair),
|
||||
ed25519_keypair_public_key(Pair, PublicKey) :-
|
||||
must_be_byte_chars(Pair, ed25519_keypair_public_key),
|
||||
'$ed25519_keypair_public_key'(Pair, PublicKey).
|
||||
|
||||
ed25519_sign(Key0, Data0, Signature, Options) :-
|
||||
options_data_bytes(Options, Data0, Data),
|
||||
encoding_bytes(octet, Key0, Key),
|
||||
'$ed25519_sign'(Key, Data, Signature0),
|
||||
ed25519_sign(Key, Data0, Signature, Options) :-
|
||||
must_be_byte_chars(Key, ed25519_sign),
|
||||
options_data_chars(Options, Data0, Data, Encoding),
|
||||
'$ed25519_sign'(Key, Data, Encoding, Signature0),
|
||||
hex_bytes(Signature, Signature0).
|
||||
|
||||
ed25519_verify(Key0, Data0, Signature0, Options) :-
|
||||
options_data_bytes(Options, Data0, Data),
|
||||
encoding_bytes(octet, Key0, Key),
|
||||
ed25519_verify(Key, Data0, Signature0, Options) :-
|
||||
must_be_byte_chars(Key, ed25519_verify),
|
||||
options_data_chars(Options, Data0, Data, Encoding),
|
||||
hex_bytes(Signature0, Signature),
|
||||
'$ed25519_verify'(Key, Data, Signature).
|
||||
'$ed25519_verify'(Key, Data, Encoding, Signature).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Modular multiplicative inverse.
|
||||
X25519: ECDH key exchange over Curve25519
|
||||
=========================================
|
||||
|
||||
Compute Y = X^(-1) mod p, using the extended Euclidean algorithm.
|
||||
Points on Curve25519 are represented as lists of characters that denote
|
||||
the u-coordinate of the Montgomery curve.
|
||||
|
||||
- curve25519_generator(-Gs)
|
||||
Gs is the generator point of Curve25519.
|
||||
|
||||
- curve25519_scalar_mult(+Scalar, +Ps, -Rs)
|
||||
Scalar must be an integer between 0 and 2^256-1,
|
||||
or a list of 32 bytes, and Ps must be a point on the curve.
|
||||
Computes the point Rs = Scalar*Ps as mandated by X25519.
|
||||
|
||||
Alice and Bob can use this to establish a shared secret as follows,
|
||||
where Gs is the generator point of Curve25519:
|
||||
|
||||
1. Alice creates a random integer a and sends As = a*Gs to Bob.
|
||||
2. Bob creates a random integer b and sends Bs = b*Gs to Alice.
|
||||
3. Alice computes Rs = a*Bs.
|
||||
4. Bob computes Rs = b*As.
|
||||
5. Alice and Bob use crypto_data_hkdf/4 on Rs with suitable
|
||||
(same) parameters to obtain lists of bytes that can be used as
|
||||
keys and initialization vectors for symmetric encryption.
|
||||
|
||||
If a and b are kept secret, this method is considered very secure.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
multiplicative_inverse_modulo_p(X, P, Y) :-
|
||||
eea(X, P, _, _, Y),
|
||||
R #= X*Y mod P,
|
||||
zcompare(C, 1, R),
|
||||
must_be_one(C, X, P, Y).
|
||||
curve25519_generator(Gs) :-
|
||||
length(Gs0, 32),
|
||||
Gs0 = [9|Zs],
|
||||
maplist(=(0), Zs),
|
||||
maplist(char_code, Gs, Gs0).
|
||||
|
||||
must_be_one(=, _, _, _).
|
||||
must_be_one(>, X, P, Y) :- throw(multiplicative_inverse_modulo_p(X,P,Y)).
|
||||
must_be_one(<, X, P, Y) :- throw(multiplicative_inverse_modulo_p(X,P,Y)).
|
||||
curve25519_scalar_mult(Scalar, Point, Result) :-
|
||||
( integer_si(Scalar) ->
|
||||
length(ScalarBytes, 32),
|
||||
bytes_integer(ScalarBytes, Scalar)
|
||||
; ScalarBytes = Scalar,
|
||||
must_be_bytes(ScalarBytes, curve25519_scalar_mult/3),
|
||||
length(ScalarBytes, 32)
|
||||
),
|
||||
maplist(char_code, Point, PointBytes),
|
||||
'$curve25519_scalar_mult'(ScalarBytes, PointBytes, Result).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Extended Euclidean algorithm.
|
||||
|
||||
Computes the GCD and the Bézout coefficients S and T.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
eea(I, J, G, S, T) :-
|
||||
State0 = state(1,0,0,1),
|
||||
eea_loop(I, J, State0, G, S, T).
|
||||
|
||||
eea_loop(I, J, State0, G, S, T) :-
|
||||
zcompare(C, 0, J),
|
||||
eea_(C, I, J, State0, G, S, T).
|
||||
|
||||
eea_(=, I, _, state(_,_,U,V), I, U, V).
|
||||
eea_(<, I0, J0, state(S0,T0,U0,V0), I, U, V) :-
|
||||
Q #= I0 // J0,
|
||||
R #= I0 mod J0,
|
||||
S1 #= U0 - (Q*S0),
|
||||
T1 #= V0 - (Q*T0),
|
||||
eea_loop(J0, R, state(S1,T1,S0,T0), I, U, V).
|
||||
bytes_integer(Bs, N) :-
|
||||
foldl(pow, Bs, 0-0, N-_).
|
||||
|
||||
pow(B, N0-I0, N-I) :-
|
||||
B in 0..255,
|
||||
N #= N0 + B*256^I0,
|
||||
I #= I0 + 1.
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Operations on Elliptic Curves
|
||||
@@ -728,7 +727,7 @@ eea_(<, I0, J0, state(S0,T0,U0,V0), I, U, V) :-
|
||||
|
||||
Sample use: Establishing a shared secret S, using ECDH key exchange.
|
||||
|
||||
?- crypto_name_curve(Name, C),
|
||||
?- crypto_name_curve(secp256k1, C),
|
||||
crypto_curve_generator(C, Generator),
|
||||
PrivateKey = 10,
|
||||
crypto_curve_scalar_mult(C, PrivateKey, Generator, PublicKey),
|
||||
@@ -737,95 +736,44 @@ eea_(<, I0, J0, state(S0,T0,U0,V0), I, U, V) :-
|
||||
crypto_curve_scalar_mult(C, Random, PublicKey, S),
|
||||
crypto_curve_scalar_mult(C, PrivateKey, R, S).
|
||||
|
||||
For better security, new code should use Curve25519 instead.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
An elliptic curve over a prime field F_p is represented as:
|
||||
|
||||
curve(P,A,B,point(X,Y),Order,Cofactor).
|
||||
curve(Name,P,A,B,point(X,Y),Order,FieldLength,Cofactor).
|
||||
|
||||
First, we define suitable accessors.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
curve_p(curve(P,_,_,_,_,_), P).
|
||||
curve_a(curve(_,A,_,_,_,_), A).
|
||||
curve_b(curve(_,_,B,_,_,_), B).
|
||||
curve_name(curve(Name,_,_,_,_,_,_,_), Name).
|
||||
curve_p(curve(_,P,_,_,_,_,_,_), P).
|
||||
curve_a(curve(_,_,A,_,_,_,_,_), A).
|
||||
curve_b(curve(_,_,_,B,_,_,_,_), B).
|
||||
curve_field_length(curve(_,_,_,_,_,_,FieldLength,_), FieldLength).
|
||||
|
||||
crypto_curve_order(curve(_,_,_,_,Order,_), Order).
|
||||
crypto_curve_generator(curve(_,_,_,G,_,_), G).
|
||||
crypto_curve_generator(curve(_,_,_,_,G,_,_,_), G).
|
||||
crypto_curve_order(curve(_,_,_,_,_,Order,_,_), Order).
|
||||
|
||||
crypto_curve_scalar_mult(Curve, Scalar, point(X,Y), point(RX, RY)) :-
|
||||
must_be(integer, Scalar),
|
||||
must_be_on_curve(Curve, point(X,Y)),
|
||||
curve_name(Curve, Name),
|
||||
curve_field_length(Curve, L0),
|
||||
L #= 2*L0, % for hex encoding
|
||||
phrase(format_("04~|~`0t~16r~*+~`0t~16r~*+", [X,L,Y,L]), Hex),
|
||||
hex_bytes(Hex, Bytes),
|
||||
'$crypto_curve_scalar_mult'(Name, Scalar, Bytes, SX, SY),
|
||||
number_chars(RX, SX),
|
||||
number_chars(RY, SY).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Scalar point multiplication.
|
||||
|
||||
R = k*Q.
|
||||
|
||||
The Montgomery ladder method is used to mitigate side-channel
|
||||
attacks such as timing attacks, since the number of multiplications
|
||||
and additions is independent of the private key K. This method does
|
||||
not even reveal the key's Hamming weight (number of 1s).
|
||||
?- crypto_name_curve(secp256k1, Curve),
|
||||
crypto_curve_generator(Curve, G),
|
||||
crypto_curve_scalar_mult(Curve, 2, G, R).
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
crypto_curve_scalar_mult(Curve, K, Q, R) :-
|
||||
msb(K, Upper),
|
||||
scalar_multiplication(Curve, K, Upper, ml(null,Q)-R),
|
||||
must_be_on_curve(Curve, R).
|
||||
|
||||
scalar_multiplication(Curve, K, I, R0-R) :-
|
||||
zcompare(C, -1, I),
|
||||
scalar_mult_(C, Curve, K, I, R0-R).
|
||||
|
||||
scalar_mult_(=, _, _, _, ml(R,_)-R).
|
||||
scalar_mult_(<, Curve, K, I0, ML0-R) :-
|
||||
BitSet #= K /\ (1 << I0),
|
||||
zcompare(C, 0, BitSet),
|
||||
montgomery_step(C, Curve, ML0, ML1),
|
||||
I1 #= I0 - 1,
|
||||
scalar_multiplication(Curve, K, I1, ML1-R).
|
||||
|
||||
montgomery_step(=, Curve, ml(R0,S0), ml(R,S)) :-
|
||||
curve_points_addition(Curve, R0, S0, S),
|
||||
curve_point_double(Curve, R0, R).
|
||||
montgomery_step(<, Curve, ml(R0,S0), ml(R,S)) :-
|
||||
curve_points_addition(Curve, R0, S0, R),
|
||||
curve_point_double(Curve, S0, S).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Doubling a point: R = A + A.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
curve_point_double(_, null, null).
|
||||
curve_point_double(Curve, point(AX,AY), R) :-
|
||||
curve_p(Curve, P),
|
||||
curve_a(Curve, A),
|
||||
Numerator #= (3*AX^2 + A) mod P,
|
||||
Denom0 #= 2*AY mod P,
|
||||
multiplicative_inverse_modulo_p(Denom0, P, Denom),
|
||||
S #= (Numerator*Denom) mod P,
|
||||
R = point(RX,RY),
|
||||
RX #= (S^2 - 2*AX) mod P,
|
||||
RY #= (S*(AX - RX) - AY) mod P,
|
||||
must_be_on_curve(Curve, R).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Adding two points.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
curve_points_addition(Curve, P, Q, R) :-
|
||||
curve_points_addition_(P, Curve, Q, R).
|
||||
|
||||
curve_points_addition_(null, _, P, P).
|
||||
curve_points_addition_(P, _, null, P).
|
||||
curve_points_addition_(point(AX,AY), Curve, point(BX,BY), R) :-
|
||||
curve_p(Curve, P),
|
||||
Numerator #= (AY - BY) mod P,
|
||||
Denom0 #= (AX - BX) mod P,
|
||||
multiplicative_inverse_modulo_p(Denom0, P, Denom),
|
||||
S #= (Numerator * Denom) mod P,
|
||||
R = point(RX,RY),
|
||||
RX #= (S^2 - AX - BX) mod P,
|
||||
RY #= (S*(AX - RX) - AY) mod P,
|
||||
must_be_on_curve(Curve, R).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Validation.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
@@ -838,7 +786,7 @@ curve_contains_point(Curve, point(QX,QY)) :-
|
||||
|
||||
must_be_on_curve(Curve, P) :-
|
||||
\+ curve_contains_point(Curve, P),
|
||||
throw(not_on_curve(P)).
|
||||
domain_error(point_on_curve, P, crypto_elliptic_curves).
|
||||
must_be_on_curve(Curve, P) :- curve_contains_point(Curve, P).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@@ -855,21 +803,38 @@ must_be_on_curve(Curve, P) :- curve_contains_point(Curve, P).
|
||||
-text -no_seed -name secp256k1
|
||||
|
||||
You must remove the leading "04:" from the generator.
|
||||
|
||||
The field length depends on the order of the curve and can be computed
|
||||
with order_field_length/2.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
order_field_length(Order, L) :-
|
||||
fitting_exponent(Order, 0, E),
|
||||
L #= (E + 7) // 8.
|
||||
|
||||
fitting_exponent(N, E0, E) :-
|
||||
( 2^E0 #>= N -> E #= E0
|
||||
; E1 #= E0 + 1,
|
||||
fitting_exponent(N, E1, E)
|
||||
).
|
||||
|
||||
crypto_name_curve(secp112r1,
|
||||
curve(0x00db7c2abf62e35e668076bead208b,
|
||||
curve(secp112r1,
|
||||
0x00db7c2abf62e35e668076bead208b,
|
||||
0x00db7c2abf62e35e668076bead2088,
|
||||
0x659ef8ba043916eede8911702b22,
|
||||
point(0x09487239995a5ee76b55f9c2f098,
|
||||
0xa89ce5af8724c0a23e0e0ff77500),
|
||||
0x00db7c2abf62e35e7628dfac6561c5,
|
||||
14,
|
||||
1)).
|
||||
crypto_name_curve(secp256k1,
|
||||
curve(0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
|
||||
curve(secp256k1,
|
||||
0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
|
||||
0x0,
|
||||
0x7,
|
||||
point(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
|
||||
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8),
|
||||
0x00fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
|
||||
32,
|
||||
1)).
|
||||
238
src/lib/csv.pl
Normal file
238
src/lib/csv.pl
Normal file
@@ -0,0 +1,238 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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, []).
|
||||
164
src/lib/dcgs.pl
Normal file
164
src/lib/dcgs.pl
Normal file
@@ -0,0 +1,164 @@
|
||||
:- module(dcgs,
|
||||
[op(1105, xfy, '|'),
|
||||
phrase/2,
|
||||
phrase/3,
|
||||
seq//1,
|
||||
seqq//1,
|
||||
... //0
|
||||
]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(lists), [append/3, member/2]).
|
||||
:- use_module(library(loader), [strip_module/3]).
|
||||
|
||||
load_context(GRBody, Module, GRBody0) :-
|
||||
strip_module(GRBody, Module, GRBody0),
|
||||
( nonvar(Module) ->
|
||||
true
|
||||
; prolog_load_context(module, Module) ->
|
||||
true
|
||||
; true
|
||||
).
|
||||
|
||||
|
||||
:- meta_predicate phrase(2, ?).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?).
|
||||
|
||||
phrase(GRBody, S0) :-
|
||||
phrase(GRBody, S0, []).
|
||||
|
||||
phrase(GRBody, S0, S) :-
|
||||
load_context(GRBody, Module, GRBody0),
|
||||
( var(GRBody0) ->
|
||||
instantiation_error(phrase/3)
|
||||
; dcg_body(GRBody0, S0, S, GRBody1, Module) ->
|
||||
call(GRBody1)
|
||||
; type_error(callable, GRBody0, phrase/3)
|
||||
).
|
||||
|
||||
|
||||
module_call_qualified(M, Call, Call1) :-
|
||||
( nonvar(M) -> Call1 = M:Call
|
||||
; Call = Call1
|
||||
).
|
||||
|
||||
|
||||
% 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),
|
||||
Goal =.. GoalUniv.
|
||||
|
||||
dcg_terminals(Terminals, S0, S, S0 = List) :-
|
||||
append(Terminals, S, List).
|
||||
|
||||
dcg_body(Var, S0, S, Body, M) :-
|
||||
var(Var),
|
||||
module_call_qualified(M, Var, Var1),
|
||||
Body = phrase(Var1, S0, S).
|
||||
dcg_body(GRBody, S0, S, Body, M) :-
|
||||
nonvar(GRBody),
|
||||
dcg_constr(GRBody),
|
||||
dcg_cbody(GRBody, S0, S, Body, M).
|
||||
dcg_body(NonTerminal, S0, S, Goal1, M) :-
|
||||
nonvar(NonTerminal),
|
||||
\+ dcg_constr(NonTerminal),
|
||||
NonTerminal \= ( _ -> _ ),
|
||||
NonTerminal \= ( \+ _ ),
|
||||
module_call_qualified(M, Goal, Goal1),
|
||||
dcg_non_terminal(NonTerminal, S0, S, Goal).
|
||||
|
||||
% 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(!). % 7.14.10
|
||||
%% dcg_constr(\+ _). % 7.14.11 - not (existence implementation dep.)
|
||||
dcg_constr((_->_)). % 7.14.12 - if-then (existence implementation dep.)
|
||||
|
||||
% The principal functor of the first argument indicates
|
||||
% the construct to be expanded.
|
||||
dcg_cbody([], S0, S, S0 = S, _M).
|
||||
dcg_cbody([T|Ts], S0, S, Goal, _M) :-
|
||||
must_be(list, [T|Ts]),
|
||||
dcg_terminals([T|Ts], S0, S, Goal).
|
||||
dcg_cbody(( GRFirst, GRSecond ), S0, S, ( First, Second ), M) :-
|
||||
dcg_body(GRFirst, S0, S1, First, M),
|
||||
dcg_body(GRSecond, S1, S, Second, M).
|
||||
dcg_cbody(( GREither ; GROr ), S0, S, ( Either ; Or ), M) :-
|
||||
\+ subsumes_term(( _ -> _ ), GREither),
|
||||
dcg_body(GREither, S0, S, Either, M),
|
||||
dcg_body(GROr, S0, S, Or, M).
|
||||
dcg_cbody(( GRCond ; GRElse ), S0, S, ( Cond ; Else ), M) :-
|
||||
subsumes_term(( _GRIf -> _GRThen ), GRCond),
|
||||
dcg_cbody(GRCond, S0, S, Cond, M),
|
||||
dcg_body(GRElse, S0, S, Else, M).
|
||||
dcg_cbody(( GREither '|' GROr ), S0, S, ( Either ; Or ), M) :-
|
||||
dcg_body(GREither, S0, S, Either, M),
|
||||
dcg_body(GROr, S0, S, Or, M).
|
||||
dcg_cbody({Goal}, S0, S, ( Goal1, S0 = S ), M) :-
|
||||
module_call_qualified(M, Goal, Goal1).
|
||||
dcg_cbody(call(Cont), S0, S, call(Cont1, S0, S), M) :-
|
||||
module_call_qualified(M, Cont, Cont1).
|
||||
dcg_cbody(phrase(Body), S0, S, phrase(Body1, S0, S), M) :-
|
||||
module_call_qualified(M, Body, Body1).
|
||||
dcg_cbody(!, S0, S, ( !, S0 = S ), _M).
|
||||
dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody1,S0,_), S0 = S ), M) :-
|
||||
module_call_qualified(M, GRBody, GRBody1).
|
||||
dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then ), M) :-
|
||||
dcg_body(GRIf, S0, S1, If, M),
|
||||
dcg_body(GRThen, S1, S, Then, M).
|
||||
|
||||
user:term_expansion(Term0, Term) :-
|
||||
nonvar(Term0),
|
||||
dcg_rule(Term0, Term).
|
||||
|
||||
% Describes a sequence
|
||||
seq([]) --> [].
|
||||
seq([E|Es]) --> [E], seq(Es).
|
||||
|
||||
% Describes a sequence of sequences
|
||||
seqq([]) --> [].
|
||||
seqq([Es|Ess]) --> seq(Es), seqq(Ess).
|
||||
|
||||
% Describes an arbitrary number of elements
|
||||
... --> [] | [_], ... .
|
||||
|
||||
user:goal_expansion(phrase(GRBody, S, S0), GRBody1) :-
|
||||
load_context(GRBody, M, GRBody0),
|
||||
nonvar(GRBody0),
|
||||
catch(dcgs:dcg_body(GRBody0, S, S0, GRBody1, M),
|
||||
error(E, must_be/2),
|
||||
( GRBody1 = throw(error(E, must_be/2)) )
|
||||
).
|
||||
|
||||
user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])).
|
||||
26
src/lib/debug.pl
Normal file
26
src/lib/debug.pl
Normal file
@@ -0,0 +1,26 @@
|
||||
% Source: 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).
|
||||
|
||||
$-(G_0) :-
|
||||
catch(G_0, Ex, ( portray_clause(exception:Ex:G_0), throw(Ex) ) ).
|
||||
|
||||
$(G_0) :-
|
||||
portray_clause(call:G_0),
|
||||
$-G_0,
|
||||
portray_clause(exit:G_0).
|
||||
|
||||
*(_).
|
||||
24
src/lib/diag.pl
Normal file
24
src/lib/diag.pl
Normal file
@@ -0,0 +1,24 @@
|
||||
:- module(diag, [wam_instructions/2]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
|
||||
|
||||
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))
|
||||
).
|
||||
|
||||
|
||||
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))
|
||||
).
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
put_dif_att(Var, X, Y) :-
|
||||
( get_atts(Var, +dif(Z)) ->
|
||||
sort([X \== Y | Z], NewZ),
|
||||
put_atts(Var, +dif(NewZ))
|
||||
sort([X \== Y | Z], NewZ),
|
||||
put_atts(Var, +dif(NewZ))
|
||||
; put_atts(Var, +dif([X \== Y]))
|
||||
).
|
||||
|
||||
@@ -21,8 +21,8 @@ dif_set_variables([Var|Vars], X, Y) :-
|
||||
append_goals([], _).
|
||||
append_goals([Var|Vars], Goals) :-
|
||||
( get_atts(Var, +dif(VarGoals)) ->
|
||||
append(Goals, VarGoals, NewGoals0),
|
||||
sort(NewGoals0, NewGoals)
|
||||
append(Goals, VarGoals, NewGoals0),
|
||||
sort(NewGoals0, NewGoals)
|
||||
; NewGoals = Goals
|
||||
),
|
||||
put_atts(Var, +dif(NewGoals)),
|
||||
@@ -30,23 +30,27 @@ append_goals([Var|Vars], Goals) :-
|
||||
|
||||
verify_attributes(Var, Value, Goals) :-
|
||||
( get_atts(Var, +dif(Goals)) ->
|
||||
term_variables(Value, ValueVars),
|
||||
append_goals(ValueVars, Goals)
|
||||
term_variables(Value, ValueVars),
|
||||
append_goals(ValueVars, Goals)
|
||||
; Goals = []
|
||||
).
|
||||
|
||||
% Probably the world's worst dif/2 implementation. I'm open to
|
||||
% suggestions for improvement.
|
||||
|
||||
dif(X, Y) :- X \== Y,
|
||||
( term_variables(X, XVars), term_variables(Y, YVars),
|
||||
dif_set_variables(XVars, X, Y),
|
||||
dif_set_variables(YVars, X, Y)
|
||||
).
|
||||
dif(X, Y) :-
|
||||
X \== Y,
|
||||
( X \= Y -> true
|
||||
; ( term_variables(X, XVars),
|
||||
term_variables(Y, YVars),
|
||||
dif_set_variables(XVars, X, Y),
|
||||
dif_set_variables(YVars, X, Y)
|
||||
)
|
||||
).
|
||||
|
||||
gather_dif_goals([]) --> [].
|
||||
gather_dif_goals([(X \== Y) | Goals]) -->
|
||||
[dif(X, Y)],
|
||||
[dif:dif(X, Y)],
|
||||
gather_dif_goals(Goals).
|
||||
|
||||
attribute_goals(X) -->
|
||||
@@ -1,3 +1,8 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written September 2018 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/1,
|
||||
@@ -5,12 +10,6 @@
|
||||
type_error/3
|
||||
]).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written September 2018 by Markus Triska (triska@metalevel.at)
|
||||
I place this code in the public domain. Use it in any way you want.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
must_be(Type, Term)
|
||||
|
||||
@@ -25,9 +24,12 @@
|
||||
|
||||
Currently, the following types are supported:
|
||||
|
||||
- integer
|
||||
- atom
|
||||
- list.
|
||||
- boolean
|
||||
- character
|
||||
- chars
|
||||
- integer
|
||||
- list
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
must_be(Type, Term) :-
|
||||
@@ -39,12 +41,31 @@ must_be_(Type, _) :-
|
||||
instantiation_error(must_be/2).
|
||||
must_be_(var, Term) :-
|
||||
( var(Term) -> true
|
||||
; throw(error(uninstantiation_error, must_be/2))
|
||||
; throw(error(uninstantiation_error(Term), must_be/2))
|
||||
).
|
||||
must_be_(integer, Term) :- check_(integer, integer, Term).
|
||||
must_be_(atom, Term) :- check_(atom, atom, Term).
|
||||
must_be_(list, Term) :- check_(ilist, list, Term).
|
||||
must_be_(type, Term) :- check_(type, type, Term).
|
||||
must_be_(character, T) :- check_(error:character, character, T).
|
||||
must_be_(chars, Ls) :-
|
||||
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_(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).
|
||||
|
||||
% 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)
|
||||
@@ -52,15 +73,27 @@ check_(Pred, Type, Term) :-
|
||||
; type_error(Type, Term, must_be/2)
|
||||
).
|
||||
|
||||
ilist(V) :- var(V), instantiation_error(must_be/2).
|
||||
ilist([]).
|
||||
ilist([_|Ls]) :- ilist(Ls).
|
||||
boolean(B) :- ( B == true ; B == false ).
|
||||
|
||||
character(C) :-
|
||||
atom(C),
|
||||
atom_length(C, 1).
|
||||
|
||||
ilist(Ls) :-
|
||||
'$skip_max_list'(_, _, Ls, Rs),
|
||||
( var(Rs) ->
|
||||
instantiation_error(must_be/2)
|
||||
; Rs == []
|
||||
).
|
||||
|
||||
type(type).
|
||||
type(integer).
|
||||
type(atom).
|
||||
type(character).
|
||||
type(chars).
|
||||
type(list).
|
||||
type(var).
|
||||
type(boolean).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
can_be(Type, Term)
|
||||
@@ -85,12 +118,16 @@ can_be(Type, Term) :-
|
||||
|
||||
can_(integer, Term) :- integer(Term).
|
||||
can_(atom, Term) :- atom(Term).
|
||||
can_(character, T) :- character(T).
|
||||
can_(chars, Ls) :- '$is_partial_string'(Ls).
|
||||
can_(list, Term) :- list_or_partial_list(Term).
|
||||
can_(boolean, Term) :- boolean(Term).
|
||||
|
||||
list_or_partial_list(Var) :- var(Var).
|
||||
list_or_partial_list([]).
|
||||
list_or_partial_list([_|Ls]) :-
|
||||
list_or_partial_list(Ls).
|
||||
list_or_partial_list(Ls) :-
|
||||
'$skip_max_list'(_, _, Ls, Rs),
|
||||
( var(Rs) -> true
|
||||
; Rs == []
|
||||
).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Shorthands for throwing ISO errors.
|
||||
228
src/lib/files.pl
Normal file
228
src/lib/files.pl
Normal file
@@ -0,0 +1,228 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written June 2020 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,
|
||||
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)).
|
||||
|
||||
list_of_chars(Cs) :-
|
||||
must_be(list, Cs),
|
||||
maplist(must_be(character), Cs).
|
||||
|
||||
directory_files(Directory, Files) :-
|
||||
list_of_chars(Directory),
|
||||
can_be(list, Files),
|
||||
'$directory_files'(Directory, Files).
|
||||
|
||||
file_size(File, Size) :-
|
||||
file_must_exist(File, file_size/2),
|
||||
list_of_chars(File),
|
||||
can_be(integer, Size),
|
||||
'$file_size'(File, Size).
|
||||
|
||||
file_exists(File) :-
|
||||
list_of_chars(File),
|
||||
'$file_exists'(File).
|
||||
|
||||
directory_exists(Directory) :-
|
||||
list_of_chars(Directory),
|
||||
'$directory_exists'(Directory).
|
||||
|
||||
make_directory(Directory) :-
|
||||
list_of_chars(Directory),
|
||||
'$make_directory'(Directory).
|
||||
|
||||
make_directory_path(Directory) :-
|
||||
list_of_chars(Directory),
|
||||
'$make_directory_path'(Directory).
|
||||
|
||||
delete_file(File) :-
|
||||
file_must_exist(File, delete_file/1),
|
||||
list_of_chars(File),
|
||||
'$delete_file'(File).
|
||||
|
||||
rename_file(File, Renamed) :-
|
||||
file_must_exist(File, rename_file/2),
|
||||
list_of_chars(File),
|
||||
list_of_chars(Renamed),
|
||||
'$rename_file'(File, Renamed).
|
||||
|
||||
delete_directory(Directory) :-
|
||||
directory_must_exist(Directory, delete_directory/1),
|
||||
list_of_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))
|
||||
).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Dir0 is the current working directory, and the working directory
|
||||
is changed to Dir.
|
||||
|
||||
Use working_directory(Ds, Ds) 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).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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(list, Ps),
|
||||
maplist(must_be(character), 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) :-
|
||||
file_time_(File, modification, T).
|
||||
|
||||
file_access_time(File, T) :-
|
||||
file_time_(File, access, T).
|
||||
|
||||
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(list_of_chars, Segments),
|
||||
append_with_separator(Segments, Sep, Path)
|
||||
; list_of_chars(Path),
|
||||
path_to_segments(Path, Sep, Segments)
|
||||
).
|
||||
|
||||
append_with_separator([], _, []).
|
||||
append_with_separator([Segment|Segments], Sep, Path) :-
|
||||
append_with_separator_(Segments, Segment, Sep, Path).
|
||||
|
||||
append_with_separator_([], Segment, _, Segment).
|
||||
append_with_separator_([Segment|Segments], Prev, Sep, Path) :-
|
||||
append(Prev, [Sep|Rest], Path),
|
||||
append_with_separator_(Segments, Segment, Sep, Rest).
|
||||
|
||||
path_to_segments(Path, Sep, Segments) :-
|
||||
( append(Front, [Sep|Ps], Path) ->
|
||||
Segments = [Front|Rest],
|
||||
path_to_segments(Ps, Sep, Rest)
|
||||
; Segments = [Path]
|
||||
).
|
||||
@@ -1,9 +1,9 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written March 2020 by Markus Triska (triska@metalevel.at)
|
||||
Written 2020, 2021, 2022 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
This library provides the nonterminal format_//2 to describe
|
||||
formatted strings. format/2 is provided for impure output.
|
||||
formatted strings. format/[2,3] are provided for impure output.
|
||||
|
||||
Usage:
|
||||
======
|
||||
@@ -28,9 +28,13 @@
|
||||
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
|
||||
@@ -55,7 +59,12 @@
|
||||
|
||||
If at all possible, format_//2 should be used, to stress pure parts
|
||||
that enable easy testing etc. If necessary, you can emit the list Ls
|
||||
with maplist(write, 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.
|
||||
|
||||
The entire library only works if the Prolog flag double_quotes
|
||||
is set to chars, the default value in Scryer Prolog. This should
|
||||
@@ -64,8 +73,7 @@
|
||||
Example:
|
||||
|
||||
?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs).
|
||||
%@ Cs = "hello\n......there!"
|
||||
%@ ; false.
|
||||
%@ Cs = "hello\n......there!".
|
||||
|
||||
I place this code in the public domain. Use it in any way you want.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
@@ -73,7 +81,9 @@
|
||||
:- module(format, [format_//2,
|
||||
format/2,
|
||||
format/3,
|
||||
portray_clause_//1,
|
||||
portray_clause/1,
|
||||
portray_clause/2,
|
||||
listing/1
|
||||
]).
|
||||
|
||||
@@ -82,11 +92,13 @@
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(between)).
|
||||
:- use_module(library(pio)).
|
||||
|
||||
format_(Fs, Args) -->
|
||||
{ must_be(list, Fs),
|
||||
must_be(list, Args),
|
||||
phrase(cells(Fs,Args,0,[]), Cells) },
|
||||
unique_variable_names(Args, VNs),
|
||||
phrase(cells(Fs,Args,0,[],VNs), Cells) },
|
||||
format_cells(Cells).
|
||||
|
||||
format_cells([]) --> [].
|
||||
@@ -119,14 +131,11 @@ format_elements([E|Es]) -->
|
||||
format_element(E),
|
||||
format_elements(Es).
|
||||
|
||||
format_element(chars(Cs)) --> list(Cs).
|
||||
format_element(chars(Cs)) --> seq(Cs).
|
||||
format_element(glue(Fill,Num)) -->
|
||||
{ length(Ls, Num),
|
||||
maplist(=(Fill), Ls) },
|
||||
list(Ls).
|
||||
|
||||
list([]) --> [].
|
||||
list([L|Ls]) --> [L], list(Ls).
|
||||
seq(Ls).
|
||||
|
||||
elements_gluevars([], N, N) --> [].
|
||||
elements_gluevars([E|Es], N0, N) -->
|
||||
@@ -140,7 +149,7 @@ element_gluevar(glue(_,V), N, N) --> [V].
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Our key datastructure is a list of cells and newlines.
|
||||
A cell has the shape from_to(From,To,Elements), where
|
||||
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,
|
||||
@@ -152,76 +161,86 @@ element_gluevar(glue(_,V), N, N) --> [V].
|
||||
available space is distributed.
|
||||
|
||||
newline is used if ~n occurs in a format string.
|
||||
It is is used because a newline character does not
|
||||
It is used because a newline character does not
|
||||
consume whitespace in the sense of format strings.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
cells([], Args, Tab, Es) -->
|
||||
cells([], Args, Tab, Es, _) --> !,
|
||||
( { Args == [] } -> cell(Tab, Tab, Es)
|
||||
; { domain_error(no_remaining_arguments, Args, format_//2) }
|
||||
; { domain_error(empty_list, Args, format_//2) }
|
||||
).
|
||||
cells([~,~|Fs], Args, Tab, Es) --> !,
|
||||
cells(Fs, Args, Tab, [chars("~")|Es]).
|
||||
cells([~,w|Fs], [Arg|Args], Tab, Es) --> !,
|
||||
{ write_term_to_chars(Arg, [], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es]).
|
||||
cells([~,q|Fs], [Arg|Args], Tab, Es) --> !,
|
||||
{ write_term_to_chars(Arg, [quoted(true)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es]).
|
||||
cells([~,a|Fs], [Arg|Args], Tab, Es) --> !,
|
||||
cells([~,~|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [chars("~")|Es], VNs).
|
||||
cells([~,w|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ write_term_to_chars(Arg, [numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
cells([~,q|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ write_term_to_chars(Arg, [quoted(true),numbervars(true),variable_names(VNs)], Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
cells([~,a|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ atom_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es]).
|
||||
cells([~|Fs0], Args0, Tab, Es) -->
|
||||
{ numeric_argument(Fs0, Num, [d|Fs], Args0, [Arg|Args]) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [d|Fs], Args0, [Arg0|Args]) },
|
||||
!,
|
||||
{ number_chars(Arg, Cs0) },
|
||||
{ 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.",list(Zs),list(Cs0)), Cs)
|
||||
phrase(("0.",seq(Zs),seq(Cs0)), Cs)
|
||||
; BeforeComma is L - Num,
|
||||
length(Bs, BeforeComma),
|
||||
append(Bs, Ds, Cs0),
|
||||
phrase((list(Bs),".",list(Ds)), Cs)
|
||||
phrase((seq(Bs),".",seq(Ds)), Cs)
|
||||
) }
|
||||
),
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es]).
|
||||
cells([~|Fs0], Args0, Tab, Es) -->
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['D'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ number_chars(Num, NCs),
|
||||
phrase(("~",list(NCs),"d"), FStr),
|
||||
phrase(format_(FStr, [Arg]), Cs0),
|
||||
phrase(upto_what(Bs0, .), Cs0, Ds),
|
||||
reverse(Bs0, Bs1),
|
||||
phrase(groups_of_three(Bs1), Bs2),
|
||||
reverse(Bs2, Bs),
|
||||
append(Bs, Ds, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es]).
|
||||
cells([~,i|Fs], [_|Args], Tab, Es) --> !,
|
||||
cells(Fs, Args, Tab, Es).
|
||||
cells([~,n|Fs], Args, Tab, Es) --> !,
|
||||
{ separate_digits_fractional(Arg, ',', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['U'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ separate_digits_fractional(Arg, '_', Num, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num0, ['L'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ ( Num0 =:= 0 ->
|
||||
Num = 72
|
||||
; Num = Num0
|
||||
),
|
||||
phrase(format_("~d", [Arg]), Cs0),
|
||||
phrase(split_lines_width(Cs0, Num), Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
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, []).
|
||||
cells([~|Fs0], Args0, Tab, Es) -->
|
||||
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, []).
|
||||
cells([~,s|Fs], [Arg|Args], Tab, Es) --> !,
|
||||
cells(Fs, Args, Tab, [chars(Arg)|Es]).
|
||||
cells([~,f|Fs], [Arg|Args], Tab, Es) --> !,
|
||||
{ number_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es]).
|
||||
cells([~|Fs0], Args0, Tab, Es) -->
|
||||
cells(Fs, Args, 0, [], VNs).
|
||||
cells([~,s|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [chars(Arg)|Es], VNs).
|
||||
cells([~,f|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||
{ format_number_chars(Arg, Chars) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [f|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ number_chars(Arg, Cs0),
|
||||
{ format_number_chars(Arg, Cs0),
|
||||
phrase(upto_what(Bs, .), Cs0, Cs),
|
||||
( Num =:= 0 -> Chars = Bs
|
||||
; ( Cs = ['.'|Rest] ->
|
||||
@@ -245,39 +264,54 @@ cells([~|Fs0], Args0, Tab, Es) -->
|
||||
),
|
||||
append(Bs, ['.'|Ds], Chars)
|
||||
) },
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es]).
|
||||
cells([~|Fs0], Args0, Tab, Es) -->
|
||||
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||
cells([~,r|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells([~,'8',r|Fs], Args, Tab, Es, VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [r|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ integer_to_radix(Arg, Num, lowercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es]).
|
||||
cells([~|Fs0], Args0, Tab, Es) -->
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
cells([~,'R'|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells([~,'8','R'|Fs], Args, Tab, Es, VNs).
|
||||
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, ['R'|Fs], Args0, [Arg|Args]) },
|
||||
!,
|
||||
{ integer_to_radix(Arg, Num, uppercase, Cs) },
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es]).
|
||||
cells([~,'`',Char,t|Fs], Args, Tab, Es) --> !,
|
||||
cells(Fs, Args, Tab, [glue(Char,_)|Es]).
|
||||
cells([~,t|Fs], Args, Tab, Es) --> !,
|
||||
cells(Fs, Args, Tab, [glue(' ',_)|Es]).
|
||||
cells([~|Fs0], Args0, Tab, Es) -->
|
||||
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||
cells([~,'`',Char,t|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [glue(Char,_)|Es], VNs).
|
||||
cells([~,t|Fs], Args, Tab, Es, VNs) --> !,
|
||||
cells(Fs, Args, Tab, [glue(' ',_)|Es], VNs).
|
||||
cells([~,'|'|Fs], Args, Tab0, Es, VNs) --> !,
|
||||
{ phrase(elements_gluevars(Es, 0, Width), _),
|
||||
Tab is Tab0 + Width },
|
||||
cell(Tab0, Tab, Es),
|
||||
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, []).
|
||||
cells([~|Fs0], Args0, Tab0, Es) -->
|
||||
cells(Fs, Args, Num, [], VNs).
|
||||
cells([~|Fs0], Args0, Tab0, Es, VNs) -->
|
||||
{ numeric_argument(Fs0, Num, [+|Fs], Args0, Args) },
|
||||
!,
|
||||
{ Tab is Tab0 + Num },
|
||||
cell(Tab0, Tab, Es),
|
||||
cells(Fs, Args, Tab, []).
|
||||
cells([~,C|_], _, _, _) -->
|
||||
{ atom_chars(A, [~,C]),
|
||||
domain_error(format_string, A, format_//2) }.
|
||||
cells(Fs0, Args, Tab, 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]).
|
||||
cells(Fs, Args, Tab, [chars(Fs1)|Es], VNs).
|
||||
|
||||
format_number_chars(N0, Chars) :-
|
||||
N is N0, % evaluate compound expression
|
||||
number_chars(N, Chars).
|
||||
|
||||
n_newlines(0) --> !.
|
||||
n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N).
|
||||
@@ -289,12 +323,30 @@ Cs = [a,b,c], Rest = [~,t,e,s,t].
|
||||
Cs = [a,b,c], 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]) --> !, [A,B,C], ",", groups_of_three([D|Rs]).
|
||||
groups_of_three(Ls) --> list(Ls).
|
||||
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 == [] } -> []
|
||||
@@ -302,36 +354,39 @@ cell(From, To, Es0) -->
|
||||
[cell(From,To,Es)]
|
||||
).
|
||||
|
||||
%?- numeric_argument("2f", Num, ['f'|Fs], Args0, Args).
|
||||
%?- format:numeric_argument("2f", Num, [f|Fs], Args0, Args).
|
||||
|
||||
%?- numeric_argument("100b", Num, Rs, Args0, Args).
|
||||
%?- format:numeric_argument("100b", Num, Rs, Args0, Args).
|
||||
|
||||
numeric_argument(Ds, Num, Rest, Args0, Args) :-
|
||||
( Ds = [*|Rest] ->
|
||||
Args0 = [Num|Args]
|
||||
; numeric_argument_(Ds, [], Ns, Rest),
|
||||
foldl(pow10, Ns, 0-0, Num-_),
|
||||
; phrase(numeric_argument_(Ds, Rest), Ns),
|
||||
foldl(plus_times10, Ns, 0, Num),
|
||||
Args0 = Args
|
||||
).
|
||||
|
||||
numeric_argument_([D|Ds], Ns0, Ns, Rest) :-
|
||||
( member(D, "0123456789") ->
|
||||
number_chars(N, [D]),
|
||||
numeric_argument_(Ds, [N|Ns0], Ns, Rest)
|
||||
; Ns = Ns0,
|
||||
Rest = [D|Ds]
|
||||
numeric_argument_([D|Ds], Rest) -->
|
||||
( { member(D, "0123456789") } ->
|
||||
{ number_chars(N, [D]) },
|
||||
[N],
|
||||
numeric_argument_(Ds, Rest)
|
||||
; { Rest = [D|Ds] }
|
||||
).
|
||||
|
||||
|
||||
pow10(D, N0-Pow0, N-Pow) :-
|
||||
N is N0 + D*10^Pow0,
|
||||
Pow is Pow0 + 1.
|
||||
plus_times10(D, N0, N) :- N is D + N0*10.
|
||||
|
||||
integer_to_radix(I, R, Which, Cs) :-
|
||||
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) ->
|
||||
domain_error(radix, R, format_//2)
|
||||
phrase(radix_error(Which,R), Es),
|
||||
domain_error(format_string, Es, format_//2)
|
||||
; true
|
||||
),
|
||||
digits(Which, Ds),
|
||||
@@ -347,8 +402,7 @@ integer_to_radix_(0, _, _) --> !.
|
||||
integer_to_radix_(I0, R, Ds) -->
|
||||
{ M is I0 mod R,
|
||||
nth0(M, Ds, D),
|
||||
I is I0 // R
|
||||
},
|
||||
I is I0 // R },
|
||||
[D],
|
||||
integer_to_radix_(I, R, Ds).
|
||||
|
||||
@@ -361,78 +415,58 @@ digits(uppercase, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
format(Fs, Args) :-
|
||||
phrase(format_(Fs, Args), Cs),
|
||||
maplist(write, Cs).
|
||||
current_output(Stream),
|
||||
format(Stream, Fs, Args).
|
||||
|
||||
format(Stream, Fs, Args) :-
|
||||
phrase(format_(Fs, Args), Cs),
|
||||
( stream_property(Stream, type(binary)) ->
|
||||
% maplist(char_code, Cs, Bytes) is currently a lot slower
|
||||
% than first converting Cs to an atom, and then to codes.
|
||||
% In the future, we can ideally avoid creating an atom here,
|
||||
% since an atom leaves traces in the system.
|
||||
atom_chars(A, Cs),
|
||||
atom_codes(A, Bytes),
|
||||
( member(NonByte, Bytes), NonByte > 255 ->
|
||||
char_code(Char, NonByte),
|
||||
throw(error(representation_error(Char), format/3))
|
||||
; true
|
||||
),
|
||||
% For binary streams, we use a specialised internal predicate
|
||||
% that uses only a single "write" operation for efficiency.
|
||||
'$put_bytes'(Stream, Bytes)
|
||||
; maplist(put_char(Stream), Cs)
|
||||
).
|
||||
phrase_to_stream(format_(Fs, Args), Stream),
|
||||
flush_output(Stream).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
?- phrase(cells("hello", [], 0, []), Cs).
|
||||
?- phrase(format:cells("hello", [], 0, [], []), Cs).
|
||||
|
||||
?- phrase(cells("hello~10|", [], 0, []), Cs).
|
||||
?- phrase(cells("~ta~t~10|", [], 0, []), Cs).
|
||||
?- phrase(format:cells("hello~10|", [], 0, [], []), Cs).
|
||||
?- phrase(format:cells("~ta~t~10|", [], 0, [], []), Cs).
|
||||
|
||||
?- phrase(format_("~`at~50|", []), Ls).
|
||||
|
||||
?- phrase(cells("~`at~50|", [], 0, []), Cs),
|
||||
phrase(format_cells(Cs), Ls).
|
||||
?- phrase(cells("~ta~t~tb~tc~21|", [], 0, []), Cs).
|
||||
Cs = [cell(0,21,[glue(' ',_38),chars([a]),glue(' ',_62),glue(' ',_67),chars([b]),glue(' ',_91),chars([c])])].
|
||||
?- phrase(cells("~ta~t~4|", [], 0, []), Cs).
|
||||
Cs = [cell(0,4,[glue(' ',_38),chars([a]),glue(' ',_62)])].
|
||||
?- 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_cell(cell(0,1,[glue(a,_94)])), Ls).
|
||||
?- phrase(format:format_cell(cell(0,1,[glue(a,_94)])), Ls).
|
||||
|
||||
?- phrase(format_cell(cell(0,50,[chars("hello")])), 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
|
||||
; false.
|
||||
a true.
|
||||
|
||||
?- format("~ta~tb~tc~10|", []).
|
||||
a b c true
|
||||
; false.
|
||||
a b c true.
|
||||
|
||||
?- format("~tabc~3|", []).
|
||||
|
||||
?- format("~ta~t~4|", []).
|
||||
|
||||
?- format("~ta~t~tb~tc~20|", []).
|
||||
a b c true
|
||||
; false.
|
||||
a b c true.
|
||||
|
||||
?- format("~2f~n", [3]).
|
||||
3.00
|
||||
true
|
||||
true.
|
||||
|
||||
?- format("~20f", [0.1]).
|
||||
0.10000000000000000000 true % this should use higher accuracy!
|
||||
; false.
|
||||
0.10000000000000000000 true.
|
||||
|
||||
?- X is atan(2), format("~7f~n", [X]).
|
||||
1.1071487
|
||||
X = 1.1071487177940906
|
||||
X = 1.1071487177940906.
|
||||
|
||||
?- format("~`at~50|~n", []).
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
@@ -441,10 +475,10 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
?- format("~t~N", []).
|
||||
|
||||
?- format("~q", [.]).
|
||||
'.' true
|
||||
'.' true.
|
||||
|
||||
?- format("~12r", [300]).
|
||||
210 true
|
||||
210 true.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@@ -458,21 +492,28 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
portray_clause(Term) :-
|
||||
phrase(portray_clause_(Term), Ls),
|
||||
maplist(write, Ls).
|
||||
current_output(Out),
|
||||
portray_clause(Out, Term).
|
||||
|
||||
portray_clause(Stream, Term) :-
|
||||
phrase_to_stream(portray_clause_(Term), Stream),
|
||||
flush_output(Stream).
|
||||
|
||||
portray_clause_(Term) -->
|
||||
{ term_variables(Term, Vs),
|
||||
foldl(var_name, Vs, VNs, 0, _) },
|
||||
{ unique_variable_names(Term, VNs) },
|
||||
portray_(Term, VNs), ".\n".
|
||||
|
||||
unique_variable_names(Term, VNs) :-
|
||||
term_variables(Term, Vs),
|
||||
foldl(var_name, Vs, VNs, 0, _).
|
||||
|
||||
var_name(V, Name=V, Num0, Num) :-
|
||||
charsio:fabricate_var_name(numbervars, Name, Num0),
|
||||
Num is Num0 + 1.
|
||||
|
||||
literal(Lit, VNs) -->
|
||||
{ write_term_to_chars(Lit, [quoted(true),variable_names(VNs)], Ls) },
|
||||
list(Ls).
|
||||
seq(Ls).
|
||||
|
||||
portray_(Var, VNs) --> { var(Var) }, !, literal(Var, VNs).
|
||||
portray_((Head :- Body), VNs) --> !,
|
||||
@@ -490,35 +531,50 @@ body_(Var, C, I, VNs) --> { var(Var) }, !,
|
||||
body_((A,B), C, I, VNs) --> !,
|
||||
body_(A, C, I, VNs), ",\n",
|
||||
body_(B, 0, I, VNs).
|
||||
body_((A ; Else), C, I, VNs) --> % ( If -> Then ; Else )
|
||||
{ nonvar(A), A = (If -> Then) },
|
||||
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, C1, I, VNs).
|
||||
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, C1, I, VNs).
|
||||
else_branch(B, I, VNs).
|
||||
body_(Goal, C, I, VNs) -->
|
||||
indent_to(C, I), literal(Goal, VNs).
|
||||
|
||||
|
||||
else_branch(Else, C, I, 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),
|
||||
"; ",
|
||||
body_(Else, C, C, VNs), "\n",
|
||||
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) -->
|
||||
{ Delta is Indent - CurrentColumn },
|
||||
format_("~t~*|", [Delta]).
|
||||
format_("~t~*|", [Indent-CurrentColumn]).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
?- portray_clause(a).
|
||||
@@ -533,7 +589,7 @@ a :-
|
||||
b,
|
||||
c,
|
||||
d.
|
||||
true
|
||||
true.
|
||||
|
||||
|
||||
?- portray_clause([a,b,c,d]).
|
||||
@@ -3,6 +3,8 @@
|
||||
:- use_module(library(atts)).
|
||||
:- use_module(library(dcgs)).
|
||||
|
||||
:- meta_predicate freeze(?, 0).
|
||||
|
||||
:- attribute frozen/1.
|
||||
|
||||
verify_attributes(Var, Other, Goals) :-
|
||||
84
src/lib/http/http_open.pl
Normal file
84
src/lib/http/http_open.pl
Normal file
@@ -0,0 +1,84 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020, 2021 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
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. Redirects are followed.
|
||||
|
||||
Currently, Options must be the empty list. Options may be
|
||||
added in the future to give more control over the connection.
|
||||
|
||||
We use HTTP/1.0 until we can read chunked transfer-encoding.
|
||||
|
||||
Example:
|
||||
|
||||
?- http_open("https://github.com/mthom/scryer-prolog", S, []).
|
||||
%@ S = '$stream'(0x7fcfc9e00f00).
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(http_open, [http_open/3]).
|
||||
|
||||
:- use_module(library(sockets)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(lists), [member/2]).
|
||||
:- use_module(library(tls)).
|
||||
|
||||
http_open(Address, Stream, Options) :-
|
||||
must_be(list, Options),
|
||||
must_be(list, Address),
|
||||
once(phrase((seq(SchemeCs), "://", seq(Rest)), Address)),
|
||||
atom_chars(Scheme, SchemeCs),
|
||||
chars_host_url(Rest, Host, URL),
|
||||
connect(Scheme, Host, Stream0),
|
||||
format(Stream0, "\
|
||||
GET ~s HTTP/1.0\r\n\
|
||||
Host: ~w\r\n\
|
||||
User-Agent: Scryer Prolog\r\n\
|
||||
Connection: close\r\n\r\n\
|
||||
", [URL,Host]),
|
||||
read_line_to_chars(Stream0, StatusLine, []),
|
||||
once(phrase(("HTTP/1.",(['0']|['1'])," ",[D1]), StatusLine, _)),
|
||||
read_header_lines(Stream0, HeaderLines),
|
||||
handle_response(D1, HeaderLines, Stream0, Stream).
|
||||
|
||||
handle_response('2', _, Stream, Stream). % ok
|
||||
handle_response('3', HeaderLines, Stream0, Stream) :- % redirect
|
||||
close(Stream0),
|
||||
once((member(Line, HeaderLines),
|
||||
phrase(("Location: ",seq(Location),"\r\n"), Line))),
|
||||
http_open(Location, Stream, []).
|
||||
|
||||
% Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
|
||||
|
||||
read_header_lines(Stream, Hs) :-
|
||||
read_line_to_chars(Stream, Cs, []),
|
||||
( Cs == "" -> Hs = []
|
||||
; Cs == "\r\n" -> Hs = []
|
||||
; Hs = [Cs|Rest],
|
||||
read_header_lines(Stream, Rest)
|
||||
).
|
||||
|
||||
chars_host_url(Cs, Host, [/|Us]) :-
|
||||
( phrase((seq(Hs),"/",seq(Us)), Cs) ->
|
||||
true
|
||||
; Hs = Cs,
|
||||
Us = []
|
||||
),
|
||||
atom_chars(Host, Hs).
|
||||
|
||||
connect(https, Host, Stream) :-
|
||||
socket_client_open(Host:443, Stream0, []),
|
||||
atom_chars(Host, HostChars),
|
||||
tls_client_context(Context, [hostname(HostChars)]),
|
||||
tls_client_negotiate(Context, Stream0, Stream).
|
||||
connect(http, Host, Stream) :-
|
||||
socket_client_open(Host:80, Stream, []).
|
||||
|
||||
432
src/lib/http/http_server.pl
Normal file
432
src/lib/http/http_server.pl
Normal file
@@ -0,0 +1,432 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written in December 2020 by Adrián Arroyo (adrian.arroyocalle@gmail.com)
|
||||
Part of Scryer Prolog
|
||||
|
||||
This library provides an starting point to build HTTP server based applications.
|
||||
It currently implements a subset of HTTP/1.0. It is recommended to put a reverse
|
||||
proxy like nginx in front of this server to have access to more advanced features
|
||||
(gzip compression, HTTPS, ...)
|
||||
|
||||
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.
|
||||
|
||||
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)).
|
||||
|
||||
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
|
||||
- HTTP Basic Auth
|
||||
- Keep-Alive support
|
||||
- Session handling via cookies
|
||||
- HTML Templating
|
||||
|
||||
I place this code in the public domain. Use it in any way you want.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(http_server, [
|
||||
http_listen/2,
|
||||
http_headers/2,
|
||||
http_status_code/2,
|
||||
http_body/2,
|
||||
http_redirect/2,
|
||||
http_query/3,
|
||||
url_decode//1
|
||||
]).
|
||||
|
||||
:- meta_predicate http_listen(?, 2).
|
||||
|
||||
:- use_module(library(sockets)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(time)).
|
||||
:- use_module(library(crypto)).
|
||||
|
||||
% Module prefix workaround with meta_predicate
|
||||
http_listen(Port, Module:Handlers0) :-
|
||||
maplist(module_qualification(Module), Handlers0, Handlers),
|
||||
http_listen_(Port, Handlers).
|
||||
|
||||
module_qualification(M, H0, H) :-
|
||||
H0 =.. [Method, Path, Goal],
|
||||
H =.. [Method, Path, M:Goal].
|
||||
|
||||
% Server initialization
|
||||
http_listen_(Port, Handlers) :-
|
||||
must_be(integer, Port),
|
||||
must_be(list, Handlers),
|
||||
once(socket_server_open(Port, Socket)),
|
||||
format("Listening at port ~d\n", [Port]),
|
||||
accept_loop(Socket, Handlers).
|
||||
|
||||
% Server loop
|
||||
accept_loop(Socket, Handlers) :-
|
||||
setup_call_cleanup(socket_server_accept(Socket, _Client, Stream, [type(binary)]),
|
||||
(
|
||||
read_header_lines(Stream, Lines),
|
||||
[Request|Headers] = Lines,
|
||||
(
|
||||
(phrase(parse_request(_Version, Method, Path, Queries), Request), maplist(map_parse_header, Headers, HeadersKV)) -> (
|
||||
(
|
||||
member("content-length"-ContentLength, HeadersKV) ->
|
||||
(number_chars(ContentLengthN, ContentLength), get_bytes(Stream, ContentLengthN, Body))
|
||||
;true
|
||||
),
|
||||
current_time(Time),
|
||||
phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString),
|
||||
format("~s ~w ~s\n", [TimeString, Method, Path]),
|
||||
(
|
||||
match_handler(Handlers, Method, Path, Handler) ->
|
||||
(
|
||||
HttpRequest = http_request(HeadersKV, binary(Body), Queries),
|
||||
HttpResponse = http_response(_, _, _),
|
||||
(call(Handler, HttpRequest, HttpResponse) ->
|
||||
send_response(Stream, HttpResponse)
|
||||
; format(Stream, "HTTP/1.0 500 Internal Server Error\r\n\r\n", [])
|
||||
)
|
||||
)
|
||||
; format(Stream, "HTTP/1.0 404 Not Found\r\n\r\n", [])
|
||||
)
|
||||
);(
|
||||
format(Stream, "HTTP/1.0 400 Bad Request\r\n\r\n", []) % bad format
|
||||
)
|
||||
),
|
||||
! % Remove
|
||||
), close(Stream)),
|
||||
accept_loop(Socket, Handlers).
|
||||
|
||||
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.
|
||||
|
||||
|
||||
% Helper and recommended predicates
|
||||
|
||||
http_headers(http_request(Headers, _, _), Headers).
|
||||
http_headers(http_response(_, _, Headers), Headers).
|
||||
|
||||
http_body(http_request(_, binary(ByteBody), _), text(TextBody)) :- chars_utf8bytes(TextBody, ByteBody).
|
||||
http_body(http_request(Headers, binary(ByteBody), _), form(FormBody)) :-
|
||||
member("content-type"-"application/x-www-form-urlencoded", Headers),
|
||||
chars_utf8bytes(TextBody, ByteBody),
|
||||
phrase(parse_queries(FormBody), TextBody).
|
||||
http_body(http_request(_, Body, _), Body).
|
||||
http_body(http_response(_, Body, _), Body).
|
||||
|
||||
http_status_code(http_response(StatusCode, _, _), StatusCode).
|
||||
|
||||
http_redirect(http_response(307, text("Moved Temporarily"), ["Location"-Uri]), Uri).
|
||||
|
||||
http_query(http_request(_, _, Queries), Key, Value) :- member(Key-Value, Queries).
|
||||
|
||||
% Route matching
|
||||
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([]) --> [].
|
||||
|
||||
% Send responses
|
||||
send_response(Stream, http_response(StatusCode0, file(Filename), Headers)) :-
|
||||
default(StatusCode0, 200, StatusCode),
|
||||
format(Stream, "HTTP/1.0 ~d\r\n", [StatusCode]),
|
||||
overwrite_header("connection"-"Close", Headers, Headers0),
|
||||
write_headers(Stream, Headers0),
|
||||
format(Stream, "\r\n", []),
|
||||
setup_call_cleanup(
|
||||
open(Filename, read, FileStream, [type(binary)]),
|
||||
pipe_bytes(FileStream, Stream),
|
||||
close(FileStream)
|
||||
).
|
||||
|
||||
send_response(Stream, http_response(StatusCode0, text(TextResponse), Headers)) :-
|
||||
default(StatusCode0, 200, StatusCode),
|
||||
format(Stream, "HTTP/1.0 ~d\r\n", [StatusCode]),
|
||||
overwrite_header("content-type"-"text/plain", Headers, Headers0),
|
||||
overwrite_header("connection"-"Close", Headers0, Headers1),
|
||||
write_headers(Stream, Headers1),
|
||||
format(Stream, "\r\n~s", [TextResponse]).
|
||||
|
||||
send_response(Stream, http_response(StatusCode0, binary(BinaryResponse), Headers)) :-
|
||||
default(StatusCode0, 200, StatusCode),
|
||||
format(Stream, "HTTP/1.0 ~d\r\n", [StatusCode]),
|
||||
overwrite_header("connection"-"Close", Headers, Headers0),
|
||||
write_headers(Stream, Headers0),
|
||||
format(Stream, "\r\n", []),
|
||||
put_bytes(Stream, BinaryResponse).
|
||||
|
||||
default(Var, Default, Out) :-
|
||||
(var(Var) -> Out = Default
|
||||
; Var = Out
|
||||
).
|
||||
|
||||
header([]) --> [].
|
||||
header([Key-Value|Headers]) -->
|
||||
format_("~s: ~s\r\n", [Key, Value]),
|
||||
header(Headers).
|
||||
|
||||
write_headers(Stream, Headers) :-
|
||||
phrase(header(Headers), Cs),
|
||||
format(Stream, "~s", [Cs]).
|
||||
|
||||
overwrite_header(Key-Value, [], [Key-Value]).
|
||||
overwrite_header(Key-Value, [Header|Headers], [Header|HeadersOut]) :-
|
||||
Header = Key0-_,
|
||||
Key0 \= Key,
|
||||
overwrite_header(Key-Value, Headers, HeadersOut).
|
||||
overwrite_header(Key-Value, [Header|Headers], [NewHeader|Headers]) :-
|
||||
Header = Key-_,
|
||||
NewHeader = Key-Value.
|
||||
|
||||
parse_request(http_version(Major, Minor), Method, Path, Queries) -->
|
||||
method(Method),
|
||||
" ",
|
||||
parse_path(Path, Queries),
|
||||
" ",
|
||||
"HTTP/",
|
||||
natural(Major),
|
||||
".",
|
||||
natural(Minor),
|
||||
"\r\n".
|
||||
|
||||
parse_path(Path, Queries) -->
|
||||
string_without("?", Path),
|
||||
"?",
|
||||
parse_queries(Queries).
|
||||
|
||||
parse_path(Path, []) -->
|
||||
string_without(" ", Path).
|
||||
|
||||
parse_queries([Key-Value|Queries]) -->
|
||||
string_without("=", Key0),
|
||||
{
|
||||
phrase(url_decode(Key), Key0)
|
||||
},
|
||||
"=",
|
||||
string_without("&", Value0),
|
||||
{
|
||||
phrase(url_decode(Value), Value0)
|
||||
},
|
||||
"&",
|
||||
parse_queries(Queries).
|
||||
|
||||
parse_queries([Key-Value]) -->
|
||||
string_without("=", Key0),
|
||||
{
|
||||
phrase(url_decode(Key), Key0)
|
||||
},
|
||||
"=",
|
||||
string_without(" ", Value0),
|
||||
{
|
||||
phrase(url_decode(Value), Value0)
|
||||
}.
|
||||
|
||||
map_parse_header(Header, HeaderKV) :-
|
||||
phrase(parse_header(HeaderKV), Header).
|
||||
|
||||
parse_header(Key-Value) -->
|
||||
string_without(":", Key0),
|
||||
{
|
||||
chars_lower(Key0, Key)
|
||||
},
|
||||
": ",
|
||||
string_without("\r", Value),
|
||||
"\r\n".
|
||||
|
||||
method(options) --> "OPTIONS".
|
||||
method(get) --> "GET".
|
||||
method(head) --> "HEAD".
|
||||
method(post) --> "POST".
|
||||
method(put) --> "PUT".
|
||||
method(delete) --> "DELETE".
|
||||
|
||||
string_without(Not, [Char|String]) -->
|
||||
[Char],
|
||||
{
|
||||
\+ member(Char, Not)
|
||||
},
|
||||
string_without(Not, String).
|
||||
|
||||
string_without(_, []) -->
|
||||
[].
|
||||
|
||||
natural(Nat) -->
|
||||
natural_(NatChars),
|
||||
{
|
||||
number_chars(Nat, NatChars)
|
||||
}.
|
||||
|
||||
natural_([Nat|Nats]) -->
|
||||
[Nat],
|
||||
{
|
||||
char_type(Nat, decimal_digit)
|
||||
},
|
||||
natural_(Nats).
|
||||
|
||||
natural_([]) -->
|
||||
[].
|
||||
|
||||
read_header_lines(Stream, Hs) :-
|
||||
read_line_to_chars(Stream, Cs, []),
|
||||
( Cs == "" -> Hs = []
|
||||
; Cs == "\r\n" -> Hs = []
|
||||
; Hs = [Cs|Rest],
|
||||
read_header_lines(Stream, Rest)
|
||||
).
|
||||
|
||||
get_bytes(Stream, Length, Res) :- get_bytes(Stream, Length, [], Res).
|
||||
get_bytes(Stream, Length, Acc, Res) :-
|
||||
(Length > 0 -> (
|
||||
get_byte(Stream, B),
|
||||
B =\= -1,
|
||||
get_bytes(Stream, Length - 1, [B|Acc], Res)
|
||||
); reverse(Acc, Res)).
|
||||
|
||||
put_bytes(_, []).
|
||||
put_bytes(Stream, [Byte|Bytes]) :-
|
||||
put_byte(Stream, Byte),
|
||||
put_bytes(Stream, Bytes).
|
||||
|
||||
pipe_bytes(StreamIn, StreamOut) :-
|
||||
get_byte(StreamIn, Byte),
|
||||
(
|
||||
Byte =\= -1 ->
|
||||
(
|
||||
put_byte(StreamOut, Byte),
|
||||
pipe_bytes(StreamIn, StreamOut)
|
||||
)
|
||||
; true).
|
||||
|
||||
% WARNING: This only works for ASCII chars. This code can be modified to support
|
||||
% Latin1 characters also but a completely different approach is needed for other
|
||||
% languages. Since HTTP internals are ASCII, this is fine for this usecase.
|
||||
chars_lower(Chars, Lower) :-
|
||||
maplist(char_lower, Chars, Lower).
|
||||
char_lower(Char, Lower) :-
|
||||
char_code(Char, Code),
|
||||
((Code >= 65,Code =< 90) ->
|
||||
LowerCode is Code + 32,
|
||||
char_code(Lower, LowerCode)
|
||||
; Char = Lower).
|
||||
|
||||
% Decodes a UTF-8 URL Encoded string: RFC-1738
|
||||
url_decode([Char|Chars]) -->
|
||||
[Char],
|
||||
{
|
||||
Char \= '%'
|
||||
},
|
||||
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([]) --> [].
|
||||
@@ -3,75 +3,93 @@
|
||||
|
||||
%% ?- use_module(library(iso_ext)).
|
||||
|
||||
:- module(iso_ext, [bb_b_put/2, bb_get/2, bb_put/2, call_cleanup/2,
|
||||
call_with_inference_limit/3, forall/2,
|
||||
partial_string/1, partial_string/3,
|
||||
partial_string_tail/2, setup_call_cleanup/3,
|
||||
variant/2]).
|
||||
:- module(iso_ext, [bb_b_put/2,
|
||||
bb_get/2,
|
||||
bb_put/2,
|
||||
call_cleanup/2,
|
||||
call_with_inference_limit/3,
|
||||
forall/2,
|
||||
partial_string/1,
|
||||
partial_string/3,
|
||||
partial_string_tail/2,
|
||||
setup_call_cleanup/3,
|
||||
call_nth/2,
|
||||
% variant/2,
|
||||
copy_term_nat/2]).
|
||||
|
||||
:- use_module(library(error), [can_be/2,
|
||||
domain_error/3,
|
||||
instantiation_error/1,
|
||||
type_error/3]).
|
||||
|
||||
:- use_module(library(lists), [maplist/3]).
|
||||
|
||||
:- meta_predicate(forall(0, 0)).
|
||||
|
||||
forall(Generate, Test) :-
|
||||
\+ (Generate, \+ Test).
|
||||
|
||||
%% (non-)backtrackable global variables.
|
||||
|
||||
bb_put(Key, Value) :- atom(Key), !, '$store_global_var'(Key, Value).
|
||||
bb_put(Key, _) :- throw(error(type_error(atom, Key), bb_put/2)).
|
||||
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, NewValue) :-
|
||||
( '$bb_get_with_offset'(Key, OldValue, OldOffset) ->
|
||||
call_cleanup((store_global_var_with_offset(Key, NewValue) ; false),
|
||||
reset_global_var_at_offset(Key, OldValue, OldOffset))
|
||||
; call_cleanup((store_global_var_with_offset(Key, NewValue) ; false),
|
||||
reset_global_var_at_key(Key))
|
||||
bb_b_put(Key, Value) :-
|
||||
( atom(Key) ->
|
||||
'$store_backtrackable_global_var'(Key, Value)
|
||||
; type_error(atom, Key, bb_b_put/2)
|
||||
).
|
||||
|
||||
store_global_var_with_offset(Key, Value) :- '$store_global_var_with_offset'(Key, Value).
|
||||
|
||||
store_global_var(Key, Value) :- '$store_global_var'(Key, Value).
|
||||
|
||||
reset_global_var_at_key(Key) :- '$reset_global_var_at_key'(Key).
|
||||
|
||||
reset_global_var_at_offset(Key, Value, Offset) :- '$reset_global_var_at_offset'(Key, Value, Offset).
|
||||
|
||||
'$bb_get_with_offset'(Key, OldValue, Offset) :-
|
||||
atom(Key), !, '$fetch_global_var_with_offset'(Key, OldValue, Offset).
|
||||
'$bb_get_with_offset'(Key, _, _) :-
|
||||
throw(error(type_error(atom, Key), bb_b_put/2)).
|
||||
|
||||
bb_get(Key, Value) :- atom(Key), !, '$fetch_global_var'(Key, Value).
|
||||
bb_get(Key, _) :- throw(error(type_error(atom, Key), bb_get/2)).
|
||||
|
||||
call_cleanup(G, C) :- setup_call_cleanup(true, G, C).
|
||||
bb_get(Key, Value) :-
|
||||
( atom(Key) ->
|
||||
'$fetch_global_var'(Key, Value)
|
||||
; type_error(atom, Key, bb_get/2)
|
||||
).
|
||||
|
||||
|
||||
% setup_call_cleanup.
|
||||
|
||||
:- meta_predicate(call_cleanup(0, 0)).
|
||||
|
||||
call_cleanup(G, C) :- setup_call_cleanup(true, G, C).
|
||||
|
||||
:- meta_predicate(setup_call_cleanup(0, 0, 0)).
|
||||
|
||||
setup_call_cleanup(S, G, C) :-
|
||||
'$get_b_value'(B),
|
||||
call(S),
|
||||
'$call'(S),
|
||||
'$set_cp_by_default'(B),
|
||||
'$get_current_block'(Bb),
|
||||
( '$call_with_default_policy'(var(C)) ->
|
||||
throw(error(instantiation_error, setup_call_cleanup/3))
|
||||
( C = _:CC,
|
||||
'$call_with_default_policy'(var(CC)) ->
|
||||
instantiation_error(setup_call_cleanup/3)
|
||||
; '$call_with_default_policy'(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, NBb), call(G),
|
||||
'$get_cp'(Cp),
|
||||
'$install_scc_cleaner'(C, NBb),
|
||||
call(G),
|
||||
( '$check_cp'(Cp) ->
|
||||
'$reset_block'(Bb),
|
||||
'$call_with_default_policy'(run_cleaners_without_handling(Cp))
|
||||
; '$call_with_default_policy'(true)
|
||||
; '$reset_block'(NBb),
|
||||
'$fail').
|
||||
'$fail'
|
||||
).
|
||||
scc_helper(_, _, Bb) :-
|
||||
'$reset_block'(Bb),
|
||||
'$get_ball'(Ball),
|
||||
'$call_with_default_policy'(run_cleaners_with_handling),
|
||||
'$erase_ball',
|
||||
'$call_with_default_policy'(run_cleaners_with_handling),
|
||||
'$call_with_default_policy'(throw(Ball)).
|
||||
scc_helper(_, _, _) :-
|
||||
'$get_cp'(Cp),
|
||||
@@ -80,7 +98,8 @@ scc_helper(_, _, _) :-
|
||||
|
||||
:- non_counted_backtracking run_cleaners_with_handling/0.
|
||||
run_cleaners_with_handling :-
|
||||
'$get_scc_cleaner'(C), '$get_level'(B),
|
||||
'$get_scc_cleaner'(C),
|
||||
'$get_level'(B),
|
||||
'$call_with_default_policy'(catch(C, _, true)),
|
||||
'$set_cp_by_default'(B),
|
||||
'$call_with_default_policy'(run_cleaners_with_handling).
|
||||
@@ -101,10 +120,10 @@ run_cleaners_without_handling(Cp) :-
|
||||
% call_with_inference_limit
|
||||
|
||||
:- non_counted_backtracking end_block/4.
|
||||
end_block(_, Bb, NBb, L) :-
|
||||
end_block(_, Bb, NBb, _L) :-
|
||||
'$clean_up_block'(NBb),
|
||||
'$reset_block'(Bb).
|
||||
end_block(B, Bb, NBb, L) :-
|
||||
end_block(B, _Bb, NBb, L) :-
|
||||
'$install_inference_counter'(B, L, _),
|
||||
'$reset_block'(NBb),
|
||||
'$fail'.
|
||||
@@ -115,17 +134,34 @@ handle_ile(B, E, _) :-
|
||||
'$remove_call_policy_check'(B),
|
||||
'$call_with_default_policy'(throw(E)).
|
||||
|
||||
:- meta_predicate(call_with_inference_limit(0, ?, ?)).
|
||||
|
||||
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_default_policy'(call_with_inference_limit(G, L, R, Bb, B)),
|
||||
'$remove_call_policy_check'(B).
|
||||
|
||||
install_inference_counter(B, L, Count0) :-
|
||||
'$install_inference_counter'(B, L, Count0).
|
||||
|
||||
:- meta_predicate(call_with_inference_limit(0,?,?,?,?)).
|
||||
|
||||
:- non_counted_backtracking call_with_inference_limit/5.
|
||||
|
||||
call_with_inference_limit(G, L, R, Bb, B) :-
|
||||
'$install_new_block'(NBb),
|
||||
'$install_inference_counter'(B, L, Count0),
|
||||
call(G),
|
||||
'$call'(G),
|
||||
'$inference_level'(R, B),
|
||||
'$remove_inference_counter'(B, Count1),
|
||||
'$call_with_default_policy'(is(Diff, L - (Count1 - Count0))),
|
||||
@@ -142,14 +178,12 @@ call_with_inference_limit(_, _, R, Bb, B) :-
|
||||
'$erase_ball',
|
||||
'$call_with_default_policy'(handle_ile(B, Ball, R)).
|
||||
|
||||
variant(X, Y) :- '$variant'(X, Y).
|
||||
|
||||
partial_string(String, L, L0) :-
|
||||
( String == [] ->
|
||||
L = L0
|
||||
; catch(atom_chars(Atom, String),
|
||||
error(E, _),
|
||||
throw(error(E, partial_string/3))),
|
||||
error(E, _),
|
||||
throw(error(E, partial_string/3))),
|
||||
'$create_partial_string'(Atom, L, L0)
|
||||
).
|
||||
|
||||
@@ -161,3 +195,41 @@ partial_string_tail(String, Tail) :-
|
||||
'$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) :-
|
||||
can_be(integer, N),
|
||||
( integer(N), N =< 0,
|
||||
domain_error(positive_integer, N, call_nth/2)
|
||||
; true
|
||||
),
|
||||
setup_call_cleanup(call_nth_nesting(ID),
|
||||
( Goal,
|
||||
retract(i_call_nth_nesting(ID,N0)),
|
||||
N1 is N0 + 1,
|
||||
asserta(i_call_nth_nesting(ID,N1)),
|
||||
( integer(N) ->
|
||||
N = N1,
|
||||
!
|
||||
; N = N1
|
||||
)
|
||||
),
|
||||
( retract(i_call_nth_nesting(ID,_)),
|
||||
retract(i_call_nth_counter(ID))
|
||||
)).
|
||||
|
||||
call_nth_nesting(ID) :-
|
||||
( i_call_nth_counter(ID0) ->
|
||||
ID is ID0 + 1
|
||||
; ID = 0
|
||||
),
|
||||
asserta(i_call_nth_nesting(ID, 0)),
|
||||
asserta(i_call_nth_counter(ID)).
|
||||
|
||||
|
||||
copy_term_nat(Source, Dest) :-
|
||||
'$copy_term_without_attr_vars'(Source, Dest).
|
||||
230
src/lib/lambda.pl
Normal file
230
src/lib/lambda.pl
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
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
|
||||
|
||||
@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),_)).
|
||||
303
src/lib/lists.pl
Normal file
303
src/lib/lists.pl
Normal file
@@ -0,0 +1,303 @@
|
||||
:- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5,
|
||||
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,
|
||||
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, ?, ?, ?, ?).
|
||||
|
||||
length(Xs0, N) :-
|
||||
'$skip_max_list'(M, N, Xs0,Xs),
|
||||
!,
|
||||
( Xs == [] -> N = M
|
||||
; nonvar(Xs) -> var(N), Xs = [_|_], throw(error(resource_error(finite_memory),length/2))
|
||||
; nonvar(N) -> R is N-M, length_rundown(Xs, R)
|
||||
; N == Xs -> throw(error(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_addendum([], N, N).
|
||||
length_addendum([_|Xs], N, M) :-
|
||||
M1 is M + 1,
|
||||
length_addendum(Xs, N, M1).
|
||||
|
||||
length_rundown(Xs, 0) :- !, Xs = [].
|
||||
length_rundown([_|Xs], N) :-
|
||||
N1 is N-1,
|
||||
length_rundown(Xs, N1).
|
||||
|
||||
|
||||
member(X, [X|_]).
|
||||
member(X, [_|Xs]) :- member(X, Xs).
|
||||
|
||||
|
||||
select(X, [X|Xs], Xs).
|
||||
select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys).
|
||||
|
||||
|
||||
append([], []).
|
||||
append([L0|Ls0], Ls) :-
|
||||
append(L0, Rest, Ls),
|
||||
append(Ls0, Rest).
|
||||
|
||||
|
||||
append([], R, R).
|
||||
append([X|L], R, [X|S]) :- append(L, R, S).
|
||||
|
||||
|
||||
memberchk(X, Xs) :- member(X, Xs), !.
|
||||
|
||||
|
||||
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(_, []).
|
||||
maplist(Cont1, [E1|E1s]) :-
|
||||
call(Cont1, E1),
|
||||
maplist(Cont1, E1s).
|
||||
|
||||
maplist(_, [], []).
|
||||
maplist(Cont2, [E1|E1s], [E2|E2s]) :-
|
||||
call(Cont2, E1, E2),
|
||||
maplist(Cont2, E1s, E2s).
|
||||
|
||||
maplist(_, [], [], []).
|
||||
maplist(Cont3, [E1|E1s], [E2|E2s], [E3|E3s]) :-
|
||||
call(Cont3, E1, E2, E3),
|
||||
maplist(Cont3, E1s, E2s, E3s).
|
||||
|
||||
maplist(_, [], [], [], []).
|
||||
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s]) :-
|
||||
call(Cont, E1, E2, E3, E4),
|
||||
maplist(Cont, E1s, E2s, E3s, E4s).
|
||||
|
||||
|
||||
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(_, [], [], [], [], [], []).
|
||||
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(_, [], [], [], [], [], [], []).
|
||||
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(_, [], [], [], [], [], [], [], []).
|
||||
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(Ls, S) :-
|
||||
foldl(lists:sum_, Ls, 0, S).
|
||||
|
||||
sum_(L, S0, S) :- S is S0 + L.
|
||||
|
||||
|
||||
|
||||
same_length([], []).
|
||||
same_length([_|As], [_|Bs]) :-
|
||||
same_length(As, Bs).
|
||||
|
||||
|
||||
foldl(Goal_3, Ls, A0, A) :-
|
||||
foldl_(Ls, Goal_3, A0, A).
|
||||
|
||||
foldl_([], _, A, A).
|
||||
foldl_([L|Ls], G_3, A0, A) :-
|
||||
call(G_3, L, A0, A1),
|
||||
foldl_(Ls, G_3, A1, A).
|
||||
|
||||
|
||||
foldl(Goal_4, Xs, Ys, A0, A) :-
|
||||
foldl_(Xs, Ys, Goal_4, A0, A).
|
||||
|
||||
|
||||
foldl_([], [], _, A, A).
|
||||
foldl_([X|Xs], [Y|Ys], G_4, A0, A) :-
|
||||
call(G_4, X, Y, A0, A1),
|
||||
foldl_(Xs, Ys, G_4, A1, A).
|
||||
|
||||
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, 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, Es, E) :-
|
||||
can_be(integer, N),
|
||||
can_be(list, Es),
|
||||
( integer(N) ->
|
||||
nth0_index(N, Es, E)
|
||||
; nth0_search(N, Es, E)
|
||||
).
|
||||
|
||||
nth0_index(0, [E|_], E) :- !.
|
||||
nth0_index(N, [_|Es], E) :-
|
||||
N > 0,
|
||||
N1 is N - 1,
|
||||
nth0_index(N1, Es, E).
|
||||
|
||||
nth0_search(N, Es, E) :-
|
||||
nth0_search(0, N, Es, E).
|
||||
|
||||
nth0_search(N, N, [E|_], E).
|
||||
nth0_search(N0, N, [_|Es], E) :-
|
||||
N1 is N0 + 1,
|
||||
nth0_search(N1, N, Es, E).
|
||||
|
||||
|
||||
list_max([N|Ns], Max) :-
|
||||
foldl(lists:list_max_, Ns, N, Max).
|
||||
|
||||
list_max_(N, Max0, Max) :-
|
||||
Max is max(N, Max0).
|
||||
|
||||
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.
|
||||
% ==
|
||||
%
|
||||
% @error 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).
|
||||
129
src/lib/ops_and_meta_predicates.pl
Normal file
129
src/lib/ops_and_meta_predicates.pl
Normal file
@@ -0,0 +1,129 @@
|
||||
:- op(400, yfx, /).
|
||||
|
||||
% module resolution operator.
|
||||
:- op(600, xfy, :).
|
||||
|
||||
:- op(1199, fx, meta_predicate).
|
||||
|
||||
/* this is an implementation specific declarative operator used to implement call_with_inference_limit/3
|
||||
and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice
|
||||
instructions are unchanged. */
|
||||
:- 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(500, yfx, xor).
|
||||
:- 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, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||
@@ -89,7 +89,7 @@ because the order it relies on may have been changed.
|
||||
% setof/3.
|
||||
|
||||
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([]).
|
||||
71
src/lib/os.pl
Normal file
71
src/lib/os.pl
Normal file
@@ -0,0 +1,71 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(os, [getenv/2,
|
||||
setenv/2,
|
||||
unsetenv/1,
|
||||
shell/1,
|
||||
shell/2,
|
||||
pid/1]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(si)).
|
||||
|
||||
getenv(Key, Value) :-
|
||||
must_be_env_var(Key),
|
||||
'$getenv'(Key, Value).
|
||||
|
||||
setenv(Key, Value) :-
|
||||
must_be_env_var(Key),
|
||||
must_be_chars(Value),
|
||||
'$setenv'(Key, Value).
|
||||
|
||||
unsetenv(Key) :-
|
||||
must_be_env_var(Key),
|
||||
'$unsetenv'(Key).
|
||||
|
||||
shell(Command) :- shell(Command, 0).
|
||||
shell(Command, Status) :-
|
||||
must_be_chars(Command),
|
||||
can_be(integer, Status),
|
||||
'$shell'(Command, Status).
|
||||
|
||||
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).
|
||||
@@ -5,6 +5,8 @@
|
||||
map_list_to_pairs/3]).
|
||||
|
||||
|
||||
:- meta_predicate map_list_to_pairs(0, ?, ?).
|
||||
|
||||
pairs_keys_values([], [], []).
|
||||
pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :-
|
||||
pairs_keys_values(ABs, As, Bs).
|
||||
114
src/lib/pio.pl
Normal file
114
src/lib/pio.pl
Normal file
@@ -0,0 +1,114 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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 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_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(iso_ext), [setup_call_cleanup/3, partial_string/3]).
|
||||
:- use_module(library(lists), [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_to_file(2, ?)).
|
||||
:- meta_predicate(phrase_to_stream(2, ?)).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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(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]),
|
||||
( stream_to_lazy_list(Stream, Xs),
|
||||
phrase(NT, Xs) ),
|
||||
close(Stream))
|
||||
).
|
||||
|
||||
|
||||
stream_to_lazy_list(Stream, Xs) :-
|
||||
stream_property(Stream, position(Pos)),
|
||||
freeze(Xs, reader_step(Stream, Pos, Xs)).
|
||||
|
||||
reader_step(Stream, Pos, Xs0) :-
|
||||
set_stream_position(Stream, Pos),
|
||||
( at_end_of_stream(Stream)
|
||||
-> Xs0 = []
|
||||
; get_n_chars(Stream, 4096, Cs),
|
||||
partial_string(Cs, Xs0, Xs),
|
||||
stream_to_lazy_list(Stream, Xs)
|
||||
).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
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
|
||||
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(byte_char, 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), writing the string described
|
||||
by GRBody to File.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
phrase_to_file(GRBody, File) :-
|
||||
phrase_to_file(GRBody, File, []).
|
||||
|
||||
phrase_to_file(GRBody, File, Options) :-
|
||||
setup_call_cleanup(open(File, write, Stream, Options),
|
||||
phrase_to_stream(GRBody, Stream),
|
||||
close(Stream)).
|
||||
@@ -22,11 +22,11 @@ random(R) :-
|
||||
random_integer(Lower, Upper, R) :-
|
||||
var(R),
|
||||
( (var(Lower) ; var(Upper)) ->
|
||||
instantiation_error(random_integer/3)
|
||||
instantiation_error(random_integer/3)
|
||||
; \+ integer(Lower) ->
|
||||
domain_error(integer, Lower, random_integer/3)
|
||||
type_error(integer, Lower, random_integer/3)
|
||||
; \+ integer(Upper) ->
|
||||
domain_error(integer, Upper, random_integer/3)
|
||||
type_error(integer, Upper, random_integer/3)
|
||||
; Upper > Lower,
|
||||
random(R0),
|
||||
R is floor((Upper - Lower) * R0 + Lower)
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
:- use_module(library(dif)).
|
||||
|
||||
:- meta_predicate(if_(1, 0, 0)).
|
||||
|
||||
if_(If_1, Then_0, Else_0) :-
|
||||
call(If_1, T),
|
||||
( T == true -> call(Then_0)
|
||||
@@ -26,6 +28,8 @@ dif(X, Y, T) :-
|
||||
non(true, false).
|
||||
non(false, true).
|
||||
|
||||
:- meta_predicate(tfilter(2, ?, ?)).
|
||||
|
||||
tfilter(C_2, Es, Fs) :-
|
||||
i_tfilter(Es, C_2, Fs).
|
||||
|
||||
@@ -34,6 +38,8 @@ i_tfilter([E|Es], C_2, Fs0) :-
|
||||
if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs),
|
||||
i_tfilter(Es, C_2, Fs).
|
||||
|
||||
:- meta_predicate(tpartition(2, ?, ?, ?)).
|
||||
|
||||
tpartition(P_2, Xs, Ts, Fs) :-
|
||||
i_tpartition(Xs, P_2, Ts, Fs).
|
||||
|
||||
@@ -44,12 +50,18 @@ i_tpartition([X|Xs], P_2, Ts0, Fs0) :-
|
||||
, ( Fs0 = [X|Fs], Ts0 = Ts ) ),
|
||||
i_tpartition(Xs, P_2, Ts, Fs).
|
||||
|
||||
:- meta_predicate(','(1, 1, ?)).
|
||||
|
||||
','(A_1, B_1, T) :-
|
||||
if_(A_1, call(B_1, T), T = false).
|
||||
|
||||
:- meta_predicate(';'(1, 1, ?)).
|
||||
|
||||
';'(A_1, B_1, T) :-
|
||||
if_(A_1, T = true, call(B_1, T)).
|
||||
|
||||
:- meta_predicate(cond_t(1, 0, ?)).
|
||||
|
||||
cond_t(If_1, Then_0, T) :-
|
||||
if_(If_1, ( Then_0, T = true ), T = false ).
|
||||
|
||||
@@ -60,8 +72,13 @@ i_memberd_t([], _, false).
|
||||
i_memberd_t([X|Xs], E, T) :-
|
||||
if_( X = E, T = true, i_memberd_t(Xs, E, T) ).
|
||||
|
||||
:- meta_predicate(tmember(2, ?)).
|
||||
|
||||
tmember(P_2, [X|Xs]) :-
|
||||
if_( call(P_2, X), true, tmember(P_2, Xs) ).
|
||||
|
||||
:- meta_predicate(tmember_t(2, ?, ?)).
|
||||
|
||||
tmember_t(_P_2, [], false).
|
||||
tmember_t(P_2, [X|Xs], T) :-
|
||||
if_( call(P_2, X), T = true, tmember_t(P_2, Xs, T) ).
|
||||
166
src/lib/serialization/abnf.pl
Normal file
166
src/lib/serialization/abnf.pl
Normal file
@@ -0,0 +1,166 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written Apr 2021 by Aram Panasenco (panasenco@ucla.edu)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
[Core Rules](https://tools.ietf.org/html/rfc5234#appendix-B.1) of the
|
||||
Augmented Backus-Naur Form specification (ABNF - RFC 5234). ABNF commonly
|
||||
serves as the definition language for IETF communication protocols, so
|
||||
having these DCGs can be extremely useful for reasoning about most IETF
|
||||
syntaxes. The DCGs are presented in the order they appear in the RFC.
|
||||
While some DCGs below use `char_type/2`, the most common ones are defined
|
||||
manually in order to take advantage of Prolog's first-argument indexing.
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, Aram Panasenco
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(abnf, [abnf_alpha//1,
|
||||
abnf_bit//1,
|
||||
abnf_char//1,
|
||||
abnf_cr//0,
|
||||
abnf_crlf//0,
|
||||
abnf_ctl//1,
|
||||
abnf_digit//1,
|
||||
abnf_dquote//0,
|
||||
abnf_hexdig//1,
|
||||
abnf_htab//0,
|
||||
abnf_lf//0,
|
||||
abnf_lwsp//0,
|
||||
abnf_octet//1,
|
||||
abnf_sp//0,
|
||||
abnf_vchar//1,
|
||||
abnf_wsp//0 ]).
|
||||
|
||||
:- use_module(library(charsio)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(dif)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
abnf_alpha('a') --> "a".
|
||||
abnf_alpha('b') --> "b".
|
||||
abnf_alpha('c') --> "c".
|
||||
abnf_alpha('d') --> "d".
|
||||
abnf_alpha('e') --> "e".
|
||||
abnf_alpha('f') --> "f".
|
||||
abnf_alpha('g') --> "g".
|
||||
abnf_alpha('h') --> "h".
|
||||
abnf_alpha('i') --> "i".
|
||||
abnf_alpha('j') --> "j".
|
||||
abnf_alpha('k') --> "k".
|
||||
abnf_alpha('l') --> "l".
|
||||
abnf_alpha('m') --> "m".
|
||||
abnf_alpha('n') --> "n".
|
||||
abnf_alpha('o') --> "o".
|
||||
abnf_alpha('p') --> "p".
|
||||
abnf_alpha('q') --> "q".
|
||||
abnf_alpha('r') --> "r".
|
||||
abnf_alpha('s') --> "s".
|
||||
abnf_alpha('t') --> "t".
|
||||
abnf_alpha('u') --> "u".
|
||||
abnf_alpha('v') --> "v".
|
||||
abnf_alpha('w') --> "w".
|
||||
abnf_alpha('x') --> "x".
|
||||
abnf_alpha('y') --> "y".
|
||||
abnf_alpha('z') --> "z".
|
||||
abnf_alpha('A') --> "A".
|
||||
abnf_alpha('B') --> "B".
|
||||
abnf_alpha('C') --> "C".
|
||||
abnf_alpha('D') --> "D".
|
||||
abnf_alpha('E') --> "E".
|
||||
abnf_alpha('F') --> "F".
|
||||
abnf_alpha('G') --> "G".
|
||||
abnf_alpha('H') --> "H".
|
||||
abnf_alpha('I') --> "I".
|
||||
abnf_alpha('J') --> "J".
|
||||
abnf_alpha('K') --> "K".
|
||||
abnf_alpha('L') --> "L".
|
||||
abnf_alpha('M') --> "M".
|
||||
abnf_alpha('N') --> "N".
|
||||
abnf_alpha('O') --> "O".
|
||||
abnf_alpha('P') --> "P".
|
||||
abnf_alpha('Q') --> "Q".
|
||||
abnf_alpha('R') --> "R".
|
||||
abnf_alpha('S') --> "S".
|
||||
abnf_alpha('T') --> "T".
|
||||
abnf_alpha('U') --> "U".
|
||||
abnf_alpha('V') --> "V".
|
||||
abnf_alpha('W') --> "W".
|
||||
abnf_alpha('X') --> "X".
|
||||
abnf_alpha('Y') --> "Y".
|
||||
abnf_alpha('Z') --> "Z".
|
||||
|
||||
abnf_bit('0') --> "0".
|
||||
abnf_bit('1') --> "1".
|
||||
|
||||
abnf_char(Char) --> [Char], { dif(Char, '\x0000\'), char_type(Char, ascii) }. %'
|
||||
|
||||
abnf_cr --> "\r".
|
||||
|
||||
abnf_crlf --> "\r\n".
|
||||
|
||||
abnf_ctl(Char) --> [Char], { char_type(Char, ascii), char_type(Char, control) }.
|
||||
|
||||
abnf_digit('0') --> "0".
|
||||
abnf_digit('1') --> "1".
|
||||
abnf_digit('2') --> "2".
|
||||
abnf_digit('3') --> "3".
|
||||
abnf_digit('4') --> "4".
|
||||
abnf_digit('5') --> "5".
|
||||
abnf_digit('6') --> "6".
|
||||
abnf_digit('7') --> "7".
|
||||
abnf_digit('8') --> "8".
|
||||
abnf_digit('9') --> "9".
|
||||
|
||||
abnf_dquote --> "\"".
|
||||
|
||||
abnf_hexdig(Char) --> abnf_digit(Char).
|
||||
abnf_hexdig('A') --> "A".
|
||||
abnf_hexdig('B') --> "B".
|
||||
abnf_hexdig('C') --> "C".
|
||||
abnf_hexdig('D') --> "D".
|
||||
abnf_hexdig('E') --> "E".
|
||||
abnf_hexdig('F') --> "F".
|
||||
|
||||
abnf_htab --> "\t".
|
||||
|
||||
abnf_lf --> "\n".
|
||||
|
||||
abnf_lwsp --> "".
|
||||
abnf_lwsp --> abnf_wsp, abnf_lwsp.
|
||||
abnf_lwsp --> abnf_crlf, abnf_wsp, abnf_lwsp.
|
||||
|
||||
abnf_octet(Char) --> [Char], char_type(Char, octet).
|
||||
|
||||
abnf_sp --> " ".
|
||||
|
||||
abnf_vchar(Char) --> [Char], char_type(Char, ascii_graphic).
|
||||
|
||||
abnf_wsp --> abnf_sp.
|
||||
abnf_wsp --> abnf_htab.
|
||||
273
src/lib/serialization/json.pl
Normal file
273
src/lib/serialization/json.pl
Normal file
@@ -0,0 +1,273 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written Apr 2021 by Aram Panasenco (panasenco@ucla.edu)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
`json_chars//1` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
|
||||
or [`phrase/2`](src/lib/dcgs.pl) to parse and generate [JSON](https://www.json.org/json-en.html).
|
||||
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, Aram Panasenco
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(json, [
|
||||
json_chars//1
|
||||
]).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(dif)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
/* The DCGs are written to match the McKeeman form presented on the right side of https://www.json.org/json-en.html
|
||||
as closely as possible. Note that the names in the McKeeman form conflict with the pictures on the site. */
|
||||
json_chars(Internal) --> json_element(Internal).
|
||||
|
||||
/* Because it's impossible to distinguish between an empty array [] and an empty string "", we distinguish between
|
||||
different types of values based on their principal functor. The principal functors match the types defined in
|
||||
the JSON Schema spec here: https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.1.1
|
||||
EXCEPT we don't yet support the integer type. There are plans for more JSON Schema support in the near future. */
|
||||
json_value(pairs(Pairs)) --> json_object(Pairs).
|
||||
json_value(list(List)) --> json_array(List).
|
||||
json_value(string(Chars)) --> json_string(Chars).
|
||||
json_value(number(Number)) --> json_number(Number).
|
||||
json_value(boolean(Bool)) --> json_boolean(Bool).
|
||||
json_value(null) --> "null".
|
||||
|
||||
/* We pull json_boolean out into its own predicate in order to take advantage of first argument indexing and not leave
|
||||
choice points. For more details, watch this video on decomposing arguments: https://youtu.be/FZLofckPu4A?t=1648 */
|
||||
json_boolean(true) --> "true".
|
||||
json_boolean(false) --> "false".
|
||||
|
||||
json_object([]) --> "{", json_ws, "}".
|
||||
json_object([Pair|Pairs]) -->
|
||||
"{",
|
||||
json_members(Pairs, Pair),
|
||||
"}".
|
||||
|
||||
/* `json_members//2` below is implemented with a lagged argument to take advantage of first argument indexing.
|
||||
This is a pure performance-driven decision that doesn't affect the logic. The predicate could equivalently be
|
||||
implementes as `json_members//1` below:
|
||||
```
|
||||
json_members([Key-Value, Pair2 | Pairs]) --> json_member(Key, Value), ",", json_members([Pair2 | Pairs]).
|
||||
```
|
||||
That's a logically equivalent and equally clean representation to the lagged argument. However, it leaves
|
||||
choice points, while using the lagged argument doesn't. For more info, watch: https://youtu.be/FZLofckPu4A?t=1737
|
||||
*/
|
||||
json_members([], Key-Value) --> json_member(Key, Value).
|
||||
json_members([NextPair|Pairs], Key-Value) -->
|
||||
json_member(Key, Value),
|
||||
",",
|
||||
json_members(Pairs, NextPair).
|
||||
|
||||
json_member(string(Key), Value) --> json_ws, json_string(Key), json_ws, ":", json_element(Value).
|
||||
|
||||
json_array([]) --> "[", json_ws, "]".
|
||||
json_array([Value|Values]) --> "[", json_elements(Values, Value), "]".
|
||||
|
||||
/* Also using a lagged argument with `json_elements//2` to take advantage of first-argument indexing */
|
||||
json_elements([], Value) --> json_element(Value).
|
||||
json_elements([NextValue|Values], Value) -->
|
||||
json_element(Value),
|
||||
",",
|
||||
json_elements(Values, NextValue).
|
||||
|
||||
json_element(Value) --> json_ws, json_value(Value), json_ws.
|
||||
|
||||
json_string(Chars) --> "\"", json_characters(Chars), "\"".
|
||||
|
||||
json_characters("") --> "".
|
||||
json_characters([Char|Chars]) --> json_character(Char), json_characters(Chars).
|
||||
|
||||
/* Note on variable instantiation checks (`var/1` and `nonvar/1`) used below and in Prolog in general.
|
||||
Instantiation checks should never be used to change the logic of your program. Instead, they are one of
|
||||
many tools to adjust the 'control' or 'search strategy' used by Prolog to execute the logic of your program.
|
||||
For a general overview of the idea, read Bob Kowalski's "Algorithm = Logic + Control":
|
||||
https://www.doc.ic.ac.uk/~rak/papers/algorithm%20=%20logic%20+%20control.pdf
|
||||
For an introduction to search strategies in Prolog, read: https://www.metalevel.at/prolog/sorting#searching
|
||||
It's tempting to use instantiation checks to be more strict while generating and more relaxed while parsing.
|
||||
In fact, the early version of this library aimed to return exactly one result when generating. However, doing that
|
||||
is **wrong** and leads to difficult-to-catch bugs. Instead, adjust the search strategy to return the most ideal
|
||||
and strictest answer FIRST and then return less ideal answers on backtracking.
|
||||
As an example, consider a string containing just the forward slash. The JSON standard recommends the forward slash
|
||||
be escaped with a backslash, but allows it to not be escaped. Attempting to force stricter behavior with
|
||||
instantiation checks can lead to this confusing mess:
|
||||
```
|
||||
phrase(json:json_characters("/"), External).
|
||||
External = "\\/".
|
||||
?- phrase(json:json_characters(Internal), "/").
|
||||
Internal = "/"
|
||||
; false.
|
||||
?- phrase(json:json_characters("/"), "/").
|
||||
false.
|
||||
```
|
||||
To avoid such bugs, never use instantiation checks to reduce the number of right answers, but rather to adjust
|
||||
the *path* used to traverse those answers. */
|
||||
|
||||
escape_char('"', '"').
|
||||
escape_char('\\', '\\').
|
||||
escape_char('/', '/').
|
||||
escape_char('\b', 'b').
|
||||
escape_char('\f', 'f').
|
||||
escape_char('\n', 'n').
|
||||
escape_char('\r', 'r').
|
||||
escape_char('\t', 't').
|
||||
|
||||
json_character(EscapeChar) -->
|
||||
{ escape_char(EscapeChar, PrintChar) },
|
||||
"\\",
|
||||
[PrintChar].
|
||||
json_character(PrintChar) -->
|
||||
[PrintChar],
|
||||
{ dif(PrintChar, '\\'),
|
||||
dif(PrintChar, '"'),
|
||||
char_code(PrintChar, PrintCharCode),
|
||||
PrintCharCode >= 32 }.
|
||||
json_character(EscapeChar) -->
|
||||
"\\u",
|
||||
json_hex(H1),
|
||||
json_hex(H2),
|
||||
json_hex(H3),
|
||||
json_hex(H4),
|
||||
{ ( nonvar(H1) ->
|
||||
EscapeCharCode is H1 * 16^3 + H2 * 16^2 + H3 * 16 + H4,
|
||||
char_code(EscapeChar, EscapeCharCode)
|
||||
; char_code(EscapeChar, EscapeCharCode),
|
||||
H1 is (EscapeCharCode // 16^3) mod 16,
|
||||
H2 is (EscapeCharCode // 16^2) mod 16,
|
||||
H3 is (EscapeCharCode // 16^1) mod 16,
|
||||
H4 is (EscapeCharCode // 16^0) mod 16
|
||||
) }.
|
||||
|
||||
json_hex(Digit) --> json_digit(Digit).
|
||||
json_hex(10) --> "a".
|
||||
json_hex(11) --> "b".
|
||||
json_hex(12) --> "c".
|
||||
json_hex(13) --> "d".
|
||||
json_hex(14) --> "e".
|
||||
json_hex(15) --> "f".
|
||||
json_hex(10) --> "A".
|
||||
json_hex(11) --> "B".
|
||||
json_hex(12) --> "C".
|
||||
json_hex(13) --> "D".
|
||||
json_hex(14) --> "E".
|
||||
json_hex(15) --> "F".
|
||||
|
||||
/* I can't think of any alternatives to using `number_chars/2` when generating, though this leads
|
||||
to under-reporting of correct solutions. At least matching solutions unify when both are instantiated...
|
||||
```
|
||||
?- phrase(json:json_number(N), "123E2").
|
||||
N = 12300
|
||||
; false.
|
||||
?- phrase(json:json_number(12300), Cs).
|
||||
Cs = "12300".
|
||||
?- phrase(json:json_number(12300), "123E2").
|
||||
true
|
||||
; false.
|
||||
```
|
||||
*/
|
||||
parsing, [C] --> [C], { nonvar(C) }.
|
||||
|
||||
json_number(Number) -->
|
||||
( parsing ->
|
||||
json_sign_noplus(Sign),
|
||||
json_integer(Integer),
|
||||
json_fraction(Fraction),
|
||||
json_exponent(Exponent),
|
||||
{ ( Exponent >= 0 ->
|
||||
Base = 10
|
||||
; Base = 10.0
|
||||
),
|
||||
Number is Sign * (Integer + Fraction) * Base ^ Exponent }
|
||||
; { number_chars(Number, NumberChars) },
|
||||
NumberChars
|
||||
).
|
||||
|
||||
json_integer(Digit) --> json_digit(Digit).
|
||||
json_integer(TotalValue) -->
|
||||
json_onenine(FirstDigit),
|
||||
json_digits(RemainingValue, Power),
|
||||
{ TotalValue is FirstDigit * 10 ^ (Power + 1) + RemainingValue }.
|
||||
|
||||
json_digits(Digit, 0) --> json_digit(Digit).
|
||||
json_digits(Value, Power) -->
|
||||
json_digit(FirstDigit),
|
||||
json_digits(RemainingValue, NextPower),
|
||||
{ Power is NextPower + 1,
|
||||
Value is FirstDigit * 10^Power + RemainingValue }.
|
||||
|
||||
json_digit(0) --> "0".
|
||||
json_digit(Digit) --> json_onenine(Digit).
|
||||
|
||||
json_onenine(1) --> "1".
|
||||
json_onenine(2) --> "2".
|
||||
json_onenine(3) --> "3".
|
||||
json_onenine(4) --> "4".
|
||||
json_onenine(5) --> "5".
|
||||
json_onenine(6) --> "6".
|
||||
json_onenine(7) --> "7".
|
||||
json_onenine(8) --> "8".
|
||||
json_onenine(9) --> "9".
|
||||
|
||||
json_fraction(0) --> "".
|
||||
json_fraction(Fraction) -->
|
||||
".",
|
||||
json_digits(Value, Power),
|
||||
{ Fraction is Value / 10.0 ^ (Power + 1) }.
|
||||
|
||||
json_exponent(0) --> "".
|
||||
json_exponent(Exponent) -->
|
||||
json_exponent_signifier,
|
||||
json_sign(Sign),
|
||||
json_digits(Value, _),
|
||||
{ Exponent is Sign * Value }.
|
||||
|
||||
json_exponent_signifier --> "E".
|
||||
json_exponent_signifier --> "e".
|
||||
|
||||
json_sign_noplus(1) --> "".
|
||||
json_sign_noplus(-1) --> "-".
|
||||
|
||||
json_sign(Sign) --> json_sign_noplus(Sign).
|
||||
json_sign(1) --> "+".
|
||||
|
||||
/* Make `json_ws/0` greedy when parsing, lazy when generating */
|
||||
json_ws_empty --> "".
|
||||
json_ws_nonempty --> " ".
|
||||
json_ws_nonempty --> "\n".
|
||||
json_ws_nonempty --> "\r".
|
||||
json_ws_nonempty --> "\t".
|
||||
json_ws_greedy --> json_ws_nonempty, json_ws_greedy.
|
||||
json_ws_greedy --> json_ws_empty.
|
||||
json_ws_lazy --> json_ws_empty.
|
||||
json_ws_lazy --> json_ws_nonempty, json_ws_lazy.
|
||||
json_ws -->
|
||||
( parsing ->
|
||||
json_ws_greedy
|
||||
; json_ws_lazy
|
||||
).
|
||||
83
src/lib/sgml.pl
Normal file
83
src/lib/sgml.pl
Normal file
@@ -0,0 +1,83 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Predicates for parsing HTML and XML documents.
|
||||
Written June 2020 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
Currently, two predicates are provided:
|
||||
|
||||
- load_html(+Source, -Es, +Options)
|
||||
- load_xml(+Source, -Es, +Options)
|
||||
|
||||
These predicates parse HTML and XML documents, respectively.
|
||||
|
||||
Source must be a stream, specified as stream(S), or a file,
|
||||
specified as file(Name), where Name is a list of characters, or a
|
||||
list of characters with the document contents.
|
||||
|
||||
Es is unified with the abstract syntax tree of the parsed document,
|
||||
represented as a list of elements where each is of the form:
|
||||
|
||||
* a list of characters, representing text
|
||||
* element(Name, Attrs, Children)
|
||||
- Name is the name of the tag
|
||||
- Attrs is a list of Key=Value pairs:
|
||||
Key is an atom, and Value is a list of characters
|
||||
- Children is a list of elements as specified here.
|
||||
|
||||
Currently, Options are ignored. In the future, more options may be
|
||||
provided to control parsing.
|
||||
|
||||
Example:
|
||||
|
||||
?- load_html("<html><head><title>Hello!</title></head></html>", Es, []).
|
||||
|
||||
Yielding:
|
||||
|
||||
Es = [element(html,[],
|
||||
[element(head,[],
|
||||
[element(title,[],
|
||||
["Hello!"])]),
|
||||
element(body,[],[])])].
|
||||
|
||||
library(xpath) provides convenient reasoning about parsed documents.
|
||||
For example, to fetch the title of the document above, we can use:
|
||||
|
||||
?- load_html("<html><head><title>Hello!</title></head></html>", Es, []),
|
||||
xpath(Es, //title(text), T).
|
||||
|
||||
Yielding T = "Hello!".
|
||||
|
||||
Use http_open/3 from library(http/http_open) to read answers from
|
||||
web servers via streams.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(sgml, [load_html/3,
|
||||
load_xml/3]).
|
||||
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(pio)).
|
||||
:- use_module(library(charsio)).
|
||||
|
||||
load_html(Source, Es, Options) :-
|
||||
load_structure_(Source, Es, Options, html).
|
||||
load_xml(Source, Es, Options) :-
|
||||
load_structure_(Source, Es, Options, xml).
|
||||
|
||||
load_structure_([], [], _, _).
|
||||
load_structure_([C|Cs], [E], Options, What) :-
|
||||
load_(What, [C|Cs], E, Options).
|
||||
load_structure_(file(Fs), [E], Options, What) :-
|
||||
must_be(list, Options),
|
||||
must_be(list, Fs),
|
||||
atom_chars(File, Fs),
|
||||
once(phrase_from_file(seq(Cs), File)),
|
||||
load_(What, Cs, E, Options).
|
||||
load_structure_(stream(Stream), [E], Options, What) :-
|
||||
must_be(list, Options),
|
||||
get_n_chars(Stream, _, Cs),
|
||||
load_(What, Cs, E, Options).
|
||||
|
||||
load_(html, Cs, E, Options) :- '$load_html'(Cs, E, Options).
|
||||
load_(xml, Cs, E, Options) :- '$load_xml'(Cs, E, Options).
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
:- use_module(library(error)).
|
||||
|
||||
|
||||
socket_client_open(Addr, Stream, Options) :-
|
||||
( var(Addr) ->
|
||||
throw(error(instantiation_error, socket_client_open/3))
|
||||
@@ -8,12 +8,12 @@
|
||||
op(1150, fx, table)
|
||||
]).
|
||||
|
||||
:- use_module('tabling/double_linked_list').
|
||||
:- use_module('tabling/table_data_structure').
|
||||
:- use_module('tabling/batched_worklist').
|
||||
:- use_module('tabling/wrapper').
|
||||
:- use_module('tabling/global_worklist').
|
||||
:- use_module('tabling/table_link_manager').
|
||||
:- use_module(library(tabling/double_linked_list)).
|
||||
:- use_module(library(tabling/table_data_structure)).
|
||||
:- use_module(library(tabling/batched_worklist)).
|
||||
:- use_module(library(tabling/global_worklist)).
|
||||
:- use_module(library(tabling/table_link_manager)).
|
||||
:- use_module(library(tabling/wrapper)).
|
||||
|
||||
:- use_module(library(cont)).
|
||||
:- use_module(library(lists)).
|
||||
@@ -66,6 +66,9 @@ table_and_status_for_variant(V,T,S) :-
|
||||
table_for_variant(V,T),
|
||||
tbd_table_status(T,S).
|
||||
|
||||
|
||||
:- meta_predicate start_tabling(?, 0).
|
||||
|
||||
start_tabling(Wrapper,Worker) :-
|
||||
put_new_trie_table_link,
|
||||
put_new_global_worklist,
|
||||
@@ -45,14 +45,16 @@
|
||||
wkl_worklist_work_done/1 % +WorkList
|
||||
]).
|
||||
|
||||
:- use_module(global_worklist).
|
||||
:- use_module(double_linked_list).
|
||||
:- use_module(library(tabling/global_worklist)).
|
||||
:- use_module(library(tabling/double_linked_list)).
|
||||
|
||||
:- use_module(library(atts)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
:- attribute executing_all_work/1, worklist_presence/1, wkl_answer_cluster/1, wkl_suspension_cluster/1, wkl_answer_cluster_pointer_flag/1.
|
||||
|
||||
verify_attributes(_, _, []).
|
||||
|
||||
/** <module> Tabling Worklist management
|
||||
|
||||
A batched worklist: a worklist that clusters suspensions and answers as
|
||||
@@ -161,6 +163,8 @@ wkl_p_swap_answer_continuation(Worklist,InnerAnswerClusterPointer,SuspensionClus
|
||||
wkl_p_update_righmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer).
|
||||
|
||||
% Update the pointer if the answer cluster it points to is no longer the rightmost inner answer cluster.
|
||||
% Strangely, this predicate was intentionally named "wkl_p_update_righmost_inner_answer_cluster_pointer"
|
||||
% in the original library.
|
||||
wkl_p_update_righmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer) :-
|
||||
( wkl_p_answer_cluster_currently_moved_completely(Worklist,InnerAnswerClusterPointer) ->
|
||||
wkl_p_find_new_rightmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer,NewRiacPointer),
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
:- attribute table_global_worklist/1.
|
||||
|
||||
verify_attributes(_, _, []).
|
||||
|
||||
put_new_global_worklist :-
|
||||
( bb_get(table_global_worklist_initialized, _) ->
|
||||
true
|
||||
@@ -15,8 +15,8 @@
|
||||
get_nb_identifiers/3 % +Table, -NbWorklistID, -NbAnswerTreeID
|
||||
]).
|
||||
|
||||
:- use_module(table_link_manager).
|
||||
:- use_module(trie).
|
||||
:- use_module(library(tabling/table_link_manager)).
|
||||
:- use_module(library(tabling/trie)).
|
||||
|
||||
/* Part of SWI-Prolog
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
:- use_module(batched_worklist).
|
||||
:- use_module(library(tabling/batched_worklist)).
|
||||
|
||||
:- use_module(library(atts)).
|
||||
:- use_module(library(gensym)).
|
||||
@@ -61,6 +61,8 @@
|
||||
|
||||
:- attribute table_status/1, newly_created_table_identifiers/1.
|
||||
|
||||
verify_attributes(_, _, []).
|
||||
|
||||
% This file defines the table datastructure.
|
||||
%
|
||||
% The table datastructure contains the following sub-structures:
|
||||
@@ -47,10 +47,12 @@
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(terms)).
|
||||
|
||||
:- use_module(trie).
|
||||
:- use_module(library(tabling/trie)).
|
||||
|
||||
:- attribute trie_table_link/1.
|
||||
|
||||
verify_attributes(_, _, []).
|
||||
|
||||
% This file defines a call pattern trie.
|
||||
%
|
||||
% This data structure keeps the relation between a variant and the
|
||||
@@ -41,12 +41,16 @@
|
||||
trie_get_all_values/2 % +Trie, -Value
|
||||
]).
|
||||
|
||||
:- use_module(library(format)).
|
||||
|
||||
:- use_module(library(assoc)).
|
||||
:- use_module(library(atts)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
:- attribute maybe_just/1, children/1.
|
||||
|
||||
verify_attributes(_, _, []).
|
||||
|
||||
% Implementation of a prefix tree, a.k.a. trie %
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
@@ -66,7 +70,7 @@
|
||||
% p_trie_arity_univ(+Term,-FunctorData,-ArgumentsList).
|
||||
p_trie_arity_univ(Term,functor_data(Name,Arity),Arguments) :-
|
||||
( var(Term) ->
|
||||
Name = var,
|
||||
Name = Term,
|
||||
Arity = 0,
|
||||
Arguments = []
|
||||
; Term =.. [Name|Arguments],
|
||||
@@ -40,6 +40,8 @@
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(error)).
|
||||
|
||||
:- multifile(tabled/2).
|
||||
|
||||
%%:- multifile
|
||||
%% system:term_expansion/2,
|
||||
%% tabled/2.
|
||||
@@ -54,8 +56,8 @@
|
||||
%% table(PIList) :-
|
||||
%% throw(error(context_error(nodirective, table(PIList)), _)).
|
||||
|
||||
instantiation_error(Var) :-
|
||||
throw(error(instantiation_error(Var), _)).
|
||||
%% instantiation_error(Var) :-
|
||||
%% throw(error(instantiation_error(Var), _)).
|
||||
|
||||
wrappers(Var) -->
|
||||
{ var(Var), !,
|
||||
@@ -75,7 +77,7 @@ wrappers(Name/Arity) -->
|
||||
atom_concat(Name, ' tabled', WrapName),
|
||||
Head =.. [Name|Args],
|
||||
WrappedHead =.. [WrapName|Args],
|
||||
'$module_of'(Module, Name) %prolog_load_context(module, Module)
|
||||
prolog_load_context(module, Module)
|
||||
},
|
||||
[ ( Head :-
|
||||
start_tabling(Module:Head, WrappedHead)
|
||||
@@ -93,10 +95,15 @@ rename((Head --> Body), (NewHead --> Body), Module) :- !,
|
||||
functor(Head, Name, Arity),
|
||||
PlainArity is Arity+1,
|
||||
functor(PlainHead, Name, PlainArity),
|
||||
table_wrapper:tabled(PlainHead, Module),
|
||||
catch(table_wrapper:tabled(PlainHead, Module),
|
||||
error(existence_error(procedure, tabled/2), _),
|
||||
false),
|
||||
rename_term(Head, NewHead).
|
||||
rename(Head, NewHead, Module) :-
|
||||
table_wrapper:tabled(Head, Module), !,
|
||||
catch(table_wrapper:tabled(Head, Module),
|
||||
error(existence_error(procedure, tabled/2), _),
|
||||
false),
|
||||
!,
|
||||
rename_term(Head, NewHead).
|
||||
|
||||
rename_term(Compound0, Compound) :-
|
||||
@@ -109,10 +116,10 @@ rename_term(Name, WrapName) :-
|
||||
|
||||
|
||||
user:term_expansion(Term0, Clauses) :-
|
||||
nonvar(Term0),
|
||||
nonvar(Term0),
|
||||
Term0 = (:- table Preds),
|
||||
phrase(wrappers(Preds), Clauses).
|
||||
user:term_expansion(Clause, NewClause) :-
|
||||
nonvar(Clause),
|
||||
'$module_of'(Module, Clause),
|
||||
nonvar(Clause),
|
||||
prolog_load_context(module, Module),
|
||||
rename(Clause, NewClause, Module).
|
||||
152
src/lib/time.pl
Normal file
152
src/lib/time.pl
Normal file
@@ -0,0 +1,152 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020, 2021 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
This library provides predicates for reasoning about time.
|
||||
|
||||
current_time(T) yields the current system time in an opaque form,
|
||||
called a time stamp. Use format_time//2 to describe strings that
|
||||
contain attributes of the time stamp.
|
||||
|
||||
The nonterminal format_time//2 describes a list of characters that
|
||||
are formatted according to a format string. Usage:
|
||||
|
||||
phrase(format_time(FormatString, TimeStamp), Cs)
|
||||
|
||||
TimeStamp represents a moment in time in an opaque form, as for
|
||||
example obtained by current_time/1.
|
||||
|
||||
FormatString is a list of characters that are interpreted literally,
|
||||
except for the following specifiers (and possibly more in the future):
|
||||
|
||||
%Y year of the time stamp. Example: 2020.
|
||||
%m month number (01-12), zero-padded to 2 digits
|
||||
%d day number (01-31), zero-padded to 2 digits
|
||||
%H hour number (00-24), zero-padded to 2 digits
|
||||
%M minute number (00-59), zero-padded to 2 digits
|
||||
%S second number (00-60), zero-padded to 2 digits
|
||||
%b abbreviated month name, always 3 letters
|
||||
%a abbreviated weekday name, always 3 letters
|
||||
%A full weekday name
|
||||
%j day of the year (001-366), zero-padded to 3 digits
|
||||
%% the literal %
|
||||
|
||||
Example:
|
||||
|
||||
?- current_time(T), phrase(format_time("%d.%m.%Y (%H:%M:%S)", T), Cs).
|
||||
T = [...], Cs = "11.06.2020 (00:24:32)".
|
||||
|
||||
sleep(S) sleeps for S seconds (a floating point number).
|
||||
|
||||
time(Goal) reports the execution time of Goal.
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(time, [max_sleep_time/1, sleep/1, time/1, current_time/1, format_time//2]).
|
||||
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(charsio), [read_from_chars/2]).
|
||||
|
||||
current_time(T) :-
|
||||
'$current_time'(T0),
|
||||
read_from_chars(T0, T).
|
||||
|
||||
format_time([], _) --> [].
|
||||
format_time(['%','%'|Fs], T) --> !, "%", format_time(Fs, T).
|
||||
format_time(['%',Spec|Fs], T) --> !,
|
||||
( { member(Spec=Value, T) } ->
|
||||
seq(Value)
|
||||
; { domain_error(time_specifier, Spec, format_time//2) }
|
||||
),
|
||||
format_time(Fs, T).
|
||||
format_time([F|Fs], T) --> [F], format_time(Fs, T).
|
||||
|
||||
max_sleep_time(0xfffffffffffffbff).
|
||||
|
||||
sleep(T) :-
|
||||
builtins:must_be_number(T, sleep),
|
||||
( T < 0 ->
|
||||
domain_error(not_less_than_zero, T, sleep/1)
|
||||
; max_sleep_time(N), T > N ->
|
||||
throw(error(representation_error(max_sleep_time), sleep/1))
|
||||
; '$sleep'(T)
|
||||
).
|
||||
|
||||
|
||||
% '$cpu_now' can be replaced by statistics/2 once that is implemented.
|
||||
|
||||
:- meta_predicate time(0).
|
||||
|
||||
:- dynamic(time_id/1).
|
||||
:- dynamic(time_state/2).
|
||||
|
||||
time_next_id(N) :-
|
||||
( retract(time_id(N0)) ->
|
||||
N is N0 + 1
|
||||
; N = 0
|
||||
),
|
||||
asserta(time_id(N)).
|
||||
|
||||
time(Goal) :-
|
||||
'$cpu_now'(T0),
|
||||
time_next_id(ID),
|
||||
setup_call_cleanup(asserta(time_state(ID, T0)),
|
||||
( call_cleanup(catch(Goal, E, (report_time(ID),throw(E))),
|
||||
Det = true),
|
||||
time_true(ID),
|
||||
( Det == true -> !
|
||||
; true
|
||||
)
|
||||
; report_time(ID),
|
||||
false
|
||||
),
|
||||
retract(time_state(ID, _))).
|
||||
|
||||
time_true(ID) :-
|
||||
report_time(ID).
|
||||
time_true(ID) :-
|
||||
% on backtracking, update the stored CPU time for this ID
|
||||
retract(time_state(ID, _)),
|
||||
'$cpu_now'(T0),
|
||||
asserta(time_state(ID, T0)),
|
||||
false.
|
||||
|
||||
report_time(ID) :-
|
||||
time_state(ID, T0),
|
||||
'$cpu_now'(T),
|
||||
Time is T - T0,
|
||||
( bb_get('$first_answer', true) ->
|
||||
Pre = " ", Post = ""
|
||||
; Pre = "", Post = " "
|
||||
),
|
||||
format("~s% CPU time: ~3fs~n~s", [Pre,Time,Post]).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
?- time((true;false)).
|
||||
%@ % CPU time: 0.006s
|
||||
%@ true
|
||||
%@ ; % CPU time: 0.001s
|
||||
%@ false.
|
||||
|
||||
:- time(use_module(library(clpz))).
|
||||
%@ % CPU time: 3.711s
|
||||
%@ true.
|
||||
|
||||
:- time(use_module(library(lists))).
|
||||
%@ % CPU time: 0.006s
|
||||
%@ true.
|
||||
|
||||
?- time(member(X, "abc")).
|
||||
%@ % CPU time: 0.005s
|
||||
%@ X = a
|
||||
%@ ; % CPU time: 0.000s
|
||||
%@ X = b
|
||||
%@ ; % CPU time: 0.000s
|
||||
%@ X = c
|
||||
%@ ; % CPU time: 0.000s
|
||||
%@ false.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
110
src/lib/tls.pl
Normal file
110
src/lib/tls.pl
Normal file
@@ -0,0 +1,110 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Negotiation of TLS connections.
|
||||
Written Dec. 2021 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(tls, [tls_client_context/2, % -Context, +Options
|
||||
tls_client_negotiate/3, % +Context, +Stream0, -Stream
|
||||
tls_server_context/2, % -Context, +Options
|
||||
tls_server_negotiate/3 % +Context, +Stream0, -Stream
|
||||
]).
|
||||
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(error)).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
TLS Clients
|
||||
===========
|
||||
|
||||
Use tls_client_context/2 to create a TLS context, for example with:
|
||||
|
||||
tls_client_context(Context, [hostname("metalevel.at")])
|
||||
|
||||
Using the context and an existing stream S0 (for example, the
|
||||
result of socket_client_open/3), a TLS stream S can be negotiated
|
||||
with:
|
||||
|
||||
tls_client_negotiate(Context, S0, S)
|
||||
|
||||
S will be an encrypted and authenticated stream with the server.
|
||||
|
||||
The advantage of separating the creation of the client context from
|
||||
negotiating a connection is that the context can be created only once,
|
||||
and quickly reused if needed. This is currently not implemented: In
|
||||
the present implementation, a new internal "Connector" is created for
|
||||
every connection, using the specified hostname.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
tls_client_context(tls_context(Host), Options) :-
|
||||
must_be(list, Options),
|
||||
( member(hostname(Host), Options) ->
|
||||
must_be(chars, Host)
|
||||
; Host = ""
|
||||
).
|
||||
|
||||
tls_client_negotiate(tls_context(Host), S0, S) :-
|
||||
'$tls_client_connect'(Host, S0, S).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
TLS Servers
|
||||
===========
|
||||
|
||||
Use tls_server_context/2 to create a TLS context, for example with:
|
||||
|
||||
tls_server_context(Context, [pkcs12(Chars)])
|
||||
|
||||
where Chars is a list of characters with the contents of a
|
||||
DER-formatted PKCS #12 archive. The option password(Ps) can be used
|
||||
to specify the password Ps (also a string) for decrypting the key.
|
||||
On some versions of OSX, and potentially also on other platforms,
|
||||
empty passwords are not supported.
|
||||
|
||||
The archive should contain a leaf certificate and its private key,
|
||||
as well any intermediate certificates that should be sent to
|
||||
clients to allow them to build a chain to a trusted root. The chain
|
||||
certificates should be in order from the leaf certificate towards
|
||||
the root.
|
||||
|
||||
PKCS #12 archives typically have the file extension .p12 or .pfx,
|
||||
and can be created with the OpenSSL pkcs12 tool:
|
||||
|
||||
$ openssl pkcs12 -export -out identity.pfx \
|
||||
-inkey key.pem -in cert.pem -certfile chain_certs.pem
|
||||
|
||||
|
||||
You can use phrase_from_file/3 from library(pio) and seq//1 from
|
||||
library(dcgs) to read the contents of "identity.pfx" into a string:
|
||||
|
||||
phrase_from_file(seq(Chars), "identity.pfx", [type(binary)])
|
||||
|
||||
The obtained context should be treated as an opaque Prolog term.
|
||||
|
||||
Using the context and an existing stream S0 (for example, the
|
||||
result of socket_server_accept/4), a TLS stream S can be negotiated
|
||||
by a Prolog-based server with:
|
||||
|
||||
tls_server_negotiate(Context, S0, S)
|
||||
|
||||
S will be an encrypted and authenticated stream with the client.
|
||||
|
||||
The advantage of separating the creation of the server context from
|
||||
negotiating a connection is that the context can be created only
|
||||
once, and quickly cloned for every incoming connection. This is
|
||||
currently not implemented: In the present implementation, a new context
|
||||
is created for every connection, using the specified parameters.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
tls_server_context(tls_context(Cert,Password), Options) :-
|
||||
( member(pcks12(Cert), Options) ->
|
||||
must_be(chars, Cert)
|
||||
; domain_error(contains_pcks12, Options, tls_server_context/2)
|
||||
),
|
||||
( member(password(Password), Options) ->
|
||||
must_be(chars, Password)
|
||||
; Password = ""
|
||||
).
|
||||
|
||||
tls_server_negotiate(tls_context(Cert,Password), S0, S) :-
|
||||
'$tls_accept_client'(Cert, Password, S0, S).
|
||||
|
||||
630
src/lib/ugraphs.pl
Normal file
630
src/lib/ugraphs.pl
Normal file
@@ -0,0 +1,630 @@
|
||||
/* Author: R.A.O'Keefe, Vitor Santos Costa, Jan Wielemaker
|
||||
E-mail: J.Wielemaker@vu.nl
|
||||
WWW: http://www.swi-prolog.org
|
||||
Copyright (c) 1984-2021, VU University Amsterdam
|
||||
CWI, 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.
|
||||
*/
|
||||
|
||||
:- module(ugraphs,
|
||||
[ add_edges/3, % +Graph, +Edges, -NewGraph
|
||||
add_vertices/3, % +Graph, +Vertices, -NewGraph
|
||||
complement/2, % +Graph, -NewGraph
|
||||
compose/3, % +LeftGraph, +RightGraph, -NewGraph
|
||||
del_edges/3, % +Graph, +Edges, -NewGraph
|
||||
del_vertices/3, % +Graph, +Vertices, -NewGraph
|
||||
edges/2, % +Graph, -Edges
|
||||
neighbors/3, % +Vertex, +Graph, -Vertices
|
||||
neighbours/3, % +Vertex, +Graph, -Vertices
|
||||
reachable/3, % +Vertex, +Graph, -Vertices
|
||||
top_sort/2, % +Graph, -Sort
|
||||
top_sort/3, % +Graph, -Sort0, -Sort
|
||||
transitive_closure/2, % +Graph, -Closure
|
||||
transpose_ugraph/2, % +Graph, -NewGraph
|
||||
vertices/2, % +Graph, -Vertices
|
||||
vertices_edges_to_ugraph/3, % +Vertices, +Edges, -Graph
|
||||
ugraph_union/3, % +Graph1, +Graph2, -Graph
|
||||
connect_ugraph/3 % +Graph1, -Start, -Graph
|
||||
]).
|
||||
|
||||
/** <module> Graph manipulation library
|
||||
|
||||
The S-representation of a graph is a list of (vertex-neighbours) pairs,
|
||||
where the pairs are in standard order (as produced by keysort) and the
|
||||
neighbours of each vertex are also in standard order (as produced by
|
||||
sort). This form is convenient for many calculations.
|
||||
|
||||
A new UGraph from raw data can be created using
|
||||
vertices_edges_to_ugraph/3.
|
||||
|
||||
Adapted to support some of the functionality of the SICStus ugraphs
|
||||
library by Vitor Santos Costa.
|
||||
|
||||
Ported from YAP 5.0.1 to SWI-Prolog by Jan Wielemaker.
|
||||
|
||||
@author R.A.O'Keefe
|
||||
@author Vitor Santos Costa
|
||||
@author Jan Wielemaker
|
||||
@license BSD-2 or Artistic 2.0
|
||||
*/
|
||||
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(pairs)).
|
||||
:- use_module(library(ordsets)).
|
||||
|
||||
%! vertices(+Graph, -Vertices)
|
||||
%
|
||||
% Unify Vertices with all vertices appearing in Graph. Example:
|
||||
%
|
||||
% ?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], L).
|
||||
% L = [1, 2, 3, 4, 5]
|
||||
|
||||
vertices([], []) :- !.
|
||||
vertices([Vertex-_|Graph], [Vertex|Vertices]) :-
|
||||
vertices(Graph, Vertices).
|
||||
|
||||
|
||||
%! vertices_edges_to_ugraph(+Vertices, +Edges, -UGraph) is det.
|
||||
%
|
||||
% Create a UGraph from Vertices and edges. Given a graph with a
|
||||
% set of Vertices and a set of Edges, Graph must unify with the
|
||||
% corresponding S-representation. Note that the vertices without
|
||||
% edges will appear in Vertices but not in Edges. Moreover, it is
|
||||
% sufficient for a vertice to appear in Edges.
|
||||
%
|
||||
% ==
|
||||
% ?- vertices_edges_to_ugraph([],[1-3,2-4,4-5,1-5], L).
|
||||
% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[]]
|
||||
% ==
|
||||
%
|
||||
% In this case all vertices are defined implicitly. The next
|
||||
% example shows three unconnected vertices:
|
||||
%
|
||||
% ==
|
||||
% ?- vertices_edges_to_ugraph([6,7,8],[1-3,2-4,4-5,1-5], L).
|
||||
% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[], 6-[], 7-[], 8-[]]
|
||||
% ==
|
||||
|
||||
vertices_edges_to_ugraph(Vertices, Edges, Graph) :-
|
||||
sort(Edges, EdgeSet),
|
||||
p_to_s_vertices(EdgeSet, IVertexBag),
|
||||
append(Vertices, IVertexBag, VertexBag),
|
||||
sort(VertexBag, VertexSet),
|
||||
p_to_s_group(VertexSet, EdgeSet, Graph).
|
||||
|
||||
|
||||
%! add_vertices(+Graph, +Vertices, -NewGraph)
|
||||
%
|
||||
% Unify NewGraph with a new graph obtained by adding the list of
|
||||
% Vertices to Graph. Example:
|
||||
%
|
||||
% ```
|
||||
% ?- add_vertices([1-[3,5],2-[]], [0,1,2,9], NG).
|
||||
% NG = [0-[], 1-[3,5], 2-[], 9-[]]
|
||||
% ```
|
||||
|
||||
% replace with real msort/2 when available
|
||||
msort_(List, Sorted) :-
|
||||
pairs_keys(Pairs, List),
|
||||
keysort(Pairs, SortedPairs),
|
||||
pairs_keys(SortedPairs, Sorted).
|
||||
|
||||
add_vertices(Graph, Vertices, NewGraph) :-
|
||||
% msort/2 not available in Scryer Prolog yet: msort(Vertices, V1),
|
||||
msort_(Vertices, V1),
|
||||
add_vertices_to_s_graph(V1, Graph, NewGraph).
|
||||
|
||||
add_vertices_to_s_graph(L, [], NL) :-
|
||||
!,
|
||||
add_empty_vertices(L, NL).
|
||||
add_vertices_to_s_graph([], L, L) :- !.
|
||||
add_vertices_to_s_graph([V1|VL], [V-Edges|G], NGL) :-
|
||||
compare(Res, V1, V),
|
||||
add_vertices_to_s_graph(Res, V1, VL, V, Edges, G, NGL).
|
||||
|
||||
add_vertices_to_s_graph(=, _, VL, V, Edges, G, [V-Edges|NGL]) :-
|
||||
add_vertices_to_s_graph(VL, G, NGL).
|
||||
add_vertices_to_s_graph(<, V1, VL, V, Edges, G, [V1-[]|NGL]) :-
|
||||
add_vertices_to_s_graph(VL, [V-Edges|G], NGL).
|
||||
add_vertices_to_s_graph(>, V1, VL, V, Edges, G, [V-Edges|NGL]) :-
|
||||
add_vertices_to_s_graph([V1|VL], G, NGL).
|
||||
|
||||
add_empty_vertices([], []).
|
||||
add_empty_vertices([V|G], [V-[]|NG]) :-
|
||||
add_empty_vertices(G, NG).
|
||||
|
||||
%! del_vertices(+Graph, +Vertices, -NewGraph) is det.
|
||||
%
|
||||
% Unify NewGraph with a new graph obtained by deleting the list of
|
||||
% Vertices and all the edges that start from or go to a vertex in
|
||||
% Vertices to the Graph. Example:
|
||||
%
|
||||
% ==
|
||||
% ?- del_vertices([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[2,6],8-[]],
|
||||
% [2,1],
|
||||
% NL).
|
||||
% NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]]
|
||||
% ==
|
||||
%
|
||||
% @compat Upto 5.6.48 the argument order was (+Vertices, +Graph,
|
||||
% -NewGraph). Both YAP and SWI-Prolog have changed the argument
|
||||
% order for compatibility with recent SICStus as well as
|
||||
% consistency with del_edges/3.
|
||||
|
||||
del_vertices(Graph, Vertices, NewGraph) :-
|
||||
sort(Vertices, V1), % JW: was msort
|
||||
( V1 = []
|
||||
-> Graph = NewGraph
|
||||
; del_vertices(Graph, V1, V1, NewGraph)
|
||||
).
|
||||
|
||||
del_vertices(G, [], V1, NG) :-
|
||||
!,
|
||||
del_remaining_edges_for_vertices(G, V1, NG).
|
||||
del_vertices([], _, _, []).
|
||||
del_vertices([V-Edges|G], [V0|Vs], V1, NG) :-
|
||||
compare(Res, V, V0),
|
||||
split_on_del_vertices(Res, V,Edges, [V0|Vs], NVs, V1, NG, NGr),
|
||||
del_vertices(G, NVs, V1, NGr).
|
||||
|
||||
del_remaining_edges_for_vertices([], _, []).
|
||||
del_remaining_edges_for_vertices([V0-Edges|G], V1, [V0-NEdges|NG]) :-
|
||||
ord_subtract(Edges, V1, NEdges),
|
||||
del_remaining_edges_for_vertices(G, V1, NG).
|
||||
|
||||
split_on_del_vertices(<, V, Edges, Vs, Vs, V1, [V-NEdges|NG], NG) :-
|
||||
ord_subtract(Edges, V1, NEdges).
|
||||
split_on_del_vertices(>, V, Edges, [_|Vs], Vs, V1, [V-NEdges|NG], NG) :-
|
||||
ord_subtract(Edges, V1, NEdges).
|
||||
split_on_del_vertices(=, _, _, [_|Vs], Vs, _, NG, NG).
|
||||
|
||||
%! add_edges(+Graph, +Edges, -NewGraph)
|
||||
%
|
||||
% Unify NewGraph with a new graph obtained by adding the list of Edges
|
||||
% to Graph. Example:
|
||||
%
|
||||
% ```
|
||||
% ?- add_edges([1-[3,5],2-[4],3-[],4-[5],
|
||||
% 5-[],6-[],7-[],8-[]],
|
||||
% [1-6,2-3,3-2,5-7,3-2,4-5],
|
||||
% NL).
|
||||
% NL = [1-[3,5,6], 2-[3,4], 3-[2], 4-[5],
|
||||
% 5-[7], 6-[], 7-[], 8-[]]
|
||||
% ```
|
||||
|
||||
add_edges(Graph, Edges, NewGraph) :-
|
||||
p_to_s_graph(Edges, G1),
|
||||
ugraph_union(Graph, G1, NewGraph).
|
||||
|
||||
%! ugraph_union(+Graph1, +Graph2, -NewGraph)
|
||||
%
|
||||
% NewGraph is the union of Graph1 and Graph2. Example:
|
||||
%
|
||||
% ```
|
||||
% ?- ugraph_union([1-[2],2-[3]],[2-[4],3-[1,2,4]],L).
|
||||
% L = [1-[2], 2-[3,4], 3-[1,2,4]]
|
||||
% ```
|
||||
|
||||
ugraph_union(Set1, [], Set1) :- !.
|
||||
ugraph_union([], Set2, Set2) :- !.
|
||||
ugraph_union([Head1-E1|Tail1], [Head2-E2|Tail2], Union) :-
|
||||
compare(Order, Head1, Head2),
|
||||
ugraph_union(Order, Head1-E1, Tail1, Head2-E2, Tail2, Union).
|
||||
|
||||
ugraph_union(=, Head-E1, Tail1, _-E2, Tail2, [Head-Es|Union]) :-
|
||||
ord_union(E1, E2, Es),
|
||||
ugraph_union(Tail1, Tail2, Union).
|
||||
ugraph_union(<, Head1, Tail1, Head2, Tail2, [Head1|Union]) :-
|
||||
ugraph_union(Tail1, [Head2|Tail2], Union).
|
||||
ugraph_union(>, Head1, Tail1, Head2, Tail2, [Head2|Union]) :-
|
||||
ugraph_union([Head1|Tail1], Tail2, Union).
|
||||
|
||||
%! del_edges(+Graph, +Edges, -NewGraph)
|
||||
%
|
||||
% Unify NewGraph with a new graph obtained by removing the list of
|
||||
% Edges from Graph. Notice that no vertices are deleted. Example:
|
||||
%
|
||||
% ```
|
||||
% ?- del_edges([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[],8-[]],
|
||||
% [1-6,2-3,3-2,5-7,3-2,4-5,1-3],
|
||||
% NL).
|
||||
% NL = [1-[5],2-[4],3-[],4-[],5-[],6-[],7-[],8-[]]
|
||||
% ```
|
||||
|
||||
del_edges(Graph, Edges, NewGraph) :-
|
||||
p_to_s_graph(Edges, G1),
|
||||
graph_subtract(Graph, G1, NewGraph).
|
||||
|
||||
%! graph_subtract(+Set1, +Set2, ?Difference)
|
||||
%
|
||||
% Is based on ord_subtract
|
||||
|
||||
graph_subtract(Set1, [], Set1) :- !.
|
||||
graph_subtract([], _, []).
|
||||
graph_subtract([Head1-E1|Tail1], [Head2-E2|Tail2], Difference) :-
|
||||
compare(Order, Head1, Head2),
|
||||
graph_subtract(Order, Head1-E1, Tail1, Head2-E2, Tail2, Difference).
|
||||
|
||||
graph_subtract(=, H-E1, Tail1, _-E2, Tail2, [H-E|Difference]) :-
|
||||
ord_subtract(E1,E2,E),
|
||||
graph_subtract(Tail1, Tail2, Difference).
|
||||
graph_subtract(<, Head1, Tail1, Head2, Tail2, [Head1|Difference]) :-
|
||||
graph_subtract(Tail1, [Head2|Tail2], Difference).
|
||||
graph_subtract(>, Head1, Tail1, _, Tail2, Difference) :-
|
||||
graph_subtract([Head1|Tail1], Tail2, Difference).
|
||||
|
||||
%! edges(+Graph, -Edges)
|
||||
%
|
||||
% Unify Edges with all edges appearing in Graph. Example:
|
||||
%
|
||||
% ?- edges([1-[3,5],2-[4],3-[],4-[5],5-[]], L).
|
||||
% L = [1-3, 1-5, 2-4, 4-5]
|
||||
|
||||
edges(Graph, Edges) :-
|
||||
s_to_p_graph(Graph, Edges).
|
||||
|
||||
p_to_s_graph(P_Graph, S_Graph) :-
|
||||
sort(P_Graph, EdgeSet),
|
||||
p_to_s_vertices(EdgeSet, VertexBag),
|
||||
sort(VertexBag, VertexSet),
|
||||
p_to_s_group(VertexSet, EdgeSet, S_Graph).
|
||||
|
||||
|
||||
p_to_s_vertices([], []).
|
||||
p_to_s_vertices([A-Z|Edges], [A,Z|Vertices]) :-
|
||||
p_to_s_vertices(Edges, Vertices).
|
||||
|
||||
|
||||
p_to_s_group([], _, []).
|
||||
p_to_s_group([Vertex|Vertices], EdgeSet, [Vertex-Neibs|G]) :-
|
||||
p_to_s_group(EdgeSet, Vertex, Neibs, RestEdges),
|
||||
p_to_s_group(Vertices, RestEdges, G).
|
||||
|
||||
|
||||
p_to_s_group([V1-X|Edges], V2, [X|Neibs], RestEdges) :- V1 == V2,
|
||||
!,
|
||||
p_to_s_group(Edges, V2, Neibs, RestEdges).
|
||||
p_to_s_group(Edges, _, [], Edges).
|
||||
|
||||
|
||||
|
||||
s_to_p_graph([], []) :- !.
|
||||
s_to_p_graph([Vertex-Neibs|G], P_Graph) :-
|
||||
s_to_p_graph(Neibs, Vertex, P_Graph, Rest_P_Graph),
|
||||
s_to_p_graph(G, Rest_P_Graph).
|
||||
|
||||
|
||||
s_to_p_graph([], _, P_Graph, P_Graph) :- !.
|
||||
s_to_p_graph([Neib|Neibs], Vertex, [Vertex-Neib|P], Rest_P) :-
|
||||
s_to_p_graph(Neibs, Vertex, P, Rest_P).
|
||||
|
||||
%! transitive_closure(+Graph, -Closure)
|
||||
%
|
||||
% Generate the graph Closure as the transitive closure of Graph.
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% ?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L).
|
||||
% L = [1-[2,3,4,5,6], 2-[4,5,6], 4-[6]]
|
||||
% ```
|
||||
|
||||
transitive_closure(Graph, Closure) :-
|
||||
warshall(Graph, Graph, Closure).
|
||||
|
||||
warshall([], Closure, Closure) :- !.
|
||||
warshall([V-_|G], E, Closure) :-
|
||||
memberchk(V-Y, E), % Y := E(v)
|
||||
warshall(E, V, Y, NewE),
|
||||
warshall(G, NewE, Closure).
|
||||
|
||||
|
||||
warshall([X-Neibs|G], V, Y, [X-NewNeibs|NewG]) :-
|
||||
memberchk(V, Neibs),
|
||||
!,
|
||||
ord_union(Neibs, Y, NewNeibs),
|
||||
warshall(G, V, Y, NewG).
|
||||
warshall([X-Neibs|G], V, Y, [X-Neibs|NewG]) :-
|
||||
!,
|
||||
warshall(G, V, Y, NewG).
|
||||
warshall([], _, _, []).
|
||||
|
||||
%! transpose_ugraph(Graph, NewGraph) is det.
|
||||
%
|
||||
% Unify NewGraph with a new graph obtained from Graph by replacing
|
||||
% all edges of the form V1-V2 by edges of the form V2-V1. The cost
|
||||
% is O(|V|*log(|V|)). Notice that an undirected graph is its own
|
||||
% transpose. Example:
|
||||
%
|
||||
% ==
|
||||
% ?- transpose([1-[3,5],2-[4],3-[],4-[5],
|
||||
% 5-[],6-[],7-[],8-[]], NL).
|
||||
% NL = [1-[],2-[],3-[1],4-[2],5-[1,4],6-[],7-[],8-[]]
|
||||
% ==
|
||||
%
|
||||
% @compat This predicate used to be known as transpose/2.
|
||||
% Following SICStus 4, we reserve transpose/2 for matrix
|
||||
% transposition and renamed ugraph transposition to
|
||||
% transpose_ugraph/2.
|
||||
|
||||
transpose_ugraph(Graph, NewGraph) :-
|
||||
edges(Graph, Edges),
|
||||
vertices(Graph, Vertices),
|
||||
flip_edges(Edges, TransposedEdges),
|
||||
vertices_edges_to_ugraph(Vertices, TransposedEdges, NewGraph).
|
||||
|
||||
flip_edges([], []).
|
||||
flip_edges([Key-Val|Pairs], [Val-Key|Flipped]) :-
|
||||
flip_edges(Pairs, Flipped).
|
||||
|
||||
%! compose(+LeftGraph, +RightGraph, -NewGraph)
|
||||
%
|
||||
% Compose NewGraph by connecting the _drains_ of LeftGraph to the
|
||||
% _sources_ of RightGraph. Example:
|
||||
%
|
||||
% ?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L).
|
||||
% L = [1-[4], 2-[1,2,4], 3-[]]
|
||||
|
||||
compose(G1, G2, Composition) :-
|
||||
vertices(G1, V1),
|
||||
vertices(G2, V2),
|
||||
ord_union(V1, V2, V),
|
||||
compose(V, G1, G2, Composition).
|
||||
|
||||
compose([], _, _, []) :- !.
|
||||
compose([Vertex|Vertices], [Vertex-Neibs|G1], G2,
|
||||
[Vertex-Comp|Composition]) :-
|
||||
!,
|
||||
compose1(Neibs, G2, [], Comp),
|
||||
compose(Vertices, G1, G2, Composition).
|
||||
compose([Vertex|Vertices], G1, G2, [Vertex-[]|Composition]) :-
|
||||
compose(Vertices, G1, G2, Composition).
|
||||
|
||||
|
||||
compose1([V1|Vs1], [V2-N2|G2], SoFar, Comp) :-
|
||||
compare(Rel, V1, V2),
|
||||
!,
|
||||
compose1(Rel, V1, Vs1, V2, N2, G2, SoFar, Comp).
|
||||
compose1(_, _, Comp, Comp).
|
||||
|
||||
|
||||
compose1(<, _, Vs1, V2, N2, G2, SoFar, Comp) :-
|
||||
!,
|
||||
compose1(Vs1, [V2-N2|G2], SoFar, Comp).
|
||||
compose1(>, V1, Vs1, _, _, G2, SoFar, Comp) :-
|
||||
!,
|
||||
compose1([V1|Vs1], G2, SoFar, Comp).
|
||||
compose1(=, V1, Vs1, V1, N2, G2, SoFar, Comp) :-
|
||||
ord_union(N2, SoFar, Next),
|
||||
compose1(Vs1, G2, Next, Comp).
|
||||
|
||||
%! top_sort(+Graph, -Sorted) is semidet.
|
||||
%! top_sort(+Graph, -Sorted, ?Tail) is semidet.
|
||||
%
|
||||
% Sorted is a topological sorted list of nodes in Graph. A
|
||||
% toplogical sort is possible if the graph is connected and
|
||||
% acyclic. In the example we show how topological sorting works
|
||||
% for a linear graph:
|
||||
%
|
||||
% ==
|
||||
% ?- top_sort([1-[2], 2-[3], 3-[]], L).
|
||||
% L = [1, 2, 3]
|
||||
% ==
|
||||
%
|
||||
% The predicate top_sort/3 is a difference list version of
|
||||
% top_sort/2.
|
||||
|
||||
top_sort(Graph, Sorted) :-
|
||||
vertices_and_zeros(Graph, Vertices, Counts0),
|
||||
count_edges(Graph, Vertices, Counts0, Counts1),
|
||||
select_zeros(Counts1, Vertices, Zeros),
|
||||
top_sort(Zeros, Sorted, Graph, Vertices, Counts1).
|
||||
|
||||
top_sort(Graph, Sorted0, Sorted) :-
|
||||
vertices_and_zeros(Graph, Vertices, Counts0),
|
||||
count_edges(Graph, Vertices, Counts0, Counts1),
|
||||
select_zeros(Counts1, Vertices, Zeros),
|
||||
top_sort(Zeros, Sorted, Sorted0, Graph, Vertices, Counts1).
|
||||
|
||||
|
||||
vertices_and_zeros([], [], []) :- !.
|
||||
vertices_and_zeros([Vertex-_|Graph], [Vertex|Vertices], [0|Zeros]) :-
|
||||
vertices_and_zeros(Graph, Vertices, Zeros).
|
||||
|
||||
|
||||
count_edges([], _, Counts, Counts) :- !.
|
||||
count_edges([_-Neibs|Graph], Vertices, Counts0, Counts2) :-
|
||||
incr_list(Neibs, Vertices, Counts0, Counts1),
|
||||
count_edges(Graph, Vertices, Counts1, Counts2).
|
||||
|
||||
|
||||
incr_list([], _, Counts, Counts) :- !.
|
||||
incr_list([V1|Neibs], [V2|Vertices], [M|Counts0], [N|Counts1]) :-
|
||||
V1 == V2,
|
||||
!,
|
||||
N is M+1,
|
||||
incr_list(Neibs, Vertices, Counts0, Counts1).
|
||||
incr_list(Neibs, [_|Vertices], [N|Counts0], [N|Counts1]) :-
|
||||
incr_list(Neibs, Vertices, Counts0, Counts1).
|
||||
|
||||
|
||||
select_zeros([], [], []) :- !.
|
||||
select_zeros([0|Counts], [Vertex|Vertices], [Vertex|Zeros]) :-
|
||||
!,
|
||||
select_zeros(Counts, Vertices, Zeros).
|
||||
select_zeros([_|Counts], [_|Vertices], Zeros) :-
|
||||
select_zeros(Counts, Vertices, Zeros).
|
||||
|
||||
|
||||
|
||||
top_sort([], [], Graph, _, Counts) :-
|
||||
!,
|
||||
vertices_and_zeros(Graph, _, Counts).
|
||||
top_sort([Zero|Zeros], [Zero|Sorted], Graph, Vertices, Counts1) :-
|
||||
graph_memberchk(Zero-Neibs, Graph),
|
||||
decr_list(Neibs, Vertices, Counts1, Counts2, Zeros, NewZeros),
|
||||
top_sort(NewZeros, Sorted, Graph, Vertices, Counts2).
|
||||
|
||||
top_sort([], Sorted0, Sorted0, Graph, _, Counts) :-
|
||||
!,
|
||||
vertices_and_zeros(Graph, _, Counts).
|
||||
top_sort([Zero|Zeros], [Zero|Sorted], Sorted0, Graph, Vertices, Counts1) :-
|
||||
graph_memberchk(Zero-Neibs, Graph),
|
||||
decr_list(Neibs, Vertices, Counts1, Counts2, Zeros, NewZeros),
|
||||
top_sort(NewZeros, Sorted, Sorted0, Graph, Vertices, Counts2).
|
||||
|
||||
graph_memberchk(Element1-Edges, [Element2-Edges2|_]) :-
|
||||
Element1 == Element2,
|
||||
!,
|
||||
Edges = Edges2.
|
||||
graph_memberchk(Element, [_|Rest]) :-
|
||||
graph_memberchk(Element, Rest).
|
||||
|
||||
|
||||
decr_list([], _, Counts, Counts, Zeros, Zeros) :- !.
|
||||
decr_list([V1|Neibs], [V2|Vertices], [1|Counts1], [0|Counts2], Zi, Zo) :-
|
||||
V1 == V2,
|
||||
!,
|
||||
decr_list(Neibs, Vertices, Counts1, Counts2, [V2|Zi], Zo).
|
||||
decr_list([V1|Neibs], [V2|Vertices], [N|Counts1], [M|Counts2], Zi, Zo) :-
|
||||
V1 == V2,
|
||||
!,
|
||||
M is N-1,
|
||||
decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo).
|
||||
decr_list(Neibs, [_|Vertices], [N|Counts1], [N|Counts2], Zi, Zo) :-
|
||||
decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo).
|
||||
|
||||
|
||||
%! neighbors(+Vertex, +Graph, -Neigbours) is det.
|
||||
%! neighbours(+Vertex, +Graph, -Neigbours) is det.
|
||||
%
|
||||
% Neigbours is a sorted list of the neighbours of Vertex in Graph.
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% ?- neighbours(4,[1-[3,5],2-[4],3-[],
|
||||
% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL).
|
||||
% NL = [1,2,7,5]
|
||||
% ```
|
||||
|
||||
neighbors(Vertex, Graph, Neig) :-
|
||||
neighbours(Vertex, Graph, Neig).
|
||||
|
||||
neighbours(V,[V0-Neig|_],Neig) :-
|
||||
V == V0,
|
||||
!.
|
||||
neighbours(V,[_|G],Neig) :-
|
||||
neighbours(V,G,Neig).
|
||||
|
||||
|
||||
%! connect_ugraph(+UGraphIn, -Start, -UGraphOut) is det.
|
||||
%
|
||||
% Adds Start as an additional vertex that is connected to all vertices
|
||||
% in UGraphIn. This can be used to create an topological sort for a
|
||||
% not connected graph. Start is before any vertex in UGraphIn in the
|
||||
% standard order of terms. No vertex in UGraphIn can be a variable.
|
||||
%
|
||||
% Can be used to order a not-connected graph as follows:
|
||||
%
|
||||
% ```
|
||||
% top_sort_unconnected(Graph, Vertices) :-
|
||||
% ( top_sort(Graph, Vertices)
|
||||
% -> true
|
||||
% ; connect_ugraph(Graph, Start, Connected),
|
||||
% top_sort(Connected, Ordered0),
|
||||
% Ordered0 = [Start|Vertices]
|
||||
% ).
|
||||
% ```
|
||||
|
||||
connect_ugraph([], 0, []) :- !.
|
||||
connect_ugraph(Graph, Start, [Start-Vertices|Graph]) :-
|
||||
vertices(Graph, Vertices),
|
||||
Vertices = [First|_],
|
||||
before(First, Start).
|
||||
|
||||
%! before(+Term, -Before) is det.
|
||||
%
|
||||
% Unify Before to a term that comes before Term in the standard
|
||||
% order of terms.
|
||||
%
|
||||
% @error instantiation_error if Term is unbound.
|
||||
|
||||
before(X, _) :-
|
||||
var(X),
|
||||
!,
|
||||
instantiation_error(X).
|
||||
before(Number, Start) :-
|
||||
number(Number),
|
||||
!,
|
||||
Start is Number - 1.
|
||||
before(_, 0).
|
||||
|
||||
|
||||
%! complement(+UGraphIn, -UGraphOut)
|
||||
%
|
||||
% UGraphOut is a ugraph with an edge between all vertices that are
|
||||
% _not_ connected in UGraphIn and all edges from UGraphIn removed.
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% ?- complement([1-[3,5],2-[4],3-[],
|
||||
% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL).
|
||||
% NL = [1-[2,4,6,7,8],2-[1,3,5,6,7,8],3-[1,2,4,5,6,7,8],
|
||||
% 4-[3,5,6,8],5-[1,2,3,4,6,7,8],6-[1,2,3,4,5,7,8],
|
||||
% 7-[1,2,3,4,5,6,8],8-[1,2,3,4,5,6,7]]
|
||||
% ```
|
||||
%
|
||||
% @tbd Simple two-step algorithm. You could be smarter, I suppose.
|
||||
|
||||
complement(G, NG) :-
|
||||
vertices(G,Vs),
|
||||
complement(G,Vs,NG).
|
||||
|
||||
complement([], _, []).
|
||||
complement([V-Ns|G], Vs, [V-INs|NG]) :-
|
||||
ord_add_element(Ns,V,Ns1),
|
||||
ord_subtract(Vs,Ns1,INs),
|
||||
complement(G, Vs, NG).
|
||||
|
||||
%! reachable(+Vertex, +UGraph, -Vertices)
|
||||
%
|
||||
% True when Vertices is an ordered set of vertices reachable in
|
||||
% UGraph, including Vertex. Example:
|
||||
%
|
||||
% ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V).
|
||||
% V = [1, 3, 5]
|
||||
|
||||
reachable(N, G, Rs) :-
|
||||
reachable([N], G, [N], Rs).
|
||||
|
||||
reachable([], _, Rs, Rs).
|
||||
reachable([N|Ns], G, Rs0, RsF) :-
|
||||
neighbours(N, G, Nei),
|
||||
ord_union(Rs0, Nei, Rs1, D),
|
||||
append(Ns, D, Nsi),
|
||||
reachable(Nsi, G, Rs1, RsF).
|
||||
107
src/lib/uuid.pl
Normal file
107
src/lib/uuid.pl
Normal file
@@ -0,0 +1,107 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written in February 2021 by Adrián Arroyo (adrian.arroyocalle@gmail.com)
|
||||
Part of Scryer-Prolog
|
||||
This library provides reasoning about UUID (only version 4 right now).
|
||||
There are three predicates:
|
||||
* uuidv4/1, to generate a new UUIDv4
|
||||
* uuidv4_string/1, to generate a new UUIDv4 in string hex representation
|
||||
* uuid_string/2, to converte between UUID list of bytes and UUID hex representation
|
||||
|
||||
Examples:
|
||||
?- uuidv4(X).
|
||||
X = [42,147,248,242,117,196,79,2,129,159|...].
|
||||
?- uuidv4_string(X).
|
||||
X = "428499fc-76e3-4240- ...".
|
||||
?- uuidv4(X), uuid_string(X, S).
|
||||
X = [173,12,244,152,139,118,64,139,137,4|...], S = "ad0cf498-8b76-408b- ...".
|
||||
?- uuid_string(X, "61ae692e-eaf6-4199-8dd3-9f01db70a20b").
|
||||
X = [97,174,105,46,234,246,65,153,141,211|...].
|
||||
|
||||
I place this code in the public domain. Use it in any way you want.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(uuid, [
|
||||
uuidv4/1,
|
||||
uuidv4_string/1,
|
||||
uuid_string/2
|
||||
]).
|
||||
|
||||
:- use_module(library(crypto)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
/*
|
||||
An UUID is made of 16 bytes, composed of 5 sections:
|
||||
time_low - 4
|
||||
time_mid - 2
|
||||
time_hi_and_version - 2
|
||||
clock_seq_hi_and_res_clock_seq_low - 2
|
||||
node - 6
|
||||
UUID v4 can be generated from a set of 16 random bytes: https://www.rfc-archive.org/getrfc.php?rfc=4122#gsc.tab=0 (section 4.4)
|
||||
*/
|
||||
uuidv4(Uuid) :-
|
||||
crypto_n_random_bytes(16, Bytes),
|
||||
Bytes = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16],
|
||||
byte_bits(B9, BitsClockSeqHi0),
|
||||
BitsClockSeqHi0 = [_X7, _X6, X5, X4, X3, X2, X1, X0],
|
||||
NewBitsClockSeqHi0 = [1, 0, X5, X4, X3, X2, X1, X0],
|
||||
byte_bits(NewClockSeqHi0, NewBitsClockSeqHi0),
|
||||
byte_bits(B7, BitsTimeHi),
|
||||
BitsTimeHi = [_Y7, _Y6, _Y5, _Y4, Y3, Y2, Y1, Y0],
|
||||
NewBitsTimeHi = [0, 1, 0, 0, Y3, Y2, Y1, Y0],
|
||||
byte_bits(NewTimeHi, NewBitsTimeHi),
|
||||
Uuid = [B1, B2, B3, B4, B5, B6, NewTimeHi, B8, NewClockSeqHi0, B10, B11, B12, B13, B14, B15, B16].
|
||||
|
||||
uuidv4_string(String) :- uuidv4(Uuid), uuid_string(Uuid, String).
|
||||
|
||||
uuid_string(Uuid, String) :-
|
||||
Uuid = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16],
|
||||
phrase(uuid_([S1, S2, S3, S4, S5]), String),
|
||||
hex_bytes(S1, [B1, B2, B3, B4]),
|
||||
hex_bytes(S2, [B5, B6]),
|
||||
hex_bytes(S3, [B7, B8]),
|
||||
hex_bytes(S4, [B9, B10]),
|
||||
hex_bytes(S5, [B11, B12, B13, B14, B15, B16]).
|
||||
|
||||
uuid_([S1, S2, S3, S4, S5]) -->
|
||||
{
|
||||
length(S1, 8),
|
||||
length(S2, 4),
|
||||
length(S3, 4),
|
||||
length(S4, 4),
|
||||
length(S5, 12)
|
||||
},
|
||||
S1,
|
||||
"-",
|
||||
S2,
|
||||
"-",
|
||||
S3,
|
||||
"-",
|
||||
S4,
|
||||
"-",
|
||||
S5.
|
||||
|
||||
byte_bits(Byte, Bits) :-
|
||||
\+ var(Byte),
|
||||
byte_bits_(Byte, Bits),
|
||||
length(Bits, 8),!.
|
||||
|
||||
byte_bits(Byte, Bits) :-
|
||||
\+ var(Bits),
|
||||
length(Bits, 8),
|
||||
byte_bits__(Byte, Bits),!.
|
||||
|
||||
byte_bits_(0, [0]).
|
||||
byte_bits_(1, [1]).
|
||||
byte_bits_(Byte, Bits) :-
|
||||
R is Byte // 2,
|
||||
M is Byte mod 2,
|
||||
byte_bits_(R, Bits0),
|
||||
append(Bits0, [M], Bits).
|
||||
|
||||
byte_bits__(0, []).
|
||||
byte_bits__(Byte, Bits) :-
|
||||
length(Bits, N),
|
||||
Bits = [Bit|Bits0],
|
||||
byte_bits__(Byte0, Bits0),
|
||||
Byte is Byte0 + Bit*(2 ^ (N-1)).
|
||||
639
src/lib/xpath.pl
Normal file
639
src/lib/xpath.pl
Normal file
@@ -0,0 +1,639 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
The original copyright and licence information is shown below.
|
||||
|
||||
Adapted in June 2020 by Markus Triska (triska@metalevel.at)
|
||||
Part of Scryer Prolog.
|
||||
|
||||
In order to improve portability and reliability of this library, I
|
||||
have changed various parts of the original code to make it
|
||||
conforming to the Prolog ISO standard. I have removed all
|
||||
occurrences of proprietary non-standard ad hoc data types.
|
||||
|
||||
In Scryer Prolog, we represent strings as lists of characters.
|
||||
This makes them amenable to processing with DCGs and other
|
||||
built-in mechanisms such as existing predicates on lists.
|
||||
Scryer Prolog represents lists of characters very compactly,
|
||||
making it an ideal representation for large amounts of text. It
|
||||
fully conforms to the ISO standard and is very portable.
|
||||
|
||||
This library is intended to be used in tandem with load_html/3
|
||||
from library(sgml), which creates DOM trees from markup documents.
|
||||
It can be combined with http_open/3 from library(http/http_open)
|
||||
to read such documents directly from network connections.
|
||||
|
||||
For instance, to inspect all links to Prolog files (*.pl) on
|
||||
Scryer Prolog's project page, we can use:
|
||||
|
||||
:- use_module(library(http/http_open)).
|
||||
:- use_module(library(sgml)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(xpath)).
|
||||
|
||||
link_to_pl_file(File) :-
|
||||
http_open("https://github.com/mthom/scryer-prolog", S, []),
|
||||
load_html(stream(S), DOM, []),
|
||||
xpath(DOM, //a(@href), File),
|
||||
append(_, ".pl", File).
|
||||
|
||||
Yielding:
|
||||
|
||||
?- link_to_pl_file(File).
|
||||
%@ File = "/mthom/scryer-prolog/blob/master/src/lib/tabling.pl"
|
||||
%@ ; File = "/mthom/scryer-prolog/blob/master/src/lib/dif.pl"
|
||||
%@ ; File = "/mthom/scryer-prolog/blob/master/src/lib/freeze.pl"
|
||||
%@ ; ...
|
||||
|
||||
Parts of the original functionality may not yet work. Please
|
||||
consider such parts opportunities for improvements, and file
|
||||
issues in case you need additional features.
|
||||
|
||||
The original copyright information follows.
|
||||
|
||||
======================================================================
|
||||
|
||||
|
||||
Author: Jan Wielemaker
|
||||
E-mail: J.Wielemaker@vu.nl
|
||||
WWW: http://www.swi-prolog.org
|
||||
Copyright (c) 2009-2019, University of Amsterdam
|
||||
VU University Amsterdam
|
||||
CWI, Amsterdam
|
||||
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.
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
:- module(xpath,
|
||||
[ xpath/3, % +DOM, +Spec, -Value
|
||||
xpath_chk/3, % +DOM, +Spec, -Value
|
||||
|
||||
op(400, fx, //),
|
||||
op(400, fx, /),
|
||||
op(200, fy, @)
|
||||
]).
|
||||
|
||||
:- use_module(library(lists),[member/2,memberchk/2]).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(si)).
|
||||
|
||||
/** <module> Select nodes in an XML DOM
|
||||
|
||||
The library xpath.pl provides predicates to select nodes from an XML DOM
|
||||
tree as produced by library(sgml) based on descriptions inspired by the
|
||||
XPath language.
|
||||
|
||||
The predicate xpath/3 selects a sub-structure of the DOM
|
||||
non-deterministically based on an XPath-like specification. Not all
|
||||
selectors of XPath are implemented, but the ability to mix xpath/3 calls
|
||||
with arbitrary Prolog code provides a powerful tool for extracting
|
||||
information from XML parse-trees.
|
||||
|
||||
@see http://www.w3.org/TR/xpath
|
||||
*/
|
||||
|
||||
element_name(element(Name,_,_), Name).
|
||||
element_attributes(element(_,Attributes,_), Attributes).
|
||||
element_content(element(_,_,Content), Content).
|
||||
|
||||
%! xpath_chk(+DOM, +Spec, ?Content) is semidet.
|
||||
%
|
||||
% Semi-deterministic version of xpath/3.
|
||||
|
||||
xpath_chk(DOM, Spec, Content) :-
|
||||
xpath(DOM, Spec, Content),
|
||||
!.
|
||||
|
||||
%! xpath(+DOM, +Spec, ?Content) is nondet.
|
||||
%
|
||||
% Match an element in a DOM structure. The syntax is inspired by
|
||||
% XPath, using () rather than [] to select inside an element.
|
||||
% First we can construct paths using / and //:
|
||||
%
|
||||
% $ =|//|=Term :
|
||||
% Select any node in the DOM matching term.
|
||||
% $ =|/|=Term :
|
||||
% Match the root against Term.
|
||||
% $ Term :
|
||||
% Select the immediate children of the root matching Term.
|
||||
%
|
||||
% The Terms above are of type _callable_. The functor specifies
|
||||
% the element name. The element name '*' refers to any element.
|
||||
% The name =self= refers to the top-element itself and is often
|
||||
% used for processing matches of an earlier xpath/3 query. A term
|
||||
% NS:Term refers to an XML name in the namespace NS. Optional
|
||||
% arguments specify additional constraints and functions. The
|
||||
% arguments are processed from left to right. Defined conditional
|
||||
% argument values are:
|
||||
%
|
||||
% $ index(?Index) :
|
||||
% True if the element is the Index-th child of its parent,
|
||||
% where 1 denotes the first child. Index can be one of:
|
||||
% $ `Var` :
|
||||
% `Var` is unified with the index of the matched element.
|
||||
% $ =last= :
|
||||
% True for the last element.
|
||||
% $ =last= - `IntExpr` :
|
||||
% True for the last-minus-nth element. For example,
|
||||
% `last-1` is the element directly preceding the last one.
|
||||
% $ `IntExpr` :
|
||||
% True for the element whose index equals `IntExpr`.
|
||||
% $ Integer :
|
||||
% The N-th element with the given name, with 1 denoting the
|
||||
% first element. Same as index(Integer).
|
||||
% $ =last= :
|
||||
% The last element with the given name. Same as
|
||||
% index(last).
|
||||
% $ =last= - IntExpr :
|
||||
% The IntExpr-th element before the last.
|
||||
% Same as index(last-IntExpr).
|
||||
%
|
||||
% Defined function argument values are:
|
||||
%
|
||||
% $ =self= :
|
||||
% Evaluate to the entire element
|
||||
% $ =content= :
|
||||
% Evaluate to the content of the element (a list)
|
||||
% $ =text= :
|
||||
% Evaluates to all text from the sub-tree, represented
|
||||
% as a list of characters.
|
||||
% $ `text(atom)` :
|
||||
% Evaluates to all text from the sub-tree as an atom.
|
||||
% $ =normalize_space= :
|
||||
% As =text=, but uses normalize_space/2 to normalise
|
||||
% white-space in the output
|
||||
% $ =number= :
|
||||
% Extract an integer or float from the value. Ignores
|
||||
% leading and trailing white-space
|
||||
% $ =|@|=Attribute :
|
||||
% Evaluates to the value of the given attribute. Attribute
|
||||
% can be a compound term. In this case the functor name
|
||||
% denotes the element and arguments perform transformations
|
||||
% on the attribute value. Defined transformations are:
|
||||
%
|
||||
% - number
|
||||
% Translate the value into a number using
|
||||
% xsd_number_chars/2.
|
||||
% - integer
|
||||
% As `number`, but subsequently transform the value
|
||||
% into an integer using the round/1 function.
|
||||
% - float
|
||||
% As `number`, but subsequently transform the value
|
||||
% into a float using the float/1 function.
|
||||
% - lower
|
||||
% Translate the value to lower case, preserving
|
||||
% the type.
|
||||
% - upper
|
||||
% Translate the value to upper case, preserving
|
||||
% the type.
|
||||
%
|
||||
% In addition, the argument-list can be _conditions_:
|
||||
%
|
||||
% $ Left = Right :
|
||||
% Succeeds if the left-hand unifies with the right-hand.
|
||||
% If the left-hand side is a function, this is evaluated.
|
||||
% The right-hand side is _never_ evaluated, and thus the
|
||||
% condition `content = content` defines that the content
|
||||
% of the element is the atom `content`.
|
||||
% The functions `lower_case` and `upper_case` can be applied
|
||||
% to Right (see example below).
|
||||
% $ contains(Haystack, Needle) :
|
||||
% Succeeds if Needle is a sub-list of Haystack.
|
||||
% $ XPath :
|
||||
% Succeeds if XPath matches in the currently selected
|
||||
% sub-DOM. For example, the following expression finds
|
||||
% an =h3= element inside a =div= element, where the =div=
|
||||
% element itself contains an =h2= child with a =strong=
|
||||
% child.
|
||||
%
|
||||
% ==
|
||||
% //div(h2/strong)/h3
|
||||
% ==
|
||||
%
|
||||
% This is equivalent to the conjunction of XPath goals below.
|
||||
%
|
||||
% ==
|
||||
% ...,
|
||||
% xpath(DOM, //(div), Div),
|
||||
% xpath(Div, h2/strong, _),
|
||||
% xpath(Div, h3, Result)
|
||||
% ==
|
||||
%
|
||||
% **Examples**:
|
||||
%
|
||||
% Match each table-row in DOM:
|
||||
%
|
||||
% ==
|
||||
% xpath(DOM, //tr, TR)
|
||||
% ==
|
||||
%
|
||||
% Match the last cell of each tablerow in DOM. This example
|
||||
% illustrates that a result can be the input of subsequent xpath/3
|
||||
% queries. Using multiple queries on the intermediate TR term
|
||||
% guarantee that all results come from the same table-row:
|
||||
%
|
||||
% ==
|
||||
% xpath(DOM, //tr, TR),
|
||||
% xpath(TR, /td(last), TD)
|
||||
% ==
|
||||
%
|
||||
% Match each =href= attribute in an <a> element
|
||||
%
|
||||
% ==
|
||||
% xpath(DOM, //a(@href), HREF)
|
||||
% ==
|
||||
%
|
||||
% Suppose we have a table containing rows where each first column
|
||||
% is the name of a product with a link to details and the second
|
||||
% is the price (a number). The following predicate matches the
|
||||
% name, URL and price:
|
||||
%
|
||||
% ==
|
||||
% product(DOM, Name, URL, Price) :-
|
||||
% xpath(DOM, //tr, TR),
|
||||
% xpath(TR, td(1), C1),
|
||||
% xpath(C1, /self(normalize_space), Name),
|
||||
% xpath(C1, a(@href), URL),
|
||||
% xpath(TR, td(2, number), Price).
|
||||
% ==
|
||||
%
|
||||
% Suppose we want to select books with genre="thriller" from a
|
||||
% tree containing elements =|<book genre=...>|=
|
||||
%
|
||||
% ==
|
||||
% thriller(DOM, Book) :-
|
||||
% xpath(DOM, //book(@genre=thiller), Book).
|
||||
% ==
|
||||
%
|
||||
% Match the elements =|<table align="center">|= _and_ =|<table
|
||||
% align="CENTER">|=:
|
||||
%
|
||||
% ```prolog
|
||||
% //table(@align(lower) = center)
|
||||
% ```
|
||||
%
|
||||
% Get the `width` and `height` of a `div` element as a number,
|
||||
% and the `div` node itself:
|
||||
%
|
||||
% ==
|
||||
% xpath(DOM, //div(@width(number)=W, @height(number)=H), Div)
|
||||
% ==
|
||||
%
|
||||
% Note that `div` is an infix operator, so parentheses must be
|
||||
% used in cases like the following:
|
||||
%
|
||||
% ==
|
||||
% xpath(DOM, //(div), Div)
|
||||
% ==
|
||||
|
||||
xpath(DOM, Spec, Content) :-
|
||||
in_dom(Spec, DOM, Content).
|
||||
|
||||
in_dom(//Spec, DOM, Value) :-
|
||||
!,
|
||||
element_spec(Spec, Name, Modifiers),
|
||||
sub_dom(I, Len, Name, E, DOM),
|
||||
modifiers(Modifiers, I, Len, E, Value).
|
||||
in_dom(/Spec, E, Value) :-
|
||||
!,
|
||||
element_spec(Spec, Name, Modifiers),
|
||||
( Name == self
|
||||
-> true
|
||||
; element_name(E, Name)
|
||||
),
|
||||
modifiers(Modifiers, 1, 1, E, Value).
|
||||
in_dom(A/B, DOM, Value) :-
|
||||
!,
|
||||
in_dom(A, DOM, Value0),
|
||||
in_dom(B, Value0, Value).
|
||||
in_dom(A//B, DOM, Value) :-
|
||||
!,
|
||||
in_dom(A, DOM, Value0),
|
||||
in_dom(//B, Value0, Value).
|
||||
in_dom(Spec, element(_, _, Content), Value) :-
|
||||
element_spec(Spec, Name, Modifiers),
|
||||
count_named_elements(Content, Name, CLen),
|
||||
CLen > 0,
|
||||
nth_element(N, Name, E, Content),
|
||||
modifiers(Modifiers, N, CLen, E, Value).
|
||||
|
||||
element_spec(Var, _, _) :-
|
||||
var(Var),
|
||||
!,
|
||||
instantiation_error(Var).
|
||||
element_spec(NS:Term, NS:Name, Modifiers) :-
|
||||
!,
|
||||
callable_name_arguments(Term, Name0, Modifiers),
|
||||
star(Name0, Name).
|
||||
element_spec(Term, Name, Modifiers) :-
|
||||
!,
|
||||
callable_name_arguments(Term, Name0, Modifiers),
|
||||
star(Name0, Name).
|
||||
|
||||
callable_name_arguments(Atom, Name, Arguments) :-
|
||||
atom(Atom),
|
||||
!,
|
||||
Name = Atom, Arguments = [].
|
||||
callable_name_arguments(Compound, Name, Arguments) :-
|
||||
Compound =.. [Name|Arguments].
|
||||
|
||||
|
||||
star(*, _) :- !.
|
||||
star(Name, Name).
|
||||
|
||||
|
||||
%! sub_dom(-Index, -Count, +Name, -Sub, +DOM) is nondet.
|
||||
%
|
||||
% Sub is a node in DOM with Name.
|
||||
%
|
||||
% @param Count is the total number of nodes in the content
|
||||
% list Sub appears that have the same name.
|
||||
% @param Index is the 1-based index of Sub of nodes with
|
||||
% Name.
|
||||
|
||||
sub_dom(1, 1, Name, DOM, DOM) :-
|
||||
element_name(DOM, Name0),
|
||||
\+ Name \= Name0.
|
||||
sub_dom(N, Len, Name, E, element(_,_,Content)) :-
|
||||
!,
|
||||
sub_dom_2(N, Len, Name, E, Content).
|
||||
sub_dom(N, Len, Name, E, Content) :-
|
||||
list_si(Content),
|
||||
sub_dom_2(N, Len, Name, E, Content).
|
||||
|
||||
sub_dom_2(N, Len, Name, Element, Content) :-
|
||||
( count_named_elements(Content, Name, Len),
|
||||
nth_element(N, Name, Element, Content)
|
||||
; member(element(_,_,C2), Content),
|
||||
sub_dom_2(N, Len, Name, Element, C2)
|
||||
).
|
||||
|
||||
|
||||
%! count_named_elements(+Content, +Name, -Count) is det.
|
||||
%
|
||||
% Count is the number of nodes with Name in Content.
|
||||
|
||||
count_named_elements(Content, Name, Count) :-
|
||||
count_named_elements(Content, Name, 0, Count).
|
||||
|
||||
count_named_elements([], _, Count, Count).
|
||||
count_named_elements([element(Name,_,_)|T], Name0, C0, C) :-
|
||||
\+ Name \= Name0,
|
||||
!,
|
||||
C1 is C0+1,
|
||||
count_named_elements(T, Name0, C1, C).
|
||||
count_named_elements([_|T], Name, C0, C) :-
|
||||
count_named_elements(T, Name, C0, C).
|
||||
|
||||
|
||||
%! nth_element(?N, +Name, -Element, +Content:list) is nondet.
|
||||
%
|
||||
% True if Element is the N-th element with name in Content.
|
||||
|
||||
nth_element(N, Name, Element, Content) :-
|
||||
nth_element_(1, N, Name, Element, Content).
|
||||
|
||||
nth_element_(I, N, Name, E, [H|T]) :-
|
||||
element_name(H, Name0),
|
||||
\+ Name \= Name0,
|
||||
!,
|
||||
( N = I,
|
||||
E = H
|
||||
; I2 is I + 1,
|
||||
( nonvar(N), I2 > N
|
||||
-> !, fail
|
||||
; true
|
||||
),
|
||||
nth_element_(I2, N, Name, E, T)
|
||||
).
|
||||
nth_element_(I, N, Name, E, [_|T]) :-
|
||||
nth_element_(I, N, Name, E, T).
|
||||
|
||||
|
||||
%! modifiers(+Modifiers, +I, +Clen, +DOM, -Value)
|
||||
%
|
||||
%
|
||||
|
||||
modifiers([], _, _, Value, Value).
|
||||
modifiers([H|T], I, L, Value0, Value) :-
|
||||
modifier(H, I, L, Value0, Value1),
|
||||
modifiers(T, I, L, Value1, Value).
|
||||
|
||||
modifier(M, _, _, _, _) :-
|
||||
var(M),
|
||||
!,
|
||||
instantiation_error(M).
|
||||
modifier(Index, I, L, Value0, Value) :-
|
||||
implicit_index_modifier(Index),
|
||||
!,
|
||||
Value = Value0,
|
||||
index_modifier(Index, I, L).
|
||||
modifier(index(Index), I, L, Value, Value) :-
|
||||
!,
|
||||
index_modifier(Index, I, L).
|
||||
modifier(Function, _, _, In, Out) :-
|
||||
xpath_function(Function),
|
||||
!,
|
||||
xpath_function(Function, In, Out).
|
||||
modifier(Function, _, _, In, Out) :-
|
||||
xpath_condition(Function, In),
|
||||
Out = In.
|
||||
|
||||
implicit_index_modifier(I) :-
|
||||
integer(I),
|
||||
!.
|
||||
implicit_index_modifier(last).
|
||||
implicit_index_modifier(last-_Expr).
|
||||
|
||||
index_modifier(Var, I, _L) :-
|
||||
var(Var),
|
||||
!,
|
||||
Var = I.
|
||||
index_modifier(last, I, L) :-
|
||||
!,
|
||||
I =:= L.
|
||||
index_modifier(last-Expr, I, L) :-
|
||||
!,
|
||||
I =:= L-Expr.
|
||||
index_modifier(N, I, _) :-
|
||||
N =:= I.
|
||||
|
||||
xpath_function(self, DOM, DOM). % self
|
||||
xpath_function(content, Element, Value) :- % content
|
||||
element_content(Element, Value).
|
||||
xpath_function(text, DOM, Text) :- % text
|
||||
text_of_dom(DOM, chars, Text).
|
||||
xpath_function(text(As), DOM, Text) :- % text(atom)
|
||||
text_of_dom(DOM, As, Text).
|
||||
xpath_function(normalize_space, DOM, Text) :- % normalize_space
|
||||
text_of_dom(DOM, chars, Text0),
|
||||
normalize_space(Text0, Text).
|
||||
xpath_function(number, DOM, Number) :- % number
|
||||
text_of_dom(DOM, chars, Text0),
|
||||
normalize_space(Text0, Text),
|
||||
catch(xsd_number_chars(Number, Text), _, fail).
|
||||
xpath_function(@Name, element(_, Attrs, _), Value) :- % @Name
|
||||
( atom(Name)
|
||||
-> memberchk(Name=Value, Attrs)
|
||||
; compound(Name)
|
||||
-> Name =.. [AName|AOps],
|
||||
memberchk(AName=Value0, Attrs),
|
||||
translate_attribute(AOps, Value0, Value)
|
||||
; member(Name=Value, Attrs)
|
||||
).
|
||||
xpath_function(quote(Value), _, Value). % quote(Value)
|
||||
|
||||
xpath_function(self).
|
||||
xpath_function(content).
|
||||
xpath_function(text).
|
||||
xpath_function(text(_)).
|
||||
xpath_function(normalize_space).
|
||||
xpath_function(number).
|
||||
xpath_function(@_).
|
||||
xpath_function(quote(_)).
|
||||
|
||||
translate_attribute([], Value, Value).
|
||||
translate_attribute([H|T], Value0, Value) :-
|
||||
translate_attr(H, Value0, Value1),
|
||||
translate_attribute(T, Value1, Value).
|
||||
|
||||
translate_attr(number, Value0, Value) :-
|
||||
xsd_number_chars(Value, Value0).
|
||||
translate_attr(integer, Value0, Value) :-
|
||||
xsd_number_chars(Value1, Value0),
|
||||
Value is round(Value1).
|
||||
translate_attr(float, Value0, Value) :-
|
||||
xsd_number_chars(Value1, Value0),
|
||||
Value is float(Value1).
|
||||
% The implementation of these translations is left for later.
|
||||
% translate_attr(lower, Value0, Value) :- ...
|
||||
% translate_attr(upper, Value0, Value) :- ...
|
||||
|
||||
xpath_condition(Left = Right, Value) :- % =
|
||||
!,
|
||||
var_or_function(Left, Value, LeftValue),
|
||||
process_equality(LeftValue, Right).
|
||||
xpath_condition(contains(Haystack, Needle), Value) :- % contains(Haystack, Needle)
|
||||
!,
|
||||
val_or_function(Haystack, Value, HaystackValue),
|
||||
val_or_function(Needle, Value, NeedleValue),
|
||||
( phrase((...,seq(NeedleValue),...), HaystackValue)
|
||||
-> true
|
||||
).
|
||||
xpath_condition(Spec, Dom) :-
|
||||
in_dom(Spec, Dom, _).
|
||||
|
||||
|
||||
%! process_equality(+Left, +Right) is semidet.
|
||||
%
|
||||
% Provides (very) partial support for XSLT functions that can be
|
||||
% applied according to the XPath 2 specification.
|
||||
%
|
||||
% For example the XPath expression in [1], and the equivalent
|
||||
% Prolog expression in [2], would both match the HTML element in
|
||||
% [3].
|
||||
%
|
||||
% ==
|
||||
% [1] //table[align=lower-case(center)]
|
||||
% [2] //table(@align=lower_case(center))
|
||||
% [3] <table align="CENTER">
|
||||
% ==
|
||||
|
||||
process_equality(Left, Right) :-
|
||||
var(Right),
|
||||
!,
|
||||
Left = Right.
|
||||
process_equality(Left, lower_case(Right)) :-
|
||||
!,
|
||||
downcase_atom(Left, Right).
|
||||
process_equality(Left, upper_case(Right)) :-
|
||||
!,
|
||||
upcase_atom(Left, Right).
|
||||
process_equality(Left, Right) :-
|
||||
Left = Right.
|
||||
|
||||
|
||||
var_or_function(Arg, _, Arg) :-
|
||||
var(Arg),
|
||||
!.
|
||||
var_or_function(Func, Value0, Value) :-
|
||||
xpath_function(Func),
|
||||
!,
|
||||
xpath_function(Func, Value0, Value).
|
||||
var_or_function(Value, _, Value).
|
||||
|
||||
val_or_function(Arg, _, Arg) :-
|
||||
var(Arg),
|
||||
!,
|
||||
instantiation_error(Arg).
|
||||
val_or_function(Func, Value0, Value) :- % TBD
|
||||
xpath_function(Func, Value0, Value),
|
||||
!.
|
||||
val_or_function(Value, _, Value).
|
||||
|
||||
|
||||
%! text_of_dom(+DOM, +As, -Text:(chars | atom)) is det.
|
||||
%
|
||||
% Text is the joined textual content of DOM.
|
||||
|
||||
text_of_dom(DOM, As, Text) :-
|
||||
phrase(text_of(DOM), Text0),
|
||||
( As == chars ->
|
||||
Text = Text0
|
||||
; As == atom ->
|
||||
atom_chars(Text, Text0)
|
||||
; domain_error(As, atom_or_chars, xpath)
|
||||
).
|
||||
|
||||
text_of(element(_,_,Content)) -->
|
||||
text_of_list(Content).
|
||||
text_of([]) -->
|
||||
[].
|
||||
text_of([H|T]) -->
|
||||
text_of(H),
|
||||
text_of(T).
|
||||
|
||||
|
||||
text_of_list([]) -->
|
||||
[].
|
||||
text_of_list([H|T]) -->
|
||||
text_of_1(H),
|
||||
text_of_list(T).
|
||||
|
||||
|
||||
text_of_1(element(_,_,Content)) -->
|
||||
text_of_list(Content).
|
||||
text_of_1([C|Cs]) --> seq([C|Cs]).
|
||||
|
||||
% For now, we use number_chars/2 to parse XML numbers.
|
||||
% If the need arises, we can extend this to additional
|
||||
% float constants and syntax that may occur in XML files.
|
||||
|
||||
xsd_number_chars(Number, Chars) :-
|
||||
number_chars(Number, Chars).
|
||||
|
||||
normalize_space(Text0, Text) :-
|
||||
Text0 = Text. % no conversion for the moment.
|
||||
1370
src/loader.pl
Normal file
1370
src/loader.pl
Normal file
File diff suppressed because it is too large
Load Diff
1432
src/machine/arithmetic_ops.rs
Normal file
1432
src/machine/arithmetic_ops.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,11 @@
|
||||
:- module('$atts', []).
|
||||
|
||||
|
||||
driver(Vars, Values) :-
|
||||
iterate(Vars, Values, ListOfListsOfGoalLists),
|
||||
!,
|
||||
call_goals(ListOfListsOfGoalLists),
|
||||
'$reset_attr_var_state',
|
||||
'$return_from_verify_attr'.
|
||||
|
||||
iterate([Var|VarBindings], [Value|ValueBindings], [ListOfGoalLists | ListsCubed]) :-
|
||||
@@ -13,9 +15,9 @@ iterate([Var|VarBindings], [Value|ValueBindings], [ListOfGoalLists | ListsCubed]
|
||||
iterate(VarBindings, ValueBindings, ListsCubed).
|
||||
iterate([], [], []).
|
||||
|
||||
|
||||
gather_modules(Attrs, []) :- var(Attrs), !.
|
||||
gather_modules([Attr|Attrs], [Module|Modules]) :-
|
||||
'$module_of'(Module, Attr), % write the owning module of Attr to Module.
|
||||
gather_modules([Module:_|Attrs], [Module|Modules]) :-
|
||||
gather_modules(Attrs, Modules).
|
||||
|
||||
call_verify_attributes(Attrs, _, _, []) :-
|
||||
@@ -26,27 +28,30 @@ call_verify_attributes([Attr|Attrs], Var, Value, ListOfGoalLists) :-
|
||||
sort(Modules0, Modules),
|
||||
verify_attrs(Modules, Var, Value, ListOfGoalLists).
|
||||
|
||||
verify_attrs([Module|Modules], Var, Value, [Goals|ListOfGoalLists]) :-
|
||||
|
||||
verify_attrs([Module|Modules], Var, Value, [Module-Goals|ListOfGoalLists]) :-
|
||||
catch(Module:verify_attributes(Var, Value, Goals),
|
||||
error(evaluation_error((Module:verify_attributes)/3), verify_attributes/3),
|
||||
Goals = []),
|
||||
verify_attrs(Modules, Var, Value, ListOfGoalLists).
|
||||
verify_attrs([], _, _, []).
|
||||
|
||||
|
||||
call_goals([ListOfGoalLists | ListsCubed]) :-
|
||||
call_goals_0(ListOfGoalLists),
|
||||
call_goals(ListsCubed).
|
||||
call_goals([]).
|
||||
|
||||
call_goals_0([GoalList | GoalLists]) :-
|
||||
( var(GoalList), throw(error(instantiation_error, call_goals_0/1))
|
||||
call_goals_0([Module-GoalList | GoalLists]) :-
|
||||
( var(GoalList),
|
||||
throw(error(instantiation_error, call_goals_0/1))
|
||||
; true
|
||||
),
|
||||
call_goals_1(GoalList),
|
||||
call_goals_1(GoalList, Module),
|
||||
call_goals_0(GoalLists).
|
||||
call_goals_0([]).
|
||||
|
||||
call_goals_1([Goal | Goals]) :-
|
||||
call(Goal),
|
||||
call_goals_1(Goals).
|
||||
call_goals_1([]).
|
||||
call_goals_1([Goal | Goals], Module) :-
|
||||
call(Module:Goal),
|
||||
call_goals_1(Goals, Module).
|
||||
call_goals_1([], _).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user