Compare commits
1537 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb9d865635 | ||
|
|
694c87cb61 | ||
|
|
c41aba6b90 | ||
|
|
28ea672e36 | ||
|
|
9366a48d6d | ||
|
|
c7e1f5d568 | ||
|
|
d91ee5b77c | ||
|
|
fd97b84916 | ||
|
|
23f59970cb | ||
|
|
8781e03863 | ||
|
|
f2940ddfcf | ||
|
|
140149f051 | ||
|
|
9055370326 | ||
|
|
1af62c8d11 | ||
|
|
ec5430826e | ||
|
|
b2cccab768 | ||
|
|
579aa8acd4 | ||
|
|
9c2cb144b5 | ||
|
|
3d04689660 | ||
|
|
a891cc4edf | ||
|
|
706ab2ae5b | ||
|
|
9c1de8e00b | ||
|
|
1109e05e06 | ||
|
|
ce313b8a6b | ||
|
|
7c96b91663 | ||
|
|
87e966e185 | ||
|
|
1e018681de | ||
|
|
5b324680ba | ||
|
|
abca5fc405 | ||
|
|
6a455d2866 | ||
|
|
70da818101 | ||
|
|
5ec837acb4 | ||
|
|
76e33d051c | ||
|
|
7c5318b784 | ||
|
|
47892bf24a | ||
|
|
8a9cd7779c | ||
|
|
d4c0277065 | ||
|
|
069e132c0e | ||
|
|
4e6c138099 | ||
|
|
0ab355eada | ||
|
|
44825826df | ||
|
|
612f09893c | ||
|
|
e9f507b868 | ||
|
|
78278c804f | ||
|
|
b51460a59a | ||
|
|
181be5be3f | ||
|
|
91d4e91f53 | ||
|
|
ad3ae7991b | ||
|
|
c7caf6b7a9 | ||
|
|
4422ffe39f | ||
|
|
1ff52f70aa | ||
|
|
fce45167a5 | ||
|
|
b8f384045c | ||
|
|
ea95a7900c | ||
|
|
edea1273c8 | ||
|
|
ec9c763211 | ||
|
|
5a08117e75 | ||
|
|
0aec980aa8 | ||
|
|
6b05ee5130 | ||
|
|
1ffbf63d20 | ||
|
|
b905d2758d | ||
|
|
311d985145 | ||
|
|
a9a06b5297 | ||
|
|
6b8e620495 | ||
|
|
577f85099d | ||
|
|
607c84a79b | ||
|
|
2eec6499ff | ||
|
|
cd1150c11d | ||
|
|
987bbdecf5 | ||
|
|
a68394b6f2 | ||
|
|
3d6fbabc86 | ||
|
|
1479b5d38c | ||
|
|
336311ecc8 | ||
|
|
e9bb35c895 | ||
|
|
ab4f93dcee | ||
|
|
140a199805 | ||
|
|
232b66cfc3 | ||
|
|
11ba7e47fd | ||
|
|
c36d4a9c9a | ||
|
|
4b5c22864e | ||
|
|
5e1faeb5d2 | ||
|
|
4b7c2ba6c8 | ||
|
|
b9285f8de1 | ||
|
|
084fc84590 | ||
|
|
7a9b71cc03 | ||
|
|
6b49653754 | ||
|
|
d5fa8cc211 | ||
|
|
10a11c293e | ||
|
|
e486862db8 | ||
|
|
1c8aae0839 | ||
|
|
6feb1f8b82 | ||
|
|
bef8eb538c | ||
|
|
4e3b066555 | ||
|
|
4d23542ef3 | ||
|
|
47a6b498e3 | ||
|
|
b6f77f4e6f | ||
|
|
b96cd06129 | ||
|
|
e8eb6765bd | ||
|
|
8df552e952 | ||
|
|
18c52e076f | ||
|
|
1810dabc14 | ||
|
|
609a3a229f | ||
|
|
dff56643f0 | ||
|
|
77f8d52271 | ||
|
|
1e9821ec0c | ||
|
|
66e047083a | ||
|
|
e6438e79d8 | ||
|
|
a5e72679bc | ||
|
|
15cc916333 | ||
|
|
2f3de51e55 | ||
|
|
cea1353fbb | ||
|
|
bfbafd4168 | ||
|
|
27a15a2464 | ||
|
|
c2a87f898e | ||
|
|
aaa75c3ffc | ||
|
|
2dfa108034 | ||
|
|
4895cb7b22 | ||
|
|
2d3eb8483f | ||
|
|
36ef988514 | ||
|
|
d58dd7cc91 | ||
|
|
7e06abebd5 | ||
|
|
8dd7f51ae3 | ||
|
|
6030fae685 | ||
|
|
0f502fff84 | ||
|
|
789f662ec8 | ||
|
|
72536037ca | ||
|
|
e4eefc92f4 | ||
|
|
23c4e935b4 | ||
|
|
68b4951bc9 | ||
|
|
ed89c43e48 | ||
|
|
16137ace0e | ||
|
|
b4ef7556da | ||
|
|
eea83d9474 | ||
|
|
9d5264c5a3 | ||
|
|
66075bf45b | ||
|
|
35b8f69f92 | ||
|
|
2d5fe21b05 | ||
|
|
b448681872 | ||
|
|
05c14d5780 | ||
|
|
bb9de52a53 | ||
|
|
7595ec16e5 | ||
|
|
d8cf0f320d | ||
|
|
d644a3996e | ||
|
|
525050c407 | ||
|
|
468088fd6b | ||
|
|
95c192a988 | ||
|
|
d6287fae06 | ||
|
|
9e98f75495 | ||
|
|
56681af1b6 | ||
|
|
8bf3d4ea71 | ||
|
|
00eab4d415 | ||
|
|
0876d46880 | ||
|
|
ed803d30cd | ||
|
|
b6504bf377 | ||
|
|
85b4f235ef | ||
|
|
064bfa5e0d | ||
|
|
4a6ffb6b9e | ||
|
|
d952b7ace8 | ||
|
|
3b6138eaaa | ||
|
|
f3167f6b5f | ||
|
|
8c99d748e2 | ||
|
|
0ab928063e | ||
|
|
c9a34bde3f | ||
|
|
7f85c682b8 | ||
|
|
181185bad6 | ||
|
|
01a9fd9e25 | ||
|
|
3be00045cf | ||
|
|
86176dce8d | ||
|
|
1fa8a0a969 | ||
|
|
d5fe2e88d1 | ||
|
|
9177dbce7f | ||
|
|
55a896367d | ||
|
|
f2e49c84cc | ||
|
|
afa0bea1b1 | ||
|
|
f7e31fceac | ||
|
|
c490d818fb | ||
|
|
32bdedde6d | ||
|
|
48960f1beb | ||
|
|
cf65cbe2f1 | ||
|
|
45c35eca3e | ||
|
|
bb2a8c9090 | ||
|
|
6798b22b49 | ||
|
|
3acbe2a418 | ||
|
|
c45cdd6ea0 | ||
|
|
f520c38b60 | ||
|
|
046a2175ab | ||
|
|
83def95383 | ||
|
|
012fefa37e | ||
|
|
4878e42e07 | ||
|
|
1a7992e524 | ||
|
|
94d7c6dc5f | ||
|
|
23bfb4da50 | ||
|
|
1f21a5fe62 | ||
|
|
2173a8e3cb | ||
|
|
067e2633af | ||
|
|
d916da9ba8 | ||
|
|
c2fc33afec | ||
|
|
3d4e400ed2 | ||
|
|
75d719d204 | ||
|
|
172e3e9f34 | ||
|
|
8e32bdd8db | ||
|
|
895b02b641 | ||
|
|
d06b5f7ab5 | ||
|
|
e88ec6736c | ||
|
|
922abf21e7 | ||
|
|
c55bfa2c6e | ||
|
|
5463c1d47f | ||
|
|
3513a9c7ba | ||
|
|
45db236ec7 | ||
|
|
5848ff5735 | ||
|
|
64c9ac4071 | ||
|
|
75fc922898 | ||
|
|
59e2c3fd5e | ||
|
|
5bd73741cf | ||
|
|
fcfa3beaa3 | ||
|
|
dfbdaa9dab | ||
|
|
5f1f07e5a1 | ||
|
|
eace0d9b37 | ||
|
|
d83b32a200 | ||
|
|
9cefb33388 | ||
|
|
bd5d6d0686 | ||
|
|
ecebaf8216 | ||
|
|
8762647b55 | ||
|
|
664c0ee77c | ||
|
|
7a9c3d351d | ||
|
|
69e52d1ed8 | ||
|
|
bd594e0c05 | ||
|
|
3bf42860e2 | ||
|
|
0532f51cc2 | ||
|
|
1808597258 | ||
|
|
2744989cf1 | ||
|
|
0d04abb5c7 | ||
|
|
495168e7bb | ||
|
|
475fbf9561 | ||
|
|
c6264cf098 | ||
|
|
bc9123affb | ||
|
|
7fd12972ad | ||
|
|
2d19243b3b | ||
|
|
8ad4f188f2 | ||
|
|
0c19c56909 | ||
|
|
de35baadf3 | ||
|
|
66c209f9e4 | ||
|
|
d7a3ed2d4a | ||
|
|
0c25ffc26e | ||
|
|
9f864574de | ||
|
|
a38f7c8524 | ||
|
|
bf85cd404c | ||
|
|
102adb3544 | ||
|
|
c2b360d03e | ||
|
|
1bec1b7002 | ||
|
|
1b4b4807a5 | ||
|
|
d4d135f2a9 | ||
|
|
96faad1c01 | ||
|
|
d55d5f15ae | ||
|
|
26e4560429 | ||
|
|
775bd3a08b | ||
|
|
d92d8bce89 | ||
|
|
1b91663244 | ||
|
|
8fb673e93e | ||
|
|
d1372d9b3b | ||
|
|
063f0da565 | ||
|
|
c893247107 | ||
|
|
a5adcfff4c | ||
|
|
d3583276b3 | ||
|
|
55d8de1b23 | ||
|
|
5b829636cb | ||
|
|
68ac92a616 | ||
|
|
db20cda27a | ||
|
|
60dd47c696 | ||
|
|
42da543980 | ||
|
|
e4ea547623 | ||
|
|
33a6c81a07 | ||
|
|
9ba503cd6b | ||
|
|
f3f3dccf8e | ||
|
|
2f0718e885 | ||
|
|
7b8001d060 | ||
|
|
3c022e4332 | ||
|
|
37e34c5209 | ||
|
|
4b71607215 | ||
|
|
e62875ac90 | ||
|
|
835345b89e | ||
|
|
4ddacc707d | ||
|
|
0d653a2ce6 | ||
|
|
3208260ef8 | ||
|
|
2e57789d10 | ||
|
|
b3006f6fd8 | ||
|
|
142ddcd57a | ||
|
|
a562793fce | ||
|
|
e6c4ecfc10 | ||
|
|
5ff579f793 | ||
|
|
8df346f377 | ||
|
|
af76068297 | ||
|
|
5dddf0a460 | ||
|
|
1854338ff4 | ||
|
|
11b96875e0 | ||
|
|
6462c524f4 | ||
|
|
b703303dd4 | ||
|
|
ab77b6b28e | ||
|
|
0da9d1c036 | ||
|
|
88436b8627 | ||
|
|
f6116510a1 | ||
|
|
5a132aaff4 | ||
|
|
59992c8af2 | ||
|
|
ff3ec78df9 | ||
|
|
b980ae1e8c | ||
|
|
97b9d488d4 | ||
|
|
3ebf8d5db9 | ||
|
|
89e33903b2 | ||
|
|
6a610ac57d | ||
|
|
413f797155 | ||
|
|
6c8dc49216 | ||
|
|
9ded1447ec | ||
|
|
516ed1fd5b | ||
|
|
1a4f8f992b | ||
|
|
23adfef281 | ||
|
|
20b6816562 | ||
|
|
c6400550e1 | ||
|
|
f26749ad79 | ||
|
|
177c98fa95 | ||
|
|
7baa187863 | ||
|
|
11d504e8f4 | ||
|
|
83378e4373 | ||
|
|
06abe2302b | ||
|
|
f3e5f7879d | ||
|
|
bc5125b719 | ||
|
|
c6d23f9a0b | ||
|
|
3b8afce7c7 | ||
|
|
39e28f1f2a | ||
|
|
08363b1d73 | ||
|
|
91aa0f200e | ||
|
|
d9e190096c | ||
|
|
4dca86d5cf | ||
|
|
d3b628bb24 | ||
|
|
cfbb05fb1b | ||
|
|
340d428c88 | ||
|
|
d22bbc54c7 | ||
|
|
c5cda99f9d | ||
|
|
67854e0720 | ||
|
|
cfe257495a | ||
|
|
5f4e701461 | ||
|
|
e3622e0860 | ||
|
|
f0e6b8ca47 | ||
|
|
ef0b239e70 | ||
|
|
590d80268e | ||
|
|
a5de329712 | ||
|
|
2889a4438b | ||
|
|
f5952088e3 | ||
|
|
bcacb49c05 | ||
|
|
976b8e426d | ||
|
|
90ecd34cfd | ||
|
|
35670c8654 | ||
|
|
07bc97fb31 | ||
|
|
d01806ee2d | ||
|
|
a54e42961c | ||
|
|
2e46aa33c0 | ||
|
|
d90cf6384c | ||
|
|
af3f676683 | ||
|
|
abf980d603 | ||
|
|
693b6d2547 | ||
|
|
06313cac64 | ||
|
|
cf1e315d72 | ||
|
|
955e1799c8 | ||
|
|
3db86f1e25 | ||
|
|
6f9b6a29c4 | ||
|
|
b056d40eba | ||
|
|
708c3bc3ce | ||
|
|
39efc12dc8 | ||
|
|
4211151fe6 | ||
|
|
3974b6e6cd | ||
|
|
b24df6e195 | ||
|
|
bcd33dc8e3 | ||
|
|
6c66c236fb | ||
|
|
520121b2b2 | ||
|
|
48c1d05151 | ||
|
|
8ba61a1da1 | ||
|
|
cd129e32a7 | ||
|
|
2b3e43f160 | ||
|
|
d7bf04d2c0 | ||
|
|
4cde8cd501 | ||
|
|
fb4e627e62 | ||
|
|
ef3a97cedd | ||
|
|
1a86ad5cec | ||
|
|
529c401eee | ||
|
|
a87236fea2 | ||
|
|
975c1ca62c | ||
|
|
d02b9d848c | ||
|
|
f340f9ac94 | ||
|
|
1b3f290037 | ||
|
|
b551ef315f | ||
|
|
10bb6ab3bb | ||
|
|
4af57b0dd3 | ||
|
|
bc613eeff9 | ||
|
|
7507e88406 | ||
|
|
073f281f1e | ||
|
|
3355b49724 | ||
|
|
0e2db4a23e | ||
|
|
addc817cca | ||
|
|
a0a86d0f62 | ||
|
|
b24e7cce38 | ||
|
|
ffd1b7069f | ||
|
|
0404c3bd94 | ||
|
|
d0b74a95f4 | ||
|
|
0288d5dc19 | ||
|
|
9d06229cba | ||
|
|
c60ada8421 | ||
|
|
74a1d5cf38 | ||
|
|
43532e5322 | ||
|
|
68cd1d6631 | ||
|
|
afc18cc390 | ||
|
|
57d15936cb | ||
|
|
14406dbf76 | ||
|
|
12c561cee0 | ||
|
|
f79b8a1ca5 | ||
|
|
637daa5bda | ||
|
|
2eae6b4be7 | ||
|
|
dc5e935ecd | ||
|
|
538085169a | ||
|
|
7e8a635e7e | ||
|
|
74e76b6f97 | ||
|
|
cb6309d370 | ||
|
|
8065889862 | ||
|
|
e7a8950d09 | ||
|
|
88e9dc2177 | ||
|
|
2c555c969e | ||
|
|
77c04c3a14 | ||
|
|
48cea6efdf | ||
|
|
89a90d9522 | ||
|
|
0026f3fdef | ||
|
|
ee054fd99c | ||
|
|
7abdade7c1 | ||
|
|
79a50de697 | ||
|
|
e47aef5615 | ||
|
|
b96ae1bac0 | ||
|
|
7dafcb0860 | ||
|
|
5a6a686f42 | ||
|
|
b576bb55ef | ||
|
|
fc8205d375 | ||
|
|
7eb0669de5 | ||
|
|
86161ccf2a | ||
|
|
5e55732cb0 | ||
|
|
a87f0481b8 | ||
|
|
c3d61361f8 | ||
|
|
a48934e31c | ||
|
|
ac75f67e2a | ||
|
|
45cfa6c8d8 | ||
|
|
25a06b0bea | ||
|
|
91af72e8b1 | ||
|
|
fd9b354c70 | ||
|
|
1647e67dd4 | ||
|
|
803e120a51 | ||
|
|
6ba374c62b | ||
|
|
ca28f24e42 | ||
|
|
0d4c38138a | ||
|
|
fe291e90f0 | ||
|
|
220e1e8d83 | ||
|
|
2c0fb3adb0 | ||
|
|
b851eefb03 | ||
|
|
eaad2d8b1f | ||
|
|
575d235296 | ||
|
|
faf74519dc | ||
|
|
66becaf91c | ||
|
|
6610ba67c4 | ||
|
|
e05dd5ebb5 | ||
|
|
cf77f29988 | ||
|
|
2be8ed886b | ||
|
|
21e5b0ab52 | ||
|
|
d72cb74ffa | ||
|
|
3c988d555f | ||
|
|
5981a65cc2 | ||
|
|
9900762747 | ||
|
|
0db2ca6659 | ||
|
|
8e68626f14 | ||
|
|
72a88d1f91 | ||
|
|
bd75e9c184 | ||
|
|
23df16ecf9 | ||
|
|
387732d2f9 | ||
|
|
75b01d5021 | ||
|
|
b7fa5db570 | ||
|
|
297f126f66 | ||
|
|
bd6400f17f | ||
|
|
21e55023a4 | ||
|
|
49e024bdd8 | ||
|
|
57877107d8 | ||
|
|
e9b4a99c8f | ||
|
|
7bc4876071 | ||
|
|
e7899ee15e | ||
|
|
7ea20d9ce9 | ||
|
|
3045f327d5 | ||
|
|
2d1f182c49 | ||
|
|
0677f51717 | ||
|
|
a9bfeb0e96 | ||
|
|
c44d27fb7f | ||
|
|
a0a01b8ba7 | ||
|
|
e32215e19a | ||
|
|
893fb0e3cc | ||
|
|
1d2a838717 | ||
|
|
8457a19792 | ||
|
|
0b2638b201 | ||
|
|
4dce57a1b6 | ||
|
|
658b835a70 | ||
|
|
52af955463 | ||
|
|
09e886db0b | ||
|
|
f668640e3d | ||
|
|
afcd44deaa | ||
|
|
8c8c21c63b | ||
|
|
55dabbe16a | ||
|
|
494bd7b79c | ||
|
|
320ee072e6 | ||
|
|
67ef5fe8e6 | ||
|
|
7c5c700bb8 | ||
|
|
dd51908439 | ||
|
|
6a0f638d40 | ||
|
|
2480c6633c | ||
|
|
ca62e54652 | ||
|
|
44dd9ae9fb | ||
|
|
3f4d62952a | ||
|
|
391cda919f | ||
|
|
44fc61ed86 | ||
|
|
f70685d53c | ||
|
|
807abfef4f | ||
|
|
810d4c51f9 | ||
|
|
1a0684cd20 | ||
|
|
47d811c250 | ||
|
|
4d0998ef72 | ||
|
|
5d2b0377d8 | ||
|
|
f6d69b6051 | ||
|
|
490496f381 | ||
|
|
bc4f719931 | ||
|
|
ded4a75b9c | ||
|
|
9e75eb35a0 | ||
|
|
326fb99849 | ||
|
|
fe1ced9f58 | ||
|
|
00d0502cbd | ||
|
|
c30bd51b91 | ||
|
|
9391dd9d51 | ||
|
|
3cd112b60f | ||
|
|
dce0c43e26 | ||
|
|
80e228f236 | ||
|
|
8ee2545b05 | ||
|
|
9bcc56e337 | ||
|
|
3c5a94452a | ||
|
|
fce7f8aa68 | ||
|
|
9e13f18463 | ||
|
|
2905f6b465 | ||
|
|
9d3f3eb013 | ||
|
|
75b5afa759 | ||
|
|
78c2f19e72 | ||
|
|
9a66a626f7 | ||
|
|
f42b7f4efa | ||
|
|
bf9654e138 | ||
|
|
4decd1d784 | ||
|
|
3e7cd24814 | ||
|
|
3e0bece53a | ||
|
|
c1b89b3b06 | ||
|
|
f5eadb2957 | ||
|
|
ee393c66dd | ||
|
|
72a7765b58 | ||
|
|
176daeec03 | ||
|
|
066f740819 | ||
|
|
04121644a3 | ||
|
|
1e37a0d9e6 | ||
|
|
fc8d33a98a | ||
|
|
2089727c7f | ||
|
|
fbbf705d10 | ||
|
|
e08c302756 | ||
|
|
87ef3519d9 | ||
|
|
9002c33046 | ||
|
|
b9ad6f4fd2 | ||
|
|
57db17853e | ||
|
|
58555d598b | ||
|
|
0eeae24049 | ||
|
|
3def7c66ad | ||
|
|
eeac3bc436 | ||
|
|
bdebc7f32e | ||
|
|
e7d6811948 | ||
|
|
0e263dd753 | ||
|
|
56bbcb1cb3 | ||
|
|
f02648022a | ||
|
|
9bb1f4e6f8 | ||
|
|
d219f0bdc6 | ||
|
|
c8d0f6ff20 | ||
|
|
2813c75292 | ||
|
|
581e055359 | ||
|
|
88a2cfc5e1 | ||
|
|
fb29830521 | ||
|
|
6540fea4da | ||
|
|
4fd33b015e | ||
|
|
d2fcdb3b6c | ||
|
|
c2fe0876c2 | ||
|
|
39aebd7144 | ||
|
|
6a7d0b8c0a | ||
|
|
7b92bee596 | ||
|
|
f7eda362c7 | ||
|
|
0fb8459bc1 | ||
|
|
42d9b57733 | ||
|
|
ebe143d9c9 | ||
|
|
8acbdfbf1d | ||
|
|
6eb36226b1 | ||
|
|
21984f0bbe | ||
|
|
5eac945bc0 | ||
|
|
2736b4198a | ||
|
|
a4f2f7faa6 | ||
|
|
94923175a8 | ||
|
|
f1c629e7bf | ||
|
|
5d0c60bd24 | ||
|
|
59f08cd651 | ||
|
|
63db194140 | ||
|
|
38db8d4e1a | ||
|
|
c2e8cbb846 | ||
|
|
3c567b895f | ||
|
|
673c75fb72 | ||
|
|
7a576ee36e | ||
|
|
73c09e3365 | ||
|
|
07dd2b279e | ||
|
|
c7943d2521 | ||
|
|
9b483d381f | ||
|
|
4e25337779 | ||
|
|
1e5041b8b0 | ||
|
|
3f4bbe9b9e | ||
|
|
56eaf883dc | ||
|
|
d8c0af7ded | ||
|
|
8724a6b6e2 | ||
|
|
b968b2c438 | ||
|
|
533d1ea9ab | ||
|
|
c9e32c449a | ||
|
|
d9e42bfcba | ||
|
|
f552564fc1 | ||
|
|
b21a096516 | ||
|
|
d0b25de554 | ||
|
|
bba836dc31 | ||
|
|
14fc8e2efa | ||
|
|
d8bd4fbea6 | ||
|
|
7cc89da879 | ||
|
|
baed12f45e | ||
|
|
dfbf291725 | ||
|
|
cb07074246 | ||
|
|
0198fe90b6 | ||
|
|
061221b073 | ||
|
|
7e4cfede7d | ||
|
|
48225c3c0a | ||
|
|
02b89362b2 | ||
|
|
e0e812f95c | ||
|
|
2cbc7e4f9a | ||
|
|
7bec37bfd8 | ||
|
|
828550687c | ||
|
|
d3caba4073 | ||
|
|
7e802706fe | ||
|
|
c87e3eac08 | ||
|
|
5b1b12ec9e | ||
|
|
c0c6f13d44 | ||
|
|
20f192daa1 | ||
|
|
0e73b53803 | ||
|
|
e2923c378e | ||
|
|
216d4da85a | ||
|
|
407e775282 | ||
|
|
e71afa63b0 | ||
|
|
30fd99679a | ||
|
|
a9ef15cdfa | ||
|
|
b08442b46f | ||
|
|
f56e2fc48e | ||
|
|
5452b55e38 | ||
|
|
bec8d36961 | ||
|
|
8188e3d0cf | ||
|
|
10e92eec32 | ||
|
|
37f2336eee | ||
|
|
290cb1b517 | ||
|
|
7520fe7000 | ||
|
|
9f861dfe89 | ||
|
|
fffb87d013 | ||
|
|
0cb731c584 | ||
|
|
233faea200 | ||
|
|
1b9015a049 | ||
|
|
111de1462c | ||
|
|
7fb0b4a8df | ||
|
|
627c49c5db | ||
|
|
665b1ad58a | ||
|
|
4623e9d7fc | ||
|
|
a3f0290432 | ||
|
|
69b1798af7 | ||
|
|
10af206024 | ||
|
|
6c23d7aec8 | ||
|
|
7937ccee30 | ||
|
|
b98e8c34eb | ||
|
|
5c2059b4e8 | ||
|
|
f4be0cf4b3 | ||
|
|
5f7abda22d | ||
|
|
51424aed32 | ||
|
|
914fb09ed0 | ||
|
|
7ea7e5c951 | ||
|
|
c69807b416 | ||
|
|
08320119f1 | ||
|
|
49dcd9bb4a | ||
|
|
8f41603101 | ||
|
|
d437609365 | ||
|
|
e1c681fffe | ||
|
|
d4d47182b4 | ||
|
|
bc2d0191ff | ||
|
|
bd222ed2bf | ||
|
|
ceac824e2b | ||
|
|
93b835ae0d | ||
|
|
aa2d57cf37 | ||
|
|
f6d4821a68 | ||
|
|
fa025bcf39 | ||
|
|
842176a595 | ||
|
|
0fb74b56b3 | ||
|
|
87abcd6a52 | ||
|
|
0a71e40030 | ||
|
|
1b9db035ba | ||
|
|
34c3035c2b | ||
|
|
d92951ba5b | ||
|
|
c5749cbbb1 | ||
|
|
a2e5bcc137 | ||
|
|
761d707b69 | ||
|
|
d327a05e12 | ||
|
|
6477d21e24 | ||
|
|
d3612e956e | ||
|
|
5a3ee3a46e | ||
|
|
23cb743ce2 | ||
|
|
40e5a2dc35 | ||
|
|
73eb4079ef | ||
|
|
5b60c8aa7e | ||
|
|
8c5a688566 | ||
|
|
7ea9706c94 | ||
|
|
5976e2d873 | ||
|
|
d32d452583 | ||
|
|
b77bdabca6 | ||
|
|
e8971e0d8b | ||
|
|
498c4660d0 | ||
|
|
54c142fc0d | ||
|
|
6079402dc4 | ||
|
|
3d4a7f97e1 | ||
|
|
d6e04beb95 | ||
|
|
9a225e6244 | ||
|
|
a03f00628b | ||
|
|
360485d830 | ||
|
|
3f1cfd2995 | ||
|
|
08e2b601f1 | ||
|
|
101ed9a633 | ||
|
|
81ecd17b93 | ||
|
|
5a78f02dcb | ||
|
|
0a08464d4f | ||
|
|
a638d42d92 | ||
|
|
90256ea2f5 | ||
|
|
90aefd35f0 | ||
|
|
2f428b7261 | ||
|
|
f935060b2b | ||
|
|
a367812348 | ||
|
|
8e6a89b279 | ||
|
|
0747697d10 | ||
|
|
8c7494885f | ||
|
|
590a0b8077 | ||
|
|
064d261357 | ||
|
|
adb96710bf | ||
|
|
dd64268bc7 | ||
|
|
aecbd0eda3 | ||
|
|
2c1b7e3b14 | ||
|
|
3eb6357892 | ||
|
|
e0c98a5c79 | ||
|
|
dfb60ec2ed | ||
|
|
b85e260b92 | ||
|
|
4d72845c58 | ||
|
|
9067f76bd6 | ||
|
|
6c447da730 | ||
|
|
1a9f6f06df | ||
|
|
5f8bdc564b | ||
|
|
f6498f2a7b | ||
|
|
0ef5f7f9b1 | ||
|
|
a323a4dd8e | ||
|
|
d5581ffd05 | ||
|
|
77968ab33a | ||
|
|
265a5955d7 | ||
|
|
465ab2fa34 | ||
|
|
d60bfef924 | ||
|
|
5225587ff5 | ||
|
|
fec790e108 | ||
|
|
eb5ea28bcd | ||
|
|
a9fe2ab5c4 | ||
|
|
f2db0886dd | ||
|
|
f6926a9642 | ||
|
|
d3442bb08f | ||
|
|
a08f5c3016 | ||
|
|
eff892ccb8 | ||
|
|
b565eaaf5a | ||
|
|
3831a371a4 | ||
|
|
77ec8ebfc3 | ||
|
|
c272e4d1e8 | ||
|
|
d69b7f41f2 | ||
|
|
c8a47d839a | ||
|
|
b3db8913c6 | ||
|
|
e8f8f34a76 | ||
|
|
5bec2c87cb | ||
|
|
d4d283480f | ||
|
|
71a524662d | ||
|
|
172aef7e26 | ||
|
|
395b5faa2d | ||
|
|
f4a765c5ed | ||
|
|
f5ad845d57 | ||
|
|
2a70ca375c | ||
|
|
396c589743 | ||
|
|
7670b81633 | ||
|
|
f9b98f97b6 | ||
|
|
00bf39204d | ||
|
|
e9ba3ad223 | ||
|
|
6b6666be47 | ||
|
|
e2a413df78 | ||
|
|
b24f68eee7 | ||
|
|
72c1a0222c | ||
|
|
bd97083268 | ||
|
|
e7cf5d4f3d | ||
|
|
2429971bcd | ||
|
|
170a71bd02 | ||
|
|
14efbb1356 | ||
|
|
20cd0dd77a | ||
|
|
2db1fac1eb | ||
|
|
ce8490ed41 | ||
|
|
7e208984de | ||
|
|
4d29a3ae3c | ||
|
|
547c63b28e | ||
|
|
9004aae597 | ||
|
|
1bdfc5a1a4 | ||
|
|
195273f01d | ||
|
|
605aea2211 | ||
|
|
d1afcb941f | ||
|
|
cc7e21170f | ||
|
|
30602c0849 | ||
|
|
1dabe95899 | ||
|
|
4995c0ed94 | ||
|
|
df82dbe5f5 | ||
|
|
de9c74e1d9 | ||
|
|
d89eb9ff0e | ||
|
|
5041042925 | ||
|
|
0d983e63a1 | ||
|
|
3f7a60d84b | ||
|
|
1eb9fcf521 | ||
|
|
1571690bbb | ||
|
|
eb2133e648 | ||
|
|
9107b3ddbe | ||
|
|
64433bd8dd | ||
|
|
bc6159c538 | ||
|
|
8e5954f36f | ||
|
|
b53ef148a0 | ||
|
|
3f950490f9 | ||
|
|
49b1c1368e | ||
|
|
bdb5df104a | ||
|
|
700778e574 | ||
|
|
a104b35cea | ||
|
|
bbbf95705b | ||
|
|
927871d73d | ||
|
|
62419e975f | ||
|
|
3fc2c4223b | ||
|
|
84da884211 | ||
|
|
1bc8e9aebf | ||
|
|
a228e46a39 | ||
|
|
81913a5987 | ||
|
|
2d7f31a60d | ||
|
|
804858d736 | ||
|
|
222be9cf6c | ||
|
|
75a52f032b | ||
|
|
a9e0a51059 | ||
|
|
164b993064 | ||
|
|
62b103fa23 | ||
|
|
e17eb01f76 | ||
|
|
2fe55e4715 | ||
|
|
a8a82e45a0 | ||
|
|
fcba140ffe | ||
|
|
b96ff781bc | ||
|
|
94392f248c | ||
|
|
24e6c31c44 | ||
|
|
cab0e4395a | ||
|
|
ae66e299e6 | ||
|
|
88ad2ee103 | ||
|
|
48f3f4ca37 | ||
|
|
f3ab17a3c0 | ||
|
|
5749d43fb6 | ||
|
|
f5d808e68f | ||
|
|
88f1160e2b | ||
|
|
b7218d2279 | ||
|
|
e928a0aaea | ||
|
|
a2447ecaa3 | ||
|
|
09a089d5f1 | ||
|
|
137b0cd837 | ||
|
|
4ffdd9eb15 | ||
|
|
4e29099ed9 | ||
|
|
8900df6f13 | ||
|
|
814c034683 | ||
|
|
e1ec4bee75 | ||
|
|
6ed7767512 | ||
|
|
08b18af8a0 | ||
|
|
75908ab88f | ||
|
|
fcba33997f | ||
|
|
134be307d3 | ||
|
|
092f8e6482 | ||
|
|
cf5960afad | ||
|
|
e5204e55d3 | ||
|
|
61b14d1cd9 | ||
|
|
c2e3b47d29 | ||
|
|
a239007db0 | ||
|
|
c5e5eb8b86 | ||
|
|
a4d15bfb88 | ||
|
|
b33158b92e | ||
|
|
3b30c4d487 | ||
|
|
dfd7ac633a | ||
|
|
213f8a604e | ||
|
|
13499352fd | ||
|
|
70aa4934c5 | ||
|
|
1d538ee70c | ||
|
|
2ed2e6d62f | ||
|
|
aea0570d2b | ||
|
|
def33a77da | ||
|
|
29db677a9e | ||
|
|
d362ca3a6d | ||
|
|
3796792421 | ||
|
|
b8020db029 | ||
|
|
a90030ca2d | ||
|
|
3f8d3afe5c | ||
|
|
155004bdbb | ||
|
|
534c74b67c | ||
|
|
5428935ab1 | ||
|
|
fc14d089b1 | ||
|
|
4e543238e1 | ||
|
|
6579a542b8 | ||
|
|
27e3dcea6c | ||
|
|
01f73b11d8 | ||
|
|
dd07226ab4 | ||
|
|
67d856e4b7 | ||
|
|
6ad1e57123 | ||
|
|
27a52ef56a | ||
|
|
d290596c1d | ||
|
|
342b4a3b52 | ||
|
|
19652e6367 | ||
|
|
1c76c869ef | ||
|
|
706d842102 | ||
|
|
2889631b61 | ||
|
|
5b9f0f45a9 | ||
|
|
ecc059bf8b | ||
|
|
e1f4a50e65 | ||
|
|
ec2c5a9b2c | ||
|
|
4efbc20a4f | ||
|
|
657e4f12bb | ||
|
|
22ffb1f53f | ||
|
|
4854da4805 | ||
|
|
33a0df20c3 | ||
|
|
e4d1e4b7a0 | ||
|
|
dbc193ab2a | ||
|
|
7bde174b40 | ||
|
|
cf8014582b | ||
|
|
e2796dd351 | ||
|
|
0696a18e0b | ||
|
|
0cc9388af0 | ||
|
|
a73529969a | ||
|
|
c55de17ca8 | ||
|
|
41a751e7f5 | ||
|
|
b9a53e441e | ||
|
|
add62c2093 | ||
|
|
de01ac233c | ||
|
|
c9e926f451 | ||
|
|
8d405849be | ||
|
|
5fbdb1af9f | ||
|
|
83350f8866 | ||
|
|
b4ccd889af | ||
|
|
35a3f2dc91 | ||
|
|
ed7a4514de | ||
|
|
2e295e354a | ||
|
|
d71d9cc8ef | ||
|
|
108b62d839 | ||
|
|
46dfaa5b28 | ||
|
|
323e9c3eb3 | ||
|
|
d677d3d2ef | ||
|
|
c39b239d78 | ||
|
|
5f3ab823fd | ||
|
|
03d1da4bf2 | ||
|
|
78656d220b | ||
|
|
7e7aa7992a | ||
|
|
a53b4df8b6 | ||
|
|
3ba2b8ade6 | ||
|
|
79cb4cd6a5 | ||
|
|
d3ab4b5def | ||
|
|
cdeb07520f | ||
|
|
cc77ef680d | ||
|
|
e75ebd9b6e | ||
|
|
62c9b8390b | ||
|
|
0724c044d6 | ||
|
|
bed4afe74f | ||
|
|
d4263cc8b9 | ||
|
|
b4b11465a1 | ||
|
|
daaebc59cb | ||
|
|
79a74038ac | ||
|
|
314baabf1d | ||
|
|
a24fbb8f61 | ||
|
|
32eaab0783 | ||
|
|
e185b626bd | ||
|
|
74dc94f6bc | ||
|
|
099d9aaca6 | ||
|
|
5fa3b9f2fb | ||
|
|
ad8e2ad4f6 | ||
|
|
8203eff47b | ||
|
|
ad333047d9 | ||
|
|
6e5d2d6a36 | ||
|
|
4b610b6293 | ||
|
|
2bd998e82f | ||
|
|
c55cc3c472 | ||
|
|
674483a4c6 | ||
|
|
a16f84560d | ||
|
|
1b4500339e | ||
|
|
3143468751 | ||
|
|
f627b32355 | ||
|
|
10ba6fb773 | ||
|
|
2d3f1e51ec | ||
|
|
1c23336cff | ||
|
|
a622ffddfe | ||
|
|
79cf0c63c4 | ||
|
|
d57a592273 | ||
|
|
4f15802fbc | ||
|
|
357ae7a8aa | ||
|
|
ac00ae4daf | ||
|
|
249f034a38 | ||
|
|
32c612b747 | ||
|
|
386f7cf65d | ||
|
|
8dc07882b6 | ||
|
|
320802633f | ||
|
|
dcc4b91b35 | ||
|
|
e7c27ed999 | ||
|
|
928c9c9aad | ||
|
|
4a90f13dee | ||
|
|
61dddcee3f | ||
|
|
4c510001ce | ||
|
|
82dfa690d2 | ||
|
|
2768beec4c | ||
|
|
bd0aa16322 | ||
|
|
a1746a5bf1 | ||
|
|
fb502fb66b | ||
|
|
c9a69263b6 | ||
|
|
f0c8056334 | ||
|
|
17dd9239b6 | ||
|
|
6c691d9223 | ||
|
|
920644afaf | ||
|
|
8668aee7fd | ||
|
|
4f386b3e5e | ||
|
|
0f4667d942 | ||
|
|
5705c42820 | ||
|
|
f5f0d00a75 | ||
|
|
ec75e21898 | ||
|
|
94e9e17c79 | ||
|
|
5134e64cae | ||
|
|
53d39ccb4b | ||
|
|
b227e160f0 | ||
|
|
88a2b82f7e | ||
|
|
0016365446 | ||
|
|
0fa8feb475 | ||
|
|
eb872f8fca | ||
|
|
5fd78320dd | ||
|
|
3195042c7d | ||
|
|
b0234fa8b7 | ||
|
|
2a629fee37 | ||
|
|
bf13700a95 | ||
|
|
83268f5ff6 | ||
|
|
1eb42654e0 | ||
|
|
e05c34d1ee | ||
|
|
8325bb11e2 | ||
|
|
c1b577f2c7 | ||
|
|
29a61db275 | ||
|
|
e2c61485a1 | ||
|
|
65f200e02c | ||
|
|
aa9432e24f | ||
|
|
30d1585468 | ||
|
|
376c5263f0 | ||
|
|
bcd6d4e8a7 | ||
|
|
32eb896c56 | ||
|
|
4603163e88 | ||
|
|
b3c9fb2fea | ||
|
|
d66ea07ba4 | ||
|
|
496279c92d | ||
|
|
e0ae925ca3 | ||
|
|
1799c19933 | ||
|
|
94eddd3b42 | ||
|
|
79829cb6d2 | ||
|
|
0d20ca0bf2 | ||
|
|
3ede5ea28f | ||
|
|
cfd15f4e02 | ||
|
|
1f9ff2dd88 | ||
|
|
7740a39a4d | ||
|
|
5eda2dfe0e | ||
|
|
1509c6a471 | ||
|
|
19acd608a5 | ||
|
|
bc81ff28d8 | ||
|
|
ba867e95c3 | ||
|
|
53f99ca434 | ||
|
|
8958ea036e | ||
|
|
d3563c30e7 | ||
|
|
0f017339b7 | ||
|
|
4d3a3d806f | ||
|
|
10a0f708b9 | ||
|
|
409d3c168d | ||
|
|
005220b6d4 | ||
|
|
8462b039d3 | ||
|
|
2e3cabce6b | ||
|
|
2c6b615b8c | ||
|
|
f910e013a6 | ||
|
|
5f1b03261c | ||
|
|
862197fc96 | ||
|
|
44ebd894d5 | ||
|
|
b85ff7db36 | ||
|
|
22fd216de5 | ||
|
|
2f02c428cc | ||
|
|
1732413ee1 | ||
|
|
33325f1574 | ||
|
|
4f288c6fd9 | ||
|
|
5ffb4597b3 | ||
|
|
d9de38b935 | ||
|
|
ad459b83c0 | ||
|
|
d96ea6b815 | ||
|
|
40c3e31955 | ||
|
|
f84c831cda | ||
|
|
904cf97f7d | ||
|
|
791e22dc24 | ||
|
|
c82a0b3d2d | ||
|
|
2fc887724b | ||
|
|
e16762a137 | ||
|
|
4a07ffac69 | ||
|
|
9331c4ce9e | ||
|
|
184ef94e7e | ||
|
|
d67740d44d | ||
|
|
7b30daf88d | ||
|
|
d5e76da475 | ||
|
|
5d91c1af81 | ||
|
|
bb013e78d2 | ||
|
|
c22816de08 | ||
|
|
9bb851d437 | ||
|
|
c8a9f8dcf5 | ||
|
|
e8653ffe32 | ||
|
|
5499df418a | ||
|
|
b586618411 | ||
|
|
3b5730be54 | ||
|
|
961c4a1b9c | ||
|
|
72c5908f09 | ||
|
|
2a588ff943 | ||
|
|
24f11b5ae9 | ||
|
|
1d44d99875 | ||
|
|
eebcdfb24f | ||
|
|
526b56a650 | ||
|
|
20675f56e6 | ||
|
|
e5fbdeb3a9 | ||
|
|
7cc92ec1c1 | ||
|
|
fe0bf6f2ef | ||
|
|
828c19d478 | ||
|
|
15db77558c | ||
|
|
18cf4a3bd3 | ||
|
|
e0378acfc2 | ||
|
|
53cb2af202 | ||
|
|
f74005a166 | ||
|
|
b0df981b01 | ||
|
|
888d844ec5 | ||
|
|
3a7eb9fc0c | ||
|
|
46276aacd9 | ||
|
|
3a7a29868f | ||
|
|
0060c8988a | ||
|
|
458f482c2a | ||
|
|
cc9d6587a3 | ||
|
|
dadf1b6ff9 | ||
|
|
db56bb92f0 | ||
|
|
e82e36f51e | ||
|
|
f9aad34d28 | ||
|
|
b2d720b853 | ||
|
|
c10c05e924 | ||
|
|
8909246725 | ||
|
|
48c0b0ab3c | ||
|
|
cbbed310c5 | ||
|
|
9e9c3b6342 | ||
|
|
fefd262c2e | ||
|
|
1108c99688 | ||
|
|
93da831ef0 | ||
|
|
72bf45912e | ||
|
|
5d94276c4f | ||
|
|
b43f27030e | ||
|
|
2845f55157 | ||
|
|
e254d84710 | ||
|
|
0ef7b5488d | ||
|
|
56b04f8df8 | ||
|
|
2d036c6b25 | ||
|
|
ad3be5c848 | ||
|
|
a0544cc345 | ||
|
|
3245261162 | ||
|
|
21b7348c6a | ||
|
|
54d4dc6e1e | ||
|
|
e00d864199 | ||
|
|
ad6fc89a24 | ||
|
|
fc4823ce8e | ||
|
|
0bd9831eec | ||
|
|
dec2ef70c7 | ||
|
|
d19d8ea770 | ||
|
|
34f7752c0f | ||
|
|
da4d061067 | ||
|
|
85155439be | ||
|
|
dd32e69061 | ||
|
|
1c0c660c32 | ||
|
|
2eac902c33 | ||
|
|
9f5322d309 | ||
|
|
f5c2f6f9e9 | ||
|
|
e0e3b180e7 | ||
|
|
041dc039d4 | ||
|
|
ac755b0ada | ||
|
|
c14259060c | ||
|
|
a423eb5323 | ||
|
|
fd732550d8 | ||
|
|
fac7ba70c8 | ||
|
|
e9f8b35918 | ||
|
|
23034dd4f5 | ||
|
|
70ad44adfd | ||
|
|
95ca8a2630 | ||
|
|
a7fd3c50e5 | ||
|
|
5639f40994 | ||
|
|
33a8262334 | ||
|
|
9b4f7ad696 | ||
|
|
eecd809d9a | ||
|
|
240d3cb8f1 | ||
|
|
5720b7b94c | ||
|
|
3855d7ea02 | ||
|
|
9f3351469d | ||
|
|
2c4a0cf65b | ||
|
|
a8f360c8fd | ||
|
|
b3cff6555f | ||
|
|
69706ecab0 | ||
|
|
05d3b97eae | ||
|
|
a38fb41e37 | ||
|
|
204ae377e3 | ||
|
|
3e5a652874 | ||
|
|
862ae26631 | ||
|
|
9e0208ec71 | ||
|
|
510530973c | ||
|
|
4e69301562 | ||
|
|
df06d4b9a2 | ||
|
|
b60561c3bb | ||
|
|
fffa503968 | ||
|
|
8754157810 | ||
|
|
0763686892 | ||
|
|
50776748a7 | ||
|
|
c07274a6fa | ||
|
|
4084005ee7 | ||
|
|
40fce574cf | ||
|
|
fd761735d4 | ||
|
|
9cb98bdcdc | ||
|
|
b5b1f7ff8a | ||
|
|
a86019d53b | ||
|
|
8cd7d08573 | ||
|
|
6d6f6ed8fc | ||
|
|
c9aed1381f | ||
|
|
49ba5f30c5 | ||
|
|
3c6149fd27 | ||
|
|
23f804eda3 | ||
|
|
4e321db189 | ||
|
|
716fde5784 | ||
|
|
c70b873397 | ||
|
|
4c0da691b8 | ||
|
|
d1d36f9af0 | ||
|
|
56efb4ffba | ||
|
|
540bc71873 | ||
|
|
c1df7ead1a | ||
|
|
d170c807e3 | ||
|
|
50c0abc786 | ||
|
|
c5057127ee | ||
|
|
ea7b1a9592 | ||
|
|
26d483f2d7 | ||
|
|
67fce58708 | ||
|
|
ea5771c442 | ||
|
|
fa8a1faeb3 | ||
|
|
d60a000af6 | ||
|
|
3d68e2b74f | ||
|
|
42f6d48abf | ||
|
|
5587957602 | ||
|
|
1d313ebf25 | ||
|
|
cf92349c1d | ||
|
|
b0ae44bc25 | ||
|
|
0c94124fdf | ||
|
|
22298ddb41 | ||
|
|
6f927b9941 | ||
|
|
dc5626f34d | ||
|
|
2717ec2759 | ||
|
|
d75449ea63 | ||
|
|
034f5b16bd | ||
|
|
ff420f1e67 | ||
|
|
5be771842f | ||
|
|
a131c93a14 | ||
|
|
3da3546ef8 | ||
|
|
47f5223c37 | ||
|
|
b8c50eb0fe | ||
|
|
fe05082ddc | ||
|
|
414acda9e0 | ||
|
|
47e3a5e75a | ||
|
|
573df892bc | ||
|
|
bfced59949 | ||
|
|
9a63b1751b | ||
|
|
53ae8b9bd1 | ||
|
|
bf3cc5a367 | ||
|
|
ac00bc6b70 | ||
|
|
ab62603c5a | ||
|
|
dd247cd541 | ||
|
|
0d7a9f32d6 | ||
|
|
0d85ef4404 | ||
|
|
8e9273a96a | ||
|
|
2dfdaaa4ba | ||
|
|
407094cdcb | ||
|
|
0111db6744 | ||
|
|
a49230c0e1 | ||
|
|
d04abc2021 | ||
|
|
b5368207d6 | ||
|
|
b8400e0c92 | ||
|
|
ef6b6cd279 | ||
|
|
e5903d8d6e | ||
|
|
e2a3db1b82 | ||
|
|
9e220e8416 | ||
|
|
605c233753 | ||
|
|
aafe18c2b5 | ||
|
|
262e8af813 | ||
|
|
75aa4ce8a0 | ||
|
|
a494cdaf92 | ||
|
|
848425418b | ||
|
|
989bed88fe | ||
|
|
99f66200f7 | ||
|
|
b642bd2cfd | ||
|
|
c062b7c9da | ||
|
|
7df7901532 | ||
|
|
9dabce819a | ||
|
|
a019109857 | ||
|
|
46cdf14ecc | ||
|
|
fd5ff20b68 | ||
|
|
81f9666e82 | ||
|
|
aa8659d5c7 | ||
|
|
9df2df0f9f | ||
|
|
98d38d763b | ||
|
|
319a4622b3 | ||
|
|
59b4c6dacb | ||
|
|
ec6d725587 | ||
|
|
6f19649186 | ||
|
|
6a583b9b5b | ||
|
|
6617b79cd8 | ||
|
|
46b695d093 | ||
|
|
de8a017e80 | ||
|
|
b71ec91f57 | ||
|
|
1b7c226779 | ||
|
|
ca5a5b4392 | ||
|
|
6ae7ae0210 | ||
|
|
b911d2fda4 | ||
|
|
847a92f580 | ||
|
|
a346493de1 | ||
|
|
efd5d6efab | ||
|
|
b6df5a4b7f | ||
|
|
34ae56e319 | ||
|
|
5870824d01 | ||
|
|
1dbadbfc35 | ||
|
|
d3f1cd7411 | ||
|
|
a821daadd3 | ||
|
|
a39f4b4487 | ||
|
|
f7b401b9b6 | ||
|
|
c342d18f92 | ||
|
|
e0e52a3090 | ||
|
|
6f92480315 | ||
|
|
490d008edf | ||
|
|
ccee17c573 | ||
|
|
dd4832e40b | ||
|
|
ff5b870a9f | ||
|
|
a371580201 | ||
|
|
b499c575bc | ||
|
|
f5a6268cef | ||
|
|
2df4083602 | ||
|
|
2495fb796d | ||
|
|
f7b49740c1 | ||
|
|
7e765fe726 | ||
|
|
1d339f74d1 | ||
|
|
a3bb288f01 | ||
|
|
0e1226573a | ||
|
|
8151b65d12 | ||
|
|
eefd36b50d | ||
|
|
6f4d769187 | ||
|
|
20b76a703a | ||
|
|
dcc09b7bb5 | ||
|
|
4ea0dee90a | ||
|
|
e61116d35a | ||
|
|
f7256c75d5 | ||
|
|
2696fd1291 | ||
|
|
b139620fba | ||
|
|
97115a9c1c | ||
|
|
36134c61b2 | ||
|
|
77cf0fd87b | ||
|
|
ab77a1cbc0 | ||
|
|
8490892493 | ||
|
|
5f35dffa34 | ||
|
|
89a4b5a6ff | ||
|
|
931de7e39c | ||
|
|
ff41d6aef9 | ||
|
|
8e1ba58551 | ||
|
|
2b1692a860 | ||
|
|
7a2981e21c | ||
|
|
f8717359d7 | ||
|
|
fe395e3a8c | ||
|
|
eb9fe851af | ||
|
|
d61851ad02 | ||
|
|
de7a408903 | ||
|
|
4b0f60fe71 | ||
|
|
a167bcff5e | ||
|
|
fb812e6335 | ||
|
|
3fd557c74a | ||
|
|
9b1272da51 | ||
|
|
ae2402f4a7 | ||
|
|
c98e869564 | ||
|
|
e1a019ec56 | ||
|
|
2d7fcbf23d | ||
|
|
b604177ca1 | ||
|
|
f27597cab5 | ||
|
|
40619c8184 | ||
|
|
b0a5df2838 | ||
|
|
ae9232a2cb | ||
|
|
ad28d0025a | ||
|
|
d6772c5ca5 | ||
|
|
a2400767ed | ||
|
|
1873b655a8 | ||
|
|
e27c2b59c8 | ||
|
|
4b50608642 | ||
|
|
e3a7f9eb7a | ||
|
|
c130b3a92e | ||
|
|
20fb6d6669 | ||
|
|
caf20cd43d | ||
|
|
608d661355 | ||
|
|
31c868818b | ||
|
|
443c4b6cd0 | ||
|
|
ee425bb7f0 | ||
|
|
9d1c026231 | ||
|
|
6d7612ead6 | ||
|
|
b3e31094ff | ||
|
|
c9750fab2c | ||
|
|
5a1348e4d1 | ||
|
|
b64d45a74e | ||
|
|
ba5a4c3945 | ||
|
|
9ba37a932b | ||
|
|
8ce377d033 | ||
|
|
a7f3dafd81 | ||
|
|
fb38b4b906 | ||
|
|
f0a1a90b0e | ||
|
|
fb331d39a6 | ||
|
|
7a3e6e19b1 | ||
|
|
5a063c541d | ||
|
|
eef25ec638 | ||
|
|
7141fb18c1 | ||
|
|
2c92be5e3d | ||
|
|
cae0733149 | ||
|
|
5d6ca7a88e | ||
|
|
23b8a4d364 | ||
|
|
c0df8cb578 | ||
|
|
4693c23a49 | ||
|
|
86364ff278 | ||
|
|
60f1f1cb3d | ||
|
|
ab3c520006 | ||
|
|
3798dcb98d | ||
|
|
9daf290432 | ||
|
|
3438f88c1e | ||
|
|
2c8858ff2d | ||
|
|
45ed9405b0 | ||
|
|
075ca78dd2 | ||
|
|
1d964ef921 | ||
|
|
0c6d9b1d3b | ||
|
|
1713f1222b | ||
|
|
450591d5a9 | ||
|
|
5c4863dfb1 | ||
|
|
c6a82cbc07 | ||
|
|
cc8f074d33 | ||
|
|
196d74c3a0 | ||
|
|
f28e87b240 | ||
|
|
2d77ef4245 | ||
|
|
3f578e24b5 | ||
|
|
fb5ee5e190 | ||
|
|
63e8378310 | ||
|
|
c24ebaf506 | ||
|
|
d3a1f11dfe | ||
|
|
f96e694ff8 | ||
|
|
d5d635e50f | ||
|
|
1ad4ce9822 | ||
|
|
3f971174e4 | ||
|
|
16e257ea32 | ||
|
|
37f1f8ac14 | ||
|
|
474fa9b3ec | ||
|
|
6d7b14ceb5 | ||
|
|
af14832b6c | ||
|
|
9ef185978f | ||
|
|
38bb44664c | ||
|
|
124d924ff5 | ||
|
|
e8091d8add | ||
|
|
500bd53b6a | ||
|
|
6996ca832d | ||
|
|
2e15ab44ab | ||
|
|
2ae5472872 | ||
|
|
98a32790cd | ||
|
|
6dcefcfb71 | ||
|
|
988366e37f | ||
|
|
98a37905b8 | ||
|
|
bd832d0fbb | ||
|
|
1f7e18f2a9 | ||
|
|
105e9c8e88 | ||
|
|
99e1a5f117 | ||
|
|
b35b49f7b3 | ||
|
|
5d064b18e6 | ||
|
|
11ea92288d | ||
|
|
1ea6ae9fd9 | ||
|
|
57d739673a | ||
|
|
4d8f76e922 | ||
|
|
fb41d4ab0f | ||
|
|
023ee688d3 | ||
|
|
5f8f0d8573 | ||
|
|
f56e0a6586 | ||
|
|
2c96420bc4 | ||
|
|
8d95eb57fa | ||
|
|
a9a5e79e85 | ||
|
|
2b720b8d0b | ||
|
|
93a0ab7f0a | ||
|
|
18dd55d1f4 | ||
|
|
c9074d8d96 | ||
|
|
f6c995157e | ||
|
|
a059a9fbdc | ||
|
|
2ac6596ba9 | ||
|
|
2d1f57e839 | ||
|
|
7799ed36f0 | ||
|
|
db5ed345bc | ||
|
|
082c6a9e64 | ||
|
|
b74a4d5225 | ||
|
|
16a572c250 | ||
|
|
6a8a2d6f56 | ||
|
|
237c855f10 | ||
|
|
15d18484e3 | ||
|
|
378e9b5539 | ||
|
|
7a135bd87c | ||
|
|
9e907e36e2 | ||
|
|
9ac71f304d | ||
|
|
ab9b604d02 | ||
|
|
155645307a | ||
|
|
4ca7ac4851 | ||
|
|
02eb24cd57 | ||
|
|
787fbe1bb6 | ||
|
|
7678858bab | ||
|
|
bf7a28f07c |
6
.dockerignore
Executable file
6
.dockerignore
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
target
|
||||||
|
Dockerfile
|
||||||
|
README.md
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.gitmodules
|
||||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*.png binary
|
||||||
|
*.pl text eol=lf
|
||||||
|
*.rs text eol=lf diff=rust
|
||||||
|
*.md text eol=lf diff=markdown
|
||||||
52
.github/workflows/docker-publish.yml
vendored
Normal file
52
.github/workflows/docker-publish.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: Docker Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: [ 'v*.*.*' ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: mjt128/scryer-prolog
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Workaround: https://github.com/docker/build-push-action/issues/461
|
||||||
|
- name: Setup Docker buildx
|
||||||
|
uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf
|
||||||
|
|
||||||
|
# Login against Docker registry
|
||||||
|
# https://github.com/docker/login-action
|
||||||
|
- name: Log into registry
|
||||||
|
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Extract Docker image tag from git tag. E.g. if git tag is "v0.19.1" then use
|
||||||
|
# Docker image tag "0.19.1". Tag "latest" is automatically synced with newest
|
||||||
|
# version.
|
||||||
|
# https://github.com/docker/metadata-action
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||||
|
with:
|
||||||
|
images: docker.io/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
|
||||||
|
# Build and push Docker image with Buildx
|
||||||
|
# https://github.com/docker/build-push-action
|
||||||
|
- name: Build and push Docker image
|
||||||
|
id: build-and-push
|
||||||
|
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
71
.github/workflows/test.yml
vendored
Normal file
71
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
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
|
||||||
|
msrv:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-20.04, macos-10.15]
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Install cargo-msrv
|
||||||
|
uses: baptiste0928/cargo-install@v1.1.0
|
||||||
|
with:
|
||||||
|
crate: cargo-msrv
|
||||||
|
- name: Verify MSRV
|
||||||
|
run: cargo msrv --verify
|
||||||
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: msys2 {0}
|
||||||
|
steps:
|
||||||
|
- name: Setup MSYS2
|
||||||
|
uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
update: true
|
||||||
|
install: >-
|
||||||
|
base-devel
|
||||||
|
mingw-w64-x86_64-rust
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Test on Windows
|
||||||
|
run: cargo test --verbose --all
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
src/static_atoms.rs
|
||||||
target/
|
target/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
14
.travis.yml
14
.travis.yml
@@ -1,14 +0,0 @@
|
|||||||
language: rust
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
- nightly
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- rust: nightly
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
script:
|
|
||||||
- cargo build --verbose --all
|
|
||||||
- cargo test --verbose --all
|
|
||||||
- cargo test --verbose --all --no-default-features --features num
|
|
||||||
2636
Cargo.lock
generated
2636
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
78
Cargo.toml
78
Cargo.toml
@@ -1,32 +1,76 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "scryer-prolog"
|
name = "scryer-prolog"
|
||||||
version = "0.8.118"
|
version = "0.9.1"
|
||||||
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
authors = ["Mark Thom <markjordanthom@gmail.com>"]
|
||||||
build = "build.rs"
|
edition = "2021"
|
||||||
repository = "https://github.com/mthom/scryer-prolog"
|
|
||||||
description = "A modern Prolog implementation written mostly in Rust."
|
description = "A modern Prolog implementation written mostly in Rust."
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/mthom/scryer-prolog"
|
||||||
license = "BSD-3-Clause"
|
license = "BSD-3-Clause"
|
||||||
edition = "2018"
|
keywords = ["prolog", "prolog-interpreter", "prolog-system"]
|
||||||
|
categories = ["command-line-utilities"]
|
||||||
|
build = "build/main.rs"
|
||||||
|
rust-version = "1.61"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["rug"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
indexmap = "1.0.2"
|
indexmap = "1.0.2"
|
||||||
|
proc-macro2 = "1.0.36"
|
||||||
[features]
|
quote = "1.0.15"
|
||||||
default = ["rug", "prolog_parser/rug"]
|
strum = "0.23"
|
||||||
num = ["num-rug-adapter", "prolog_parser/num"]
|
strum_macros = "0.23"
|
||||||
|
syn = { version = "1.0.88", features = ['full', 'visit', 'extra-traits'] }
|
||||||
|
to-syn-value = "0.1.0"
|
||||||
|
to-syn-value_derive = "0.1.0"
|
||||||
|
walkdir = "2"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossterm = "0.16.0"
|
cpu-time = "1.0.0"
|
||||||
dirs = "2.0.2"
|
crossterm = "0.20.0"
|
||||||
downcast = "0.10.0"
|
dirs-next = "2.0.0"
|
||||||
|
divrem = "0.1.0"
|
||||||
|
fxhash = "0.2.1"
|
||||||
git-version = "0.3.4"
|
git-version = "0.3.4"
|
||||||
|
hostname = "0.3.1"
|
||||||
indexmap = "1.0.2"
|
indexmap = "1.0.2"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
lexical = "5.2.2"
|
||||||
libc = "0.2.62"
|
libc = "0.2.62"
|
||||||
nix = "0.15.0"
|
modular-bitfield = "0.11.2"
|
||||||
num-rug-adapter = { optional = true, version = "0.1.1" }
|
ctrlc = "3.2.2"
|
||||||
ordered-float = "0.5.0"
|
ordered-float = "2.6.0"
|
||||||
prolog_parser = { version = "0.8.49", default-features = false }
|
phf = { version = "0.9", features = ["macros"] }
|
||||||
ref_thread_local = "0.0.0"
|
ref_thread_local = "0.0.0"
|
||||||
rug = { version = "1.4.0", optional = true }
|
rug = { version = "1.15.0", optional = true }
|
||||||
rustyline = "6.0.0"
|
rustyline = "9.0.0"
|
||||||
|
ring = "0.16.13"
|
||||||
|
ripemd160 = "0.8.0"
|
||||||
|
sha3 = "0.8.2"
|
||||||
|
blake2 = "0.8.1"
|
||||||
|
crrl ="0.2.0"
|
||||||
|
native-tls = "0.2.4"
|
||||||
|
chrono = "0.4.11"
|
||||||
|
select = "0.4.3"
|
||||||
|
roxmltree = "0.11.0"
|
||||||
|
base64 = "0.12.3"
|
||||||
|
smallvec = "1.8.0"
|
||||||
|
sodiumoxide = "0.2.6"
|
||||||
|
static_assertions = "1.1.0"
|
||||||
|
ryu = "1.0.9"
|
||||||
|
hyper = { version = "0.14", features = ["full"] }
|
||||||
|
hyper-tls = "0.5.0"
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
futures = "0.3"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
assert_cmd = "1.0.3"
|
||||||
|
predicates-core = "1.0.2"
|
||||||
|
serial_test = "0.5.1"
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = true
|
||||||
|
|||||||
26
Dockerfile
Executable file
26
Dockerfile
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
# See https://github.com/LukeMathWalker/cargo-chef
|
||||||
|
ARG RUST_VERSION=1.60-buster
|
||||||
|
FROM rust:${RUST_VERSION} as planner
|
||||||
|
WORKDIR /scryer-prolog
|
||||||
|
RUN cargo install cargo-chef
|
||||||
|
COPY . .
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
|
FROM rust:${RUST_VERSION} as cacher
|
||||||
|
WORKDIR /scryer-prolog
|
||||||
|
RUN cargo install cargo-chef
|
||||||
|
COPY --from=planner /scryer-prolog/recipe.json recipe.json
|
||||||
|
RUN cargo chef cook --release --recipe-path recipe.json
|
||||||
|
|
||||||
|
FROM rust:${RUST_VERSION} as builder
|
||||||
|
WORKDIR /scryer-prolog
|
||||||
|
COPY . .
|
||||||
|
# Copy over the cached dependencies
|
||||||
|
COPY --from=cacher /scryer-prolog/target target
|
||||||
|
COPY --from=cacher $CARGO_HOME $CARGO_HOME
|
||||||
|
RUN cargo build --release --bin scryer-prolog
|
||||||
|
|
||||||
|
FROM debian:stable-slim
|
||||||
|
COPY --from=builder /scryer-prolog/target/release/scryer-prolog /usr/local/bin
|
||||||
|
ENV RUST_BACKTRACE=1
|
||||||
|
ENTRYPOINT ["/usr/local/bin/scryer-prolog"]
|
||||||
512
README.md
512
README.md
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
# Scryer Prolog
|
# Scryer Prolog
|
||||||
|
|
||||||
Scryer Prolog aims to become to ISO Prolog what GHC is to Haskell: an open
|
Scryer Prolog aims to become to ISO Prolog what GHC is to Haskell: an open
|
||||||
@@ -5,6 +6,8 @@ source industrial strength production environment that is also a
|
|||||||
testbed for bleeding edge research in logic and constraint
|
testbed for bleeding edge research in logic and constraint
|
||||||
programming, which is itself written in a high-level language.
|
programming, which is itself written in a high-level language.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Phase 1
|
## Phase 1
|
||||||
|
|
||||||
Produce an implementation of the Warren Abstract Machine in Rust, done
|
Produce an implementation of the Warren Abstract Machine in Rust, done
|
||||||
@@ -40,7 +43,7 @@ Extend Scryer Prolog to include the following, among other features:
|
|||||||
- [x] Support for `verify_attributes/3`
|
- [x] Support for `verify_attributes/3`
|
||||||
- [x] Support for `attribute_goals/2` and `project_attributes/2`
|
- [x] Support for `attribute_goals/2` and `project_attributes/2`
|
||||||
- [x] `call_residue_vars/2`
|
- [x] `call_residue_vars/2`
|
||||||
- [x] `if_` and related predicates, following the developments of the
|
- [x] `if_/3` and related predicates, following the developments of the
|
||||||
paper "Indexing `dif/2`".
|
paper "Indexing `dif/2`".
|
||||||
- [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`).
|
- [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`).
|
||||||
- [x] Clause creation and destruction (`asserta/1`, `assertz/1`,
|
- [x] Clause creation and destruction (`asserta/1`, `assertz/1`,
|
||||||
@@ -55,9 +58,18 @@ Extend Scryer Prolog to include the following, among other features:
|
|||||||
- [x] A _redone_ representation of strings as difference lists of
|
- [x] A _redone_ representation of strings as difference lists of
|
||||||
characters, using a packed internal representation.
|
characters, using a packed internal representation.
|
||||||
- [x] clp(B) and clp(ℤ) as builtin libraries.
|
- [x] clp(B) and clp(ℤ) as builtin libraries.
|
||||||
- [ ] Streams and predicates for stream control (_in progress_).
|
- [x] Streams and predicates for stream control.
|
||||||
- [ ] A compacting garbage collector satisfying the five
|
- [x] A simple sockets library representing TCP connections as streams.
|
||||||
properties of "Precise Garbage Collection in Prolog."
|
- [x] Incremental compilation and loading process, newly written,
|
||||||
|
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.
|
- [ ] Mode declarations.
|
||||||
|
|
||||||
## Phase 3
|
## Phase 3
|
||||||
@@ -90,6 +102,8 @@ strings.
|
|||||||
|
|
||||||
## Installing Scryer Prolog
|
## Installing Scryer Prolog
|
||||||
|
|
||||||
|
### Native Install
|
||||||
|
|
||||||
First, install the latest stable version of
|
First, install the latest stable version of
|
||||||
[Rust](https://www.rust-lang.org/en-US/install.html) using your
|
[Rust](https://www.rust-lang.org/en-US/install.html) using your
|
||||||
preferred method. Scryer tends to use features from newer Rust
|
preferred method. Scryer tends to use features from newer Rust
|
||||||
@@ -99,20 +113,8 @@ Rust updated to the latest stable release; any existing Rust
|
|||||||
distribution should be uninstalled from your system before rustup is
|
distribution should be uninstalled from your system before rustup is
|
||||||
used.
|
used.
|
||||||
|
|
||||||
Scryer Prolog can be installed with cargo, like so:
|
Currently the only way to install the latest version of Scryer is to
|
||||||
|
clone directly from this git repository, which can be done as follows:
|
||||||
```
|
|
||||||
$> cargo install scryer-prolog
|
|
||||||
```
|
|
||||||
|
|
||||||
cargo will download and install the libraries Scryer Prolog uses
|
|
||||||
automatically from crates.io. You can find the `scryer-prolog`
|
|
||||||
executable in `~/.cargo/bin`.
|
|
||||||
|
|
||||||
Publishing Rust crates to crates.io and pushing to git are entirely
|
|
||||||
distinct, independent processes, so to be sure you have the latest
|
|
||||||
commit, it is recommended to clone directly from this git repository,
|
|
||||||
which can be done as follows:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$> git clone https://github.com/mthom/scryer-prolog
|
$> git clone https://github.com/mthom/scryer-prolog
|
||||||
@@ -123,62 +125,105 @@ $> cargo run [--release]
|
|||||||
The optional `--release` flag will perform various optimizations,
|
The optional `--release` flag will perform various optimizations,
|
||||||
producing a faster executable.
|
producing a faster executable.
|
||||||
|
|
||||||
|
On Windows, Scryer Prolog is easier to build inside a [MSYS2](https://www.msys2.org/)
|
||||||
|
environment as some crates may require native C compilation. However,
|
||||||
|
the resulting binary does not need MSYS2 to run. When executing Scryer in a shell, it is recommended to use a more advanced shell than mintty (the default MSYS2 shell). The [Windows Terminal](https://github.com/microsoft/terminal) works correctly.
|
||||||
|
|
||||||
|
To build a Windows Installer, you'll need first Scryer Prolog compiled in release mode, then, with WiX Toolset installed, execute:
|
||||||
|
```
|
||||||
|
candle.exe scryer-prolog.wxs
|
||||||
|
light.exe scryer-prolog.wixobj
|
||||||
|
```
|
||||||
|
It will generate a very basic MSI file which installs the main executable and a shortcut in the Start Menu. It can be installed with a double-click. To uninstall, go to the Control Panel and uninstall as usual.
|
||||||
|
|
||||||
|
Scryer Prolog must be built with **Rust 1.57 and up**.
|
||||||
|
|
||||||
|
### Docker Install
|
||||||
|
|
||||||
|
First, install [Docker](https://docs.docker.com/get-docker/) on Linux,
|
||||||
|
Windows, or Mac.
|
||||||
|
|
||||||
|
Once Docker is installed, you can download and run Scryer Prolog with a single
|
||||||
|
command:
|
||||||
|
```
|
||||||
|
$> docker run -it mjt128/scryer-prolog
|
||||||
|
```
|
||||||
|
|
||||||
|
To consult your Prolog files, bind mount your programs folder as a
|
||||||
|
[Docker volume](https://docs.docker.com/storage/volumes/):
|
||||||
|
|
||||||
|
```
|
||||||
|
$> docker run -v /home/user/prolog:/mnt -it mjt128/scryer-prolog
|
||||||
|
?- consult('/mnt/program.pl').
|
||||||
|
true.
|
||||||
|
```
|
||||||
|
|
||||||
|
This works on Windows too:
|
||||||
|
|
||||||
|
```
|
||||||
|
$> docker run -v C:\Users\user\Documents\prolog:/mnt -it mjt128/scryer-prolog
|
||||||
|
?- consult('/mnt/program.pl').
|
||||||
|
true.
|
||||||
|
```
|
||||||
|
|
||||||
## Tutorial
|
## Tutorial
|
||||||
To enter a multi-clause predicate, the directive "[user]" is used.
|
|
||||||
|
|
||||||
For example,
|
Prolog files are loaded by specifying them as arguments on the command
|
||||||
```
|
line. For example, to load `program.pl`, use:
|
||||||
?- [user].
|
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
|
||||||
p(f(f(X)), h(W), Y) :- g(W), h(W), f(X).
|
|
||||||
p(X, Y, Z) :- h(Y), z(Z).
|
|
||||||
?- [user].
|
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
|
||||||
h(x). h(y).
|
|
||||||
h(z).
|
|
||||||
```
|
|
||||||
In the example, `Enter + Ctrl-D` is used to terminate the standard
|
|
||||||
input stream. The instructive message is always printed.
|
|
||||||
|
|
||||||
Queries are issued as
|
|
||||||
```
|
```
|
||||||
?- p(X, Y, Z).
|
$> scryer-prolog program.pl
|
||||||
```
|
```
|
||||||
|
|
||||||
Pressing `SPACE` will backtrack through other possible answers, if any exist.
|
Loading a Prolog file is also called “consulting” it. The built-in
|
||||||
Pressing `.` will abort the search and return to the prompt.
|
predicate `consult/1` can be used to consult a file from within
|
||||||
|
Prolog:
|
||||||
|
|
||||||
Wildcards work as well:
|
```
|
||||||
|
?- consult('program.pl').
|
||||||
|
```
|
||||||
|
|
||||||
|
As an abbreviation for `consult/1`, you can specify a *list* of
|
||||||
|
program files, given as *atoms*:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- ['program.pl'].
|
||||||
|
```
|
||||||
|
|
||||||
|
The special notation `[user]` is used to read Prolog text from
|
||||||
|
standard input. For example,
|
||||||
|
|
||||||
```
|
```
|
||||||
?- [user].
|
?- [user].
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
hello(declarative_world).
|
||||||
member(X, [X|_]).
|
hello(pure_world).
|
||||||
member(X, [_|Xs]) :- member(X, Xs).
|
|
||||||
?- member(X, [a, b, c]).
|
|
||||||
X = a
|
|
||||||
; X = b
|
|
||||||
; X = c
|
|
||||||
; false.
|
|
||||||
```
|
|
||||||
and so do conjunctive queries:
|
|
||||||
```
|
|
||||||
?- [user].
|
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
|
||||||
f(X) :- g(X).
|
|
||||||
g(x). g(y). g(z).
|
|
||||||
h(call(f, X)).
|
|
||||||
?- h(X), X.
|
|
||||||
X = call(f,x)
|
|
||||||
; X = call(f,y)
|
|
||||||
; X = call(f,z).
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the values of variables belonging to successful queries are
|
Pressing `RETURN` followed by `Ctrl-d` stops reading from
|
||||||
printed out, on one line each. Uninstantiated variables are denoted by
|
standard input and consults the entered Prolog text.
|
||||||
a number preceded by an underscore (`X = _0` in an example above).
|
|
||||||
|
After a program is consulted, you can ask *queries* about the
|
||||||
|
predicates it defines. For example, with the program shown above:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- hello(What).
|
||||||
|
What = declarative_world
|
||||||
|
; What = pure_world.
|
||||||
|
```
|
||||||
|
|
||||||
|
Press `SPACE` to show further answers, if any exist. Press `RETURN`
|
||||||
|
or `.` to abort the search and return to the
|
||||||
|
toplevel prompt. Press `f` to see up to the next multiple of
|
||||||
|
5 answers, and `a` to see all answers. Press `h` to show a help
|
||||||
|
message.
|
||||||
|
|
||||||
|
Use `TAB` to complete atoms and predicate names in queries. For
|
||||||
|
instance, after consulting the program above, typing `decl` followed
|
||||||
|
by `TAB` yields `declarative_world`. Press `TAB` repeatedly
|
||||||
|
to cycle through alternative completions.
|
||||||
|
|
||||||
|
To quit Scryer Prolog, use the standard predicate `halt/0`:
|
||||||
|
|
||||||
To quit scryer-prolog, type
|
|
||||||
```
|
```
|
||||||
?- halt.
|
?- halt.
|
||||||
```
|
```
|
||||||
@@ -190,17 +235,68 @@ arithmetic operators with the usual precedences,
|
|||||||
|
|
||||||
```
|
```
|
||||||
?- write_canonical(-5 + 3 - (2 * 4) // 8), nl.
|
?- write_canonical(-5 + 3 - (2 * 4) // 8), nl.
|
||||||
-(+(-5,3),//(*(2,4),8))
|
-(+(-5,3),//(*(2,4),8))
|
||||||
true.
|
true.
|
||||||
```
|
```
|
||||||
|
|
||||||
New operators can be defined using the `op` declaration.
|
New operators can be defined using the `op` declaration.
|
||||||
|
|
||||||
|
### First instantiated argument indexing
|
||||||
|
|
||||||
|
Scryer Prolog indexes on the leftmost argument that is not a variable
|
||||||
|
in all clauses of a predicate's definition. We call this strategy
|
||||||
|
first *instantiated* argument indexing.
|
||||||
|
|
||||||
|
A key motivation for first instantiated argument indexing is to enable
|
||||||
|
indexing for meta-predicates such as `maplist/N` and `foldl/N`, whose
|
||||||
|
first argument is a partial goal that is a variable in the definition
|
||||||
|
of these predicates and therefore cannot be used for indexing.
|
||||||
|
|
||||||
|
For example, a natural definition of `maplist/2` reads:
|
||||||
|
|
||||||
|
```
|
||||||
|
maplist(_, []).
|
||||||
|
maplist(Goal_1, [L|Ls]) :-
|
||||||
|
call(Goal_1, L),
|
||||||
|
maplist(Goal_1, Ls).
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, first instantiated argument indexing automatically uses
|
||||||
|
the *second* argument for indexing, and thus prevents choicepoints for
|
||||||
|
calls with lists of fixed lengths (and deterministic goals).
|
||||||
|
Conveniently, no auxiliary predicates with reordered arguments are
|
||||||
|
needed to benefit from indexing in such cases.
|
||||||
|
|
||||||
|
Conventional first argument indexing naturally arises as a
|
||||||
|
special case of this strategy, if the first argument is instantiated
|
||||||
|
in any clause of a predicate's definition.
|
||||||
|
|
||||||
### Strings and partial strings
|
### Strings and partial strings
|
||||||
|
|
||||||
|
A very compact internal representation of *strings* is one of the key
|
||||||
|
innovations of Scryer Prolog. This means that terms which appear as
|
||||||
|
lists of characters to Prolog programs are stored in packed
|
||||||
|
UTF-8 encoding by the engine.
|
||||||
|
|
||||||
|
Without this innovation, storing a list of characters in memory
|
||||||
|
would use one memory cell per character, one memory cell per
|
||||||
|
list constructor, and one memory cell for each tail that occurs
|
||||||
|
in the list. Since one memory cell takes 8 bytes on 64-bit
|
||||||
|
machines, the packed representation used by Scryer Prolog yields
|
||||||
|
an up to **24-fold reduction** of memory usage, and
|
||||||
|
corresponding reduction of memory accesses when creating and
|
||||||
|
processing strings.
|
||||||
|
|
||||||
|
Scryer Prolog's compact internal string representation makes it
|
||||||
|
ideally suited for the use case Prolog was originally developed for:
|
||||||
|
efficient and convenient text processing, especially with definite
|
||||||
|
clause grammars (DCGs) as provided by
|
||||||
|
[`library(dcgs)`](src/lib/dcgs.pl) and
|
||||||
|
[`library(pio)`](src/lib/pio.pl) to transparently apply DCGs to files.
|
||||||
|
|
||||||
In Scryer Prolog, the default value of the Prolog flag `double_quotes`
|
In Scryer Prolog, the default value of the Prolog flag `double_quotes`
|
||||||
is `chars`, which is also the recommended setting. This means that
|
is `chars`, which is also the recommended setting. This means that
|
||||||
double-quoted strings are interpreted as lists of *characters*, in the
|
lists of characters can be written as double-quoted strings, in the
|
||||||
tradition of Marseille Prolog.
|
tradition of Marseille Prolog.
|
||||||
|
|
||||||
For example, the following query succeeds:
|
For example, the following query succeeds:
|
||||||
@@ -210,15 +306,9 @@ For example, the following query succeeds:
|
|||||||
true.
|
true.
|
||||||
```
|
```
|
||||||
|
|
||||||
Internally, strings are represented very compactly in packed
|
This shows that the string `"abc"`, which is represented as a sequence
|
||||||
UTF-8 encoding. A naive representation of strings as lists of
|
of 3 bytes internally, appears to Prolog programs as a list of
|
||||||
characters would use one memory cell per character, one
|
characters.
|
||||||
memory cell per list constructor, and one memory cell for
|
|
||||||
each tail that occurs in the list. Since one memory cell takes
|
|
||||||
8 bytes on 64-bit machines, the packed representation used by
|
|
||||||
Scryer Prolog yields an up to **24-fold reduction** of
|
|
||||||
memory usage, and corresponding reduction of memory accesses when
|
|
||||||
creating and processing strings.
|
|
||||||
|
|
||||||
Scryer Prolog uses the same efficient encoding for *partial* strings,
|
Scryer Prolog uses the same efficient encoding for *partial* strings,
|
||||||
which appear to Prolog code as partial lists of characters. The
|
which appear to Prolog code as partial lists of characters. The
|
||||||
@@ -241,15 +331,120 @@ the above example, posting <tt>Ls0 = [a,b,c|Ls]</tt> yields
|
|||||||
the exact same internal representation, and has the advantage that
|
the exact same internal representation, and has the advantage that
|
||||||
only the standard predicate `(=)/2` is used.
|
only the standard predicate `(=)/2` is used.
|
||||||
|
|
||||||
Definite clause grammars as provided by `library(dcgs)` are ideally
|
The efficient internal representation of strings and partial strings
|
||||||
suited for reasoning about strings.
|
was first proposed and explained by Ulrich Neumerkel in
|
||||||
|
issues [#24](https://github.com/mthom/scryer-prolog/issues/24)
|
||||||
|
and [#95](https://github.com/mthom/scryer-prolog/issues/95), and
|
||||||
|
Scryer Prolog is the first Prolog system that implements it.
|
||||||
|
|
||||||
|
### Occurs check and cyclic terms
|
||||||
|
|
||||||
|
The *occurs check* is an element of algorithms that perform
|
||||||
|
syntactic unification, causing the unification to fail if a variable
|
||||||
|
is unified with a term that contains that variable as a proper
|
||||||
|
subterm. For efficiency, the *occurs check* is omitted by default
|
||||||
|
in Scryer Prolog and many other Prolog systems.
|
||||||
|
|
||||||
|
In Scryer Prolog, 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)
|
||||||
|
|
||||||
|
One of the foremost attractions of Prolog is that logical consequences
|
||||||
|
of pure programs can be derived by various execution strategies
|
||||||
|
that differ regarding essential properties such as termination,
|
||||||
|
completeness and efficiency.
|
||||||
|
|
||||||
|
The default execution strategy of Prolog is depth-first search with
|
||||||
|
chronological backtracking. This strategy is very efficient. Its main
|
||||||
|
drawback is that it is *incomplete*: It may fail to find any solution
|
||||||
|
even if one exists.
|
||||||
|
|
||||||
|
Scryer Prolog supports an alternative execution strategy which is
|
||||||
|
called *tabling* and also known as tabled execution and
|
||||||
|
SLG resolution. To enable tabled execution for a predicate, use
|
||||||
|
[`library(tabling)`](src/lib/tabling.pl) and add a `(table)/1`
|
||||||
|
directive for the desired predicate indicator. For example, if we
|
||||||
|
write:
|
||||||
|
|
||||||
|
```
|
||||||
|
:- use_module(library(tabling)).
|
||||||
|
:- table a/0.
|
||||||
|
|
||||||
|
a :- a.
|
||||||
|
```
|
||||||
|
|
||||||
|
Then the query `?- a.` *terminates* (and fails), whereas it
|
||||||
|
does not terminate with the default execution strategy.
|
||||||
|
|
||||||
|
Scryer Prolog implements tabling via *delimited continuations* as
|
||||||
|
described in [*Tabling as a Library with Delimited
|
||||||
|
Control*](https://biblio.ugent.be/publication/6880648/file/6885145.pdf)
|
||||||
|
by Desouter et. al.
|
||||||
|
|
||||||
|
### Constraint Logic Programming (CLP)
|
||||||
|
|
||||||
|
Scryer Prolog provides excellent support for Constraint Logic
|
||||||
|
Programming (CLP), which is the amalgamation of
|
||||||
|
Logic Programming (LP) and Constraints.
|
||||||
|
|
||||||
|
In addition to built-in support for [`dif/2`](src/lib/dif.pl),
|
||||||
|
[`freeze/2`](src/lib/freeze.pl),
|
||||||
|
[CLP(B)](src/lib/clpb.pl) and [CLP(ℤ)](src/lib/clpz.pl),
|
||||||
|
Scryer provides a convenient way to implement new user-defined
|
||||||
|
constraints: *Attributed variables* are available via
|
||||||
|
[`library(atts)`](src/lib/atts.pl) as in SICStus Prolog,
|
||||||
|
which is one of the most sophisticated and fastest constraint systems
|
||||||
|
in existence. In [`library(iso_ext)`](src/lib/iso_ext.pl),
|
||||||
|
Scryer provides predicates for backtrackable (`bb_b_put/2`) and
|
||||||
|
non-backtrackable (`bb_put/2`) global variables, which are needed to
|
||||||
|
implement certain types of constraint solvers.
|
||||||
|
|
||||||
|
These features make Scryer Prolog an ideal platform for teaching,
|
||||||
|
learning and developing portable CLP applications.
|
||||||
|
|
||||||
### Modules
|
### Modules
|
||||||
|
|
||||||
Scryer has a simple predicate-based module system. It provides a
|
Scryer has a simple predicate-based module system. It provides a
|
||||||
way to separate units of code into distinct namespaces, for both
|
way to separate units of code into distinct namespaces, for both
|
||||||
predicates and operators. See the files
|
predicates and operators. See the files
|
||||||
[`src/prolog/lib/*.pl`](src/prolog/lib) for
|
[`src/lib/*.pl`](src/lib) for
|
||||||
examples.
|
examples.
|
||||||
|
|
||||||
At the time of this writing, many predicates reside in their own
|
At the time of this writing, many predicates reside in their own
|
||||||
@@ -257,31 +452,31 @@ modules that need to be imported before they can be used.
|
|||||||
The modules that ship with Scryer Prolog are also called
|
The modules that ship with Scryer Prolog are also called
|
||||||
*library* modules or *libraries*, and include:
|
*library* modules or *libraries*, and include:
|
||||||
|
|
||||||
* [`lists`](src/prolog/lib/lists.pl)
|
* [`lists`](src/lib/lists.pl)
|
||||||
providing `length/2`, `member/2`, `select/3`, `append/[2,3]`,
|
providing `length/2`, `member/2`, `select/3`, `append/[2,3]`,
|
||||||
`foldl/[4,5]`, `maplist/[2-9]`, `same_length/2`, `transpose/2` etc.
|
`foldl/[4,5]`, `maplist/[2-9]`, `same_length/2`, `transpose/2` etc.
|
||||||
* [`dcgs`](src/prolog/lib/dcgs.pl)
|
* [`dcgs`](src/lib/dcgs.pl)
|
||||||
Definite Clause Grammars (DCGs), a built-in grammar mechanism
|
Definite Clause Grammars (DCGs), a built-in grammar mechanism
|
||||||
that uses the operator `(-->)/2` to define grammar rules,
|
that uses the operator `(-->)/2` to define grammar rules,
|
||||||
and the predicates `phrase/[2,3]` to invoke them.
|
and the predicates `phrase/[2,3]` to invoke them.
|
||||||
* [`dif`](src/prolog/lib/dif.pl)
|
* [`dif`](src/lib/dif.pl)
|
||||||
The predicate `dif/2` provides declarative disequality:
|
The predicate `dif/2` provides declarative disequality:
|
||||||
It is true if and only if its arguments are different, and
|
It is true if and only if its arguments are different, and
|
||||||
delays the test until a sound decision can be made.
|
delays the test until a sound decision can be made.
|
||||||
* [`reif`](src/prolog/lib/reif.pl)
|
* [`reif`](src/lib/reif.pl)
|
||||||
providing `if_/3`, `tfilter/3` and related predicates
|
providing `if_/3`, `tfilter/3` and related predicates
|
||||||
as described in *Indexing dif/2*.
|
as described in *Indexing dif/2*.
|
||||||
* [`clpz`](src/prolog/lib/clpz.pl)
|
* [`clpz`](src/lib/clpz.pl)
|
||||||
CLP(ℤ): Constraint Logic Programming over Integers,
|
CLP(ℤ): Constraint Logic Programming over Integers,
|
||||||
providing declarative integer arithmetic via `(#=)/2`, `(#\=)/2`,
|
providing declarative integer arithmetic via `(#=)/2`, `(#\=)/2`,
|
||||||
`(#>=)/2` etc., and various global constraints and
|
`(#>=)/2` etc., and various global constraints and
|
||||||
enumeration predicates for solving combinatorial tasks.
|
enumeration predicates for solving combinatorial tasks.
|
||||||
* [`pairs`](src/prolog/lib/pairs.pl)
|
* [`pairs`](src/lib/pairs.pl)
|
||||||
By convention, *pairs* are Prolog terms with
|
By convention, *pairs* are Prolog terms with
|
||||||
principal functor `(-)/2`, written as `Key-Value`.
|
principal functor `(-)/2`, written as `Key-Value`.
|
||||||
This library provides `pairs_keys_values/3`,
|
This library provides `pairs_keys_values/3`,
|
||||||
`pairs_keys/2`, and other predicates to reason about pairs.
|
`pairs_keys/2`, and other predicates to reason about pairs.
|
||||||
* [`si`](src/prolog/lib/si.pl)
|
* [`si`](src/lib/si.pl)
|
||||||
The predicates `atom_si/1`, `integer_si/1`, `atomic_si/1`
|
The predicates `atom_si/1`, `integer_si/1`, `atomic_si/1`
|
||||||
and `list_si/1` implement sound type checks. They raise
|
and `list_si/1` implement sound type checks. They raise
|
||||||
instantiation errors if no decision can be made.
|
instantiation errors if no decision can be made.
|
||||||
@@ -290,25 +485,105 @@ The modules that ship with Scryer Prolog are also called
|
|||||||
write `integer_si(X)` to ensure soundness of your programs.
|
write `integer_si(X)` to ensure soundness of your programs.
|
||||||
"si" stands for *sufficiently instantiated*, and also for
|
"si" stands for *sufficiently instantiated*, and also for
|
||||||
*sound inference*.
|
*sound inference*.
|
||||||
* [`error`](src/prolog/lib/error.pl)
|
* [`debug`](src/lib/debug.pl)
|
||||||
`must_be/2` and `can_be/2` complement the type checks provided
|
Various predicates that allow for declarative debugging.
|
||||||
by `library(si)`, and are especially useful for Prolog library
|
* [`pio`](src/lib/pio.pl)
|
||||||
authors.
|
`phrase_from_file/2` applies a DCG nonterminal to the contents of a
|
||||||
* [`tabling`](src/prolog/lib/tabling.pl)
|
file, reading lazily only as much as is needed. Due to the compact
|
||||||
|
internal string representation, also extremely large files can be
|
||||||
|
efficiently processed with Scryer Prolog in this way.
|
||||||
|
`phrase_to_file/2` and `phrase_to_stream/2` write lists of
|
||||||
|
characters described by DCGs to files and streams, respectively.
|
||||||
|
* [`lambda`](src/lib/lambda.pl)
|
||||||
|
Lambda expressions to simplify higher order programming.
|
||||||
|
* [`charsio`](src/lib/charsio.pl) Various predicates that are useful
|
||||||
|
for parsing and reasoning about characters, notably `char_type/2` to
|
||||||
|
classify characters according to their type, and conversion
|
||||||
|
predicates for different encodings of strings.
|
||||||
|
* [`error`](src/lib/error.pl)
|
||||||
|
`must_be/2` and `can_be/2` complement the type checks provided by
|
||||||
|
[`library(si)`](src/lib/si.pl), and are especially useful for
|
||||||
|
Prolog library authors.
|
||||||
|
* [`tabling`](src/lib/tabling.pl)
|
||||||
The operator `(table)/1` is used in directives that prepare
|
The operator `(table)/1` is used in directives that prepare
|
||||||
predicates for tabled execution (SLG resolution).
|
predicates for tabled execution (SLG resolution).
|
||||||
* [`format`](src/prolog/lib/format.pl)
|
* [`format`](src/lib/format.pl)
|
||||||
The nonterminal `format_//2` is used to describe formatted output,
|
The nonterminal `format_//2` is used to describe formatted output,
|
||||||
arranging arguments according to a given format string.
|
arranging arguments according to a given format string.
|
||||||
The predicate `format/2` is provided for impure output.
|
The predicates `format/[2,3]`, `portray_clause/[1,2]` and `listing/1`
|
||||||
* [`assoc`](src/prolog/lib/assoc.pl)
|
provide formatted *impure* output.
|
||||||
|
* [`assoc`](src/lib/assoc.pl)
|
||||||
providing `empty_assoc/1`, `get_assoc/3`, `put_assoc/4` etc.
|
providing `empty_assoc/1`, `get_assoc/3`, `put_assoc/4` etc.
|
||||||
to manage elements in AVL trees which ensure
|
to manage elements in AVL trees which ensure
|
||||||
*O*(log(*N*)) access.
|
*O*(log(*N*)) access.
|
||||||
* [`clpb`](src/prolog/lib/clpb.pl)
|
* [`ordsets`](src/lib/ordsets.pl)
|
||||||
|
represents ordered sets as lists.
|
||||||
|
* [`clpb`](src/lib/clpb.pl)
|
||||||
CLP(B): Constraint Logic Programming over Boolean variables,
|
CLP(B): Constraint Logic Programming over Boolean variables,
|
||||||
a BDD-based SAT solver provided via the predicates
|
a BDD-based SAT solver provided via the predicates
|
||||||
`sat/1`, `taut/2`, `labeling/1` etc.
|
`sat/1`, `taut/2`, `labeling/1` etc.
|
||||||
|
* [`arithmetic`](src/lib/arithmetic.pl)
|
||||||
|
Arithmetic predicates such as `lsb/2`, `msb/2` and
|
||||||
|
`number_to_rational/2`.
|
||||||
|
* [`time`](src/lib/time.pl) Predicates for reasoning about
|
||||||
|
time, including `time/1` to measure the CPU time of a goal,
|
||||||
|
`current_time/1` to obtain the current system time, the nonterminal
|
||||||
|
`format_time//2` to describe strings with dates and times, and
|
||||||
|
`sleep/1` to slow down a computation.
|
||||||
|
* [`files`](src/lib/files.pl)
|
||||||
|
Predicates for reasoning about files and directories, such as
|
||||||
|
`directory_files/2`, `file_exists/1` and `file_size/2`.
|
||||||
|
* [`cont`](src/lib/cont.pl)
|
||||||
|
Provides *delimited continuations* via `reset/3` and `shift/1`.
|
||||||
|
* [`random`](src/lib/random.pl)
|
||||||
|
Probabilistic predicates and random number generators.
|
||||||
|
* [`http/http_open`](src/lib/http/http_open.pl) Open a stream to
|
||||||
|
read answers from web servers. HTTPS is also supported.
|
||||||
|
* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.1 and HTTP/2.0 web server. Uses [Hyper](https://hyper.rs) as a backend. Supports some query and form handling.
|
||||||
|
* [`sgml`](src/lib/sgml.pl)
|
||||||
|
`load_html/3` and `load_xml/3` represent HTML and XML documents
|
||||||
|
as Prolog terms for convenient and efficient reasoning. Use
|
||||||
|
[`library(xpath)`](src/lib/iso_ext.pl) to extract information from
|
||||||
|
parsed documents.
|
||||||
|
* [`csv`](src/lib/csv.pl)
|
||||||
|
`parse_csv//1` and `parse_csv//2` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
|
||||||
|
or [`phrase/2`](src/lib/dcgs.pl) to parse csv
|
||||||
|
* [`serialization/abnf`](src/lib/serialization/abnf.pl)
|
||||||
|
DCGs describing the
|
||||||
|
[ABNF grammar core (RFC 5234)](https://tools.ietf.org/html/rfc5234#appendix-B.1),
|
||||||
|
which is used to describe many [IETF](https://www.ietf.org/standards/rfcs/)
|
||||||
|
syntaxes, such as [HTTP v1.1](https://www.rfc-editor.org/rfc/rfc7230.html#page-82),
|
||||||
|
[SMTP](https://www.rfc-editor.org/rfc/rfc5321.html),
|
||||||
|
[iCalendar](https://www.rfc-editor.org/rfc/rfc5545.html), and more.
|
||||||
|
* [`serialization/json`](src/lib/serialization/json.pl)
|
||||||
|
`json_chars//1` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
|
||||||
|
or [`phrase/2`](src/lib/dcgs.pl) to parse and generate
|
||||||
|
[JSON](https://www.json.org/json-en.html).
|
||||||
|
* [`xpath`](src/lib/xpath.pl)
|
||||||
|
The predicate `xpath/3` is used for convenient reasoning about HTML
|
||||||
|
and XML documents, inspired by the XPath language. This library
|
||||||
|
is often used together with [`library(sgml)`](src/lib/sgml.pl).
|
||||||
|
* [`sockets`](src/lib/sockets.pl)
|
||||||
|
Predicates for opening and accepting TCP connections as streams.
|
||||||
|
* [`os`](src/lib/os.pl)
|
||||||
|
Predicates for reasoning about environment variables.
|
||||||
|
* [`iso_ext`](src/lib/iso_ext.pl)
|
||||||
|
Conforming extensions to and candidates for inclusion in the Prolog
|
||||||
|
ISO standard, such as `setup_call_cleanup/3`, `call_nth/2` and
|
||||||
|
`call_with_inference_limit/3`.
|
||||||
|
* [`crypto`](src/lib/crypto.pl)
|
||||||
|
Cryptographically secure random numbers and hashes, HMAC-based key
|
||||||
|
derivation (HKDF), password-based key derivation (PBKDF2),
|
||||||
|
public key signatures and signature verification with Ed25519,
|
||||||
|
ECDH key exchange over Curve25519 (X25519), authenticated symmetric
|
||||||
|
encryption with ChaCha20-Poly1305, and reasoning about elliptic curves.
|
||||||
|
* [`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
|
||||||
|
* [`simplex`](src/lib/simplex.pl) Providing `assignment/2`,
|
||||||
|
`transportation/4` and other predicates for solving linear
|
||||||
|
programming problems.
|
||||||
|
|
||||||
To use predicates provided by the `lists` library, write:
|
To use predicates provided by the `lists` library, write:
|
||||||
|
|
||||||
@@ -345,7 +620,6 @@ REPL:
|
|||||||
|
|
||||||
```
|
```
|
||||||
?- [user].
|
?- [user].
|
||||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
|
||||||
:- module(test, [local_member/2]).
|
:- module(test, [local_member/2]).
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
@@ -354,3 +628,55 @@ local_member(X, Xs) :- member(X, Xs).
|
|||||||
|
|
||||||
The user listing can also be terminated by placing `end_of_file.` at
|
The user listing can also be terminated by placing `end_of_file.` at
|
||||||
the end of the stream.
|
the end of the stream.
|
||||||
|
|
||||||
|
### Configuration file
|
||||||
|
|
||||||
|
At startup, Scryer Prolog consults the file `~/.scryerrc`, if the file
|
||||||
|
exists. This file is useful to automatically load libraries and define
|
||||||
|
predicates that you need often.
|
||||||
|
|
||||||
|
For example, a sensible starting point for `~/.scryerrc` is:
|
||||||
|
|
||||||
|
```
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(reif)).
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development environment
|
||||||
|
|
||||||
|
To write and edit Prolog programs, we recommend
|
||||||
|
[GNU Emacs](https://www.gnu.org/software/emacs/) with the
|
||||||
|
[Prolog mode](https://bruda.ca/emacs/prolog_mode_for_emacs)
|
||||||
|
maintained by Stefan Bruda.
|
||||||
|
|
||||||
|
Use [ediprolog](https://www.metalevel.at/ediprolog/) to consult
|
||||||
|
Prolog code and evaluate Prolog queries in arbitrary
|
||||||
|
Emacs buffers.
|
||||||
|
|
||||||
|
Emacs definitions that show Prolog terms as trees are available
|
||||||
|
in [tools](tools).
|
||||||
|
|
||||||
|
To *debug* Prolog code, we recommend the predicates from
|
||||||
|
[**`library(debug)`**](src/lib/debug.pl), most notably:
|
||||||
|
|
||||||
|
- `(*)/1` to *"generalize away"* a Prolog goal. Use it to debug
|
||||||
|
unexpected failures by generalizing your definitions until they
|
||||||
|
succeed. Simply place `*` in front of a goal to generalize it away.
|
||||||
|
- `($)/1` to emit a *trace* of the execution, showing when a goal
|
||||||
|
is invoked, and when it has succeeded. Place `$` in front of a goal
|
||||||
|
to emit this information for that goal.
|
||||||
|
|
||||||
|
This way of debugging Prolog code has several major benefits, such as:
|
||||||
|
It stays close to the actual Prolog code under consideration, it does
|
||||||
|
not need additional tools and formalisms for its application, and
|
||||||
|
further, it encourages declarative reasoning that can in principle
|
||||||
|
also be performed automatically.
|
||||||
|
|
||||||
|
## 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)!
|
||||||
|
|||||||
60
build.rs
60
build.rs
@@ -1,60 +0,0 @@
|
|||||||
extern crate indexmap;
|
|
||||||
|
|
||||||
use indexmap::IndexSet;
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::fs::{File, copy, read_dir};
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
fn main()
|
|
||||||
{
|
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
|
||||||
let dest_path = Path::new(&out_dir).join("libraries.rs");
|
|
||||||
|
|
||||||
let mut libraries = File::create(&dest_path).unwrap();
|
|
||||||
let mut library_index = IndexSet::new();
|
|
||||||
|
|
||||||
let paths = read_dir("./src/prolog/lib").unwrap();
|
|
||||||
|
|
||||||
for item in paths {
|
|
||||||
let item = item.unwrap().path();
|
|
||||||
|
|
||||||
if let Some(file_name) = item.file_name() {
|
|
||||||
if let Some(ext) = item.extension() {
|
|
||||||
if ext == "pl" {
|
|
||||||
let file_stem = item.file_stem().unwrap();
|
|
||||||
let file_str = file_stem.to_string_lossy().to_uppercase();
|
|
||||||
let dest = Path::new(&out_dir).join(file_name);
|
|
||||||
|
|
||||||
match copy(&item, dest) {
|
|
||||||
Ok(_) => {},
|
|
||||||
Err(e) => panic!("die: {:?}", e)
|
|
||||||
};
|
|
||||||
|
|
||||||
let include_line = format!("static {}: &str = include_str!(\"{}.pl\");\n",
|
|
||||||
file_str, file_stem.to_string_lossy());
|
|
||||||
|
|
||||||
libraries.write_all(include_line.as_bytes()).unwrap();
|
|
||||||
library_index.insert(file_stem.to_string_lossy().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
libraries.write_all(b"\nref_thread_local! {
|
|
||||||
pub static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
|
|
||||||
let mut m = IndexMap::new();\n").unwrap();
|
|
||||||
|
|
||||||
for item in library_index {
|
|
||||||
let line = format!("\n m.insert(\"{}\", {});", item, item.to_uppercase());
|
|
||||||
libraries.write_all(line.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
libraries.write_all(b"\n\n m\n };
|
|
||||||
}\n").unwrap();
|
|
||||||
|
|
||||||
libraries.write_all(b"\npub static PROJECT_DIR: &'static str = \"").unwrap();
|
|
||||||
libraries.write_all(env::var("CARGO_MANIFEST_DIR").unwrap().as_bytes()).unwrap();
|
|
||||||
libraries.write_all(b"\";\n").unwrap();
|
|
||||||
}
|
|
||||||
3396
build/instructions_template.rs
Normal file
3396
build/instructions_template.rs
Normal file
File diff suppressed because it is too large
Load Diff
114
build/main.rs
Normal file
114
build/main.rs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
mod instructions_template;
|
||||||
|
mod static_string_indexing;
|
||||||
|
|
||||||
|
use instructions_template::generate_instructions_rs;
|
||||||
|
use static_string_indexing::index_static_strings;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
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 has_rustfmt = Command::new("rustfmt")
|
||||||
|
.arg("--version")
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.status()
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
if !has_rustfmt {
|
||||||
|
println!("Failed to run rustfmt, will skip formatting generated files.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let dest_path = Path::new(&out_dir).join("libraries.rs");
|
||||||
|
|
||||||
|
let mut libraries = File::create(&dest_path).unwrap();
|
||||||
|
let lib_path = Path::new("src/lib");
|
||||||
|
|
||||||
|
libraries
|
||||||
|
.write_all(
|
||||||
|
b"ref_thread_local::ref_thread_local! {
|
||||||
|
pub(crate) static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
|
||||||
|
let mut m = IndexMap::new();\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
find_prolog_files(&mut libraries, "", &lib_path);
|
||||||
|
libraries.write_all(b"\n m\n };\n}\n").unwrap();
|
||||||
|
|
||||||
|
let instructions_path = Path::new(&out_dir).join("instructions.rs");
|
||||||
|
let mut instructions_file = File::create(&instructions_path).unwrap();
|
||||||
|
|
||||||
|
let quoted_output = generate_instructions_rs();
|
||||||
|
|
||||||
|
instructions_file
|
||||||
|
.write_all(quoted_output.to_string().as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if has_rustfmt {
|
||||||
|
format_generated_file(instructions_path.as_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
let static_atoms_path = Path::new(&out_dir).join("static_atoms.rs");
|
||||||
|
let mut static_atoms_file = File::create(&static_atoms_path).unwrap();
|
||||||
|
|
||||||
|
let quoted_output = index_static_strings(&instructions_path);
|
||||||
|
|
||||||
|
static_atoms_file
|
||||||
|
.write_all(quoted_output.to_string().as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if has_rustfmt {
|
||||||
|
format_generated_file(static_atoms_path.as_path());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=src/");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_generated_file(path: &Path) {
|
||||||
|
Command::new("rustfmt")
|
||||||
|
.arg(path.as_os_str())
|
||||||
|
.spawn()
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"{}: rustfmt was detected as available, but failed to format generated file '{}'",
|
||||||
|
err,
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
177
build/static_string_indexing.rs
Normal file
177
build/static_string_indexing.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") || path.is_ident("match_untyped_arena_ptr") {
|
||||||
|
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 } };)*
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static STATIC_ATOMS_MAP: phf::Map<&'static str, Atom> = phf::phf_map! {
|
||||||
|
#(#static_strs => { Atom { index: #indices } },)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
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 |
31
scryer-prolog.wxs
Normal file
31
scryer-prolog.wxs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Product Name="Scryer Prolog" Manufacturer="Scryer Prolog contributors" Id="*" UpgradeCode="cfb2dee4-5dd5-4d7d-b426-cd7340810559" Language="1033" Codepage="1252" Version="0.9.0">
|
||||||
|
<Package Description="An open source industrial strength production environment for ISO Prolog that is also a testbed for bleeding edge research in logic and constraint programming, which is itself written in a high-level language." Platform="x64" Keywords="prolog" Id="*" Compressed="yes" InstallScope="perMachine" InstallerVersion="300" Languages="1033" SummaryCodepage="1252" Manufacturer="Scryer Prolog contributors"/>
|
||||||
|
<Property Id="APPHELPLINK" Value="https://github.com/mthom/scryer-prolog"/>
|
||||||
|
<Media Id="1" Cabinet="scryer.cab" EmbedCab="yes" />
|
||||||
|
|
||||||
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
|
<Directory Id="ProgramFilesFolder" Name="PFiles">
|
||||||
|
<Directory Id="INSTALLDIR" Name="Scryer Prolog">
|
||||||
|
<Component Id="MainExecutable" Guid="1b41ceda-ba18-47f9-911b-ee41b4f20921">
|
||||||
|
<File Id="ScryerPrologEXE" Name="scryer-prolog.exe" DiskId="1" Source="target/release/scryer-prolog.exe" KeyPath="yes" Checksum="yes"/>
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="ProgramMenuFolder">
|
||||||
|
<Component Id="ApplicationShortcut" Guid="8c9b14a3-e7b1-4d30-a892-61d7371dcae2">
|
||||||
|
<Shortcut Id="ApplicationStarMenuShortcut" Name="Scryer Prolog" Description="Launch Scryer Prolog" Target="[#ScryerPrologEXE]" WorkingDirectory="INSTALLDIR"/>
|
||||||
|
<RemoveFolder Id="ApplicationShortcut" On="uninstall"/>
|
||||||
|
<RegistryValue Root="HKCU" Key="Software\Microsoft\ScryerProlog" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Feature Id="Complete" Level="1" Display="expand" ConfigurableDirectory="INSTALLDIR">
|
||||||
|
<ComponentRef Id="MainExecutable"/>
|
||||||
|
<ComponentRef Id="ApplicationShortcut"/>
|
||||||
|
</Feature>
|
||||||
|
</Product>
|
||||||
|
</Wix>
|
||||||
|
|
||||||
@@ -1,41 +1,57 @@
|
|||||||
use prolog_parser::ast::*;
|
use crate::parser::ast::*;
|
||||||
|
use crate::temp_v;
|
||||||
|
|
||||||
use crate::prolog::fixtures::*;
|
use crate::fixtures::*;
|
||||||
use crate::prolog::forms::*;
|
use crate::forms::*;
|
||||||
use crate::prolog::machine::machine_indices::*;
|
use crate::instructions::*;
|
||||||
use crate::prolog::targets::*;
|
use crate::machine::machine_indices::*;
|
||||||
|
use crate::targets::*;
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub trait Allocator<'a> {
|
pub(crate) trait Allocator {
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
|
|
||||||
fn mark_anon_var<Target>(&mut self, _: Level, _: GenContext, _: &mut Vec<Target>)
|
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
|
||||||
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>(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
_: Rc<Var>,
|
lvl: Level,
|
||||||
_: Level,
|
context: GenContext,
|
||||||
_: &'a Cell<VarReg>,
|
code: &mut Code,
|
||||||
_: GenContext,
|
);
|
||||||
_: &mut Vec<Target>,
|
|
||||||
_: RegType,
|
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
|
||||||
_: bool,
|
&mut self,
|
||||||
) where
|
lvl: Level,
|
||||||
Target: CompilationTarget<'a>;
|
context: GenContext,
|
||||||
fn mark_var<Target>(&mut self, _: Rc<Var>, _: Level, _: &'a Cell<VarReg>, _: GenContext, _: &mut Vec<Target>)
|
cell: &'a Cell<RegType>,
|
||||||
where
|
code: &mut Code,
|
||||||
Target: CompilationTarget<'a>;
|
);
|
||||||
|
|
||||||
|
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(&mut self);
|
||||||
fn reset_contents(&mut self) {}
|
fn reset_contents(&mut self) {}
|
||||||
fn reset_arg(&mut self, _: usize);
|
fn reset_arg(&mut self, arg_num: usize);
|
||||||
fn reset_at_head(&mut self, _: &Vec<Box<Term>>);
|
fn reset_at_head(&mut self, args: &Vec<Term>);
|
||||||
|
|
||||||
fn advance_arg(&mut self);
|
fn advance_arg(&mut self);
|
||||||
|
|
||||||
@@ -43,11 +59,12 @@ pub trait Allocator<'a> {
|
|||||||
fn bindings_mut(&mut self) -> &mut AllocVarDict;
|
fn bindings_mut(&mut self) -> &mut AllocVarDict;
|
||||||
|
|
||||||
fn take_bindings(self) -> AllocVarDict;
|
fn take_bindings(self) -> AllocVarDict;
|
||||||
|
fn max_reg_allocated(&self) -> usize;
|
||||||
|
|
||||||
fn drain_var_data(
|
fn drain_var_data<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
vs: VariableFixtures<'a>,
|
vs: VariableFixtures<'a>,
|
||||||
num_of_chunks: usize
|
num_of_chunks: usize,
|
||||||
) -> VariableFixtures<'a> {
|
) -> VariableFixtures<'a> {
|
||||||
let mut perm_vs = VariableFixtures::new();
|
let mut perm_vs = VariableFixtures::new();
|
||||||
|
|
||||||
@@ -71,17 +88,17 @@ pub trait Allocator<'a> {
|
|||||||
perm_vs
|
perm_vs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, var: Rc<Var>) -> RegType {
|
fn get(&self, var: Rc<String>) -> RegType {
|
||||||
self.bindings()
|
self.bindings()
|
||||||
.get(&var)
|
.get(&var)
|
||||||
.map_or(temp_v!(0), |v| v.as_reg_type())
|
.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
|
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() {
|
match self.bindings_mut().get_mut(&var).unwrap() {
|
||||||
&mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(),
|
&mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(),
|
||||||
&mut VarData::Perm(ref mut s) => *s = r.reg_num(),
|
&mut VarData::Perm(ref mut s) => *s = r.reg_num(),
|
||||||
1100
src/arena.rs
Normal file
1100
src/arena.rs
Normal file
File diff suppressed because it is too large
Load Diff
724
src/arithmetic.rs
Normal file
724
src/arithmetic.rs
Normal file
@@ -0,0 +1,724 @@
|
|||||||
|
use crate::allocator::*;
|
||||||
|
use crate::arena::*;
|
||||||
|
use crate::atom_table::*;
|
||||||
|
use crate::debray_allocator::*;
|
||||||
|
use crate::forms::*;
|
||||||
|
use crate::instructions::*;
|
||||||
|
use crate::iterators::*;
|
||||||
|
use crate::targets::QueryInstruction;
|
||||||
|
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 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) => {
|
||||||
|
TermIterState::Clause(Level::Shallow, 0, cell, *name, terms)
|
||||||
|
}
|
||||||
|
/* match ClauseType::from(*name, terms.len()) {
|
||||||
|
ct @ ClauseType::Named(..) => {
|
||||||
|
Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms))
|
||||||
|
}
|
||||||
|
ct @ 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(..) | Term::CompleteString(..) => {
|
||||||
|
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, name, subterms) => {
|
||||||
|
let arity = subterms.len();
|
||||||
|
|
||||||
|
if child_num == arity {
|
||||||
|
return Some(Ok(ArithTermRef::Op(name, arity)));
|
||||||
|
} else {
|
||||||
|
self.state_stack.push(TermIterState::Clause(
|
||||||
|
lvl,
|
||||||
|
child_num + 1,
|
||||||
|
cell,
|
||||||
|
name,
|
||||||
|
subterms,
|
||||||
|
));
|
||||||
|
|
||||||
|
self.push_subterm(lvl.child_level(), &subterms[child_num]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))),
|
||||||
|
TermIterState::Var(lvl, cell, var) => {
|
||||||
|
return Some(Ok(ArithTermRef::Var(lvl, cell, var.clone())));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Some(Err(ArithmeticError::NonEvaluableFunctor(
|
||||||
|
Literal::Atom(atom!(".")),
|
||||||
|
2,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ArithmeticEvaluator<'a> {
|
||||||
|
marker: &'a mut DebrayAllocator,
|
||||||
|
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.as_ptr()))),
|
||||||
|
Literal::Rational(n) => interm.push(ArithmeticTerm::Number(Number::Rational(*n))),
|
||||||
|
Literal::Atom(name) if name == &atom!("e") => interm.push(ArithmeticTerm::Number(
|
||||||
|
Number::Float(OrderedFloat(std::f64::consts::E))
|
||||||
|
)),
|
||||||
|
Literal::Atom(name) if name == &atom!("pi") => interm.push(ArithmeticTerm::Number(
|
||||||
|
Number::Float(OrderedFloat(std::f64::consts::PI))
|
||||||
|
)),
|
||||||
|
Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
|
||||||
|
Number::Float(OrderedFloat(std::f64::EPSILON))
|
||||||
|
)),
|
||||||
|
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArithmeticEvaluator<'a> {
|
||||||
|
pub(crate) fn new(marker: &'a mut DebrayAllocator, 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 compile_is(
|
||||||
|
&mut self,
|
||||||
|
src: &'a Term,
|
||||||
|
term_loc: GenContext,
|
||||||
|
arg: usize,
|
||||||
|
) -> 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 lvl == Level::Shallow {
|
||||||
|
self.marker.mark_non_callable(
|
||||||
|
name.clone(),
|
||||||
|
arg,
|
||||||
|
term_loc,
|
||||||
|
cell,
|
||||||
|
&mut code,
|
||||||
|
)
|
||||||
|
} else if term_loc.is_last() || cell.get().norm().reg_num() == 0 {
|
||||||
|
self.marker.mark_var::<QueryInstruction>(
|
||||||
|
name.clone(),
|
||||||
|
lvl,
|
||||||
|
cell,
|
||||||
|
term_loc,
|
||||||
|
&mut code,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.marker.get_binding(&name).unwrap()
|
||||||
|
} 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(i) => {
|
||||||
|
if let Some(n) = i.to_i64() {
|
||||||
|
fixnum!(Number, n, arena)
|
||||||
|
} else {
|
||||||
|
*n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&Number::Fixnum(_) => *n,
|
||||||
|
&Number::Float(f) => {
|
||||||
|
let f = f.floor();
|
||||||
|
|
||||||
|
const I64_MIN_TO_F: OrderedFloat<f64> = OrderedFloat(i64::MIN as f64);
|
||||||
|
const I64_MAX_TO_F: OrderedFloat<f64> = OrderedFloat(i64::MAX as f64);
|
||||||
|
|
||||||
|
if I64_MIN_TO_F <= f && f <= I64_MAX_TO_F {
|
||||||
|
fixnum!(Number, f.into_inner() as i64, arena)
|
||||||
|
} else {
|
||||||
|
Number::Integer(arena_alloc!(Integer::from_f64(f.into_inner()).unwrap(), 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);
|
||||||
|
|
||||||
|
if let Some(floor) = floor.to_i64() {
|
||||||
|
fixnum!(Number, floor, arena)
|
||||||
|
} else {
|
||||||
|
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(n: &Number) -> Result<f64, EvalError> {
|
||||||
|
classify_float(rnd_f(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn classify_float(f: f64) -> Result<f64, EvalError> {
|
||||||
|
match f.classify() {
|
||||||
|
FpCategory::Normal | FpCategory::Zero => Ok(f),
|
||||||
|
FpCategory::Infinite => {
|
||||||
|
if OrderedFloat(f) == OrderedFloat(f64::MAX) {
|
||||||
|
Ok(f)
|
||||||
|
} else {
|
||||||
|
Err(EvalError::FloatOverflow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FpCategory::Nan => Err(EvalError::Undefined),
|
||||||
|
_ => Ok(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn float_fn_to_f(n: i64) -> Result<f64, EvalError> {
|
||||||
|
classify_float(n as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn float_i_to_f(n: &Integer) -> Result<f64, EvalError> {
|
||||||
|
classify_float(n.to_f64())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn float_r_to_f(r: &Rational) -> Result<f64, EvalError> {
|
||||||
|
classify_float(r.to_f64())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn add_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||||
|
Ok(OrderedFloat(classify_float(f1 + f2)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn mul_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||||
|
Ok(OrderedFloat(classify_float(f1 * f2)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn div_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||||
|
if FpCategory::Zero == f2.classify() {
|
||||||
|
Err(EvalError::ZeroDivisor)
|
||||||
|
} else {
|
||||||
|
Ok(OrderedFloat(classify_float(f1 / f2)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Number> for Number {
|
||||||
|
type Output = Result<Number, EvalError>;
|
||||||
|
|
||||||
|
fn div(self, rhs: Number) -> Self::Output {
|
||||||
|
match (self, rhs) {
|
||||||
|
(Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_fn_to_f(n1.get_num())?,
|
||||||
|
float_fn_to_f(n2.get_num())?,
|
||||||
|
)?)),
|
||||||
|
(Number::Fixnum(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_fn_to_f(n1.get_num())?,
|
||||||
|
float_i_to_f(&n2)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_i_to_f(&n1)?,
|
||||||
|
float_fn_to_f(n2.get_num())?,
|
||||||
|
)?)),
|
||||||
|
(Number::Fixnum(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_fn_to_f(n1.get_num())?,
|
||||||
|
float_r_to_f(&n2)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Rational(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_r_to_f(&n1)?,
|
||||||
|
float_fn_to_f(n2.get_num())?,
|
||||||
|
)?)),
|
||||||
|
(Number::Fixnum(n1), Number::Float(OrderedFloat(n2))) => {
|
||||||
|
Ok(Number::Float(div_f(float_fn_to_f(n1.get_num())?, n2)?))
|
||||||
|
}
|
||||||
|
(Number::Float(OrderedFloat(n1)), Number::Fixnum(n2)) => {
|
||||||
|
Ok(Number::Float(div_f(n1, float_fn_to_f(n2.get_num())?)?))
|
||||||
|
}
|
||||||
|
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_i_to_f(&n1)?,
|
||||||
|
float_i_to_f(&n2)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Integer(n1), Number::Float(OrderedFloat(n2))) => {
|
||||||
|
Ok(Number::Float(div_f(float_i_to_f(&n1)?, n2)?))
|
||||||
|
}
|
||||||
|
(Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
|
||||||
|
Ok(Number::Float(div_f(n2, float_i_to_f(&n1)?)?))
|
||||||
|
}
|
||||||
|
(Number::Integer(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
|
||||||
|
float_i_to_f(&n1)?,
|
||||||
|
float_r_to_f(&n2)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Rational(n2), Number::Integer(n1)) => Ok(Number::Float(div_f(
|
||||||
|
float_r_to_f(&n2)?,
|
||||||
|
float_i_to_f(&n1)?,
|
||||||
|
)?)),
|
||||||
|
(Number::Rational(n1), Number::Float(OrderedFloat(n2))) => {
|
||||||
|
Ok(Number::Float(div_f(float_r_to_f(&n1)?, n2)?))
|
||||||
|
}
|
||||||
|
(Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
|
||||||
|
Ok(Number::Float(div_f(n2, float_r_to_f(&n1)?)?))
|
||||||
|
}
|
||||||
|
(Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
|
||||||
|
Ok(Number::Float(div_f(f1, f2)?))
|
||||||
|
}
|
||||||
|
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Float(div_f(
|
||||||
|
float_r_to_f(&r1)?,
|
||||||
|
float_r_to_f(&r2)?,
|
||||||
|
)?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Number {
|
||||||
|
fn eq(&self, rhs: &Self) -> bool {
|
||||||
|
match (self, rhs) {
|
||||||
|
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.eq(&n2),
|
||||||
|
(&Number::Fixnum(n1), &Number::Integer(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::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())
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/bin/scryer-prolog.rs
Normal file
11
src/bin/scryer-prolog.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fn main() {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
use scryer_prolog::*;
|
||||||
|
|
||||||
|
ctrlc::set_handler(move || {
|
||||||
|
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
let mut wam = machine::Machine::new();
|
||||||
|
wam.run_top_level();
|
||||||
|
}
|
||||||
1302
src/codegen.rs
Normal file
1302
src/codegen.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,35 +1,40 @@
|
|||||||
use indexmap::IndexMap;
|
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::temp_v;
|
||||||
use crate::prolog::fixtures::*;
|
|
||||||
use crate::prolog::forms::*;
|
use fxhash::FxBuildHasher;
|
||||||
use crate::prolog::machine::machine_indices::*;
|
|
||||||
use crate::prolog::targets::*;
|
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub struct DebrayAllocator {
|
#[derive(Debug)]
|
||||||
bindings: IndexMap<Rc<Var>, VarData>,
|
pub(crate) struct DebrayAllocator {
|
||||||
|
bindings: IndexMap<Rc<String>, VarData, FxBuildHasher>,
|
||||||
arg_c: usize,
|
arg_c: usize,
|
||||||
temp_lb: usize,
|
temp_lb: usize,
|
||||||
arity: usize, // 0 if not at head.
|
arity: usize, // 0 if not at head.
|
||||||
contents: IndexMap<usize, Rc<Var>>,
|
contents: IndexMap<usize, Rc<String>, FxBuildHasher>,
|
||||||
in_use: BTreeSet<usize>,
|
in_use: BTreeSet<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebrayAllocator {
|
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) {
|
match self.contents.get(&self.arg_c) {
|
||||||
Some(t_var) if **t_var != *var => true,
|
Some(t_var) if **t_var != *var => true,
|
||||||
_ => false,
|
_ => 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() {
|
match self.bindings.get(var).unwrap() {
|
||||||
&VarData::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)),
|
&VarData::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)),
|
||||||
_ => false,
|
_ => false,
|
||||||
@@ -42,7 +47,7 @@ impl DebrayAllocator {
|
|||||||
in_use_range || self.in_use.contains(&r)
|
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) {
|
match self.bindings.get(var) {
|
||||||
Some(&VarData::Temp(_, _, ref tvd)) => {
|
Some(&VarData::Temp(_, _, ref tvd)) => {
|
||||||
for &(_, reg) in tvd.use_set.iter() {
|
for &(_, reg) in tvd.use_set.iter() {
|
||||||
@@ -68,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) {
|
match self.bindings.get(var) {
|
||||||
Some(&VarData::Temp(_, _, ref tvd)) => {
|
Some(&VarData::Temp(_, _, ref tvd)) => {
|
||||||
for &(_, reg) in tvd.use_set.iter() {
|
for &(_, reg) in tvd.use_set.iter() {
|
||||||
@@ -96,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.
|
// we want to allocate a register to the k^{th} parameter, par_k.
|
||||||
// par_k may not be a temporary variable.
|
// par_k may not be a temporary variable.
|
||||||
let k = self.arg_c;
|
let k = self.arg_c;
|
||||||
@@ -121,10 +126,11 @@ impl DebrayAllocator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evacuate_arg<'a, Target>(&mut self, chunk_num: usize, target: &mut Vec<Target>)
|
fn evacuate_arg<'a, Target: CompilationTarget<'a>>(
|
||||||
where
|
&mut self,
|
||||||
Target: CompilationTarget<'a>,
|
chunk_num: usize,
|
||||||
{
|
code: &mut Code,
|
||||||
|
) {
|
||||||
match self.alloc_in_last_goal_hint(chunk_num) {
|
match self.alloc_in_last_goal_hint(chunk_num) {
|
||||||
Some((var, r)) => {
|
Some((var, r)) => {
|
||||||
let k = self.arg_c;
|
let k = self.arg_c;
|
||||||
@@ -132,7 +138,7 @@ impl DebrayAllocator {
|
|||||||
if r != k {
|
if r != k {
|
||||||
let r = RegType::Temp(r);
|
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.swap_remove(&k);
|
||||||
self.contents.insert(r.reg_num(), var.clone());
|
self.contents.insert(r.reg_num(), var.clone());
|
||||||
@@ -145,20 +151,17 @@ impl DebrayAllocator {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_reg_to_var<'a, Target>(
|
fn alloc_reg_to_var<'a, Target: CompilationTarget<'a>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
var: &Var,
|
var: &String,
|
||||||
lvl: Level,
|
lvl: Level,
|
||||||
term_loc: GenContext,
|
term_loc: GenContext,
|
||||||
target: &mut Vec<Target>,
|
target: &mut Vec<Instruction>,
|
||||||
) -> usize
|
) -> usize {
|
||||||
where
|
|
||||||
Target: CompilationTarget<'a>,
|
|
||||||
{
|
|
||||||
match term_loc {
|
match term_loc {
|
||||||
GenContext::Head => {
|
GenContext::Head => {
|
||||||
if let Level::Shallow = lvl {
|
if let Level::Shallow = lvl {
|
||||||
self.evacuate_arg(0, target);
|
self.evacuate_arg::<Target>(0, target);
|
||||||
self.alloc_with_cr(var)
|
self.alloc_with_cr(var)
|
||||||
} else {
|
} else {
|
||||||
self.alloc_with_ca(var)
|
self.alloc_with_ca(var)
|
||||||
@@ -167,7 +170,7 @@ impl DebrayAllocator {
|
|||||||
GenContext::Mid(_) => self.alloc_with_ca(var),
|
GenContext::Mid(_) => self.alloc_with_ca(var),
|
||||||
GenContext::Last(chunk_num) => {
|
GenContext::Last(chunk_num) => {
|
||||||
if let Level::Shallow = lvl {
|
if let Level::Shallow = lvl {
|
||||||
self.evacuate_arg(chunk_num, target);
|
self.evacuate_arg::<Target>(chunk_num, target);
|
||||||
self.alloc_with_cr(var)
|
self.alloc_with_cr(var)
|
||||||
} else {
|
} else {
|
||||||
self.alloc_with_ca(var)
|
self.alloc_with_ca(var)
|
||||||
@@ -191,7 +194,7 @@ impl DebrayAllocator {
|
|||||||
final_index
|
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 {
|
match term_loc {
|
||||||
GenContext::Head if !r.is_perm() => r.reg_num() == k,
|
GenContext::Head if !r.is_perm() => r.reg_num() == k,
|
||||||
_ => match self.bindings().get(var).unwrap() {
|
_ => match self.bindings().get(var).unwrap() {
|
||||||
@@ -202,49 +205,49 @@ impl DebrayAllocator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Allocator<'a> for DebrayAllocator {
|
impl Allocator for DebrayAllocator {
|
||||||
fn new() -> DebrayAllocator {
|
fn new() -> DebrayAllocator {
|
||||||
DebrayAllocator {
|
DebrayAllocator {
|
||||||
arity: 0,
|
arity: 0,
|
||||||
arg_c: 1,
|
arg_c: 1,
|
||||||
temp_lb: 1,
|
temp_lb: 1,
|
||||||
bindings: IndexMap::new(),
|
bindings: IndexMap::with_hasher(FxBuildHasher::default()),
|
||||||
contents: IndexMap::new(),
|
contents: IndexMap::with_hasher(FxBuildHasher::default()),
|
||||||
in_use: BTreeSet::new(),
|
in_use: BTreeSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_anon_var<Target>(&mut self, lvl: Level, term_loc: GenContext, target: &mut Vec<Target>)
|
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
|
||||||
where
|
&mut self,
|
||||||
Target: CompilationTarget<'a>,
|
lvl: Level,
|
||||||
{
|
term_loc: GenContext,
|
||||||
|
code: &mut Code,
|
||||||
|
) {
|
||||||
let r = RegType::Temp(self.alloc_reg_to_non_var());
|
let r = RegType::Temp(self.alloc_reg_to_non_var());
|
||||||
|
|
||||||
match lvl {
|
match lvl {
|
||||||
Level::Deep => target.push(Target::subterm_to_variable(r)),
|
Level::Deep => code.push(Target::subterm_to_variable(r)),
|
||||||
Level::Root | Level::Shallow => {
|
Level::Root | Level::Shallow => {
|
||||||
let k = self.arg_c;
|
let k = self.arg_c;
|
||||||
|
|
||||||
if let GenContext::Last(chunk_num) = term_loc {
|
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;
|
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,
|
&mut self,
|
||||||
lvl: Level,
|
lvl: Level,
|
||||||
term_loc: GenContext,
|
term_loc: GenContext,
|
||||||
cell: &Cell<RegType>,
|
cell: &'a Cell<RegType>,
|
||||||
target: &mut Vec<Target>,
|
code: &mut Code,
|
||||||
) where
|
) {
|
||||||
Target: CompilationTarget<'a>,
|
|
||||||
{
|
|
||||||
let r = cell.get();
|
let r = cell.get();
|
||||||
|
|
||||||
let r = match lvl {
|
let r = match lvl {
|
||||||
@@ -252,7 +255,7 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
|||||||
let k = self.arg_c;
|
let k = self.arg_c;
|
||||||
|
|
||||||
if let GenContext::Last(chunk_num) = term_loc {
|
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;
|
self.arg_c += 1;
|
||||||
@@ -268,20 +271,18 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
|||||||
cell.set(r);
|
cell.set(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_var<Target>(
|
fn mark_var<'a, Target: CompilationTarget<'a>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
var: Rc<Var>,
|
var: Rc<String>,
|
||||||
lvl: Level,
|
lvl: Level,
|
||||||
cell: &'a Cell<VarReg>,
|
cell: &'a Cell<VarReg>,
|
||||||
term_loc: GenContext,
|
term_loc: GenContext,
|
||||||
target: &mut Vec<Target>,
|
code: &mut Code,
|
||||||
) where
|
) {
|
||||||
Target: CompilationTarget<'a>,
|
|
||||||
{
|
|
||||||
let (r, is_new_var) = match self.get(var.clone()) {
|
let (r, is_new_var) = match self.get(var.clone()) {
|
||||||
RegType::Temp(0) => {
|
RegType::Temp(0) => {
|
||||||
// here, r is temporary *and* unassigned.
|
// 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)));
|
cell.set(VarReg::Norm(RegType::Temp(o)));
|
||||||
|
|
||||||
(RegType::Temp(o), true)
|
(RegType::Temp(o), true)
|
||||||
@@ -295,27 +296,25 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
|||||||
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,
|
&mut self,
|
||||||
var: Rc<Var>,
|
var: Rc<String>,
|
||||||
lvl: Level,
|
lvl: Level,
|
||||||
cell: &'a Cell<VarReg>,
|
cell: &'a Cell<VarReg>,
|
||||||
term_loc: GenContext,
|
term_loc: GenContext,
|
||||||
target: &mut Vec<Target>,
|
code: &mut Code,
|
||||||
r: RegType,
|
r: RegType,
|
||||||
is_new_var: bool,
|
is_new_var: bool,
|
||||||
) where
|
) {
|
||||||
Target: CompilationTarget<'a>,
|
|
||||||
{
|
|
||||||
match lvl {
|
match lvl {
|
||||||
Level::Root | Level::Shallow => {
|
Level::Root | Level::Shallow => {
|
||||||
let k = self.arg_c;
|
let k = self.arg_c;
|
||||||
|
|
||||||
if self.is_curr_arg_distinct_from(&var) {
|
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;
|
self.arg_c += 1;
|
||||||
@@ -324,24 +323,24 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
|||||||
|
|
||||||
if !self.in_place(&var, term_loc, r, k) {
|
if !self.in_place(&var, term_loc, r, k) {
|
||||||
if is_new_var {
|
if is_new_var {
|
||||||
target.push(Target::argument_to_variable(r, k));
|
code.push(Target::argument_to_variable(r, k));
|
||||||
} else {
|
} else {
|
||||||
target.push(Target::argument_to_value(r, k));
|
code.push(Target::argument_to_value(r, k));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Level::Deep if is_new_var => {
|
Level::Deep if is_new_var => {
|
||||||
if let GenContext::Head = term_loc {
|
if let GenContext::Head = term_loc {
|
||||||
if self.occurs_shallowly_in_head(&var, r.reg_num()) {
|
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 {
|
} else {
|
||||||
target.push(Target::subterm_to_variable(r));
|
code.push(Target::subterm_to_variable(r));
|
||||||
}
|
}
|
||||||
} else {
|
} 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() {
|
if !r.is_perm() {
|
||||||
@@ -380,12 +379,12 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
|||||||
self.bindings
|
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.reset_arg(args.len());
|
||||||
self.arity = args.len();
|
self.arity = args.len();
|
||||||
|
|
||||||
for (idx, arg) in args.iter().enumerate() {
|
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());
|
let r = self.get(var.clone());
|
||||||
|
|
||||||
if !r.is_perm() && r.reg_num() == 0 {
|
if !r.is_perm() && r.reg_num() == 0 {
|
||||||
@@ -402,4 +401,9 @@ impl<'a> Allocator<'a> for DebrayAllocator {
|
|||||||
self.arg_c = 1;
|
self.arg_c = 1;
|
||||||
self.temp_lb = arity + 1;
|
self.temp_lb = arity + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn max_reg_allocated(&self) -> usize {
|
||||||
|
std::cmp::max(self.temp_lb, self.arg_c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
219
src/examples/bimetatrans/README.md
Normal file
219
src/examples/bimetatrans/README.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
### BiMetaTrans 1.0
|
||||||
|
|
||||||
|
BiMetaTrans(Prolog, RuleML) is a bidirectional translator capable of
|
||||||
|
parsing and generating a well-formed sublanguage of RuleML/XML to/from
|
||||||
|
a metalogical encoding of our own design called Prolog/'$V'. For later
|
||||||
|
ease of reference:
|
||||||
|
|
||||||
|
* Prolog/'$V' stands for "Metalogic Prolog with variable-as-'$V'-term encoding"
|
||||||
|
* RuleML/XML stands for "RuleML in XML with any kind of rendering"
|
||||||
|
* RuleML/xmL stands for "RuleML in minified XML"
|
||||||
|
* RuleML/Xml stands for "RuleML in indented XML"
|
||||||
|
|
||||||
|
"Minified XML" refers to XML with only necessary whitespace, between
|
||||||
|
element names and attributes, and pairs of attributes, with all
|
||||||
|
indentation stripped out. "Indented XML" contains indentation and
|
||||||
|
newlines, and is usually formatted for human readers.
|
||||||
|
|
||||||
|
The specification of Prolog/'$V', and of the NafHornlogEq sublanguage
|
||||||
|
of RuleML/XML targeted by BiMetaTrans, is contained in the preprint
|
||||||
|
[Invertible Bidirectional Metalogical Translation Between Prolog and
|
||||||
|
RuleML/XML for Knowledge Representation and
|
||||||
|
Querying](http://ruleml.org/papers/RuleMLXMLBiDirTransScryer.pdf) and
|
||||||
|
its accompanying talk
|
||||||
|
[slides](http://ruleml.org/talks/RuleMLXMLBiDirTransScryer-talk.pdf). The
|
||||||
|
preprint and talk also describe the implementation of BiMetaTrans
|
||||||
|
found here, and outlines a strategy for proving the invertibility of
|
||||||
|
BiMetaTrans.
|
||||||
|
|
||||||
|
BiMetaTrans exports a single public predicate subsuming its user
|
||||||
|
API, `parse_ruleml/3`. It has two modes, each corresponding to a single
|
||||||
|
direction of translation:
|
||||||
|
|
||||||
|
```
|
||||||
|
parse_ruleml(+AssertItems, +QueryItems, ?XML) (Prolog->RuleML)
|
||||||
|
parse_ruleml(?AssertItems, ?QueryItems, +XML) (RuleML->Prolog)
|
||||||
|
```
|
||||||
|
|
||||||
|
The modes constrain the inputs to fit one of two patterns, the
|
||||||
|
first where AssertItems and QueryItems are instantiated and XML is
|
||||||
|
possibly a variable, and conversely for the second. Instantiated
|
||||||
|
inputs are expected to be ground, meaning they should not contain
|
||||||
|
free variables.
|
||||||
|
|
||||||
|
We explore several examples of its use in Scryer Prolog.
|
||||||
|
|
||||||
|
Loading BiMetaTrans from the Scryer REPL:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- use_module('src/examples/bimetatrans/bimetatrans').
|
||||||
|
```
|
||||||
|
|
||||||
|
Using `write/1` to print the string to standard output, capturing the
|
||||||
|
`Prolog->RuleML` direction (`write/1` is used because it does not
|
||||||
|
print strings with escape characters, ie., `\"` for double quote):
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml([people('Alex',male),people('Alex',female),people('Siri',female)], [], XML),
|
||||||
|
write(XML).
|
||||||
|
"<Assert mapClosure="universal"><Atom><Rel>people</Rel><Ind>Alex</Ind><Data iso:type="symbol">male</Data></Atom><Atom><Rel>people</Rel><Ind>Alex</Ind><Data iso:type="symbol">female</Data></Atom><Atom><Rel>people</Rel><Ind>Siri</Ind><Data iso:type="symbol">female</Data></Atom></Assert>" XML = "<Assert mapClosure= ...".
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the XML input can contain extraneous whitespace and
|
||||||
|
indentation while generated XML never does (ie., the XML generated by
|
||||||
|
BiMetaTrans is always Ruleml/xmL). `\` is used to continue ISO Prolog
|
||||||
|
strings to the next line but is never stored to the string by the
|
||||||
|
Prolog reader.
|
||||||
|
|
||||||
|
Performing the inverse translation of the previous example
|
||||||
|
(`RuleML->Prolog`), in RuleML/Xml:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml(AssertItems, QueryItems,
|
||||||
|
"<Assert mapClosure=\"universal\">\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>people</Rel>\
|
||||||
|
<Ind>Alex</Ind>\
|
||||||
|
<Data iso:type=\"symbol\">male</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>people</Rel>\
|
||||||
|
<Ind>Alex</Ind>\
|
||||||
|
<Data iso:type=\"symbol\">female</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>people</Rel>\
|
||||||
|
<Ind>Siri</Ind>\
|
||||||
|
<Data iso:type=\"symbol\">female</Data>\
|
||||||
|
</Atom>\
|
||||||
|
</Assert>").
|
||||||
|
AssertItems = [people('Alex',male),people('Alex',female),people('Siri',female)], QueryItems = [].
|
||||||
|
```
|
||||||
|
|
||||||
|
Double quote characters within strings must be escaped as in
|
||||||
|
`\"`. Non-empty AssertItems and QueryItems lists generated to
|
||||||
|
RuleML/xmL simultaneously:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml([a(item), b(item), c(item)], [(?- p, q, r(1), s(-2.222342432), t("attached")), (?- u, v('$V'(q)))], XML),
|
||||||
|
write(XML).
|
||||||
|
"<Assert mapClosure="universal"><Atom><Rel>a</Rel><Data iso:type="symbol">item</Data></Atom><Atom><Rel>b</Rel><Data iso:type="symbol">item</Data></Atom><Atom><Rel>c</Rel><Data iso:type="symbol">item</Data></Atom></Assert><Query closure="existential"><And><Atom><Rel>p</Rel></Atom><Atom><Rel>q</Rel></Atom><Atom><Rel>r</Rel><Data iso:type="number">1</Data></Atom><Atom><Rel>s</Rel><Data iso:type="number">-2.222342432</Data></Atom><Atom><Rel>t</Rel><Data iso:type="string">"attached"</Data></Atom></And></Query><Query closure="existential"><And><Atom><Rel>u</Rel></Atom><Atom><Rel>v</Rel><Var>q</Var></Atom></And></Query>" XML = "<Assert mapClosure= ...".
|
||||||
|
```
|
||||||
|
|
||||||
|
To insert escape characters into the printed RuleML/xmL, we use
|
||||||
|
`writeq/1` in place of `write/1`:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml([a(item), b(item), c(item)], [(?- p, q, r(1), s(-2.222342432), t("attached")), (?- u, v('$V'(q)))], XML),
|
||||||
|
writeq(XML).
|
||||||
|
"<Assert mapClosure=\"universal\"><Atom><Rel>a</Rel><Data iso:type=\"symbol\">item</Data></Atom><Atom><Rel>b</Rel><Data iso:type=\"symbol\">item</Data></Atom><Atom><Rel>c</Rel><Data iso:type=\"symbol\">item</Data></Atom></Assert><Query closure=\"existential\"><And><Atom><Rel>p</Rel></Atom><Atom><Rel>q</Rel></Atom><Atom><Rel>r</Rel><Data iso:type=\"number\">1</Data></Atom><Atom><Rel>s</Rel><Data iso:type=\"number\">-2.222342432</Data></Atom><Atom><Rel>t</Rel><Data iso:type=\"string\">\"attached\"</Data></Atom></And></Query><Query closure=\"existential\"><And><Atom><Rel>u</Rel></Atom><Atom><Rel>v</Rel><Var>q</Var></Atom></And></Query>" XML = "<Assert mapClosure= ...".
|
||||||
|
```
|
||||||
|
|
||||||
|
XML can be pretty printed using a [free online XML pretty
|
||||||
|
printer](https://codebeautify.org/xmlviewer), as here:
|
||||||
|
|
||||||
|
```
|
||||||
|
<Assert mapClosure=\"universal\">
|
||||||
|
<Atom>
|
||||||
|
<Rel>a</Rel>
|
||||||
|
<Data iso:type=\"symbol\">item</Data>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>b</Rel>
|
||||||
|
<Data iso:type=\"symbol\">item</Data>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>c</Rel>
|
||||||
|
<Data iso:type=\"symbol\">item</Data>
|
||||||
|
</Atom>
|
||||||
|
</Assert>
|
||||||
|
<Query closure=\"existential\">
|
||||||
|
<And>
|
||||||
|
<Atom>
|
||||||
|
<Rel>p</Rel>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>q</Rel>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>r</Rel>
|
||||||
|
<Data iso:type=\"number\">1</Data>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>s</Rel>
|
||||||
|
<Data iso:type=\"number\">-2.222342432</Data>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>t</Rel>
|
||||||
|
<Data iso:type=\"string\">\"attached\"</Data>
|
||||||
|
</Atom>
|
||||||
|
</And>
|
||||||
|
</Query>
|
||||||
|
<Query closure=\"existential\">
|
||||||
|
<And>
|
||||||
|
<Atom>
|
||||||
|
<Rel>u</Rel>
|
||||||
|
</Atom>
|
||||||
|
<Atom>
|
||||||
|
<Rel>v</Rel>
|
||||||
|
<Var>q</Var>
|
||||||
|
</Atom>
|
||||||
|
</And>
|
||||||
|
</Query>
|
||||||
|
```
|
||||||
|
|
||||||
|
The AssertItems and QueryItems lists can then be recovered using this
|
||||||
|
RuleML/Xml string, with the `\` character added to the end of each
|
||||||
|
line:
|
||||||
|
|
||||||
|
```
|
||||||
|
?- parse_ruleml(AssertItems, QueryItems,
|
||||||
|
"<Assert mapClosure=\"universal\">\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>a</Rel>\
|
||||||
|
<Data iso:type=\"symbol\">item</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>b</Rel>\
|
||||||
|
<Data iso:type=\"symbol\">item</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>c</Rel>\
|
||||||
|
<Data iso:type=\"symbol\">item</Data>\
|
||||||
|
</Atom>\
|
||||||
|
</Assert>\
|
||||||
|
<Query closure=\"existential\">\
|
||||||
|
<And>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>p</Rel>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>q</Rel>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>r</Rel>\
|
||||||
|
<Data iso:type=\"number\">1</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>s</Rel>\
|
||||||
|
<Data iso:type=\"number\">-2.222342432</Data>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>t</Rel>\
|
||||||
|
<Data iso:type=\"string\">\"attached\"</Data>\
|
||||||
|
</Atom>\
|
||||||
|
</And>\
|
||||||
|
</Query>\
|
||||||
|
<Query closure=\"existential\">\
|
||||||
|
<And>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>u</Rel>\
|
||||||
|
</Atom>\
|
||||||
|
<Atom>\
|
||||||
|
<Rel>v</Rel>\
|
||||||
|
<Var>q</Var>\
|
||||||
|
</Atom>\
|
||||||
|
</And>\
|
||||||
|
</Query>").
|
||||||
|
AssertItems = [a(item),b(item),c(item)], QueryItems = [(?-p,q,r(1),s(-2.222342432),t("attached")),(?-u,v('$V'(q)))].
|
||||||
|
```
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
:- module(ruleml_xml_parser, [parse_ruleml/3]).
|
:- module(bimetatrans, [parse_ruleml/3]).
|
||||||
|
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(iso_ext)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
:- set_prolog_flag(double_quotes, chars).
|
:- set_prolog_flag(double_quotes, chars).
|
||||||
|
|
||||||
@@ -17,28 +19,34 @@
|
|||||||
* all indentation stripped out. "Indented XML" contains indentation
|
* all indentation stripped out. "Indented XML" contains indentation
|
||||||
* and newlines, and is usually formatted for human readers.
|
* and newlines, and is usually formatted for human readers.
|
||||||
*
|
*
|
||||||
* parse_ruleml/3 is the sole public predicate of BiMetaTrans(RuleML,
|
* parse_ruleml/3 is the sole public predicate of BiMetaTrans(Prolog,
|
||||||
* Prolog), a bidirectional translator capable of parsing well-formed
|
* RuleML), a bidirectional translator capable of parsing well-formed
|
||||||
* RuleML/XML (valid w.r.t. a proposed anchor schema nafhornlogeq
|
* RuleML/XML (valid w.r.t. a proposed anchor schema nafhornlogeq
|
||||||
* defining a sublanguage of the existing anchor schema naffologeq) to
|
* defining a sublanguage of the existing anchor schema naffologeq) to
|
||||||
* an equivalent Prolog/'$V' term and back to RuleML/xmL. It has the
|
* an equivalent Prolog/'$V' term and back to RuleML/xmL. It has the
|
||||||
* modes
|
* modes
|
||||||
*
|
*
|
||||||
* +AssertItem, +QueryItems, ?XML (Prolog->RuleML)
|
* +AssertItems, +QueryItems, ?XML (Prolog->RuleML)
|
||||||
* ?AssertItem, ?QueryItems, +XML (RuleML->Prolog)
|
* ?AssertItems, ?QueryItems, +XML (RuleML->Prolog)
|
||||||
*
|
*
|
||||||
* The modes constrain the inputs to fit one of two patterns, the
|
* The modes constrain the inputs to fit one of two patterns, the
|
||||||
* first where AssertItem and QueryItem are instantiated and XML is
|
* first where AssertItems and QueryItems are instantiated and XML is
|
||||||
* possibly a variable, and conversely for the second. Instantiated
|
* possibly a variable, and conversely for the second. Instantiated
|
||||||
* inputs are expected to be ground, meaning they should not contain
|
* inputs are expected to be ground, meaning they should not contain
|
||||||
* free variables.
|
* free variables.
|
||||||
*
|
*
|
||||||
|
* A RuleML/XML document is assumed to contain an optional Assert
|
||||||
|
* element (with zero or more children) and zero or more Query
|
||||||
|
* elements. The children of the Assert element are the Items of
|
||||||
|
* AssertItems, while the Items of QueryItems each correspond to a
|
||||||
|
* single Query element.
|
||||||
|
*
|
||||||
* parse_ruleml/3 is meant to subsume an invertible function in the
|
* parse_ruleml/3 is meant to subsume an invertible function in the
|
||||||
* sense that the top-level query
|
* sense that the top-level query
|
||||||
*
|
*
|
||||||
* ?- parse_ruleml(AssertItem, QueryItems, XML0), % Prolog->RuleML
|
* ?- parse_ruleml(AssertItems, QueryItems, XML0), % Prolog->RuleML
|
||||||
* parse_ruleml(AssertItem0, QueryItems0, XML), % RuleML->Prolog
|
* parse_ruleml(AssertItems0, QueryItems0, XML), % RuleML->Prolog
|
||||||
* AssertItem0 == AssertItem, % Test for expected round-trip results.
|
* AssertItems0 == AssertItems, % Test for expected round-trip results.
|
||||||
* QueryItems0 == QueryItems,
|
* QueryItems0 == QueryItems,
|
||||||
* XML0 == XML.
|
* XML0 == XML.
|
||||||
*
|
*
|
||||||
@@ -46,10 +54,11 @@
|
|||||||
* not 0-appended are instantiated and therefore ground.
|
* not 0-appended are instantiated and therefore ground.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
parse_ruleml(AssertItem, QueryItem, XML) :-
|
parse_ruleml(AssertItems, QueryItems, XML) :-
|
||||||
( ( var(AssertItem) ; var(QueryItem) ), var(XML) ->
|
( ( var(AssertItems) ; var(QueryItems) ), var(XML) ->
|
||||||
throw(error(instantiation_error, parse_ruleml/2))
|
throw(error(instantiation_error, parse_ruleml/2))
|
||||||
; phrase(ruleml_top_level_items(AssertItem, QueryItem), XML)
|
; phrase(ruleml_top_level_items(AssertItems, QueryItems), XML),
|
||||||
|
!
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
@@ -80,24 +89,15 @@ parse_header -->
|
|||||||
*
|
*
|
||||||
* The two arguments are (un)instantiated depending on the direction
|
* The two arguments are (un)instantiated depending on the direction
|
||||||
* in which parse_ruleml/2 operates. In either direction, once
|
* in which parse_ruleml/2 operates. In either direction, once
|
||||||
* ruleml_query_item(QueryItems) or ruleml_assert(AssertItem)
|
* ruleml_query_item(QueryItems) or ruleml_assert(AssertItems)
|
||||||
* succeeds:
|
* succeeds, AssertItems and QueryItems are ground lists of terms,
|
||||||
*
|
* corresponding to Assert/Query performative(s) of the RuleML/XML KB.
|
||||||
* 1) AssertItem/QueryItems is a fully ground list of terms,
|
|
||||||
* corresponding to an Assert/Query performative of the RuleML/XML KB.
|
|
||||||
*
|
|
||||||
* 2) It would not be meaningful to backtrack after the last success,
|
|
||||||
* thus undoing its effects; that is because we have already refuted
|
|
||||||
* the possibility of there being further items for the grammar to
|
|
||||||
* consume. Hence we see that the cut (!) does not restrict the
|
|
||||||
* generality of the grammar.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ruleml_top_level_items(AssertItem, QueryItems) -->
|
ruleml_top_level_items(AssertItems, QueryItems) -->
|
||||||
( ruleml_assert(AssertItem),
|
( ruleml_assert(AssertItems),
|
||||||
ruleml_query_items(QueryItems),
|
ruleml_query_items(QueryItems)
|
||||||
!
|
; { AssertItems = [] },
|
||||||
; { AssertItem = [] },
|
|
||||||
ruleml_query_items(QueryItems)
|
ruleml_query_items(QueryItems)
|
||||||
).
|
).
|
||||||
|
|
||||||
@@ -167,22 +167,16 @@ ruleml_top_level_items(AssertItem, QueryItems) -->
|
|||||||
* outset of ruleml_assert//1 will cause it to fail, meaning that
|
* outset of ruleml_assert//1 will cause it to fail, meaning that
|
||||||
* an empty <Assert></Assert> element is never emitted to the output
|
* an empty <Assert></Assert> element is never emitted to the output
|
||||||
* RuleML/xmL.
|
* RuleML/xmL.
|
||||||
*
|
|
||||||
* The two cuts are green cuts, meaning they do not change the meaning
|
|
||||||
* or output of the program, they simply prevent needless
|
|
||||||
* backtracking.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ruleml_assert(Items) -->
|
ruleml_assert(Items) -->
|
||||||
( { var(Items) } ->
|
( { var(Items) } ->
|
||||||
list_ws("<Assert mapClosure=\"universal\">"),
|
list_ws("<Assert mapClosure=\"universal\">"),
|
||||||
ruleml_assert_items(Items),
|
ruleml_assert_items(Items),
|
||||||
!,
|
|
||||||
list_ws("</Assert>")
|
list_ws("</Assert>")
|
||||||
; "<Assert mapClosure=\"universal\">",
|
; "<Assert mapClosure=\"universal\">",
|
||||||
{ Items \== [] },
|
{ Items \== [] },
|
||||||
ruleml_assert_items(Items),
|
ruleml_assert_items(Items),
|
||||||
!,
|
|
||||||
"</Assert>"
|
"</Assert>"
|
||||||
).
|
).
|
||||||
|
|
||||||
@@ -204,7 +198,6 @@ ruleml_assert(Items) -->
|
|||||||
|
|
||||||
ruleml_assert_items([Item | Items]) -->
|
ruleml_assert_items([Item | Items]) -->
|
||||||
ruleml_assert_item(Item),
|
ruleml_assert_item(Item),
|
||||||
!,
|
|
||||||
ruleml_assert_items(Items).
|
ruleml_assert_items(Items).
|
||||||
ruleml_assert_items([]) --> [].
|
ruleml_assert_items([]) --> [].
|
||||||
|
|
||||||
@@ -212,7 +205,6 @@ ruleml_assert_items([]) --> [].
|
|||||||
ruleml_query_items(Items) -->
|
ruleml_query_items(Items) -->
|
||||||
( { var(Items) } ->
|
( { var(Items) } ->
|
||||||
ruleml_query_item(Item),
|
ruleml_query_item(Item),
|
||||||
!,
|
|
||||||
( { var(Item) } ->
|
( { var(Item) } ->
|
||||||
ruleml_query_items(Items)
|
ruleml_query_items(Items)
|
||||||
; { Items = [Item | Items0] },
|
; { Items = [Item | Items0] },
|
||||||
@@ -220,7 +212,6 @@ ruleml_query_items(Items) -->
|
|||||||
)
|
)
|
||||||
; { Items = [Item | Items0] },
|
; { Items = [Item | Items0] },
|
||||||
ruleml_query_item(Item),
|
ruleml_query_item(Item),
|
||||||
!,
|
|
||||||
ruleml_query_items(Items0)
|
ruleml_query_items(Items0)
|
||||||
).
|
).
|
||||||
ruleml_query_items([]) --> [].
|
ruleml_query_items([]) --> [].
|
||||||
@@ -277,7 +268,7 @@ space(' ') --> " ".
|
|||||||
|
|
||||||
decimal_point('.') --> ".".
|
decimal_point('.') --> ".".
|
||||||
|
|
||||||
sign('-') --> "-", !.
|
sign('-') --> "-".
|
||||||
sign('+') --> "+".
|
sign('+') --> "+".
|
||||||
|
|
||||||
double_quote('"') --> "\"".
|
double_quote('"') --> "\"".
|
||||||
@@ -330,7 +321,6 @@ ruleml_condition(Item) -->
|
|||||||
|
|
||||||
ruleml_item_conjunction([Item | Items]) -->
|
ruleml_item_conjunction([Item | Items]) -->
|
||||||
ruleml_condition(Item),
|
ruleml_condition(Item),
|
||||||
!,
|
|
||||||
ruleml_item_conjunction(Items).
|
ruleml_item_conjunction(Items).
|
||||||
ruleml_item_conjunction([]) --> [].
|
ruleml_item_conjunction([]) --> [].
|
||||||
|
|
||||||
@@ -402,7 +392,6 @@ ruleml_conjunction_of_items(Items) -->
|
|||||||
|
|
||||||
ruleml_item_disjunction([Item | Items]) -->
|
ruleml_item_disjunction([Item | Items]) -->
|
||||||
ruleml_condition(Item),
|
ruleml_condition(Item),
|
||||||
!,
|
|
||||||
ruleml_item_disjunction(Items).
|
ruleml_item_disjunction(Items).
|
||||||
ruleml_item_disjunction([]) --> [].
|
ruleml_item_disjunction([]) --> [].
|
||||||
|
|
||||||
@@ -498,7 +487,7 @@ ruleml_plex(Plex) -->
|
|||||||
list_ws("</Plex>")
|
list_ws("</Plex>")
|
||||||
; list_ws("<Plex/>")
|
; list_ws("<Plex/>")
|
||||||
)
|
)
|
||||||
; { \+ string(Plex),
|
; { ( \+ partial_string(Plex) ; Plex == [] ),
|
||||||
acyclic_term(Plex) },
|
acyclic_term(Plex) },
|
||||||
( { functor(Plex, ('.'), 2) } ->
|
( { functor(Plex, ('.'), 2) } ->
|
||||||
{ split_plex(Plex, PlexItems, RepoVar) },
|
{ split_plex(Plex, PlexItems, RepoVar) },
|
||||||
@@ -547,7 +536,6 @@ ruleml_naf(Item) -->
|
|||||||
|
|
||||||
ruleml_atoms([Item|Items]) -->
|
ruleml_atoms([Item|Items]) -->
|
||||||
ruleml_atom(Item),
|
ruleml_atom(Item),
|
||||||
!,
|
|
||||||
ruleml_atoms(Items).
|
ruleml_atoms(Items).
|
||||||
ruleml_atoms([]) --> [].
|
ruleml_atoms([]) --> [].
|
||||||
|
|
||||||
@@ -582,13 +570,11 @@ ruleml_atom(Item) -->
|
|||||||
list_ws("<Atom>"),
|
list_ws("<Atom>"),
|
||||||
list_ws("<Rel>"),
|
list_ws("<Rel>"),
|
||||||
prolog_symbol(Name),
|
prolog_symbol(Name),
|
||||||
{ Name \== (','), Name \== (';') },
|
|
||||||
list_ws("</Rel>"),
|
list_ws("</Rel>"),
|
||||||
ruleml_items(Args),
|
ruleml_items(Args),
|
||||||
list_ws("</Atom>"),
|
list_ws("</Atom>"),
|
||||||
{ Item =.. [Name | Args] }
|
{ Item =.. [Name | Args] }
|
||||||
; { Item =.. [Name | Args] },
|
; { Item =.. [Name | Args] },
|
||||||
{ Name \== (','), Name \== (';') },
|
|
||||||
"<Atom>",
|
"<Atom>",
|
||||||
"<Rel>",
|
"<Rel>",
|
||||||
prolog_symbol(Name),
|
prolog_symbol(Name),
|
||||||
@@ -652,7 +638,6 @@ ruleml_expr(Item) -->
|
|||||||
( list_ws("<repo>"),
|
( list_ws("<repo>"),
|
||||||
ruleml_item(RepoItem),
|
ruleml_item(RepoItem),
|
||||||
list_ws("</repo>"),
|
list_ws("</repo>"),
|
||||||
!,
|
|
||||||
{ fold_commas(Args, ArgsCommas) },
|
{ fold_commas(Args, ArgsCommas) },
|
||||||
{ Item =.. [Name, (ArgsCommas | RepoItem)] }
|
{ Item =.. [Name, (ArgsCommas | RepoItem)] }
|
||||||
; { Item =.. [Name | Args] }
|
; { Item =.. [Name | Args] }
|
||||||
@@ -789,7 +774,7 @@ constant_chars(symbol, Constant, Chars) :-
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* ruleml_data//1 delegates to ruleml_data_contents//2 to determine
|
* ruleml_data//1 delegates to ruleml_data_contents//2 to determine
|
||||||
* the contents of <Data> nodes, with adjoining xsi:type elements.
|
* the contents of <Data> nodes, with adjoining iso:type elements.
|
||||||
*
|
*
|
||||||
* constant_chars/3 performs type-driven conversion between
|
* constant_chars/3 performs type-driven conversion between
|
||||||
* Prolog/'$V' and RuleML/XML in both directions;
|
* Prolog/'$V' and RuleML/XML in both directions;
|
||||||
@@ -799,13 +784,13 @@ constant_chars(symbol, Constant, Chars) :-
|
|||||||
|
|
||||||
ruleml_data(Name) -->
|
ruleml_data(Name) -->
|
||||||
( { var(Name) } ->
|
( { var(Name) } ->
|
||||||
list_ws("<Data xsi:type=\""),
|
list_ws("<Data iso:type=\""),
|
||||||
prolog_symbol(Type),
|
prolog_symbol(Type),
|
||||||
list_ws("\">"),
|
list_ws("\">"),
|
||||||
ruleml_data_contents(Type, Cs),
|
ruleml_data_contents(Type, Cs),
|
||||||
{ constant_chars(Type, Name, Cs) },
|
{ constant_chars(Type, Name, Cs) },
|
||||||
list_ws("</Data>")
|
list_ws("</Data>")
|
||||||
; "<Data xsi:type=\"",
|
; "<Data iso:type=\"",
|
||||||
{ constant_chars(Type, Name, Cs) },
|
{ constant_chars(Type, Name, Cs) },
|
||||||
prolog_symbol(Type),
|
prolog_symbol(Type),
|
||||||
"\">",
|
"\">",
|
||||||
@@ -823,15 +808,12 @@ ruleml_data(Name) -->
|
|||||||
* converting from lists of characters (here denoted as Cs) to the
|
* converting from lists of characters (here denoted as Cs) to the
|
||||||
* named data type in ISO Prolog. The type information is given
|
* named data type in ISO Prolog. The type information is given
|
||||||
* in the first argument.
|
* in the first argument.
|
||||||
*
|
|
||||||
* Grammatically, ruleml_data_contents//3 is a disjoint union of its
|
|
||||||
* subgrammars, justifying the cut at the end of the first two rules.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ruleml_data_contents(number, Cs) -->
|
ruleml_data_contents(number, Cs) -->
|
||||||
ruleml_number(Cs), !.
|
ruleml_number(Cs).
|
||||||
ruleml_data_contents(symbol, Cs) -->
|
ruleml_data_contents(symbol, Cs) -->
|
||||||
ruleml_symbol(Cs), !.
|
ruleml_symbol(Cs).
|
||||||
ruleml_data_contents(string, Cs) -->
|
ruleml_data_contents(string, Cs) -->
|
||||||
ruleml_string(Cs).
|
ruleml_string(Cs).
|
||||||
|
|
||||||
@@ -1195,8 +1177,7 @@ fold_semicolons(List, Output) :-
|
|||||||
* complicated by Scryer's current lack of multi-argument indexing.
|
* complicated by Scryer's current lack of multi-argument indexing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fold_list([Item], _, Item) :-
|
fold_list([Item], _, Item).
|
||||||
!.
|
|
||||||
fold_list([Item|Items], F, Form) :-
|
fold_list([Item|Items], F, Form) :-
|
||||||
Form =.. [F, Item, Fs],
|
Form =.. [F, Item, Fs],
|
||||||
fold_list(Items, F, Fs).
|
fold_list(Items, F, Fs).
|
||||||
4061
src/examples/bimetatrans/bimetatrans_tests.pl
Normal file
4061
src/examples/bimetatrans/bimetatrans_tests.pl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,6 @@ verify_attributes(Var, Other, Goals) :-
|
|||||||
( Els = [] -> % exactly one element
|
( Els = [] -> % exactly one element
|
||||||
Goals = [Other=El] % implied binding
|
Goals = [Other=El] % implied binding
|
||||||
; Goals = [],
|
; Goals = [],
|
||||||
put_atts(Other, -dom(_)),
|
|
||||||
put_atts(Other, dom(Dc))% rescue intersection
|
put_atts(Other, dom(Dc))% rescue intersection
|
||||||
)
|
)
|
||||||
; Goals = [],
|
; Goals = [],
|
||||||
31
src/examples/echo_server.pl
Normal file
31
src/examples/echo_server.pl
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
:- module(echo_server, [echo_server/0,
|
||||||
|
echo_server/1]).
|
||||||
|
|
||||||
|
:- use_module(library(format)).
|
||||||
|
:- use_module(library(sockets)).
|
||||||
|
|
||||||
|
|
||||||
|
echo_server :-
|
||||||
|
echo_server('127.0.0.1').
|
||||||
|
|
||||||
|
|
||||||
|
echo_server(Addr) :-
|
||||||
|
socket_server_open(Addr:Port, ServerSocket),
|
||||||
|
format("echo_server: connection opened at ~w:~d~n", [Addr, Port]),
|
||||||
|
socket_server_accept(ServerSocket, Client, Stream, [eof_action(eof_code)]),
|
||||||
|
format("echo_server: connection accepted from ~a~n", [Client]),
|
||||||
|
!,
|
||||||
|
echo_loop(Stream),
|
||||||
|
socket_server_close(ServerSocket).
|
||||||
|
|
||||||
|
|
||||||
|
echo_loop(Stream) :-
|
||||||
|
read_term(Stream, Term, []),
|
||||||
|
( Term == end_of_file ->
|
||||||
|
true
|
||||||
|
;
|
||||||
|
format("received: ~w~n", [Term]),
|
||||||
|
!,
|
||||||
|
echo_loop(Stream)
|
||||||
|
).
|
||||||
|
|
||||||
@@ -11,31 +11,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
:- module(least_time, [find_min_time/2,
|
:- module(least_time, [find_min_time/2,
|
||||||
write_time_nl/1]).
|
write_time_nl/1]).
|
||||||
|
|
||||||
|
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
:- use_module(library(format)).
|
:- use_module(library(format)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
:- use_module(library(reif)).
|
:- use_module(library(reif)).
|
||||||
|
|
||||||
|
|
||||||
permutation([], []).
|
|
||||||
permutation([X|Xs], Ys) :-
|
|
||||||
permutation(Xs, Yss),
|
|
||||||
select(X, Ys, Yss).
|
|
||||||
|
|
||||||
|
|
||||||
valid_time([H1,H2,M1,M2], T) :-
|
valid_time([H1,H2,M1,M2], T) :-
|
||||||
memberd_t(H1, [0,1,2], TH1),
|
memberd_t(H1, [0,1,2], TH1),
|
||||||
memberd_t(H2, [0,1,2,3,4,5,6,7,8,9], TH2),
|
memberd_t(H2, [0,1,2,3,4,5,6,7,8,9], TH2),
|
||||||
memberd_t(M1, [0,1,2,3,4,5], TM1),
|
memberd_t(M1, [0,1,2,3,4,5], TM1),
|
||||||
memberd_t(M2, [0,1,2,3,4,5,6,7,8,9], TM2),
|
memberd_t(M2, [0,1,2,3,4,5,6,7,8,9], TM2),
|
||||||
( maplist(=(true), [TH1, TH2, TM1, TM2]) ->
|
( maplist(=(true), [TH1, TH2, TM1, TM2]) ->
|
||||||
( H1 =:= 2 ->
|
( H1 =:= 2 ->
|
||||||
( H2 =< 3 ->
|
( H2 =< 3 ->
|
||||||
T = true
|
T = true
|
||||||
; T = false
|
; T = false
|
||||||
)
|
)
|
||||||
; T = true
|
; T = true
|
||||||
)
|
)
|
||||||
; T = false
|
; T = false
|
||||||
@@ -49,15 +44,7 @@ permuted_times(Time, PermutedTimes) :-
|
|||||||
|
|
||||||
find_min_time(Time, Min) :-
|
find_min_time(Time, Min) :-
|
||||||
valid_time(Time, true),
|
valid_time(Time, true),
|
||||||
permuted_times(Time, PermutedTimes),
|
permuted_times(Time, [Min|_]).
|
||||||
find_min_time_(PermutedTimes, Time, Min).
|
|
||||||
|
|
||||||
find_min_time_([], Min, Min).
|
|
||||||
find_min_time_([Time|Times], MinSoFar, Min) :-
|
|
||||||
( Time @< MinSoFar ->
|
|
||||||
find_min_time_(Times, Time, Min)
|
|
||||||
; find_min_time_(Times, MinSoFar, Min)
|
|
||||||
).
|
|
||||||
|
|
||||||
|
|
||||||
write_time_nl(Time) :-
|
write_time_nl(Time) :-
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
:- use_module(library(dif)).
|
:- use_module(library(dif)).
|
||||||
|
:- use_module(library(format)).
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
pl_resolution(Clauses0, Chain) :-
|
pl_resolution(Clauses0, Chain) :-
|
||||||
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::forms::*;
|
||||||
use crate::prolog::instructions::*;
|
use crate::instructions::*;
|
||||||
use crate::prolog::iterators::*;
|
use crate::iterators::*;
|
||||||
|
|
||||||
use indexmap::{IndexMap, IndexSet};
|
use indexmap::{IndexMap, IndexSet};
|
||||||
|
|
||||||
@@ -13,22 +13,24 @@ use std::rc::Rc;
|
|||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
// labeled with chunk numbers.
|
// labeled with chunk numbers.
|
||||||
pub enum VarStatus {
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum VarStatus {
|
||||||
Perm(usize),
|
Perm(usize),
|
||||||
Temp(usize, TempVarData), // Perm(chunk_num) | Temp(chunk_num, _)
|
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.
|
// Perm: 0 initially, a stack register once processed.
|
||||||
// Temp: labeled with chunk_num and temp offset (unassigned if 0).
|
// Temp: labeled with chunk_num and temp offset (unassigned if 0).
|
||||||
pub enum VarData {
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum VarData {
|
||||||
Perm(usize),
|
Perm(usize),
|
||||||
Temp(usize, usize, TempVarData),
|
Temp(usize, usize, TempVarData),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VarData {
|
impl VarData {
|
||||||
pub fn as_reg_type(&self) -> RegType {
|
pub(crate) fn as_reg_type(&self) -> RegType {
|
||||||
match self {
|
match self {
|
||||||
&VarData::Temp(_, r, _) => RegType::Temp(r),
|
&VarData::Temp(_, r, _) => RegType::Temp(r),
|
||||||
&VarData::Perm(r) => RegType::Perm(r),
|
&VarData::Perm(r) => RegType::Perm(r),
|
||||||
@@ -36,15 +38,16 @@ impl VarData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TempVarData {
|
#[derive(Debug)]
|
||||||
pub last_term_arity: usize,
|
pub(crate) struct TempVarData {
|
||||||
pub use_set: OccurrenceSet,
|
pub(crate) last_term_arity: usize,
|
||||||
pub no_use_set: BTreeSet<usize>,
|
pub(crate) use_set: OccurrenceSet,
|
||||||
pub conflict_set: BTreeSet<usize>,
|
pub(crate) no_use_set: BTreeSet<usize>,
|
||||||
|
pub(crate) conflict_set: BTreeSet<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TempVarData {
|
impl TempVarData {
|
||||||
pub fn new(last_term_arity: usize) -> Self {
|
pub(crate) fn new(last_term_arity: usize) -> Self {
|
||||||
TempVarData {
|
TempVarData {
|
||||||
last_term_arity: last_term_arity,
|
last_term_arity: last_term_arity,
|
||||||
use_set: BTreeSet::new(),
|
use_set: BTreeSet::new(),
|
||||||
@@ -53,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() {
|
for &(_, nreg) in self.use_set.iter() {
|
||||||
if reg == nreg {
|
if reg == nreg {
|
||||||
return true;
|
return true;
|
||||||
@@ -63,7 +66,7 @@ impl TempVarData {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn populate_conflict_set(&mut self) {
|
pub(crate) fn populate_conflict_set(&mut self) {
|
||||||
if self.last_term_arity > 0 {
|
if self.last_term_arity > 0 {
|
||||||
let arity = self.last_term_arity;
|
let arity = self.last_term_arity;
|
||||||
let mut conflict_set: BTreeSet<usize> = (1..arity).collect();
|
let mut conflict_set: BTreeSet<usize> = (1..arity).collect();
|
||||||
@@ -79,30 +82,30 @@ impl TempVarData {
|
|||||||
|
|
||||||
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
|
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
|
||||||
|
|
||||||
pub struct VariableFixtures<'a>{
|
#[derive(Debug)]
|
||||||
perm_vars: IndexMap<Rc<Var>, VariableFixture<'a>>,
|
pub(crate) struct VariableFixtures<'a> {
|
||||||
last_chunk_temp_vars: IndexSet<Rc<Var>>
|
perm_vars: IndexMap<Rc<String>, VariableFixture<'a>>,
|
||||||
|
last_chunk_temp_vars: IndexSet<Rc<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VariableFixtures<'a> {
|
impl<'a> VariableFixtures<'a> {
|
||||||
pub fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
VariableFixtures {
|
VariableFixtures {
|
||||||
perm_vars: IndexMap::new(),
|
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);
|
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);
|
self.last_chunk_temp_vars.insert(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
// computes no_use and conflict sets for all temp vars.
|
// 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:
|
// three stages:
|
||||||
// 1. move the use sets of each variable to a local IndexMap, use_set
|
// 1. move the use sets of each variable to a local IndexMap, use_set
|
||||||
// (iterate mutably, swap mutable refs).
|
// (iterate mutably, swap mutable refs).
|
||||||
@@ -112,7 +115,7 @@ impl<'a> VariableFixtures<'a> {
|
|||||||
// Compute the conflict set of u.
|
// Compute the conflict set of u.
|
||||||
|
|
||||||
// 1.
|
// 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() {
|
for (var, &mut (ref mut var_status, _)) in self.iter_mut() {
|
||||||
if let &mut VarStatus::Temp(_, ref mut var_data) = var_status {
|
if let &mut VarStatus::Temp(_, ref mut var_data) = var_status {
|
||||||
@@ -150,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)
|
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()
|
self.perm_vars.iter_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,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;
|
let mut var_count = 0;
|
||||||
|
|
||||||
for &(ref var_status, _) in self.values() {
|
for &(ref var_status, _) in self.values() {
|
||||||
@@ -181,7 +184,7 @@ impl<'a> VariableFixtures<'a> {
|
|||||||
var_count
|
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
|
where
|
||||||
I: Iterator<Item = TermRef<'a>>,
|
I: Iterator<Item = TermRef<'a>>,
|
||||||
{
|
{
|
||||||
@@ -215,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()
|
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()
|
self.perm_vars.values()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> usize {
|
pub(crate) fn size(&self) -> usize {
|
||||||
self.perm_vars.len()
|
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
|
let mut values_vec: Vec<_> = self
|
||||||
.values()
|
.values()
|
||||||
.filter_map(|ref v| match &v.0 {
|
.filter_map(|ref v| match &v.0 {
|
||||||
@@ -248,43 +251,42 @@ impl<'a> VariableFixtures<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UnsafeVarMarker {
|
#[derive(Debug)]
|
||||||
pub unsafe_vars: IndexMap<RegType, usize>,
|
pub(crate) struct UnsafeVarMarker {
|
||||||
pub safe_vars: IndexSet<RegType>,
|
pub(crate) unsafe_vars: IndexMap<RegType, usize>,
|
||||||
|
pub(crate) safe_vars: IndexSet<RegType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnsafeVarMarker {
|
impl UnsafeVarMarker {
|
||||||
pub fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
UnsafeVarMarker {
|
UnsafeVarMarker {
|
||||||
unsafe_vars: IndexMap::new(),
|
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 {
|
UnsafeVarMarker {
|
||||||
unsafe_vars: IndexMap::new(),
|
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 {
|
match query_instr {
|
||||||
&QueryInstruction::PutVariable(r @ RegType::Temp(_), _)
|
&Instruction::PutVariable(r @ RegType::Temp(_), _) |
|
||||||
| &QueryInstruction::SetVariable(r) => {
|
&Instruction::SetVariable(r) => {
|
||||||
self.safe_vars.insert(r);
|
self.safe_vars.insert(r);
|
||||||
true
|
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 {
|
match query_instr {
|
||||||
&QueryInstruction::PutValue(r @ RegType::Perm(_), _)
|
&Instruction::PutValue(r @ RegType::Perm(_), _) |
|
||||||
| &QueryInstruction::SetValue(r) => {
|
&Instruction::SetValue(r) => {
|
||||||
let p = self.unsafe_vars.entry(r).or_insert(0);
|
let p = self.unsafe_vars.entry(r).or_insert(0);
|
||||||
*p = phase;
|
*p = phase;
|
||||||
}
|
}
|
||||||
@@ -292,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 {
|
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 let Some(p) = self.unsafe_vars.swap_remove(&RegType::Perm(i)) {
|
||||||
if p == phase {
|
if p == phase {
|
||||||
*query_instr = QueryInstruction::PutUnsafeValue(i, arg);
|
*query_instr = Instruction::PutUnsafeValue(i, arg);
|
||||||
self.safe_vars.insert(RegType::Perm(i));
|
self.safe_vars.insert(RegType::Perm(i));
|
||||||
} else {
|
} else {
|
||||||
self.unsafe_vars.insert(RegType::Perm(i), p);
|
self.unsafe_vars.insert(RegType::Perm(i), p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&mut QueryInstruction::SetValue(r) => {
|
&mut Instruction::SetValue(r) => {
|
||||||
if !self.safe_vars.contains(&r) {
|
if !self.safe_vars.contains(&r) {
|
||||||
*query_instr = QueryInstruction::SetLocalValue(r);
|
*query_instr = Instruction::SetLocalValue(r);
|
||||||
|
|
||||||
self.safe_vars.insert(r);
|
self.safe_vars.insert(r);
|
||||||
self.unsafe_vars.remove(&r);
|
self.unsafe_vars.remove(&r);
|
||||||
920
src/forms.rs
Normal file
920
src/forms.rs
Normal file
@@ -0,0 +1,920 @@
|
|||||||
|
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 std::cell::Cell;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
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, PartialEq, Eq)]
|
||||||
|
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, Copy)]
|
||||||
|
pub enum CallPolicy {
|
||||||
|
Default,
|
||||||
|
Counted,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum QueryTerm {
|
||||||
|
// register, clause type, subterms, clause call policy.
|
||||||
|
Clause(Cell<RegType>, ClauseType, Vec<Term>, CallPolicy),
|
||||||
|
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_call_policy(&mut self, cp: CallPolicy) {
|
||||||
|
match self {
|
||||||
|
&mut QueryTerm::Clause(_, _, _, ref mut clause_cp) => *clause_cp = cp,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
match self {
|
||||||
|
Term::Clause(_, name, terms) => {
|
||||||
|
|
||||||
|
match name {
|
||||||
|
atom!(":-") => {
|
||||||
|
match terms.len() {
|
||||||
|
1 => None, // a declaration.
|
||||||
|
2 => terms[0].name(),
|
||||||
|
_ => Some(*name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Some(*name), //str_buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Literal(_, Literal::Atom(name)) => Some(*name),
|
||||||
|
_ => 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,
|
||||||
|
Colon,
|
||||||
|
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_atom(&self, atom_tbl: &mut AtomTable) -> Atom {
|
||||||
|
match self {
|
||||||
|
&AtomOrString::Atom(atom) => {
|
||||||
|
atom
|
||||||
|
}
|
||||||
|
AtomOrString::String(string) => {
|
||||||
|
atom_tbl.build_with(&string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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(OrderedFloat(f)) => Literal::from(float_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(OrderedFloat(n)) => HeapCellValue::from(float_alloc!(n, arena)),
|
||||||
|
Number::Rational(n) => typed_arena_ptr_as_cell!(n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Number {
|
||||||
|
pub(crate) fn sign(&self) -> Number {
|
||||||
|
match self {
|
||||||
|
&Number::Float(f) if f == 0.0 => Number::Float(OrderedFloat(0f64)),
|
||||||
|
&Number::Float(f) => Number::Float(OrderedFloat(f.signum())),
|
||||||
|
_ => {
|
||||||
|
if self.is_positive() {
|
||||||
|
Number::Fixnum(Fixnum::build_with(1))
|
||||||
|
} else if self.is_negative() {
|
||||||
|
Number::Fixnum(Fixnum::build_with(-1))
|
||||||
|
} else {
|
||||||
|
Number::Fixnum(Fixnum::build_with(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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: VecDeque<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: VecDeque::new(),
|
||||||
|
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: VecDeque<ClauseIndexInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PredicateSkeleton {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
PredicateSkeleton {
|
||||||
|
core: LocalPredicateSkeleton::new(),
|
||||||
|
clauses: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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(
|
||||||
|
&mut self,
|
||||||
|
clause_clause_loc: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
let search_result = self.core.clause_clause_locs
|
||||||
|
.make_contiguous()[0..self.core.clause_assert_margin]
|
||||||
|
.binary_search_by(|loc| clause_clause_loc.cmp(&loc));
|
||||||
|
|
||||||
|
match search_result {
|
||||||
|
Ok(loc) => Some(loc),
|
||||||
|
Err(_) => {
|
||||||
|
self.core.clause_clause_locs
|
||||||
|
.make_contiguous()[self.core.clause_assert_margin..]
|
||||||
|
.binary_search_by(|loc| loc.cmp(&clause_clause_loc))
|
||||||
|
.map(|loc| loc + self.core.clause_assert_margin)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2718
src/heap_iter.rs
Normal file
2718
src/heap_iter.rs
Normal file
File diff suppressed because it is too large
Load Diff
1891
src/heap_print.rs
Normal file
1891
src/heap_print.rs
Normal file
File diff suppressed because it is too large
Load Diff
25
src/http.rs
Normal file
25
src/http.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use std::convert::Infallible;
|
||||||
|
|
||||||
|
use hyper::{Response, Request, Body};
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
|
||||||
|
pub struct HttpListener {
|
||||||
|
pub incoming: Receiver<HttpRequest>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct HttpRequest {
|
||||||
|
pub request: Request<Body>,
|
||||||
|
pub response: HttpResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type HttpResponse = Sender<Response<Body>>;
|
||||||
|
|
||||||
|
pub async fn serve_req(req: Request<Body>, tx: Arc<Mutex<Sender<HttpRequest>>>) -> Result<Response<Body>, Infallible> {
|
||||||
|
let (response_tx, mut rx) = channel(1);
|
||||||
|
let http_request = HttpRequest { request: req, response: response_tx };
|
||||||
|
tx.lock().await.send(http_request).await.unwrap();
|
||||||
|
Ok(rx.recv().await.unwrap())
|
||||||
|
}
|
||||||
1558
src/indexing.rs
Normal file
1558
src/indexing.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,130 +1,77 @@
|
|||||||
use prolog_parser::ast::*;
|
use crate::atom_table::*;
|
||||||
|
use crate::forms::*;
|
||||||
use crate::prolog::clause_types::*;
|
use crate::instructions::*;
|
||||||
use crate::prolog::forms::*;
|
use crate::parser::ast::*;
|
||||||
use crate::prolog::machine::machine_indices::*;
|
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt;
|
||||||
use std::iter::*;
|
use std::iter::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TermRef<'a> {
|
pub(crate) enum TermRef<'a> {
|
||||||
AnonVar(Level),
|
AnonVar(Level),
|
||||||
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||||
Constant(Level, &'a Cell<RegType>, &'a Constant),
|
Literal(Level, &'a Cell<RegType>, &'a Literal),
|
||||||
Clause(Level, &'a Cell<RegType>, ClauseType, &'a Vec<Box<Term>>),
|
Clause(Level, &'a Cell<RegType>, Atom, &'a Vec<Term>),
|
||||||
PartialString(Level, &'a Cell<RegType>, String, Option<&'a Term>),
|
PartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
|
||||||
Var(Level, &'a Cell<VarReg>, Rc<Var>),
|
CompleteString(Level, &'a Cell<RegType>, Atom),
|
||||||
|
Var(Level, &'a Cell<VarReg>, Rc<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TermRef<'a> {
|
impl<'a> TermRef<'a> {
|
||||||
pub fn level(self) -> Level {
|
pub(crate) fn level(self) -> Level {
|
||||||
match self {
|
match self {
|
||||||
TermRef::AnonVar(lvl)
|
TermRef::AnonVar(lvl)
|
||||||
| TermRef::Cons(lvl, ..)
|
| TermRef::Cons(lvl, ..)
|
||||||
| TermRef::Constant(lvl, ..)
|
| TermRef::Literal(lvl, ..)
|
||||||
| TermRef::Var(lvl, ..)
|
| TermRef::Var(lvl, ..)
|
||||||
| TermRef::Clause(lvl, ..) => lvl,
|
| TermRef::Clause(lvl, ..)
|
||||||
| TermRef::PartialString(lvl, ..) => lvl,
|
| TermRef::CompleteString(lvl, ..)
|
||||||
|
| TermRef::PartialString(lvl, ..) => lvl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum TermIterState<'a> {
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum TermIterState<'a> {
|
||||||
AnonVar(Level),
|
AnonVar(Level),
|
||||||
Constant(Level, &'a Cell<RegType>, &'a Constant),
|
Literal(Level, &'a Cell<RegType>, &'a Literal),
|
||||||
Clause(
|
Clause(Level, usize, &'a Cell<RegType>, Atom, &'a Vec<Term>),
|
||||||
Level,
|
|
||||||
usize,
|
|
||||||
&'a Cell<RegType>,
|
|
||||||
ClauseType,
|
|
||||||
&'a Vec<Box<Term>>,
|
|
||||||
),
|
|
||||||
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||||
FinalCons(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>),
|
InitialPartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
|
||||||
Var(Level, &'a Cell<VarReg>, Rc<Var>),
|
FinalPartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
|
||||||
}
|
CompleteString(Level, &'a Cell<RegType>, Atom),
|
||||||
|
Var(Level, &'a Cell<VarReg>, Rc<String>),
|
||||||
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));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TermIterState<'a> {
|
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 {
|
match term {
|
||||||
&Term::AnonVar => {
|
Term::AnonVar => TermIterState::AnonVar(lvl),
|
||||||
TermIterState::AnonVar(lvl)
|
Term::Clause(cell, name, subterms) => {
|
||||||
|
TermIterState::Clause(lvl, 0, cell, *name, subterms)
|
||||||
}
|
}
|
||||||
&Term::Clause(ref cell, ref name, ref subterms, ref spec) => {
|
Term::Cons(cell, head, tail) => {
|
||||||
let ct = if let Some(spec) = spec {
|
|
||||||
ClauseType::Op(name.clone(), spec.clone(), CodeIndex::default())
|
|
||||||
} else {
|
|
||||||
ClauseType::Named(name.clone(), subterms.len(), CodeIndex::default())
|
|
||||||
};
|
|
||||||
|
|
||||||
TermIterState::Clause(lvl, 0, cell, ct, subterms)
|
|
||||||
}
|
|
||||||
&Term::Cons(ref cell, ref head, ref tail) => {
|
|
||||||
TermIterState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref())
|
TermIterState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref())
|
||||||
}
|
}
|
||||||
&Term::Constant(ref cell, ref constant) => {
|
Term::Literal(cell, constant) => TermIterState::Literal(lvl, cell, constant),
|
||||||
TermIterState::Constant(lvl, cell, constant)
|
Term::PartialString(cell, string_buf, tail) => {
|
||||||
|
TermIterState::InitialPartialString(lvl, cell, string_buf, tail)
|
||||||
}
|
}
|
||||||
&Term::Var(ref cell, ref var) => {
|
Term::CompleteString(cell, atom) => {
|
||||||
TermIterState::Var(lvl, cell, var.clone())
|
TermIterState::CompleteString(lvl, cell, *atom)
|
||||||
}
|
}
|
||||||
|
Term::Var(cell, var) => TermIterState::Var(lvl, cell, var.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct QueryIterator<'a> {
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct QueryIterator<'a> {
|
||||||
state_stack: Vec<TermIterState<'a>>,
|
state_stack: Vec<TermIterState<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,11 +81,11 @@ impl<'a> QueryIterator<'a> {
|
|||||||
.push(TermIterState::subterm_to_state(lvl, term));
|
.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
|
let state_stack = terms
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt.as_ref()))
|
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
QueryIterator { state_stack }
|
QueryIterator { state_stack }
|
||||||
@@ -146,30 +93,20 @@ impl<'a> QueryIterator<'a> {
|
|||||||
|
|
||||||
fn from_term(term: &'a Term) -> Self {
|
fn from_term(term: &'a Term) -> Self {
|
||||||
let state = match term {
|
let state = match term {
|
||||||
&Term::AnonVar => {
|
Term::AnonVar | Term::Cons(..) | Term::Literal(..) |
|
||||||
|
Term::PartialString(..) | Term::CompleteString(..) => {
|
||||||
return QueryIterator {
|
return QueryIterator {
|
||||||
state_stack: vec![],
|
state_stack: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Term::Clause(ref r, ref name, ref terms, ref fixity) => TermIterState::Clause(
|
Term::Clause(r, name, terms) => TermIterState::Clause(
|
||||||
Level::Root,
|
Level::Root,
|
||||||
0,
|
0,
|
||||||
r,
|
r,
|
||||||
ClauseType::from(name.clone(), terms.len(), fixity.clone()),
|
*name,
|
||||||
terms,
|
terms,
|
||||||
),
|
),
|
||||||
&Term::Cons(..) => {
|
Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, var.clone()),
|
||||||
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()),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QueryIterator {
|
QueryIterator {
|
||||||
@@ -179,20 +116,20 @@ impl<'a> QueryIterator<'a> {
|
|||||||
|
|
||||||
fn new(term: &'a QueryTerm) -> Self {
|
fn new(term: &'a QueryTerm) -> Self {
|
||||||
match term {
|
match term {
|
||||||
&QueryTerm::Clause(ref cell, ClauseType::CallN, ref terms, _) => {
|
&QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => {
|
||||||
let state = TermIterState::Clause(Level::Root, 1, cell, ClauseType::CallN, terms);
|
let state = TermIterState::Clause(Level::Root, 1, cell, atom!("$call"), terms);
|
||||||
QueryIterator {
|
QueryIterator {
|
||||||
state_stack: vec![state],
|
state_stack: vec![state],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
|
&QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
|
||||||
let state = TermIterState::Clause(Level::Root, 0, cell, ct.clone(), terms);
|
let state = TermIterState::Clause(Level::Root, 0, cell, ct.name(), terms);
|
||||||
QueryIterator {
|
QueryIterator {
|
||||||
state_stack: vec![state],
|
state_stack: vec![state],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&QueryTerm::UnblockedCut(ref cell) => {
|
&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 {
|
QueryIterator {
|
||||||
state_stack: vec![state],
|
state_stack: vec![state],
|
||||||
}
|
}
|
||||||
@@ -228,20 +165,17 @@ impl<'a> Iterator for QueryIterator<'a> {
|
|||||||
TermIterState::AnonVar(lvl) => {
|
TermIterState::AnonVar(lvl) => {
|
||||||
return Some(TermRef::AnonVar(lvl));
|
return Some(TermRef::AnonVar(lvl));
|
||||||
}
|
}
|
||||||
TermIterState::Clause(lvl, child_num, cell, ct, child_terms) => {
|
TermIterState::Clause(lvl, child_num, cell, name, child_terms) => {
|
||||||
if child_num == child_terms.len() {
|
if child_num == child_terms.len() {
|
||||||
match ct {
|
match name {
|
||||||
ClauseType::CallN => {
|
atom!("$call") if lvl == Level::Root => {
|
||||||
self.push_subterm(Level::Shallow, child_terms[0].as_ref())
|
self.push_subterm(Level::Shallow, &child_terms[0]);
|
||||||
}
|
|
||||||
ClauseType::Named(..) | ClauseType::Op(..) => {
|
|
||||||
return match lvl {
|
|
||||||
Level::Root => None,
|
|
||||||
lvl => Some(TermRef::Clause(lvl, cell, ct, child_terms)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return None;
|
return match lvl {
|
||||||
|
Level::Root => None,
|
||||||
|
lvl => Some(TermRef::Clause(lvl, cell, name, child_terms)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -249,40 +183,34 @@ impl<'a> Iterator for QueryIterator<'a> {
|
|||||||
lvl,
|
lvl,
|
||||||
child_num + 1,
|
child_num + 1,
|
||||||
cell,
|
cell,
|
||||||
ct,
|
name,
|
||||||
child_terms,
|
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) => {
|
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
||||||
if let Some((string, tail)) = is_partial_string(head, tail) {
|
self.state_stack.push(TermIterState::FinalCons(lvl, cell, head, tail));
|
||||||
self.state_stack.push(TermIterState::PartialString(
|
|
||||||
lvl,
|
|
||||||
cell,
|
|
||||||
string,
|
|
||||||
tail,
|
|
||||||
));
|
|
||||||
|
|
||||||
if let Some(tail) = tail {
|
self.push_subterm(lvl.child_level(), tail);
|
||||||
self.push_subterm(lvl.child_level(), tail);
|
self.push_subterm(lvl.child_level(), head);
|
||||||
}
|
|
||||||
} 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::PartialString(lvl, cell, string, tail) => {
|
TermIterState::InitialPartialString(lvl, cell, string, tail) => {
|
||||||
return Some(TermRef::PartialString(lvl, cell, string, tail));
|
self.state_stack.push(TermIterState::FinalPartialString(lvl, cell, string, tail));
|
||||||
|
self.push_subterm(lvl.child_level(), tail);
|
||||||
|
}
|
||||||
|
TermIterState::FinalPartialString(lvl, cell, atom, tail) => {
|
||||||
|
return Some(TermRef::PartialString(lvl, cell, atom, tail));
|
||||||
|
}
|
||||||
|
TermIterState::CompleteString(lvl, cell, atom) => {
|
||||||
|
return Some(TermRef::CompleteString(lvl, cell, atom));
|
||||||
}
|
}
|
||||||
TermIterState::FinalCons(lvl, cell, head, tail) => {
|
TermIterState::FinalCons(lvl, cell, head, tail) => {
|
||||||
return Some(TermRef::Cons(lvl, cell, head, tail));
|
return Some(TermRef::Cons(lvl, cell, head, tail));
|
||||||
}
|
}
|
||||||
TermIterState::Constant(lvl, cell, constant) => {
|
TermIterState::Literal(lvl, cell, constant) => {
|
||||||
return Some(TermRef::Constant(lvl, cell, constant));
|
return Some(TermRef::Literal(lvl, cell, constant));
|
||||||
}
|
}
|
||||||
TermIterState::Var(lvl, cell, var) => {
|
TermIterState::Var(lvl, cell, var) => {
|
||||||
return Some(TermRef::Var(lvl, cell, var));
|
return Some(TermRef::Var(lvl, cell, var));
|
||||||
@@ -294,20 +222,22 @@ impl<'a> Iterator for QueryIterator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FactIterator<'a> {
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct FactIterator<'a> {
|
||||||
state_queue: VecDeque<TermIterState<'a>>,
|
state_queue: VecDeque<TermIterState<'a>>,
|
||||||
iterable_root: bool,
|
iterable_root: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FactIterator<'a> {
|
impl<'a> FactIterator<'a> {
|
||||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
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
|
let state_queue = terms
|
||||||
.iter()
|
.iter()
|
||||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt.as_ref()))
|
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
FactIterator {
|
FactIterator {
|
||||||
@@ -318,23 +248,37 @@ impl<'a> FactIterator<'a> {
|
|||||||
|
|
||||||
fn new(term: &'a Term, iterable_root: bool) -> Self {
|
fn new(term: &'a Term, iterable_root: bool) -> Self {
|
||||||
let states = match term {
|
let states = match term {
|
||||||
&Term::AnonVar => {
|
Term::AnonVar => {
|
||||||
vec![TermIterState::AnonVar(Level::Root)]
|
vec![TermIterState::AnonVar(Level::Root)]
|
||||||
}
|
}
|
||||||
&Term::Clause(ref cell, ref name, ref terms, ref fixity) => {
|
Term::Clause(cell, name, terms) => {
|
||||||
let ct = ClauseType::from(name.clone(), terms.len(), fixity.clone());
|
vec![TermIterState::Clause(Level::Root, 0, cell, *name, terms)]
|
||||||
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,
|
Level::Root,
|
||||||
cell,
|
cell,
|
||||||
head.as_ref(),
|
head.as_ref(),
|
||||||
tail.as_ref(),
|
tail.as_ref(),
|
||||||
)],
|
)],
|
||||||
&Term::Constant(ref cell, ref constant) => {
|
Term::PartialString(cell, string_buf, tail) => {
|
||||||
vec![TermIterState::Constant(Level::Root, cell, constant)]
|
vec![TermIterState::InitialPartialString(
|
||||||
|
Level::Root,
|
||||||
|
cell,
|
||||||
|
string_buf,
|
||||||
|
tail,
|
||||||
|
)]
|
||||||
}
|
}
|
||||||
&Term::Var(ref cell, ref var) => {
|
Term::CompleteString(cell, atom) => {
|
||||||
|
vec![TermIterState::CompleteString(
|
||||||
|
Level::Root,
|
||||||
|
cell,
|
||||||
|
*atom,
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
Term::Literal(cell, constant) => {
|
||||||
|
vec![TermIterState::Literal(Level::Root, cell, constant)]
|
||||||
|
}
|
||||||
|
Term::Var(cell, var) => {
|
||||||
vec![TermIterState::Var(Level::Root, cell, var.clone())]
|
vec![TermIterState::Var(Level::Root, cell, var.clone())]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -355,38 +299,36 @@ impl<'a> Iterator for FactIterator<'a> {
|
|||||||
TermIterState::AnonVar(lvl) => {
|
TermIterState::AnonVar(lvl) => {
|
||||||
return Some(TermRef::AnonVar(lvl));
|
return Some(TermRef::AnonVar(lvl));
|
||||||
}
|
}
|
||||||
TermIterState::Clause(lvl, _, cell, ct, child_terms) => {
|
TermIterState::Clause(lvl, _, cell, name, child_terms) => {
|
||||||
for child_term in child_terms {
|
for child_term in child_terms {
|
||||||
self.push_subterm(lvl.child_level(), child_term);
|
self.push_subterm(lvl.child_level(), child_term);
|
||||||
}
|
}
|
||||||
|
|
||||||
match lvl {
|
match lvl {
|
||||||
Level::Root if !self.iterable_root => continue,
|
Level::Root if !self.iterable_root => continue,
|
||||||
_ => return Some(TermRef::Clause(lvl, cell, ct, child_terms)),
|
_ => return Some(TermRef::Clause(lvl, cell, name, child_terms)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
||||||
if let Some((string, tail)) = is_partial_string(head, tail) {
|
self.push_subterm(Level::Deep, head);
|
||||||
if let Some(tail) = tail {
|
self.push_subterm(Level::Deep, tail);
|
||||||
self.push_subterm(Level::Deep, tail);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(TermRef::PartialString(lvl, cell, string, tail));
|
return Some(TermRef::Cons(lvl, cell, head, tail));
|
||||||
} else {
|
|
||||||
self.push_subterm(Level::Deep, head);
|
|
||||||
self.push_subterm(Level::Deep, tail);
|
|
||||||
|
|
||||||
return Some(TermRef::Cons(lvl, cell, head, tail));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TermIterState::Constant(lvl, cell, constant) => {
|
TermIterState::InitialPartialString(lvl, cell, string_buf, tail) => {
|
||||||
return Some(TermRef::Constant(lvl, cell, constant))
|
self.push_subterm(Level::Deep, tail);
|
||||||
|
return Some(TermRef::PartialString(lvl, cell, string_buf, tail));
|
||||||
|
}
|
||||||
|
TermIterState::CompleteString(lvl, cell, atom) => {
|
||||||
|
return Some(TermRef::CompleteString(lvl, cell, atom));
|
||||||
|
}
|
||||||
|
TermIterState::Literal(lvl, cell, constant) => {
|
||||||
|
return Some(TermRef::Literal(lvl, cell, constant))
|
||||||
}
|
}
|
||||||
TermIterState::Var(lvl, cell, var) => {
|
TermIterState::Var(lvl, cell, var) => {
|
||||||
return Some(TermRef::Var(lvl, cell, var));
|
return Some(TermRef::Var(lvl, cell, var));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,27 +336,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)
|
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)
|
FactIterator::new(term, iterable_root)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ChunkedTerm<'a> {
|
#[derive(Debug)]
|
||||||
HeadClause(ClauseName, &'a Vec<Box<Term>>),
|
pub(crate) enum ChunkedTerm<'a> {
|
||||||
|
HeadClause(Atom, &'a Vec<Term>),
|
||||||
BodyTerm(&'a QueryTerm),
|
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)
|
QueryIterator::new(query_term)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ChunkedTerm<'a> {
|
impl<'a> ChunkedTerm<'a> {
|
||||||
pub fn post_order_iter(&self) -> QueryIterator<'a> {
|
pub(crate) fn post_order_iter(&self) -> QueryIterator<'a> {
|
||||||
match self {
|
match self {
|
||||||
&ChunkedTerm::BodyTerm(ref qt) => QueryIterator::new(qt),
|
&ChunkedTerm::BodyTerm(qt) => QueryIterator::new(qt),
|
||||||
&ChunkedTerm::HeadClause(_, terms) => QueryIterator::from_rule_head_clause(terms),
|
&ChunkedTerm::HeadClause(_, terms) => QueryIterator::from_rule_head_clause(terms),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,18 +375,30 @@ fn contains_cut_var<'a, Iter: Iterator<Item = &'a Term>>(terms: Iter) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ChunkedIterator<'a> {
|
pub(crate) struct ChunkedIterator<'a> {
|
||||||
pub chunk_num: usize,
|
pub(crate) chunk_num: usize,
|
||||||
iter: Box<dyn Iterator<Item = ChunkedTerm<'a>> + 'a>,
|
iter: Box<dyn Iterator<Item = ChunkedTerm<'a>> + 'a>,
|
||||||
deep_cut_encountered: bool,
|
deep_cut_encountered: bool,
|
||||||
cut_var_in_head: bool,
|
cut_var_in_head: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> fmt::Debug for ChunkedIterator<'a> {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt.debug_struct("ChunkedIterator")
|
||||||
|
.field("chunk_num", &self.chunk_num)
|
||||||
|
// Hacky solution.
|
||||||
|
.field("iter", &"Box<dyn Iterator<Item = ChunkedTerm<'a>> + 'a>")
|
||||||
|
.field("deep_cut_encountered", &self.deep_cut_encountered)
|
||||||
|
.field("cut_var_in_head", &self.cut_var_in_head)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ChunkedIteratorItem<'a> = (usize, usize, Vec<ChunkedTerm<'a>>);
|
type ChunkedIteratorItem<'a> = (usize, usize, Vec<ChunkedTerm<'a>>);
|
||||||
type RuleBodyIteratorItem<'a> = (usize, usize, Vec<&'a QueryTerm>);
|
type RuleBodyIteratorItem<'a> = (usize, usize, Vec<&'a QueryTerm>);
|
||||||
|
|
||||||
impl<'a> ChunkedIterator<'a> {
|
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)| {
|
Box::new(self.filter_map(|(cn, lt_arity, terms)| {
|
||||||
let filtered_terms: Vec<_> = terms
|
let filtered_terms: Vec<_> = terms
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -461,16 +416,7 @@ impl<'a> ChunkedIterator<'a> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_term_sequence(terms: &'a [QueryTerm]) -> Self {
|
pub(crate) fn from_rule_body(p1: &'a QueryTerm, clauses: &'a Vec<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 {
|
|
||||||
let inner_iter = Box::new(once(ChunkedTerm::BodyTerm(p1)));
|
let inner_iter = Box::new(once(ChunkedTerm::BodyTerm(p1)));
|
||||||
let iter = inner_iter.chain(clauses.iter().map(|t| ChunkedTerm::BodyTerm(t)));
|
let iter = inner_iter.chain(clauses.iter().map(|t| ChunkedTerm::BodyTerm(t)));
|
||||||
|
|
||||||
@@ -482,7 +428,7 @@ impl<'a> ChunkedIterator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_rule(rule: &'a Rule) -> Self {
|
pub(crate) fn from_rule(rule: &'a Rule) -> Self {
|
||||||
let &Rule {
|
let &Rule {
|
||||||
head: (ref name, ref args, ref p1),
|
head: (ref name, ref args, ref p1),
|
||||||
ref clauses,
|
ref clauses,
|
||||||
@@ -500,7 +446,7 @@ impl<'a> ChunkedIterator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encountered_deep_cut(&self) -> bool {
|
pub(crate) fn encountered_deep_cut(&self) -> bool {
|
||||||
self.deep_cut_encountered
|
self.deep_cut_encountered
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,7 +458,7 @@ impl<'a> ChunkedIterator<'a> {
|
|||||||
while let Some(term) = item {
|
while let Some(term) = item {
|
||||||
match term {
|
match term {
|
||||||
ChunkedTerm::HeadClause(_, terms) => {
|
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;
|
self.cut_var_in_head = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,13 +488,16 @@ impl<'a> ChunkedIterator<'a> {
|
|||||||
arity = 1;
|
arity = 1;
|
||||||
break;
|
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(_), ..)) => {
|
ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => {
|
||||||
result.push(term)
|
result.push(term)
|
||||||
}
|
}
|
||||||
ChunkedTerm::BodyTerm(&QueryTerm::Clause(
|
ChunkedTerm::BodyTerm(&QueryTerm::Clause(
|
||||||
_,
|
_,
|
||||||
ClauseType::CallN,
|
ClauseType::CallN(_),
|
||||||
ref subterms,
|
ref subterms,
|
||||||
_,
|
_,
|
||||||
)) => {
|
)) => {
|
||||||
36
src/lib.rs
Normal file
36
src/lib.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#![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 http;
|
||||||
|
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 repl_helper;
|
||||||
|
mod targets;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
|
use instructions::instr;
|
||||||
121
src/lib/arithmetic.pl
Normal file
121
src/lib/arithmetic.pl
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
:- module(arithmetic, [expmod/4, lsb/2, msb/2, number_to_rational/2,
|
||||||
|
number_to_rational/3, popcount/2,
|
||||||
|
rational_numerator_denominator/3]).
|
||||||
|
|
||||||
|
:- use_module(library(charsio), [write_term_to_chars/3]).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(lists), [append/3, member/2]).
|
||||||
|
|
||||||
|
expmod(Base, Expo, Mod, R) :-
|
||||||
|
( member(N, [Base, Expo, Mod]), var(N) -> instantiation_error(expmod/4)
|
||||||
|
; member(N, [Base, Expo, Mod]), \+ integer(N) ->
|
||||||
|
type_error(integer, N, expmod/4)
|
||||||
|
; Expo < 0 -> domain_error(not_less_than_zero, Expo, expmod/4)
|
||||||
|
; expmod_(Base, Expo, Mod, 1, R)
|
||||||
|
).
|
||||||
|
|
||||||
|
expmod_(_, _, 1, _, 0) :- !.
|
||||||
|
expmod_(_, 0, _, R, R) :- !.
|
||||||
|
expmod_(Base0, Expo0, Mod, C0, R) :-
|
||||||
|
Expo0 /\ 1 =:= 1,
|
||||||
|
C is (C0 * Base0) mod Mod,
|
||||||
|
!,
|
||||||
|
Expo is Expo0 >> 1,
|
||||||
|
Base is (Base0 * Base0) mod Mod,
|
||||||
|
expmod_(Base, Expo, Mod, C, R).
|
||||||
|
expmod_(Base0, Expo0, Mod, C, R) :-
|
||||||
|
Expo is Expo0 >> 1,
|
||||||
|
Base is (Base0 * Base0) mod Mod,
|
||||||
|
expmod_(Base, Expo, Mod, C, R).
|
||||||
|
|
||||||
|
lsb(X, N) :-
|
||||||
|
builtins:must_be_number(X, lsb/2),
|
||||||
|
( \+ integer(X) -> type_error(integer, X, lsb/2)
|
||||||
|
; X < 1 -> domain_error(not_less_than_one, X, lsb/2)
|
||||||
|
; builtins:can_be_number(N, lsb/2),
|
||||||
|
X1 is X /\ (-X),
|
||||||
|
msb_(X1, -1, N)
|
||||||
|
).
|
||||||
|
|
||||||
|
msb(X, N) :-
|
||||||
|
builtins:must_be_number(X, msb/2),
|
||||||
|
( \+ integer(X) -> type_error(integer, X, msb/2)
|
||||||
|
; X < 1 -> domain_error(not_less_than_one, X, msb/2)
|
||||||
|
; builtins:can_be_number(N, msb/2),
|
||||||
|
X1 is X >> 1,
|
||||||
|
msb_(X1, 0, N)
|
||||||
|
).
|
||||||
|
|
||||||
|
msb_(0, N, N) :- !.
|
||||||
|
msb_(X, M, N) :-
|
||||||
|
X1 is X >> 1,
|
||||||
|
M1 is M + 1,
|
||||||
|
msb_(X1, M1, N).
|
||||||
|
|
||||||
|
number_to_rational(Real, Fraction) :-
|
||||||
|
( var(Real) -> instantiation_error(number_to_rational/2)
|
||||||
|
; integer(Real) -> Fraction is Real rdiv 1
|
||||||
|
; (rational(Real) ; float(Real)) ->
|
||||||
|
number_to_rational(1.0e-6, Real, Fraction)
|
||||||
|
; type_error(number, Real, number_to_rational/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
% If 0 <= Eps0 <= 1e-16 then the search is for "infinite" precision.
|
||||||
|
number_to_rational(Eps0, Real0, Fraction) :-
|
||||||
|
( var(Eps0) -> instantiation_error(number_to_rational/3)
|
||||||
|
; \+ number(Eps0) -> type_error(number, Eps0, number_to_rational/3)
|
||||||
|
; Eps0 < 0 -> domain_error(not_less_than_zero, Eps0, number_to_rational/3)
|
||||||
|
; Eps_ is Eps0 rdiv 1,
|
||||||
|
rational_numerator_denominator(Eps_, EpsN, EpsD),
|
||||||
|
Eps = EpsN/EpsD
|
||||||
|
),
|
||||||
|
( var(Real0) -> instantiation_error(number_to_rational/3)
|
||||||
|
; \+ number(Real0) -> type_error(number, Eps0, number_to_rational/3)
|
||||||
|
; Real_ is Real0 rdiv 1,
|
||||||
|
rational_numerator_denominator(Real_, RealN, RealD),
|
||||||
|
Real = RealN/RealD
|
||||||
|
),
|
||||||
|
E0/E1 = Eps,
|
||||||
|
P0/Q0 = Real,
|
||||||
|
( P0 < 0 -> I1 is -1 + P0 // Q0
|
||||||
|
; I1 is P0 // Q0
|
||||||
|
),
|
||||||
|
P1 is P0 mod Q0,
|
||||||
|
Q1 = Q0,
|
||||||
|
( P1 =:= 0 -> Fraction is I1 + 0 rdiv 1
|
||||||
|
; Qn1n is max(P1 * E1 - Q1 * E0, 0),
|
||||||
|
Qn1d is Q1 * E1,
|
||||||
|
Qn1 = Qn1n/Qn1d,
|
||||||
|
Qp1n is P1 * E1 + Q1 * E0,
|
||||||
|
Qp1d = Qn1d,
|
||||||
|
Qp1 = Qp1n/Qp1d,
|
||||||
|
stern_brocot_(Qn1, Qp1, 0/1, 1/0, P2/Q2),
|
||||||
|
Fraction is I1 + P2 rdiv Q2
|
||||||
|
),
|
||||||
|
!.
|
||||||
|
|
||||||
|
stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, C/D, Fraction) :-
|
||||||
|
Fn1 is A + C,
|
||||||
|
Fd1 is B + D,
|
||||||
|
simplify_fraction(Fn1/Fd1, Fn/Fd),
|
||||||
|
S1 is sign(Fn * Qnd - Fd * Qnn),
|
||||||
|
S2 is sign(Fn * Qpd - Fd * Qpn),
|
||||||
|
( S1 < 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, Fn/Fd, C/D, Fraction)
|
||||||
|
; S2 > 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, Fn/Fd, Fraction)
|
||||||
|
; Fraction = Fn/Fd
|
||||||
|
).
|
||||||
|
|
||||||
|
simplify_fraction(A0/B0, A/B) :-
|
||||||
|
G is gcd(A0, B0),
|
||||||
|
A is A0 // G,
|
||||||
|
B is B0 // G.
|
||||||
|
|
||||||
|
rational_numerator_denominator(R, N, D) :-
|
||||||
|
write_term_to_chars(R, [], Cs),
|
||||||
|
append(Ns, [' ', r, d, i, v, ' '|Ds], Cs),
|
||||||
|
number_chars(N, Ns),
|
||||||
|
number_chars(D, Ds).
|
||||||
|
|
||||||
|
popcount(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
|
@author R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
:- meta_predicate map_assoc(1, ?).
|
||||||
:- meta_predicate
|
:- meta_predicate map_assoc(2, ?, ?).
|
||||||
map_assoc(1, ?),
|
|
||||||
map_assoc(2, ?, ?).
|
|
||||||
*/
|
|
||||||
|
|
||||||
%! empty_assoc(?Assoc) is semidet.
|
%! empty_assoc(?Assoc) is semidet.
|
||||||
%
|
%
|
||||||
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).
|
||||||
@@ -9,14 +9,21 @@ between(Lower, Upper, X) :-
|
|||||||
must_be(integer, Lower),
|
must_be(integer, Lower),
|
||||||
must_be(integer, Upper),
|
must_be(integer, Upper),
|
||||||
can_be(integer, X),
|
can_be(integer, X),
|
||||||
between_(Lower, Upper, X).
|
( nonvar(X) ->
|
||||||
|
Lower =< X,
|
||||||
|
X =< Upper
|
||||||
|
; Lower =< Upper,
|
||||||
|
between_(Lower, Upper, X)
|
||||||
|
).
|
||||||
|
|
||||||
between_(Lower, Upper, Lower) :-
|
between_(Lower, Upper, Lower1) :-
|
||||||
Lower =< Upper.
|
Lower < Upper,
|
||||||
between_(Lower1, Upper, X) :-
|
!,
|
||||||
Lower1 < Upper,
|
( Lower1 = Lower
|
||||||
Lower2 is Lower1 + 1,
|
; Lower0 is Lower + 1,
|
||||||
between_(Lower2, Upper, X).
|
between_(Lower0, Upper, Lower1)
|
||||||
|
).
|
||||||
|
between_(Lower, Lower, Lower).
|
||||||
|
|
||||||
enumerate_nats(I, I).
|
enumerate_nats(I, I).
|
||||||
enumerate_nats(I0, N) :-
|
enumerate_nats(I0, N) :-
|
||||||
1648
src/lib/builtins.pl
Normal file
1648
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(octet_character, N, chars_base64/3)
|
||||||
|
; '$chars_base64'(Cs, Bs, Padding, Charset)
|
||||||
|
)
|
||||||
|
).
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
:- module(clpb, [op(300, fy, ~),
|
:- module(clpb, [op(300, fy, ~),
|
||||||
op(500, yfx, #),
|
op(500, yfx, #),
|
||||||
sat/1,
|
sat/1,
|
||||||
taut/2,
|
taut/2,
|
||||||
labeling/1,
|
labeling/1,
|
||||||
sat_count/2,
|
sat_count/2,
|
||||||
@@ -31,9 +31,10 @@
|
|||||||
:- use_module(library(atts)).
|
:- use_module(library(atts)).
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
:- use_module(library(iso_ext)).
|
:- use_module(library(iso_ext)).
|
||||||
|
:- use_module(library(random)).
|
||||||
:- use_module(library(pairs)).
|
:- use_module(library(pairs)).
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
:- use_module(library(error), []).
|
:- use_module(library(error), [domain_error/3, type_error/3]).
|
||||||
|
|
||||||
:- attribute
|
:- attribute
|
||||||
clpb/1,
|
clpb/1,
|
||||||
@@ -48,17 +49,6 @@
|
|||||||
Compatibility predicates.
|
Compatibility predicates.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
group_pairs_by_key([], []).
|
|
||||||
group_pairs_by_key([M-N|T0], [M-[N|TN]|T]) :-
|
|
||||||
same_key(M, T0, TN, T1),
|
|
||||||
group_pairs_by_key(T1, T).
|
|
||||||
|
|
||||||
same_key(M0, [M-N|T0], [N|TN], T) :-
|
|
||||||
M0 == M,
|
|
||||||
!,
|
|
||||||
same_key(M, T0, TN, T).
|
|
||||||
same_key(_, L, [], L).
|
|
||||||
|
|
||||||
must_be(What, Term) :- must_be(What, unknown(Term)-1, Term).
|
must_be(What, Term) :- must_be(What, unknown(Term)-1, Term).
|
||||||
|
|
||||||
must_be(acyclic, Where, Term) :- !,
|
must_be(acyclic, Where, Term) :- !,
|
||||||
@@ -97,16 +87,10 @@ instantiation_error(_, Goal-Arg) :-
|
|||||||
domain_error(Expectation, Term) :-
|
domain_error(Expectation, Term) :-
|
||||||
domain_error(Expectation, Term, unknown(Term)-1).
|
domain_error(Expectation, Term, unknown(Term)-1).
|
||||||
|
|
||||||
domain_error(Expectation, Term, Goal-Arg) :-
|
|
||||||
throw(error(domain_error(Expectation, Term), domain_error(Goal, Arg, Expectation, Term))).
|
|
||||||
|
|
||||||
|
|
||||||
type_error(Expectation, Term) :-
|
type_error(Expectation, Term) :-
|
||||||
type_error(Expectation, Term, unknown(Term)-1).
|
type_error(Expectation, Term, unknown(Term)-1).
|
||||||
|
|
||||||
type_error(Expectation, Term, Goal-Arg) :-
|
|
||||||
throw(error(type_error(Expectation, Term), type_error(Goal, Arg, Expectation, Term))).
|
|
||||||
|
|
||||||
partition(Pred, Ls0, As, Bs) :-
|
partition(Pred, Ls0, As, Bs) :-
|
||||||
include(Pred, Ls0, As),
|
include(Pred, Ls0, As),
|
||||||
exclude(Pred, Ls0, Bs).
|
exclude(Pred, Ls0, Bs).
|
||||||
@@ -847,9 +831,8 @@ verify_attributes(Var, Other, Gs) :-
|
|||||||
( integer(Other) ->
|
( integer(Other) ->
|
||||||
( between(0, 1, Other) ->
|
( between(0, 1, Other) ->
|
||||||
root_get_formula_bdd(Root, Sat, BDD0),
|
root_get_formula_bdd(Root, Sat, BDD0),
|
||||||
bdd_restriction(BDD0, I, Other, BDD),
|
|
||||||
root_put_formula_bdd(Root, Sat, 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)
|
; no_truth_value(Other)
|
||||||
)
|
)
|
||||||
; atom(Other) ->
|
; atom(Other) ->
|
||||||
@@ -1139,6 +1122,8 @@ indomain(1).
|
|||||||
% CountAnd = 1.
|
% CountAnd = 1.
|
||||||
% ==
|
% ==
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sat_count(Sat0, N) :-
|
sat_count(Sat0, N) :-
|
||||||
catch((parse_sat(Sat0, Sat),
|
catch((parse_sat(Sat0, Sat),
|
||||||
sat_bdd(Sat, BDD),
|
sat_bdd(Sat, BDD),
|
||||||
@@ -1293,13 +1278,18 @@ weighted_maximum(Ws, Vars, Max) :-
|
|||||||
maplist(var_with_index, Vars, IVs),
|
maplist(var_with_index, Vars, IVs),
|
||||||
pairs_keys_values(Pairs0, IVs, Ws),
|
pairs_keys_values(Pairs0, IVs, Ws),
|
||||||
keysort(Pairs0, Pairs1),
|
keysort(Pairs0, Pairs1),
|
||||||
pairs_keys_values(Pairs1, IVs1, WeightsIndexOrder),
|
% sum linear combinations of repeated variables
|
||||||
|
group_pairs_by_key(Pairs1, Groups),
|
||||||
|
maplist(group_sumweights_pair, Groups, Pairs2),
|
||||||
|
pairs_keys_values(Pairs2, IVs1, WeightsIndexOrder),
|
||||||
pairs_values(IVs1, VarsIndexOrder),
|
pairs_values(IVs1, VarsIndexOrder),
|
||||||
% Pairs is a list of Var-Weight terms, in index order of Vars
|
% Pairs is a list of Var-Weight terms, in index order of Vars
|
||||||
pairs_keys_values(Pairs, VarsIndexOrder, WeightsIndexOrder),
|
pairs_keys_values(Pairs, VarsIndexOrder, WeightsIndexOrder),
|
||||||
bdd_maximum(BDD, Pairs, Max),
|
bdd_maximum(BDD, Pairs, Max),
|
||||||
max_labeling(BDD, Pairs).
|
max_labeling(BDD, Pairs).
|
||||||
|
|
||||||
|
group_sumweights_pair((I-V)-Ws, (I-V)-W) :- sum_list(Ws, W).
|
||||||
|
|
||||||
max_labeling(1, Pairs) :- max_upto(Pairs, _, _).
|
max_labeling(1, Pairs) :- max_upto(Pairs, _, _).
|
||||||
max_labeling(node(_,Var,Low,High,Aux), Pairs0) :-
|
max_labeling(node(_,Var,Low,High,Aux), Pairs0) :-
|
||||||
max_upto(Pairs0, Var, Pairs),
|
max_upto(Pairs0, Var, Pairs),
|
||||||
@@ -1534,8 +1524,8 @@ pairs_([], _) --> [].
|
|||||||
pairs_([B|Bs], A) --> [A-B], pairs_(Bs, A).
|
pairs_([B|Bs], A) --> [A-B], pairs_(Bs, A).
|
||||||
|
|
||||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
Set the Prolog flag clpb_residuals to bdd to obtain the BDD nodes
|
Assert clpb:clpb_residuals(bdd) to obtain the BDD nodes as
|
||||||
as residuals. Note that they cannot be used as regular goals.
|
residuals. Note that they cannot be used as regular goals.
|
||||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
nodes([]) --> [].
|
nodes([]) --> [].
|
||||||
@@ -1563,9 +1553,10 @@ sats([]) --> [].
|
|||||||
sats([A|As]) --> [clpb:sat(A)], sats(As).
|
sats([A|As]) --> [clpb:sat(A)], sats(As).
|
||||||
|
|
||||||
booleans([]) --> [].
|
booleans([]) --> [].
|
||||||
booleans([B|Bs]) --> boolean(B), { del_clpb(B) }, booleans(Bs).
|
booleans([B|Bs]) --> boolean(B), booleans(Bs).
|
||||||
|
|
||||||
boolean(Var) -->
|
boolean(Var) -->
|
||||||
|
{ del_clpb(Var) },
|
||||||
( { get_attr(Var, clpb_omit_boolean, true) } -> []
|
( { get_attr(Var, clpb_omit_boolean, true) } -> []
|
||||||
; [clpb:sat(Var =:= Var)]
|
; [clpb:sat(Var =:= Var)]
|
||||||
).
|
).
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
|||||||
:- module(cont, [reset/3, shift/1]).
|
:- module(cont, [reset/3, shift/1]).
|
||||||
|
|
||||||
|
:- meta_predicate reset(0, ?, ?).
|
||||||
|
|
||||||
reset(Goal, Ball, Cont) :-
|
reset(Goal, Ball, Cont) :-
|
||||||
call(Goal),
|
call(Goal),
|
||||||
'$reset_cont_marker',
|
'$reset_cont_marker',
|
||||||
@@ -10,8 +12,8 @@ shift(Ball) :-
|
|||||||
'$nextEP'(first, E, P),
|
'$nextEP'(first, E, P),
|
||||||
get_chunks(E, P, L),
|
get_chunks(E, P, L),
|
||||||
( L == [] ->
|
( L == [] ->
|
||||||
Cont = none
|
Cont = cont(true)
|
||||||
; Cont = cont(call_continuation(L))
|
; Cont = cont(cont:call_continuation(L))
|
||||||
),
|
),
|
||||||
'$write_cont_and_term'(_, _, Cont, Ball),
|
'$write_cont_and_term'(_, _, Cont, Ball),
|
||||||
'$unwind_environments'.
|
'$unwind_environments'.
|
||||||
838
src/lib/crypto.pl
Normal file
838
src/lib/crypto.pl
Normal file
@@ -0,0 +1,838 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Written 2020, 2021, 2022 by Markus Triska (triska@metalevel.at)
|
||||||
|
Part of Scryer Prolog.
|
||||||
|
|
||||||
|
Predicates for cryptographic applications.
|
||||||
|
|
||||||
|
This library assumes that the Prolog flag double_quotes is set to chars.
|
||||||
|
In Scryer Prolog, lists of characters are very efficiently represented,
|
||||||
|
and strings have the advantage that the atom table remains unmodified.
|
||||||
|
|
||||||
|
Especially for cryptographic applications, it is an advantage that
|
||||||
|
using strings leaves little trace of what was processed in the system.
|
||||||
|
|
||||||
|
For predicates that accept an encoding/1 option to specify the encoding
|
||||||
|
of the input data, if encoding(octet) is used, then the input can also
|
||||||
|
be specified as a list of bytes, i.e., integers between 0 and 255.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
:- module(crypto,
|
||||||
|
[hex_bytes/2, % ?Hex, ?Bytes
|
||||||
|
crypto_n_random_bytes/2, % +N, -Bytes
|
||||||
|
crypto_data_hash/3, % +Data, -Hash, +Options
|
||||||
|
crypto_data_hkdf/4, % +Data, +Length, -Bytes, +Options
|
||||||
|
crypto_password_hash/2, % +Password, ?Hash
|
||||||
|
crypto_password_hash/3, % +Password, -Hash, +Options
|
||||||
|
crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options
|
||||||
|
crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options
|
||||||
|
ed25519_new_keypair/1, % -KeyPair
|
||||||
|
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
|
||||||
|
crypto_curve_scalar_mult/4 % +Curve, +Scalar, +Point, -Result
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(between)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(clpz)).
|
||||||
|
:- 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.
|
||||||
|
|
||||||
|
Relation between a hexadecimal sequence and a list of bytes. Hex
|
||||||
|
is a string of hexadecimal numbers. Bytes is a list of *integers*
|
||||||
|
between 0 and 255 that represent the sequence as a list of bytes.
|
||||||
|
At least one of the arguments must be instantiated.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
?- hex_bytes("501ACE", Bs).
|
||||||
|
Bs = [80,26,206].
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
|
||||||
|
hex_bytes(Hs, Bytes) :-
|
||||||
|
( ground(Hs) ->
|
||||||
|
must_be(chars, Hs),
|
||||||
|
( phrase(hex_bytes(Hs), Bytes) ->
|
||||||
|
true
|
||||||
|
; domain_error(hex_encoding, Hs, hex_bytes/2)
|
||||||
|
)
|
||||||
|
; must_be_bytes(Bytes, hex_bytes/2),
|
||||||
|
phrase(bytes_hex(Bytes), Hs)
|
||||||
|
).
|
||||||
|
|
||||||
|
hex_bytes([]) --> [].
|
||||||
|
hex_bytes([H1,H2|Hs]) --> [Byte],
|
||||||
|
{ char_hexval(H1, High),
|
||||||
|
char_hexval(H2, Low),
|
||||||
|
Byte #= High*16 + Low },
|
||||||
|
hex_bytes(Hs).
|
||||||
|
|
||||||
|
bytes_hex([]) --> [].
|
||||||
|
bytes_hex([B|Bs]) --> [C0,C1],
|
||||||
|
{ High #= B>>4,
|
||||||
|
Low #= B /\ 0xf,
|
||||||
|
char_hexval(C0, High),
|
||||||
|
char_hexval(C1, Low)
|
||||||
|
},
|
||||||
|
bytes_hex(Bs).
|
||||||
|
|
||||||
|
char_hexval(C, H) :- nth0(H, "0123456789abcdef", C), !.
|
||||||
|
char_hexval(C, H) :- nth0(H, "0123456789ABCDEF", C), !.
|
||||||
|
|
||||||
|
|
||||||
|
must_be_bytes(Bytes, Context) :-
|
||||||
|
must_be(list, Bytes),
|
||||||
|
maplist(must_be(integer), Bytes),
|
||||||
|
( member(B, Bytes), \+ between(0, 255, B) ->
|
||||||
|
type_error(byte, B, Context)
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
must_be_octet_chars(Chars, Context) :-
|
||||||
|
must_be(chars, Chars),
|
||||||
|
( '$first_non_octet'(Chars, F) ->
|
||||||
|
domain_error(octet_character, F, Context)
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Cryptographically secure random numbers
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
crypto_n_random_bytes(+N, -Bytes) is det
|
||||||
|
|
||||||
|
Bytes is unified with a list of N cryptographically secure
|
||||||
|
pseudo-random bytes. Each byte is an integer between 0 and 255. If
|
||||||
|
the internal pseudo-random number generator (PRNG) has not been
|
||||||
|
seeded with enough entropy to ensure an unpredictable byte
|
||||||
|
sequence, an exception is thrown.
|
||||||
|
|
||||||
|
One way to relate such a list of bytes to an _integer_ is to use
|
||||||
|
CLP(ℤ) constraints as follows:
|
||||||
|
|
||||||
|
:- use_module(library(clpz)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
With this definition, we can generate a random 256-bit integer
|
||||||
|
_from_ a list of 32 random _bytes_:
|
||||||
|
|
||||||
|
?- 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).
|
||||||
|
|
||||||
|
The above relation also works in the other direction, letting you
|
||||||
|
translate an integer _to_ a list of bytes. In addition, you can
|
||||||
|
use hex_bytes/2 to convert bytes to _tokens_ that can be easily
|
||||||
|
exchanged in your applications.
|
||||||
|
|
||||||
|
?- crypto_n_random_bytes(12, Bs),
|
||||||
|
hex_bytes(Hex, Bs).
|
||||||
|
Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ...".
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
|
||||||
|
crypto_n_random_bytes(N, Bs) :-
|
||||||
|
must_be(integer, N),
|
||||||
|
length(Bs, N),
|
||||||
|
maplist(crypto_random_byte, Bs).
|
||||||
|
|
||||||
|
crypto_random_byte(B) :- '$crypto_random_byte'(B).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Hashing
|
||||||
|
=======
|
||||||
|
|
||||||
|
crypto_data_hash(+Data, -Hash, +Options)
|
||||||
|
|
||||||
|
Where Data is a list of characters, and Hash is the computed hash
|
||||||
|
as a list of hexadecimal characters.
|
||||||
|
|
||||||
|
Options is a list of:
|
||||||
|
|
||||||
|
- algorithm(+A)
|
||||||
|
where A is one of ripemd160, sha256, sha384, sha512, sha512_256,
|
||||||
|
sha3_224, sha3_256, sha3_384, sha3_512, blake2s256, blake2b512,
|
||||||
|
or a variable. If A is a variable, then it is unified with the
|
||||||
|
default algorithm, which is an algorithm that is considered
|
||||||
|
cryptographically secure at the time of this writing.
|
||||||
|
- encoding(+Encoding)
|
||||||
|
The default encoding is utf8. The alternative is octet,
|
||||||
|
to treat the input as a list of raw bytes.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
?- crypto_data_hash("abc", Hs, [algorithm(sha256)]).
|
||||||
|
Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad".
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
SHA256 is the current default for several hash-related predicates.
|
||||||
|
It is deemed sufficiently secure for the foreseeable future. Yet,
|
||||||
|
application programmers must be aware that the default may change in
|
||||||
|
future versions. The hash predicates all yield the algorithm they
|
||||||
|
used if a Prolog variable is used for the pertaining option.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
crypto_data_hash(Data0, Hash, Options0) :-
|
||||||
|
must_be(list, Options0),
|
||||||
|
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, Encoding, HashBytes, A),
|
||||||
|
hex_bytes(Hash, HashBytes).
|
||||||
|
|
||||||
|
options_data_chars(Options, Data, Chars, Encoding) :-
|
||||||
|
option(encoding(Encoding), Options, utf8),
|
||||||
|
must_be(atom, Encoding),
|
||||||
|
encoding_chars(Encoding, Data, Chars).
|
||||||
|
|
||||||
|
default_hash(sha256).
|
||||||
|
|
||||||
|
functor_hash_options(F, Hash, Options0, [Option|Options]) :-
|
||||||
|
Option =.. [F,Hash],
|
||||||
|
( select(Option, Options0, Options) ->
|
||||||
|
( var(Hash) ->
|
||||||
|
default_hash(Hash)
|
||||||
|
; must_be(atom, Hash)
|
||||||
|
)
|
||||||
|
; Options = Options0,
|
||||||
|
default_hash(Hash)
|
||||||
|
).
|
||||||
|
|
||||||
|
hash_algorithm(ripemd160).
|
||||||
|
hash_algorithm(sha256).
|
||||||
|
hash_algorithm(sha512).
|
||||||
|
hash_algorithm(sha384).
|
||||||
|
hash_algorithm(sha512_256).
|
||||||
|
hash_algorithm(sha3_224).
|
||||||
|
hash_algorithm(sha3_256).
|
||||||
|
hash_algorithm(sha3_384).
|
||||||
|
hash_algorithm(sha3_512).
|
||||||
|
hash_algorithm(blake2s256).
|
||||||
|
hash_algorithm(blake2b512).
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det.
|
||||||
|
|
||||||
|
Concentrate possibly dispersed entropy of Data and then expand it
|
||||||
|
to the desired length. Data is a list of characters.
|
||||||
|
|
||||||
|
Bytes is unified with a list of bytes of length Length, and is
|
||||||
|
suitable as input keying material and initialization vectors to
|
||||||
|
symmetric encryption algorithms.
|
||||||
|
|
||||||
|
Admissible options are:
|
||||||
|
|
||||||
|
- algorithm(+Algorithm)
|
||||||
|
One of sha256, sha384 or sha512. If you specify a variable,
|
||||||
|
then it is unified with the algorithm that was used, which is a
|
||||||
|
cryptographically secure algorithm by default.
|
||||||
|
- info(+Info)
|
||||||
|
Optional context and application specific information,
|
||||||
|
specified as a list of characters. The default is [].
|
||||||
|
- salt(+List)
|
||||||
|
Optionally, a list of bytes that are used as salt. The
|
||||||
|
default is all zeroes.
|
||||||
|
- encoding(+Encoding)
|
||||||
|
The default encoding is utf8. The alternative is octet,
|
||||||
|
to treat the input as a list of raw bytes.
|
||||||
|
|
||||||
|
The `info/1` option can be used to generate multiple keys from a
|
||||||
|
single master key, using for example values such as "key" and
|
||||||
|
"iv", or the name of a file that is to be encrypted.
|
||||||
|
|
||||||
|
See crypto_n_random_bytes/2 to obtain a suitable salt.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
crypto_data_hkdf(Data0, L, Bytes, Options0) :-
|
||||||
|
functor_hash_options(algorithm, Algorithm, Options0, Options),
|
||||||
|
( hkdf_algorithm(Algorithm) -> true
|
||||||
|
; domain_error(hkdf_algorithm, Algorithm, crypto_data_hkdf/4)
|
||||||
|
),
|
||||||
|
must_be(integer, L),
|
||||||
|
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, Encoding, SaltBytes, Info, Algorithm, L, Bytes).
|
||||||
|
|
||||||
|
hkdf_algorithm(sha256).
|
||||||
|
hkdf_algorithm(sha384).
|
||||||
|
hkdf_algorithm(sha512).
|
||||||
|
|
||||||
|
option(What, Options, Default) :-
|
||||||
|
( member(V, Options), var(V) ->
|
||||||
|
instantiation_error(option/3)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
( member(What, Options) -> true
|
||||||
|
; What =.. [_,Default]
|
||||||
|
).
|
||||||
|
|
||||||
|
chars_bytes_(Cs, Bytes, Context) :-
|
||||||
|
must_be(list, Cs),
|
||||||
|
( maplist(integer, Cs) -> Bytes = Cs
|
||||||
|
; chars_utf8bytes(Cs, Bytes)
|
||||||
|
),
|
||||||
|
must_be_bytes(Bytes, Context).
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
The so-called modular crypt format (MCF) is a standard for encoding
|
||||||
|
password hash strings. However, there's no official specification
|
||||||
|
document describing it. Nor is there a central registry of
|
||||||
|
identifiers or rules. This page describes what is known about it:
|
||||||
|
|
||||||
|
https://pythonhosted.org/passlib/modular_crypt_format.html
|
||||||
|
|
||||||
|
As of 2016, the MCF is deprecated in favor of the PHC String Format:
|
||||||
|
|
||||||
|
https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md
|
||||||
|
|
||||||
|
This is what we are using below. For the time being, it is best to
|
||||||
|
treat these hashes as opaque terms in applications. Please let me
|
||||||
|
know if you need to rely on any specifics of this format.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
crypto_password_hash(+Password, ?Hash) is semidet.
|
||||||
|
|
||||||
|
If Hash is instantiated, the predicate succeeds _iff_ the hash
|
||||||
|
matches the given password. Otherwise, the call is equivalent to
|
||||||
|
crypto_password_hash(Password, Hash, []) and computes a
|
||||||
|
password-based hash using the default options.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
crypto_password_hash(Password0, Hash) :-
|
||||||
|
( nonvar(Hash) ->
|
||||||
|
chars_bytes_(Password0, Password, crypto_password_hash/2),
|
||||||
|
must_be(list, Hash),
|
||||||
|
dollar_segments(Hash, [[],"pbkdf2-sha512",[t,=|CsIterations],SaltB64,HashB64]),
|
||||||
|
number_chars(Iterations, CsIterations),
|
||||||
|
bytes_base64(SaltBytes, SaltB64),
|
||||||
|
bytes_base64(HashBytes, HashB64),
|
||||||
|
'$crypto_password_hash'(Password, SaltBytes, Iterations, HashBytes)
|
||||||
|
; crypto_password_hash(Password0, Hash, [])
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
dollar_segments(Ls, Segments) :-
|
||||||
|
( append(Front, [$|Ds], Ls) ->
|
||||||
|
Segments = [Front|Rest],
|
||||||
|
dollar_segments(Ds, Rest)
|
||||||
|
; Segments = [Ls]
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
crypto_password_hash(+Password, -Hash, +Options) is det.
|
||||||
|
|
||||||
|
Derive Hash based on Password. This predicate is similar to
|
||||||
|
crypto_data_hash/3 in that it derives a hash from given data.
|
||||||
|
However, it is tailored for the specific use case of _passwords_.
|
||||||
|
One essential distinction is that for this use case, the derivation
|
||||||
|
of a hash should be _as slow as possible_ to counteract brute-force
|
||||||
|
attacks over possible passwords.
|
||||||
|
|
||||||
|
Another important distinction is that equal passwords must yield,
|
||||||
|
with very high probability, _different_ hashes. For this reason,
|
||||||
|
cryptographically strong random numbers are automatically added to
|
||||||
|
the password before a hash is derived.
|
||||||
|
|
||||||
|
Hash is unified with a string that contains the computed hash and
|
||||||
|
all parameters that were used, except for the password. Instead of
|
||||||
|
storing passwords, store these hashes. Later, you can verify the
|
||||||
|
validity of a password with crypto_password_hash/2, comparing the
|
||||||
|
then entered password to the stored hash. If you need to export this
|
||||||
|
atom, you should treat it as opaque ASCII data with up to 255 bytes
|
||||||
|
of length. The maximal length may increase in the future.
|
||||||
|
|
||||||
|
Admissible options are:
|
||||||
|
|
||||||
|
- algorithm(+Algorithm)
|
||||||
|
The algorithm to use. Currently, the only available algorithm
|
||||||
|
is 'pbkdf2-sha512', which is therefore also the default.
|
||||||
|
- cost(+C)
|
||||||
|
C is an integer, denoting the binary logarithm of the number
|
||||||
|
of _iterations_ used for the derivation of the hash. This
|
||||||
|
means that the number of iterations is set to 2^C. Currently,
|
||||||
|
the default is 17, and thus more than one hundred _thousand_
|
||||||
|
iterations. You should set this option as high as your server
|
||||||
|
and users can tolerate. The default is subject to change and
|
||||||
|
will likely increase in the future or adapt to new algorithms.
|
||||||
|
- salt(+Salt)
|
||||||
|
Use the given list of bytes as salt. By default,
|
||||||
|
cryptographically secure random numbers are generated for this
|
||||||
|
purpose. The default is intended to be secure, and constitutes
|
||||||
|
the typical use case of this predicate.
|
||||||
|
|
||||||
|
Currently, PBKDF2 with SHA-512 is used as the hash derivation
|
||||||
|
function, using 128 bits of salt. All default parameters, including
|
||||||
|
the algorithm, are subject to change, and other algorithms will also
|
||||||
|
become available in the future. Since computed hashes store all
|
||||||
|
parameters that were used during their derivation, such changes will
|
||||||
|
not affect the operation of existing deployments. Note though that
|
||||||
|
new hashes will then be computed with the new default parameters.
|
||||||
|
|
||||||
|
See crypto_data_hkdf/4 for generating keys from Hash.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
crypto_password_hash(Password0, Hash, Options) :-
|
||||||
|
chars_bytes_(Password0, Password, crypto_password_hash/3),
|
||||||
|
must_be(list, Options),
|
||||||
|
option(cost(C), Options, 17),
|
||||||
|
Iterations #= 2^C,
|
||||||
|
Algorithm = 'pbkdf2-sha512', % current default and only option
|
||||||
|
option(algorithm(Algorithm), Options, Algorithm),
|
||||||
|
( member(salt(SaltBytes), Options) ->
|
||||||
|
must_be_bytes(SaltBytes, crypto_password_hash/2)
|
||||||
|
; crypto_n_random_bytes(16, SaltBytes)
|
||||||
|
),
|
||||||
|
'$crypto_password_hash'(Password, SaltBytes, Iterations, HashBytes),
|
||||||
|
bytes_base64(HashBytes, HashB64),
|
||||||
|
bytes_base64(SaltBytes, SaltB64),
|
||||||
|
phrase(format_("$pbkdf2-sha512$t=~d$~s$~s", [Iterations,SaltB64,HashB64]), Hash).
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Bidirectional Bytes <-> Base64 conversion *without padding*.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
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)])
|
||||||
|
).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
crypto_data_encrypt(+PlainText,
|
||||||
|
+Algorithm,
|
||||||
|
+Key,
|
||||||
|
+IV,
|
||||||
|
-CipherText,
|
||||||
|
+Options).
|
||||||
|
|
||||||
|
Encrypt the given PlainText, using the symmetric algorithm
|
||||||
|
Algorithm, key Key, and initialization vector (or nonce) IV, to
|
||||||
|
give CipherText.
|
||||||
|
|
||||||
|
PlainText must be a list of characters, Key and IV must be lists of
|
||||||
|
bytes, and CipherText is created as a list of characters.
|
||||||
|
|
||||||
|
Keys and IVs can be chosen at random (using for example
|
||||||
|
crypto_n_random_bytes/2) or derived from input keying material (IKM)
|
||||||
|
using for example crypto_data_hkdf/4. This input is often a shared
|
||||||
|
secret, such as a negotiated point on an elliptic curve, or the hash
|
||||||
|
that was computed from a password via crypto_password_hash/3 with a
|
||||||
|
freshly generated and specified _salt_.
|
||||||
|
|
||||||
|
Reusing the same combination of Key and IV typically leaks at least
|
||||||
|
_some_ information about the plaintext. For example, identical
|
||||||
|
plaintexts will then correspond to identical ciphertexts. For some
|
||||||
|
algorithms, reusing an IV with the same Key has disastrous results
|
||||||
|
and can cause the loss of all properties that are otherwise
|
||||||
|
guaranteed. Especially in such cases, an IV is also called a
|
||||||
|
_nonce_ (number used once).
|
||||||
|
|
||||||
|
It is safe to store and transfer the used initialization vector (or
|
||||||
|
nonce) in plain text, but the key _must be kept secret_.
|
||||||
|
|
||||||
|
Currently, the only supported algorithm is 'chacha20-poly1305', a
|
||||||
|
powerful and efficient _authenticated_ encryption scheme, providing
|
||||||
|
secrecy and at the same time reliable protection against undetected
|
||||||
|
_modifications_ of the encrypted data. This is a very good choice
|
||||||
|
for virtually all use cases. It is a stream cipher and can encrypt
|
||||||
|
data of any length up to 256 GB. Further, the encrypted data has
|
||||||
|
exactly the same length as the original, and no padding is used.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
- encoding(+Encoding)
|
||||||
|
Encoding to use for PlainText. Default is utf8. The alternative
|
||||||
|
is octet to treat PlainText as raw bytes.
|
||||||
|
|
||||||
|
- tag(-List)
|
||||||
|
For authenticated encryption schemes, List is unified with a
|
||||||
|
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_,
|
||||||
|
respectively:
|
||||||
|
|
||||||
|
?- Algorithm = 'chacha20-poly1305',
|
||||||
|
crypto_n_random_bytes(32, Key),
|
||||||
|
crypto_n_random_bytes(12, IV),
|
||||||
|
crypto_data_encrypt("this text is to be encrypted", Algorithm,
|
||||||
|
Key, IV, CipherText, [tag(Tag)]),
|
||||||
|
crypto_data_decrypt(CipherText, Algorithm,
|
||||||
|
Key, IV, RecoveredText, [tag(Tag)]).
|
||||||
|
|
||||||
|
Yielding:
|
||||||
|
|
||||||
|
Algorithm = 'chacha20-poly1305',
|
||||||
|
Key = [113,247,153,134,177,220,13,193,50,150|...],
|
||||||
|
IV = [135,20,149,153,63,35,68,114,247,171|...],
|
||||||
|
CipherText = "\x94\0Ej\x94\®Â\x95\óÑÆXÃn¾ð©b\x1c\ ...",
|
||||||
|
RecoveredText = "this text is to be ...",
|
||||||
|
Tag = [152,117,152,17,162,75,150,206,144,40|...]
|
||||||
|
|
||||||
|
In this example, we use crypto_n_random_bytes/2 to generate a key
|
||||||
|
and nonce from cryptographically secure random numbers. For
|
||||||
|
repeated applications, you must ensure that a nonce is only used
|
||||||
|
_once_ together with the same key. Note that for _authenticated_
|
||||||
|
encryption schemes, the _tag_ that was computed during encryption
|
||||||
|
is necessary for decryption. It is safe to store and transfer the
|
||||||
|
tag in plain text.
|
||||||
|
|
||||||
|
See also crypto_data_decrypt/6, and hex_bytes/2 for conversion
|
||||||
|
between bytes and hex encoding.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :-
|
||||||
|
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)
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
+Algorithm,
|
||||||
|
+Key,
|
||||||
|
+IV,
|
||||||
|
-PlainText,
|
||||||
|
+Options).
|
||||||
|
|
||||||
|
Decrypt the given CipherText, using the symmetric algorithm
|
||||||
|
Algorithm, key Key, and initialization vector IV, to give
|
||||||
|
PlainText. CipherText must be a list of characters, and Key and IV
|
||||||
|
must be lists of bytes. PlainText is created as a list of
|
||||||
|
characters.
|
||||||
|
|
||||||
|
Currently, the only supported algorithm is 'chacha20-poly1305',
|
||||||
|
a very secure, fast and versatile authenticated encryption method.
|
||||||
|
|
||||||
|
Options is a list of:
|
||||||
|
|
||||||
|
- encoding(+Encoding)
|
||||||
|
Encoding to use for PlainText. The default is utf8. The
|
||||||
|
alternative is octet, which is used if the data are raw bytes.
|
||||||
|
|
||||||
|
- 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) :-
|
||||||
|
option(tag(Tag), Options, []),
|
||||||
|
must_be_bytes(Tag, crypto_data_decrypt/6),
|
||||||
|
must_be_bytes(Key, crypto_data_decrypt/6),
|
||||||
|
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]),
|
||||||
|
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)
|
||||||
|
),
|
||||||
|
algorithm_key_iv(Algorithm, Key, IV),
|
||||||
|
'$crypto_data_decrypt'(CipherText, AAD, Key, IV, Encoding, PlainText).
|
||||||
|
|
||||||
|
|
||||||
|
encoding_chars(octet, Bs, Cs) :-
|
||||||
|
must_be(list, Bs),
|
||||||
|
( maplist(integer, Bs) ->
|
||||||
|
maplist(char_code, Cs, Bs)
|
||||||
|
; Bs = Cs
|
||||||
|
),
|
||||||
|
must_be_octet_chars(Cs, crypto_encoding).
|
||||||
|
encoding_chars(utf8, Cs, Cs) :-
|
||||||
|
must_be(chars, Cs).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Digital signatures with Ed25519
|
||||||
|
===============================
|
||||||
|
|
||||||
|
- ed25519_new_keypair(-Pair)
|
||||||
|
Yields a new Ed25519 key pair Pair, a list of characters. The
|
||||||
|
pair contains the private key and must be kept absolutely secret.
|
||||||
|
Pair can be used for signing. Its public key can be obtained
|
||||||
|
with ed25519_keypair_public_key/2.
|
||||||
|
|
||||||
|
- ed25519_keypair_public_key(+Pair, -PublicKey)
|
||||||
|
PublicKey is the public key of the given key pair. The public key
|
||||||
|
can be used for signature verification, and can be shared freely.
|
||||||
|
The public key is represented as a list of characters.
|
||||||
|
|
||||||
|
- ed25519_sign(+Key, +Data, -Signature, +Options)
|
||||||
|
Key and Data must be lists of characters. Key is a key pair in
|
||||||
|
PKCS#8 v2 format as generated by ed25519_new_keypair/1. Sign Data
|
||||||
|
with Key, yielding Signature as a list of hexadecimal characters.
|
||||||
|
|
||||||
|
- ed25519_verify(+Key, +Data, +Signature, +Options)
|
||||||
|
Key and Data must be lists of characters. Key is a public key.
|
||||||
|
Succeeds if Data was signed with the private key corresponding to
|
||||||
|
Key, where Signature is a list of hexadecimal characters as
|
||||||
|
generated by ed25519_sign/4. Fails otherwise.
|
||||||
|
|
||||||
|
Currently, the only option for signing and verifying is:
|
||||||
|
|
||||||
|
- encoding(+Encoding)
|
||||||
|
The default encoding of Data is utf8. The alternative is octet,
|
||||||
|
which treats Data as a list of raw bytes.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
ed25519_new_keypair(Pair) :-
|
||||||
|
'$ed25519_new_keypair'(Pair).
|
||||||
|
|
||||||
|
ed25519_keypair_public_key(Pair, PublicKey) :-
|
||||||
|
must_be_octet_chars(Pair, ed25519_keypair_public_key),
|
||||||
|
'$ed25519_keypair_public_key'(Pair, PublicKey).
|
||||||
|
|
||||||
|
ed25519_sign(Key, Data0, Signature, Options) :-
|
||||||
|
must_be_octet_chars(Key, ed25519_sign),
|
||||||
|
options_data_chars(Options, Data0, Data, Encoding),
|
||||||
|
'$ed25519_sign'(Key, Data, Encoding, Signature0),
|
||||||
|
hex_bytes(Signature, Signature0).
|
||||||
|
|
||||||
|
ed25519_verify(Key, Data0, Signature0, Options) :-
|
||||||
|
must_be_octet_chars(Key, ed25519_verify),
|
||||||
|
options_data_chars(Options, Data0, Data, Encoding),
|
||||||
|
hex_bytes(Signature0, Signature),
|
||||||
|
'$ed25519_verify'(Key, Data, Encoding, Signature).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
X25519: ECDH key exchange over Curve25519
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
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.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
curve25519_generator(Gs) :-
|
||||||
|
length(Gs0, 32),
|
||||||
|
Gs0 = [9|Zs],
|
||||||
|
maplist(=(0), Zs),
|
||||||
|
maplist(char_code, Gs, Gs0).
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
bytes_integer(Bs, N) :-
|
||||||
|
foldl(pow, Bs, t(0,0,N), t(N,_,_)).
|
||||||
|
|
||||||
|
pow(B, t(N0,P0,I0), t(N,P,I)) :-
|
||||||
|
( integer(I0) ->
|
||||||
|
B #= I0 mod 256,
|
||||||
|
I #= I0 >> 8
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
B in 0..255,
|
||||||
|
N #= N0 + B*256^P0,
|
||||||
|
P #= P0 + 1.
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Operations on Elliptic Curves
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Sample use: Establishing a shared secret S, using ECDH key exchange.
|
||||||
|
|
||||||
|
?- crypto_name_curve(secp256k1, C),
|
||||||
|
crypto_curve_generator(C, Generator),
|
||||||
|
PrivateKey = 10,
|
||||||
|
crypto_curve_scalar_mult(C, PrivateKey, Generator, PublicKey),
|
||||||
|
Random = 12,
|
||||||
|
crypto_curve_scalar_mult(C, Random, Generator, R),
|
||||||
|
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(Name,P,A,B,point(X,Y),Order,FieldLength,Cofactor).
|
||||||
|
|
||||||
|
First, we define suitable accessors.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
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_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]), PointHex),
|
||||||
|
hex_bytes(PointHex, PointBytes),
|
||||||
|
once(bytes_integer(ScalarBytes, Scalar)),
|
||||||
|
'$crypto_curve_scalar_mult'(Name, ScalarBytes, PointBytes, [_|Us]),
|
||||||
|
maplist(char_code, Us, Bs),
|
||||||
|
length(XBs, 32),
|
||||||
|
append(XBs, YBs, Bs),
|
||||||
|
maplist(reverse, [XBs,YBs], RBs),
|
||||||
|
maplist(bytes_integer, RBs, [RX,RY]).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
?- crypto_name_curve(secp256k1, Curve),
|
||||||
|
crypto_curve_generator(Curve, G),
|
||||||
|
crypto_curve_scalar_mult(Curve, 2, G, R).
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Validation.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
curve_contains_point(Curve, point(QX,QY)) :-
|
||||||
|
curve_a(Curve, A),
|
||||||
|
curve_b(Curve, B),
|
||||||
|
curve_p(Curve, P),
|
||||||
|
QY^2 mod P #= (QX^3 + A*QX + B) mod P.
|
||||||
|
|
||||||
|
must_be_on_curve(Curve, P) :-
|
||||||
|
\+ curve_contains_point(Curve, P),
|
||||||
|
domain_error(point_on_curve, P, crypto_elliptic_curves).
|
||||||
|
must_be_on_curve(Curve, P) :- curve_contains_point(Curve, P).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Predefined curves
|
||||||
|
=================
|
||||||
|
|
||||||
|
List available curves:
|
||||||
|
|
||||||
|
$ openssl ecparam -list_curves
|
||||||
|
|
||||||
|
Show curve parameters for secp256k1:
|
||||||
|
|
||||||
|
$ openssl ecparam -param_enc explicit -conv_form uncompressed \
|
||||||
|
-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(secp256k1,
|
||||||
|
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, []).
|
||||||
@@ -1,50 +1,41 @@
|
|||||||
:- module(dcgs, [op(1200, xfx, -->),
|
:- module(dcgs,
|
||||||
op(1105, xfy, '|'),
|
[op(1105, xfy, '|'),
|
||||||
phrase/2,
|
phrase/2,
|
||||||
phrase/3]).
|
phrase/3,
|
||||||
|
seq//1,
|
||||||
|
seqq//1,
|
||||||
|
... //0
|
||||||
|
]).
|
||||||
|
|
||||||
:- use_module(library(error)).
|
:- use_module(library(error)).
|
||||||
:- use_module(library(lists), [append/3]).
|
:- use_module(library(iso_ext)).
|
||||||
|
:- use_module(library(lists), [append/3, member/2]).
|
||||||
|
:- use_module(library(loader), [strip_module/3]).
|
||||||
|
|
||||||
user:term_expansion(Term0, Term) :-
|
:- meta_predicate phrase(2, ?).
|
||||||
nonvar(Term0),
|
|
||||||
dcg_rule(Term0, (Head :- Body)),
|
:- meta_predicate phrase(2, ?, ?).
|
||||||
Term = (Head :- Body).
|
|
||||||
|
|
||||||
phrase(GRBody, S0) :-
|
phrase(GRBody, S0) :-
|
||||||
phrase(GRBody, S0, []).
|
phrase(GRBody, S0, []).
|
||||||
|
|
||||||
phrase(GRBody, S0, S) :-
|
phrase(GRBody, S0, S) :-
|
||||||
( var(GRBody) -> throw(error(instantiation_error, phrase/3))
|
strip_module(GRBody, M, GRBody1),
|
||||||
; dcg_constr(GRBody) -> phrase_(GRBody, S0, S)
|
( var(GRBody) ->
|
||||||
; functor(GRBody, _, _) -> call(GRBody, S0, S)
|
instantiation_error(phrase/3)
|
||||||
; throw(error(type_error(callable, GRBody), phrase/3))
|
; nonvar(GRBody1),
|
||||||
|
dcg_constr(GRBody1),
|
||||||
|
dcg_body(GRBody1, S0, S, GRBody2) ->
|
||||||
|
call(M:GRBody2)
|
||||||
|
; call(M:GRBody1, S0, S)
|
||||||
).
|
).
|
||||||
|
|
||||||
phrase_([], S, S).
|
|
||||||
phrase_(!, S, S).
|
module_call_qualified(M, Call, Call1) :-
|
||||||
phrase_((A, B), S0, S) :-
|
( nonvar(M) -> Call1 = M:Call
|
||||||
phrase(A, S0, S1), phrase(B, S1, S).
|
; Call = Call1
|
||||||
phrase_((A -> B ; C), S0, S) :-
|
|
||||||
!,
|
|
||||||
( phrase(A, S0, S1) ->
|
|
||||||
phrase(B, S1, S)
|
|
||||||
; phrase(C, S0, S)
|
|
||||||
).
|
).
|
||||||
phrase_((A ; B), S0, S) :-
|
|
||||||
( phrase(A, S0, S) ; phrase(B, S0, S) ).
|
|
||||||
phrase_((A | B), S0, S) :-
|
|
||||||
( phrase(A, S0, S) ; phrase(B, S0, S) ).
|
|
||||||
phrase_({G}, S0, S) :-
|
|
||||||
( call(G), S0 = S ).
|
|
||||||
phrase_(call(G), S0, S) :-
|
|
||||||
call(G, S0, S).
|
|
||||||
phrase_((A -> B), S0, S) :-
|
|
||||||
phrase((A -> B ; fail), S0, S).
|
|
||||||
phrase_(phrase(NonTerminal), S0, S) :-
|
|
||||||
phrase(NonTerminal, S0, S).
|
|
||||||
phrase_([T|Ts], S0, S) :-
|
|
||||||
append([T|Ts], S, S0).
|
|
||||||
|
|
||||||
% The same version of the below two dcg_rule clauses, but with module scoping.
|
% The same version of the below two dcg_rule clauses, but with module scoping.
|
||||||
dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :-
|
dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :-
|
||||||
@@ -84,12 +75,14 @@ dcg_body(GRBody, S0, S, Body) :-
|
|||||||
nonvar(GRBody),
|
nonvar(GRBody),
|
||||||
dcg_constr(GRBody),
|
dcg_constr(GRBody),
|
||||||
dcg_cbody(GRBody, S0, S, Body).
|
dcg_cbody(GRBody, S0, S, Body).
|
||||||
dcg_body(NonTerminal, S0, S, Goal) :-
|
dcg_body(NonTerminal, S0, S, Goal1) :-
|
||||||
nonvar(NonTerminal),
|
nonvar(NonTerminal),
|
||||||
\+ dcg_constr(NonTerminal),
|
\+ dcg_constr(NonTerminal),
|
||||||
NonTerminal \= ( _ -> _ ),
|
NonTerminal \= ( _ -> _ ),
|
||||||
NonTerminal \= ( \+ _ ),
|
NonTerminal \= ( \+ _ ),
|
||||||
dcg_non_terminal(NonTerminal, S0, S, Goal).
|
loader:strip_module(NonTerminal, M, NonTerminal0),
|
||||||
|
dcg_non_terminal(NonTerminal0, S0, S, Goal0),
|
||||||
|
module_call_qualified(M, Goal0, Goal1).
|
||||||
|
|
||||||
% The following constructs in a grammar rule body
|
% The following constructs in a grammar rule body
|
||||||
% are defined in the corresponding subclauses.
|
% are defined in the corresponding subclauses.
|
||||||
@@ -133,3 +126,43 @@ dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )).
|
|||||||
dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :-
|
dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :-
|
||||||
dcg_body(GRIf, S0, S1, If),
|
dcg_body(GRIf, S0, S1, If),
|
||||||
dcg_body(GRThen, S1, S, Then).
|
dcg_body(GRThen, S1, S, Then).
|
||||||
|
|
||||||
|
user:term_expansion(Term0, Term) :-
|
||||||
|
nonvar(Term0),
|
||||||
|
dcg_rule(Term0, Term).
|
||||||
|
|
||||||
|
% Describes a sequence
|
||||||
|
seq(Xs, Cs0,Cs) :-
|
||||||
|
var(Xs),
|
||||||
|
Cs0 == [],
|
||||||
|
!,
|
||||||
|
Xs = [],
|
||||||
|
Cs0 = Cs.
|
||||||
|
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
|
||||||
|
...(Cs0,Cs) :-
|
||||||
|
Cs0 == [],
|
||||||
|
!,
|
||||||
|
Cs0 = Cs.
|
||||||
|
... --> [] | [_], ... .
|
||||||
|
|
||||||
|
error_goal(error(E, must_be/2), error(E, must_be/2)).
|
||||||
|
error_goal(error(E, (=..)/2), error(E, (=..)/2)).
|
||||||
|
error_goal(E, _) :- throw(E).
|
||||||
|
|
||||||
|
user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
|
||||||
|
loader:strip_module(GRBody, M, GRBody0),
|
||||||
|
nonvar(GRBody0),
|
||||||
|
catch(dcgs:dcg_body(GRBody0, S, S0, GRBody1),
|
||||||
|
E,
|
||||||
|
dcgs:error_goal(E, GRBody1)
|
||||||
|
),
|
||||||
|
module_call_qualified(M, GRBody1, GRBody2).
|
||||||
|
|
||||||
|
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) :-
|
put_dif_att(Var, X, Y) :-
|
||||||
( get_atts(Var, +dif(Z)) ->
|
( get_atts(Var, +dif(Z)) ->
|
||||||
sort([X \== Y | Z], NewZ),
|
sort([X \== Y | Z], NewZ),
|
||||||
put_atts(Var, +dif(NewZ))
|
put_atts(Var, +dif(NewZ))
|
||||||
; put_atts(Var, +dif([X \== Y]))
|
; put_atts(Var, +dif([X \== Y]))
|
||||||
).
|
).
|
||||||
|
|
||||||
@@ -21,8 +21,8 @@ dif_set_variables([Var|Vars], X, Y) :-
|
|||||||
append_goals([], _).
|
append_goals([], _).
|
||||||
append_goals([Var|Vars], Goals) :-
|
append_goals([Var|Vars], Goals) :-
|
||||||
( get_atts(Var, +dif(VarGoals)) ->
|
( get_atts(Var, +dif(VarGoals)) ->
|
||||||
append(Goals, VarGoals, NewGoals0),
|
append(Goals, VarGoals, NewGoals0),
|
||||||
sort(NewGoals0, NewGoals)
|
sort(NewGoals0, NewGoals)
|
||||||
; NewGoals = Goals
|
; NewGoals = Goals
|
||||||
),
|
),
|
||||||
put_atts(Var, +dif(NewGoals)),
|
put_atts(Var, +dif(NewGoals)),
|
||||||
@@ -30,23 +30,27 @@ append_goals([Var|Vars], Goals) :-
|
|||||||
|
|
||||||
verify_attributes(Var, Value, Goals) :-
|
verify_attributes(Var, Value, Goals) :-
|
||||||
( get_atts(Var, +dif(Goals)) ->
|
( get_atts(Var, +dif(Goals)) ->
|
||||||
term_variables(Value, ValueVars),
|
term_variables(Value, ValueVars),
|
||||||
append_goals(ValueVars, Goals)
|
append_goals(ValueVars, Goals)
|
||||||
; Goals = []
|
; Goals = []
|
||||||
).
|
).
|
||||||
|
|
||||||
% Probably the world's worst dif/2 implementation. I'm open to
|
% Probably the world's worst dif/2 implementation. I'm open to
|
||||||
% suggestions for improvement.
|
% suggestions for improvement.
|
||||||
|
|
||||||
dif(X, Y) :- X \== Y,
|
dif(X, Y) :-
|
||||||
( term_variables(X, XVars), term_variables(Y, YVars),
|
X \== Y,
|
||||||
dif_set_variables(XVars, X, Y),
|
( X \= Y -> true
|
||||||
dif_set_variables(YVars, X, Y)
|
; ( 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([]) --> [].
|
||||||
gather_dif_goals([(X \== Y) | Goals]) -->
|
gather_dif_goals([(X \== Y) | Goals]) -->
|
||||||
[dif(X, Y)],
|
[dif:dif(X, Y)],
|
||||||
gather_dif_goals(Goals).
|
gather_dif_goals(Goals).
|
||||||
|
|
||||||
attribute_goals(X) -->
|
attribute_goals(X) -->
|
||||||
224
src/lib/error.pl
Normal file
224
src/lib/error.pl
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Written 2018-2022 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,
|
||||||
|
domain_error/3,
|
||||||
|
type_error/3
|
||||||
|
]).
|
||||||
|
|
||||||
|
|
||||||
|
:- meta_predicate check_(1, ?, ?).
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
must_be(Type, Term)
|
||||||
|
|
||||||
|
This predicate is intended for type-checks of built-in predicates.
|
||||||
|
|
||||||
|
It asserts that Term is:
|
||||||
|
|
||||||
|
1) instantiated *and*
|
||||||
|
2) instantiated to an instance of the given Type.
|
||||||
|
|
||||||
|
It corresponds to usage mode +Term.
|
||||||
|
|
||||||
|
Currently, the following types are supported:
|
||||||
|
|
||||||
|
- atom
|
||||||
|
- boolean
|
||||||
|
- character
|
||||||
|
- chars
|
||||||
|
- in_character
|
||||||
|
- integer
|
||||||
|
- list
|
||||||
|
- octet_character
|
||||||
|
- octet_chars
|
||||||
|
- term
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
must_be(Type, Term) :-
|
||||||
|
must_be_(type, Type),
|
||||||
|
must_be_(Type, Term).
|
||||||
|
|
||||||
|
must_be_(Type, _) :-
|
||||||
|
var(Type),
|
||||||
|
instantiation_error(must_be/2).
|
||||||
|
must_be_(var, Term) :-
|
||||||
|
( var(Term) -> true
|
||||||
|
; throw(error(uninstantiation_error(Term), must_be/2))
|
||||||
|
).
|
||||||
|
must_be_(integer, Term) :- check_(integer, integer, Term).
|
||||||
|
must_be_(not_less_than_zero, N) :-
|
||||||
|
must_be(integer, N),
|
||||||
|
( N >= 0 -> true
|
||||||
|
; domain_error(not_less_than_zero, N, must_be/2)
|
||||||
|
).
|
||||||
|
must_be_(atom, Term) :- check_(atom, atom, Term).
|
||||||
|
must_be_(character, T) :- check_(error:character, character, T).
|
||||||
|
must_be_(in_character, T) :- check_(error:in_character, in_character, T).
|
||||||
|
must_be_(chars, Ls) :-
|
||||||
|
can_be(chars, Ls), % prioritize type errors over instantiation errors
|
||||||
|
must_be(list, Ls),
|
||||||
|
( '$is_partial_string'(Ls) ->
|
||||||
|
% The expected case (success) uses a very fast test.
|
||||||
|
% We cannot use partial_string/1 from library(iso_ext),
|
||||||
|
% because that library itself imports library(error).
|
||||||
|
true
|
||||||
|
; all_characters(Ls)
|
||||||
|
).
|
||||||
|
must_be_(octet_character, C) :-
|
||||||
|
must_be(character, C),
|
||||||
|
( octet_character(C) -> true
|
||||||
|
; domain_error(octet_character, C, must_be/2)
|
||||||
|
).
|
||||||
|
must_be_(octet_chars, Cs) :-
|
||||||
|
must_be(chars, Cs),
|
||||||
|
( '$first_non_octet'(Cs, C) ->
|
||||||
|
domain_error(octet_character, C, must_be/2)
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
must_be_(list, Term) :- check_(error:ilist, list, Term).
|
||||||
|
must_be_(type, Term) :- check_(error:type, type, Term).
|
||||||
|
must_be_(boolean, Term) :- check_(error:boolean, boolean, Term).
|
||||||
|
must_be_(term, Term) :-
|
||||||
|
( \+ ground(Term) ->
|
||||||
|
instantiation_error(must_be/2)
|
||||||
|
; \+ acyclic_term(Term) ->
|
||||||
|
type_error(term, Term, must_be/2)
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
% We cannot use maplist(must_be(character), Cs), because library(lists)
|
||||||
|
% uses library(error), so importing it would create a cyclic dependency.
|
||||||
|
|
||||||
|
all_characters([]).
|
||||||
|
all_characters([C|Cs]) :-
|
||||||
|
must_be(character, C),
|
||||||
|
all_characters(Cs).
|
||||||
|
|
||||||
|
check_(Pred, Type, Term) :-
|
||||||
|
( var(Term) -> instantiation_error(must_be/2)
|
||||||
|
; call(Pred, Term) -> true
|
||||||
|
; type_error(Type, Term, must_be/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
boolean(B) :- ( B == true ; B == false ).
|
||||||
|
|
||||||
|
character(C) :-
|
||||||
|
atom(C),
|
||||||
|
atom_length(C, 1).
|
||||||
|
|
||||||
|
octet_character(C) :-
|
||||||
|
char_code(C, Code),
|
||||||
|
0 =< Code, Code =< 0xff.
|
||||||
|
|
||||||
|
in_character(C) :-
|
||||||
|
( character(C)
|
||||||
|
; C == end_of_file
|
||||||
|
).
|
||||||
|
|
||||||
|
ilist(Ls) :-
|
||||||
|
'$skip_max_list'(_, _, Ls, Rs),
|
||||||
|
( var(Rs) ->
|
||||||
|
instantiation_error(must_be/2)
|
||||||
|
; Rs == []
|
||||||
|
).
|
||||||
|
|
||||||
|
type(type).
|
||||||
|
type(integer).
|
||||||
|
type(atom).
|
||||||
|
type(character).
|
||||||
|
type(in_character).
|
||||||
|
type(octet_character).
|
||||||
|
type(octet_chars).
|
||||||
|
type(chars).
|
||||||
|
type(list).
|
||||||
|
type(var).
|
||||||
|
type(boolean).
|
||||||
|
type(term).
|
||||||
|
type(not_less_than_zero).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
can_be(Type, Term)
|
||||||
|
|
||||||
|
This predicate is intended for type-checks of built-in predicates.
|
||||||
|
|
||||||
|
It asserts that there is a substitution which, if applied to Term,
|
||||||
|
makes it an instance of Type.
|
||||||
|
|
||||||
|
It corresponds to usage mode ?Term.
|
||||||
|
|
||||||
|
It supports the same types as must_be/2.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
|
||||||
|
can_be(Type, Term) :-
|
||||||
|
must_be(type, Type),
|
||||||
|
( var(Term) -> true
|
||||||
|
; can_(Type, Term) -> true
|
||||||
|
; type_error(Type, Term, can_be/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
can_(integer, Term) :- integer(Term).
|
||||||
|
can_(not_less_than_zero, N) :-
|
||||||
|
( integer(N) ->
|
||||||
|
( N >= 0 -> true
|
||||||
|
; domain_error(not_less_than_zero, N, can_be/2)
|
||||||
|
)
|
||||||
|
; type_error(integer, N, can_be/2)
|
||||||
|
).
|
||||||
|
can_(atom, Term) :- atom(Term).
|
||||||
|
can_(character, T) :- character(T).
|
||||||
|
can_(in_character, T) :- in_character(T).
|
||||||
|
can_(chars, Ls) :-
|
||||||
|
( '$is_partial_string'(Ls) -> true
|
||||||
|
; can_be(list, Ls),
|
||||||
|
can_be_chars(Ls)
|
||||||
|
).
|
||||||
|
can_(octet_character, C) :-
|
||||||
|
( octet_character(C) -> true
|
||||||
|
; domain_error(octet_character, C, can_be/2)
|
||||||
|
).
|
||||||
|
can_(octet_chars, Cs) :-
|
||||||
|
can_be(chars, Cs),
|
||||||
|
( '$skip_max_list'(_, _, Cs, []), % temporarily turn Cs into a list
|
||||||
|
'$first_non_octet'(Cs, C) ->
|
||||||
|
domain_error(octet_character, C, can_be/2)
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
can_(list, Term) :- list_or_partial_list(Term).
|
||||||
|
can_(boolean, Term) :- boolean(Term).
|
||||||
|
can_(term, Term) :-
|
||||||
|
( acyclic_term(Term) ->
|
||||||
|
true
|
||||||
|
; type_error(term, Term, can_be/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
can_be_chars(Var) :- var(Var), !.
|
||||||
|
can_be_chars([]).
|
||||||
|
can_be_chars([X|Xs]) :-
|
||||||
|
can_be(character, X),
|
||||||
|
can_be_chars(Xs).
|
||||||
|
|
||||||
|
list_or_partial_list(Ls) :-
|
||||||
|
'$skip_max_list'(_, _, Ls, Rs),
|
||||||
|
( var(Rs) -> true
|
||||||
|
; Rs == []
|
||||||
|
).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Shorthands for throwing ISO errors.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
instantiation_error(Context) :-
|
||||||
|
throw(error(instantiation_error, Context)).
|
||||||
|
|
||||||
|
domain_error(Type, Term, Context) :-
|
||||||
|
throw(error(domain_error(Type, Term), Context)).
|
||||||
|
|
||||||
|
type_error(Type, Term, Context) :-
|
||||||
|
throw(error(type_error(Type, Term), Context)).
|
||||||
221
src/lib/files.pl
Normal file
221
src/lib/files.pl
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Written 2020, 2022 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)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
|
||||||
|
directory_files(Directory, Files) :-
|
||||||
|
must_be(chars, Directory),
|
||||||
|
can_be(list, Files),
|
||||||
|
'$directory_files'(Directory, Files).
|
||||||
|
|
||||||
|
file_size(File, Size) :-
|
||||||
|
file_must_exist(File, file_size/2),
|
||||||
|
can_be(integer, Size),
|
||||||
|
'$file_size'(File, Size).
|
||||||
|
|
||||||
|
file_exists(File) :-
|
||||||
|
must_be(chars, File),
|
||||||
|
'$file_exists'(File).
|
||||||
|
|
||||||
|
directory_exists(Directory) :-
|
||||||
|
must_be(chars, Directory),
|
||||||
|
'$directory_exists'(Directory).
|
||||||
|
|
||||||
|
make_directory(Directory) :-
|
||||||
|
must_be(chars, Directory),
|
||||||
|
'$make_directory'(Directory).
|
||||||
|
|
||||||
|
make_directory_path(Directory) :-
|
||||||
|
must_be(chars, Directory),
|
||||||
|
'$make_directory_path'(Directory).
|
||||||
|
|
||||||
|
delete_file(File) :-
|
||||||
|
file_must_exist(File, delete_file/1),
|
||||||
|
'$delete_file'(File).
|
||||||
|
|
||||||
|
rename_file(File, Renamed) :-
|
||||||
|
file_must_exist(File, rename_file/2),
|
||||||
|
must_be(chars, Renamed),
|
||||||
|
'$rename_file'(File, Renamed).
|
||||||
|
|
||||||
|
delete_directory(Directory) :-
|
||||||
|
directory_must_exist(Directory, delete_directory/1),
|
||||||
|
must_be(chars, Directory),
|
||||||
|
'$delete_directory'(Directory).
|
||||||
|
|
||||||
|
file_must_exist(File, Context) :-
|
||||||
|
( file_exists(File) -> true
|
||||||
|
; throw(error(existence_error(file, File), Context))
|
||||||
|
).
|
||||||
|
|
||||||
|
directory_must_exist(Directory, Context) :-
|
||||||
|
( directory_exists(Directory) -> true
|
||||||
|
; throw(error(existence_error(directory, Directory), Context))
|
||||||
|
).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
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(chars, Ps),
|
||||||
|
can_be(list, Cs),
|
||||||
|
'$path_canonical'(Ps, Cs).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
T is, respectively, the modification, access or creation time of File.
|
||||||
|
T is a time stamp, suitable for use in format_time//2 in library(time).
|
||||||
|
|
||||||
|
For two time stamps A and B, if A precedes B, then A @< B holds.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
file_modification_time(File, T) :-
|
||||||
|
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(must_be(chars), Segments),
|
||||||
|
phrase(append_with_separator(Segments, Sep), Path)
|
||||||
|
; must_be(chars, Path),
|
||||||
|
path_to_segments(Path, Sep, Segments)
|
||||||
|
).
|
||||||
|
|
||||||
|
append_with_separator([], _) --> [].
|
||||||
|
append_with_separator([Segment|Segments], Sep) -->
|
||||||
|
append_with_separator_(Segments, Segment, Sep).
|
||||||
|
|
||||||
|
append_with_separator_([], Segment, _) --> seq(Segment).
|
||||||
|
append_with_separator_([Segment|Segments], Prev, Sep) -->
|
||||||
|
seq(Prev), [Sep],
|
||||||
|
append_with_separator_(Segments, Segment, Sep).
|
||||||
|
|
||||||
|
path_to_segments(Path, Sep, Segments) :-
|
||||||
|
( append(Front, [Sep|Ps], Path) ->
|
||||||
|
Segments = [Front|Rest],
|
||||||
|
path_to_segments(Ps, Sep, Rest)
|
||||||
|
; Segments = [Path]
|
||||||
|
).
|
||||||
636
src/lib/format.pl
Normal file
636
src/lib/format.pl
Normal file
@@ -0,0 +1,636 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
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,3] are provided for impure output.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
======
|
||||||
|
|
||||||
|
phrase(format_(FormatString, Arguments), Ls)
|
||||||
|
|
||||||
|
format_//2 describes a list of characters Ls that are formatted
|
||||||
|
according to FormatString. FormatString is a string (i.e.,
|
||||||
|
a list of characters) that specifies the layout of Ls.
|
||||||
|
The characters in FormatString are used literally, except
|
||||||
|
for the following tokens with special meaning:
|
||||||
|
|
||||||
|
~w use the next available argument from Arguments here
|
||||||
|
~q use the next argument here, formatted as by writeq/1
|
||||||
|
~a use the next argument here, which must be an atom
|
||||||
|
~s use the next argument here, which must be a string
|
||||||
|
~d use the next argument here, which must be an integer
|
||||||
|
~f use the next argument here, a floating point number
|
||||||
|
~Nf where N is an integer: format the float argument
|
||||||
|
using N digits after the decimal point
|
||||||
|
~Nd like ~d, placing the last N digits after a decimal point;
|
||||||
|
if N is 0 or omitted, no decimal point is used.
|
||||||
|
~ND like ~Nd, separating digits to the left of the decimal point
|
||||||
|
in groups of three, using the character "," (comma)
|
||||||
|
~NU like ~ND, using "_" (underscore) to separate groups of digits
|
||||||
|
~NL format an integer so that at most N digits appear on a line.
|
||||||
|
If N is 0 or omitted, it defaults to 72.
|
||||||
|
~Nr where N is an integer between 2 and 36: format the
|
||||||
|
next argument, which must be an integer, in radix N.
|
||||||
|
The characters "a" to "z" are used for radices 10 to 36.
|
||||||
|
If N is omitted, it defaults to 8 (octal).
|
||||||
|
~NR like ~Nr, except that "A" to "Z" are used for radices > 9
|
||||||
|
~| place a tab stop at this position
|
||||||
|
~N| where N is an integer: place a tab stop at text column N
|
||||||
|
~N+ where N is an integer: place a tab stop N characters
|
||||||
|
after the previous tab stop (or start of line)
|
||||||
|
~t distribute spaces evenly between the two closest tab stops
|
||||||
|
~`Ct like ~t, use character C instead of spaces to fill the space
|
||||||
|
~n newline
|
||||||
|
~Nn N newlines
|
||||||
|
~i ignore the next argument
|
||||||
|
~~ the literal ~
|
||||||
|
|
||||||
|
Instead of ~N, you can write ~* to use the next argument from Arguments
|
||||||
|
as the numeric argument.
|
||||||
|
|
||||||
|
The predicate format/2 is like format_//2, except that it outputs
|
||||||
|
the text on the terminal instead of describing it declaratively.
|
||||||
|
|
||||||
|
format/3, used as format(Stream, FormatString, Arguments), outputs
|
||||||
|
the described string to the given Stream. If Stream is a binary
|
||||||
|
stream, then the code of each emitted character must be in 0..255.
|
||||||
|
|
||||||
|
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(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
|
||||||
|
also stay that way, to encourage a sensible environment.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs).
|
||||||
|
%@ Cs = "hello\n......there!".
|
||||||
|
|
||||||
|
I place this code in the public domain. Use it in any way you want.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
:- module(format, [format_//2,
|
||||||
|
format/2,
|
||||||
|
format/3,
|
||||||
|
portray_clause_//1,
|
||||||
|
portray_clause/1,
|
||||||
|
portray_clause/2,
|
||||||
|
listing/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(charsio)).
|
||||||
|
:- use_module(library(between)).
|
||||||
|
:- use_module(library(pio)).
|
||||||
|
|
||||||
|
format_(Fs, Args) -->
|
||||||
|
{ must_be(list, Fs),
|
||||||
|
must_be(list, Args),
|
||||||
|
unique_variable_names(Args, VNs),
|
||||||
|
phrase(cells(Fs,Args,0,[],VNs), Cells) },
|
||||||
|
format_cells(Cells).
|
||||||
|
|
||||||
|
format_cells([]) --> [].
|
||||||
|
format_cells([Cell|Cells]) -->
|
||||||
|
format_cell(Cell),
|
||||||
|
format_cells(Cells).
|
||||||
|
|
||||||
|
format_cell(newline) --> "\n".
|
||||||
|
format_cell(cell(From,To,Es)) -->
|
||||||
|
% distribute the space between the glue elements
|
||||||
|
{ phrase(elements_gluevars(Es, 0, Length), Vs),
|
||||||
|
( Vs = [] -> true
|
||||||
|
; Space is To - From - Length,
|
||||||
|
( Space =< 0 -> maplist(=(0), Vs)
|
||||||
|
; length(Vs, NumGlue),
|
||||||
|
Distr is Space // NumGlue,
|
||||||
|
Delta is Space - Distr*NumGlue,
|
||||||
|
( Delta =:= 0 ->
|
||||||
|
maplist(=(Distr), Vs)
|
||||||
|
; BigGlue is Distr + Delta,
|
||||||
|
reverse(Vs, [BigGlue|Rest]),
|
||||||
|
maplist(=(Distr), Rest)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) },
|
||||||
|
format_elements(Es).
|
||||||
|
|
||||||
|
format_elements([]) --> [].
|
||||||
|
format_elements([E|Es]) -->
|
||||||
|
format_element(E),
|
||||||
|
format_elements(Es).
|
||||||
|
|
||||||
|
format_element(chars(Cs)) --> seq(Cs).
|
||||||
|
format_element(glue(Fill,Num)) -->
|
||||||
|
{ length(Ls, Num),
|
||||||
|
maplist(=(Fill), Ls) },
|
||||||
|
seq(Ls).
|
||||||
|
|
||||||
|
elements_gluevars([], N, N) --> [].
|
||||||
|
elements_gluevars([E|Es], N0, N) -->
|
||||||
|
element_gluevar(E, N0, N1),
|
||||||
|
elements_gluevars(Es, N1, N).
|
||||||
|
|
||||||
|
element_gluevar(chars(Cs), N0, N) -->
|
||||||
|
{ length(Cs, L),
|
||||||
|
N is N0 + L }.
|
||||||
|
element_gluevar(glue(_,V), N, N) --> [V].
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Our key datastructure is a list of cells and newlines.
|
||||||
|
A cell has the shape cell(From,To,Elements), where
|
||||||
|
From and To denote the positions of surrounding tab stops.
|
||||||
|
|
||||||
|
Elements is a list of elements that occur in a cell,
|
||||||
|
namely terms of the form chars(Cs) and glue(Char, Var).
|
||||||
|
"glue" elements (TeX terminology) are evenly stretched
|
||||||
|
to fill the remaining whitespace in the cell. For each
|
||||||
|
glue element, the character Char is used for filling,
|
||||||
|
and Var is a free variable that is used when the
|
||||||
|
available space is distributed.
|
||||||
|
|
||||||
|
newline is used if ~n occurs in a format string.
|
||||||
|
It is used because a newline character does not
|
||||||
|
consume whitespace in the sense of format strings.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
cells([], Args, Tab, Es, _) --> !,
|
||||||
|
( { Args == [] } -> cell(Tab, Tab, Es)
|
||||||
|
; { domain_error(empty_list, Args, format_//2) }
|
||||||
|
).
|
||||||
|
cells([~,~|Fs], Args, Tab, Es, VNs) --> !,
|
||||||
|
cells(Fs, Args, Tab, [chars("~")|Es], VNs).
|
||||||
|
cells([~,w|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||||
|
{ 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], VNs).
|
||||||
|
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||||
|
{ numeric_argument(Fs0, Num, [d|Fs], Args0, [Arg0|Args]) },
|
||||||
|
!,
|
||||||
|
{ Arg is Arg0, % evaluate compound expression
|
||||||
|
must_be(integer, Arg),
|
||||||
|
number_chars(Arg, Cs0) },
|
||||||
|
( { Num =:= 0 } -> { Cs = Cs0 }
|
||||||
|
; { length(Cs0, L),
|
||||||
|
( L =< Num ->
|
||||||
|
Delta is Num - L,
|
||||||
|
length(Zs, Delta),
|
||||||
|
maplist(=('0'), Zs),
|
||||||
|
phrase(("0.",seq(Zs),seq(Cs0)), Cs)
|
||||||
|
; BeforeComma is L - Num,
|
||||||
|
length(Bs, BeforeComma),
|
||||||
|
append(Bs, Ds, Cs0),
|
||||||
|
phrase((seq(Bs),".",seq(Ds)), Cs)
|
||||||
|
) }
|
||||||
|
),
|
||||||
|
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||||
|
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||||
|
{ numeric_argument(Fs0, Num, ['D'|Fs], Args0, [Arg|Args]) },
|
||||||
|
!,
|
||||||
|
{ separate_digits_fractional(Arg, ',', Num, Cs) },
|
||||||
|
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||||
|
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, [], VNs).
|
||||||
|
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||||
|
{ numeric_argument(Fs0, Num, [n|Fs], Args0, Args) },
|
||||||
|
!,
|
||||||
|
cell(Tab, Tab, Es),
|
||||||
|
n_newlines(Num),
|
||||||
|
cells(Fs, Args, 0, [], VNs).
|
||||||
|
cells([~,s|Fs], [Arg|Args], Tab, Es, VNs) --> !,
|
||||||
|
cells(Fs, Args, Tab, [chars(Arg)|Es], VNs).
|
||||||
|
cells([~,f|Fs], [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]) },
|
||||||
|
!,
|
||||||
|
{ format_number_chars(Arg, Cs0),
|
||||||
|
phrase(upto_what(Bs, .), Cs0, Cs),
|
||||||
|
( Num =:= 0 -> Chars = Bs
|
||||||
|
; ( Cs = ['.'|Rest] ->
|
||||||
|
length(Rest, L),
|
||||||
|
( Num < L ->
|
||||||
|
length(Ds, Num),
|
||||||
|
append(Ds, _, Rest)
|
||||||
|
; Num =:= L ->
|
||||||
|
Ds = Rest
|
||||||
|
; Num > L,
|
||||||
|
Delta is Num - L,
|
||||||
|
% we should look into the float with
|
||||||
|
% greater accuracy here, and use the
|
||||||
|
% actual digits instead of 0.
|
||||||
|
length(Zs, Delta),
|
||||||
|
maplist(=('0'), Zs),
|
||||||
|
append(Rest, Zs, Ds)
|
||||||
|
)
|
||||||
|
; length(Ds, Num),
|
||||||
|
maplist(=('0'), Ds)
|
||||||
|
),
|
||||||
|
append(Bs, ['.'|Ds], Chars)
|
||||||
|
) },
|
||||||
|
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
|
||||||
|
cells([~,r|Fs], Args, Tab, Es, VNs) --> !,
|
||||||
|
cells([~,'8',r|Fs], Args, Tab, Es, VNs).
|
||||||
|
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||||
|
{ numeric_argument(Fs0, Num, [r|Fs], Args0, [Arg|Args]) },
|
||||||
|
!,
|
||||||
|
{ integer_to_radix(Arg, Num, lowercase, Cs) },
|
||||||
|
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||||
|
cells([~,'R'|Fs], Args, Tab, Es, VNs) --> !,
|
||||||
|
cells([~,'8','R'|Fs], Args, Tab, Es, VNs).
|
||||||
|
cells([~|Fs0], Args0, Tab, Es, VNs) -->
|
||||||
|
{ numeric_argument(Fs0, Num, ['R'|Fs], Args0, [Arg|Args]) },
|
||||||
|
!,
|
||||||
|
{ integer_to_radix(Arg, Num, uppercase, Cs) },
|
||||||
|
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
|
||||||
|
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, [], 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, [], VNs).
|
||||||
|
cells([~|Cs], Args, _, _, _) -->
|
||||||
|
( { Args == [] } ->
|
||||||
|
{ domain_error(non_empty_list, [], format_//2) }
|
||||||
|
; { domain_error(format_string, [~|Cs], format_//2) }
|
||||||
|
).
|
||||||
|
cells(Fs0, Args, Tab, Es, VNs) -->
|
||||||
|
{ phrase(upto_what(Fs1, ~), Fs0, Fs),
|
||||||
|
Fs1 = [_|_] },
|
||||||
|
cells(Fs, Args, Tab, [chars(Fs1)|Es], VNs).
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
?- phrase(upto_what(Cs, ~), "abc~test", Rest).
|
||||||
|
Cs = [a,b,c], Rest = [~,t,e,s,t].
|
||||||
|
?- phrase(upto_what(Cs, ~), "abc", Rest).
|
||||||
|
Cs = [a,b,c], Rest = [].
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
separate_digits_fractional(Arg, Sep, Num, Cs) :-
|
||||||
|
number_chars(Num, NCs),
|
||||||
|
phrase(("~",seq(NCs),"d"), FStr),
|
||||||
|
phrase(format_(FStr, [Arg]), Cs0),
|
||||||
|
phrase(upto_what(Bs0, .), Cs0, Ds),
|
||||||
|
reverse(Bs0, Bs1),
|
||||||
|
phrase(groups_of_three(Bs1,Sep), Bs2),
|
||||||
|
reverse(Bs2, Bs),
|
||||||
|
append(Bs, Ds, Cs).
|
||||||
|
|
||||||
|
upto_what([], W), [W] --> [W], !.
|
||||||
|
upto_what([C|Cs], W) --> [C], !, upto_what(Cs, W).
|
||||||
|
upto_what([], _) --> [].
|
||||||
|
|
||||||
|
groups_of_three([A,B,C,D|Rs], Sep) --> !, [A,B,C,Sep], groups_of_three([D|Rs], Sep).
|
||||||
|
groups_of_three(Ls, _) --> seq(Ls).
|
||||||
|
|
||||||
|
split_lines_width(Cs, Num) -->
|
||||||
|
( { length(Prefix, Num),
|
||||||
|
append(Prefix, [R|Rs], Cs) } ->
|
||||||
|
seq(Prefix), "_\n",
|
||||||
|
split_lines_width([R|Rs], Num)
|
||||||
|
; seq(Cs)
|
||||||
|
).
|
||||||
|
|
||||||
|
cell(From, To, Es0) -->
|
||||||
|
( { Es0 == [] } -> []
|
||||||
|
; { reverse(Es0, Es) },
|
||||||
|
[cell(From,To,Es)]
|
||||||
|
).
|
||||||
|
|
||||||
|
%?- format:numeric_argument("2f", Num, [f|Fs], Args0, Args).
|
||||||
|
|
||||||
|
%?- format:numeric_argument("100b", Num, Rs, Args0, Args).
|
||||||
|
|
||||||
|
numeric_argument(Ds, Num, Rest, Args0, Args) :-
|
||||||
|
( Ds = [*|Rest] ->
|
||||||
|
Args0 = [Num|Args]
|
||||||
|
; phrase(numeric_argument_(Ds, Rest), Ns),
|
||||||
|
foldl(plus_times10, Ns, 0, Num),
|
||||||
|
Args0 = Args
|
||||||
|
).
|
||||||
|
|
||||||
|
numeric_argument_([D|Ds], Rest) -->
|
||||||
|
( { member(D, "0123456789") } ->
|
||||||
|
{ number_chars(N, [D]) },
|
||||||
|
[N],
|
||||||
|
numeric_argument_(Ds, Rest)
|
||||||
|
; { Rest = [D|Ds] }
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
plus_times10(D, N0, N) :- N is D + N0*10.
|
||||||
|
|
||||||
|
radix_error(lowercase, R) --> format_("~~~dr", [R]).
|
||||||
|
radix_error(uppercase, R) --> format_("~~~dR", [R]).
|
||||||
|
|
||||||
|
integer_to_radix(I0, R, Which, Cs) :-
|
||||||
|
I is I0, % evaluate compound expression
|
||||||
|
must_be(integer, I),
|
||||||
|
must_be(integer, R),
|
||||||
|
( \+ between(2, 36, R) ->
|
||||||
|
phrase(radix_error(Which,R), Es),
|
||||||
|
domain_error(format_string, Es, format_//2)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
digits(Which, Ds),
|
||||||
|
( I < 0 ->
|
||||||
|
Pos is abs(I),
|
||||||
|
phrase(integer_to_radix_(Pos, R, Ds), Cs0, "-")
|
||||||
|
; I =:= 0 -> Cs0 = "0"
|
||||||
|
; phrase(integer_to_radix_(I, R, Ds), Cs0)
|
||||||
|
),
|
||||||
|
reverse(Cs0, Cs).
|
||||||
|
|
||||||
|
integer_to_radix_(0, _, _) --> !.
|
||||||
|
integer_to_radix_(I0, R, Ds) -->
|
||||||
|
{ M is I0 mod R,
|
||||||
|
nth0(M, Ds, D),
|
||||||
|
I is I0 // R },
|
||||||
|
[D],
|
||||||
|
integer_to_radix_(I, R, Ds).
|
||||||
|
|
||||||
|
digits(lowercase, "0123456789abcdefghijklmnopqrstuvwxyz").
|
||||||
|
digits(uppercase, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").
|
||||||
|
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Impure I/O, implemented as a small wrapper over format_//2.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
format(Fs, Args) :-
|
||||||
|
current_output(Stream),
|
||||||
|
format(Stream, Fs, Args).
|
||||||
|
|
||||||
|
format(Stream, Fs, Args) :-
|
||||||
|
phrase_to_stream(format_(Fs, Args), Stream),
|
||||||
|
flush_output(Stream).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
?- phrase(format:cells("hello", [], 0, [], []), Cs).
|
||||||
|
|
||||||
|
?- phrase(format:cells("hello~10|", [], 0, [], []), Cs).
|
||||||
|
?- phrase(format:cells("~ta~t~10|", [], 0, [], []), Cs).
|
||||||
|
|
||||||
|
?- phrase(format_("~`at~50|", []), Ls).
|
||||||
|
|
||||||
|
?- phrase(format:cells("~`at~50|", [], 0, [], []), Cs),
|
||||||
|
phrase(format:format_cells(Cs), Ls).
|
||||||
|
?- phrase(format:cells("~ta~t~tb~tc~21|", [], 0, [], []), Cs).
|
||||||
|
Cs = [cell(0,21,[glue(' ',_A),chars("a"),glue(' ',_B),glue(' ',_C),chars("b"),glue(' ',_D),chars("c ...")])]
|
||||||
|
?- phrase(format:cells("~ta~t~4|", [], 0, [], []), Cs).
|
||||||
|
Cs = [cell(0,4,[glue(' ',_A),chars("a"),glue(' ',_B)])]
|
||||||
|
|
||||||
|
?- phrase(format:format_cell(cell(0,1,[glue(a,_94)])), Ls).
|
||||||
|
|
||||||
|
?- phrase(format:format_cell(cell(0,50,[chars("hello")])), Ls).
|
||||||
|
|
||||||
|
?- phrase(format_("~`at~50|~n", []), Ls).
|
||||||
|
?- phrase(format_("hello~n~tthere~6|", []), Ls).
|
||||||
|
|
||||||
|
?- format("~ta~t~4|", []).
|
||||||
|
a true.
|
||||||
|
|
||||||
|
?- format("~ta~tb~tc~10|", []).
|
||||||
|
a b c true.
|
||||||
|
|
||||||
|
?- format("~tabc~3|", []).
|
||||||
|
|
||||||
|
?- format("~ta~t~4|", []).
|
||||||
|
|
||||||
|
?- format("~ta~t~tb~tc~20|", []).
|
||||||
|
a b c true.
|
||||||
|
|
||||||
|
?- format("~2f~n", [3]).
|
||||||
|
3.00
|
||||||
|
true.
|
||||||
|
|
||||||
|
?- format("~20f", [0.1]).
|
||||||
|
0.10000000000000000000 true.
|
||||||
|
|
||||||
|
?- X is atan(2), format("~7f~n", [X]).
|
||||||
|
1.1071487
|
||||||
|
X = 1.1071487177940906.
|
||||||
|
|
||||||
|
?- format("~`at~50|~n", []).
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
true
|
||||||
|
|
||||||
|
?- format("~t~N", []).
|
||||||
|
|
||||||
|
?- format("~q", [.]).
|
||||||
|
'.' true.
|
||||||
|
|
||||||
|
?- format("~12r", [300]).
|
||||||
|
210 true.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
We also provide rudimentary versions of portray_clause/1 and listing/1.
|
||||||
|
|
||||||
|
In the eventual library organization, portray_clause/1 and
|
||||||
|
related predicates may be placed in their own dedicated library.
|
||||||
|
|
||||||
|
portray_clause/1 is useful for printing solutions in such a way
|
||||||
|
that they can be read back with read/1.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
portray_clause(Term) :-
|
||||||
|
current_output(Out),
|
||||||
|
portray_clause(Out, Term).
|
||||||
|
|
||||||
|
portray_clause(Stream, Term) :-
|
||||||
|
phrase_to_stream(portray_clause_(Term), Stream),
|
||||||
|
flush_output(Stream).
|
||||||
|
|
||||||
|
portray_clause_(Term) -->
|
||||||
|
{ unique_variable_names(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) },
|
||||||
|
seq(Ls).
|
||||||
|
|
||||||
|
portray_(Var, VNs) --> { var(Var) }, !, literal(Var, VNs).
|
||||||
|
portray_((Head :- Body), VNs) --> !,
|
||||||
|
literal(Head, VNs), " :-\n",
|
||||||
|
body_(Body, 0, 3, VNs).
|
||||||
|
portray_((Head --> Body), VNs) --> !,
|
||||||
|
literal(Head, VNs), " -->\n",
|
||||||
|
body_(Body, 0, 3, VNs).
|
||||||
|
portray_(Any, VNs) --> literal(Any, VNs).
|
||||||
|
|
||||||
|
|
||||||
|
body_(Var, C, I, VNs) --> { var(Var) }, !,
|
||||||
|
indent_to(C, I),
|
||||||
|
literal(Var, VNs).
|
||||||
|
body_((A,B), C, I, VNs) --> !,
|
||||||
|
body_(A, C, I, VNs), ",\n",
|
||||||
|
body_(B, 0, I, VNs).
|
||||||
|
body_(Body, C, I, VNs) -->
|
||||||
|
{ body_if_then_else(Body, If, Then, Else) },
|
||||||
|
!,
|
||||||
|
indent_to(C, I),
|
||||||
|
"( ",
|
||||||
|
{ C1 is I + 3 },
|
||||||
|
body_(If, C1, C1, VNs), " ->\n",
|
||||||
|
body_(Then, 0, C1, VNs), "\n",
|
||||||
|
else_branch(Else, I, VNs).
|
||||||
|
body_((A;B), C, I, VNs) --> !,
|
||||||
|
indent_to(C, I),
|
||||||
|
"( ",
|
||||||
|
{ C1 is I + 3 },
|
||||||
|
body_(A, C1, C1, VNs), "\n",
|
||||||
|
else_branch(B, I, VNs).
|
||||||
|
body_(Goal, C, I, VNs) -->
|
||||||
|
indent_to(C, I), literal(Goal, VNs).
|
||||||
|
|
||||||
|
|
||||||
|
% True iff Body has the shape ( If -> Then ; Else ).
|
||||||
|
body_if_then_else(Body, If, Then, Else) :-
|
||||||
|
nonvar(Body),
|
||||||
|
Body = (A ; Else),
|
||||||
|
nonvar(A),
|
||||||
|
A = (If -> Then).
|
||||||
|
|
||||||
|
else_branch(Else, I, VNs) -->
|
||||||
|
indent_to(0, I),
|
||||||
|
"; ",
|
||||||
|
{ C is I + 3 },
|
||||||
|
( { body_if_then_else(Else, If, Then, NextElse) } ->
|
||||||
|
body_(If, C, C, VNs), " ->\n",
|
||||||
|
body_(Then, 0, C, VNs), "\n",
|
||||||
|
else_branch(NextElse, I, VNs)
|
||||||
|
; { nonvar(Else), Else = ( A ; B ) } ->
|
||||||
|
body_(A, C, C, VNs), "\n",
|
||||||
|
else_branch(B, I, VNs)
|
||||||
|
; body_(Else, C, C, VNs), "\n",
|
||||||
|
indent_to(0, I),
|
||||||
|
")"
|
||||||
|
).
|
||||||
|
|
||||||
|
indent_to(CurrentColumn, Indent) -->
|
||||||
|
format_("~t~*|", [Indent-CurrentColumn]).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
?- portray_clause(a).
|
||||||
|
a.
|
||||||
|
|
||||||
|
?- portray_clause((a :- b)).
|
||||||
|
a :-
|
||||||
|
b.
|
||||||
|
|
||||||
|
?- portray_clause((a :- b, c, d)).
|
||||||
|
a :-
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
d.
|
||||||
|
true.
|
||||||
|
|
||||||
|
|
||||||
|
?- portray_clause([a,b,c,d]).
|
||||||
|
"abcd".
|
||||||
|
|
||||||
|
?- portray_clause(X).
|
||||||
|
?- portray_clause((f(X) :- X)).
|
||||||
|
|
||||||
|
?- portray_clause((h :- ( a -> b; c))).
|
||||||
|
|
||||||
|
?- portray_clause((h :- ( (a -> x ; y) -> b; c))).
|
||||||
|
|
||||||
|
?- portray_clause((h(X) :- ( (a(X) ; y(A,B)) -> b; c))).
|
||||||
|
|
||||||
|
?- portray_clause((h :- (a,d;b,c) ; (b,e;d))).
|
||||||
|
|
||||||
|
?- portray_clause((a :- b ; c ; d)).
|
||||||
|
|
||||||
|
?- portray_clause((h :- L = '.')).
|
||||||
|
|
||||||
|
?- portray_clause(-->(a, (b, {t}, d))).
|
||||||
|
|
||||||
|
?- portray_clause((A :- B)).
|
||||||
|
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
listing(PI) :-
|
||||||
|
nonvar(PI),
|
||||||
|
( PI = Name/Arity0 ->
|
||||||
|
Arity = Arity0
|
||||||
|
; PI = Name//Arity0 ->
|
||||||
|
Arity is Arity0 + 2
|
||||||
|
; type_error(predicate_indicator, PI, listing/1)
|
||||||
|
),
|
||||||
|
functor(Head, Name, Arity),
|
||||||
|
\+ \+ clause(Head, _), % only true if there is at least one clause
|
||||||
|
( clause(Head, Body),
|
||||||
|
( Body == true ->
|
||||||
|
portray_clause(Head)
|
||||||
|
; portray_clause((Head :- Body))
|
||||||
|
),
|
||||||
|
false
|
||||||
|
; true
|
||||||
|
).
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
:- use_module(library(atts)).
|
:- use_module(library(atts)).
|
||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
|
|
||||||
|
:- meta_predicate freeze(-, 0).
|
||||||
|
|
||||||
:- attribute frozen/1.
|
:- attribute frozen/1.
|
||||||
|
|
||||||
verify_attributes(Var, Other, Goals) :-
|
verify_attributes(Var, Other, Goals) :-
|
||||||
68
src/lib/http/http_open.pl
Normal file
68
src/lib/http/http_open.pl
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Written 2022 by Adrián Arroyo Calle (adrian.arroyocalle@gmail.com)
|
||||||
|
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.
|
||||||
|
|
||||||
|
Options supported:
|
||||||
|
|
||||||
|
* method(+Method): Sets the HTTP method of the call. Method can be get (default), head, delete, post, put or patch.
|
||||||
|
* data(+Data): Data to be sent in the request. Useful for POST, PUT and PATCH operations.
|
||||||
|
* size(-Size): Unifies with the value of the Content-Length header
|
||||||
|
* request_headers(+RequestHeaders): Headers to be used in the request
|
||||||
|
* headers(-ListHeaders): Unifies with a list with all headers returned in the response
|
||||||
|
* status_code(-Code): Unifies with the status code of the request (200, 201, 404, ...)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
?- http_open("https://github.com/mthom/scryer-prolog", S, []).
|
||||||
|
%@ S = '$stream'(0x7fcfc9e00f00).
|
||||||
|
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
:- module(http_open, [http_open/3]).
|
||||||
|
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
|
http_open(Address, Response, Options) :-
|
||||||
|
parse_http_options(Options, OptionValues),
|
||||||
|
( member(method(Method), OptionValues) -> true; Method = get),
|
||||||
|
( member(data(Data), OptionValues) -> true; Data = []),
|
||||||
|
( member(request_headers(RequestHeaders), OptionValues) -> true; RequestHeaders = ['user-agent'("Scryer Prolog")]),
|
||||||
|
( member(status_code(Code), OptionValues) -> true; true),
|
||||||
|
( member(headers(Headers), OptionValues) -> true; true),
|
||||||
|
( member(size(Size), OptionValues) -> member('content-length'(Size), Headers); true),
|
||||||
|
'$http_open'(Address, Response, Method, Code, Data, Headers, RequestHeaders).
|
||||||
|
|
||||||
|
parse_http_options(Options, OptionValues) :-
|
||||||
|
maplist(parse_http_options_, Options, OptionValues).
|
||||||
|
|
||||||
|
parse_http_options_(method(Method), method(Method)) :-
|
||||||
|
( var(Method) ->
|
||||||
|
throw(error(instantiation_error, http_open/3))
|
||||||
|
;
|
||||||
|
member(Method, [get, post, put, delete, patch, head]) -> true
|
||||||
|
;
|
||||||
|
throw(error(domain_error(http_option, method(Method)), _))
|
||||||
|
).
|
||||||
|
|
||||||
|
parse_http_options_(data(Data), data(Data)) :-
|
||||||
|
( var(Data) ->
|
||||||
|
throw(error(instantiation_error, http_open/3))
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
parse_http_options_(request_headers(Headers), request_headers(Headers)) :-
|
||||||
|
( var(Headers) ->
|
||||||
|
throw(error(instantiation_error, http_open/3))
|
||||||
|
; true
|
||||||
|
).
|
||||||
|
|
||||||
|
parse_http_options_(size(Size), size(Size)).
|
||||||
|
parse_http_options_(status_code(Code), status_code(Code)).
|
||||||
|
parse_http_options_(headers(Headers), headers(Headers)).
|
||||||
315
src/lib/http/http_server.pl
Normal file
315
src/lib/http/http_server.pl
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Written in December 2020 by Adrián Arroyo (adrian.arroyocalle@gmail.com)
|
||||||
|
Updated in March 2022 by Adrián Arroyo to use the Hyper backend
|
||||||
|
Part of Scryer Prolog
|
||||||
|
|
||||||
|
This library provides an starting point to build HTTP server based applications.
|
||||||
|
It is based on Hyper, which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However,
|
||||||
|
some advanced features that Hyper provides are still not accesible.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
==========
|
||||||
|
The main predicate of the library is http_listen/2, which needs a port number
|
||||||
|
(usually 80) and a list of handlers. A handler is a compound term with the functor
|
||||||
|
as one HTTP method (in lowercase) and followed by a Route Match and a predicate
|
||||||
|
which will handle the call.
|
||||||
|
|
||||||
|
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
|
||||||
|
- 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
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- meta_predicate http_listen(?, :).
|
||||||
|
|
||||||
|
:- use_module(library(charsio)).
|
||||||
|
:- use_module(library(crypto)).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(format)).
|
||||||
|
:- use_module(library(iso_ext)).
|
||||||
|
:- use_module(library(lists)).
|
||||||
|
:- use_module(library(pio)).
|
||||||
|
:- use_module(library(time)).
|
||||||
|
|
||||||
|
http_listen(Port, Module:Handlers0) :-
|
||||||
|
must_be(integer, Port),
|
||||||
|
must_be(list, 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].
|
||||||
|
|
||||||
|
http_listen_(Port, Handlers) :-
|
||||||
|
phrase(format_("0.0.0.0:~d", [Port]), Addr),
|
||||||
|
'$http_listen'(Addr, HttpListener),!,
|
||||||
|
format("Listening at ~s\n", [Addr]),
|
||||||
|
http_loop(HttpListener, Handlers).
|
||||||
|
|
||||||
|
http_loop(HttpListener, Handlers) :-
|
||||||
|
'$http_accept'(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle),
|
||||||
|
current_time(Time),
|
||||||
|
phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString),
|
||||||
|
format("~s ~w ~s\n", [TimeString, RequestMethod, RequestPath]),
|
||||||
|
maplist(map_header_kv, RequestHeaders, RequestHeadersKV),
|
||||||
|
phrase(parse_queries(RequestQueries), RequestQuery),
|
||||||
|
(
|
||||||
|
match_handler(Handlers, RequestMethod, RequestPath, Handler) ->
|
||||||
|
(
|
||||||
|
HttpRequest = http_request(RequestHeadersKV, stream(RequestStream), RequestQueries),
|
||||||
|
HttpResponse = http_response(_, _, _),
|
||||||
|
(call(Handler, HttpRequest, HttpResponse) ->
|
||||||
|
send_response(ResponseHandle, HttpResponse)
|
||||||
|
; (
|
||||||
|
'$http_answer'(ResponseHandle, 500, [], ResponseStream),
|
||||||
|
call_cleanup(format(ResponseStream, "Internal Server Error", []), close(ResponseStream)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
; (
|
||||||
|
'$http_answer'(ResponseHandle, 404, [], ResponseStream),
|
||||||
|
call_cleanup(format(ResponseStream, "Not Found"), close(ResponseStream)))
|
||||||
|
),
|
||||||
|
http_loop(HttpListener, Handlers).
|
||||||
|
|
||||||
|
send_response(ResponseHandle, http_response(StatusCode0, text(ResponseText), ResponseHeaders0)) :-
|
||||||
|
default(StatusCode0, 200, StatusCode),
|
||||||
|
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
|
||||||
|
'$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
|
||||||
|
call_cleanup(
|
||||||
|
format(ResponseStream, "~s", [ResponseText]),
|
||||||
|
close(ResponseStream)
|
||||||
|
).
|
||||||
|
|
||||||
|
send_response(ResponseHandle, http_response(StatusCode0, bytes(ResponseBytes), ResponseHeaders0)) :-
|
||||||
|
default(StatusCode0, 200, StatusCode),
|
||||||
|
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
|
||||||
|
'$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
|
||||||
|
call_cleanup(
|
||||||
|
format(ResponseStream, "~s", [ResponseBytes]),
|
||||||
|
close(ResponseStream)
|
||||||
|
).
|
||||||
|
|
||||||
|
send_response(ResponseHandle, http_response(StatusCode0, file(Filename), ResponseHeaders0)) :-
|
||||||
|
default(StatusCode0, 200, StatusCode),
|
||||||
|
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
|
||||||
|
'$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
|
||||||
|
call_cleanup(
|
||||||
|
setup_call_cleanup(
|
||||||
|
open(Filename, read, FileStream, [type(binary)]),
|
||||||
|
(
|
||||||
|
get_n_chars(FileStream, _, FileCs),
|
||||||
|
format(ResponseStream, "~s", [FileCs])
|
||||||
|
),
|
||||||
|
close(FileStream)
|
||||||
|
),
|
||||||
|
close(ResponseStream)
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
default(Var, Default, Out) :-
|
||||||
|
(var(Var) -> Out = Default
|
||||||
|
; Var = Out
|
||||||
|
).
|
||||||
|
|
||||||
|
map_header_kv(T, K-V) :-
|
||||||
|
T =.. [K0, V],
|
||||||
|
atom_chars(K0, K).
|
||||||
|
|
||||||
|
map_header_kv_2(T, K-V) :-
|
||||||
|
atom_chars(K0, K),
|
||||||
|
T =.. [K0, V].
|
||||||
|
|
||||||
|
match_handler(Handlers, Method, "/", Handler) :-
|
||||||
|
member(H, Handlers),
|
||||||
|
H =.. [Method, /, Handler].
|
||||||
|
match_handler(Handlers, Method, Path, Handler) :-
|
||||||
|
member(H, Handlers),
|
||||||
|
copy_term(H, H1),
|
||||||
|
H1 =.. [Method, Pattern, Handler],
|
||||||
|
\+ var(Pattern),
|
||||||
|
phrase(path(Pattern), Path).
|
||||||
|
match_handler(Handlers, Method, Path, Handler) :-
|
||||||
|
member(H, Handlers),
|
||||||
|
copy_term(H, H1),
|
||||||
|
H1 =.. [Method, Var, Handler],
|
||||||
|
var(Var),
|
||||||
|
Var = Path.
|
||||||
|
|
||||||
|
path(Pattern) -->
|
||||||
|
{
|
||||||
|
Pattern =.. Parts,
|
||||||
|
length(Parts, 3),
|
||||||
|
nth0(1, Parts, Pattern0),
|
||||||
|
nth0(2, Parts, PartAtom),
|
||||||
|
(var(PartAtom) -> Part = PartAtom; atom_chars(PartAtom, Part))
|
||||||
|
},
|
||||||
|
path(Pattern0),
|
||||||
|
"/",
|
||||||
|
string_without("/", Part).
|
||||||
|
|
||||||
|
path(Pattern) -->
|
||||||
|
{
|
||||||
|
Pattern =.. Parts,
|
||||||
|
Parts = [PartAtom],
|
||||||
|
(var(PartAtom) -> Part = PartAtom; atom_chars(PartAtom, Part))
|
||||||
|
},
|
||||||
|
"/",
|
||||||
|
string_without("/", Part).
|
||||||
|
|
||||||
|
path([]) --> [].
|
||||||
|
|
||||||
|
string_without(Not, [Char|String]) -->
|
||||||
|
[Char],
|
||||||
|
{
|
||||||
|
\+ member(Char, Not)
|
||||||
|
},
|
||||||
|
string_without(Not, String).
|
||||||
|
|
||||||
|
string_without(_, []) -->
|
||||||
|
[].
|
||||||
|
|
||||||
|
http_headers(http_request(Headers, _, _), Headers).
|
||||||
|
http_headers(http_response(_, _, Headers), Headers).
|
||||||
|
|
||||||
|
http_body(http_request(_, stream(StreamBody), _), bytes(BytesBody)) :- get_n_chars(StreamBody, _, BytesBody).
|
||||||
|
http_body(http_request(_, stream(StreamBody), _), text(TextBody)) :- get_n_chars(StreamBody, _, TextBody).
|
||||||
|
http_body(http_request(Headers, stream(StreamBody), _), form(FormBody)) :-
|
||||||
|
member("content-type"-"application/x-www-form-urlencoded", Headers),
|
||||||
|
get_n_chars(StreamBody, _, TextBody),
|
||||||
|
phrase(parse_queries(FormBody), TextBody).
|
||||||
|
http_body(http_request(_, Body, _), Body).
|
||||||
|
http_body(http_response(_, Body, _), Body).
|
||||||
|
|
||||||
|
http_status_code(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).
|
||||||
|
|
||||||
|
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)
|
||||||
|
}.
|
||||||
|
|
||||||
|
parse_queries([]) -->
|
||||||
|
[].
|
||||||
|
|
||||||
|
% 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([]) --> [].
|
||||||
265
src/lib/iso_ext.pl
Normal file
265
src/lib/iso_ext.pl
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
:- 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,
|
||||||
|
copy_term_nat/2,
|
||||||
|
asserta/2,
|
||||||
|
assertz/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)
|
||||||
|
; type_error(atom, Key, bb_put/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
%% backtrackable global variables.
|
||||||
|
|
||||||
|
bb_b_put(Key, Value) :-
|
||||||
|
( atom(Key) ->
|
||||||
|
'$store_backtrackable_global_var'(Key, Value)
|
||||||
|
; type_error(atom, Key, bb_b_put/2)
|
||||||
|
).
|
||||||
|
|
||||||
|
bb_get(Key, Value) :-
|
||||||
|
( 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)).
|
||||||
|
|
||||||
|
:- non_counted_backtracking setup_call_cleanup/3.
|
||||||
|
|
||||||
|
setup_call_cleanup(S, G, C) :-
|
||||||
|
'$get_b_value'(B),
|
||||||
|
'$call_with_inference_counting'(call(S)),
|
||||||
|
'$set_cp_by_default'(B),
|
||||||
|
'$get_current_block'(Bb),
|
||||||
|
( C = _:CC,
|
||||||
|
var(CC) ->
|
||||||
|
instantiation_error(setup_call_cleanup/3)
|
||||||
|
; scc_helper(C, G, Bb)
|
||||||
|
).
|
||||||
|
|
||||||
|
:- meta_predicate(scc_helper(?,0,?)).
|
||||||
|
|
||||||
|
:- non_counted_backtracking scc_helper/3.
|
||||||
|
|
||||||
|
scc_helper(C, G, Bb) :-
|
||||||
|
'$get_cp'(Cp),
|
||||||
|
'$install_scc_cleaner'(C, NBb),
|
||||||
|
'$call_with_inference_counting'(call(G)),
|
||||||
|
( '$check_cp'(Cp) ->
|
||||||
|
'$reset_block'(Bb),
|
||||||
|
run_cleaners_without_handling(Cp)
|
||||||
|
; true
|
||||||
|
; '$reset_block'(NBb),
|
||||||
|
'$fail'
|
||||||
|
).
|
||||||
|
scc_helper(_, _, Bb) :-
|
||||||
|
'$reset_block'(Bb),
|
||||||
|
'$push_ball_stack',
|
||||||
|
run_cleaners_with_handling,
|
||||||
|
'$pop_from_ball_stack',
|
||||||
|
'$unwind_stack'.
|
||||||
|
scc_helper(_, _, _) :-
|
||||||
|
'$get_cp'(Cp),
|
||||||
|
run_cleaners_without_handling(Cp),
|
||||||
|
'$fail'.
|
||||||
|
|
||||||
|
:- non_counted_backtracking run_cleaners_with_handling/0.
|
||||||
|
|
||||||
|
run_cleaners_with_handling :-
|
||||||
|
'$get_scc_cleaner'(C),
|
||||||
|
'$get_level'(B),
|
||||||
|
catch(C, _, true),
|
||||||
|
'$set_cp_by_default'(B),
|
||||||
|
run_cleaners_with_handling.
|
||||||
|
run_cleaners_with_handling :-
|
||||||
|
'$restore_cut_policy'.
|
||||||
|
|
||||||
|
:- non_counted_backtracking run_cleaners_without_handling/1.
|
||||||
|
|
||||||
|
run_cleaners_without_handling(Cp) :-
|
||||||
|
'$get_scc_cleaner'(C),
|
||||||
|
'$get_level'(B),
|
||||||
|
call(C),
|
||||||
|
'$set_cp_by_default'(B),
|
||||||
|
run_cleaners_without_handling(Cp).
|
||||||
|
run_cleaners_without_handling(Cp) :-
|
||||||
|
'$set_cp_by_default'(Cp),
|
||||||
|
'$restore_cut_policy'.
|
||||||
|
|
||||||
|
% call_with_inference_limit
|
||||||
|
|
||||||
|
:- non_counted_backtracking end_block/4.
|
||||||
|
|
||||||
|
end_block(_, Bb, NBb, _L) :-
|
||||||
|
'$clean_up_block'(NBb),
|
||||||
|
'$reset_block'(Bb).
|
||||||
|
end_block(B, _Bb, NBb, L) :-
|
||||||
|
'$install_inference_counter'(B, L, _),
|
||||||
|
'$reset_block'(NBb),
|
||||||
|
'$fail'.
|
||||||
|
|
||||||
|
:- non_counted_backtracking handle_ile/3.
|
||||||
|
|
||||||
|
handle_ile(B, inference_limit_exceeded(B), inference_limit_exceeded) :-
|
||||||
|
!,
|
||||||
|
'$pop_ball_stack'.
|
||||||
|
handle_ile(B, _, _) :-
|
||||||
|
'$remove_call_policy_check'(B),
|
||||||
|
'$pop_from_ball_stack',
|
||||||
|
'$unwind_stack'.
|
||||||
|
|
||||||
|
:- meta_predicate(call_with_inference_limit(0, ?, ?)).
|
||||||
|
|
||||||
|
:- non_counted_backtracking call_with_inference_limit/3.
|
||||||
|
|
||||||
|
call_with_inference_limit(G, L, R) :-
|
||||||
|
( integer(L) ->
|
||||||
|
( L < 0 ->
|
||||||
|
domain_error(not_less_than_zero, L, call_with_inference_limit/3)
|
||||||
|
; true
|
||||||
|
)
|
||||||
|
; var(L) ->
|
||||||
|
instantiation_error(call_with_inference_limit/3)
|
||||||
|
; type_error(integer, L, call_with_inference_limit/3)
|
||||||
|
),
|
||||||
|
'$get_current_block'(Bb),
|
||||||
|
'$get_b_value'(B),
|
||||||
|
call_with_inference_limit(G, L, R, Bb, B),
|
||||||
|
'$remove_call_policy_check'(B).
|
||||||
|
|
||||||
|
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_with_inference_counting'(call(G)),
|
||||||
|
'$inference_level'(R, B),
|
||||||
|
'$remove_inference_counter'(B, Count1),
|
||||||
|
Diff is L - (Count1 - Count0),
|
||||||
|
end_block(B, Bb, NBb, Diff).
|
||||||
|
call_with_inference_limit(_, _, R, Bb, B) :-
|
||||||
|
'$reset_block'(Bb),
|
||||||
|
'$remove_inference_counter'(B, _),
|
||||||
|
( '$get_ball'(Ball),
|
||||||
|
'$push_ball_stack',
|
||||||
|
'$get_level'(Cp),
|
||||||
|
'$set_cp_by_default'(Cp)
|
||||||
|
; '$remove_call_policy_check'(B),
|
||||||
|
'$fail'
|
||||||
|
),
|
||||||
|
handle_ile(B, Ball, R).
|
||||||
|
|
||||||
|
partial_string(String, L, L0) :-
|
||||||
|
( String == [] ->
|
||||||
|
L = L0
|
||||||
|
; catch(atom_chars(Atom, String),
|
||||||
|
error(E, _),
|
||||||
|
throw(error(E, partial_string/3))),
|
||||||
|
'$create_partial_string'(Atom, L, L0)
|
||||||
|
).
|
||||||
|
|
||||||
|
partial_string(String) :-
|
||||||
|
'$is_partial_string'(String).
|
||||||
|
|
||||||
|
partial_string_tail(String, Tail) :-
|
||||||
|
( partial_string(String) ->
|
||||||
|
'$partial_string_tail'(String, Tail)
|
||||||
|
; throw(error(type_error(partial_string, String), partial_string_tail/2))
|
||||||
|
).
|
||||||
|
|
||||||
|
:- dynamic(i_call_nth_nesting/2).
|
||||||
|
:- dynamic(i_call_nth_counter/1).
|
||||||
|
|
||||||
|
:- meta_predicate(call_nth(0, ?)).
|
||||||
|
|
||||||
|
call_nth(Goal, N) :-
|
||||||
|
can_be(integer, N),
|
||||||
|
( integer(N) ->
|
||||||
|
( N < 0 ->
|
||||||
|
domain_error(not_less_than_zero, N, call_nth/2)
|
||||||
|
; N > 0
|
||||||
|
)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
setup_call_cleanup(call_nth_nesting(C, ID),
|
||||||
|
( Goal,
|
||||||
|
bb_get(ID, N0),
|
||||||
|
N1 is N0 + 1,
|
||||||
|
bb_put(ID, N1),
|
||||||
|
( integer(N) ->
|
||||||
|
N = N1,
|
||||||
|
!
|
||||||
|
; N = N1
|
||||||
|
)
|
||||||
|
),
|
||||||
|
( bb_get(i_call_nth_counter, C) ->
|
||||||
|
C1 is C - 1,
|
||||||
|
bb_put(i_call_nth_counter, C1)
|
||||||
|
; true
|
||||||
|
)).
|
||||||
|
|
||||||
|
call_nth_nesting(C, ID) :-
|
||||||
|
( bb_get(i_call_nth_counter, C0) ->
|
||||||
|
C is C0 + 1
|
||||||
|
; C = 0
|
||||||
|
),
|
||||||
|
number_chars(C, Cs),
|
||||||
|
atom_chars(Atom, Cs),
|
||||||
|
atom_concat(i_call_nth_nesting_, Atom, ID),
|
||||||
|
bb_put(ID, 0),
|
||||||
|
bb_put(i_call_nth_counter, C).
|
||||||
|
|
||||||
|
|
||||||
|
copy_term_nat(Source, Dest) :-
|
||||||
|
'$copy_term_without_attr_vars'(Source, Dest).
|
||||||
|
|
||||||
|
|
||||||
|
asserta(Module, (Head :- Body)) :-
|
||||||
|
!,
|
||||||
|
'$asserta'(Module, Head, Body).
|
||||||
|
asserta(Module, Fact) :-
|
||||||
|
'$asserta'(Module, Fact, true).
|
||||||
|
|
||||||
|
assertz(Module, (Head :- Body)) :-
|
||||||
|
!,
|
||||||
|
'$assertz'(Module, Head, Body).
|
||||||
|
assertz(Module, Fact) :-
|
||||||
|
'$assertz'(Module, Fact, true).
|
||||||
|
|
||||||
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),_)).
|
||||||
378
src/lib/lists.pl
Normal file
378
src/lib/lists.pl
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
:- 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, nth0/4, nth1/3, nth1/4,
|
||||||
|
sum_list/2, transpose/2, list_to_set/2, list_max/2,
|
||||||
|
list_min/2, permutation/2]).
|
||||||
|
|
||||||
|
/* Author: Mark Thom, Jan Wielemaker, and Richard O'Keefe
|
||||||
|
Copyright (c) 2018-2021, Mark Thom
|
||||||
|
Copyright (c) 2002-2020, University of Amsterdam
|
||||||
|
VU University Amsterdam
|
||||||
|
SWI-Prolog Solutions b.v.
|
||||||
|
All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
|
||||||
|
|
||||||
|
:- meta_predicate maplist(1, ?).
|
||||||
|
:- meta_predicate maplist(2, ?, ?).
|
||||||
|
:- meta_predicate maplist(3, ?, ?, ?).
|
||||||
|
:- meta_predicate maplist(4, ?, ?, ?, ?).
|
||||||
|
:- meta_predicate maplist(5, ?, ?, ?, ?, ?).
|
||||||
|
:- meta_predicate maplist(6, ?, ?, ?, ?, ?, ?).
|
||||||
|
:- meta_predicate maplist(7, ?, ?, ?, ?, ?, ?, ?).
|
||||||
|
:- meta_predicate maplist(8, ?, ?, ?, ?, ?, ?, ?, ?).
|
||||||
|
|
||||||
|
:- meta_predicate foldl(3, ?, ?, ?).
|
||||||
|
:- meta_predicate foldl(4, ?, ?, ?, ?).
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
|
||||||
|
:- meta_predicate(resource_error(+,:)).
|
||||||
|
|
||||||
|
resource_error(Resource, Context) :-
|
||||||
|
throw(error(resource_error(Resource), Context)).
|
||||||
|
|
||||||
|
length(Xs0, N) :-
|
||||||
|
'$skip_max_list'(M, N, Xs0,Xs),
|
||||||
|
!,
|
||||||
|
( Xs == [] -> N = M
|
||||||
|
; nonvar(Xs) -> var(N), Xs = [_|_], resource_error(finite_memory,length/2)
|
||||||
|
; nonvar(N) -> R is N-M, length_rundown(Xs, R)
|
||||||
|
; N == Xs -> failingvarskip(Xs), resource_error(finite_memory,length/2)
|
||||||
|
; length_addendum(Xs, N, M)
|
||||||
|
).
|
||||||
|
length(_, N) :-
|
||||||
|
integer(N), !,
|
||||||
|
domain_error(not_less_than_zero, N, length/2).
|
||||||
|
length(_, N) :-
|
||||||
|
type_error(integer, N, length/2).
|
||||||
|
|
||||||
|
length_rundown(Xs, 0) :- !, Xs = [].
|
||||||
|
length_rundown(Vs, N) :-
|
||||||
|
\+ \+ '$project_atts':copy_term(Vs,Vs,[]), % unconstrained
|
||||||
|
!,
|
||||||
|
'$det_length_rundown'(Vs, N).
|
||||||
|
length_rundown([_|Xs], N) :- % force unification
|
||||||
|
N1 is N-1,
|
||||||
|
length(Xs, N1). % maybe some new info on Xs
|
||||||
|
|
||||||
|
failingvarskip(Xs) :-
|
||||||
|
\+ \+ '$project_atts':copy_term(Xs,Xs,[]), % unconstrained
|
||||||
|
!.
|
||||||
|
failingvarskip([_|Xs0]) :- % force unification
|
||||||
|
'$skip_max_list'(_, _, Xs0,Xs),
|
||||||
|
( nonvar(Xs) -> Xs = [_|_]
|
||||||
|
; failingvarskip(Xs)
|
||||||
|
).
|
||||||
|
|
||||||
|
length_addendum([], N, N).
|
||||||
|
length_addendum([_|Xs], N, M) :-
|
||||||
|
M1 is M + 1,
|
||||||
|
length_addendum(Xs, N, M1).
|
||||||
|
|
||||||
|
|
||||||
|
member(X, [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, Es0, E) :-
|
||||||
|
nonvar(N),
|
||||||
|
'$skip_max_list'(Skip, N, Es0,Es1),
|
||||||
|
!,
|
||||||
|
( Skip == N
|
||||||
|
-> Es1 = [E|_]
|
||||||
|
; ( var(Es1) ; Es1 = [_|_] ) % a partial or infinite list
|
||||||
|
-> R is N-Skip,
|
||||||
|
skipn(R,Es1,Es2),
|
||||||
|
Es2 = [E|_]
|
||||||
|
).
|
||||||
|
nth0(N, Es0, E) :-
|
||||||
|
can_be(not_less_than_zero, N),
|
||||||
|
Es0 = [E0|Es1],
|
||||||
|
nth0_el(0,N, E0,E, Es1).
|
||||||
|
|
||||||
|
skipn(N0, Es0,Es) :-
|
||||||
|
N0>0,
|
||||||
|
!, % should not be necessary #1028
|
||||||
|
N1 is N0-1,
|
||||||
|
Es0 = [_|Es1],
|
||||||
|
skipn(N1, Es1,Es).
|
||||||
|
skipn(0, Es,Es).
|
||||||
|
|
||||||
|
nth0_el(N0,N, E0,E, Es0) :-
|
||||||
|
Es0 == [],
|
||||||
|
!, % indexing
|
||||||
|
N0 = N,
|
||||||
|
E0 = E.
|
||||||
|
nth0_el(N,N, E,E, _).
|
||||||
|
nth0_el(N0,N, _,E, [E0|Es0]) :-
|
||||||
|
N1 is N0+1,
|
||||||
|
nth0_el(N1,N, E0,E, Es0).
|
||||||
|
|
||||||
|
nth1(N, Es0, E) :-
|
||||||
|
N \== 0,
|
||||||
|
nth0(N, [_|Es0], E),
|
||||||
|
N \== 0.
|
||||||
|
|
||||||
|
skipn(N0, Es0,Es, Xs0,Xs) :-
|
||||||
|
N0>0,
|
||||||
|
!, % should not be necessary #1028
|
||||||
|
N1 is N0-1,
|
||||||
|
Es0 = [E|Es1],
|
||||||
|
Xs0 = [E|Xs1],
|
||||||
|
skipn(N1, Es1,Es, Xs1,Xs).
|
||||||
|
skipn(0, Es,Es, Xs,Xs).
|
||||||
|
|
||||||
|
nth0(N, Es0, E, Es) :-
|
||||||
|
integer(N),
|
||||||
|
N >= 0,
|
||||||
|
!,
|
||||||
|
skipn(N, Es0,Es1, Es,Es2),
|
||||||
|
Es1 = [E|Es2].
|
||||||
|
nth0(N, Es0, E, Es) :-
|
||||||
|
can_be(not_less_than_zero, N),
|
||||||
|
Es0 = [E0|Es1],
|
||||||
|
nth0_elx(0,N, E0,E, Es1, Es).
|
||||||
|
|
||||||
|
nth0_elx(N0,N, E0,E, Es0, Es) :-
|
||||||
|
Es0 == [],
|
||||||
|
!,
|
||||||
|
N0 = N,
|
||||||
|
E0 = E,
|
||||||
|
Es0 = Es.
|
||||||
|
nth0_elx(N,N, E,E, Es, Es).
|
||||||
|
nth0_elx(N0,N, E0,E, [E1|Es0], [E0|Es]) :-
|
||||||
|
N1 is N0+1,
|
||||||
|
nth0_elx(N1,N, E1,E, Es0, Es).
|
||||||
|
|
||||||
|
% p.p.8.5
|
||||||
|
|
||||||
|
nth1(N, Es0, E, Es) :-
|
||||||
|
N \== 0,
|
||||||
|
nth0(N, [_|Es0], E, [_|Es]),
|
||||||
|
N \== 0.
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
% setof/3.
|
||||||
|
|
||||||
is_ordset(Term) :-
|
is_ordset(Term) :-
|
||||||
'$skip_max_list'(_, -1, Term, Tail), Tail == [], %% is_list(Term),
|
'$skip_max_list'(_, _, Term, Tail), Tail == [], %% is_list(Term),
|
||||||
is_ordset2(Term).
|
is_ordset2(Term).
|
||||||
|
|
||||||
is_ordset2([]).
|
is_ordset2([]).
|
||||||
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).
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
:- module(pairs, [pairs_keys_values/3,
|
:- module(pairs, [pairs_keys_values/3,
|
||||||
pairs_keys/2,
|
pairs_keys/2,
|
||||||
pairs_values/2,
|
pairs_values/2,
|
||||||
|
group_pairs_by_key/2,
|
||||||
map_list_to_pairs/3]).
|
map_list_to_pairs/3]).
|
||||||
|
|
||||||
|
|
||||||
|
:- meta_predicate map_list_to_pairs(2, ?, ?).
|
||||||
|
|
||||||
pairs_keys_values([], [], []).
|
pairs_keys_values([], [], []).
|
||||||
pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :-
|
pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :-
|
||||||
pairs_keys_values(ABs, As, Bs).
|
pairs_keys_values(ABs, As, Bs).
|
||||||
@@ -19,3 +22,14 @@ map_list_to_pairs2([], _, []).
|
|||||||
map_list_to_pairs2([H|T0], Pred, [K-H|T]) :-
|
map_list_to_pairs2([H|T0], Pred, [K-H|T]) :-
|
||||||
call(Pred, H, K),
|
call(Pred, H, K),
|
||||||
map_list_to_pairs2(T0, Pred, T).
|
map_list_to_pairs2(T0, Pred, T).
|
||||||
|
|
||||||
|
|
||||||
|
group_pairs_by_key([], []).
|
||||||
|
group_pairs_by_key([K-V|KVs0], [K-[V|Vs]|KVs]) :-
|
||||||
|
same_key(K, KVs0, Vs, KVs1),
|
||||||
|
group_pairs_by_key(KVs1, KVs).
|
||||||
|
|
||||||
|
same_key(K0, [K1-V|KVs0], [V|Vs], KVs) :-
|
||||||
|
K0 == K1, !,
|
||||||
|
same_key(K0, KVs0, Vs, KVs).
|
||||||
|
same_key(_, KVs, [], KVs).
|
||||||
115
src/lib/pio.pl
Normal file
115
src/lib/pio.pl
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Pure I/O
|
||||||
|
========
|
||||||
|
|
||||||
|
Our goal is to encourage the use of definite clause grammars (DCGs)
|
||||||
|
for describing strings. The predicates phrase_from_file/[2,3],
|
||||||
|
phrase_to_file/[2,3] and phrase_to_stream/2 let us apply DCGs
|
||||||
|
transparently to files and streams, and therefore decouple side-effects
|
||||||
|
from declarative descriptions.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
:- module(pio, [phrase_from_file/2,
|
||||||
|
phrase_from_file/3,
|
||||||
|
phrase_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_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(octet_character, N, phrase_to_stream/2)
|
||||||
|
; true
|
||||||
|
)
|
||||||
|
; true
|
||||||
|
),
|
||||||
|
% we use a specialised internal predicate that uses only a
|
||||||
|
% single "write" operation for efficiency. It is equivalent to
|
||||||
|
% maplist(put_char(Stream), Cs). It also works for binary streams.
|
||||||
|
'$put_chars'(Stream, Cs).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
phrase_to_file(+GRBody, +File), 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)).
|
||||||
59
src/lib/random.pl
Normal file
59
src/lib/random.pl
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
:- module(random, [maybe/0, random/1, random_integer/3, set_random/1]).
|
||||||
|
|
||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
To retain desirable declarative properties, predicates that internally
|
||||||
|
use random numbers should be equipped with an argument that specifies
|
||||||
|
the random seed. This makes everything completely reproducible.
|
||||||
|
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
|
||||||
|
% succeeds with probability 0.5.
|
||||||
|
maybe :- '$maybe'.
|
||||||
|
|
||||||
|
% The higher the precision, the slower it gets.
|
||||||
|
random_number_precision(64).
|
||||||
|
|
||||||
|
random(R) :-
|
||||||
|
var(R),
|
||||||
|
random_number_precision(N),
|
||||||
|
rnd(N, R).
|
||||||
|
|
||||||
|
random_integer(Lower, Upper, R) :-
|
||||||
|
var(R),
|
||||||
|
( (var(Lower) ; var(Upper)) ->
|
||||||
|
instantiation_error(random_integer/3)
|
||||||
|
; \+ integer(Lower) ->
|
||||||
|
type_error(integer, Lower, random_integer/3)
|
||||||
|
; \+ integer(Upper) ->
|
||||||
|
type_error(integer, Upper, random_integer/3)
|
||||||
|
; Upper > Lower,
|
||||||
|
random(R0),
|
||||||
|
R is floor((Upper - Lower) * R0 + Lower)
|
||||||
|
).
|
||||||
|
|
||||||
|
rnd(N, R) :-
|
||||||
|
rnd_(N, 0, R).
|
||||||
|
|
||||||
|
rnd_(0, R, R) :- !.
|
||||||
|
rnd_(N, R0, R) :-
|
||||||
|
maybe,
|
||||||
|
!,
|
||||||
|
N1 is N - 1,
|
||||||
|
rnd_(N1, R0, R).
|
||||||
|
rnd_(N, R0, R) :-
|
||||||
|
N1 is N - 1,
|
||||||
|
R1 is R0 + 1.0 / 2.0 ^ N,
|
||||||
|
rnd_(N1, R1, R).
|
||||||
|
|
||||||
|
set_random(Seed) :-
|
||||||
|
( nonvar(Seed) ->
|
||||||
|
( Seed = seed(S) ->
|
||||||
|
( var(S) -> instantiation_error(set_random/1)
|
||||||
|
; integer(S) -> '$set_seed'(S)
|
||||||
|
; type_error(integer, S, set_random/1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
; instantiation_error(set_random/1)
|
||||||
|
).
|
||||||
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
:- module(reif, [if_/3, (=)/3, (',')/3, (;)/3, cond_t/3, dif/3,
|
:- module(reif, [if_/3, (=)/3, (',')/3, (;)/3, cond_t/3, dif/3,
|
||||||
memberd_t/3, tfilter/3, tmember/2, tmember_t/3,
|
memberd_t/3, tfilter/3, tmember/2, tmember_t/3,
|
||||||
tpartition/4]).
|
tpartition/4]).
|
||||||
|
|
||||||
:- use_module(library(dif)).
|
:- use_module(library(dif)).
|
||||||
|
|
||||||
|
:- meta_predicate(if_(1, 0, 0)).
|
||||||
|
|
||||||
if_(If_1, Then_0, Else_0) :-
|
if_(If_1, Then_0, Else_0) :-
|
||||||
call(If_1, T),
|
call(If_1, T),
|
||||||
( T == true -> call(Then_0)
|
( T == true -> call(Then_0)
|
||||||
@@ -26,13 +28,14 @@ dif(X, Y, T) :-
|
|||||||
non(true, false).
|
non(true, false).
|
||||||
non(false, true).
|
non(false, true).
|
||||||
|
|
||||||
tfilter(C_2, Es, Fs) :-
|
:- meta_predicate(tfilter(2, ?, ?)).
|
||||||
i_tfilter(Es, C_2, Fs).
|
|
||||||
|
|
||||||
i_tfilter([], _, []).
|
tfilter(_, [], []).
|
||||||
i_tfilter([E|Es], C_2, Fs0) :-
|
tfilter(C_2, [E|Es], Fs0) :-
|
||||||
if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs),
|
if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs),
|
||||||
i_tfilter(Es, C_2, Fs).
|
tfilter(C_2, Es, Fs).
|
||||||
|
|
||||||
|
:- meta_predicate(tpartition(2, ?, ?, ?)).
|
||||||
|
|
||||||
tpartition(P_2, Xs, Ts, Fs) :-
|
tpartition(P_2, Xs, Ts, Fs) :-
|
||||||
i_tpartition(Xs, P_2, Ts, Fs).
|
i_tpartition(Xs, P_2, Ts, Fs).
|
||||||
@@ -44,12 +47,18 @@ i_tpartition([X|Xs], P_2, Ts0, Fs0) :-
|
|||||||
, ( Fs0 = [X|Fs], Ts0 = Ts ) ),
|
, ( Fs0 = [X|Fs], Ts0 = Ts ) ),
|
||||||
i_tpartition(Xs, P_2, Ts, Fs).
|
i_tpartition(Xs, P_2, Ts, Fs).
|
||||||
|
|
||||||
|
:- meta_predicate(','(1, 1, ?)).
|
||||||
|
|
||||||
','(A_1, B_1, T) :-
|
','(A_1, B_1, T) :-
|
||||||
if_(A_1, call(B_1, T), T = false).
|
if_(A_1, call(B_1, T), T = false).
|
||||||
|
|
||||||
|
:- meta_predicate(';'(1, 1, ?)).
|
||||||
|
|
||||||
';'(A_1, B_1, T) :-
|
';'(A_1, B_1, T) :-
|
||||||
if_(A_1, T = true, call(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) :-
|
cond_t(If_1, Then_0, T) :-
|
||||||
if_(If_1, ( Then_0, T = true ), T = false ).
|
if_(If_1, ( Then_0, T = true ), T = false ).
|
||||||
|
|
||||||
@@ -60,8 +69,13 @@ i_memberd_t([], _, false).
|
|||||||
i_memberd_t([X|Xs], E, T) :-
|
i_memberd_t([X|Xs], E, T) :-
|
||||||
if_( X = E, T = true, i_memberd_t(Xs, E, T) ).
|
if_( X = E, T = true, i_memberd_t(Xs, E, T) ).
|
||||||
|
|
||||||
|
:- meta_predicate(tmember(2, ?)).
|
||||||
|
|
||||||
tmember(P_2, [X|Xs]) :-
|
tmember(P_2, [X|Xs]) :-
|
||||||
if_( call(P_2, X), true, tmember(P_2, 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) :-
|
tmember_t(P_2, [X|Xs], T) :-
|
||||||
if_( call(P_2, X), T = true, tmember_t(P_2, 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
|
||||||
|
).
|
||||||
96
src/lib/sgml.pl
Normal file
96
src/lib/sgml.pl
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Predicates for parsing HTML and XML documents.
|
||||||
|
Written 2020-2022 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 one of:
|
||||||
|
|
||||||
|
- a list of characters with the document contents
|
||||||
|
- stream(S), specifying a stream S from which to read the content
|
||||||
|
- file(Name), where Name is a list of characters specifying a file name.
|
||||||
|
|
||||||
|
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, an atom, 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) :-
|
||||||
|
must_be_source(Source, load_html/3),
|
||||||
|
must_be(list, Options),
|
||||||
|
load_structure_(Source, Es, Options, html).
|
||||||
|
load_xml(Source, Es, Options) :-
|
||||||
|
must_be_source(Source, load_xml/3),
|
||||||
|
must_be(list, Options),
|
||||||
|
load_structure_(Source, Es, Options, xml).
|
||||||
|
|
||||||
|
must_be_source(Source, Context) :-
|
||||||
|
( var(Source) -> instantiation_error(Context)
|
||||||
|
; is_sgml_source(Source) -> true
|
||||||
|
; domain_error(sgml_source, Source, Context)
|
||||||
|
).
|
||||||
|
|
||||||
|
is_sgml_source(file(Fs)) :- must_be(chars, Fs).
|
||||||
|
is_sgml_source(stream(_)).
|
||||||
|
is_sgml_source([]).
|
||||||
|
is_sgml_source([C|Cs]) :- must_be(chars, [C|Cs]).
|
||||||
|
|
||||||
|
load_structure_([], [], _, _).
|
||||||
|
load_structure_([C|Cs], [E], Options, What) :-
|
||||||
|
load_(What, [C|Cs], E, Options).
|
||||||
|
load_structure_(file(Fs), [E], Options, What) :-
|
||||||
|
once(phrase_from_file(seq(Cs), Fs)),
|
||||||
|
load_(What, Cs, E, Options).
|
||||||
|
load_structure_(stream(Stream), [E], Options, What) :-
|
||||||
|
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).
|
||||||
@@ -27,7 +27,8 @@
|
|||||||
:- module(si, [atom_si/1,
|
:- module(si, [atom_si/1,
|
||||||
integer_si/1,
|
integer_si/1,
|
||||||
atomic_si/1,
|
atomic_si/1,
|
||||||
list_si/1]).
|
list_si/1,
|
||||||
|
chars_si/1]).
|
||||||
|
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
@@ -42,6 +43,16 @@ integer_si(I) :-
|
|||||||
atomic_si(AC) :-
|
atomic_si(AC) :-
|
||||||
functor(AC,_,0).
|
functor(AC,_,0).
|
||||||
|
|
||||||
list_si(L) :-
|
% list_si(L) :-
|
||||||
\+ \+ length(L, _),
|
% \+ \+ length(L, _),
|
||||||
sort(L, _).
|
% sort(L, _).
|
||||||
|
|
||||||
|
list_si(L0) :-
|
||||||
|
'$skip_max_list'(_,_, L0,L),
|
||||||
|
( nonvar(L) -> L = []
|
||||||
|
; throw(error(instantiation_error, list_si/1))
|
||||||
|
).
|
||||||
|
|
||||||
|
chars_si(Cs) :-
|
||||||
|
list_si(Cs),
|
||||||
|
'$is_partial_string'(Cs).
|
||||||
1379
src/lib/simplex.pl
Normal file
1379
src/lib/simplex.pl
Normal file
File diff suppressed because it is too large
Load Diff
57
src/lib/sockets.pl
Normal file
57
src/lib/sockets.pl
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
:- module(sockets, [socket_client_open/3,
|
||||||
|
socket_server_open/2,
|
||||||
|
socket_server_accept/4,
|
||||||
|
socket_server_close/1,
|
||||||
|
current_hostname/1]).
|
||||||
|
|
||||||
|
:- use_module(library(error)).
|
||||||
|
|
||||||
|
socket_client_open(Addr, Stream, Options) :-
|
||||||
|
( var(Addr) ->
|
||||||
|
throw(error(instantiation_error, socket_client_open/3))
|
||||||
|
;
|
||||||
|
true
|
||||||
|
),
|
||||||
|
must_be(var, Stream),
|
||||||
|
must_be(list, Options),
|
||||||
|
( Addr = Address:Port,
|
||||||
|
atom(Address),
|
||||||
|
( atom(Port) ; integer(Port) ) ->
|
||||||
|
true
|
||||||
|
;
|
||||||
|
throw(error(type_error(socket_address, Addr), socket_client_open/3))
|
||||||
|
),
|
||||||
|
builtins:parse_stream_options(Options,
|
||||||
|
[Alias, EOFAction, Reposition, Type],
|
||||||
|
socket_client_open/3),
|
||||||
|
'$socket_client_open'(Address, Port, Stream, Alias, EOFAction, Reposition, Type).
|
||||||
|
|
||||||
|
|
||||||
|
socket_server_open(Addr, ServerSocket) :-
|
||||||
|
must_be(var, ServerSocket),
|
||||||
|
( ( integer(Addr) ; var(Addr) ) ->
|
||||||
|
'$socket_server_open'([], Addr, ServerSocket)
|
||||||
|
;
|
||||||
|
Addr = Address:Port,
|
||||||
|
must_be(atom, Address),
|
||||||
|
can_be(integer, Port),
|
||||||
|
'$socket_server_open'(Address, Port, ServerSocket)
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
socket_server_accept(ServerSocket, Client, Stream, Options) :-
|
||||||
|
must_be(var, Client),
|
||||||
|
must_be(var, Stream),
|
||||||
|
builtins:parse_stream_options(Options,
|
||||||
|
[Alias, EOFAction, Reposition, Type],
|
||||||
|
socket_server_accept/4),
|
||||||
|
'$socket_server_accept'(ServerSocket, Client, Stream, Alias, EOFAction, Reposition, Type).
|
||||||
|
|
||||||
|
|
||||||
|
socket_server_close(ServerSocket) :-
|
||||||
|
'$socket_server_close'(ServerSocket).
|
||||||
|
|
||||||
|
|
||||||
|
current_hostname(HostName) :-
|
||||||
|
'$current_hostname'(HostName).
|
||||||
@@ -8,12 +8,12 @@
|
|||||||
op(1150, fx, table)
|
op(1150, fx, table)
|
||||||
]).
|
]).
|
||||||
|
|
||||||
:- use_module('tabling/double_linked_list').
|
:- use_module(library(tabling/double_linked_list)).
|
||||||
:- use_module('tabling/table_data_structure').
|
:- use_module(library(tabling/table_data_structure)).
|
||||||
:- use_module('tabling/batched_worklist').
|
:- use_module(library(tabling/batched_worklist)).
|
||||||
:- use_module('tabling/wrapper').
|
:- use_module(library(tabling/global_worklist)).
|
||||||
:- use_module('tabling/global_worklist').
|
:- use_module(library(tabling/table_link_manager)).
|
||||||
:- use_module('tabling/table_link_manager').
|
:- use_module(library(tabling/wrapper)).
|
||||||
|
|
||||||
:- use_module(library(cont)).
|
:- use_module(library(cont)).
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
@@ -66,6 +66,9 @@ table_and_status_for_variant(V,T,S) :-
|
|||||||
table_for_variant(V,T),
|
table_for_variant(V,T),
|
||||||
tbd_table_status(T,S).
|
tbd_table_status(T,S).
|
||||||
|
|
||||||
|
|
||||||
|
:- meta_predicate start_tabling(?, :).
|
||||||
|
|
||||||
start_tabling(Wrapper,Worker) :-
|
start_tabling(Wrapper,Worker) :-
|
||||||
put_new_trie_table_link,
|
put_new_trie_table_link,
|
||||||
put_new_global_worklist,
|
put_new_global_worklist,
|
||||||
@@ -136,18 +139,14 @@ activate(Wrapper,Worker,T) :-
|
|||||||
delim(Wrapper,Worker,Table) :-
|
delim(Wrapper,Worker,Table) :-
|
||||||
% debug(tabling, 'ACT: ~p on ~p', [Wrapper, Table]),
|
% debug(tabling, 'ACT: ~p on ~p', [Wrapper, Table]),
|
||||||
reset(Worker,SourceCall,Continuation),
|
reset(Worker,SourceCall,Continuation),
|
||||||
( Continuation == none, var(SourceCall) ->
|
( Continuation = none ->
|
||||||
( add_answer(Table,Wrapper)
|
( add_answer(Table,Wrapper)
|
||||||
-> true %debug(tabling, 'ADD: ~p', [Wrapper])
|
-> true %debug(tabling, 'ADD: ~p', [Wrapper])
|
||||||
; %debug(tabling, 'DUP: ~p', [Wrapper]),
|
; %debug(tabling, 'DUP: ~p', [Wrapper]),
|
||||||
fail
|
fail
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
( Continuation = cont(Cont) ->
|
Continuation = cont(Cont),
|
||||||
true
|
|
||||||
; Continuation = none ->
|
|
||||||
Cont = true
|
|
||||||
),
|
|
||||||
SourceCall = call_info(_,SourceTable),
|
SourceCall = call_info(_,SourceTable),
|
||||||
TargetCall = call_info(Wrapper,Table),
|
TargetCall = call_info(Wrapper,Table),
|
||||||
Dependency = dependency(SourceCall,Cont,TargetCall),
|
Dependency = dependency(SourceCall,Cont,TargetCall),
|
||||||
@@ -45,14 +45,16 @@
|
|||||||
wkl_worklist_work_done/1 % +WorkList
|
wkl_worklist_work_done/1 % +WorkList
|
||||||
]).
|
]).
|
||||||
|
|
||||||
:- use_module(global_worklist).
|
:- use_module(library(tabling/global_worklist)).
|
||||||
:- use_module(double_linked_list).
|
:- use_module(library(tabling/double_linked_list)).
|
||||||
|
|
||||||
:- use_module(library(atts)).
|
:- use_module(library(atts)).
|
||||||
:- use_module(library(lists)).
|
:- 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.
|
:- 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
|
/** <module> Tabling Worklist management
|
||||||
|
|
||||||
A batched worklist: a worklist that clusters suspensions and answers as
|
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).
|
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.
|
% 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_update_righmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer) :-
|
||||||
( wkl_p_answer_cluster_currently_moved_completely(Worklist,InnerAnswerClusterPointer) ->
|
( wkl_p_answer_cluster_currently_moved_completely(Worklist,InnerAnswerClusterPointer) ->
|
||||||
wkl_p_find_new_rightmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer,NewRiacPointer),
|
wkl_p_find_new_rightmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer,NewRiacPointer),
|
||||||
@@ -291,7 +295,7 @@ wkl_add_to_existing_answer_cluster(Worklist, Answer) :-
|
|||||||
wkl_add_to_new_answer_cluster(
|
wkl_add_to_new_answer_cluster(
|
||||||
wkl_worklist(Dll,_Ria,_FlagExecutingWork,_AlreadyInMetaworklist,_TableIdentifier),
|
wkl_worklist(Dll,_Ria,_FlagExecutingWork,_AlreadyInMetaworklist,_TableIdentifier),
|
||||||
Answer,AnswerClusterPointer
|
Answer,AnswerClusterPointer
|
||||||
) :-
|
) :-
|
||||||
dll_append_left(Dll,wkl_answer_cluster(AnswerFlag),AnswerClusterPointer),
|
dll_append_left(Dll,wkl_answer_cluster(AnswerFlag),AnswerClusterPointer),
|
||||||
put_atts(AnswerFlag, wkl_answer_cluster([Answer])).
|
put_atts(AnswerFlag, wkl_answer_cluster([Answer])).
|
||||||
|
|
||||||
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
:- attribute table_global_worklist/1.
|
:- attribute table_global_worklist/1.
|
||||||
|
|
||||||
|
verify_attributes(_, _, []).
|
||||||
|
|
||||||
put_new_global_worklist :-
|
put_new_global_worklist :-
|
||||||
( bb_get(table_global_worklist_initialized, _) ->
|
( bb_get(table_global_worklist_initialized, _) ->
|
||||||
true
|
true
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
get_nb_identifiers/3 % +Table, -NbWorklistID, -NbAnswerTreeID
|
get_nb_identifiers/3 % +Table, -NbWorklistID, -NbAnswerTreeID
|
||||||
]).
|
]).
|
||||||
|
|
||||||
:- use_module(table_link_manager).
|
:- use_module(library(tabling/table_link_manager)).
|
||||||
:- use_module(trie).
|
:- use_module(library(tabling/trie)).
|
||||||
|
|
||||||
/* Part of SWI-Prolog
|
/* Part of SWI-Prolog
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
POSSIBILITY OF SUCH DAMAGE.
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
:- use_module(batched_worklist).
|
:- use_module(library(tabling/batched_worklist)).
|
||||||
|
|
||||||
:- use_module(library(atts)).
|
:- use_module(library(atts)).
|
||||||
:- use_module(library(gensym)).
|
:- use_module(library(gensym)).
|
||||||
@@ -61,6 +61,8 @@
|
|||||||
|
|
||||||
:- attribute table_status/1, newly_created_table_identifiers/1.
|
:- attribute table_status/1, newly_created_table_identifiers/1.
|
||||||
|
|
||||||
|
verify_attributes(_, _, []).
|
||||||
|
|
||||||
% This file defines the table datastructure.
|
% This file defines the table datastructure.
|
||||||
%
|
%
|
||||||
% The table datastructure contains the following sub-structures:
|
% The table datastructure contains the following sub-structures:
|
||||||
@@ -47,10 +47,12 @@
|
|||||||
:- use_module(library(iso_ext)).
|
:- use_module(library(iso_ext)).
|
||||||
:- use_module(library(terms)).
|
:- use_module(library(terms)).
|
||||||
|
|
||||||
:- use_module(trie).
|
:- use_module(library(tabling/trie)).
|
||||||
|
|
||||||
:- attribute trie_table_link/1.
|
:- attribute trie_table_link/1.
|
||||||
|
|
||||||
|
verify_attributes(_, _, []).
|
||||||
|
|
||||||
% This file defines a call pattern trie.
|
% This file defines a call pattern trie.
|
||||||
%
|
%
|
||||||
% This data structure keeps the relation between a variant and the
|
% This data structure keeps the relation between a variant and the
|
||||||
@@ -41,12 +41,16 @@
|
|||||||
trie_get_all_values/2 % +Trie, -Value
|
trie_get_all_values/2 % +Trie, -Value
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
:- use_module(library(format)).
|
||||||
|
|
||||||
:- use_module(library(assoc)).
|
:- use_module(library(assoc)).
|
||||||
:- use_module(library(atts)).
|
:- use_module(library(atts)).
|
||||||
:- use_module(library(lists)).
|
:- use_module(library(lists)).
|
||||||
|
|
||||||
:- attribute maybe_just/1, children/1.
|
:- attribute maybe_just/1, children/1.
|
||||||
|
|
||||||
|
verify_attributes(_, _, []).
|
||||||
|
|
||||||
% Implementation of a prefix tree, a.k.a. trie %
|
% 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,-FunctorData,-ArgumentsList).
|
||||||
p_trie_arity_univ(Term,functor_data(Name,Arity),Arguments) :-
|
p_trie_arity_univ(Term,functor_data(Name,Arity),Arguments) :-
|
||||||
( var(Term) ->
|
( var(Term) ->
|
||||||
Name = var,
|
Name = Term,
|
||||||
Arity = 0,
|
Arity = 0,
|
||||||
Arguments = []
|
Arguments = []
|
||||||
; Term =.. [Name|Arguments],
|
; Term =.. [Name|Arguments],
|
||||||
@@ -40,6 +40,8 @@
|
|||||||
:- use_module(library(dcgs)).
|
:- use_module(library(dcgs)).
|
||||||
:- use_module(library(error)).
|
:- use_module(library(error)).
|
||||||
|
|
||||||
|
:- multifile(tabled/2).
|
||||||
|
|
||||||
%%:- multifile
|
%%:- multifile
|
||||||
%% system:term_expansion/2,
|
%% system:term_expansion/2,
|
||||||
%% tabled/2.
|
%% tabled/2.
|
||||||
@@ -54,8 +56,8 @@
|
|||||||
%% table(PIList) :-
|
%% table(PIList) :-
|
||||||
%% throw(error(context_error(nodirective, table(PIList)), _)).
|
%% throw(error(context_error(nodirective, table(PIList)), _)).
|
||||||
|
|
||||||
instantiation_error(Var) :-
|
%% instantiation_error(Var) :-
|
||||||
throw(error(instantiation_error(Var), _)).
|
%% throw(error(instantiation_error(Var), _)).
|
||||||
|
|
||||||
wrappers(Var) -->
|
wrappers(Var) -->
|
||||||
{ var(Var), !,
|
{ var(Var), !,
|
||||||
@@ -75,7 +77,7 @@ wrappers(Name/Arity) -->
|
|||||||
atom_concat(Name, ' tabled', WrapName),
|
atom_concat(Name, ' tabled', WrapName),
|
||||||
Head =.. [Name|Args],
|
Head =.. [Name|Args],
|
||||||
WrappedHead =.. [WrapName|Args],
|
WrappedHead =.. [WrapName|Args],
|
||||||
'$module_of'(Module, Name) %prolog_load_context(module, Module)
|
prolog_load_context(module, Module)
|
||||||
},
|
},
|
||||||
[ ( Head :-
|
[ ( Head :-
|
||||||
start_tabling(Module:Head, WrappedHead)
|
start_tabling(Module:Head, WrappedHead)
|
||||||
@@ -93,10 +95,15 @@ rename((Head --> Body), (NewHead --> Body), Module) :- !,
|
|||||||
functor(Head, Name, Arity),
|
functor(Head, Name, Arity),
|
||||||
PlainArity is Arity+1,
|
PlainArity is Arity+1,
|
||||||
functor(PlainHead, Name, PlainArity),
|
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_term(Head, NewHead).
|
||||||
rename(Head, NewHead, Module) :-
|
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(Head, NewHead).
|
||||||
|
|
||||||
rename_term(Compound0, Compound) :-
|
rename_term(Compound0, Compound) :-
|
||||||
@@ -109,10 +116,10 @@ rename_term(Name, WrapName) :-
|
|||||||
|
|
||||||
|
|
||||||
user:term_expansion(Term0, Clauses) :-
|
user:term_expansion(Term0, Clauses) :-
|
||||||
nonvar(Term0),
|
nonvar(Term0),
|
||||||
Term0 = (:- table Preds),
|
Term0 = (:- table Preds),
|
||||||
phrase(wrappers(Preds), Clauses).
|
phrase(wrappers(Preds), Clauses).
|
||||||
user:term_expansion(Clause, NewClause) :-
|
user:term_expansion(Clause, NewClause) :-
|
||||||
nonvar(Clause),
|
nonvar(Clause),
|
||||||
'$module_of'(Module, Clause),
|
prolog_load_context(module, Module),
|
||||||
rename(Clause, NewClause, Module).
|
rename(Clause, NewClause, Module).
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
numbervars(Term, N0, N) :-
|
numbervars(Term, N0, N) :-
|
||||||
catch(internal_numbervars(Term, N0, N),
|
catch(internal_numbervars(Term, N0, N),
|
||||||
error(E,Ctx),
|
error(E,Ctx),
|
||||||
( ( var(Ctx) -> Ctx = numbervars/3 ; true ), throw(error(E,Ctx) ) ) ).
|
( ( var(Ctx) -> Ctx = numbervars/3 ; true ), throw(error(E,Ctx) ) ) ).
|
||||||
|
|
||||||
internal_numbervars(Term, N0, N) :-
|
internal_numbervars(Term, N0, N) :-
|
||||||
must_be(integer, N0),
|
must_be(integer, N0),
|
||||||
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('$answer_count', 0) ->
|
||||||
|
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(xpath)).
|
||||||
|
:- use_module(library(dcgs)).
|
||||||
|
|
||||||
|
link_to_pl_file(File) :-
|
||||||
|
http_open("https://github.com/mthom/scryer-prolog", S, []),
|
||||||
|
load_html(stream(S), DOM, []),
|
||||||
|
xpath(DOM, //a(@href), File),
|
||||||
|
phrase((...,".pl"), File).
|
||||||
|
|
||||||
|
Yielding:
|
||||||
|
|
||||||
|
?- link_to_pl_file(File).
|
||||||
|
%@ File = "/mthom/scryer-prolog/blob/master/src/lib/dcgs.pl"
|
||||||
|
%@ ; File = "/mthom/scryer-prolog/blob/master/src/lib/pio.pl"
|
||||||
|
%@ ; File = "/mthom/scryer-prolog/blob/master/src/lib/tabling.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.
|
||||||
2147
src/loader.pl
Normal file
2147
src/loader.pl
Normal file
File diff suppressed because it is too large
Load Diff
16
src/machine/args.rs
Normal file
16
src/machine/args.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MachineArgs {
|
||||||
|
pub add_history: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MachineArgs {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let args: BTreeSet<String> = env::args().collect();
|
||||||
|
Self {
|
||||||
|
add_history: !args.contains("--no-add-history"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1427
src/machine/arithmetic_ops.rs
Normal file
1427
src/machine/arithmetic_ops.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,11 @@
|
|||||||
|
:- module('$atts', []).
|
||||||
|
|
||||||
|
|
||||||
driver(Vars, Values) :-
|
driver(Vars, Values) :-
|
||||||
iterate(Vars, Values, ListOfListsOfGoalLists),
|
iterate(Vars, Values, ListOfListsOfGoalLists),
|
||||||
!,
|
!,
|
||||||
call_goals(ListOfListsOfGoalLists),
|
call_goals(ListOfListsOfGoalLists),
|
||||||
|
'$reset_attr_var_state',
|
||||||
'$return_from_verify_attr'.
|
'$return_from_verify_attr'.
|
||||||
|
|
||||||
iterate([Var|VarBindings], [Value|ValueBindings], [ListOfGoalLists | ListsCubed]) :-
|
iterate([Var|VarBindings], [Value|ValueBindings], [ListOfGoalLists | ListsCubed]) :-
|
||||||
@@ -11,9 +15,9 @@ iterate([Var|VarBindings], [Value|ValueBindings], [ListOfGoalLists | ListsCubed]
|
|||||||
iterate(VarBindings, ValueBindings, ListsCubed).
|
iterate(VarBindings, ValueBindings, ListsCubed).
|
||||||
iterate([], [], []).
|
iterate([], [], []).
|
||||||
|
|
||||||
|
|
||||||
gather_modules(Attrs, []) :- var(Attrs), !.
|
gather_modules(Attrs, []) :- var(Attrs), !.
|
||||||
gather_modules([Attr|Attrs], [Module|Modules]) :-
|
gather_modules([Module:_|Attrs], [Module|Modules]) :-
|
||||||
'$module_of'(Module, Attr), % write the owning module of Attr to Module.
|
|
||||||
gather_modules(Attrs, Modules).
|
gather_modules(Attrs, Modules).
|
||||||
|
|
||||||
call_verify_attributes(Attrs, _, _, []) :-
|
call_verify_attributes(Attrs, _, _, []) :-
|
||||||
@@ -24,27 +28,32 @@ call_verify_attributes([Attr|Attrs], Var, Value, ListOfGoalLists) :-
|
|||||||
sort(Modules0, Modules),
|
sort(Modules0, Modules),
|
||||||
verify_attrs(Modules, Var, Value, ListOfGoalLists).
|
verify_attrs(Modules, Var, Value, ListOfGoalLists).
|
||||||
|
|
||||||
verify_attrs([Module|Modules], Var, Value, [Goals|ListOfGoalLists]) :-
|
error_handler(M, evaluation_error((M:verify_attributes)/3), []).
|
||||||
|
% error_handler(_, existence_error(procedure, verify_attributes/3), []).
|
||||||
|
|
||||||
|
verify_attrs([Module|Modules], Var, Value, [Module-Goals|ListOfGoalLists]) :-
|
||||||
catch(Module:verify_attributes(Var, Value, Goals),
|
catch(Module:verify_attributes(Var, Value, Goals),
|
||||||
error(evaluation_error((Module:verify_attributes)/3), verify_attributes/3),
|
error(E, verify_attributes/3),
|
||||||
Goals = []),
|
error_handler(Module, E, Goals)),
|
||||||
verify_attrs(Modules, Var, Value, ListOfGoalLists).
|
verify_attrs(Modules, Var, Value, ListOfGoalLists).
|
||||||
verify_attrs([], _, _, []).
|
verify_attrs([], _, _, []).
|
||||||
|
|
||||||
|
|
||||||
call_goals([ListOfGoalLists | ListsCubed]) :-
|
call_goals([ListOfGoalLists | ListsCubed]) :-
|
||||||
call_goals_0(ListOfGoalLists),
|
call_goals_0(ListOfGoalLists),
|
||||||
call_goals(ListsCubed).
|
call_goals(ListsCubed).
|
||||||
call_goals([]).
|
call_goals([]).
|
||||||
|
|
||||||
call_goals_0([GoalList | GoalLists]) :-
|
call_goals_0([Module-GoalList | GoalLists]) :-
|
||||||
( var(GoalList), throw(error(instantiation_error, call_goals_0/1))
|
( var(GoalList),
|
||||||
|
throw(error(instantiation_error, call_goals_0/1))
|
||||||
; true
|
; true
|
||||||
),
|
),
|
||||||
call_goals_1(GoalList),
|
call_goals_1(GoalList, Module),
|
||||||
call_goals_0(GoalLists).
|
call_goals_0(GoalLists).
|
||||||
call_goals_0([]).
|
call_goals_0([]).
|
||||||
|
|
||||||
call_goals_1([Goal | Goals]) :-
|
call_goals_1([Goal | Goals], Module) :-
|
||||||
call(Goal),
|
call(Module:Goal),
|
||||||
call_goals_1(Goals).
|
call_goals_1(Goals, Module).
|
||||||
call_goals_1([]).
|
call_goals_1([], _).
|
||||||
186
src/machine/attributed_variables.rs
Normal file
186
src/machine/attributed_variables.rs
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
use crate::heap_iter::*;
|
||||||
|
use crate::machine::*;
|
||||||
|
use crate::parser::ast::*;
|
||||||
|
use crate::temp_v;
|
||||||
|
use crate::types::*;
|
||||||
|
|
||||||
|
use indexmap::IndexSet;
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::vec::IntoIter;
|
||||||
|
|
||||||
|
pub(super) type Bindings = Vec<(usize, HeapCellValue)>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct AttrVarInitializer {
|
||||||
|
pub(super) attr_var_queue: Vec<usize>,
|
||||||
|
pub(super) bindings: Bindings,
|
||||||
|
pub(super) p: usize,
|
||||||
|
pub(super) cp: usize,
|
||||||
|
// pub(super) instigating_p: usize,
|
||||||
|
pub(super) verify_attrs_loc: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttrVarInitializer {
|
||||||
|
pub(super) fn new(verify_attrs_loc: usize) -> Self {
|
||||||
|
AttrVarInitializer {
|
||||||
|
attr_var_queue: vec![],
|
||||||
|
bindings: vec![],
|
||||||
|
p: 0,
|
||||||
|
cp: 0,
|
||||||
|
verify_attrs_loc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn reset(&mut self) {
|
||||||
|
self.attr_var_queue.clear();
|
||||||
|
self.bindings.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MachineState {
|
||||||
|
pub(super) fn push_attr_var_binding(&mut self, h: usize, addr: HeapCellValue) {
|
||||||
|
if self.attr_var_init.bindings.is_empty() {
|
||||||
|
// save self.p and self.cp and ensure that the next
|
||||||
|
// instruction is InstallVerifyAttrInterrupt.
|
||||||
|
|
||||||
|
self.attr_var_init.p = self.p;
|
||||||
|
self.attr_var_init.cp = self.cp;
|
||||||
|
|
||||||
|
self.p = INSTALL_VERIFY_ATTR_INTERRUPT - 1;
|
||||||
|
self.cp = INSTALL_VERIFY_ATTR_INTERRUPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attr_var_init.bindings.push((h, addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn populate_var_and_value_lists(&mut self) -> (HeapCellValue, HeapCellValue) {
|
||||||
|
let iter = self
|
||||||
|
.attr_var_init
|
||||||
|
.bindings
|
||||||
|
.iter()
|
||||||
|
.map(|(ref h, _)| attr_var_as_cell!(*h));
|
||||||
|
|
||||||
|
let var_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
|
||||||
|
|
||||||
|
let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v);
|
||||||
|
|
||||||
|
let value_list_addr = heap_loc_as_cell!(iter_to_heap_list(&mut self.heap, iter));
|
||||||
|
(var_list_addr, value_list_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_attributes(&mut self) {
|
||||||
|
for (h, _) in &self.attr_var_init.bindings {
|
||||||
|
self.heap[*h] = attr_var_as_cell!(*h);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (var_list_addr, value_list_addr) = self.populate_var_and_value_lists();
|
||||||
|
|
||||||
|
self[temp_v!(1)] = var_list_addr;
|
||||||
|
self[temp_v!(2)] = value_list_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn gather_attr_vars_created_since(&mut self, b: usize) -> IntoIter<HeapCellValue> {
|
||||||
|
let mut attr_vars: Vec<_> = if b >= self.attr_var_init.attr_var_queue.len() {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
self.attr_var_init.attr_var_queue[b..]
|
||||||
|
.iter()
|
||||||
|
.filter_map(|h| {
|
||||||
|
read_heap_cell!(self.store(self.deref(heap_loc_as_cell!(*h))),
|
||||||
|
(HeapCellValueTag::AttrVar, h) => {
|
||||||
|
Some(attr_var_as_cell!(h))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
attr_vars.sort_unstable_by(|a1, a2| {
|
||||||
|
compare_term_test!(self, *a1, *a2).unwrap_or(Ordering::Less)
|
||||||
|
});
|
||||||
|
|
||||||
|
attr_vars.dedup();
|
||||||
|
attr_vars.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) {
|
||||||
|
self.allocate(arity + 3);
|
||||||
|
|
||||||
|
let e = self.e;
|
||||||
|
let and_frame = self.stack.index_and_frame_mut(e);
|
||||||
|
|
||||||
|
for i in 1..arity + 1 {
|
||||||
|
and_frame[i] = self.registers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
and_frame[arity + 1] =
|
||||||
|
fixnum_as_cell!(Fixnum::build_with(self.b0 as i64));
|
||||||
|
and_frame[arity + 2] =
|
||||||
|
fixnum_as_cell!(Fixnum::build_with(self.num_of_args as i64));
|
||||||
|
and_frame[arity + 3] =
|
||||||
|
fixnum_as_cell!(Fixnum::build_with(self.attr_var_init.cp as i64));
|
||||||
|
|
||||||
|
self.verify_attributes();
|
||||||
|
|
||||||
|
self.num_of_args = 3;
|
||||||
|
self.b0 = self.b;
|
||||||
|
self.p = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn attr_vars_of_term(&mut self, cell: HeapCellValue) -> Vec<HeapCellValue> {
|
||||||
|
let mut seen_set = IndexSet::new();
|
||||||
|
let mut seen_vars = vec![];
|
||||||
|
|
||||||
|
let mut iter = stackful_preorder_iter(&mut self.heap, cell);
|
||||||
|
|
||||||
|
while let Some(value) = iter.next() {
|
||||||
|
read_heap_cell!(value,
|
||||||
|
(HeapCellValueTag::AttrVar, h) => {
|
||||||
|
if seen_set.contains(&h) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = unmark_cell_bits!(value);
|
||||||
|
|
||||||
|
seen_vars.push(value);
|
||||||
|
seen_set.insert(h);
|
||||||
|
|
||||||
|
let mut l = h + 1;
|
||||||
|
// let mut list_elements = vec![];
|
||||||
|
// let iter_stack_len = iter.stack_len();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
read_heap_cell!(iter.heap[l],
|
||||||
|
(HeapCellValueTag::Lis) => {
|
||||||
|
iter.push_stack(l);
|
||||||
|
// l = elem + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
|
||||||
|
if h == l {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
l = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// iter.stack_slice_from(iter_stack_len ..).reverse();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
seen_vars
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user