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

|
||||
|
||||
## Phase 1
|
||||
|
||||
Produce an implementation of the Warren Abstract Machine in Rust, done
|
||||
according to the progression of languages in [Warren's Abstract
|
||||
Machine: A Tutorial
|
||||
Reconstruction](http://wambook.sourceforge.net/wambook.pdf).
|
||||
Machine: A Tutorial Reconstruction](https://github.com/mthom/scryer-prolog/blob/master/wambook/wambook.pdf).
|
||||
|
||||
Phase 1 has been completed in that Scryer Prolog implements in some form
|
||||
all of the WAM book, including lists, cuts, Debray allocation, first
|
||||
@@ -30,10 +39,8 @@ Extend Scryer Prolog to include the following, among other features:
|
||||
- [x] Built-in predicates for list processing and top-level declarative
|
||||
control (`setup_call_cleanup/3`, `call_with_inference_limit/3`,
|
||||
etc.)
|
||||
- [x] Default representation of strings as list of chars, using a packed
|
||||
internal representation.
|
||||
- A representation of 'partial strings' as difference lists
|
||||
of characters.
|
||||
- [x] ~~Default representation of strings as lists of characters, using a packed
|
||||
internal representation.~~
|
||||
- [x] `term_expansion/2` and `goal_expansion/2`.
|
||||
- [x] Definite Clause Grammars.
|
||||
- [x] Attributed variables using the SICStus Prolog interface and
|
||||
@@ -42,19 +49,34 @@ Extend Scryer Prolog to include the following, among other features:
|
||||
- [x] Support for `verify_attributes/3`
|
||||
- [x] Support for `attribute_goals/2` and `project_attributes/2`
|
||||
- [x] `call_residue_vars/2`
|
||||
- [x] `if_` and related predicates, following the developments of the
|
||||
paper "Indexing `dif/2`".
|
||||
- [x] `if_/3` and related predicates, following the developments of the
|
||||
paper "[Indexing `dif/2`](https://arxiv.org/abs/1607.01590)".
|
||||
- [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`).
|
||||
- [x] Clause creation and destruction (`asserta/1`, `assertz/1`,
|
||||
`retract/1`, `abolish/1`) with logical update semantics.
|
||||
- [x] Backtrackable and non-backtrackable global variables via `bb_get/2`
|
||||
`bb_put/2` (non-backtrackable) and `bb_b_put/2`
|
||||
(backtrackable).
|
||||
- [ ] Streams and predicates for stream control (_in progress_).
|
||||
- [ ] An incremental compacting garbage collector satisfying the five
|
||||
properties of "Precise Garbage Collection in Prolog."
|
||||
- [x] Delimited continuations based on reset/3, shift/1 (documented in
|
||||
"[Delimited Continuations for Prolog](https://biblio.ugent.be/publication/5646080/file/5646081)").
|
||||
- [x] Tabling library based on delimited continuations
|
||||
(documented in "[Tabling as a Library with Delimited Control](https://biblio.ugent.be/publication/6880648/file/6885145.pdf)").
|
||||
- [x] A _redone_ representation of strings as difference lists of
|
||||
characters, using a packed internal representation.
|
||||
- [x] clp(B) and clp(ℤ) as builtin libraries.
|
||||
- [x] Streams and predicates for stream control.
|
||||
- [x] A simple sockets library representing TCP connections as streams.
|
||||
- [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.
|
||||
- [x] 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](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_)
|
||||
- [ ] Mode declarations.
|
||||
- [ ] Extensions for clp(FD).
|
||||
|
||||
## Phase 3
|
||||
|
||||
@@ -72,271 +94,615 @@ nice to have in the future. They'd make a good project for anyone wanting
|
||||
to contribute code to Scryer Prolog.
|
||||
|
||||
1. Implement the global analysis techniques described in Peter van
|
||||
Roy's thesis, "Can Logic Programming Execute as Fast as Imperative
|
||||
Programming?"
|
||||
Roy's thesis, "[Can Logic Programming Execute as Fast as Imperative
|
||||
Programming?](https://www.info.ucl.ac.be/~pvr/Peter.thesis/Peter.thesis.html)"
|
||||
|
||||
2. Add unum representation and arithmetic, using either an existing
|
||||
unum implementation or an ad hoc one. Unums are described in
|
||||
Gustafson's book "The End of Error."
|
||||
Gustafson's book "[The End of Error](http://www.johngustafson.net/unums.html)."
|
||||
|
||||
3. Add support for shift/reset delimited continuations, see "Delimited
|
||||
Continuations for Prolog."
|
||||
|
||||
4. Add concurrent tables to manage shared references to atoms and
|
||||
3. Add concurrent tables to manage shared references to atoms and
|
||||
strings.
|
||||
|
||||
5. Add optional SLG resolution for fast memoization of predicates.
|
||||
|
||||
6. Add some form of JIT predicate indexing.
|
||||
4. Add some form of JIT predicate indexing.
|
||||
|
||||
## Installing Scryer Prolog
|
||||
|
||||
### Binaries
|
||||
|
||||
Precompiled binaries for several platforms are available for download
|
||||
at:
|
||||
|
||||
**https://github.com/mthom/scryer-prolog/releases/tag/v0.9.3**
|
||||
|
||||
### Native Compilation
|
||||
|
||||
First, install the latest stable version of
|
||||
[Rust](https://www.rust-lang.org/en-US/install.html) using your
|
||||
preferred method. Then install the latest Scryer Prolog with cargo,
|
||||
like so:
|
||||
preferred method. Scryer tends to use features from newer Rust
|
||||
releases, whereas Rust packages in Linux distributions, Macports,
|
||||
etc. tend to lag behind. [rustup](http://rustup.rs) will keep your
|
||||
Rust updated to the latest stable release; any existing Rust
|
||||
distribution should be uninstalled from your system before rustup is
|
||||
used.
|
||||
|
||||
Currently the only way to install the latest version of Scryer is to
|
||||
clone directly from this git repository, and compile the system. This
|
||||
can be done as follows:
|
||||
|
||||
```
|
||||
$> cargo install scryer-prolog
|
||||
$> git clone https://github.com/mthom/scryer-prolog
|
||||
$> cd scryer-prolog
|
||||
$> cargo build --release
|
||||
```
|
||||
|
||||
cargo will download and install the libraries Scryer Prolog uses
|
||||
automatically. You can find the `scryer-prolog` executable in
|
||||
`~/.cargo/bin`.
|
||||
The `--release` flag performs various optimizations, producing a
|
||||
faster executable.
|
||||
|
||||
Note on compatibility: Scryer Prolog should work on Linux, Mac OS X,
|
||||
and BSD variants on which Rust runs. Windows support hinges on
|
||||
rustyline and Termion being functional in that environment, which to
|
||||
my knowledge is not currently the case.
|
||||
After compilation, the executable `scryer-prolog` is available in the
|
||||
directory `target/release` and can be invoked to run the system.
|
||||
|
||||
## Built-in predicates
|
||||
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.
|
||||
|
||||
The following predicates are built-in to Scryer.
|
||||
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.
|
||||
|
||||
* Arithmetic support:
|
||||
* `is/2` works for `(+)/{1,2}`, `(-)/{1,2}`, `(*)/2`, `(//)/2`, `(**)/2`,
|
||||
`(^)/2`, `(div)/2`, `(/)/2`, `(rdiv)/2`, `(xor)/2`, `(rem)/2`,
|
||||
`(mod)/2`, `(/\)/2`, `(\/)/2`, `(>>)/2`,`(<<)/2`, `(\)/1`,
|
||||
`abs/1`, `sin/1`, `cos/1`, `tan/1`, `asin/1`, `acos/1`,
|
||||
`atan/1`, `atan2/2`, `log/1`, `exp/1`, `sqrt/1`, `float/1`,
|
||||
`truncate/1`, `round/1`, `floor/1`, `ceiling/1`, `pi/0`,
|
||||
`min/1`, `max/1`
|
||||
* Comparison operators: `>`, `<`, `=<`, `>=`, `=:=`, `=\=`.
|
||||
* `(:)/2`
|
||||
* `(@>)/2`
|
||||
* `(@>=)/2`
|
||||
* `(@=<)/2`
|
||||
* `(@<)/2`
|
||||
* `(\+)/1`
|
||||
* `(==)/2`
|
||||
* `(\==)/2`
|
||||
* `(=)/2`
|
||||
* `(\=)/2`
|
||||
* `(=..)/2`
|
||||
* `(->)/2`
|
||||
* `(;)/2`
|
||||
* `abolish/1`
|
||||
* `acyclic_term/2`
|
||||
* `append/3`
|
||||
* `arg/3`
|
||||
* `asserta/1`
|
||||
* `assertz/1`
|
||||
* `atom/1`
|
||||
* `atomic/1`
|
||||
* `atom_chars/2`
|
||||
* `atom_codes/2`
|
||||
* `atom_concat/3`
|
||||
* `atom_length/2`
|
||||
* `bagof/3`
|
||||
* `bb_b_put/2`
|
||||
* `bb_get/2`
|
||||
* `bb_put/2`
|
||||
* `between/3`
|
||||
* `call/1..62`
|
||||
* `call_cleanup/2`
|
||||
* `call_with_inference_limit/3`
|
||||
* `call_residue_vars/2`
|
||||
* `can_be/2`
|
||||
* `catch/3`
|
||||
* `clause/2`
|
||||
* `compare/3`
|
||||
* `compound/1`
|
||||
* `copy_term/2`
|
||||
* `current_predicate/1`
|
||||
* `current_op/3`
|
||||
* `cyclic_term/1`
|
||||
* `dif/2`
|
||||
* `expand_goal/2`
|
||||
* `expand_term/2`
|
||||
* `fail/0`
|
||||
* `false/0`
|
||||
* `findall/{3,4}`
|
||||
* `float/1`
|
||||
* `forall/2`
|
||||
* `freeze/2`
|
||||
* `functor/3`
|
||||
* `gen_int/1`
|
||||
* `gen_nat/1`
|
||||
* `get_char/1`
|
||||
* `goal_expansion/2`
|
||||
* `ground/1`
|
||||
* `halt/0`
|
||||
* `integer/1`
|
||||
* `is_list/1`
|
||||
* `is_partial_string/1`
|
||||
* `keysort/2`
|
||||
* `length/2`
|
||||
* `maplist/2..9`
|
||||
* `member/2`
|
||||
* `memberchk/2`
|
||||
* `must_be/2`
|
||||
* `nl/0`
|
||||
* `nonvar/1`
|
||||
* `number_chars/2`
|
||||
* `number_codes/2`
|
||||
* `numbervars/2`
|
||||
* `numlist/{2,3}`
|
||||
* `once/1`
|
||||
* `op/3`
|
||||
* `partial_string/2`
|
||||
* `phrase/{2,3}`
|
||||
* `rational/1`
|
||||
* `read/1`
|
||||
* `repeat/{0,1}`
|
||||
* `retract/1`
|
||||
* `reverse/2`
|
||||
* `select/3`
|
||||
* `setof/3`
|
||||
* `setup_call_cleanup/3`
|
||||
* `sort/2`
|
||||
* `string/1`
|
||||
* `sub_atom/5`
|
||||
* `subsumes_term/2`
|
||||
* `term_expansion/2`
|
||||
* `term_variables/2`
|
||||
* `throw/1`
|
||||
* `true/0`
|
||||
* `unify_with_occurs_check/2`
|
||||
* `use_module/{1,2}`
|
||||
* `user:goal_expansion/2`
|
||||
* `user:term_expansion/2`
|
||||
* `var/1`
|
||||
* `variant/2`
|
||||
* `wam_instructions/2`
|
||||
* `write/1`
|
||||
* `write_canonical/1`
|
||||
* `writeq/1`
|
||||
* `write_term/2`
|
||||
Scryer Prolog must be built with **Rust 1.70 and up**.
|
||||
|
||||
### Building WebAssembly
|
||||
|
||||
Scryer Prolog has basic WebAssembly support. You can follow `wasm-pack`'s [official instructions](https://rustwasm.github.io/docs/wasm-pack/quickstart.html) to install `wasm-pack` and build it in any way you like.
|
||||
|
||||
However, none of the [default features](https://doc.rust-lang.org/cargo/reference/features.html#the-default-feature) are currently supported. The preferred way of disabling them is passing [extra options](https://rustwasm.github.io/wasm-pack/book/commands/build.html#extra-options) to `wasm-pack`.
|
||||
|
||||
For example, if you want a minimal working package without using any bundler like `webpack`, you can do this:
|
||||
```
|
||||
wasm-pack build --target web -- --no-default-features
|
||||
```
|
||||
Then a `pkg` directory will be created, containing everything you need for a webapp. You can test whether the package is successfully built by creating an html file, adapted from `wasm-bindgen`'s [official example](https://rustwasm.github.io/wasm-bindgen/examples/without-a-bundler.html) like this:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Scryer Prolog - Sudoku Solver Example</title>
|
||||
<script type="module">
|
||||
import init, { eval_code } from './pkg/scryer_prolog.js';
|
||||
|
||||
const run = async () => {
|
||||
await init("./pkg/scryer_prolog_bg.wasm");
|
||||
let code = `
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(clpz)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
sudoku(Rows) :-
|
||||
length(Rows, 9), maplist(same_length(Rows), Rows),
|
||||
append(Rows, Vs), Vs ins 1..9,
|
||||
maplist(all_distinct, Rows),
|
||||
transpose(Rows, Columns),
|
||||
maplist(all_distinct, Columns),
|
||||
Rows = [As,Bs,Cs,Ds,Es,Fs,Gs,Hs,Is],
|
||||
blocks(As, Bs, Cs),
|
||||
blocks(Ds, Es, Fs),
|
||||
blocks(Gs, Hs, Is).
|
||||
|
||||
blocks([], [], []).
|
||||
blocks([N1,N2,N3|Ns1], [N4,N5,N6|Ns2], [N7,N8,N9|Ns3]) :-
|
||||
all_distinct([N1,N2,N3,N4,N5,N6,N7,N8,N9]),
|
||||
blocks(Ns1, Ns2, Ns3).
|
||||
|
||||
problem(1, [[_,_,_,_,_,_,_,_,_],
|
||||
[_,_,_,_,_,3,_,8,5],
|
||||
[_,_,1,_,2,_,_,_,_],
|
||||
[_,_,_,5,_,7,_,_,_],
|
||||
[_,_,4,_,_,_,1,_,_],
|
||||
[_,9,_,_,_,_,_,_,_],
|
||||
[5,_,_,_,_,_,_,7,3],
|
||||
[_,_,2,_,1,_,_,_,_],
|
||||
[_,_,_,_,4,_,_,_,9]]).
|
||||
|
||||
main :-
|
||||
problem(1, Rows), sudoku(Rows), maplist(portray_clause, Rows).
|
||||
|
||||
:- initialization(main).
|
||||
`;
|
||||
const result = eval_code(code);
|
||||
document.write(`<p>Sudoku solver returns:</p><pre>${result}</pre>`);
|
||||
}
|
||||
run();
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Then you can serve it with your favorite http server like `python -m http.server` or `npx serve`, and access the page with your browser.
|
||||
|
||||
### Docker Install
|
||||
|
||||
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
|
||||
To enter a multi-clause predicate, the directive "[user]" is used.
|
||||
|
||||
For example,
|
||||
```
|
||||
?- [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.
|
||||
Prolog files are loaded by specifying them as arguments on the command
|
||||
line. For example, to load `program.pl`, use:
|
||||
|
||||
Queries are issued as
|
||||
```
|
||||
?- p(X, Y, Z).
|
||||
$> scryer-prolog program.pl
|
||||
```
|
||||
|
||||
Pressing `SPACE` will backtrack through other possible answers, if any exist.
|
||||
Pressing `.` will abort the search and return to the prompt.
|
||||
Loading a Prolog file is also called “consulting” it. The built-in
|
||||
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].
|
||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
||||
member(X, [X|_]).
|
||||
member(X, [_|Xs]) :- member(X, Xs).
|
||||
?- member(X, [a, b, c]).
|
||||
true .
|
||||
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.
|
||||
true .
|
||||
X = call(f, x) ;
|
||||
X = call(f, y) ;
|
||||
X = call(f, z).
|
||||
hello(declarative_world).
|
||||
hello(pure_world).
|
||||
```
|
||||
|
||||
Note that the values of variables belonging to successful queries are
|
||||
printed out, on one line each. Uninstantiated variables are denoted by
|
||||
a number preceded by an underscore (`X = _0` in an example above).
|
||||
Pressing `RETURN` followed by `Ctrl-d` stops reading from
|
||||
standard input and consults the entered Prolog text.
|
||||
|
||||
After a program is consulted, you can ask *queries* about the
|
||||
predicates it defines. For example, with the program shown above:
|
||||
|
||||
To clear the database, type
|
||||
```
|
||||
?- [clear].
|
||||
?- hello(What).
|
||||
What = declarative_world
|
||||
; What = pure_world.
|
||||
```
|
||||
|
||||
To quit scryer-prolog, type
|
||||
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`:
|
||||
|
||||
```
|
||||
?- halt.
|
||||
```
|
||||
|
||||
### Starting Scryer Prolog
|
||||
|
||||
Scryer Prolog can be started from the command line by specifying
|
||||
options, files and additional arguments. All components are optional:
|
||||
|
||||
<pre>
|
||||
scryer-prolog [OPTIONS] [FILES] [-- ARGUMENTS]
|
||||
</pre>
|
||||
|
||||
The supported options are:
|
||||
|
||||
```
|
||||
-h, --help Display help message
|
||||
-v, --version Print version information and exit
|
||||
-g, --goal GOAL Run the query GOAL after consulting files
|
||||
-f Fast startup. Do not load initialization file (~/.scryerrc)
|
||||
--no-add-history Prevent adding input to history file (~/.scryer_history)
|
||||
```
|
||||
|
||||
All specified Prolog files are consulted.
|
||||
|
||||
After Prolog files, application-specific arguments can be specified on
|
||||
the command line. These arguments can be accessed from within Prolog
|
||||
applications with the predicate `argv/1`, which yields the list
|
||||
of arguments represented as strings.
|
||||
|
||||
Prolog files can also be turned into *shell scripts* as explained in
|
||||
https://github.com/mthom/scryer-prolog/issues/2170#issuecomment-1821713993.
|
||||
|
||||
### Dynamic operators
|
||||
|
||||
Scryer supports dynamic operators. Using the built-in
|
||||
arithmetic operators with the usual precedences,
|
||||
|
||||
```
|
||||
?- write_canonical(-5 + 3 - (2 * 4) // 8).
|
||||
-(+(-(5), 3), //(*(2, 4), 8))
|
||||
true.
|
||||
?- write_canonical(-5 + 3 - (2 * 4) // 8), nl.
|
||||
-(+(-5,3),//(*(2,4),8))
|
||||
true.
|
||||
```
|
||||
|
||||
New operators can be defined using the `op` declaration.
|
||||
|
||||
### Partial strings
|
||||
### First instantiated argument indexing
|
||||
|
||||
Scryer has two specialized, non-ISO builtin predicates for handling
|
||||
so-called "partial strings". Partial strings imitate difference lists
|
||||
of characters, but are much more space efficient. This efficiency
|
||||
comes at the cost of full generality -- you cannot unify the tail
|
||||
variables of two distinct partial strings, because their buffers will
|
||||
always be distinct.
|
||||
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.
|
||||
|
||||
If `X` is a free variable, the query
|
||||
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.
|
||||
|
||||
`?- partial_string("abc", X), X = [a, b, c | Y], is_partial_string(X),
|
||||
is_partial_string(Y).`
|
||||
For example, a natural definition of `maplist/2` reads:
|
||||
|
||||
will succeed. Further, if `Y` a free variable, unifying `Y` against
|
||||
another string, "def" in this case, produces the equations
|
||||
```
|
||||
maplist(_, []).
|
||||
maplist(Goal_1, [L|Ls]) :-
|
||||
call(Goal_1, L),
|
||||
maplist(Goal_1, Ls).
|
||||
```
|
||||
|
||||
`X = [a, b, c, d, e, f], Y = [d, e, f].`
|
||||
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
|
||||
|
||||
A very compact internal representation of *strings* is one of the key
|
||||
innovations of Scryer Prolog. This means that terms which appear as
|
||||
lists of characters to Prolog programs are stored in packed
|
||||
UTF-8 encoding by the engine.
|
||||
|
||||
Without this innovation, storing a list of characters in memory would
|
||||
use one WAM memory cell per character, one cell per list
|
||||
constructor, and one cell for each tail that occurs in the list. Since
|
||||
one cell takes 8 bytes in the WAM as implemented by
|
||||
Scryer Prolog, the packed representation yields an up to
|
||||
**24-fold reduction** of memory usage, and corresponding
|
||||
reduction of memory accesses when creating and processing
|
||||
strings.
|
||||
|
||||
Scryer Prolog's compact internal string representation makes it
|
||||
ideally suited for the use case Prolog was originally developed for:
|
||||
efficient and convenient text processing, especially with definite
|
||||
clause grammars (DCGs) as provided by
|
||||
[`library(dcgs)`](src/lib/dcgs.pl) and
|
||||
[`library(pio)`](src/lib/pio.pl) to transparently apply DCGs to files.
|
||||
|
||||
In Scryer Prolog, the default value of the Prolog flag `double_quotes`
|
||||
is `chars`, which is also the recommended setting. This means that
|
||||
lists of characters can be written as double-quoted strings, in the
|
||||
tradition of Marseille Prolog.
|
||||
|
||||
For example, the following query succeeds:
|
||||
|
||||
```
|
||||
?- "abc" = [a,b,c].
|
||||
true.
|
||||
```
|
||||
|
||||
This shows that the string `"abc"`, which is represented as a sequence
|
||||
of 3 bytes internally, appears to Prolog programs as a list of
|
||||
characters.
|
||||
|
||||
Scryer Prolog uses the same efficient encoding for *partial* strings,
|
||||
which appear to Prolog code as partial lists of characters. The
|
||||
predicate `partial_string/3` from `library(iso_ext)` lets you
|
||||
construct partial strings explicitly. For example:
|
||||
|
||||
```
|
||||
?- partial_string("abc", Ls0, Ls).
|
||||
Ls0 = [a,b,c|Ls].
|
||||
```
|
||||
|
||||
In this case, and as the answer illustrates, `Ls0` is
|
||||
indistinguishable from a partial list with tail `Ls`, while
|
||||
the efficient packed representation is used internally.
|
||||
|
||||
An important design goal of Scryer Prolog is to *automatically* use
|
||||
the efficient string representation whenever possible. Therefore, it
|
||||
is only very rarely necessary to use `partial_string/3` explicitly. In
|
||||
the above example, posting <tt>Ls0 = [a,b,c|Ls]</tt> yields
|
||||
the exact same internal representation, and has the advantage that
|
||||
only the standard predicate `(=)/2` is used.
|
||||
|
||||
The efficient internal representation of strings and partial 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
|
||||
|
||||
Scryer has a simple predicate-based module system. It provides a
|
||||
way to separate units of code into distinct namespaces, for both
|
||||
predicates and operators. See the files `src/prolog/lib/*.pl` for
|
||||
predicates and operators. See the files
|
||||
[`src/lib/*.pl`](src/lib) for
|
||||
examples.
|
||||
|
||||
At the time of this writing, several control and list processing
|
||||
operators and predicates are hidden in their own modules that have not
|
||||
been exported to the toplevel. To export them, write
|
||||
At the time of this writing, many predicates reside in their own
|
||||
modules that need to be imported before they can be used.
|
||||
The modules that ship with Scryer Prolog are also called
|
||||
*library* modules or *libraries*, and include:
|
||||
|
||||
* [`lists`](src/lib/lists.pl)
|
||||
providing `length/2`, `member/2`, `select/3`, `append/[2,3]`,
|
||||
`foldl/[4,5]`, `maplist/[2-9]`, `same_length/2`, `transpose/2` etc.
|
||||
* [`dcgs`](src/lib/dcgs.pl)
|
||||
Definite Clause Grammars (DCGs), a built-in grammar mechanism
|
||||
that uses the operator `(-->)/2` to define grammar rules,
|
||||
and the predicates `phrase/[2,3]` to invoke them.
|
||||
* [`dif`](src/lib/dif.pl)
|
||||
The predicate `dif/2` provides declarative disequality:
|
||||
It is true if and only if its arguments are different, and
|
||||
delays the test until a sound decision can be made.
|
||||
* [`reif`](src/lib/reif.pl)
|
||||
providing `if_/3`, `tfilter/3` and related predicates
|
||||
as described in *Indexing dif/2*.
|
||||
* [`clpz`](src/lib/clpz.pl)
|
||||
CLP(ℤ): Constraint Logic Programming over Integers,
|
||||
providing declarative integer arithmetic via `(#=)/2`, `(#\=)/2`,
|
||||
`(#>=)/2` etc., and various global constraints and
|
||||
enumeration predicates for solving combinatorial tasks.
|
||||
* [`pairs`](src/lib/pairs.pl)
|
||||
By convention, *pairs* are Prolog terms with
|
||||
principal functor `(-)/2`, written as `Key-Value`.
|
||||
This library provides `pairs_keys_values/3`,
|
||||
`pairs_keys/2`, and other predicates to reason about pairs.
|
||||
* [`si`](src/lib/si.pl)
|
||||
The predicates `atom_si/1`, `integer_si/1`, `atomic_si/1`
|
||||
and `list_si/1` implement sound type checks. They raise
|
||||
instantiation errors if no decision can be made.
|
||||
They are declarative replacements for logically flawed
|
||||
lower-level type tests. For instance, instead of `integer(X)`,
|
||||
write `integer_si(X)` to ensure soundness of your programs.
|
||||
"si" stands for *sufficiently instantiated*, and also for
|
||||
*sound inference*.
|
||||
* [`debug`](src/lib/debug.pl)
|
||||
Various predicates that allow for declarative debugging.
|
||||
* [`pio`](src/lib/pio.pl)
|
||||
`phrase_from_file/2` applies a DCG nonterminal to the contents of a
|
||||
file, reading lazily only as much as is needed. Due to the compact
|
||||
internal string representation, also extremely large files can be
|
||||
efficiently processed with Scryer Prolog in this way.
|
||||
`phrase_to_file/2` and `phrase_to_stream/2` write lists of
|
||||
characters described by DCGs to files and streams, respectively.
|
||||
* [`lambda`](src/lib/lambda.pl)
|
||||
Lambda expressions to simplify higher order programming.
|
||||
* [`charsio`](src/lib/charsio.pl) Various predicates that are useful
|
||||
for parsing and reasoning about characters, notably `char_type/2` to
|
||||
classify characters according to their type, and conversion
|
||||
predicates for different encodings of strings.
|
||||
* [`error`](src/lib/error.pl)
|
||||
`must_be/2` and `can_be/2` complement the type checks provided by
|
||||
[`library(si)`](src/lib/si.pl), and are especially useful for
|
||||
Prolog library authors.
|
||||
* [`tabling`](src/lib/tabling.pl)
|
||||
The operator `(table)/1` is used in directives that prepare
|
||||
predicates for tabled execution (SLG resolution).
|
||||
* [`format`](src/lib/format.pl)
|
||||
The nonterminal `format_//2` is used to describe formatted output,
|
||||
arranging arguments according to a given format string.
|
||||
The predicates `format/[2,3]`, `portray_clause/[1,2]` and `listing/1`
|
||||
provide formatted *impure* output.
|
||||
* [`assoc`](src/lib/assoc.pl)
|
||||
providing `empty_assoc/1`, `get_assoc/3`, `put_assoc/4` etc.
|
||||
to manage elements in AVL trees which ensure
|
||||
*O*(log(*N*)) access.
|
||||
* [`ordsets`](src/lib/ordsets.pl)
|
||||
represents ordered sets as lists.
|
||||
* [`clpb`](src/lib/clpb.pl)
|
||||
CLP(B): Constraint Logic Programming over Boolean variables,
|
||||
a BDD-based SAT solver provided via the predicates
|
||||
`sat/1`, `taut/2`, `labeling/1` etc.
|
||||
* [`arithmetic`](src/lib/arithmetic.pl)
|
||||
Arithmetic predicates such as `lsb/2`, `msb/2` and
|
||||
`number_to_rational/2`.
|
||||
* [`time`](src/lib/time.pl) Predicates for reasoning about
|
||||
time, including `time/1` to measure the CPU time of a goal,
|
||||
`current_time/1` to obtain the current system time, the nonterminal
|
||||
`format_time//2` to describe strings with dates and times, and
|
||||
`sleep/1` to slow down a computation.
|
||||
* [`files`](src/lib/files.pl)
|
||||
Predicates for reasoning about files and directories, such as
|
||||
`directory_files/2`, `file_exists/1` and `file_size/2`.
|
||||
* [`cont`](src/lib/cont.pl)
|
||||
Provides *delimited continuations* via `reset/3` and `shift/1`.
|
||||
* [`random`](src/lib/random.pl)
|
||||
Probabilistic predicates and random number generators.
|
||||
* [`http/http_open`](src/lib/http/http_open.pl) Open a stream to
|
||||
read answers from web servers. HTTPS is also supported.
|
||||
* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.1 and HTTP/2.0 web server. Uses [Warp](https://github.com/seanmonstar/warp) as a backend. Supports some query and form handling.
|
||||
* [`sgml`](src/lib/sgml.pl)
|
||||
`load_html/3` and `load_xml/3` represent HTML and XML documents
|
||||
as Prolog terms for convenient and efficient reasoning. Use
|
||||
[`library(xpath)`](src/lib/iso_ext.pl) to extract information from
|
||||
parsed documents.
|
||||
* [`csv`](src/lib/csv.pl)
|
||||
`parse_csv//1` and `parse_csv//2` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
|
||||
or [`phrase/2`](src/lib/dcgs.pl) to parse csv
|
||||
* [`serialization/abnf`](src/lib/serialization/abnf.pl)
|
||||
DCGs describing the
|
||||
[ABNF grammar core (RFC 5234)](https://tools.ietf.org/html/rfc5234#appendix-B.1),
|
||||
which is used to describe many [IETF](https://www.ietf.org/standards/rfcs/)
|
||||
syntaxes, such as [HTTP v1.1](https://www.rfc-editor.org/rfc/rfc7230.html#page-82),
|
||||
[SMTP](https://www.rfc-editor.org/rfc/rfc5321.html),
|
||||
[iCalendar](https://www.rfc-editor.org/rfc/rfc5545.html), and more.
|
||||
* [`serialization/json`](src/lib/serialization/json.pl)
|
||||
`json_chars//1` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
|
||||
or [`phrase/2`](src/lib/dcgs.pl) to parse and generate
|
||||
[JSON](https://www.json.org/json-en.html).
|
||||
* [`xpath`](src/lib/xpath.pl)
|
||||
The predicate `xpath/3` is used for convenient reasoning about HTML
|
||||
and XML documents, inspired by the XPath language. This library
|
||||
is often used together with [`library(sgml)`](src/lib/sgml.pl).
|
||||
* [`sockets`](src/lib/sockets.pl)
|
||||
Predicates for opening and accepting TCP connections as streams.
|
||||
* [`os`](src/lib/os.pl)
|
||||
Predicates for reasoning about environment variables.
|
||||
* [`iso_ext`](src/lib/iso_ext.pl)
|
||||
Conforming extensions to and candidates for inclusion in the Prolog
|
||||
ISO standard, such as `setup_call_cleanup/3`, `call_nth/2` and
|
||||
`call_with_inference_limit/3`.
|
||||
* [`crypto`](src/lib/crypto.pl)
|
||||
Cryptographically secure random numbers and hashes, HMAC-based key
|
||||
derivation (HKDF), password-based key derivation (PBKDF2),
|
||||
public key signatures and signature verification with Ed25519,
|
||||
ECDH key exchange over Curve25519 (X25519), authenticated symmetric
|
||||
encryption with ChaCha20-Poly1305, and reasoning about elliptic curves.
|
||||
* [`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:
|
||||
|
||||
```
|
||||
?- use_module(library(lists)).
|
||||
@@ -371,7 +737,6 @@ REPL:
|
||||
|
||||
```
|
||||
?- [user].
|
||||
(type Enter + Ctrl-D to terminate the stream when finished)
|
||||
:- module(test, [local_member/2]).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
@@ -379,4 +744,79 @@ local_member(X, Xs) :- member(X, Xs).
|
||||
```
|
||||
|
||||
The user listing can also be terminated by placing `end_of_file.` at
|
||||
the end of the stream.
|
||||
the end of the stream.
|
||||
|
||||
### Configuration file
|
||||
|
||||
At startup, Scryer Prolog consults the file `~/.scryerrc`, if the file
|
||||
exists. This file is useful to automatically load libraries and define
|
||||
predicates that you need often.
|
||||
|
||||
For example, a sensible starting point for `~/.scryerrc` is:
|
||||
|
||||
```
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(reif)).
|
||||
```
|
||||
|
||||
### Development environment
|
||||
|
||||
To write and edit Prolog programs, we recommend
|
||||
[GNU Emacs](https://www.gnu.org/software/emacs/) with the
|
||||
[Prolog mode](https://bruda.ca/emacs/prolog_mode_for_emacs)
|
||||
maintained by Stefan Bruda.
|
||||
|
||||
Use [ediprolog](https://www.metalevel.at/ediprolog/) to consult
|
||||
Prolog code and evaluate Prolog queries in arbitrary
|
||||
Emacs buffers.
|
||||
|
||||
Emacs definitions that show Prolog terms as trees are available
|
||||
in [tools](tools).
|
||||
|
||||
To *debug* Prolog code, we recommend the predicates from
|
||||
[**`library(debug)`**](src/lib/debug.pl), most notably:
|
||||
|
||||
- `(*)/1` to *"generalize away"* a Prolog goal. Use it to debug
|
||||
unexpected failures by generalizing your definitions until they
|
||||
succeed. Simply place `*` in front of a goal to generalize it away.
|
||||
- `($)/1` to emit a *trace* of the execution, showing when a goal
|
||||
is invoked, and when it has succeeded. Place `$` in front of a goal
|
||||
to emit this information for that goal.
|
||||
|
||||
This way of debugging Prolog code has several major benefits, such as:
|
||||
It stays close to the actual Prolog code under consideration, it does
|
||||
not need additional tools and formalisms for its application, and
|
||||
further, it encourages declarative reasoning that can in principle
|
||||
also be performed automatically.
|
||||
|
||||
## Applications
|
||||
|
||||
Scryer Prolog's strong commitment to the Prolog ISO standard makes it
|
||||
ideally suited for use in corporations and government agencies
|
||||
that are subject to strict regulations pertaining to interoperability,
|
||||
standards compliance and warranty.
|
||||
|
||||
Successful existing applications of Scryer Prolog include the
|
||||
[DocLog](https://github.com/aarroyoc/doclog) system which
|
||||
generates Scryer's own documentation and homepage, [Symbolic
|
||||
Analysis of Grants](https://www.brz.gv.at/en/BRZ-Tech-Blog/Tech-Blog-7-Symbolic-Analysis-of-Grants.html)
|
||||
by the Austrian Federal Computing Center, and parts of the
|
||||
[precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog)
|
||||
package for the analysis of dose-escalation trials in the
|
||||
safety-critical and highly regulated domain of oncology
|
||||
trial design, described in [*An Executable Specification of
|
||||
Oncology Dose-Escalation Protocols with Prolog*](https://arxiv.org/abs/2402.08334).
|
||||
|
||||
Scryer Prolog is also very well suited for teaching and learning
|
||||
Prolog, and for testing syntactic conformance and hence portability of
|
||||
existing Prolog programs.
|
||||
|
||||
## Support and discussions
|
||||
|
||||
If Scryer Prolog crashes or yields unexpected errors, consider filing
|
||||
an [issue](https://github.com/mthom/scryer-prolog/issues).
|
||||
|
||||
To get in touch with the Scryer Prolog community, participate in
|
||||
[discussions](https://github.com/mthom/scryer-prolog/discussions)
|
||||
or visit our #scryer IRC channel on [Libera](https://libera.chat)!
|
||||
|
||||
95
benches/README.md
Normal file
95
benches/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# About benches
|
||||
|
||||
The `benches` directory contains benchmarks that test scryer-prolog performance.
|
||||
|
||||
Benchmarks are run via two harnesses:
|
||||
|
||||
* `criterion` - criterion performs statistical analysis of benchmark runs and is
|
||||
great for benchmarking locally.
|
||||
* `iai-callgrind` - this runs the benchmark with callgrind, which is able to
|
||||
precisely track the number of instructions executed during the run. This is
|
||||
especially helpful in a public CI runner context where neighboring VMs can
|
||||
cause a very high wall time variance. While instructions executed is only
|
||||
correlated with the desired metric (wall time), this is a good tradeoff for CI
|
||||
where that metric is unreliable.
|
||||
|
||||
Run benchmarks with the following commands:
|
||||
|
||||
```
|
||||
cargo bench --bench run_criterion
|
||||
|
||||
# run a particular criterion benchmark
|
||||
cargo bench --bench run_criterion -- <benchmark_name>
|
||||
|
||||
# run in profiling mode which outputs flamegraphs. Set profile time in seconds:
|
||||
cargo bench --bench run_criterion -- --profile-time <time>
|
||||
|
||||
# to run iai, you need valgrind installed and to install iai-callgrind-runner
|
||||
# at the same version as is in Cargo.toml:
|
||||
cargo install iai-callgrind-runner --version 0.7.3
|
||||
|
||||
cargo bench --bench run_iai
|
||||
```
|
||||
|
||||
For consistency, both runners -- `run_iai.rs` and `run_criterion.rs` -- import
|
||||
the same setup code from `setup.rs`.
|
||||
|
||||
## Setup
|
||||
|
||||
`setup.rs` contains the setup code to run benchmarks. `fn prolog_benches()` at
|
||||
the top of the file is where the benchmarks are defined.
|
||||
|
||||
Benchmarks are organized around running queries against a prolog module file.
|
||||
Before a benchmark starts, `benchmark.setup()` is called which reads the module
|
||||
file and initializes a new `scryer_prolog::machine::Machine`.
|
||||
|
||||
Each benchmark measurement is done by running a query against the machine. In
|
||||
the case of criterion each query is run many times, in the case of iai it's run
|
||||
once.
|
||||
|
||||
## Adding benchmarks
|
||||
|
||||
This design is meant to suppoort defining lots of benchmarks.
|
||||
|
||||
To add a new benchmark:
|
||||
|
||||
* Add a new file `benches/[module].pl` that contains setup prolog code. Import
|
||||
libraries, define predicates, etc.
|
||||
* Add a new section in `setup.rs::prolog_benchmarks()` that refers to to the
|
||||
file and write a query to be benchmarked.
|
||||
* If the query mutates the machine, then use `Strategy::Fresh` so the criterion
|
||||
benchmark will recreate a new machine for each benchmark run, otherwise use
|
||||
`Strategy::Reuse` which has lower overhead. (This is not used by the iai
|
||||
benchmark because it only runs once anyway.)
|
||||
|
||||
Some tips:
|
||||
|
||||
* The goal of benchmarking is to know if a library or engine change improved
|
||||
performance or not.
|
||||
* Once a benchmark is defined and named, avoid changing it's definition. If a
|
||||
benchmark needs to change to be more useful, give the new definition a new
|
||||
name instead. This will prevent charts from showing wild changes in
|
||||
performance just because the definition changed (see previous).
|
||||
* Aim for queries to execute in less than 0.5s realtime. Longer runtimes make it
|
||||
easier for humans to see big differences, but benchmarks either run 10x slower
|
||||
(iai) or execute repeatedly to attain statistical significance (criterion) and
|
||||
in both cases benchmarking queries that take longer than about 0.5s are
|
||||
cumbersome to run.
|
||||
* Consider that the library runtime actually parses the text output of the top
|
||||
level. So don't use custom outputs or it will fail to parse. Also keep the
|
||||
output small so it doesn't just benchmark the ouput parsing code.
|
||||
* DO test the output of the benchmark run, we don't want to count broken
|
||||
benchmarks.
|
||||
|
||||
## CI
|
||||
|
||||
Both benchmark harnesses are run in `.github/workflows/ci.yaml` in the `report`
|
||||
job, and the results are published as build artifacts.
|
||||
|
||||
## Todo
|
||||
|
||||
- [ ] Currently, the execution time to load a module is not benchmarked. It
|
||||
would be nice to have at least one benchmark for loading a module (probably a
|
||||
big one).
|
||||
- [ ] Write a new action that downloads the test and benchmark results
|
||||
artifacts, plots them over time, and publishes a report to github pages.
|
||||
41
benches/csv.pl
Normal file
41
benches/csv.pl
Normal file
File diff suppressed because one or more lines are too long
130
benches/edges.pl
Normal file
130
benches/edges.pl
Normal file
@@ -0,0 +1,130 @@
|
||||
:- use_module(library(clpb)).
|
||||
:- use_module(library(assoc)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(pairs)).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Contiguous United States and DC as they appear in SGB:
|
||||
http://www-cs-faculty.stanford.edu/~uno/sgb.html
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
edge(al, fl).
|
||||
edge(al, ga).
|
||||
edge(al, ms).
|
||||
edge(al, tn).
|
||||
edge(ar, la).
|
||||
edge(ar, mo).
|
||||
edge(ar, ms).
|
||||
edge(ar, ok).
|
||||
edge(ar, tn).
|
||||
edge(ar, tx).
|
||||
edge(az, ca).
|
||||
edge(az, nm).
|
||||
edge(az, nv).
|
||||
edge(az, ut).
|
||||
edge(ca, nv).
|
||||
edge(ca, or).
|
||||
edge(co, ks).
|
||||
edge(co, ne).
|
||||
edge(co, nm).
|
||||
edge(co, ok).
|
||||
edge(co, ut).
|
||||
edge(co, wy).
|
||||
edge(ct, ma).
|
||||
edge(ct, ny).
|
||||
edge(ct, ri).
|
||||
edge(dc, md).
|
||||
edge(dc, va).
|
||||
edge(de, md).
|
||||
edge(de, nj).
|
||||
edge(de, pa).
|
||||
edge(fl, ga).
|
||||
edge(ga, nc).
|
||||
edge(ga, sc).
|
||||
edge(ga, tn).
|
||||
edge(ia, il).
|
||||
edge(ia, mn).
|
||||
edge(ia, mo).
|
||||
edge(ia, ne).
|
||||
edge(ia, sd).
|
||||
edge(ia, wi).
|
||||
edge(id, mt).
|
||||
edge(id, nv).
|
||||
edge(id, or).
|
||||
edge(id, ut).
|
||||
edge(id, wa).
|
||||
edge(id, wy).
|
||||
edge(il, in).
|
||||
edge(il, ky).
|
||||
edge(il, mo).
|
||||
edge(il, wi).
|
||||
edge(in, ky).
|
||||
edge(in, mi).
|
||||
edge(in, oh).
|
||||
edge(ks, mo).
|
||||
edge(ks, ne).
|
||||
edge(ks, ok).
|
||||
edge(ky, mo).
|
||||
edge(ky, oh).
|
||||
edge(ky, tn).
|
||||
edge(ky, va).
|
||||
edge(ky, wv).
|
||||
edge(la, ms).
|
||||
edge(la, tx).
|
||||
edge(ma, nh).
|
||||
edge(ma, ny).
|
||||
edge(ma, ri).
|
||||
edge(ma, vt).
|
||||
edge(md, pa).
|
||||
edge(md, va).
|
||||
edge(md, wv).
|
||||
edge(me, nh).
|
||||
edge(mi, oh).
|
||||
edge(mi, wi).
|
||||
edge(mn, nd).
|
||||
edge(mn, sd).
|
||||
edge(mn, wi).
|
||||
edge(mo, ne).
|
||||
edge(mo, ok).
|
||||
edge(mo, tn).
|
||||
edge(ms, tn).
|
||||
edge(mt, nd).
|
||||
edge(mt, sd).
|
||||
edge(mt, wy).
|
||||
edge(nc, sc).
|
||||
edge(nc, tn).
|
||||
edge(nc, va).
|
||||
edge(nd, sd).
|
||||
edge(ne, sd).
|
||||
edge(ne, wy).
|
||||
edge(nh, vt).
|
||||
edge(nj, ny).
|
||||
edge(nj, pa).
|
||||
edge(nm, ok).
|
||||
edge(nm, tx).
|
||||
edge(nv, or).
|
||||
edge(nv, ut).
|
||||
edge(ny, pa).
|
||||
edge(ny, vt).
|
||||
edge(oh, pa).
|
||||
edge(oh, wv).
|
||||
edge(ok, tx).
|
||||
edge(or, wa).
|
||||
edge(pa, wv).
|
||||
edge(sd, wy).
|
||||
edge(tn, va).
|
||||
edge(ut, wy).
|
||||
edge(va, wv).
|
||||
|
||||
independent_set(G, *(NBs)) :-
|
||||
findall(U-V, (edge(U, V),G@<U), Edges),
|
||||
setof(U, V^(member(U-V, Edges);member(V-U, Edges)), Nodes),
|
||||
pairs_keys_values(Pairs, Nodes, _),
|
||||
list_to_assoc(Pairs, Assoc),
|
||||
maplist(not_both(Assoc), Edges, NBs).
|
||||
|
||||
not_both(Assoc, U-V, ~BU + ~BV) :-
|
||||
get_assoc(U, Assoc, BU),
|
||||
get_assoc(V, Assoc, BV).
|
||||
|
||||
independent_set_count(G, Count) :- independent_set(G, Sat), sat_count(Sat, Count).
|
||||
2
benches/numlist.pl
Normal file
2
benches/numlist.pl
Normal file
@@ -0,0 +1,2 @@
|
||||
:- use_module(library(between)).
|
||||
run_numlist(Upper, Head) :- numlist(1, Upper, L), L = [Head|_].
|
||||
46
benches/run_criterion.rs
Normal file
46
benches/run_criterion.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use pprof::criterion::{Output, PProfProfiler};
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
mod setup;
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
fn bench_criterion(c: &mut Criterion) {
|
||||
for (&name, bench) in setup::prolog_benches().iter() {
|
||||
match bench.strategy {
|
||||
setup::Strategy::Fresh => c.bench_function(name, |b| {
|
||||
b.iter_batched(|| bench.setup(), |mut r| r(), BatchSize::LargeInput)
|
||||
}),
|
||||
setup::Strategy::Reuse => c.bench_function(name, |b| b.iter(bench.setup())),
|
||||
};
|
||||
}
|
||||
}
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn config() -> Criterion {
|
||||
Criterion::default()
|
||||
.sample_size(20)
|
||||
.with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn config() -> Criterion {
|
||||
Criterion::default().sample_size(20)
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
criterion_group!(
|
||||
name = benches;
|
||||
config = config();
|
||||
targets = bench_criterion
|
||||
);
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
criterion_main!(benches);
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
fn main() {}
|
||||
38
benches/run_iai.rs
Normal file
38
benches/run_iai.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
mod setup;
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
mod iai {
|
||||
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
|
||||
|
||||
use scryer_prolog::machine::parsed_results::QueryResolution;
|
||||
|
||||
use super::setup;
|
||||
|
||||
#[library_benchmark]
|
||||
#[bench::count_edges(setup::prolog_benches()["count_edges"].setup())]
|
||||
#[bench::numlist(setup::prolog_benches()["numlist"].setup())]
|
||||
#[bench::csv_codename(setup::prolog_benches()["csv_codename"].setup())]
|
||||
fn bench(mut run: impl FnMut() -> QueryResolution) -> QueryResolution {
|
||||
run()
|
||||
}
|
||||
|
||||
library_benchmark_group!(
|
||||
name = benches;
|
||||
benchmarks = bench
|
||||
);
|
||||
|
||||
main!(library_benchmark_groups = benches);
|
||||
|
||||
pub fn call_main() {
|
||||
main()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
fn main() {
|
||||
iai::call_main();
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
fn main() {}
|
||||
133
benches/setup.rs
Normal file
133
benches/setup.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use std::{collections::BTreeMap, fs, path::Path};
|
||||
|
||||
use maplit::btreemap;
|
||||
use scryer_prolog::machine::{
|
||||
parsed_results::{QueryResolution, Value},
|
||||
Machine,
|
||||
};
|
||||
|
||||
pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
|
||||
[
|
||||
(
|
||||
"count_edges", // name of the benchmark
|
||||
"benches/edges.pl", // name of the prolog module file to load. use the same file in multiple benchmarks
|
||||
"independent_set_count(ky, Count).", // query to benchmark in the context of the loaded module. consider making the query adjustable to tune the run time to ~0.1s
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Count" => Value::try_from("2869176".to_string()).unwrap() },
|
||||
),
|
||||
(
|
||||
"numlist",
|
||||
"benches/numlist.pl",
|
||||
"run_numlist(1000000, Head).",
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Head" => Value::try_from("1".to_string()).unwrap()},
|
||||
),
|
||||
(
|
||||
"csv_codename",
|
||||
"benches/csv.pl",
|
||||
"get_codename(\"0020\",Name).",
|
||||
Strategy::Reuse,
|
||||
btreemap! { "Name" => Value::try_from("SPACE".to_string()).unwrap()},
|
||||
),
|
||||
]
|
||||
.map(|b| {
|
||||
(
|
||||
b.0,
|
||||
PrologBenchmark {
|
||||
name: b.0,
|
||||
filename: b.1,
|
||||
query: b.2,
|
||||
strategy: b.3,
|
||||
bindings: b.4,
|
||||
},
|
||||
)
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
pub enum Strategy {
|
||||
#[allow(dead_code)]
|
||||
Fresh,
|
||||
Reuse,
|
||||
}
|
||||
|
||||
pub struct PrologBenchmark {
|
||||
pub name: &'static str,
|
||||
pub filename: &'static str,
|
||||
pub query: &'static str,
|
||||
pub strategy: Strategy,
|
||||
pub bindings: BTreeMap<&'static str, Value>,
|
||||
}
|
||||
|
||||
impl PrologBenchmark {
|
||||
pub fn make_machine(&self) -> Machine {
|
||||
let program = fs::read_to_string(self.filename).unwrap();
|
||||
let module_name = Path::new(self.filename)
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap();
|
||||
let mut machine = Machine::new_lib();
|
||||
machine.load_module_string(module_name, program);
|
||||
machine
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
pub fn setup(&self) -> impl FnMut() -> QueryResolution {
|
||||
let mut machine = self.make_machine();
|
||||
let query = self.query;
|
||||
move || {
|
||||
use criterion::black_box;
|
||||
black_box(machine.run_query(black_box(query.to_string()))).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn validate_benchmarks() {
|
||||
use super::prolog_benches;
|
||||
use scryer_prolog::machine::parsed_results::{QueryMatch, QueryResolution};
|
||||
use std::{fmt::Write, fs};
|
||||
|
||||
struct BenchResult {
|
||||
pub name: &'static str,
|
||||
pub setup_inference_count: u64,
|
||||
pub query_inference_count: u64,
|
||||
}
|
||||
|
||||
let mut results: Vec<BenchResult> = vec![];
|
||||
|
||||
for (_, r) in prolog_benches() {
|
||||
let mut machine = r.make_machine();
|
||||
let setup_inference_count = machine.get_inference_count();
|
||||
|
||||
let result = machine.run_query(r.query.to_string()).unwrap();
|
||||
let query_inference_count = machine.get_inference_count() - setup_inference_count;
|
||||
|
||||
let expected = QueryResolution::Matches(vec![QueryMatch::from(r.bindings.clone())]);
|
||||
assert_eq!(result, expected, "validating benchmark {}", r.name);
|
||||
|
||||
results.push(BenchResult {
|
||||
name: r.name,
|
||||
setup_inference_count,
|
||||
query_inference_count,
|
||||
})
|
||||
}
|
||||
|
||||
let mut json: String = Default::default();
|
||||
json.push('[');
|
||||
for r in results {
|
||||
json.push('\n');
|
||||
write!(
|
||||
json,
|
||||
r#"{{"name":"{}","setup_inference_count":{},"query_inference_count":{}}},"#,
|
||||
r.name, r.setup_inference_count, r.query_inference_count
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
json.pop(); // trailing comma
|
||||
json.push_str("\n]");
|
||||
fs::write("target/benchmark_inference_counts.json", json).expect("Unable to write file");
|
||||
}
|
||||
}
|
||||
56
build.rs
56
build.rs
@@ -1,56 +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 };
|
||||
}").unwrap();
|
||||
}
|
||||
3495
build/instructions_template.rs
Normal file
3495
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();
|
||||
}
|
||||
176
build/static_string_indexing.rs
Normal file
176
build/static_string_indexing.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::parse::*;
|
||||
use syn::visit::*;
|
||||
use syn::*;
|
||||
|
||||
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() {
|
||||
let _ = input.parse::<Token![,]>();
|
||||
arms.push(input.parse()?);
|
||||
}
|
||||
|
||||
Ok(ReadHeapCellExprAndArms { expr, arms })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for MacroFnArgs {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut args = vec![];
|
||||
|
||||
if !input.is_empty() {
|
||||
args.push(input.parse()?);
|
||||
}
|
||||
|
||||
while !input.is_empty() {
|
||||
let _ = input.parse::<Token![,]>();
|
||||
args.push(input.parse()?);
|
||||
}
|
||||
|
||||
Ok(MacroFnArgs { args })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> Visit<'ast> for StaticStrVisitor {
|
||||
fn visit_macro(&mut self, m: &'ast Macro) {
|
||||
let Macro { path, .. } = m;
|
||||
|
||||
if path.is_ident("atom") {
|
||||
if let Ok(Lit::Str(string)) = m.parse_body::<Lit>() {
|
||||
self.static_strs.insert(string.value());
|
||||
}
|
||||
} else if path.is_ident("read_heap_cell") || path.is_ident("match_untyped_arena_ptr") {
|
||||
if let Ok(m) = m.parse_body::<ReadHeapCellExprAndArms>() {
|
||||
self.visit_expr(&m.expr);
|
||||
|
||||
for e in m.arms {
|
||||
self.visit_arm(&e);
|
||||
}
|
||||
}
|
||||
} else if let Ok(m) = m.parse_body::<MacroFnArgs>() {
|
||||
for e in m.args {
|
||||
self.visit_expr(&e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if let Ok(syntax) = process_filepath(instruction_rs_path) {
|
||||
visitor.visit_file(&syntax)
|
||||
}
|
||||
|
||||
let indices = (0..visitor.static_strs.len()).map(|i| (i << 3) as u64);
|
||||
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: [&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 |
28
scryer-prolog.wxs
Normal file
28
scryer-prolog.wxs
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Product Name="Scryer Prolog" Manufacturer="Scryer Prolog contributors" Id="*" UpgradeCode="cfb2dee4-5dd5-4d7d-b426-cd7340810559" Language="1033" Codepage="1252" Version="0.9.0">
|
||||
<Package Description="An open source industrial strength production environment for ISO Prolog that is also a testbed for bleeding edge research in logic and constraint programming, which is itself written in a high-level language." Platform="x64" Keywords="prolog" Id="*" Compressed="yes" InstallScope="perMachine" InstallerVersion="300" Languages="1033" SummaryCodepage="1252" Manufacturer="Scryer Prolog contributors"/>
|
||||
<Property Id="APPHELPLINK" Value="https://github.com/mthom/scryer-prolog"/>
|
||||
<Media Id="1" Cabinet="scryer.cab" EmbedCab="yes" />
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFilesFolder" Name="PFiles">
|
||||
<Directory Id="INSTALLDIR" Name="Scryer Prolog">
|
||||
<Component Id="MainExecutable" Guid="1b41ceda-ba18-47f9-911b-ee41b4f20921">
|
||||
<File Id="ScryerPrologEXE" Name="scryer-prolog.exe" DiskId="1" Source="target/release/scryer-prolog.exe" KeyPath="yes" Checksum="yes"/>
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
<Component Id="ApplicationShortcut" Guid="8c9b14a3-e7b1-4d30-a892-61d7371dcae2">
|
||||
<Shortcut Id="ApplicationStarMenuShortcut" Name="Scryer Prolog" Description="Launch Scryer Prolog" Target="[#ScryerPrologEXE]" WorkingDirectory="INSTALLDIR"/>
|
||||
<RemoveFolder Id="ApplicationShortcut" On="uninstall"/>
|
||||
<RegistryValue Root="HKCU" Key="Software\Microsoft\ScryerProlog" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
|
||||
</Component>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Feature Id="Complete" Level="1" Display="expand" ConfigurableDirectory="INSTALLDIR">
|
||||
<ComponentRef Id="MainExecutable"/>
|
||||
<ComponentRef Id="ApplicationShortcut"/>
|
||||
</Feature>
|
||||
</Product>
|
||||
</Wix>
|
||||
57
src/allocator.rs
Normal file
57
src/allocator.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::targets::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
pub(crate) trait Allocator {
|
||||
fn new() -> Self;
|
||||
|
||||
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
lvl: Level,
|
||||
context: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
);
|
||||
|
||||
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
lvl: Level,
|
||||
context: GenContext,
|
||||
cell: &'a Cell<RegType>,
|
||||
code: &mut CodeDeque,
|
||||
);
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
r: RegType,
|
||||
is_new_var: bool,
|
||||
);
|
||||
|
||||
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType;
|
||||
|
||||
fn mark_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &Cell<VarReg>,
|
||||
context: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
);
|
||||
|
||||
fn reset(&mut self);
|
||||
fn reset_arg(&mut self, arg_num: usize);
|
||||
fn reset_at_head(&mut self, args: &[Term]);
|
||||
fn reset_contents(&mut self);
|
||||
|
||||
fn advance_arg(&mut self);
|
||||
fn max_reg_allocated(&self) -> usize;
|
||||
}
|
||||
1158
src/arena.rs
Normal file
1158
src/arena.rs
Normal file
File diff suppressed because it is too large
Load Diff
729
src/arithmetic.rs
Normal file
729
src/arithmetic.rs
Normal file
@@ -0,0 +1,729 @@
|
||||
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::dashu::{Integer, Rational};
|
||||
|
||||
use crate::machine::machine_errors::*;
|
||||
|
||||
use dashu::base::Abs;
|
||||
use dashu::base::BitTest;
|
||||
use num_order::NumOrd;
|
||||
use ordered_float::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::{max, min, Ordering};
|
||||
use std::convert::TryFrom;
|
||||
use std::f64;
|
||||
use std::num::FpCategory;
|
||||
use std::ops::Div;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ArithmeticTerm {
|
||||
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 = (CodeDeque, 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)
|
||||
}
|
||||
Term::Literal(cell, cons) => TermIterState::Literal(Level::Shallow, cell, cons),
|
||||
Term::Cons(..) | Term::PartialString(..) | Term::CompleteString(..) => {
|
||||
return Err(ArithmeticError::NonEvaluableFunctor(
|
||||
Literal::Atom(atom!(".")),
|
||||
2,
|
||||
))
|
||||
}
|
||||
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Shallow, cell, var_ptr.clone()),
|
||||
};
|
||||
|
||||
Ok(ArithInstructionIterator {
|
||||
state_stack: vec![state],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ArithTermRef<'a> {
|
||||
Literal(&'a Literal),
|
||||
Op(Atom, usize), // name, arity.
|
||||
Var(Level, &'a Cell<VarReg>, VarPtr),
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ArithInstructionIterator<'a> {
|
||||
type Item = Result<ArithTermRef<'a>, ArithmeticError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(iter_state) = self.state_stack.pop() {
|
||||
match iter_state {
|
||||
TermIterState::AnonVar(_) => return Some(Err(ArithmeticError::UninstantiatedVar)),
|
||||
TermIterState::Clause(lvl, child_num, cell, name, subterms) => {
|
||||
let arity = subterms.len();
|
||||
|
||||
if child_num == arity {
|
||||
return Some(Ok(ArithTermRef::Op(name, arity)));
|
||||
} else {
|
||||
self.state_stack.push(TermIterState::Clause(
|
||||
lvl,
|
||||
child_num + 1,
|
||||
cell,
|
||||
name,
|
||||
subterms,
|
||||
));
|
||||
|
||||
self.push_subterm(lvl.child_level(), &subterms[child_num]);
|
||||
}
|
||||
}
|
||||
TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))),
|
||||
TermIterState::Var(lvl, cell, var_ptr) => {
|
||||
return Some(Ok(ArithTermRef::Var(lvl, cell, var_ptr)));
|
||||
}
|
||||
_ => {
|
||||
return Some(Err(ArithmeticError::NonEvaluableFunctor(
|
||||
Literal::Atom(atom!(".")),
|
||||
2,
|
||||
)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ArithmeticEvaluator<'a> {
|
||||
marker: &'a mut DebrayAllocator,
|
||||
interm: Vec<ArithmeticTerm>,
|
||||
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!("float_integer_part") => Ok(Instruction::FloatIntegerPart(a1, t)),
|
||||
atom!("float_fractional_part") => Ok(Instruction::FloatFractionalPart(a1, t)),
|
||||
atom!("sign") => Ok(Instruction::Sign(a1, t)),
|
||||
atom!("\\") => Ok(Instruction::BitwiseComplement(a1, t)),
|
||||
_ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 1)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_binary_instr(
|
||||
&self,
|
||||
name: Atom,
|
||||
a1: ArithmeticTerm,
|
||||
a2: ArithmeticTerm,
|
||||
t: usize,
|
||||
) -> Result<Instruction, ArithmeticError> {
|
||||
match name {
|
||||
atom!("+") => Ok(Instruction::Add(a1, a2, t)),
|
||||
atom!("-") => Ok(Instruction::Sub(a1, a2, t)),
|
||||
atom!("/") => Ok(Instruction::Div(a1, a2, t)),
|
||||
atom!("//") => Ok(Instruction::IDiv(a1, a2, t)),
|
||||
atom!("max") => Ok(Instruction::Max(a1, a2, t)),
|
||||
atom!("min") => Ok(Instruction::Min(a1, a2, t)),
|
||||
atom!("div") => Ok(Instruction::IntFloorDiv(a1, a2, t)),
|
||||
atom!("rdiv") => Ok(Instruction::RDiv(a1, a2, t)),
|
||||
atom!("*") => Ok(Instruction::Mul(a1, a2, t)),
|
||||
atom!("**") => Ok(Instruction::Pow(a1, a2, t)),
|
||||
atom!("^") => Ok(Instruction::IntPow(a1, a2, t)),
|
||||
atom!(">>") => Ok(Instruction::Shr(a1, a2, t)),
|
||||
atom!("<<") => Ok(Instruction::Shl(a1, a2, t)),
|
||||
atom!("/\\") => Ok(Instruction::And(a1, a2, t)),
|
||||
atom!("\\/") => Ok(Instruction::Or(a1, a2, t)),
|
||||
atom!("xor") => Ok(Instruction::Xor(a1, a2, t)),
|
||||
atom!("mod") => Ok(Instruction::Mod(a1, a2, t)),
|
||||
atom!("rem") => Ok(Instruction::Rem(a1, a2, t)),
|
||||
atom!("gcd") => Ok(Instruction::Gcd(a1, a2, t)),
|
||||
atom!("atan2") => Ok(Instruction::ATan2(a1, a2, t)),
|
||||
_ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 2)),
|
||||
}
|
||||
}
|
||||
|
||||
fn 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);
|
||||
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 = CodeDeque::new();
|
||||
|
||||
for term_ref in src.iter()? {
|
||||
match term_ref? {
|
||||
ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?,
|
||||
ArithTermRef::Var(lvl, cell, name) => {
|
||||
let var_num = name.to_var_num().unwrap();
|
||||
|
||||
let r = if lvl == Level::Shallow {
|
||||
self.marker
|
||||
.mark_non_callable(var_num, arg, term_loc, cell, &mut code)
|
||||
} else if term_loc.is_last() || cell.get().norm().reg_num() == 0 {
|
||||
let r = self.marker.get_binding(var_num);
|
||||
|
||||
if r.reg_num() == 0 {
|
||||
self.marker.mark_var::<QueryInstruction>(
|
||||
var_num, lvl, cell, term_loc, &mut code,
|
||||
);
|
||||
cell.get().norm()
|
||||
} else {
|
||||
self.marker.increment_running_count(var_num);
|
||||
r
|
||||
}
|
||||
} else {
|
||||
self.marker.increment_running_count(var_num);
|
||||
cell.get().norm()
|
||||
};
|
||||
|
||||
self.interm.push(ArithmeticTerm::Reg(r));
|
||||
}
|
||||
ArithTermRef::Op(name, arity) => {
|
||||
code.push_back(self.instr_from_clause(name, arity)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((code, self.interm.pop()))
|
||||
}
|
||||
}
|
||||
|
||||
// integer division rounding function -- 9.1.3.1.
|
||||
pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Number {
|
||||
match n {
|
||||
&Number::Integer(i) => {
|
||||
let result = (&*i).try_into();
|
||||
if let Ok(value) = result {
|
||||
fixnum!(Number, value, 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(f.0 as i64), arena))
|
||||
}
|
||||
}
|
||||
Number::Rational(ref r) => {
|
||||
let (_, floor) = (r.fract(), r.floor());
|
||||
|
||||
if let Ok(value) = (&floor).try_into() {
|
||||
fixnum!(Number, value, 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().value(),
|
||||
&Number::Float(OrderedFloat(f)) => f,
|
||||
Number::Rational(ref r) => r.to_f64().value(),
|
||||
}
|
||||
}
|
||||
|
||||
// floating point result function -- 9.1.4.2.
|
||||
pub(crate) fn result_f(n: &Number) -> Result<f64, EvalError> {
|
||||
classify_float(rnd_f(n))
|
||||
}
|
||||
|
||||
fn classify_float(f: f64) -> Result<f64, EvalError> {
|
||||
match f.classify() {
|
||||
FpCategory::Normal | FpCategory::Zero => Ok(f),
|
||||
FpCategory::Infinite => {
|
||||
if OrderedFloat(f) == OrderedFloat(f64::MAX) {
|
||||
Ok(f)
|
||||
} else {
|
||||
Err(EvalError::FloatOverflow)
|
||||
}
|
||||
}
|
||||
FpCategory::Nan => Err(EvalError::Undefined),
|
||||
_ => Ok(f),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn float_fn_to_f(n: i64) -> Result<f64, EvalError> {
|
||||
classify_float(n as f64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn float_i_to_f(n: &Integer) -> Result<f64, EvalError> {
|
||||
classify_float(n.to_f64().value())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn float_r_to_f(r: &Rational) -> Result<f64, EvalError> {
|
||||
classify_float(r.to_f64().value())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn add_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||
Ok(OrderedFloat(classify_float(f1 + f2)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn mul_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||
Ok(OrderedFloat(classify_float(f1 * f2)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn div_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
|
||||
if FpCategory::Zero == f2.classify() {
|
||||
Err(EvalError::ZeroDivisor)
|
||||
} else {
|
||||
Ok(OrderedFloat(classify_float(f1 / f2)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<Number> for Number {
|
||||
type Output = Result<Number, EvalError>;
|
||||
|
||||
fn div(self, rhs: Number) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
(Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
|
||||
float_fn_to_f(n1.get_num())?,
|
||||
float_fn_to_f(n2.get_num())?,
|
||||
)?)),
|
||||
(Number::Fixnum(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
|
||||
float_fn_to_f(n1.get_num())?,
|
||||
float_i_to_f(&n2)?,
|
||||
)?)),
|
||||
(Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
|
||||
float_i_to_f(&n1)?,
|
||||
float_fn_to_f(n2.get_num())?,
|
||||
)?)),
|
||||
(Number::Fixnum(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
|
||||
float_fn_to_f(n1.get_num())?,
|
||||
float_r_to_f(&n2)?,
|
||||
)?)),
|
||||
(Number::Rational(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
|
||||
float_r_to_f(&n1)?,
|
||||
float_fn_to_f(n2.get_num())?,
|
||||
)?)),
|
||||
(Number::Fixnum(n1), Number::Float(OrderedFloat(n2))) => {
|
||||
Ok(Number::Float(div_f(float_fn_to_f(n1.get_num())?, n2)?))
|
||||
}
|
||||
(Number::Float(OrderedFloat(n1)), Number::Fixnum(n2)) => {
|
||||
Ok(Number::Float(div_f(n1, float_fn_to_f(n2.get_num())?)?))
|
||||
}
|
||||
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
|
||||
float_i_to_f(&n1)?,
|
||||
float_i_to_f(&n2)?,
|
||||
)?)),
|
||||
(Number::Integer(n1), Number::Float(OrderedFloat(n2))) => {
|
||||
Ok(Number::Float(div_f(float_i_to_f(&n1)?, n2)?))
|
||||
}
|
||||
(Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
|
||||
Ok(Number::Float(div_f(n2, float_i_to_f(&n1)?)?))
|
||||
}
|
||||
(Number::Integer(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
|
||||
float_i_to_f(&n1)?,
|
||||
float_r_to_f(&n2)?,
|
||||
)?)),
|
||||
(Number::Rational(n2), Number::Integer(n1)) => Ok(Number::Float(div_f(
|
||||
float_r_to_f(&n2)?,
|
||||
float_i_to_f(&n1)?,
|
||||
)?)),
|
||||
(Number::Rational(n1), Number::Float(OrderedFloat(n2))) => {
|
||||
Ok(Number::Float(div_f(float_r_to_f(&n1)?, n2)?))
|
||||
}
|
||||
(Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
|
||||
Ok(Number::Float(div_f(n2, float_r_to_f(&n1)?)?))
|
||||
}
|
||||
(Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
|
||||
Ok(Number::Float(div_f(f1, f2)?))
|
||||
}
|
||||
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Float(div_f(
|
||||
float_r_to_f(&r1)?,
|
||||
float_r_to_f(&r2)?,
|
||||
)?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Number {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
match (self, rhs) {
|
||||
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.eq(&n2),
|
||||
(&Number::Fixnum(n1), Number::Integer(ref n2)) => n1.get_num().num_eq(&**n2),
|
||||
(Number::Integer(ref n1), &Number::Fixnum(n2)) => n1.num_eq(&n2.get_num()),
|
||||
(&Number::Fixnum(n1), Number::Rational(ref n2)) => {
|
||||
Integer::from(n1.get_num()).num_eq(&**n2)
|
||||
}
|
||||
(Number::Rational(ref n1), &Number::Fixnum(n2)) => {
|
||||
n1.num_eq(&Integer::from(n2.get_num()))
|
||||
}
|
||||
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).eq(&n2),
|
||||
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2.get_num() as f64)),
|
||||
(Number::Integer(ref n1), Number::Integer(ref n2)) => n1.eq(n2),
|
||||
(Number::Integer(ref n1), Number::Float(n2)) => {
|
||||
OrderedFloat(n1.to_f64().value()).eq(n2)
|
||||
}
|
||||
(&Number::Float(n1), Number::Integer(ref n2)) => {
|
||||
n1.eq(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(Number::Integer(ref n1), Number::Rational(ref n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
&Rational::from(&**n1) == &**n2
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
n1.num_eq(&**n2)
|
||||
}
|
||||
}
|
||||
(Number::Rational(ref n1), Number::Integer(ref n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
n1 == &Rational::from(&**n2)
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
n1.num_eq(&**n2)
|
||||
}
|
||||
}
|
||||
(Number::Rational(ref n1), &Number::Float(n2)) => {
|
||||
OrderedFloat(n1.to_f64().value()).eq(&n2)
|
||||
}
|
||||
(&Number::Float(n1), Number::Rational(ref n2)) => {
|
||||
n1.eq(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2),
|
||||
(Number::Rational(ref r1), Number::Rational(ref r2)) => r1.eq(r2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Number {}
|
||||
|
||||
impl PartialOrd<usize> for Number {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, rhs: &usize) -> Option<Ordering> {
|
||||
match self {
|
||||
Number::Fixnum(n) => {
|
||||
let n = n.get_num();
|
||||
|
||||
if n < 0i64 {
|
||||
Some(Ordering::Less)
|
||||
} else {
|
||||
(n as usize).partial_cmp(rhs)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => Some((n).num_cmp(rhs)),
|
||||
Number::Rational(r) => Some((r).num_cmp(&Integer::from(*rhs))),
|
||||
Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<usize> for Number {
|
||||
#[inline]
|
||||
fn eq(&self, rhs: &usize) -> bool {
|
||||
match self {
|
||||
Number::Fixnum(n) => {
|
||||
let n = n.get_num();
|
||||
|
||||
if n < 0i64 {
|
||||
false
|
||||
} else {
|
||||
(n as usize).eq(rhs)
|
||||
}
|
||||
}
|
||||
Number::Integer(n) => (n).num_eq(rhs),
|
||||
Number::Rational(r) => (r).num_eq(&Integer::from(*rhs)),
|
||||
Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Number {
|
||||
fn partial_cmp(&self, rhs: &Number) -> Option<Ordering> {
|
||||
Some(self.cmp(rhs))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Number {
|
||||
fn cmp(&self, rhs: &Number) -> Ordering {
|
||||
match (self, rhs) {
|
||||
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.get_num().cmp(&n2.get_num()),
|
||||
(&Number::Fixnum(n1), Number::Integer(n2)) => Integer::from(n1.get_num()).cmp(n2),
|
||||
(Number::Integer(n1), &Number::Fixnum(n2)) => (**n1).cmp(&Integer::from(n2.get_num())),
|
||||
(&Number::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1.get_num()).cmp(n2),
|
||||
(Number::Rational(n1), &Number::Fixnum(n2)) => {
|
||||
(**n1).cmp(&Rational::from(n2.get_num()))
|
||||
}
|
||||
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).cmp(&n2),
|
||||
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2.get_num() as f64)),
|
||||
(&Number::Integer(n1), &Number::Integer(n2)) => (*n1).cmp(&*n2),
|
||||
(&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(n2),
|
||||
(&Number::Float(n1), Number::Integer(ref n2)) => {
|
||||
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Integer(n1), &Number::Rational(n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
Rational::from(&**n1).cmp(n2)
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(n1), &Number::Integer(n2)) => {
|
||||
#[cfg(feature = "num")]
|
||||
{
|
||||
(&**n1).cmp(&Rational::from(&**n2))
|
||||
}
|
||||
#[cfg(not(feature = "num"))]
|
||||
{
|
||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||
}
|
||||
}
|
||||
(&Number::Rational(n1), &Number::Float(n2)) => {
|
||||
OrderedFloat(n1.to_f64().value()).cmp(&n2)
|
||||
}
|
||||
(&Number::Float(n1), &Number::Rational(n2)) => {
|
||||
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
||||
}
|
||||
(&Number::Float(f1), &Number::Float(f2)) => f1.cmp(&f2),
|
||||
(&Number::Rational(r1), &Number::Rational(r2)) => (*r1).cmp(&*r2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<HeapCellValue> 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 | HeapCellValueTag::CutPoint, n) => {
|
||||
Ok(Number::Fixnum(n))
|
||||
}
|
||||
_ => {
|
||||
Err(())
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Computes n ^ power. Ignores the sign of power.
|
||||
pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer {
|
||||
let mut power = power.abs();
|
||||
|
||||
if power.is_zero() {
|
||||
return Integer::ONE;
|
||||
}
|
||||
|
||||
let mut oddand = Integer::ONE;
|
||||
|
||||
while power.num_gt(&1) {
|
||||
if power.bit(0) {
|
||||
oddand *= &n;
|
||||
}
|
||||
|
||||
n = n.pow(2);
|
||||
power >>= 1;
|
||||
}
|
||||
|
||||
n * oddand
|
||||
}
|
||||
473
src/atom_table.rs
Normal file
473
src/atom_table.rs
Normal file
@@ -0,0 +1,473 @@
|
||||
use crate::parser::ast::MAX_ARITY;
|
||||
use crate::raw_block::*;
|
||||
use crate::rcu::{Rcu, RcuRef};
|
||||
use crate::types::*;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::Weak;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use scryer_modular_bitfield::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Atom {
|
||||
pub index: u64,
|
||||
}
|
||||
|
||||
const_assert!(mem::size_of::<Atom>() == 8);
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/static_atoms.rs"));
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl indexmap::Equivalent<Atom> for str {
|
||||
fn equivalent(&self, key: &Atom) -> bool {
|
||||
&*key.as_str() == self
|
||||
}
|
||||
}
|
||||
|
||||
const ATOM_TABLE_INIT_SIZE: usize = 1 << 16;
|
||||
const ATOM_TABLE_ALIGN: usize = 8;
|
||||
|
||||
#[inline(always)]
|
||||
fn global_atom_table() -> &'static RwLock<Weak<AtomTable>> {
|
||||
#[cfg(feature = "rust_beta_channel")]
|
||||
{
|
||||
// const Weak::new will be stabilized in 1.73 which is currently in beta,
|
||||
// till then we need a OnceLock for initialization
|
||||
static GLOBAL_ATOM_TABLE: RwLock<Weak<AtomTable>> = RwLock::const_new(Weak::new());
|
||||
&GLOBAL_ATOM_TABLE
|
||||
}
|
||||
#[cfg(not(feature = "rust_beta_channel"))]
|
||||
{
|
||||
use std::sync::OnceLock;
|
||||
static GLOBAL_ATOM_TABLE: OnceLock<RwLock<Weak<AtomTable>>> = OnceLock::new();
|
||||
GLOBAL_ATOM_TABLE.get_or_init(|| RwLock::new(Weak::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn arc_atom_table() -> Option<Arc<AtomTable>> {
|
||||
global_atom_table().read().unwrap().upgrade()
|
||||
}
|
||||
|
||||
impl RawBlockTraits for AtomTable {
|
||||
#[inline]
|
||||
fn init_size() -> usize {
|
||||
ATOM_TABLE_INIT_SIZE
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn align() -> usize {
|
||||
ATOM_TABLE_ALIGN
|
||||
}
|
||||
}
|
||||
|
||||
#[bitfield]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct AtomHeader {
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
len: B50,
|
||||
#[allow(unused)]
|
||||
padding: B13,
|
||||
}
|
||||
|
||||
impl AtomHeader {
|
||||
fn build_with(len: u64) -> Self {
|
||||
AtomHeader::new().with_len(len).with_m(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Atom {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||
self.as_str().hash(hasher)
|
||||
// hasher.write_usize(self.index)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! is_char {
|
||||
($s:expr) => {
|
||||
!$s.is_empty() && $s.chars().nth(1).is_none()
|
||||
};
|
||||
}
|
||||
|
||||
pub enum AtomString<'a> {
|
||||
Static(&'a str),
|
||||
Dynamic(AtomTableRef<str>),
|
||||
}
|
||||
|
||||
impl AtomString<'_> {
|
||||
pub fn map<F>(self, f: F) -> Self
|
||||
where
|
||||
for<'a> F: FnOnce(&'a str) -> &'a str,
|
||||
{
|
||||
match self {
|
||||
Self::Static(reference) => Self::Static(f(reference)),
|
||||
Self::Dynamic(guard) => Self::Dynamic(AtomTableRef::map(guard, f)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AtomString<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Debug::fmt(self.deref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AtomString<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.deref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for AtomString<'_> {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Static(reference) => reference,
|
||||
Self::Dynamic(guard) => guard.deref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
impl rustyline::completion::Candidate for AtomString<'_> {
|
||||
fn display(&self) -> &str {
|
||||
self.deref()
|
||||
}
|
||||
|
||||
fn replacement(&self) -> &str {
|
||||
self.deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Atom {
|
||||
#[inline(always)]
|
||||
pub fn is_static(self) -> bool {
|
||||
(self.index as usize) < STRINGS.len() << 3
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr(self) -> Option<AtomTableRef<u8>> {
|
||||
if self.is_static() {
|
||||
None
|
||||
} else {
|
||||
let atom_table =
|
||||
arc_atom_table().expect("We should only have an Atom while there is an AtomTable");
|
||||
unsafe {
|
||||
AtomTableRef::try_map(atom_table.buf(), |buf| {
|
||||
(buf as *const u8)
|
||||
.add((self.index as usize) - (STRINGS.len() << 3))
|
||||
.as_ref()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from(index: u64) -> Self {
|
||||
Self { index }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn len(self) -> usize {
|
||||
if self.is_static() {
|
||||
STRINGS[(self.index >> 3) as usize].len()
|
||||
} else {
|
||||
let ptr = self.as_ptr().unwrap();
|
||||
let ptr = ptr.deref() as *const u8 as *const AtomHeader;
|
||||
unsafe { ptr::read(ptr) }.len() as _
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn flat_index(self) -> u64 {
|
||||
self.index >> 3
|
||||
}
|
||||
|
||||
pub fn as_char(self) -> Option<char> {
|
||||
let s = self.as_str();
|
||||
let mut it = s.chars();
|
||||
|
||||
let c1 = it.next();
|
||||
let c2 = it.next();
|
||||
|
||||
if c2.is_none() {
|
||||
c1
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> AtomString<'static> {
|
||||
if self.is_static() {
|
||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||
} else if let Some(ptr) = self.as_ptr() {
|
||||
AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| {
|
||||
let header =
|
||||
// Miri seems to hit this line a lot
|
||||
unsafe { ptr::read::<AtomHeader>(ptr as *const u8 as *const AtomHeader) };
|
||||
let len = header.len() as usize;
|
||||
let buf = unsafe { (ptr as *const u8).add(mem::size_of::<AtomHeader>()) };
|
||||
|
||||
unsafe { str::from_utf8_unchecked(slice::from_raw_parts(buf, len)) }
|
||||
}))
|
||||
} else {
|
||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn defrock_brackets(&self, atom_tbl: &AtomTable) -> Self {
|
||||
let s = self.as_str();
|
||||
|
||||
let sub_str = if s.starts_with('(') && s.ends_with(')') {
|
||||
&s['('.len_utf8()..s.len() - ')'.len_utf8()]
|
||||
} else {
|
||||
return *self;
|
||||
};
|
||||
|
||||
AtomTable::build_with(atom_tbl, sub_str)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn write_to_ptr(string: &str, ptr: *mut u8) {
|
||||
ptr::write(ptr as *mut _, AtomHeader::build_with(string.len() as u64));
|
||||
let str_ptr = ptr.add(mem::size_of::<AtomHeader>());
|
||||
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr, string.len());
|
||||
}
|
||||
|
||||
impl PartialOrd for Atom {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Atom) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Atom {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Atom) -> Ordering {
|
||||
self.as_str().cmp(&*other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InnerAtomTable {
|
||||
block: RawBlock<AtomTable>,
|
||||
pub table: Rcu<IndexSet<Atom>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AtomTable {
|
||||
inner: Rcu<InnerAtomTable>,
|
||||
// this lock is taking during resizing
|
||||
update: Mutex<()>,
|
||||
}
|
||||
|
||||
pub type AtomTableRef<M> = RcuRef<InnerAtomTable, M>;
|
||||
|
||||
impl InnerAtomTable {
|
||||
#[inline(always)]
|
||||
fn lookup_str(self: &InnerAtomTable, string: &str) -> Option<Atom> {
|
||||
STATIC_ATOMS_MAP
|
||||
.get(string)
|
||||
.cloned()
|
||||
.or_else(|| self.table.active_epoch().get(string).cloned())
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomTable {
|
||||
#[inline]
|
||||
pub fn new() -> Arc<Self> {
|
||||
let upgraded = global_atom_table().read().unwrap().upgrade();
|
||||
// don't inline upgraded, otherwise temporary will be dropped too late in case of None
|
||||
if let Some(atom_table) = upgraded {
|
||||
atom_table
|
||||
} else {
|
||||
let mut guard = global_atom_table().write().unwrap();
|
||||
// try to upgrade again in case we lost the race on the write lock
|
||||
if let Some(atom_table) = guard.upgrade() {
|
||||
atom_table
|
||||
} else {
|
||||
let atom_table = Arc::new(Self {
|
||||
inner: Rcu::new(InnerAtomTable {
|
||||
block: RawBlock::new(),
|
||||
table: Rcu::new(IndexSet::new()),
|
||||
}),
|
||||
update: Mutex::new(()),
|
||||
});
|
||||
*guard = Arc::downgrade(&atom_table);
|
||||
atom_table
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn buf(&self) -> AtomTableRef<u8> {
|
||||
AtomTableRef::<InnerAtomTable>::map(self.inner.active_epoch(), |inner| {
|
||||
unsafe { inner.block.base.as_ref() }.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
|
||||
self.inner.active_epoch().table.active_epoch()
|
||||
}
|
||||
|
||||
pub fn build_with(atom_table: &AtomTable, string: &str) -> Atom {
|
||||
loop {
|
||||
let mut block_epoch = atom_table.inner.active_epoch();
|
||||
let mut table_epoch = block_epoch.table.active_epoch();
|
||||
|
||||
if let Some(atom) = block_epoch.lookup_str(string) {
|
||||
return atom;
|
||||
}
|
||||
|
||||
// take a lock to prevent concurrent updates
|
||||
let update_guard = atom_table.update.lock().unwrap();
|
||||
|
||||
let is_same_allocation =
|
||||
RcuRef::same_epoch(&block_epoch, &atom_table.inner.active_epoch());
|
||||
let is_same_atom_list =
|
||||
RcuRef::same_epoch(&table_epoch, &block_epoch.table.active_epoch());
|
||||
|
||||
if !(is_same_allocation && is_same_atom_list) {
|
||||
// some other thread raced us between our lookup and
|
||||
// us aquring the update lock,
|
||||
// try again
|
||||
continue;
|
||||
}
|
||||
|
||||
let size = mem::size_of::<AtomHeader>() + string.len();
|
||||
let align_offset = 8 * mem::align_of::<AtomHeader>();
|
||||
let size = (size & !(align_offset - 1)) + align_offset;
|
||||
|
||||
unsafe {
|
||||
let len_ptr = loop {
|
||||
let ptr = block_epoch.block.alloc(size);
|
||||
|
||||
if ptr.is_null() {
|
||||
// garbage collection would go here
|
||||
let new_block = block_epoch.block.grow_new().unwrap();
|
||||
let new_table = Rcu::new(table_epoch.clone());
|
||||
let new_alloc = InnerAtomTable {
|
||||
block: new_block,
|
||||
table: new_table,
|
||||
};
|
||||
atom_table.inner.replace(new_alloc);
|
||||
block_epoch = atom_table.inner.active_epoch();
|
||||
table_epoch = block_epoch.table.active_epoch();
|
||||
} else {
|
||||
break ptr;
|
||||
}
|
||||
};
|
||||
|
||||
let ptr_base = block_epoch.block.base as usize;
|
||||
|
||||
write_to_ptr(string, len_ptr);
|
||||
|
||||
let atom = Atom {
|
||||
index: ((STRINGS.len() << 3) + len_ptr as usize - ptr_base) as u64,
|
||||
};
|
||||
|
||||
let mut table = table_epoch.clone();
|
||||
table.insert(atom);
|
||||
block_epoch.table.replace(table);
|
||||
|
||||
// expicit drop to ensure we don't accidentally drop it early
|
||||
drop(update_guard);
|
||||
|
||||
return atom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for AtomTable {}
|
||||
unsafe impl Sync for AtomTable {}
|
||||
|
||||
#[bitfield]
|
||||
#[repr(u64)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct AtomCell {
|
||||
name: B46,
|
||||
arity: B10,
|
||||
#[allow(unused)]
|
||||
f: bool,
|
||||
#[allow(unused)]
|
||||
m: bool,
|
||||
#[allow(unused)]
|
||||
tag: B6,
|
||||
}
|
||||
|
||||
impl AtomCell {
|
||||
#[inline]
|
||||
pub fn build_with(name: u64, arity: u16, tag: HeapCellValueTag) -> Self {
|
||||
if arity > 0 {
|
||||
debug_assert!(arity as usize <= MAX_ARITY);
|
||||
|
||||
AtomCell::new()
|
||||
.with_name(name)
|
||||
.with_arity(arity)
|
||||
.with_f(false)
|
||||
.with_tag(tag as u8)
|
||||
} else {
|
||||
AtomCell::new()
|
||||
.with_name(name)
|
||||
.with_f(false)
|
||||
.with_tag(tag as u8)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_index(self) -> usize {
|
||||
self.name() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name(self) -> Atom {
|
||||
Atom::from((self.get_index() as u64) << 3)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_arity(self) -> usize {
|
||||
self.arity() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_name_and_arity(self) -> (Atom, usize) {
|
||||
(Atom::from((self.get_index() as u64) << 3), self.get_arity())
|
||||
}
|
||||
}
|
||||
27
src/bin/scryer-prolog.rs
Normal file
27
src/bin/scryer-prolog.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
fn main() -> std::process::ExitCode {
|
||||
use scryer_prolog::atom_table::Atom;
|
||||
use scryer_prolog::*;
|
||||
|
||||
#[cfg(feature = "repl")]
|
||||
ctrlc::set_handler(move || {
|
||||
scryer_prolog::machine::INTERRUPT.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
runtime.block_on(async move {
|
||||
let mut wam = machine::Machine::new(Default::default());
|
||||
wam.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 0))
|
||||
})
|
||||
}
|
||||
1352
src/codegen.rs
Normal file
1352
src/codegen.rs
Normal file
File diff suppressed because it is too large
Load Diff
908
src/debray_allocator.rs
Normal file
908
src/debray_allocator.rs
Normal file
@@ -0,0 +1,908 @@
|
||||
use crate::allocator::*;
|
||||
use crate::codegen::SubsumedBranchHits;
|
||||
use crate::forms::Level;
|
||||
use crate::instructions::*;
|
||||
use crate::machine::disjuncts::VarData;
|
||||
use crate::parser::ast::*;
|
||||
use crate::targets::*;
|
||||
use crate::variable_records::*;
|
||||
|
||||
use bit_set::*;
|
||||
use bitvec::prelude::*;
|
||||
use fxhash::FxBuildHasher;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub type BranchHits = IndexMap<usize, BitVec, FxBuildHasher>; // key: var_num, value: branch arm occurrences.
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct BranchOccurrences {
|
||||
pub hits: BranchHits,
|
||||
pub shallow_safety: BitSet<usize>, // unset means safe, set means unsafe (after the branch merge)
|
||||
pub deep_safety: BitSet<usize>,
|
||||
pub num_branches: usize,
|
||||
pub current_branch: usize,
|
||||
pub subsumed_hits: SubsumedBranchHits,
|
||||
}
|
||||
|
||||
impl BranchOccurrences {
|
||||
fn new(num_branches: usize) -> Self {
|
||||
Self {
|
||||
hits: BranchHits::with_hasher(FxBuildHasher::default()),
|
||||
shallow_safety: BitSet::default(),
|
||||
deep_safety: BitSet::default(),
|
||||
num_branches,
|
||||
current_branch: 0,
|
||||
subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
|
||||
debug_assert!(self.current_branch < self.num_branches);
|
||||
let num_branches = self.num_branches;
|
||||
|
||||
let entry = self
|
||||
.hits
|
||||
.entry(var_num)
|
||||
.or_insert_with(|| BitVec::repeat(false, num_branches));
|
||||
|
||||
entry.set(self.current_branch, true);
|
||||
self.subsumed_hits.insert(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct BranchStack {
|
||||
stack: Vec<BranchOccurrences>,
|
||||
}
|
||||
|
||||
impl Deref for BranchStack {
|
||||
type Target = Vec<BranchOccurrences>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.stack
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for BranchStack {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.stack
|
||||
}
|
||||
}
|
||||
|
||||
impl BranchStack {
|
||||
fn branch_subsumes(&self, branch: &BranchDesignator, sub_branch: &BranchDesignator) -> bool {
|
||||
if branch.branch_stack_num < sub_branch.branch_stack_num {
|
||||
if branch.branch_stack_num == 0 {
|
||||
true
|
||||
} else {
|
||||
let idx = branch.branch_stack_num - 1;
|
||||
self[idx].current_branch == branch.branch_num
|
||||
}
|
||||
} else {
|
||||
branch == sub_branch
|
||||
}
|
||||
}
|
||||
|
||||
fn safety_unneeded_in_branch(
|
||||
&self,
|
||||
safety: &VarSafetyStatus,
|
||||
branch: &BranchDesignator,
|
||||
) -> bool {
|
||||
match safety {
|
||||
VarSafetyStatus::Needed => false,
|
||||
VarSafetyStatus::LocallyUnneeded(planter_branch) => {
|
||||
self.branch_subsumes(planter_branch, branch)
|
||||
}
|
||||
VarSafetyStatus::GloballyUnneeded => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
|
||||
if let Some(occurrences) = self.last_mut() {
|
||||
occurrences.add_branch_occurrence(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_branch_stack(&mut self, num_branches: usize) {
|
||||
self.push(BranchOccurrences::new(num_branches));
|
||||
}
|
||||
|
||||
pub(crate) fn current_branch_designator(&self) -> BranchDesignator {
|
||||
let branch_stack_num = self.len();
|
||||
let branch_num = self
|
||||
.last()
|
||||
.map(|occurrences| occurrences.current_branch)
|
||||
.unwrap_or(0);
|
||||
|
||||
BranchDesignator {
|
||||
branch_stack_num,
|
||||
branch_num,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn incr_current_branch(&mut self) {
|
||||
let branch_occurrences = self.last_mut().unwrap();
|
||||
branch_occurrences.current_branch += 1;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn drain_branches(&mut self, depth: usize) -> std::vec::Drain<BranchOccurrences> {
|
||||
let start_idx = self.len() - depth;
|
||||
self.drain(start_idx..)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct DebrayAllocator {
|
||||
pub(crate) var_data: VarData, // var_data replaces bindings.
|
||||
pub(crate) branch_stack: BranchStack,
|
||||
pub(crate) in_tail_position: bool,
|
||||
arg_c: usize,
|
||||
temp_lb: usize,
|
||||
perm_lb: usize,
|
||||
arity: usize, // 0 if not at head.
|
||||
shallow_temp_mappings: IndexMap<usize, usize, FxBuildHasher>,
|
||||
in_use: BitSet<usize>, // deep and non-var allocations
|
||||
temp_free_list: Vec<usize>,
|
||||
perm_free_list: VecDeque<(usize, usize)>, // chunk_num, var_num
|
||||
}
|
||||
|
||||
impl DebrayAllocator {
|
||||
pub(crate) fn add_branch(&mut self) {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
let subsumed_hits = {
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
|
||||
std::mem::replace(
|
||||
&mut branch_occurrences.subsumed_hits,
|
||||
SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
|
||||
)
|
||||
};
|
||||
|
||||
for var_num in subsumed_hits {
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, ref mut allocation) => {
|
||||
if let PermVarAllocation::Done {
|
||||
shallow_safety,
|
||||
deep_safety,
|
||||
..
|
||||
} = allocation
|
||||
{
|
||||
if !self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(shallow_safety, &branch_designator)
|
||||
{
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.shallow_safety.insert(var_num);
|
||||
}
|
||||
|
||||
if !self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(deep_safety, &branch_designator)
|
||||
{
|
||||
let branch_occurrences = self.branch_stack.last_mut().unwrap();
|
||||
branch_occurrences.deep_safety.insert(var_num);
|
||||
}
|
||||
}
|
||||
|
||||
*allocation = PermVarAllocation::Pending;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn pop_branch(&mut self, depth: usize, subsumed_hits: SubsumedBranchHits) {
|
||||
let removed_branches = self.branch_stack.drain_branches(depth);
|
||||
|
||||
let (deep_safety, shallow_safety) = removed_branches.into_iter().fold(
|
||||
(BitSet::default(), BitSet::default()),
|
||||
|(mut deep_safety, mut shallow_safety), branch_occurrences| {
|
||||
deep_safety.union_with(&branch_occurrences.deep_safety);
|
||||
shallow_safety.union_with(&branch_occurrences.shallow_safety);
|
||||
|
||||
(deep_safety, shallow_safety)
|
||||
},
|
||||
);
|
||||
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() {
|
||||
Some(latest_branch) => {
|
||||
latest_branch.deep_safety.union_with(&deep_safety);
|
||||
latest_branch.shallow_safety.union_with(&shallow_safety);
|
||||
|
||||
(&latest_branch.deep_safety, &latest_branch.shallow_safety)
|
||||
}
|
||||
None => (&deep_safety, &shallow_safety),
|
||||
};
|
||||
|
||||
for var_num in subsumed_hits.iter().cloned() {
|
||||
let running_count = self.var_data.records[var_num].running_count;
|
||||
let num_occurrences = self.var_data.records[var_num].num_occurrences;
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, allocation) => {
|
||||
let shallow_safety = VarSafetyStatus::needed_if(
|
||||
shallow_safety.contains(var_num),
|
||||
branch_designator,
|
||||
);
|
||||
|
||||
let deep_safety = VarSafetyStatus::needed_if(
|
||||
deep_safety.contains(var_num),
|
||||
branch_designator,
|
||||
);
|
||||
|
||||
if running_count < num_occurrences {
|
||||
*allocation = PermVarAllocation::Done {
|
||||
shallow_safety,
|
||||
deep_safety,
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
if self.branch_stack.len() > 0 {
|
||||
for var_num in subsumed_hits {
|
||||
self.branch_stack.add_branch_occurrence(var_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_curr_arg_distinct_from(&self, var_num: usize) -> bool {
|
||||
match self.shallow_temp_mappings.get(&self.arg_c).cloned() {
|
||||
Some(t_var) => t_var != var_num,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn occurs_shallowly_in_head(&self, var_num: usize, r: usize) -> bool {
|
||||
match &self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Temp {
|
||||
temp_var_data,
|
||||
term_loc: GenContext::Head,
|
||||
..
|
||||
} => temp_var_data.use_set.contains(&(GenContext::Head, r)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_in_use(&self, r: usize) -> bool {
|
||||
let in_use_range = r <= self.arity && r >= self.arg_c;
|
||||
in_use_range || self.in_use.contains(r)
|
||||
}
|
||||
|
||||
fn alloc_with_cr(&self, var_num: usize) -> usize {
|
||||
match &self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Temp { temp_var_data, .. } => {
|
||||
for &(_, reg) in temp_var_data.use_set.iter() {
|
||||
if !self.is_in_use(reg) {
|
||||
return reg;
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = 0;
|
||||
|
||||
for reg in self.temp_lb.. {
|
||||
if !self.is_in_use(reg) && !temp_var_data.no_use_set.contains(reg) {
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_with_ca(&self, var_num: usize) -> usize {
|
||||
match &self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Temp { temp_var_data, .. } => {
|
||||
for &(_, reg) in temp_var_data.use_set.iter() {
|
||||
if !self.is_in_use(reg) {
|
||||
return reg;
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = 0;
|
||||
|
||||
for reg in self.temp_lb.. {
|
||||
if !self.is_in_use(reg)
|
||||
&& !temp_var_data.no_use_set.contains(reg)
|
||||
&& !temp_var_data.conflict_set.contains(reg)
|
||||
{
|
||||
result = reg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(usize, usize)> {
|
||||
// we want to allocate a register to the k^{th} parameter, par_k.
|
||||
// par_k may not be a temporary variable.
|
||||
let k = self.arg_c;
|
||||
|
||||
match self.shallow_temp_mappings.get(&k).cloned() {
|
||||
Some(t_var) => {
|
||||
// suppose this branch fires. then t_var is a
|
||||
// temp. var. belonging to the current chunk.
|
||||
// consider its use set. T == par_k iff
|
||||
// (GenContext::Last(_), k) is in t_var.use_set.
|
||||
|
||||
if let VarAlloc::Temp { temp_var_data, .. } =
|
||||
&self.var_data.records[t_var].allocation
|
||||
{
|
||||
if !temp_var_data
|
||||
.use_set
|
||||
.contains(&(GenContext::Last(chunk_num), k))
|
||||
{
|
||||
return Some((t_var, self.alloc_with_ca(t_var)));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn evacuate_arg<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
chunk_num: usize,
|
||||
code: &mut CodeDeque,
|
||||
) {
|
||||
if let Some((var_num, r)) = self.alloc_in_last_goal_hint(chunk_num) {
|
||||
let k = self.arg_c;
|
||||
|
||||
if r != k {
|
||||
let r = RegType::Temp(r);
|
||||
|
||||
code.push_back(Target::move_to_register(r, k));
|
||||
|
||||
self.shallow_temp_mappings.swap_remove(&k);
|
||||
self.shallow_temp_mappings.insert(r.reg_num(), var_num);
|
||||
|
||||
self.var_data.records[var_num]
|
||||
.allocation
|
||||
.set_register(r.reg_num());
|
||||
self.in_use.insert(r.reg_num());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn alloc_reg_to_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
term_loc: GenContext,
|
||||
target: &mut CodeDeque,
|
||||
) -> usize {
|
||||
match term_loc {
|
||||
GenContext::Head => {
|
||||
if let Level::Shallow = lvl {
|
||||
self.evacuate_arg::<Target>(0, target);
|
||||
self.alloc_with_cr(var_num)
|
||||
} else {
|
||||
self.alloc_with_ca(var_num)
|
||||
}
|
||||
}
|
||||
GenContext::Mid(_) => self.alloc_with_ca(var_num),
|
||||
GenContext::Last(chunk_num) => {
|
||||
if let Level::Shallow = lvl {
|
||||
self.evacuate_arg::<Target>(chunk_num, target);
|
||||
self.alloc_with_cr(var_num)
|
||||
} else {
|
||||
self.alloc_with_ca(var_num)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_reg_to_non_var(&mut self) -> usize {
|
||||
let mut final_index = 0;
|
||||
|
||||
while let Some(r) = self.temp_free_list.pop() {
|
||||
if !self.is_in_use(r) {
|
||||
self.in_use.insert(r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
for index in self.temp_lb.. {
|
||||
if !self.in_use.contains(index) {
|
||||
final_index = index;
|
||||
self.in_use.insert(final_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.temp_lb = final_index + 1;
|
||||
final_index
|
||||
}
|
||||
|
||||
fn in_place(&self, var_num: usize, term_loc: GenContext, r: RegType, k: usize) -> bool {
|
||||
match term_loc {
|
||||
GenContext::Head if !r.is_perm() => r.reg_num() == k,
|
||||
_ => match &self.var_data.records[var_num].allocation {
|
||||
&VarAlloc::Temp { temp_reg, .. } if r.reg_num() == k => temp_reg == k,
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_perm_var(&mut self, var_num: usize, chunk_num: usize) -> usize {
|
||||
let p = if let Some(p) = self.pop_free_perm(chunk_num) {
|
||||
p
|
||||
} else {
|
||||
let p = self.perm_lb;
|
||||
self.perm_lb += 1;
|
||||
|
||||
p
|
||||
};
|
||||
|
||||
self.var_data.records[var_num].allocation = VarAlloc::Perm(p, PermVarAllocation::done());
|
||||
p
|
||||
}
|
||||
|
||||
pub(crate) fn add_reg_to_free_list(&mut self, r: RegType) {
|
||||
if let RegType::Temp(r) = r {
|
||||
self.in_use.remove(r);
|
||||
self.temp_free_list.push(r);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_free_list(&mut self) {
|
||||
self.temp_free_list.clear();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_binding(&self, var_num: usize) -> RegType {
|
||||
self.var_data.records[var_num].allocation.as_reg_type()
|
||||
}
|
||||
|
||||
pub fn num_perm_vars(&self) -> usize {
|
||||
self.perm_lb - 1
|
||||
}
|
||||
|
||||
pub fn increment_running_count(&mut self, var_num: usize) {
|
||||
self.var_data.records[var_num].running_count += 1;
|
||||
}
|
||||
|
||||
fn add_perm_to_free_list(&mut self, chunk_num: usize, var_num: usize) {
|
||||
if let VarAlloc::Perm(..) = &self.var_data.records[var_num].allocation {
|
||||
self.perm_free_list.push_back((chunk_num, var_num));
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_free_perm(&mut self, chunk_num: usize) -> Option<usize> {
|
||||
while let Some((perm_chunk_num, var_num)) = self.perm_free_list.front().cloned() {
|
||||
if chunk_num > perm_chunk_num {
|
||||
self.perm_free_list.pop_front();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(p, PermVarAllocation::Pending) if *p > 0 => {
|
||||
return Some(std::mem::replace(p, 0));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn free_var(&mut self, chunk_num: usize, var_num: usize) {
|
||||
if let VarAlloc::Perm(_, allocation) = &mut self.var_data.records[var_num].allocation {
|
||||
*allocation = PermVarAllocation::Pending;
|
||||
self.add_perm_to_free_list(chunk_num, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mark_safe_var_unconditionally(&mut self, var_num: usize) {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
deep_safety,
|
||||
shallow_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
}
|
||||
VarAlloc::Temp { safety, .. } => {
|
||||
*safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
deep_safety,
|
||||
shallow_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
// GetVariable in head chunk is considered safe.
|
||||
if lvl == Level::Deep {
|
||||
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
} else if term_loc == GenContext::Head {
|
||||
*shallow_safety = VarSafetyStatus::GloballyUnneeded;
|
||||
} else if let Some(&temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c) {
|
||||
match &mut self.var_data.records[temp_var_num].allocation {
|
||||
VarAlloc::Temp {
|
||||
ref mut to_perm_var_num,
|
||||
..
|
||||
} => {
|
||||
*to_perm_var_num = Some(var_num);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
VarAlloc::Temp { ref mut safety, .. } => {
|
||||
*safety = VarSafetyStatus::GloballyUnneeded;
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn argument_to_value<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
r: RegType,
|
||||
arg_c: usize,
|
||||
) -> Instruction {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
ref mut shallow_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if !self.in_tail_position
|
||||
|| self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(shallow_safety, &branch_designator)
|
||||
{
|
||||
Target::argument_to_value(r, arg_c)
|
||||
} else {
|
||||
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
Target::unsafe_argument_to_value(r, arg_c)
|
||||
}
|
||||
}
|
||||
VarAlloc::Temp { .. } => {
|
||||
debug_assert!(matches!(r, RegType::Temp(_)));
|
||||
Target::argument_to_value(r, arg_c)
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn subterm_to_value<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
r: RegType,
|
||||
) -> Instruction {
|
||||
let branch_designator = self.branch_stack.current_branch_designator();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(
|
||||
_,
|
||||
PermVarAllocation::Done {
|
||||
ref mut deep_safety,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(deep_safety, &branch_designator)
|
||||
{
|
||||
Target::subterm_to_value(r)
|
||||
} else {
|
||||
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
Target::unsafe_subterm_to_value(r)
|
||||
}
|
||||
}
|
||||
VarAlloc::Temp { ref mut safety, .. } => {
|
||||
if self
|
||||
.branch_stack
|
||||
.safety_unneeded_in_branch(safety, &branch_designator)
|
||||
{
|
||||
Target::subterm_to_value(r)
|
||||
} else {
|
||||
*safety = VarSafetyStatus::unneeded(branch_designator);
|
||||
Target::unsafe_subterm_to_value(r)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Allocator for DebrayAllocator {
|
||||
fn new() -> DebrayAllocator {
|
||||
Self {
|
||||
var_data: VarData::default(),
|
||||
in_tail_position: false,
|
||||
arity: 0,
|
||||
arg_c: 1,
|
||||
temp_lb: 1,
|
||||
perm_lb: 1,
|
||||
shallow_temp_mappings: IndexMap::with_hasher(FxBuildHasher::default()),
|
||||
in_use: BitSet::default(),
|
||||
temp_free_list: vec![],
|
||||
perm_free_list: VecDeque::new(),
|
||||
branch_stack: BranchStack { stack: vec![] },
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
lvl: Level,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
) {
|
||||
let r = RegType::Temp(self.alloc_reg_to_non_var());
|
||||
|
||||
match lvl {
|
||||
Level::Deep => code.push_back(Target::subterm_to_variable(r)),
|
||||
Level::Root | Level::Shallow => {
|
||||
let k = self.arg_c;
|
||||
|
||||
if let GenContext::Last(chunk_num) = term_loc {
|
||||
self.evacuate_arg::<Target>(chunk_num, code);
|
||||
}
|
||||
|
||||
self.arg_c += 1;
|
||||
|
||||
code.push_back(Target::argument_to_variable(r, k));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
lvl: Level,
|
||||
term_loc: GenContext,
|
||||
cell: &'a Cell<RegType>,
|
||||
code: &mut CodeDeque,
|
||||
) {
|
||||
let r = cell.get();
|
||||
|
||||
let r = match lvl {
|
||||
Level::Shallow => {
|
||||
let k = self.arg_c;
|
||||
|
||||
if let GenContext::Last(chunk_num) = term_loc {
|
||||
self.evacuate_arg::<Target>(chunk_num, code);
|
||||
}
|
||||
|
||||
self.arg_c += 1;
|
||||
RegType::Temp(k)
|
||||
}
|
||||
_ if r.reg_num() == 0 => RegType::Temp(self.alloc_reg_to_non_var()),
|
||||
_ => {
|
||||
self.in_use.insert(r.reg_num());
|
||||
r
|
||||
}
|
||||
};
|
||||
|
||||
cell.set(r);
|
||||
}
|
||||
|
||||
fn mark_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
) {
|
||||
let (r, is_new_var) = match self.get_binding(var_num) {
|
||||
RegType::Temp(0) => {
|
||||
let o = self.alloc_reg_to_var::<Target>(var_num, lvl, term_loc, code);
|
||||
cell.set(VarReg::Norm(RegType::Temp(o)));
|
||||
(RegType::Temp(o), true)
|
||||
}
|
||||
RegType::Perm(0) => {
|
||||
let p = self.alloc_perm_var(var_num, term_loc.chunk_num());
|
||||
cell.set(VarReg::Norm(RegType::Perm(p)));
|
||||
(RegType::Perm(p), true)
|
||||
}
|
||||
r @ RegType::Perm(_) => {
|
||||
let is_new_var = match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Perm(_, allocation) => {
|
||||
if allocation.pending() {
|
||||
*allocation = PermVarAllocation::done();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
(r, is_new_var)
|
||||
}
|
||||
r => (r, false),
|
||||
};
|
||||
|
||||
self.mark_reserved_var::<Target>(var_num, lvl, cell, term_loc, code, r, is_new_var);
|
||||
}
|
||||
|
||||
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
|
||||
&mut self,
|
||||
var_num: usize,
|
||||
lvl: Level,
|
||||
cell: &Cell<VarReg>,
|
||||
term_loc: GenContext,
|
||||
code: &mut CodeDeque,
|
||||
r: RegType,
|
||||
is_new_var: bool,
|
||||
) {
|
||||
match lvl {
|
||||
Level::Root | Level::Shallow => {
|
||||
let k = self.arg_c;
|
||||
|
||||
if self.is_curr_arg_distinct_from(var_num) {
|
||||
self.evacuate_arg::<Target>(term_loc.chunk_num(), code);
|
||||
}
|
||||
|
||||
cell.set(VarReg::ArgAndNorm(r, k));
|
||||
|
||||
if !self.in_place(var_num, term_loc, r, k) {
|
||||
if is_new_var {
|
||||
self.mark_safe_var(var_num, lvl, term_loc);
|
||||
code.push_back(Target::argument_to_variable(r, k));
|
||||
} else {
|
||||
code.push_back(self.argument_to_value::<Target>(var_num, r, k));
|
||||
}
|
||||
}
|
||||
|
||||
self.arg_c += 1;
|
||||
}
|
||||
Level::Deep if is_new_var => {
|
||||
if let GenContext::Head = term_loc {
|
||||
if self.occurs_shallowly_in_head(var_num, r.reg_num()) {
|
||||
code.push_back(self.subterm_to_value::<Target>(var_num, r));
|
||||
} else {
|
||||
self.mark_safe_var(var_num, lvl, term_loc);
|
||||
code.push_back(Target::subterm_to_variable(r));
|
||||
}
|
||||
} else {
|
||||
self.mark_safe_var(var_num, lvl, term_loc);
|
||||
code.push_back(Target::subterm_to_variable(r));
|
||||
}
|
||||
}
|
||||
Level::Deep => code.push_back(self.subterm_to_value::<Target>(var_num, r)),
|
||||
}
|
||||
|
||||
let o = r.reg_num();
|
||||
|
||||
if !r.is_perm() {
|
||||
self.shallow_temp_mappings.insert(o, var_num);
|
||||
} else if r.is_perm() && is_new_var {
|
||||
self.branch_stack.add_branch_occurrence(var_num);
|
||||
}
|
||||
|
||||
let record = &mut self.var_data.records[var_num];
|
||||
|
||||
record.allocation.set_register(o);
|
||||
|
||||
if record.running_count < record.num_occurrences {
|
||||
record.running_count += 1;
|
||||
} else {
|
||||
self.free_var(term_loc.chunk_num(), var_num);
|
||||
}
|
||||
|
||||
self.in_use.insert(o);
|
||||
}
|
||||
|
||||
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType {
|
||||
match self.get_binding(var_num) {
|
||||
RegType::Perm(0) => RegType::Perm(self.alloc_perm_var(var_num, chunk_num)),
|
||||
RegType::Temp(0) => {
|
||||
let t = self.alloc_reg_to_non_var();
|
||||
|
||||
match &mut self.var_data.records[var_num].allocation {
|
||||
VarAlloc::Temp {
|
||||
temp_reg, safety, ..
|
||||
} => {
|
||||
*temp_reg = t;
|
||||
*safety = VarSafetyStatus::GloballyUnneeded;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
RegType::Temp(t)
|
||||
}
|
||||
r => r,
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.perm_lb = 1;
|
||||
self.shallow_temp_mappings.clear();
|
||||
self.in_use.clear();
|
||||
self.temp_free_list.clear();
|
||||
}
|
||||
|
||||
fn reset_contents(&mut self) {
|
||||
self.in_use.clear();
|
||||
self.shallow_temp_mappings.clear();
|
||||
self.temp_free_list.clear();
|
||||
}
|
||||
|
||||
fn advance_arg(&mut self) {
|
||||
self.arg_c += 1;
|
||||
}
|
||||
|
||||
fn reset_at_head(&mut self, args: &[Term]) {
|
||||
self.reset_arg(args.len());
|
||||
self.arity = args.len();
|
||||
|
||||
for (idx, arg) in args.iter().enumerate() {
|
||||
if let Term::Var(_, ref var) = arg {
|
||||
let var_num = var.to_var_num().unwrap();
|
||||
let r = self.get_binding(var_num);
|
||||
|
||||
if !r.is_perm() && r.reg_num() == 0 {
|
||||
self.in_use.insert(idx + 1);
|
||||
self.shallow_temp_mappings.insert(idx + 1, var_num);
|
||||
self.var_data.records[var_num]
|
||||
.allocation
|
||||
.set_register(idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_arg(&mut self, arity: usize) {
|
||||
self.arity = 0;
|
||||
self.arg_c = 1;
|
||||
self.temp_lb = arity + 1;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn max_reg_allocated(&self) -> usize {
|
||||
std::cmp::max(self.temp_lb, self.arg_c)
|
||||
}
|
||||
}
|
||||
219
src/examples/bimetatrans/README.md
Normal file
219
src/examples/bimetatrans/README.md
Normal file
@@ -0,0 +1,219 @@
|
||||
### BiMetaTrans 1.0
|
||||
|
||||
BiMetaTrans(Prolog, RuleML) is a bidirectional translator capable of
|
||||
parsing and generating a well-formed sublanguage of RuleML/XML to/from
|
||||
a metalogical encoding of our own design called Prolog/'$V'. For later
|
||||
ease of reference:
|
||||
|
||||
* Prolog/'$V' stands for "Metalogic Prolog with variable-as-'$V'-term encoding"
|
||||
* RuleML/XML stands for "RuleML in XML with any kind of rendering"
|
||||
* RuleML/xmL stands for "RuleML in minified XML"
|
||||
* RuleML/Xml stands for "RuleML in indented XML"
|
||||
|
||||
"Minified XML" refers to XML with only necessary whitespace, between
|
||||
element names and attributes, and pairs of attributes, with all
|
||||
indentation stripped out. "Indented XML" contains indentation and
|
||||
newlines, and is usually formatted for human readers.
|
||||
|
||||
The specification of Prolog/'$V', and of the NafHornlogEq sublanguage
|
||||
of RuleML/XML targeted by BiMetaTrans, is contained in the preprint
|
||||
[Invertible Bidirectional Metalogical Translation Between Prolog and
|
||||
RuleML/XML for Knowledge Representation and
|
||||
Querying](http://ruleml.org/papers/RuleMLXMLBiDirTransScryer.pdf) and
|
||||
its accompanying talk
|
||||
[slides](http://ruleml.org/talks/RuleMLXMLBiDirTransScryer-talk.pdf). The
|
||||
preprint and talk also describe the implementation of BiMetaTrans
|
||||
found here, and outlines a strategy for proving the invertibility of
|
||||
BiMetaTrans.
|
||||
|
||||
BiMetaTrans exports a single public predicate subsuming its user
|
||||
API, `parse_ruleml/3`. It has two modes, each corresponding to a single
|
||||
direction of translation:
|
||||
|
||||
```
|
||||
parse_ruleml(+AssertItems, +QueryItems, ?XML) (Prolog->RuleML)
|
||||
parse_ruleml(?AssertItems, ?QueryItems, +XML) (RuleML->Prolog)
|
||||
```
|
||||
|
||||
The modes constrain the inputs to fit one of two patterns, the
|
||||
first where AssertItems and QueryItems are instantiated and XML is
|
||||
possibly a variable, and conversely for the second. Instantiated
|
||||
inputs are expected to be ground, meaning they should not contain
|
||||
free variables.
|
||||
|
||||
We explore several examples of its use in Scryer Prolog.
|
||||
|
||||
Loading BiMetaTrans from the Scryer REPL:
|
||||
|
||||
```
|
||||
?- use_module('src/examples/bimetatrans/bimetatrans').
|
||||
```
|
||||
|
||||
Using `write/1` to print the string to standard output, capturing the
|
||||
`Prolog->RuleML` direction (`write/1` is used because it does not
|
||||
print strings with escape characters, ie., `\"` for double quote):
|
||||
|
||||
```
|
||||
?- parse_ruleml([people('Alex',male),people('Alex',female),people('Siri',female)], [], XML),
|
||||
write(XML).
|
||||
"<Assert mapClosure="universal"><Atom><Rel>people</Rel><Ind>Alex</Ind><Data iso:type="symbol">male</Data></Atom><Atom><Rel>people</Rel><Ind>Alex</Ind><Data iso:type="symbol">female</Data></Atom><Atom><Rel>people</Rel><Ind>Siri</Ind><Data iso:type="symbol">female</Data></Atom></Assert>" XML = "<Assert mapClosure= ...".
|
||||
```
|
||||
|
||||
Note that the XML input can contain extraneous whitespace and
|
||||
indentation while generated XML never does (ie., the XML generated by
|
||||
BiMetaTrans is always Ruleml/xmL). `\` is used to continue ISO Prolog
|
||||
strings to the next line but is never stored to the string by the
|
||||
Prolog reader.
|
||||
|
||||
Performing the inverse translation of the previous example
|
||||
(`RuleML->Prolog`), in RuleML/Xml:
|
||||
|
||||
```
|
||||
?- parse_ruleml(AssertItems, QueryItems,
|
||||
"<Assert mapClosure=\"universal\">\
|
||||
<Atom>\
|
||||
<Rel>people</Rel>\
|
||||
<Ind>Alex</Ind>\
|
||||
<Data iso:type=\"symbol\">male</Data>\
|
||||
</Atom>\
|
||||
<Atom>\
|
||||
<Rel>people</Rel>\
|
||||
<Ind>Alex</Ind>\
|
||||
<Data iso:type=\"symbol\">female</Data>\
|
||||
</Atom>\
|
||||
<Atom>\
|
||||
<Rel>people</Rel>\
|
||||
<Ind>Siri</Ind>\
|
||||
<Data iso:type=\"symbol\">female</Data>\
|
||||
</Atom>\
|
||||
</Assert>").
|
||||
AssertItems = [people('Alex',male),people('Alex',female),people('Siri',female)], QueryItems = [].
|
||||
```
|
||||
|
||||
Double quote characters within strings must be escaped as in
|
||||
`\"`. Non-empty AssertItems and QueryItems lists generated to
|
||||
RuleML/xmL simultaneously:
|
||||
|
||||
```
|
||||
?- parse_ruleml([a(item), b(item), c(item)], [(?- p, q, r(1), s(-2.222342432), t("attached")), (?- u, v('$V'(q)))], XML),
|
||||
write(XML).
|
||||
"<Assert mapClosure="universal"><Atom><Rel>a</Rel><Data iso:type="symbol">item</Data></Atom><Atom><Rel>b</Rel><Data iso:type="symbol">item</Data></Atom><Atom><Rel>c</Rel><Data iso:type="symbol">item</Data></Atom></Assert><Query closure="existential"><And><Atom><Rel>p</Rel></Atom><Atom><Rel>q</Rel></Atom><Atom><Rel>r</Rel><Data iso:type="number">1</Data></Atom><Atom><Rel>s</Rel><Data iso:type="number">-2.222342432</Data></Atom><Atom><Rel>t</Rel><Data iso:type="string">"attached"</Data></Atom></And></Query><Query closure="existential"><And><Atom><Rel>u</Rel></Atom><Atom><Rel>v</Rel><Var>q</Var></Atom></And></Query>" XML = "<Assert mapClosure= ...".
|
||||
```
|
||||
|
||||
To insert escape characters into the printed RuleML/xmL, we use
|
||||
`writeq/1` in place of `write/1`:
|
||||
|
||||
```
|
||||
?- parse_ruleml([a(item), b(item), c(item)], [(?- p, q, r(1), s(-2.222342432), t("attached")), (?- u, v('$V'(q)))], XML),
|
||||
writeq(XML).
|
||||
"<Assert mapClosure=\"universal\"><Atom><Rel>a</Rel><Data iso:type=\"symbol\">item</Data></Atom><Atom><Rel>b</Rel><Data iso:type=\"symbol\">item</Data></Atom><Atom><Rel>c</Rel><Data iso:type=\"symbol\">item</Data></Atom></Assert><Query closure=\"existential\"><And><Atom><Rel>p</Rel></Atom><Atom><Rel>q</Rel></Atom><Atom><Rel>r</Rel><Data iso:type=\"number\">1</Data></Atom><Atom><Rel>s</Rel><Data iso:type=\"number\">-2.222342432</Data></Atom><Atom><Rel>t</Rel><Data iso:type=\"string\">\"attached\"</Data></Atom></And></Query><Query closure=\"existential\"><And><Atom><Rel>u</Rel></Atom><Atom><Rel>v</Rel><Var>q</Var></Atom></And></Query>" XML = "<Assert mapClosure= ...".
|
||||
```
|
||||
|
||||
XML can be pretty printed using a [free online XML pretty
|
||||
printer](https://codebeautify.org/xmlviewer), as here:
|
||||
|
||||
```
|
||||
<Assert mapClosure=\"universal\">
|
||||
<Atom>
|
||||
<Rel>a</Rel>
|
||||
<Data iso:type=\"symbol\">item</Data>
|
||||
</Atom>
|
||||
<Atom>
|
||||
<Rel>b</Rel>
|
||||
<Data iso:type=\"symbol\">item</Data>
|
||||
</Atom>
|
||||
<Atom>
|
||||
<Rel>c</Rel>
|
||||
<Data iso:type=\"symbol\">item</Data>
|
||||
</Atom>
|
||||
</Assert>
|
||||
<Query closure=\"existential\">
|
||||
<And>
|
||||
<Atom>
|
||||
<Rel>p</Rel>
|
||||
</Atom>
|
||||
<Atom>
|
||||
<Rel>q</Rel>
|
||||
</Atom>
|
||||
<Atom>
|
||||
<Rel>r</Rel>
|
||||
<Data iso:type=\"number\">1</Data>
|
||||
</Atom>
|
||||
<Atom>
|
||||
<Rel>s</Rel>
|
||||
<Data iso:type=\"number\">-2.222342432</Data>
|
||||
</Atom>
|
||||
<Atom>
|
||||
<Rel>t</Rel>
|
||||
<Data iso:type=\"string\">\"attached\"</Data>
|
||||
</Atom>
|
||||
</And>
|
||||
</Query>
|
||||
<Query closure=\"existential\">
|
||||
<And>
|
||||
<Atom>
|
||||
<Rel>u</Rel>
|
||||
</Atom>
|
||||
<Atom>
|
||||
<Rel>v</Rel>
|
||||
<Var>q</Var>
|
||||
</Atom>
|
||||
</And>
|
||||
</Query>
|
||||
```
|
||||
|
||||
The AssertItems and QueryItems lists can then be recovered using this
|
||||
RuleML/Xml string, with the `\` character added to the end of each
|
||||
line:
|
||||
|
||||
```
|
||||
?- parse_ruleml(AssertItems, QueryItems,
|
||||
"<Assert mapClosure=\"universal\">\
|
||||
<Atom>\
|
||||
<Rel>a</Rel>\
|
||||
<Data iso:type=\"symbol\">item</Data>\
|
||||
</Atom>\
|
||||
<Atom>\
|
||||
<Rel>b</Rel>\
|
||||
<Data iso:type=\"symbol\">item</Data>\
|
||||
</Atom>\
|
||||
<Atom>\
|
||||
<Rel>c</Rel>\
|
||||
<Data iso:type=\"symbol\">item</Data>\
|
||||
</Atom>\
|
||||
</Assert>\
|
||||
<Query closure=\"existential\">\
|
||||
<And>\
|
||||
<Atom>\
|
||||
<Rel>p</Rel>\
|
||||
</Atom>\
|
||||
<Atom>\
|
||||
<Rel>q</Rel>\
|
||||
</Atom>\
|
||||
<Atom>\
|
||||
<Rel>r</Rel>\
|
||||
<Data iso:type=\"number\">1</Data>\
|
||||
</Atom>\
|
||||
<Atom>\
|
||||
<Rel>s</Rel>\
|
||||
<Data iso:type=\"number\">-2.222342432</Data>\
|
||||
</Atom>\
|
||||
<Atom>\
|
||||
<Rel>t</Rel>\
|
||||
<Data iso:type=\"string\">\"attached\"</Data>\
|
||||
</Atom>\
|
||||
</And>\
|
||||
</Query>\
|
||||
<Query closure=\"existential\">\
|
||||
<And>\
|
||||
<Atom>\
|
||||
<Rel>u</Rel>\
|
||||
</Atom>\
|
||||
<Atom>\
|
||||
<Rel>v</Rel>\
|
||||
<Var>q</Var>\
|
||||
</Atom>\
|
||||
</And>\
|
||||
</Query>").
|
||||
AssertItems = [a(item),b(item),c(item)], QueryItems = [(?-p,q,r(1),s(-2.222342432),t("attached")),(?-u,v('$V'(q)))].
|
||||
```
|
||||
1183
src/examples/bimetatrans/bimetatrans.pl
Normal file
1183
src/examples/bimetatrans/bimetatrans.pl
Normal file
File diff suppressed because it is too large
Load Diff
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
|
||||
Goals = [Other=El] % implied binding
|
||||
; Goals = [],
|
||||
put_atts(Other, -dom(_)),
|
||||
put_atts(Other, dom(Dc))% rescue intersection
|
||||
)
|
||||
; 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)
|
||||
).
|
||||
|
||||
51
src/examples/least_time.pl
Normal file
51
src/examples/least_time.pl
Normal file
@@ -0,0 +1,51 @@
|
||||
/* least_time.pl
|
||||
*
|
||||
* By Mark Thom, 2020
|
||||
*
|
||||
* find_min_time/2 solves a problem sometimes posed in the first round
|
||||
* of Google interviews: given a time of day in 24 H format, what is the
|
||||
* lexicographically least permutation of the time that is itself a
|
||||
* valid time in 24 H format?
|
||||
*
|
||||
* Full generality is achieved using the reif library.
|
||||
*/
|
||||
|
||||
:- module(least_time, [find_min_time/2,
|
||||
write_time_nl/1]).
|
||||
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(reif)).
|
||||
|
||||
|
||||
valid_time([H1,H2,M1,M2], T) :-
|
||||
memberd_t(H1, [0,1,2], TH1),
|
||||
memberd_t(H2, [0,1,2,3,4,5,6,7,8,9], TH2),
|
||||
memberd_t(M1, [0,1,2,3,4,5], TM1),
|
||||
memberd_t(M2, [0,1,2,3,4,5,6,7,8,9], TM2),
|
||||
( maplist(=(true), [TH1, TH2, TM1, TM2]) ->
|
||||
( H1 =:= 2 ->
|
||||
( H2 =< 3 ->
|
||||
T = true
|
||||
; T = false
|
||||
)
|
||||
; T = true
|
||||
)
|
||||
; T = false
|
||||
).
|
||||
|
||||
|
||||
permuted_times(Time, PermutedTimes) :-
|
||||
setof(P, permutation(Time, P), PermutedTimes0),
|
||||
tfilter(valid_time, PermutedTimes0, PermutedTimes).
|
||||
|
||||
|
||||
find_min_time(Time, Min) :-
|
||||
valid_time(Time, true),
|
||||
permuted_times(Time, [Min|_]).
|
||||
|
||||
|
||||
write_time_nl(Time) :-
|
||||
format("\"~w~w:~w~w\"~n", Time).
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(dif)).
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
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).
|
||||
502
src/ffi.rs
Normal file
502
src/ffi.rs
Normal file
@@ -0,0 +1,502 @@
|
||||
/* How does FFI work?
|
||||
|
||||
Each WAM machine has a ForeignFunctionTable instance that contains a table of functions and structs.
|
||||
|
||||
Structs are defined via foreign_struct/2. Basic types are defined by libffi, but struct types need to
|
||||
be manually defined to get an ffi_type. Additionally, to recover structs from return arguments, we store
|
||||
fields and atom_fields, as a way to lookup the content of the struct (fields) and the nested structs (atom_fields).
|
||||
|
||||
Functions are defined via use_foreign_module/2. It opens a library and leaks the memory of the library,
|
||||
to prevent Rust freeing the memory. There's no way to recover that memory at the moment. We get a pointer for
|
||||
each function and we build a CIF for each one, with the input arguments and the return argument.
|
||||
|
||||
Exec happens via '$foreign_call', we find the function, we try to cast the values that we have to the definition
|
||||
of the function, we reserve memory for them and we build an array of pointers. To get the return argument, we
|
||||
reserve enough memory for the return and we build the Scryer values from them.
|
||||
|
||||
Structs are a bit tricky as they need to be aligned. For that, we reserve enough memory (libffi calculates that)
|
||||
and for each field: we add to the pointer until we're aligned to the next data type we're going to write, we write it,
|
||||
and finally we add the pointer the size of what we've written.
|
||||
*/
|
||||
|
||||
use crate::atom_table::Atom;
|
||||
|
||||
use std::alloc::{alloc, Layout};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error;
|
||||
use std::ffi::{c_void, CString};
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
use libffi::low::type_tag::STRUCT;
|
||||
use libffi::low::{ffi_abi_FFI_DEFAULT_ABI, ffi_cif, ffi_type, prep_cif, types, CodePtr};
|
||||
use libloading::{Library, Symbol};
|
||||
|
||||
pub struct FunctionDefinition {
|
||||
pub name: String,
|
||||
pub return_value: Atom,
|
||||
pub args: Vec<Atom>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionImpl {
|
||||
cif: ffi_cif,
|
||||
args: Vec<*mut ffi_type>,
|
||||
code_ptr: CodePtr,
|
||||
return_struct_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ForeignFunctionTable {
|
||||
table: HashMap<String, FunctionImpl>,
|
||||
structs: HashMap<String, StructImpl>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct StructImpl {
|
||||
ffi_type: ffi_type,
|
||||
fields: Vec<*mut ffi_type>,
|
||||
atom_fields: Vec<Atom>,
|
||||
}
|
||||
|
||||
struct PointerArgs {
|
||||
pointers: Vec<*mut c_void>,
|
||||
_memory: Vec<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
impl ForeignFunctionTable {
|
||||
pub fn merge(&mut self, other: ForeignFunctionTable) {
|
||||
self.table.extend(other.table);
|
||||
}
|
||||
|
||||
pub fn define_struct(&mut self, name: &str, atom_fields: Vec<Atom>) {
|
||||
let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(x)).collect();
|
||||
fields.push(std::ptr::null_mut::<ffi_type>());
|
||||
let struct_type = ffi_type {
|
||||
type_: STRUCT,
|
||||
elements: fields.as_mut_ptr(),
|
||||
..Default::default()
|
||||
};
|
||||
self.structs.insert(
|
||||
name.to_string(),
|
||||
StructImpl {
|
||||
ffi_type: struct_type,
|
||||
fields,
|
||||
atom_fields,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type {
|
||||
unsafe {
|
||||
match source {
|
||||
atom!("sint64") => addr_of_mut!(types::sint64),
|
||||
atom!("sint32") => addr_of_mut!(types::sint32),
|
||||
atom!("sint16") => addr_of_mut!(types::sint16),
|
||||
atom!("sint8") => addr_of_mut!(types::sint8),
|
||||
atom!("uint64") => addr_of_mut!(types::uint64),
|
||||
atom!("uint32") => addr_of_mut!(types::uint32),
|
||||
atom!("uint16") => addr_of_mut!(types::uint16),
|
||||
atom!("uint8") => addr_of_mut!(types::uint8),
|
||||
atom!("bool") => addr_of_mut!(types::sint8),
|
||||
atom!("void") => addr_of_mut!(types::void),
|
||||
atom!("cstr") => addr_of_mut!(types::pointer),
|
||||
atom!("ptr") => addr_of_mut!(types::pointer),
|
||||
atom!("f32") => addr_of_mut!(types::float),
|
||||
atom!("f64") => addr_of_mut!(types::double),
|
||||
struct_name => match self.structs.get_mut(&*struct_name.as_str()) {
|
||||
Some(ref mut struct_type) => &mut struct_type.ffi_type,
|
||||
None => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load_library(
|
||||
&mut self,
|
||||
library_name: &str,
|
||||
functions: &Vec<FunctionDefinition>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut ff_table: ForeignFunctionTable = Default::default();
|
||||
unsafe {
|
||||
let library = Library::new(library_name)?;
|
||||
for function in functions {
|
||||
let symbol_name: CString = CString::new(function.name.clone())?;
|
||||
let code_ptr: Symbol<*mut c_void> =
|
||||
library.get(&symbol_name.into_bytes_with_nul())?;
|
||||
let mut args: Vec<_> = function.args.iter().map(|x| self.map_type_ffi(x)).collect();
|
||||
let mut cif: ffi_cif = Default::default();
|
||||
prep_cif(
|
||||
&mut cif,
|
||||
ffi_abi_FFI_DEFAULT_ABI,
|
||||
args.len(),
|
||||
self.map_type_ffi(&function.return_value),
|
||||
args.as_mut_ptr(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let return_struct_name = if (*self.map_type_ffi(&function.return_value)).type_
|
||||
as u32
|
||||
== libffi::raw::FFI_TYPE_STRUCT
|
||||
{
|
||||
Some(function.return_value.as_str().to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
ff_table.table.insert(
|
||||
function.name.clone(),
|
||||
FunctionImpl {
|
||||
cif,
|
||||
args,
|
||||
code_ptr: CodePtr(code_ptr.into_raw().into_raw() as *mut _),
|
||||
return_struct_name,
|
||||
},
|
||||
);
|
||||
}
|
||||
std::mem::forget(library);
|
||||
}
|
||||
self.merge(ff_table);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_pointer_args(
|
||||
args: &mut [Value],
|
||||
type_args: &[*mut ffi_type],
|
||||
structs_table: &mut HashMap<String, StructImpl>,
|
||||
) -> Result<PointerArgs, FFIError> {
|
||||
let mut pointers = Vec::with_capacity(args.len());
|
||||
let mut _memory = Vec::new();
|
||||
for i in 0..args.len() {
|
||||
let field_type = type_args[i];
|
||||
unsafe {
|
||||
macro_rules! push_int {
|
||||
($type:ty) => {{
|
||||
let n: $type = <$type>::try_from(args[i].as_int()?)
|
||||
.map_err(|_| FFIError::ValueDontFit)?;
|
||||
let mut box_value = Box::new(n) as Box<dyn Any>;
|
||||
pointers.push(&mut *box_value as *mut _ as *mut c_void);
|
||||
_memory.push(box_value);
|
||||
}};
|
||||
}
|
||||
|
||||
match (*field_type).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => push_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => push_int!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => push_int!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => push_int!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => push_int!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => push_int!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => push_int!(u64),
|
||||
libffi::raw::FFI_TYPE_SINT64 => push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_FLOAT => {
|
||||
let n: f32 = args[i].as_float()? as f32;
|
||||
let mut box_value = Box::new(n) as Box<dyn Any>;
|
||||
pointers.push(&mut *box_value as *mut _ as *mut c_void);
|
||||
_memory.push(box_value);
|
||||
}
|
||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||
let n: f64 = args[i].as_float()?;
|
||||
let mut box_value = Box::new(n) as Box<dyn Any>;
|
||||
pointers.push(&mut *box_value as *mut _ as *mut c_void);
|
||||
_memory.push(box_value);
|
||||
}
|
||||
libffi::raw::FFI_TYPE_POINTER => {
|
||||
let ptr: *mut c_void = args[i].as_ptr()?;
|
||||
pointers.push(ptr);
|
||||
}
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let (mut ptr, _size, _align) =
|
||||
Self::build_struct(&mut args[i], structs_table)?;
|
||||
pointers.push(&mut *ptr as *mut _ as *mut c_void);
|
||||
_memory.push(ptr);
|
||||
}
|
||||
_ => return Err(FFIError::InvalidFFIType),
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(PointerArgs { pointers, _memory })
|
||||
}
|
||||
|
||||
fn build_struct(
|
||||
arg: &mut Value,
|
||||
structs_table: &mut HashMap<String, StructImpl>,
|
||||
) -> Result<(Box<dyn Any>, usize, usize), FFIError> {
|
||||
unsafe {
|
||||
match arg {
|
||||
Value::Struct(ref name, ref mut struct_args) => {
|
||||
if let Some(ref mut struct_type) = structs_table.clone().get_mut(name) {
|
||||
let layout = Layout::from_size_align(
|
||||
struct_type.ffi_type.size,
|
||||
struct_type.ffi_type.alignment.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let align = struct_type.ffi_type.alignment as usize;
|
||||
let size = struct_type.ffi_type.size;
|
||||
let ptr = alloc(layout) as *mut c_void;
|
||||
let mut field_ptr = ptr;
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..(struct_type.fields.len() - 1) {
|
||||
macro_rules! try_write_int {
|
||||
($type:ty) => {{
|
||||
field_ptr = field_ptr
|
||||
.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
|
||||
let n: $type = <$type>::try_from(struct_args[i].as_int()?)
|
||||
.map_err(|_| FFIError::ValueDontFit)?;
|
||||
std::ptr::write(field_ptr as *mut $type, n);
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! write {
|
||||
($type:ty, $value:expr) => {{
|
||||
let data: $type = $value;
|
||||
std::ptr::write(field_ptr as *mut $type, data);
|
||||
field_ptr = field_ptr.add(align);
|
||||
}};
|
||||
}
|
||||
|
||||
let field = struct_type.fields[i];
|
||||
match (*field).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => try_write_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => try_write_int!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => try_write_int!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => try_write_int!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => try_write_int!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => try_write_int!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => try_write_int!(u64),
|
||||
libffi::raw::FFI_TYPE_SINT64 => try_write_int!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => {
|
||||
write!(*mut c_void, struct_args[i].as_ptr()?)
|
||||
}
|
||||
libffi::raw::FFI_TYPE_FLOAT => {
|
||||
write!(f32, struct_args[i].as_float()? as f32)
|
||||
}
|
||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||
write!(f64, struct_args[i].as_float()?)
|
||||
}
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let (struct_ptr, struct_size, struct_align) =
|
||||
Self::build_struct(&mut struct_args[i], structs_table)?;
|
||||
field_ptr = field_ptr.add(field_ptr.align_offset(struct_align));
|
||||
|
||||
std::ptr::copy(
|
||||
&*struct_ptr as *const _ as *const c_void,
|
||||
field_ptr,
|
||||
struct_size,
|
||||
);
|
||||
field_ptr = field_ptr.add(struct_size);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[allow(clippy::from_raw_with_void_ptr)]
|
||||
Ok((Box::from_raw(ptr), size, align))
|
||||
} else {
|
||||
Err(FFIError::InvalidStructName)
|
||||
}
|
||||
}
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec(&mut self, name: &str, mut args: Vec<Value>) -> Result<Value, FFIError> {
|
||||
let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?;
|
||||
let mut pointer_args =
|
||||
Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs)?;
|
||||
|
||||
return unsafe {
|
||||
macro_rules! call_and_return {
|
||||
($type:ty) => {{
|
||||
let mut n: Box<u8> = Box::new(0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void,
|
||||
);
|
||||
Ok(Value::Int(i64::from(*n)))
|
||||
}};
|
||||
}
|
||||
|
||||
match (*function_impl.cif.rtype).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_VOID => call_and_return!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT8 => call_and_return!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => call_and_return!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => call_and_return!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => call_and_return!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => call_and_return!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => call_and_return!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => {
|
||||
let mut n: Box<u64> = Box::new(0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
Ok(Value::Int(
|
||||
i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?,
|
||||
))
|
||||
}
|
||||
libffi::raw::FFI_TYPE_SINT64 => call_and_return!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => call_and_return!(*mut c_void),
|
||||
libffi::raw::FFI_TYPE_FLOAT => {
|
||||
let mut n: Box<f32> = Box::new(0.0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
Ok(Value::Float((*n).into()))
|
||||
}
|
||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||
let mut n: Box<f64> = Box::new(0.0);
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *n as *mut _ as *mut c_void,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
Ok(Value::Float(*n))
|
||||
}
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let name = &function_impl
|
||||
.return_struct_name
|
||||
.clone()
|
||||
.ok_or(FFIError::StructNotFound)?;
|
||||
let struct_type = self.structs.get(name).ok_or(FFIError::StructNotFound)?;
|
||||
let layout = Layout::from_size_align(
|
||||
struct_type.ffi_type.size,
|
||||
struct_type.ffi_type.alignment.into(),
|
||||
)
|
||||
.unwrap();
|
||||
let ptr = alloc(layout) as *mut c_void;
|
||||
|
||||
libffi::raw::ffi_call(
|
||||
&mut function_impl.cif,
|
||||
Some(*function_impl.code_ptr.as_safe_fun()),
|
||||
&mut *ptr as *mut _,
|
||||
pointer_args.pointers.as_mut_ptr(),
|
||||
);
|
||||
let struct_val = self.read_struct(ptr, name, struct_type);
|
||||
#[allow(clippy::from_raw_with_void_ptr)]
|
||||
drop(Box::from_raw(ptr));
|
||||
struct_val
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn read_struct(
|
||||
&self,
|
||||
ptr: *mut c_void,
|
||||
name: &str,
|
||||
struct_type: &StructImpl,
|
||||
) -> Result<Value, FFIError> {
|
||||
unsafe {
|
||||
let mut returns = Vec::new();
|
||||
let mut field_ptr = ptr;
|
||||
|
||||
for i in 0..(struct_type.fields.len() - 1) {
|
||||
let field = struct_type.fields[i];
|
||||
|
||||
macro_rules! read_and_push_int {
|
||||
($type:ty) => {{
|
||||
field_ptr =
|
||||
field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
|
||||
let n = std::ptr::read(field_ptr as *mut $type);
|
||||
returns.push(Value::Int(i64::from(n)));
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
|
||||
}};
|
||||
}
|
||||
|
||||
match (*field).type_ as u32 {
|
||||
libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8),
|
||||
libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8),
|
||||
libffi::raw::FFI_TYPE_UINT16 => read_and_push_int!(u16),
|
||||
libffi::raw::FFI_TYPE_SINT16 => read_and_push_int!(i16),
|
||||
libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32),
|
||||
libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32),
|
||||
libffi::raw::FFI_TYPE_UINT64 => {
|
||||
field_ptr =
|
||||
field_ptr.add(field_ptr.align_offset(std::mem::align_of::<u64>()));
|
||||
let n = std::ptr::read(field_ptr as *mut u64);
|
||||
returns.push(Value::Int(
|
||||
i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?,
|
||||
));
|
||||
field_ptr = field_ptr.add(std::mem::size_of::<u64>());
|
||||
}
|
||||
libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64),
|
||||
libffi::raw::FFI_TYPE_STRUCT => {
|
||||
let substruct = struct_type.atom_fields[i].as_str();
|
||||
let struct_type = self
|
||||
.structs
|
||||
.get(&*substruct)
|
||||
.ok_or(FFIError::StructNotFound)?;
|
||||
field_ptr = field_ptr
|
||||
.add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize));
|
||||
let struct_val = self.read_struct(field_ptr, &substruct, struct_type);
|
||||
returns.push(struct_val?);
|
||||
field_ptr = field_ptr.add(struct_type.ffi_type.size);
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::Struct(name.into(), returns))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Value {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
CString(CString),
|
||||
Struct(String, Vec<Value>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
fn as_int(&self) -> Result<i64, FFIError> {
|
||||
match self {
|
||||
Value::Int(n) => Ok(*n),
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_float(&self) -> Result<f64, FFIError> {
|
||||
match self {
|
||||
Value::Float(n) => Ok(*n),
|
||||
Value::Int(n) => Ok(*n as f64),
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_ptr(&mut self) -> Result<*mut c_void, FFIError> {
|
||||
match self {
|
||||
Value::CString(ref mut cstr) => Ok(&mut *cstr as *mut _ as *mut c_void),
|
||||
Value::Int(n) => Ok(*n as *mut c_void),
|
||||
_ => Err(FFIError::ValueCast),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FFIError {
|
||||
ValueCast,
|
||||
ValueDontFit,
|
||||
InvalidFFIType,
|
||||
InvalidStructName,
|
||||
FunctionNotFound,
|
||||
StructNotFound,
|
||||
}
|
||||
1006
src/forms.rs
Normal file
1006
src/forms.rs
Normal file
File diff suppressed because it is too large
Load Diff
3254
src/heap_iter.rs
Normal file
3254
src/heap_iter.rs
Normal file
File diff suppressed because it is too large
Load Diff
2151
src/heap_print.rs
Normal file
2151
src/heap_print.rs
Normal file
File diff suppressed because it is too large
Load Diff
23
src/http.rs
Normal file
23
src/http.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use std::io::BufRead;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use warp::http;
|
||||
|
||||
pub struct HttpListener {
|
||||
pub incoming: std::sync::mpsc::Receiver<HttpRequest>,
|
||||
}
|
||||
|
||||
pub struct HttpRequest {
|
||||
pub request_data: HttpRequestData,
|
||||
pub response: HttpResponse,
|
||||
}
|
||||
|
||||
pub type HttpResponse = Arc<(Mutex<bool>, Mutex<Option<warp::reply::Response>>, Condvar)>;
|
||||
|
||||
pub struct HttpRequestData {
|
||||
pub method: http::Method,
|
||||
pub headers: http::HeaderMap,
|
||||
pub path: String,
|
||||
pub query: String,
|
||||
pub body: Box<dyn BufRead + Send>,
|
||||
}
|
||||
1589
src/indexing.rs
Normal file
1589
src/indexing.rs
Normal file
File diff suppressed because it is too large
Load Diff
451
src/iterators.rs
Normal file
451
src/iterators.rs
Normal file
@@ -0,0 +1,451 @@
|
||||
use crate::atom_table::*;
|
||||
use crate::forms::*;
|
||||
use crate::instructions::*;
|
||||
use crate::parser::ast::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::iter::*;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum TermRef<'a> {
|
||||
AnonVar(Level),
|
||||
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||
Literal(Level, &'a Cell<RegType>, &'a Literal),
|
||||
Clause(Level, &'a Cell<RegType>, Atom, &'a Vec<Term>),
|
||||
PartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
|
||||
CompleteString(Level, &'a Cell<RegType>, Atom),
|
||||
Var(Level, &'a Cell<VarReg>, VarPtr),
|
||||
}
|
||||
|
||||
/*
|
||||
impl<'a> TermRef<'a> {
|
||||
pub(crate) fn level(&self) -> Level {
|
||||
match self {
|
||||
TermRef::AnonVar(lvl) |
|
||||
TermRef::Cons(lvl, ..) |
|
||||
TermRef::Literal(lvl, ..) |
|
||||
TermRef::Var(lvl, ..) |
|
||||
TermRef::Clause(lvl, ..) |
|
||||
TermRef::CompleteString(lvl, ..) |
|
||||
TermRef::PartialString(lvl, ..) => *lvl,
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[allow(clippy::borrowed_box)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum TermIterState<'a> {
|
||||
AnonVar(Level),
|
||||
Clause(Level, usize, &'a Cell<RegType>, Atom, &'a Vec<Term>),
|
||||
Literal(Level, &'a Cell<RegType>, &'a Literal),
|
||||
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||
FinalCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
|
||||
InitialPartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
|
||||
FinalPartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
|
||||
CompleteString(Level, &'a Cell<RegType>, Atom),
|
||||
Var(Level, &'a Cell<VarReg>, VarPtr),
|
||||
}
|
||||
|
||||
impl<'a> TermIterState<'a> {
|
||||
pub(crate) fn subterm_to_state(lvl: Level, term: &'a Term) -> TermIterState<'a> {
|
||||
match term {
|
||||
Term::AnonVar => TermIterState::AnonVar(lvl),
|
||||
Term::Clause(cell, name, subterms) => {
|
||||
TermIterState::Clause(lvl, 0, cell, *name, subterms)
|
||||
}
|
||||
Term::Cons(cell, head, tail) => {
|
||||
TermIterState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref())
|
||||
}
|
||||
Term::Literal(cell, constant) => TermIterState::Literal(lvl, cell, constant),
|
||||
Term::PartialString(cell, string_buf, tail) => {
|
||||
TermIterState::InitialPartialString(lvl, cell, string_buf, tail)
|
||||
}
|
||||
Term::CompleteString(cell, atom) => TermIterState::CompleteString(lvl, cell, *atom),
|
||||
Term::Var(cell, var_ptr) => TermIterState::Var(lvl, cell, var_ptr.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct QueryIterator<'a> {
|
||||
state_stack: Vec<TermIterState<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> QueryIterator<'a> {
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_stack
|
||||
.push(TermIterState::subterm_to_state(lvl, term));
|
||||
}
|
||||
|
||||
/*
|
||||
fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
|
||||
let state_stack = terms
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||
.collect();
|
||||
|
||||
QueryIterator { state_stack }
|
||||
}
|
||||
*/
|
||||
|
||||
fn from_term(term: &'a Term) -> Self {
|
||||
let state = match term {
|
||||
Term::AnonVar
|
||||
| Term::Cons(..)
|
||||
| Term::Literal(..)
|
||||
| Term::PartialString(..)
|
||||
| Term::CompleteString(..) => {
|
||||
return QueryIterator {
|
||||
state_stack: vec![],
|
||||
}
|
||||
}
|
||||
Term::Clause(r, name, terms) => TermIterState::Clause(Level::Root, 0, r, *name, terms),
|
||||
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Root, cell, var_ptr.clone()),
|
||||
};
|
||||
|
||||
QueryIterator {
|
||||
state_stack: vec![state],
|
||||
}
|
||||
}
|
||||
|
||||
fn extend_state(&mut self, lvl: Level, term: &'a QueryTerm) {
|
||||
match term {
|
||||
QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms));
|
||||
}
|
||||
QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(term: &'a QueryTerm) -> Self {
|
||||
let mut iter = QueryIterator {
|
||||
state_stack: vec![],
|
||||
};
|
||||
iter.extend_state(Level::Root, term);
|
||||
iter
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for QueryIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(iter_state) = self.state_stack.pop() {
|
||||
match iter_state {
|
||||
TermIterState::AnonVar(lvl) => {
|
||||
return Some(TermRef::AnonVar(lvl));
|
||||
}
|
||||
TermIterState::Clause(lvl, child_num, cell, name, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
match name {
|
||||
atom!("$call") if lvl == Level::Root => {
|
||||
self.push_subterm(Level::Shallow, &child_terms[0]);
|
||||
}
|
||||
_ => {
|
||||
return match lvl {
|
||||
Level::Root => None,
|
||||
lvl => Some(TermRef::Clause(lvl, cell, name, child_terms)),
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
self.state_stack.push(TermIterState::Clause(
|
||||
lvl,
|
||||
child_num + 1,
|
||||
cell,
|
||||
name,
|
||||
child_terms,
|
||||
));
|
||||
|
||||
self.push_subterm(lvl.child_level(), &child_terms[child_num]);
|
||||
}
|
||||
}
|
||||
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::FinalCons(lvl, cell, head, tail));
|
||||
|
||||
self.push_subterm(lvl.child_level(), tail);
|
||||
self.push_subterm(lvl.child_level(), head);
|
||||
}
|
||||
TermIterState::InitialPartialString(lvl, cell, string, tail) => {
|
||||
self.state_stack
|
||||
.push(TermIterState::FinalPartialString(lvl, cell, string, tail));
|
||||
self.push_subterm(lvl.child_level(), tail);
|
||||
}
|
||||
TermIterState::FinalPartialString(lvl, cell, 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) => {
|
||||
return Some(TermRef::Cons(lvl, cell, head, tail));
|
||||
}
|
||||
TermIterState::Literal(lvl, cell, constant) => {
|
||||
return Some(TermRef::Literal(lvl, cell, constant));
|
||||
}
|
||||
TermIterState::Var(lvl, cell, var_ptr) => {
|
||||
return Some(TermRef::Var(lvl, cell, var_ptr));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct FactIterator<'a> {
|
||||
state_queue: VecDeque<TermIterState<'a>>,
|
||||
iterable_root: RootIterationPolicy,
|
||||
}
|
||||
|
||||
impl<'a> FactIterator<'a> {
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_queue
|
||||
.push_back(TermIterState::subterm_to_state(lvl, term));
|
||||
}
|
||||
|
||||
pub(crate) fn from_rule_head_clause(terms: &'a [Term]) -> Self {
|
||||
let state_queue = terms
|
||||
.iter()
|
||||
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
|
||||
.collect();
|
||||
|
||||
FactIterator {
|
||||
state_queue,
|
||||
iterable_root: RootIterationPolicy::NotIterated,
|
||||
}
|
||||
}
|
||||
|
||||
fn new(term: &'a Term, iterable_root: RootIterationPolicy) -> Self {
|
||||
let states = match term {
|
||||
Term::AnonVar => {
|
||||
vec![TermIterState::AnonVar(Level::Root)]
|
||||
}
|
||||
Term::Clause(cell, name, terms) => {
|
||||
vec![TermIterState::Clause(Level::Root, 0, cell, *name, terms)]
|
||||
}
|
||||
Term::Cons(cell, head, tail) => vec![TermIterState::InitialCons(
|
||||
Level::Root,
|
||||
cell,
|
||||
head.as_ref(),
|
||||
tail.as_ref(),
|
||||
)],
|
||||
Term::PartialString(cell, string_buf, tail) => {
|
||||
vec![TermIterState::InitialPartialString(
|
||||
Level::Root,
|
||||
cell,
|
||||
string_buf,
|
||||
tail,
|
||||
)]
|
||||
}
|
||||
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_ptr) => {
|
||||
vec![TermIterState::Var(Level::Root, cell, var_ptr.clone())]
|
||||
}
|
||||
};
|
||||
|
||||
FactIterator {
|
||||
state_queue: VecDeque::from(states),
|
||||
iterable_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FactIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(state) = self.state_queue.pop_front() {
|
||||
match state {
|
||||
TermIterState::AnonVar(lvl) => {
|
||||
return Some(TermRef::AnonVar(lvl));
|
||||
}
|
||||
TermIterState::Clause(lvl, _, cell, name, child_terms) => {
|
||||
for child_term in child_terms {
|
||||
self.push_subterm(lvl.child_level(), child_term);
|
||||
}
|
||||
|
||||
match lvl {
|
||||
Level::Root if !self.iterable_root.iterable() => continue,
|
||||
_ => return Some(TermRef::Clause(lvl, cell, name, child_terms)),
|
||||
};
|
||||
}
|
||||
TermIterState::InitialCons(lvl, cell, head, tail) => {
|
||||
self.push_subterm(Level::Deep, head);
|
||||
self.push_subterm(Level::Deep, tail);
|
||||
|
||||
return Some(TermRef::Cons(lvl, cell, head, tail));
|
||||
}
|
||||
TermIterState::InitialPartialString(lvl, cell, string_buf, tail) => {
|
||||
self.push_subterm(Level::Deep, tail);
|
||||
return Some(TermRef::PartialString(lvl, cell, string_buf, tail));
|
||||
}
|
||||
TermIterState::CompleteString(lvl, cell, atom) => {
|
||||
return Some(TermRef::CompleteString(lvl, cell, atom));
|
||||
}
|
||||
TermIterState::Literal(lvl, cell, constant) => {
|
||||
return Some(TermRef::Literal(lvl, cell, constant))
|
||||
}
|
||||
TermIterState::Var(lvl, cell, var_ptr) => {
|
||||
return Some(TermRef::Var(lvl, cell, var_ptr));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn post_order_iter(term: &'_ Term) -> QueryIterator {
|
||||
QueryIterator::from_term(term)
|
||||
}
|
||||
|
||||
pub(crate) fn breadth_first_iter(
|
||||
term: &'_ Term,
|
||||
iterable_root: RootIterationPolicy,
|
||||
) -> FactIterator {
|
||||
FactIterator::new(term, iterable_root)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum ClauseIteratorState<'a> {
|
||||
RemainingChunks(&'a VecDeque<ChunkedTerms>, usize),
|
||||
RemainingBranches(&'a Vec<VecDeque<ChunkedTerms>>, usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ClauseItem<'a> {
|
||||
FirstBranch(usize),
|
||||
NextBranch,
|
||||
BranchEnd(usize),
|
||||
Chunk(&'a VecDeque<QueryTerm>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ClauseIterator<'a> {
|
||||
state_stack: Vec<ClauseIteratorState<'a>>,
|
||||
remaining_chunks_on_stack: usize,
|
||||
}
|
||||
|
||||
fn state_from_chunked_terms(chunk_vec: &'_ VecDeque<ChunkedTerms>) -> ClauseIteratorState {
|
||||
if chunk_vec.len() == 1 {
|
||||
if let Some(ChunkedTerms::Branch(ref branches)) = chunk_vec.front() {
|
||||
return ClauseIteratorState::RemainingBranches(branches, 0);
|
||||
}
|
||||
}
|
||||
|
||||
ClauseIteratorState::RemainingChunks(chunk_vec, 0)
|
||||
}
|
||||
|
||||
impl<'a> ClauseIterator<'a> {
|
||||
pub fn new(clauses: &'a ChunkedTermVec) -> Self {
|
||||
match state_from_chunked_terms(&clauses.chunk_vec) {
|
||||
state @ ClauseIteratorState::RemainingBranches(..) => Self {
|
||||
state_stack: vec![state],
|
||||
remaining_chunks_on_stack: 0,
|
||||
},
|
||||
state @ ClauseIteratorState::RemainingChunks(..) => Self {
|
||||
state_stack: vec![state],
|
||||
remaining_chunks_on_stack: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn in_tail_position(&self) -> bool {
|
||||
self.remaining_chunks_on_stack == 0
|
||||
}
|
||||
|
||||
fn branch_end_depth(&mut self) -> usize {
|
||||
let mut depth = 1;
|
||||
|
||||
while let Some(state) = self.state_stack.pop() {
|
||||
match state {
|
||||
ClauseIteratorState::RemainingBranches(terms, focus) if terms.len() == focus => {
|
||||
depth += 1;
|
||||
}
|
||||
_ => {
|
||||
self.state_stack.push(state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
depth
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ClauseIterator<'a> {
|
||||
type Item = ClauseItem<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(state) = self.state_stack.pop() {
|
||||
match state {
|
||||
ClauseIteratorState::RemainingChunks(chunks, focus) if focus < chunks.len() => {
|
||||
if focus + 1 < chunks.len() {
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingChunks(chunks, focus + 1));
|
||||
} else {
|
||||
self.remaining_chunks_on_stack -= 1;
|
||||
}
|
||||
|
||||
match &chunks[focus] {
|
||||
ChunkedTerms::Branch(branches) => {
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingBranches(branches, 0));
|
||||
}
|
||||
ChunkedTerms::Chunk(chunk) => {
|
||||
return Some(ClauseItem::Chunk(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
ClauseIteratorState::RemainingChunks(chunks, focus) => {
|
||||
debug_assert_eq!(chunks.len(), focus);
|
||||
}
|
||||
ClauseIteratorState::RemainingBranches(branches, focus)
|
||||
if focus < branches.len() =>
|
||||
{
|
||||
self.state_stack
|
||||
.push(ClauseIteratorState::RemainingBranches(branches, focus + 1));
|
||||
let state = state_from_chunked_terms(&branches[focus]);
|
||||
|
||||
if let ClauseIteratorState::RemainingChunks(..) = &state {
|
||||
self.remaining_chunks_on_stack += 1;
|
||||
}
|
||||
|
||||
self.state_stack.push(state);
|
||||
|
||||
return if focus == 0 {
|
||||
Some(ClauseItem::FirstBranch(branches.len()))
|
||||
} else {
|
||||
Some(ClauseItem::NextBranch)
|
||||
};
|
||||
}
|
||||
ClauseIteratorState::RemainingBranches(branches, focus) => {
|
||||
debug_assert_eq!(branches.len(), focus);
|
||||
return Some(ClauseItem::BranchEnd(self.branch_end_depth()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
use std::cell::{Cell};
|
||||
use std::vec::{Vec};
|
||||
|
||||
pub type Var = String;
|
||||
|
||||
pub type Atom = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TopLevel {
|
||||
Fact(Term),
|
||||
Query(Term)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Term {
|
||||
Atom(Cell<usize>, Atom),
|
||||
Clause(Cell<usize>, Atom, Vec<Box<Term>>),
|
||||
Var(Cell<usize>, Var)
|
||||
}
|
||||
|
||||
pub enum FactInstruction {
|
||||
GetStructure(Atom, usize, usize),
|
||||
UnifyVariable(usize),
|
||||
UnifyValue(usize)
|
||||
}
|
||||
|
||||
pub enum QueryInstruction {
|
||||
PutStructure(Atom, usize, usize),
|
||||
SetVariable(usize),
|
||||
SetValue(usize),
|
||||
}
|
||||
|
||||
pub type CompiledFact = Vec<FactInstruction>;
|
||||
|
||||
pub type CompiledQuery = Vec<QueryInstruction>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Addr {
|
||||
HeapCell(usize),
|
||||
RegNum(usize)
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn set_cell(&self, cell_num: usize) {
|
||||
match self {
|
||||
&Term::Atom(ref cell, _) => cell.set(cell_num),
|
||||
&Term::Clause(ref cell, _, _) => cell.set(cell_num),
|
||||
&Term::Var(ref cell, _) => cell.set(cell_num)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
use l0::ast::{Atom, Term, FactInstruction, QueryInstruction, Var};
|
||||
use l0::iterators::{BreadthFirstIterator, PostOrderIterator};
|
||||
|
||||
use std::collections::{HashSet};
|
||||
use std::fmt;
|
||||
use std::vec::{Vec};
|
||||
|
||||
impl fmt::Display for QueryInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&QueryInstruction::PutStructure(ref a, ref s, ref r) =>
|
||||
write!(f, "put_structure {}/{}, X{}", a, s, r),
|
||||
&QueryInstruction::SetVariable(ref r) =>
|
||||
write!(f, "set_variable X{}", r),
|
||||
&QueryInstruction::SetValue(ref r) =>
|
||||
write!(f, "set_value X{}", r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FactInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&FactInstruction::GetStructure(ref a, ref s, ref r) =>
|
||||
write!(f, "get_structure {}/{}, X{}", a, s, r),
|
||||
&FactInstruction::UnifyVariable(ref r) =>
|
||||
write!(f, "unify_variable X{}", r),
|
||||
&FactInstruction::UnifyValue(ref r) =>
|
||||
write!(f, "unify_value X{}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CompilationTarget<'a> {
|
||||
type Iterator : Iterator<Item=&'a Term>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator;
|
||||
|
||||
fn to_structure(name: Atom, arity: usize, cell_num: usize) -> Self;
|
||||
fn to_value(cell_num: usize) -> Self;
|
||||
fn to_variable(cell_num: usize) -> Self;
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for FactInstruction {
|
||||
type Iterator = BreadthFirstIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.breadth_first_iter()
|
||||
}
|
||||
|
||||
fn to_structure(name: Atom, arity: usize, cell_num: usize) -> Self {
|
||||
FactInstruction::GetStructure(name, arity, cell_num)
|
||||
}
|
||||
|
||||
fn to_value(cell_num: usize) -> Self {
|
||||
FactInstruction::UnifyValue(cell_num)
|
||||
}
|
||||
|
||||
fn to_variable(cell_num: usize) -> Self {
|
||||
FactInstruction::UnifyVariable(cell_num)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for QueryInstruction {
|
||||
type Iterator = PostOrderIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.post_order_iter()
|
||||
}
|
||||
|
||||
fn to_structure(name: Atom, arity: usize, cell_num: usize) -> Self {
|
||||
QueryInstruction::PutStructure(name, arity, cell_num)
|
||||
}
|
||||
|
||||
fn to_value(cell_num: usize) -> Self {
|
||||
QueryInstruction::SetValue(cell_num)
|
||||
}
|
||||
|
||||
fn to_variable(cell_num: usize) -> Self {
|
||||
QueryInstruction::SetVariable(cell_num)
|
||||
}
|
||||
}
|
||||
|
||||
fn subterm_to_instr<'a, Target>(subterm: &'a Term,
|
||||
bindings: &mut HashSet<&'a Var>)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
match subterm {
|
||||
&Term::Atom(ref cell_num, _) =>
|
||||
Target::to_value(cell_num.get()),
|
||||
&Term::Var(ref cell_num, ref atom) if bindings.contains(atom) =>
|
||||
Target::to_value(cell_num.get()),
|
||||
&Term::Var(ref cell_num, ref atom) => {
|
||||
bindings.insert(atom);
|
||||
Target::to_variable(cell_num.get())
|
||||
},
|
||||
&Term::Clause(ref cell_num, _, _) =>
|
||||
Target::to_value(cell_num.get())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_target<'a, Target>(term: &'a Term) -> Vec<Target>
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
let mut iter = Target::iter(term);
|
||||
let mut target = Vec::<Target>::new();
|
||||
let mut bindings = HashSet::new();
|
||||
|
||||
while let Some(term) = iter.next() {
|
||||
match term {
|
||||
&Term::Atom(ref cell_num, ref atom) =>
|
||||
target.push(Target::to_structure(atom.clone(), 0, cell_num.get())),
|
||||
&Term::Clause(ref cell_num, ref atom, ref terms) => {
|
||||
target.push(Target::to_structure(atom.clone(), 0, cell_num.get()));
|
||||
|
||||
for subterm in terms {
|
||||
target.push(subterm_to_instr(subterm.as_ref(), &mut bindings));
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
||||
target
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
use l0::ast::{Term};
|
||||
|
||||
use std::collections::{VecDeque};
|
||||
use std::vec::{Vec};
|
||||
|
||||
enum DepthFirstIteratorState<'a> {
|
||||
// child no., the containing clause, its vector.
|
||||
Clause(usize, &'a Term, &'a Vec<Box<Term>>),
|
||||
NonClause(&'a Term)
|
||||
}
|
||||
|
||||
pub struct PostOrderIterator<'a> {
|
||||
state_stack: Vec<DepthFirstIteratorState<'a>>
|
||||
}
|
||||
|
||||
impl<'a> PostOrderIterator<'a> {
|
||||
fn push_clause(&mut self,
|
||||
child_num: usize,
|
||||
term: &'a Term,
|
||||
child_terms: &'a Vec<Box<Term>>)
|
||||
{
|
||||
self.state_stack.push(DepthFirstIteratorState::Clause(child_num,
|
||||
term,
|
||||
child_terms));
|
||||
}
|
||||
|
||||
fn render_new_state(term: &'a Term) -> DepthFirstIteratorState<'a> {
|
||||
match term {
|
||||
&Term::Clause(_, _, ref child_terms) =>
|
||||
DepthFirstIteratorState::Clause(0, term, child_terms),
|
||||
_ => DepthFirstIteratorState::NonClause(term)
|
||||
}
|
||||
}
|
||||
|
||||
fn push_term(&mut self, term: &'a Term) {
|
||||
self.state_stack.push(Self::render_new_state(term));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PostOrderIterator<'a> {
|
||||
type Item = &'a Term;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(iter_state) = self.state_stack.pop() {
|
||||
match iter_state {
|
||||
DepthFirstIteratorState::Clause(child_num, term, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
return Some(term);
|
||||
} else {
|
||||
self.push_clause(child_num + 1, term, child_terms);
|
||||
self.push_term(child_terms[child_num].as_ref());
|
||||
}
|
||||
},
|
||||
DepthFirstIteratorState::NonClause(term) => return Some(term),
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BreadthFirstIterator<'a> {
|
||||
state_queue : VecDeque<&'a Term>
|
||||
}
|
||||
|
||||
impl<'a> Iterator for BreadthFirstIterator<'a> {
|
||||
type Item = &'a Term;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(term) = self.state_queue.pop_front() {
|
||||
if let &Term::Clause(_, _, ref child_terms) = term {
|
||||
for term in child_terms {
|
||||
self.state_queue.push_back(term);
|
||||
}
|
||||
|
||||
return Some(term);
|
||||
}
|
||||
|
||||
return Some(term);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Term {
|
||||
pub fn post_order_iter(&'a self) -> PostOrderIterator<'a> {
|
||||
let initial_state = PostOrderIterator::render_new_state(self);
|
||||
PostOrderIterator { state_stack: vec![initial_state] }
|
||||
}
|
||||
|
||||
pub fn breadth_first_iter(&'a self) -> BreadthFirstIterator<'a> {
|
||||
let mut queue = VecDeque::new();
|
||||
queue.push_back(self);
|
||||
|
||||
BreadthFirstIterator { state_queue: queue }
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
use std::cell::{Cell};
|
||||
use l0::ast::{Atom, Term, TopLevel, Var};
|
||||
|
||||
grammar;
|
||||
|
||||
pub TopLevel: TopLevel = {
|
||||
"?-" <t:Term> "." => TopLevel::Query(t),
|
||||
<t:Term> "." => TopLevel::Fact(t),
|
||||
};
|
||||
|
||||
Atom : Atom = {
|
||||
r"[a-z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
|
||||
Var : Var = {
|
||||
r"[A-Z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
|
||||
BoxedTerm : Box<Term> = {
|
||||
<t:Term> => Box::new(t),
|
||||
};
|
||||
|
||||
Term : Term = {
|
||||
<a:Atom> "(" <ts: (<BoxedTerm> ",")*> <t:BoxedTerm> ")" => {
|
||||
let mut ts = ts;
|
||||
ts.push(t);
|
||||
Term::Clause(Cell::new(0), a, ts)
|
||||
},
|
||||
<Atom> => Term::Atom(Cell::new(0), <>),
|
||||
<Var> => Term::Var(Cell::new(0), <>),
|
||||
};
|
||||
1595
src/l0/l0_parser.rs
1595
src/l0/l0_parser.rs
File diff suppressed because it is too large
Load Diff
@@ -1,238 +0,0 @@
|
||||
use l0::ast::{Addr, Atom, CompiledFact, FactInstruction, QueryInstruction};
|
||||
|
||||
use std::vec::{Vec};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum HeapCell {
|
||||
NamedStr(usize, Atom),
|
||||
Ref(usize),
|
||||
Str(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum MachineMode {
|
||||
Read,
|
||||
Write
|
||||
}
|
||||
|
||||
type Heap = Vec<HeapCell>;
|
||||
|
||||
type Registers = Vec<HeapCell>;
|
||||
|
||||
pub struct Machine {
|
||||
h : usize,
|
||||
s : usize,
|
||||
pub fail : bool,
|
||||
heap : Heap,
|
||||
mode : MachineMode,
|
||||
pub program : Option<CompiledFact>,
|
||||
registers : Registers
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new() -> Machine {
|
||||
Machine { h : 0,
|
||||
s : 0,
|
||||
fail : false,
|
||||
heap : Vec::with_capacity(256),
|
||||
mode : MachineMode::Write,
|
||||
program : None,
|
||||
registers : vec![HeapCell::Ref(0); 33] }
|
||||
}
|
||||
|
||||
fn lookup(&self, a: Addr) -> &HeapCell {
|
||||
match a {
|
||||
Addr::HeapCell(hc) => &self.heap[hc],
|
||||
Addr::RegNum(reg) => &self.registers[reg]
|
||||
}
|
||||
}
|
||||
|
||||
fn deref(&self, a: Addr) -> Addr {
|
||||
let mut a = a;
|
||||
|
||||
loop {
|
||||
if let &HeapCell::Ref(value) = self.lookup(a) {
|
||||
if let Addr::HeapCell(av) = a {
|
||||
if value != av {
|
||||
a = Addr::HeapCell(value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
};
|
||||
}
|
||||
|
||||
fn is_unbound(hc: &HeapCell, index: usize) -> bool {
|
||||
match hc {
|
||||
&HeapCell::Ref(r) => r == index,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: try to compress this function. currently it is dog shit.
|
||||
fn bind(&mut self, a: Addr, val: usize) {
|
||||
let mut a = a;
|
||||
|
||||
loop {
|
||||
match a {
|
||||
Addr::RegNum(reg) => {
|
||||
if let HeapCell::Ref(hc) = self.registers[reg] {
|
||||
a = Addr::HeapCell(hc);
|
||||
} else if Machine::is_unbound(&self.heap[val], val) {
|
||||
self.heap[val] = self.registers[reg].clone();
|
||||
break;
|
||||
} else {
|
||||
self.fail = true;
|
||||
break;
|
||||
}
|
||||
},
|
||||
Addr::HeapCell(hc) if Machine::is_unbound(&self.heap[hc], hc) => {
|
||||
self.heap[hc] = HeapCell::Ref(val);
|
||||
break;
|
||||
},
|
||||
Addr::HeapCell(hc) if Machine::is_unbound(&self.heap[val], val) => {
|
||||
self.heap[val] = HeapCell::Ref(hc);
|
||||
break;
|
||||
},
|
||||
_ => {
|
||||
self.fail = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn unify(&mut self, a1: Addr, a2: Addr) {
|
||||
let mut pdl : Vec<Addr> = vec![a1, a2];
|
||||
|
||||
self.fail = false;
|
||||
|
||||
while !(pdl.is_empty() || self.fail) {
|
||||
let d1 = self.deref(pdl.pop().unwrap());
|
||||
let d2 = self.deref(pdl.pop().unwrap());
|
||||
|
||||
if d1 != d2 {
|
||||
match (self.lookup(d1), self.lookup(d2)) {
|
||||
(&HeapCell::Ref(hc), _) =>
|
||||
self.bind(d2, hc),
|
||||
(_, &HeapCell::Ref(hc)) =>
|
||||
self.bind(d1, hc),
|
||||
(&HeapCell::Str(a1), &HeapCell::Str(a2)) => {
|
||||
let r1 = &self.heap[a1];
|
||||
let r2 = &self.heap[a2];
|
||||
|
||||
if let &HeapCell::NamedStr(n1, ref f1) = r1 {
|
||||
if let &HeapCell::NamedStr(n2, ref f2) = r2 {
|
||||
if n1 == n2 && *f1 == *f2 {
|
||||
for i in 1 .. n1 {
|
||||
pdl.push(Addr::HeapCell(a1 + i));
|
||||
pdl.push(Addr::HeapCell(a2 + i));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.fail = true;
|
||||
},
|
||||
_ => self.fail = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_query_instr<'a, 'b : 'a>(&'a mut self, instr: &'b QueryInstruction) {
|
||||
match instr {
|
||||
&QueryInstruction::PutStructure(ref name, arity, reg) => {
|
||||
self.heap.push(HeapCell::Str(self.h + 1));
|
||||
self.heap.push(HeapCell::NamedStr(arity, name.clone()));
|
||||
|
||||
self.registers[reg] = self.heap[self.h].clone();
|
||||
|
||||
self.h += 2;
|
||||
},
|
||||
&QueryInstruction::SetVariable(reg) => {
|
||||
self.heap.push(HeapCell::Ref(self.h));
|
||||
self.registers[reg] = self.heap[self.h].clone();
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
&QueryInstruction::SetValue(reg) => {
|
||||
self.heap.push(self.registers[reg].clone());
|
||||
self.h += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_fact_instr<'a, 'b : 'a>(&'a mut self, instr: &'b FactInstruction) {
|
||||
match instr {
|
||||
&FactInstruction::GetStructure(ref name, arity, reg) => {
|
||||
let addr = self.deref(Addr::RegNum(reg));
|
||||
|
||||
match self.lookup(addr) {
|
||||
&HeapCell::Str(a) => {
|
||||
let result = &self.heap[a];
|
||||
|
||||
if let &HeapCell::NamedStr(named_arity, ref named_str) = result {
|
||||
if arity == named_arity && *name == *named_str {
|
||||
self.s = a + 1;
|
||||
self.mode = MachineMode::Read;
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
&HeapCell::Ref(r) => {
|
||||
self.heap.push(HeapCell::Str(self.h + 1));
|
||||
self.heap.push(HeapCell::NamedStr(arity, name.clone()));
|
||||
|
||||
let h = self.h;
|
||||
|
||||
self.bind(Addr::HeapCell(r), h);
|
||||
|
||||
self.h += 2;
|
||||
self.mode = MachineMode::Write;
|
||||
},
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
};
|
||||
},
|
||||
&FactInstruction::UnifyVariable(reg) => {
|
||||
match self.mode {
|
||||
MachineMode::Read => self.registers[reg] = self.heap[self.s].clone(),
|
||||
MachineMode::Write => {
|
||||
self.heap.push(HeapCell::Ref(self.h));
|
||||
self.registers[reg] = self.heap[self.h].clone();
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
},
|
||||
&FactInstruction::UnifyValue(reg) => {
|
||||
let s = self.s;
|
||||
|
||||
match self.mode {
|
||||
MachineMode::Read => self.unify(Addr::RegNum(reg), Addr::HeapCell(s)),
|
||||
MachineMode::Write => {
|
||||
self.heap.push(self.registers[reg].clone());
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_heap(&mut self) {
|
||||
let program = self.program.take();
|
||||
|
||||
*self = Machine::new();
|
||||
self.program = program;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
mod l0_parser;
|
||||
|
||||
pub mod ast;
|
||||
pub mod iterators;
|
||||
pub mod parser;
|
||||
pub mod codegen;
|
||||
pub mod machine;
|
||||
@@ -1,52 +0,0 @@
|
||||
use l0::ast::{Term, TopLevel, Var};
|
||||
use l0::l0_parser::{parse_TopLevel};
|
||||
|
||||
use std::collections::{HashMap};
|
||||
|
||||
extern crate lalrpop_util as __lalrpop_util;
|
||||
|
||||
pub type ParseResult<'a> =
|
||||
Result<TopLevel, __lalrpop_util::ParseError<usize,(usize, &'a str),()>>;
|
||||
|
||||
pub fn parse_top_level<'a>(input: &'a str) -> ParseResult {
|
||||
let result = parse_TopLevel(&*input);
|
||||
|
||||
if let Ok(result) = result {
|
||||
return Ok(mark_cells(result));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mark_cells(tl: TopLevel) -> TopLevel {
|
||||
match tl {
|
||||
TopLevel::Fact(term) => TopLevel::Fact(mark_term_cells(term)),
|
||||
TopLevel::Query(term) => TopLevel::Query(mark_term_cells(term))
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_term_cells(term: Term) -> Term {
|
||||
let mut cell_num = 1;
|
||||
|
||||
{
|
||||
let mut bindings: HashMap<&Var, usize> = HashMap::new();
|
||||
let mut iter = term.breadth_first_iter();
|
||||
|
||||
while let Some(term) = iter.next() {
|
||||
if let &Term::Var(ref cell, ref var) = term {
|
||||
let cell_num_in_map = bindings.entry(var).or_insert(cell_num);
|
||||
|
||||
if *cell_num_in_map != cell_num {
|
||||
cell.set(*cell_num_in_map);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
term.set_cell(cell_num);
|
||||
cell_num += 1;
|
||||
}
|
||||
}
|
||||
|
||||
term
|
||||
}
|
||||
123
src/l1/ast.rs
123
src/l1/ast.rs
@@ -1,123 +0,0 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
use std::vec::Vec;
|
||||
|
||||
pub type Var = String;
|
||||
|
||||
pub type Atom = String;
|
||||
|
||||
pub enum TopLevel {
|
||||
Fact(Term),
|
||||
Query(Term)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Level {
|
||||
Shallow, Deep
|
||||
}
|
||||
|
||||
impl fmt::Display for Level {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Level::Shallow => write!(f, "A"),
|
||||
&Level::Deep => write!(f, "X")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Reg {
|
||||
ArgAndNorm(usize, usize),
|
||||
Norm(usize)
|
||||
}
|
||||
|
||||
impl Reg {
|
||||
pub fn has_arg(&self) -> bool {
|
||||
match self {
|
||||
&Reg::ArgAndNorm(_, _) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn norm(&self) -> usize {
|
||||
match self {
|
||||
&Reg::ArgAndNorm(_, norm) | &Reg::Norm(norm) => norm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Term {
|
||||
Atom(Cell<usize>, Atom),
|
||||
Clause(Cell<usize>, Atom, Vec<Box<Term>>),
|
||||
Var(Cell<Reg>, Var)
|
||||
}
|
||||
|
||||
pub enum TermRef<'a> {
|
||||
Atom(Level, &'a Cell<usize>, &'a Atom),
|
||||
Clause(Level, &'a Cell<usize>, &'a Atom, &'a Vec<Box<Term>>),
|
||||
Var(Level, &'a Cell<Reg>, &'a Var)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FactInstruction {
|
||||
GetStructure(Level, Atom, usize, usize),
|
||||
GetValue(usize, usize),
|
||||
GetVariable(usize, usize),
|
||||
Proceed,
|
||||
UnifyVariable(usize),
|
||||
UnifyValue(usize)
|
||||
}
|
||||
|
||||
pub enum QueryInstruction {
|
||||
Call(Atom, usize),
|
||||
PutStructure(Level, Atom, usize, usize),
|
||||
PutValue(usize, usize),
|
||||
PutVariable(usize, usize),
|
||||
SetVariable(usize),
|
||||
SetValue(usize),
|
||||
}
|
||||
|
||||
pub type CompiledFact = Vec<FactInstruction>;
|
||||
|
||||
pub type CompiledQuery = Vec<QueryInstruction>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Addr {
|
||||
HeapCell(usize),
|
||||
RegNum(usize)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum HeapCellValue {
|
||||
NamedStr(usize, Atom),
|
||||
Ref(usize),
|
||||
Str(usize),
|
||||
}
|
||||
|
||||
pub type Heap = Vec<HeapCellValue>;
|
||||
|
||||
pub type Registers = Vec<HeapCellValue>;
|
||||
|
||||
impl Term {
|
||||
pub fn subterms(&self) -> usize {
|
||||
match self {
|
||||
&Term::Clause(_, _, ref terms) => terms.len(),
|
||||
_ => 1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Atom {
|
||||
match self {
|
||||
&Term::Atom(_, ref atom)
|
||||
| &Term::Var(_, ref atom)
|
||||
| &Term::Clause(_, ref atom, _) => atom
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arity(&self) -> usize {
|
||||
match self {
|
||||
&Term::Atom(_, _) | &Term::Var(_, _) => 0,
|
||||
&Term::Clause(_, _, ref child_terms) => child_terms.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,312 +0,0 @@
|
||||
use l1::ast::{Atom, CompiledFact, CompiledQuery, FactInstruction,
|
||||
Level, QueryInstruction, Reg, Term, TermRef, Var};
|
||||
use l1::iterators::{FactIterator, QueryIterator};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::vec::Vec;
|
||||
|
||||
impl fmt::Display for QueryInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&QueryInstruction::Call(ref name, ref arity) =>
|
||||
write!(f, "call {}/{}", name, arity),
|
||||
&QueryInstruction::PutStructure(ref lvl, ref a, ref s, ref r) =>
|
||||
write!(f, "put_structure {}/{}, {}{}", a, s, lvl, r),
|
||||
&QueryInstruction::PutValue(ref a, ref x) =>
|
||||
write!(f, "put_value X{}, A{}", x, a),
|
||||
&QueryInstruction::PutVariable(ref a, ref x) =>
|
||||
write!(f, "put_variable X{}, A{}", x, a),
|
||||
&QueryInstruction::SetVariable(ref r) =>
|
||||
write!(f, "set_variable X{}", r),
|
||||
&QueryInstruction::SetValue(ref r) =>
|
||||
write!(f, "set_value X{}", r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FactInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&FactInstruction::GetStructure(ref lvl, ref a, ref s, ref r) =>
|
||||
write!(f, "get_structure {}/{}, {}{}", a, s, lvl, r),
|
||||
&FactInstruction::GetValue(ref a, ref x) =>
|
||||
write!(f, "get_value X{}, A{}", x, a),
|
||||
&FactInstruction::GetVariable(ref a, ref x) =>
|
||||
write!(f, "get_variable X{}, A{}", x, a),
|
||||
&FactInstruction::Proceed =>
|
||||
write!(f, "proceed"),
|
||||
&FactInstruction::UnifyVariable(ref r) =>
|
||||
write!(f, "unify_variable X{}", r),
|
||||
&FactInstruction::UnifyValue(ref r) =>
|
||||
write!(f, "unify_value X{}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TermMarker<'a> {
|
||||
bindings: HashMap<&'a Var, Reg>,
|
||||
arg_c: usize,
|
||||
norm_c: usize
|
||||
}
|
||||
|
||||
impl<'a> TermMarker<'a> {
|
||||
fn new(term: &'a Term) -> TermMarker<'a> {
|
||||
TermMarker { bindings: HashMap::new(),
|
||||
arg_c: 1,
|
||||
norm_c: term.subterms() + 1 }
|
||||
}
|
||||
|
||||
fn contains_var(&self, var: &'a Var) -> bool {
|
||||
self.bindings.contains_key(var)
|
||||
}
|
||||
|
||||
fn get(&self, var: &'a Var) -> Reg {
|
||||
*self.bindings.get(var).unwrap()
|
||||
}
|
||||
|
||||
fn insert(&mut self, var: &'a Var, r: Reg) {
|
||||
self.bindings.insert(var, r);
|
||||
}
|
||||
|
||||
fn mark_non_var(&mut self, lvl: Level, cell: &Cell<usize>) {
|
||||
if cell.get() == 0 {
|
||||
match lvl {
|
||||
Level::Deep => {
|
||||
let norm = self.norm_c;
|
||||
self.norm_c += 1;
|
||||
cell.set(norm);
|
||||
},
|
||||
Level::Shallow => {
|
||||
let arg = self.arg_c;
|
||||
self.arg_c += 1;
|
||||
cell.set(arg);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_var(&mut self, lvl: Level, var: &'a Var) -> Reg {
|
||||
if self.contains_var(var) {
|
||||
let reg = self.get(var);
|
||||
|
||||
match lvl {
|
||||
Level::Deep => Reg::Norm(reg.norm()),
|
||||
Level::Shallow if reg.has_arg() => {
|
||||
let arg = self.arg_c;
|
||||
self.arg_c += 1;
|
||||
|
||||
Reg::ArgAndNorm(arg, reg.norm())
|
||||
},
|
||||
Level::Shallow => {
|
||||
let norm = reg.norm();
|
||||
let reg = Reg::ArgAndNorm(self.arg_c, norm);
|
||||
|
||||
self.arg_c += 1;
|
||||
self.insert(var, reg);
|
||||
|
||||
reg
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let reg = match lvl {
|
||||
Level::Deep => Reg::Norm(self.norm_c),
|
||||
Level::Shallow => {
|
||||
let reg = Reg::ArgAndNorm(self.arg_c, self.norm_c);
|
||||
self.arg_c += 1;
|
||||
reg
|
||||
}
|
||||
};
|
||||
|
||||
self.norm_c += 1;
|
||||
self.insert(var, reg);
|
||||
reg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait CompilationTarget<'a> {
|
||||
type Iterator : Iterator<Item=TermRef<'a>>;
|
||||
|
||||
fn iter(&'a Term) -> Self::Iterator;
|
||||
|
||||
fn to_structure(Level, Atom, usize, usize) -> Self;
|
||||
|
||||
fn argument_to_variable(usize, usize) -> Self;
|
||||
fn argument_to_value(usize, usize) -> Self;
|
||||
fn subterm_to_variable(usize) -> Self;
|
||||
fn subterm_to_value(usize) -> Self;
|
||||
|
||||
fn clause_arg_to_instr(usize) -> Self;
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for FactInstruction {
|
||||
type Iterator = FactIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.breadth_first_iter()
|
||||
}
|
||||
|
||||
fn to_structure(lvl: Level, atom: Atom, arity: usize, cell_num: usize) -> Self {
|
||||
FactInstruction::GetStructure(lvl, atom, arity, cell_num)
|
||||
}
|
||||
|
||||
fn argument_to_variable(arg: usize, val: usize) -> Self {
|
||||
FactInstruction::GetVariable(arg, val)
|
||||
}
|
||||
|
||||
fn argument_to_value(arg: usize, val: usize) -> Self {
|
||||
FactInstruction::GetValue(arg, val)
|
||||
}
|
||||
|
||||
fn subterm_to_variable(val: usize) -> Self {
|
||||
FactInstruction::UnifyVariable(val)
|
||||
}
|
||||
|
||||
fn subterm_to_value(val: usize) -> Self {
|
||||
FactInstruction::UnifyValue(val)
|
||||
}
|
||||
|
||||
fn clause_arg_to_instr(val: usize) -> Self {
|
||||
FactInstruction::UnifyVariable(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for QueryInstruction {
|
||||
type Iterator = QueryIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.post_order_iter()
|
||||
}
|
||||
|
||||
fn to_structure(lvl: Level, atom: Atom, arity: usize, cell_num: usize) -> Self {
|
||||
QueryInstruction::PutStructure(lvl, atom, arity, cell_num)
|
||||
}
|
||||
|
||||
fn argument_to_variable(arg: usize, val: usize) -> Self {
|
||||
QueryInstruction::PutVariable(arg, val)
|
||||
}
|
||||
|
||||
fn argument_to_value(arg: usize, val: usize) -> Self {
|
||||
QueryInstruction::PutValue(arg, val)
|
||||
}
|
||||
|
||||
fn subterm_to_variable(val: usize) -> Self {
|
||||
QueryInstruction::SetVariable(val)
|
||||
}
|
||||
|
||||
fn subterm_to_value(val: usize) -> Self {
|
||||
QueryInstruction::SetValue(val)
|
||||
}
|
||||
|
||||
fn clause_arg_to_instr(val: usize) -> Self {
|
||||
QueryInstruction::SetValue(val)
|
||||
}
|
||||
}
|
||||
|
||||
fn to_structure<'a, Target>(tm: &mut TermMarker<'a>,
|
||||
lvl: Level,
|
||||
name: &'a Atom,
|
||||
cell: &'a Cell<usize>,
|
||||
arity: usize)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
tm.mark_non_var(lvl, cell);
|
||||
Target::to_structure(lvl, name.clone(), arity, cell.get())
|
||||
}
|
||||
|
||||
fn non_var_subterm<'a, Target>(tm: &mut TermMarker<'a>, cell: &'a Cell<usize>)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
tm.mark_non_var(Level::Deep, cell);
|
||||
Target::clause_arg_to_instr(cell.get())
|
||||
}
|
||||
|
||||
fn var_term<'a, Target>(tm: &mut TermMarker<'a>,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<Reg>,
|
||||
var: &'a Var)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
if !tm.contains_var(var) {
|
||||
let reg = tm.mark_var(lvl, var);
|
||||
cell.set(reg);
|
||||
|
||||
match reg {
|
||||
Reg::ArgAndNorm(arg, norm) =>
|
||||
Target::argument_to_variable(arg, norm),
|
||||
Reg::Norm(norm) =>
|
||||
Target::subterm_to_variable(norm)
|
||||
}
|
||||
} else {
|
||||
let reg = tm.mark_var(lvl, var);
|
||||
cell.set(reg);
|
||||
|
||||
match reg {
|
||||
Reg::ArgAndNorm(arg, norm) =>
|
||||
Target::argument_to_value(arg, norm),
|
||||
Reg::Norm(norm) =>
|
||||
Target::subterm_to_value(norm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn subterm_to_instr<'a, Target>(tm: &mut TermMarker<'a>, subterm: &'a Term)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
match subterm {
|
||||
&Term::Atom(ref cell, _) | &Term::Clause(ref cell, _, _) =>
|
||||
non_var_subterm(tm, cell),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
var_term(tm, Level::Deep, cell, var)
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_target<'a, Target>(term: &'a Term) -> Vec<Target>
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
let iter = Target::iter(term);
|
||||
let mut target = Vec::<Target>::new();
|
||||
let mut marker = TermMarker::new(term);
|
||||
|
||||
for term in iter {
|
||||
match term {
|
||||
TermRef::Atom(lvl, term, atom) =>
|
||||
target.push(to_structure(&mut marker, lvl, atom, term, 0)),
|
||||
TermRef::Clause(lvl, term, atom, terms) => {
|
||||
target.push(to_structure(&mut marker, lvl, atom, term, terms.len()));
|
||||
|
||||
for subterm in terms {
|
||||
target.push(subterm_to_instr(&mut marker, subterm.as_ref()));
|
||||
}
|
||||
},
|
||||
TermRef::Var(lvl @ Level::Shallow, ref cell, ref var) =>
|
||||
target.push(var_term(&mut marker, lvl, cell, var)),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
target
|
||||
}
|
||||
|
||||
pub fn compile_fact(term: &Term) -> CompiledFact {
|
||||
let mut compiled_fact = compile_target(term);
|
||||
|
||||
compiled_fact.push(FactInstruction::Proceed);
|
||||
compiled_fact
|
||||
}
|
||||
|
||||
pub fn compile_query<'a>(term: &'a Term) -> CompiledQuery {
|
||||
let mut compiled_query = compile_target(term);
|
||||
|
||||
if let &Term::Clause(_, ref atom, ref terms) = term {
|
||||
compiled_query.push(QueryInstruction::Call(atom.clone(), terms.len()));
|
||||
}
|
||||
|
||||
compiled_query
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
use l1::ast::{Atom, Level, Reg, Term, TermRef, Var};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::vec::Vec;
|
||||
|
||||
enum IteratorState<'a> {
|
||||
Atom(Level, &'a Cell<usize>, &'a Atom),
|
||||
Clause(Level, usize, &'a Cell<usize>, &'a Atom, &'a Vec<Box<Term>>),
|
||||
IsolatedAtom(&'a Cell<usize>, &'a Atom),
|
||||
IsolatedVar(&'a Cell<Reg>, &'a Var),
|
||||
RootClause(usize, &'a Vec<Box<Term>>),
|
||||
Var(Level, &'a Cell<Reg>, &'a Var)
|
||||
}
|
||||
|
||||
impl<'a> IteratorState<'a> {
|
||||
fn to_state(lvl: Level, term: &'a Term) -> IteratorState<'a>
|
||||
{
|
||||
match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
IteratorState::Atom(lvl, cell, atom),
|
||||
&Term::Clause(ref cell, ref atom, ref child_terms) =>
|
||||
IteratorState::Clause(lvl, 0, cell, atom, child_terms),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
IteratorState::Var(lvl, cell, var)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryIterator<'a> {
|
||||
state_stack: Vec<IteratorState<'a>>
|
||||
}
|
||||
|
||||
impl<'a> QueryIterator<'a> {
|
||||
fn push_clause(&mut self,
|
||||
lvl: Level,
|
||||
child_num: usize,
|
||||
cell: &'a Cell<usize>,
|
||||
name: &'a Atom,
|
||||
child_terms: &'a Vec<Box<Term>>)
|
||||
{
|
||||
self.state_stack.push(IteratorState::Clause(lvl,
|
||||
child_num,
|
||||
cell,
|
||||
name,
|
||||
child_terms));
|
||||
}
|
||||
|
||||
fn push_root_clause(&mut self,
|
||||
child_num: usize,
|
||||
child_terms: &'a Vec<Box<Term>>)
|
||||
{
|
||||
self.state_stack.push(IteratorState::RootClause(child_num, child_terms));
|
||||
}
|
||||
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_stack.push(IteratorState::to_state(lvl, term));
|
||||
}
|
||||
|
||||
fn new(term: &'a Term) -> QueryIterator<'a> {
|
||||
let state = match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
IteratorState::IsolatedAtom(cell, atom),
|
||||
&Term::Clause(_, _, ref terms) =>
|
||||
IteratorState::RootClause(0, terms),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
IteratorState::IsolatedVar(cell, var)
|
||||
};
|
||||
|
||||
QueryIterator { state_stack: vec![state] }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for QueryIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(iter_state) = self.state_stack.pop() {
|
||||
match iter_state {
|
||||
IteratorState::Atom(lvl, cell, atom) =>
|
||||
return Some(TermRef::Atom(lvl, cell, atom)),
|
||||
IteratorState::Clause(lvl, child_num, cell, atom, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
return Some(TermRef::Clause(lvl, cell, atom, child_terms));
|
||||
} else {
|
||||
self.push_clause(lvl, child_num + 1, cell, atom, child_terms);
|
||||
self.push_subterm(Level::Deep, child_terms[child_num].as_ref());
|
||||
}
|
||||
},
|
||||
IteratorState::IsolatedAtom(cell, atom) =>
|
||||
return Some(TermRef::Atom(Level::Shallow, cell, atom)),
|
||||
IteratorState::IsolatedVar(cell, var) =>
|
||||
return Some(TermRef::Var(Level::Shallow, cell, var)),
|
||||
IteratorState::RootClause(child_num, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
return None;
|
||||
} else {
|
||||
self.push_root_clause(child_num + 1, child_terms);
|
||||
self.push_subterm(Level::Shallow, child_terms[child_num].as_ref());
|
||||
}
|
||||
},
|
||||
IteratorState::Var(lvl, cell, var) =>
|
||||
return Some(TermRef::Var(lvl, cell, var))
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FactIterator<'a> {
|
||||
state_queue: VecDeque<IteratorState<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> FactIterator<'a> {
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_queue.push_back(IteratorState::to_state(lvl, term));
|
||||
}
|
||||
|
||||
fn new(term: &'a Term) -> FactIterator<'a> {
|
||||
let states = match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
vec![IteratorState::IsolatedAtom(cell, atom)],
|
||||
&Term::Clause(_, _, ref terms) =>
|
||||
vec![IteratorState::RootClause(0, terms)],
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
vec![IteratorState::IsolatedVar(cell, var)]
|
||||
};
|
||||
|
||||
FactIterator { state_queue: VecDeque::from(states) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FactIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(state) = self.state_queue.pop_front() {
|
||||
match state {
|
||||
IteratorState::Atom(lvl, cell, atom) =>
|
||||
return Some(TermRef::Atom(lvl, cell, atom)),
|
||||
IteratorState::Clause(lvl, _, cell, atom, child_terms) => {
|
||||
for child_term in child_terms {
|
||||
self.push_subterm(Level::Deep, child_term);
|
||||
}
|
||||
|
||||
return Some(TermRef::Clause(lvl, cell, atom, child_terms));
|
||||
},
|
||||
IteratorState::IsolatedAtom(cell, atom) =>
|
||||
return Some(TermRef::Atom(Level::Shallow, cell, atom)),
|
||||
IteratorState::IsolatedVar(cell, var) =>
|
||||
return Some(TermRef::Var(Level::Shallow, cell, var)),
|
||||
IteratorState::RootClause(_, child_terms) => {
|
||||
for child_term in child_terms {
|
||||
self.push_subterm(Level::Shallow, child_term);
|
||||
}
|
||||
},
|
||||
IteratorState::Var(lvl, cell, var) =>
|
||||
return Some(TermRef::Var(lvl, cell, var))
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn post_order_iter(&self) -> QueryIterator {
|
||||
QueryIterator::new(self)
|
||||
}
|
||||
|
||||
pub fn breadth_first_iter(&self) -> FactIterator {
|
||||
FactIterator::new(self)
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
use std::cell::Cell;
|
||||
|
||||
use l1::ast::{Atom, Reg, Term, TopLevel, Var};
|
||||
|
||||
grammar;
|
||||
|
||||
pub TopLevel: TopLevel = {
|
||||
"?-" <t:Term> "." => TopLevel::Query(t),
|
||||
<t:Term> "." => TopLevel::Fact(t),
|
||||
};
|
||||
|
||||
Atom : Atom = {
|
||||
r"[a-z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
|
||||
Var : Var = {
|
||||
r"[A-Z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
|
||||
BoxedTerm : Box<Term> = {
|
||||
<t:Term> => Box::new(t),
|
||||
};
|
||||
|
||||
Term : Term = {
|
||||
<a:Atom> "(" <ts: (<BoxedTerm> ",")*> <t:BoxedTerm> ")" => {
|
||||
let mut ts = ts;
|
||||
ts.push(t);
|
||||
Term::Clause(Cell::new(0), a, ts)
|
||||
},
|
||||
<Atom> => Term::Atom(Cell::new(0), <>),
|
||||
<Var> => Term::Var(Cell::new(Reg::Norm(0)), <>)
|
||||
};
|
||||
1595
src/l1/l1_parser.rs
1595
src/l1/l1_parser.rs
File diff suppressed because it is too large
Load Diff
@@ -1,309 +0,0 @@
|
||||
use l1::ast::{Addr, Atom, CompiledFact, CompiledQuery,
|
||||
FactInstruction, Heap, HeapCellValue, QueryInstruction,
|
||||
Registers};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum MachineMode {
|
||||
Read,
|
||||
Write
|
||||
}
|
||||
|
||||
pub struct Machine {
|
||||
h : usize,
|
||||
s : usize,
|
||||
p : usize,
|
||||
code : CompiledFact,
|
||||
code_dir : HashMap<(Atom, usize), usize>,
|
||||
fail : bool,
|
||||
heap : Heap,
|
||||
mode : MachineMode,
|
||||
registers : Registers
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new() -> Machine {
|
||||
Machine { h : 0,
|
||||
s : 0,
|
||||
p : 0,
|
||||
code : Vec::new(),
|
||||
code_dir : HashMap::new(),
|
||||
fail : false,
|
||||
heap : Vec::with_capacity(256),
|
||||
mode : MachineMode::Write,
|
||||
registers : vec![HeapCellValue::Ref(0); 32] }
|
||||
}
|
||||
|
||||
pub fn add_fact(&mut self, mut fact: CompiledFact, name: Atom, arity: usize)
|
||||
{
|
||||
let index = self.code.len();
|
||||
|
||||
self.code.append(&mut fact);
|
||||
self.code_dir.insert((name, arity), index);
|
||||
}
|
||||
|
||||
pub fn failed(&self) -> bool {
|
||||
self.fail
|
||||
}
|
||||
|
||||
fn lookup(&self, a: Addr) -> &HeapCellValue {
|
||||
match a {
|
||||
Addr::HeapCell(hc) => &self.heap[hc],
|
||||
Addr::RegNum(reg) => &self.registers[reg]
|
||||
}
|
||||
}
|
||||
|
||||
fn deref(&self, a: Addr) -> Addr {
|
||||
let mut a = a;
|
||||
|
||||
loop {
|
||||
if let &HeapCellValue::Ref(value) = self.lookup(a) {
|
||||
if let Addr::HeapCell(av) = a {
|
||||
if value != av {
|
||||
a = Addr::HeapCell(value);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
a = Addr::HeapCell(value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
};
|
||||
}
|
||||
|
||||
fn is_unbound(hc: &HeapCellValue, index: usize) -> bool {
|
||||
match hc {
|
||||
&HeapCellValue::Ref(r) => r == index,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: try to compress this function.
|
||||
fn bind(&mut self, a: Addr, val: usize) {
|
||||
let mut a = a;
|
||||
|
||||
loop {
|
||||
match a {
|
||||
Addr::RegNum(reg) => {
|
||||
if let HeapCellValue::Ref(hc) = self.registers[reg] {
|
||||
a = Addr::HeapCell(hc);
|
||||
} else if Machine::is_unbound(&self.heap[val], val) {
|
||||
self.heap[val] = self.registers[reg].clone();
|
||||
break;
|
||||
} else {
|
||||
self.fail = true;
|
||||
break;
|
||||
}
|
||||
},
|
||||
Addr::HeapCell(hc) if Machine::is_unbound(&self.heap[hc], hc) => {
|
||||
self.heap[hc] = HeapCellValue::Ref(val);
|
||||
break;
|
||||
},
|
||||
Addr::HeapCell(hc) if Machine::is_unbound(&self.heap[val], val) => {
|
||||
self.heap[val] = HeapCellValue::Ref(hc);
|
||||
break;
|
||||
},
|
||||
_ => {
|
||||
self.fail = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn unify(&mut self, a1: Addr, a2: Addr) {
|
||||
let mut pdl = vec![a1, a2];
|
||||
|
||||
self.fail = false;
|
||||
|
||||
while !(pdl.is_empty() || self.fail) {
|
||||
let d1 = self.deref(pdl.pop().unwrap());
|
||||
let d2 = self.deref(pdl.pop().unwrap());
|
||||
|
||||
if d1 != d2 {
|
||||
match (self.lookup(d1), self.lookup(d2)) {
|
||||
(&HeapCellValue::Ref(hc), _) =>
|
||||
self.bind(d2, hc),
|
||||
(_, &HeapCellValue::Ref(hc)) =>
|
||||
self.bind(d1, hc),
|
||||
(&HeapCellValue::Str(a1), &HeapCellValue::Str(a2)) => {
|
||||
let r1 = &self.heap[a1];
|
||||
let r2 = &self.heap[a2];
|
||||
|
||||
if let &HeapCellValue::NamedStr(n1, ref f1) = r1 {
|
||||
if let &HeapCellValue::NamedStr(n2, ref f2) = r2 {
|
||||
if n1 == n2 && *f1 == *f2 {
|
||||
for i in 1 .. n1 {
|
||||
pdl.push(Addr::HeapCell(a1 + i));
|
||||
pdl.push(Addr::HeapCell(a2 + i));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.fail = true;
|
||||
},
|
||||
_ => self.fail = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_fact(&mut self) {
|
||||
loop {
|
||||
if let &FactInstruction::Proceed = &self.code[self.p] {
|
||||
break;
|
||||
} else if self.fail {
|
||||
break;
|
||||
}
|
||||
|
||||
let fact_instr = self.code[self.p].clone();
|
||||
self.execute_fact_instr(fact_instr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_query(&mut self, query: &CompiledQuery) {
|
||||
for instr in query {
|
||||
self.execute_query_instr(instr);
|
||||
|
||||
if self.fail {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_query_instr<'a, 'b: 'a>(&'a mut self, instr: &'b QueryInstruction) {
|
||||
match instr {
|
||||
&QueryInstruction::Call(ref name, arity) => {
|
||||
// why is Option<&T> not Deref?!?!?
|
||||
// is it because if the value is None, there's nothing to
|
||||
// dereference?
|
||||
let compiled_fact_index =
|
||||
self.code_dir.get(&(name.clone(), arity))
|
||||
.map(|index| *index);
|
||||
|
||||
match compiled_fact_index {
|
||||
Some(compiled_fact_index) => {
|
||||
self.p = compiled_fact_index;
|
||||
self.execute_fact();
|
||||
},
|
||||
None => self.fail = true,
|
||||
};
|
||||
}
|
||||
&QueryInstruction::PutStructure(_, ref name, arity, reg) => {
|
||||
self.heap.push(HeapCellValue::Str(self.h + 1));
|
||||
self.heap.push(HeapCellValue::NamedStr(arity, name.clone()));
|
||||
|
||||
self.registers[reg] = self.heap[self.h].clone();
|
||||
|
||||
self.h += 2;
|
||||
},
|
||||
&QueryInstruction::PutValue(arg, norm) =>
|
||||
self.registers[arg] = self.registers[norm].clone(),
|
||||
&QueryInstruction::PutVariable(arg, norm) => {
|
||||
self.heap.push(HeapCellValue::Ref(self.h));
|
||||
|
||||
self.registers[norm] = self.heap[self.h].clone();
|
||||
self.registers[arg] = self.heap[self.h].clone();
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
&QueryInstruction::SetVariable(reg) => {
|
||||
self.heap.push(HeapCellValue::Ref(self.h));
|
||||
self.registers[reg] = self.heap[self.h].clone();
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
&QueryInstruction::SetValue(reg) => {
|
||||
self.heap.push(self.registers[reg].clone());
|
||||
self.h += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_fact_instr(&mut self, instr: FactInstruction) {
|
||||
match instr {
|
||||
FactInstruction::Proceed => return,
|
||||
FactInstruction::GetStructure(_, name, arity, reg) => {
|
||||
let addr = self.deref(Addr::RegNum(reg));
|
||||
|
||||
match self.lookup(addr) {
|
||||
&HeapCellValue::Str(a) => {
|
||||
let result = &self.heap[a];
|
||||
|
||||
if let &HeapCellValue::NamedStr(named_arity, ref named_str) = result {
|
||||
if arity == named_arity && *name == *named_str {
|
||||
self.s = a + 1;
|
||||
self.mode = MachineMode::Read;
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
&HeapCellValue::Ref(r) => {
|
||||
self.heap.push(HeapCellValue::Str(self.h + 1));
|
||||
self.heap.push(HeapCellValue::NamedStr(arity, name));
|
||||
|
||||
let h = self.h;
|
||||
|
||||
self.bind(Addr::HeapCell(r), h);
|
||||
|
||||
self.h += 2;
|
||||
self.mode = MachineMode::Write;
|
||||
},
|
||||
_ => {
|
||||
self.fail = true;
|
||||
}
|
||||
};
|
||||
},
|
||||
FactInstruction::GetVariable(arg, norm) =>
|
||||
self.registers[norm] = self.registers[arg].clone(),
|
||||
FactInstruction::GetValue(arg, norm) =>
|
||||
self.unify(Addr::RegNum(norm), Addr::RegNum(arg)),
|
||||
FactInstruction::UnifyVariable(reg) => {
|
||||
match self.mode {
|
||||
MachineMode::Read => self.registers[reg] = self.heap[self.s].clone(),
|
||||
MachineMode::Write => {
|
||||
self.heap.push(HeapCellValue::Ref(self.h));
|
||||
self.registers[reg] = self.heap[self.h].clone();
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
},
|
||||
FactInstruction::UnifyValue(reg) => {
|
||||
let s = self.s;
|
||||
|
||||
match self.mode {
|
||||
MachineMode::Read => self.unify(Addr::RegNum(reg), Addr::HeapCell(s)),
|
||||
MachineMode::Write => {
|
||||
self.heap.push(self.registers[reg].clone());
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
}
|
||||
}
|
||||
|
||||
self.p += 1;
|
||||
}
|
||||
|
||||
pub fn reset_machine_state(&mut self) {
|
||||
self.h = 0;
|
||||
self.s = 0;
|
||||
self.p = 0;
|
||||
|
||||
self.fail = false;
|
||||
self.heap = Vec::with_capacity(256);
|
||||
self.mode = MachineMode::Write;
|
||||
self.registers = vec![HeapCellValue::Ref(0); 32];
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
pub mod ast;
|
||||
pub mod iterators;
|
||||
pub mod l1_parser;
|
||||
pub mod codegen;
|
||||
pub mod machine;
|
||||
253
src/l2/ast.rs
253
src/l2/ast.rs
@@ -1,253 +0,0 @@
|
||||
use std::cell::Cell;
|
||||
use std::fmt;
|
||||
use std::ops::{Add, AddAssign};
|
||||
use std::vec::Vec;
|
||||
|
||||
pub type Var = String;
|
||||
|
||||
pub type Atom = String;
|
||||
|
||||
pub enum TopLevel {
|
||||
Fact(Term),
|
||||
Rule(Rule),
|
||||
Query(Term)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Level {
|
||||
Shallow, Deep
|
||||
}
|
||||
|
||||
impl fmt::Display for Level {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Level::Shallow => write!(f, "A"),
|
||||
&Level::Deep => write!(f, "X")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum RegType {
|
||||
Perm(usize),
|
||||
Temp(usize)
|
||||
}
|
||||
|
||||
impl RegType {
|
||||
pub fn reg_num(self) -> usize {
|
||||
match self {
|
||||
RegType::Perm(reg_num) | RegType::Temp(reg_num) => reg_num
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_perm(self) -> bool {
|
||||
match self {
|
||||
RegType::Perm(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VarReg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&VarReg::Norm(RegType::Perm(reg)) => write!(f, "Y{}", reg),
|
||||
&VarReg::Norm(RegType::Temp(reg)) => write!(f, "X{}", reg),
|
||||
&VarReg::ArgAndNorm(RegType::Perm(reg), arg) =>
|
||||
write!(f, "Y{} A{}", reg, arg),
|
||||
&VarReg::ArgAndNorm(RegType::Temp(reg), arg) =>
|
||||
write!(f, "X{} A{}", reg, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegType> for Addr {
|
||||
fn from(reg: RegType) -> Addr {
|
||||
match reg {
|
||||
RegType::Perm(reg) => Addr::StackCell(reg),
|
||||
RegType::Temp(reg) => Addr::RegNum(reg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RegType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&RegType::Perm(val) => write!(f, "Y{}", val),
|
||||
&RegType::Temp(val) => write!(f, "X{}", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum VarReg {
|
||||
ArgAndNorm(RegType, usize),
|
||||
Norm(RegType)
|
||||
}
|
||||
|
||||
impl VarReg {
|
||||
pub fn norm(self) -> RegType {
|
||||
match self {
|
||||
VarReg::ArgAndNorm(reg, _) | VarReg::Norm(reg) => reg
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_register(self) -> usize {
|
||||
match self {
|
||||
VarReg::ArgAndNorm(_, root) => root,
|
||||
VarReg::Norm(root) => root.reg_num()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Term {
|
||||
Atom(Cell<RegType>, Atom),
|
||||
Clause(Cell<RegType>, Atom, Vec<Box<Term>>),
|
||||
Var(Cell<VarReg>, Var)
|
||||
}
|
||||
|
||||
pub struct Rule {
|
||||
pub head: (Term, Term),
|
||||
pub clauses: Vec<Term>
|
||||
}
|
||||
|
||||
pub enum TermRef<'a> {
|
||||
Atom(Level, &'a Cell<RegType>, &'a Atom),
|
||||
Clause(Level, &'a Cell<RegType>, &'a Atom, &'a Vec<Box<Term>>),
|
||||
Var(Level, &'a Cell<VarReg>, &'a Var)
|
||||
}
|
||||
|
||||
pub enum FactInstruction {
|
||||
GetStructure(Level, Atom, usize, RegType),
|
||||
GetValue(RegType, usize),
|
||||
GetVariable(RegType, usize),
|
||||
UnifyVariable(RegType),
|
||||
UnifyValue(RegType)
|
||||
}
|
||||
|
||||
pub enum QueryInstruction {
|
||||
PutStructure(Level, Atom, usize, RegType),
|
||||
PutValue(RegType, usize),
|
||||
PutVariable(RegType, usize),
|
||||
SetVariable(RegType),
|
||||
SetValue(RegType)
|
||||
}
|
||||
|
||||
pub enum ControlInstruction {
|
||||
Allocate(usize),
|
||||
Call(Atom, usize),
|
||||
Deallocate,
|
||||
Proceed
|
||||
}
|
||||
|
||||
pub type CompiledFact = Vec<FactInstruction>;
|
||||
|
||||
pub type CompiledQuery = Vec<QueryInstruction>;
|
||||
|
||||
pub enum Line {
|
||||
Control(ControlInstruction),
|
||||
Fact(CompiledFact),
|
||||
Query(CompiledQuery)
|
||||
}
|
||||
|
||||
pub type Code = Vec<Line>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Addr {
|
||||
HeapCell(usize),
|
||||
RegNum(usize),
|
||||
StackCell(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum HeapCellValue {
|
||||
NamedStr(usize, Atom),
|
||||
Ref(usize),
|
||||
Str(usize)
|
||||
}
|
||||
|
||||
impl HeapCellValue {
|
||||
pub fn as_ref(&self, focus: usize) -> HeapCellRef {
|
||||
match self {
|
||||
&HeapCellValue::Ref(r) => HeapCellRef::Ref(r),
|
||||
&HeapCellValue::Str(s) => HeapCellRef::Str(s),
|
||||
&HeapCellValue::NamedStr(_, _) => HeapCellRef::Str(focus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum HeapCellRef {
|
||||
Ref(usize),
|
||||
Str(usize)
|
||||
}
|
||||
|
||||
impl HeapCellRef {
|
||||
pub fn heap_offset(&self) -> usize {
|
||||
match self {
|
||||
&HeapCellRef::Ref(r) | &HeapCellRef::Str(r) => r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HeapCellRef> for HeapCellValue {
|
||||
fn from(hcr: HeapCellRef) -> HeapCellValue {
|
||||
match hcr {
|
||||
HeapCellRef::Ref(r) => HeapCellValue::Ref(r),
|
||||
HeapCellRef::Str(s) => HeapCellValue::Str(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CodePtr {
|
||||
DirEntry(usize),
|
||||
TopLevel
|
||||
}
|
||||
|
||||
impl Add<usize> for CodePtr {
|
||||
type Output = CodePtr;
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
match self {
|
||||
CodePtr::DirEntry(p) => CodePtr::DirEntry(p + rhs),
|
||||
CodePtr::TopLevel => CodePtr::TopLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<usize> for CodePtr {
|
||||
fn add_assign(&mut self, rhs: usize) {
|
||||
match self {
|
||||
&mut CodePtr::DirEntry(ref mut p) => *p += rhs,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Heap = Vec<HeapCellValue>;
|
||||
|
||||
pub type Registers = Vec<HeapCellRef>;
|
||||
|
||||
impl Term {
|
||||
pub fn subterms(&self) -> usize {
|
||||
match self {
|
||||
&Term::Clause(_, _, ref terms) => terms.len(),
|
||||
_ => 1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Atom {
|
||||
match self {
|
||||
&Term::Atom(_, ref atom)
|
||||
| &Term::Var(_, ref atom)
|
||||
| &Term::Clause(_, ref atom, _) => atom
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arity(&self) -> usize {
|
||||
match self {
|
||||
&Term::Atom(_, _) | &Term::Var(_, _) => 0,
|
||||
&Term::Clause(_, _, ref child_terms) => child_terms.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
use l2::ast::*;
|
||||
use l2::iterators::{FactIterator, QueryIterator};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::max;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::vec::Vec;
|
||||
|
||||
impl fmt::Display for FactInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&FactInstruction::GetStructure(Level::Deep, ref name, ref arity, ref r) =>
|
||||
write!(f, "get_structure {}/{}, {}", name, arity, r),
|
||||
&FactInstruction::GetStructure(Level::Shallow, ref name, ref arity, ref r) =>
|
||||
write!(f, "get_structure {}/{}, A{}", name, arity, r.reg_num()),
|
||||
&FactInstruction::GetValue(ref x, ref a) =>
|
||||
write!(f, "get_value {}, A{}", x, a),
|
||||
&FactInstruction::GetVariable(ref x, ref a) =>
|
||||
write!(f, "get_variable {}, A{}", x, a),
|
||||
&FactInstruction::UnifyVariable(ref r) =>
|
||||
write!(f, "unify_variable {}", r),
|
||||
&FactInstruction::UnifyValue(ref r) =>
|
||||
write!(f, "unify_value {}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for QueryInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&QueryInstruction::PutStructure(Level::Deep, ref name, ref arity, ref r) =>
|
||||
write!(f, "put_structure {}/{}, A{}", name, arity, r.reg_num()),
|
||||
&QueryInstruction::PutStructure(Level::Shallow, ref name, ref arity, ref r) =>
|
||||
write!(f, "put_structure {}/{}, {}", name, arity, r),
|
||||
&QueryInstruction::PutValue(ref x, ref a) =>
|
||||
write!(f, "put_value {}, A{}", x, a),
|
||||
&QueryInstruction::PutVariable(ref x, ref a) =>
|
||||
write!(f, "put_variable {}, A{}", x, a),
|
||||
&QueryInstruction::SetVariable(ref r) =>
|
||||
write!(f, "set_variable {}", r),
|
||||
&QueryInstruction::SetValue(ref r) =>
|
||||
write!(f, "set_value {}", r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ControlInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&ControlInstruction::Allocate(num_cells) =>
|
||||
write!(f, "allocate {}", num_cells),
|
||||
&ControlInstruction::Call(ref name, ref arity) =>
|
||||
write!(f, "call {}/{}", name, arity),
|
||||
&ControlInstruction::Deallocate =>
|
||||
write!(f, "deallocate"),
|
||||
&ControlInstruction::Proceed =>
|
||||
write!(f, "proceed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait CompilationTarget<'a> {
|
||||
type Iterator : Iterator<Item=TermRef<'a>>;
|
||||
|
||||
fn iter(&'a Term) -> Self::Iterator;
|
||||
|
||||
fn to_structure(Level, Atom, usize, RegType) -> Self;
|
||||
|
||||
fn argument_to_variable(RegType, usize) -> Self;
|
||||
fn argument_to_value(RegType, usize) -> Self;
|
||||
fn subterm_to_variable(RegType) -> Self;
|
||||
fn subterm_to_value(RegType) -> Self;
|
||||
|
||||
fn clause_arg_to_instr(RegType) -> Self;
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for FactInstruction {
|
||||
type Iterator = FactIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.breadth_first_iter()
|
||||
}
|
||||
|
||||
fn to_structure(lvl: Level, atom: Atom, arity: usize, reg: RegType) -> Self {
|
||||
FactInstruction::GetStructure(lvl, atom, arity, reg)
|
||||
}
|
||||
|
||||
fn argument_to_variable(arg: RegType, val: usize) -> Self {
|
||||
FactInstruction::GetVariable(arg, val)
|
||||
}
|
||||
|
||||
fn argument_to_value(arg: RegType, val: usize) -> Self {
|
||||
FactInstruction::GetValue(arg, val)
|
||||
}
|
||||
|
||||
fn subterm_to_variable(val: RegType) -> Self {
|
||||
FactInstruction::UnifyVariable(val)
|
||||
}
|
||||
|
||||
fn subterm_to_value(val: RegType) -> Self {
|
||||
FactInstruction::UnifyValue(val)
|
||||
}
|
||||
|
||||
fn clause_arg_to_instr(val: RegType) -> Self {
|
||||
FactInstruction::UnifyVariable(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for QueryInstruction {
|
||||
type Iterator = QueryIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.post_order_iter()
|
||||
}
|
||||
|
||||
fn to_structure(lvl: Level, atom: Atom, arity: usize, reg: RegType) -> Self {
|
||||
QueryInstruction::PutStructure(lvl, atom, arity, reg)
|
||||
}
|
||||
|
||||
fn argument_to_variable(arg: RegType, val: usize) -> Self {
|
||||
QueryInstruction::PutVariable(arg, val)
|
||||
}
|
||||
|
||||
fn argument_to_value(arg: RegType, val: usize) -> Self {
|
||||
QueryInstruction::PutValue(arg, val)
|
||||
}
|
||||
|
||||
fn subterm_to_variable(val: RegType) -> Self {
|
||||
QueryInstruction::SetVariable(val)
|
||||
}
|
||||
|
||||
fn subterm_to_value(val: RegType) -> Self {
|
||||
QueryInstruction::SetValue(val)
|
||||
}
|
||||
|
||||
fn clause_arg_to_instr(val: RegType) -> Self {
|
||||
QueryInstruction::SetValue(val)
|
||||
}
|
||||
}
|
||||
|
||||
struct TermMarker<'a> {
|
||||
bindings: HashMap<&'a Var, VarReg>,
|
||||
arg_c: usize,
|
||||
perm_c: usize,
|
||||
temp_c: usize
|
||||
}
|
||||
|
||||
impl<'a> TermMarker<'a> {
|
||||
fn new() -> TermMarker<'a> {
|
||||
TermMarker { bindings: HashMap::new(),
|
||||
arg_c: 1,
|
||||
perm_c: 1,
|
||||
temp_c: 1 }
|
||||
}
|
||||
|
||||
fn contains_var(&self, var: &'a Var) -> bool {
|
||||
self.bindings.contains_key(var)
|
||||
}
|
||||
|
||||
fn get(&self, var: &'a Var) -> VarReg {
|
||||
*self.bindings.get(var).unwrap()
|
||||
}
|
||||
|
||||
fn insert(&mut self, var: &'a Var, r: VarReg) {
|
||||
self.bindings.insert(var, r);
|
||||
}
|
||||
|
||||
fn mark_non_var(&mut self, lvl: Level, cell: &Cell<RegType>) {
|
||||
let reg_type = cell.get();
|
||||
|
||||
if reg_type.reg_num() == 0 {
|
||||
match lvl {
|
||||
Level::Deep if reg_type.is_perm() => {
|
||||
let perm = self.perm_c;
|
||||
self.perm_c += 1;
|
||||
cell.set(RegType::Perm(perm));
|
||||
},
|
||||
Level::Deep => {
|
||||
let temp = self.temp_c;
|
||||
self.temp_c += 1;
|
||||
cell.set(RegType::Temp(temp));
|
||||
},
|
||||
Level::Shallow if reg_type.is_perm() => {
|
||||
let arg = self.arg_c;
|
||||
self.arg_c += 1;
|
||||
cell.set(RegType::Perm(arg));
|
||||
},
|
||||
Level::Shallow => {
|
||||
let arg = self.arg_c;
|
||||
self.arg_c += 1;
|
||||
cell.set(RegType::Temp(arg));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_old_var(&mut self, lvl: Level, var: &'a Var) -> VarReg
|
||||
{
|
||||
let reg = self.get(var);
|
||||
|
||||
match lvl {
|
||||
Level::Deep => VarReg::Norm(reg.norm()),
|
||||
Level::Shallow => {
|
||||
let reg = VarReg::ArgAndNorm(reg.norm(), self.arg_c);
|
||||
|
||||
self.arg_c += 1;
|
||||
self.insert(var, reg);
|
||||
|
||||
reg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_new_var(&mut self, lvl: Level, var: &'a Var, reg: RegType) -> VarReg
|
||||
{
|
||||
let inner_reg = if reg.is_perm() {
|
||||
let perm = self.perm_c;
|
||||
self.perm_c += 1;
|
||||
RegType::Perm(perm)
|
||||
} else {
|
||||
let temp = self.temp_c;
|
||||
self.temp_c += 1;
|
||||
RegType::Temp(temp)
|
||||
};
|
||||
|
||||
let reg = match lvl {
|
||||
Level::Deep => VarReg::Norm(inner_reg),
|
||||
Level::Shallow => {
|
||||
let reg = VarReg::ArgAndNorm(inner_reg, self.arg_c);
|
||||
self.arg_c += 1;
|
||||
reg
|
||||
}
|
||||
};
|
||||
|
||||
self.insert(var, reg);
|
||||
reg
|
||||
}
|
||||
|
||||
fn advance_at_header(&mut self, term: &'a Term) {
|
||||
self.arg_c = 1;
|
||||
self.temp_c = max(term.subterms(), self.temp_c) + 1;
|
||||
}
|
||||
|
||||
fn advance(&mut self, term: &'a Term) {
|
||||
self.arg_c = 1;
|
||||
self.temp_c = term.subterms() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum TermStatus {
|
||||
New, Old, Recurrent
|
||||
}
|
||||
|
||||
pub struct CodeGenerator<'a> {
|
||||
marker: TermMarker<'a>
|
||||
}
|
||||
|
||||
type VariableFixture<'a> = (TermStatus, Vec<&'a Cell<VarReg>>);
|
||||
type VariableFixtures<'a> = HashMap<&'a Var, VariableFixture<'a>>;
|
||||
|
||||
impl<'a> CodeGenerator<'a> {
|
||||
pub fn new() -> Self {
|
||||
CodeGenerator { marker: TermMarker::new() }
|
||||
}
|
||||
|
||||
pub fn vars(&self) -> &HashMap<&Var, VarReg> {
|
||||
&self.marker.bindings
|
||||
}
|
||||
|
||||
fn to_structure<Target>(&mut self,
|
||||
lvl: Level,
|
||||
name: &'a Atom,
|
||||
cell: &'a Cell<RegType>,
|
||||
arity: usize)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
self.marker.mark_non_var(lvl, cell);
|
||||
Target::to_structure(lvl, name.clone(), arity, cell.get())
|
||||
}
|
||||
|
||||
fn var_term<Target>(&mut self,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
var: &'a Var)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
if !self.marker.contains_var(var) {
|
||||
let reg = self.marker.mark_new_var(lvl, var, cell.get().norm());
|
||||
cell.set(reg);
|
||||
|
||||
match reg {
|
||||
VarReg::ArgAndNorm(arg, norm) =>
|
||||
Target::argument_to_variable(arg, norm),
|
||||
VarReg::Norm(norm) =>
|
||||
Target::subterm_to_variable(norm)
|
||||
}
|
||||
} else {
|
||||
let reg = self.marker.mark_old_var(lvl, var);
|
||||
cell.set(reg);
|
||||
|
||||
match reg {
|
||||
VarReg::ArgAndNorm(arg, norm) =>
|
||||
Target::argument_to_value(arg, norm),
|
||||
VarReg::Norm(norm) =>
|
||||
Target::subterm_to_value(norm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn non_var_subterm<Target>(&mut self, cell: &'a Cell<RegType>) -> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
self.marker.mark_non_var(Level::Deep, cell);
|
||||
Target::clause_arg_to_instr(cell.get())
|
||||
}
|
||||
|
||||
fn subterm_to_instr<Target>(&mut self, subterm: &'a Term) -> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
match subterm {
|
||||
&Term::Atom(ref cell, _) | &Term::Clause(ref cell, _, _) =>
|
||||
self.non_var_subterm(cell),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
self.var_term(Level::Deep, cell, var)
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_target<Target>(&mut self, term: &'a Term) -> Vec<Target>
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
let iter = Target::iter(term);
|
||||
let mut target = Vec::new();
|
||||
|
||||
for term in iter {
|
||||
match term {
|
||||
TermRef::Atom(lvl, term, atom) =>
|
||||
target.push(self.to_structure(lvl, atom, term, 0)),
|
||||
TermRef::Clause(lvl, term, atom, terms) => {
|
||||
target.push(self.to_structure(lvl, atom, term, terms.len()));
|
||||
|
||||
for subterm in terms {
|
||||
target.push(self.subterm_to_instr(subterm.as_ref()));
|
||||
}
|
||||
},
|
||||
TermRef::Var(lvl @ Level::Shallow, ref cell, ref var) =>
|
||||
target.push(self.var_term(lvl, cell, var)),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
target
|
||||
}
|
||||
|
||||
fn mark_vars_in_term<Iter>(iter: Iter, vs: &mut VariableFixtures<'a>)
|
||||
where Iter : Iterator<Item=TermRef<'a>>
|
||||
{
|
||||
for term in iter {
|
||||
if let TermRef::Var(_, reg_cell, var) = term {
|
||||
let mut status = vs.entry(var)
|
||||
.or_insert((TermStatus::New, Vec::new()));
|
||||
|
||||
status.1.push(reg_cell);
|
||||
|
||||
match status.0 {
|
||||
TermStatus::Old => status.0 = TermStatus::Recurrent,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for &mut (ref mut term_status, ref mut cb) in vs.values_mut() {
|
||||
match *term_status {
|
||||
TermStatus::New => *term_status = TermStatus::Old,
|
||||
TermStatus::Recurrent => {
|
||||
for cell_reg in cb.drain(0..) {
|
||||
cell_reg.set(VarReg::Norm(RegType::Perm(0)));
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_perm_vars(rule: &'a Rule) -> VariableFixtures {
|
||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||
let mut vfs = HashMap::new();
|
||||
|
||||
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
|
||||
|
||||
Self::mark_vars_in_term(iter, &mut vfs);
|
||||
|
||||
for term in clauses {
|
||||
Self::mark_vars_in_term(term.breadth_first_iter(), &mut vfs);
|
||||
}
|
||||
|
||||
vfs
|
||||
}
|
||||
|
||||
fn add_conditional_call(compiled_query: &mut Code, term: &Term) {
|
||||
match term {
|
||||
&Term::Atom(_, ref atom) => {
|
||||
let call = ControlInstruction::Call(atom.clone(), 0);
|
||||
compiled_query.push(Line::Control(call));
|
||||
},
|
||||
&Term::Clause(_, ref atom, ref terms) => {
|
||||
let call = ControlInstruction::Call(atom.clone(), terms.len());
|
||||
compiled_query.push(Line::Control(call));
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
|
||||
let vfs = Self::mark_perm_vars(&rule);
|
||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||
let mut perm_vars = 0;
|
||||
|
||||
for &(term_status, _) in vfs.values() {
|
||||
if let TermStatus::Recurrent = term_status {
|
||||
perm_vars += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut body = Vec::new();
|
||||
|
||||
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
|
||||
|
||||
self.marker.advance(p0);
|
||||
body.push(Line::Fact(self.compile_target(p0)));
|
||||
|
||||
self.marker.advance_at_header(p1);
|
||||
body.push(Line::Query(self.compile_target(p1)));
|
||||
Self::add_conditional_call(&mut body, p1);
|
||||
|
||||
body = clauses.iter()
|
||||
.map(|ref term| self.compile_query(term))
|
||||
.fold(body, |mut body, ref mut cqs| {
|
||||
body.append(cqs);
|
||||
body
|
||||
});
|
||||
|
||||
body.push(Line::Control(ControlInstruction::Deallocate));
|
||||
|
||||
body
|
||||
}
|
||||
|
||||
pub fn compile_fact(&mut self, term: &'a Term) -> Code {
|
||||
self.marker.advance(term);
|
||||
|
||||
let mut compiled_fact = vec![Line::Fact(self.compile_target(term))];
|
||||
let proceed = Line::Control(ControlInstruction::Proceed);
|
||||
|
||||
compiled_fact.push(proceed);
|
||||
compiled_fact
|
||||
}
|
||||
|
||||
pub fn compile_query(&mut self, term: &'a Term) -> Code {
|
||||
self.marker.advance(term);
|
||||
|
||||
let mut compiled_query = vec![Line::Query(self.compile_target(term))];
|
||||
Self::add_conditional_call(&mut compiled_query, term);
|
||||
|
||||
compiled_query
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
use l2::ast::*;
|
||||
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HeapCellView<'a> {
|
||||
Str(usize, &'a Atom),
|
||||
Var(usize)
|
||||
}
|
||||
|
||||
pub struct HeapCellViewer<'a> {
|
||||
heap: &'a Heap,
|
||||
state_stack: Vec<(usize, &'a HeapCellValue)>
|
||||
}
|
||||
|
||||
impl<'a> HeapCellViewer<'a> {
|
||||
pub fn new(heap: &'a Heap, focus: usize) -> Self {
|
||||
HeapCellViewer {
|
||||
heap: heap,
|
||||
state_stack: vec![(focus, &heap[focus])]
|
||||
}
|
||||
}
|
||||
|
||||
fn follow(&self, value: &'a HeapCellValue) -> &'a HeapCellValue {
|
||||
match value {
|
||||
&HeapCellValue::NamedStr(_, _) => value,
|
||||
&HeapCellValue::Ref(cell_num) | &HeapCellValue::Str(cell_num) =>
|
||||
&self.heap[cell_num],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for HeapCellViewer<'a> {
|
||||
type Item = HeapCellView<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(hcv) = self.state_stack.pop() {
|
||||
match hcv {
|
||||
(focus, &HeapCellValue::NamedStr(arity, ref name)) => {
|
||||
for i in (1 .. arity + 1).rev() {
|
||||
self.state_stack.push((focus + i, &self.heap[focus + i]));
|
||||
}
|
||||
|
||||
return Some(HeapCellView::Str(arity, name));
|
||||
},
|
||||
(_, &HeapCellValue::Ref(cell_num)) => {
|
||||
let new_hcv = self.follow(hcv.1);
|
||||
|
||||
if hcv.1 == new_hcv {
|
||||
return Some(HeapCellView::Var(cell_num));
|
||||
} else {
|
||||
self.state_stack.push((cell_num, new_hcv));
|
||||
}
|
||||
},
|
||||
(_, &HeapCellValue::Str(cell_num)) => {
|
||||
let new_hcv = self.follow(hcv.1);
|
||||
self.state_stack.push((cell_num, new_hcv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
use l2::ast::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::vec::Vec;
|
||||
|
||||
enum IteratorState<'a> {
|
||||
Atom(Level, &'a Cell<RegType>, &'a Atom),
|
||||
Clause(Level, usize, &'a Cell<RegType>, &'a Atom, &'a Vec<Box<Term>>),
|
||||
IsolatedAtom(&'a Cell<RegType>, &'a Atom),
|
||||
IsolatedVar(&'a Cell<VarReg>, &'a Var),
|
||||
RootClause(usize, &'a Vec<Box<Term>>),
|
||||
Var(Level, &'a Cell<VarReg>, &'a Var)
|
||||
}
|
||||
|
||||
impl<'a> IteratorState<'a> {
|
||||
fn to_state(lvl: Level, term: &'a Term) -> IteratorState<'a>
|
||||
{
|
||||
match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
IteratorState::Atom(lvl, cell, atom),
|
||||
&Term::Clause(ref cell, ref atom, ref child_terms) =>
|
||||
IteratorState::Clause(lvl, 0, cell, atom, child_terms),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
IteratorState::Var(lvl, cell, var)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryIterator<'a> {
|
||||
state_stack: Vec<IteratorState<'a>>
|
||||
}
|
||||
|
||||
impl<'a> QueryIterator<'a> {
|
||||
fn push_clause(&mut self,
|
||||
lvl: Level,
|
||||
child_num: usize,
|
||||
cell: &'a Cell<RegType>,
|
||||
name: &'a Atom,
|
||||
child_terms: &'a Vec<Box<Term>>)
|
||||
{
|
||||
self.state_stack.push(IteratorState::Clause(lvl,
|
||||
child_num,
|
||||
cell,
|
||||
name,
|
||||
child_terms));
|
||||
}
|
||||
|
||||
fn push_root_clause(&mut self,
|
||||
child_num: usize,
|
||||
child_terms: &'a Vec<Box<Term>>)
|
||||
{
|
||||
self.state_stack.push(IteratorState::RootClause(child_num, child_terms));
|
||||
}
|
||||
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_stack.push(IteratorState::to_state(lvl, term));
|
||||
}
|
||||
|
||||
fn new(term: &'a Term) -> QueryIterator<'a> {
|
||||
let state = match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
IteratorState::IsolatedAtom(cell, atom),
|
||||
&Term::Clause(_, _, ref terms) =>
|
||||
IteratorState::RootClause(0, terms),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
IteratorState::IsolatedVar(cell, var)
|
||||
};
|
||||
|
||||
QueryIterator { state_stack: vec![state] }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for QueryIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(iter_state) = self.state_stack.pop() {
|
||||
match iter_state {
|
||||
IteratorState::Atom(lvl, cell, atom) =>
|
||||
return Some(TermRef::Atom(lvl, cell, atom)),
|
||||
IteratorState::Clause(lvl, child_num, cell, atom, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
return Some(TermRef::Clause(lvl, cell, atom, child_terms));
|
||||
} else {
|
||||
self.push_clause(lvl, child_num + 1, cell, atom, child_terms);
|
||||
self.push_subterm(Level::Deep, child_terms[child_num].as_ref());
|
||||
}
|
||||
},
|
||||
IteratorState::IsolatedAtom(cell, atom) =>
|
||||
return Some(TermRef::Atom(Level::Shallow, cell, atom)),
|
||||
IteratorState::IsolatedVar(cell, var) =>
|
||||
return Some(TermRef::Var(Level::Shallow, cell, var)),
|
||||
IteratorState::RootClause(child_num, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
return None;
|
||||
} else {
|
||||
self.push_root_clause(child_num + 1, child_terms);
|
||||
self.push_subterm(Level::Shallow, child_terms[child_num].as_ref());
|
||||
}
|
||||
},
|
||||
IteratorState::Var(lvl, cell, var) =>
|
||||
return Some(TermRef::Var(lvl, cell, var))
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FactIterator<'a> {
|
||||
state_queue: VecDeque<IteratorState<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> FactIterator<'a> {
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_queue.push_back(IteratorState::to_state(lvl, term));
|
||||
}
|
||||
|
||||
fn new(term: &'a Term) -> FactIterator<'a> {
|
||||
let states = match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
vec![IteratorState::IsolatedAtom(cell, atom)],
|
||||
&Term::Clause(_, _, ref terms) =>
|
||||
vec![IteratorState::RootClause(0, terms)],
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
vec![IteratorState::IsolatedVar(cell, var)]
|
||||
};
|
||||
|
||||
FactIterator { state_queue: VecDeque::from(states) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FactIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(state) = self.state_queue.pop_front() {
|
||||
match state {
|
||||
IteratorState::Atom(lvl, cell, atom) =>
|
||||
return Some(TermRef::Atom(lvl, cell, atom)),
|
||||
IteratorState::Clause(lvl, _, cell, atom, child_terms) => {
|
||||
for child_term in child_terms {
|
||||
self.push_subterm(Level::Deep, child_term);
|
||||
}
|
||||
|
||||
return Some(TermRef::Clause(lvl, cell, atom, child_terms));
|
||||
},
|
||||
IteratorState::IsolatedAtom(cell, atom) =>
|
||||
return Some(TermRef::Atom(Level::Shallow, cell, atom)),
|
||||
IteratorState::IsolatedVar(cell, var) =>
|
||||
return Some(TermRef::Var(Level::Shallow, cell, var)),
|
||||
IteratorState::RootClause(_, child_terms) => {
|
||||
for child_term in child_terms {
|
||||
self.push_subterm(Level::Shallow, child_term);
|
||||
}
|
||||
},
|
||||
IteratorState::Var(lvl, cell, var) =>
|
||||
return Some(TermRef::Var(lvl, cell, var))
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn post_order_iter(&self) -> QueryIterator {
|
||||
QueryIterator::new(self)
|
||||
}
|
||||
|
||||
pub fn breadth_first_iter(&self) -> FactIterator {
|
||||
FactIterator::new(self)
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
use l2::ast::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
grammar;
|
||||
|
||||
pub TopLevel: TopLevel = {
|
||||
"?-" <t:Term> "." => TopLevel::Query(t),
|
||||
<r:Rule> "." => TopLevel::Rule(r),
|
||||
<t:Term> "." => TopLevel::Fact(t),
|
||||
};
|
||||
|
||||
Atom : Atom = {
|
||||
r"[a-z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
|
||||
BoxedTerm : Box<Term> = {
|
||||
<t:Term> => Box::new(t),
|
||||
};
|
||||
|
||||
Clause : Term = {
|
||||
<a:Atom> "(" <ts: (<BoxedTerm> ",")*> <t:BoxedTerm> ")" => {
|
||||
let mut ts = ts;
|
||||
ts.push(t);
|
||||
Term::Clause(Cell::new(RegType::Temp(0)), a, ts)
|
||||
},
|
||||
};
|
||||
|
||||
Rule : Rule = {
|
||||
<c:Clause> ":-" <h:Term> <cs: ("," <Term>)*> =>
|
||||
Rule { head: (c, h), clauses: cs },
|
||||
<a:Atom> ":-" <h:Term> <cs: ("," <Term>)*> =>
|
||||
Rule { head: (Term::Atom(Cell::new(RegType::Temp(0)), a), h),
|
||||
clauses: cs }
|
||||
};
|
||||
|
||||
Term : Term = {
|
||||
<Clause> => <>,
|
||||
<Atom> => Term::Atom(Cell::new(RegType::Temp(0)), <>),
|
||||
<Var> => Term::Var(Cell::new(VarReg::Norm(RegType::Temp(0))), <>),
|
||||
};
|
||||
|
||||
Var : Var = {
|
||||
r"[A-Z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
1883
src/l2/l2_parser.rs
1883
src/l2/l2_parser.rs
File diff suppressed because it is too large
Load Diff
@@ -1,456 +0,0 @@
|
||||
use l2::ast::*;
|
||||
use l2::codegen::*;
|
||||
use l2::heapview::*;
|
||||
use l2::stack::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum MachineMode {
|
||||
Read,
|
||||
Write
|
||||
}
|
||||
|
||||
struct MachineState {
|
||||
h: usize,
|
||||
s: usize,
|
||||
p: CodePtr,
|
||||
cp: CodePtr,
|
||||
fail: bool,
|
||||
heap: Heap,
|
||||
mode: MachineMode,
|
||||
stack: Stack,
|
||||
registers: Registers
|
||||
}
|
||||
|
||||
type CodeDir = HashMap<(Atom, usize), usize>;
|
||||
|
||||
pub struct Machine {
|
||||
ms: MachineState,
|
||||
code: Code,
|
||||
code_dir: CodeDir
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new() -> Self {
|
||||
Machine {
|
||||
ms: MachineState::new(),
|
||||
code: Vec::new(),
|
||||
code_dir: HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn failed(&self) -> bool {
|
||||
self.ms.fail
|
||||
}
|
||||
|
||||
pub fn add_fact(&mut self, fact: &Term, mut code: Code) {
|
||||
let p = self.code.len();
|
||||
let name = fact.name().clone();
|
||||
let arity = fact.arity();
|
||||
|
||||
self.code.append(&mut code);
|
||||
self.code_dir.insert((name, arity), p);
|
||||
}
|
||||
|
||||
pub fn add_rule(&mut self, rule: &Rule, mut code: Code) {
|
||||
let p = self.code.len();
|
||||
let name = rule.head.0.name().clone();
|
||||
let arity = rule.head.0.arity();
|
||||
|
||||
self.code.append(&mut code);
|
||||
self.code_dir.insert((name, arity), p);
|
||||
}
|
||||
|
||||
fn execute_instr(&mut self, instr: &Line) -> bool {
|
||||
let mut instr = instr;
|
||||
|
||||
loop {
|
||||
match instr {
|
||||
&Line::Fact(ref fact) => {
|
||||
for fact_instr in fact {
|
||||
self.ms.execute_fact_instr(&fact_instr);
|
||||
}
|
||||
self.ms.p += 1;
|
||||
},
|
||||
&Line::Query(ref query) => {
|
||||
for query_instr in query {
|
||||
self.ms.execute_query_instr(&query_instr);
|
||||
}
|
||||
self.ms.p += 1;
|
||||
},
|
||||
&Line::Control(ref control_instr) =>
|
||||
self.ms.execute_ctrl_instr(&self.code_dir, control_instr),
|
||||
}
|
||||
|
||||
if self.failed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
match self.ms.p {
|
||||
CodePtr::DirEntry(p) if p < self.code.len() =>
|
||||
instr = &self.code[p],
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn heap_view(&self, var_dir: HashMap<&Var, HeapCellRef>) -> String {
|
||||
let mut result = String::new();
|
||||
|
||||
for (var, hcr) in var_dir {
|
||||
let mut arities = Vec::new();
|
||||
let viewer = HeapCellViewer::new(&self.ms.heap, hcr.heap_offset());
|
||||
|
||||
if result != "" {
|
||||
result += "\n";
|
||||
}
|
||||
|
||||
result += var.as_str();
|
||||
result += " = ";
|
||||
|
||||
for view in viewer {
|
||||
match arities.pop() {
|
||||
Some(n) => arities.push(n-1),
|
||||
None => {}
|
||||
}
|
||||
|
||||
if !(arities.is_empty() || result.ends_with("(")) {
|
||||
result += ", ";
|
||||
}
|
||||
|
||||
match view {
|
||||
HeapCellView::Str(arity, ref name) => {
|
||||
result += name.as_str();
|
||||
|
||||
if arity > 0 {
|
||||
arities.push(arity);
|
||||
result += "(";
|
||||
}
|
||||
},
|
||||
HeapCellView::Var(cell_num) => {
|
||||
result += "_";
|
||||
result += cell_num.to_string().as_str();
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(&0) = arities.last() {
|
||||
result += ")";
|
||||
arities.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn run_query(&mut self, code: Code, cg: &CodeGenerator) -> Option<String>
|
||||
{
|
||||
let mut succeeded = true;
|
||||
|
||||
for instr in code.iter().take(1) {
|
||||
succeeded = self.execute_instr(&instr);
|
||||
}
|
||||
|
||||
let mut heap_locs = HashMap::new();
|
||||
|
||||
if succeeded {
|
||||
for (var, vr) in cg.vars() {
|
||||
let hcr = self.ms.registers[vr.root_register()];
|
||||
heap_locs.insert(*var, hcr);
|
||||
}
|
||||
|
||||
for instr in code.iter().skip(1) {
|
||||
succeeded = self.execute_instr(&instr);
|
||||
if !succeeded {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if succeeded {
|
||||
Some(self.heap_view(heap_locs))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.ms.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineState {
|
||||
fn new() -> MachineState {
|
||||
MachineState { h: 0,
|
||||
s: 0,
|
||||
p: CodePtr::TopLevel,
|
||||
cp: CodePtr::TopLevel,
|
||||
fail: false,
|
||||
heap: Vec::with_capacity(256),
|
||||
mode: MachineMode::Write,
|
||||
stack: Stack::new(),
|
||||
registers: vec![HeapCellRef::Ref(0); 32] }
|
||||
}
|
||||
|
||||
fn register_mut(&mut self, r: RegType) -> &mut HeapCellRef {
|
||||
match r {
|
||||
RegType::Temp(r) => &mut self.registers[r],
|
||||
RegType::Perm(r) => &mut self.stack[r]
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup(&self, a: Addr) -> HeapCellRef {
|
||||
match a {
|
||||
Addr::HeapCell(r) => self.heap[r].as_ref(r),
|
||||
Addr::RegNum(r) => self.registers[r],
|
||||
Addr::StackCell(s) => self.stack[s]
|
||||
}
|
||||
}
|
||||
|
||||
fn deref(&self, a: Addr) -> Addr {
|
||||
let mut a = a;
|
||||
|
||||
loop {
|
||||
if let HeapCellRef::Ref(value) = self.lookup(a) {
|
||||
if let Addr::HeapCell(av) = a {
|
||||
if value != av {
|
||||
a = Addr::HeapCell(value);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
a = Addr::HeapCell(value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
};
|
||||
}
|
||||
|
||||
fn is_unbound(hc: &HeapCellValue, index: usize) -> bool {
|
||||
match hc {
|
||||
&HeapCellValue::Ref(r) => r == index,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
fn bind(&mut self, a: Addr, val: usize) {
|
||||
let mut a = a;
|
||||
|
||||
loop {
|
||||
match a {
|
||||
addr @ Addr::RegNum(_) | addr @ Addr::StackCell(_) => {
|
||||
if let HeapCellRef::Ref(hc) = self.lookup(addr) {
|
||||
a = Addr::HeapCell(hc);
|
||||
} else if Self::is_unbound(&self.heap[val], val) {
|
||||
self.heap[val] = HeapCellValue::from(self.lookup(addr));
|
||||
break;
|
||||
} else {
|
||||
self.fail = true;
|
||||
break;
|
||||
}
|
||||
},
|
||||
Addr::HeapCell(hc) => {
|
||||
if Self::is_unbound(&self.heap[hc], hc) {
|
||||
self.heap[hc] = HeapCellValue::Ref(val);
|
||||
break;
|
||||
} else if Self::is_unbound(&self.heap[val], val) {
|
||||
self.heap[val] = HeapCellValue::Ref(hc);
|
||||
break;
|
||||
} else {
|
||||
self.fail = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn unify(&mut self, a1: Addr, a2: Addr) {
|
||||
let mut pdl = vec![a1, a2];
|
||||
|
||||
self.fail = false;
|
||||
|
||||
while !(pdl.is_empty() || self.fail) {
|
||||
let d1 = self.deref(pdl.pop().unwrap());
|
||||
let d2 = self.deref(pdl.pop().unwrap());
|
||||
|
||||
if d1 != d2 {
|
||||
match (self.lookup(d1), self.lookup(d2)) {
|
||||
(HeapCellRef::Ref(hc), _) =>
|
||||
self.bind(d2, hc),
|
||||
(_, HeapCellRef::Ref(hc)) =>
|
||||
self.bind(d1, hc),
|
||||
(HeapCellRef::Str(a1), HeapCellRef::Str(a2)) => {
|
||||
let r1 = &self.heap[a1];
|
||||
let r2 = &self.heap[a2];
|
||||
|
||||
if let &HeapCellValue::NamedStr(n1, ref f1) = r1 {
|
||||
if let &HeapCellValue::NamedStr(n2, ref f2) = r2 {
|
||||
if n1 == n2 && *f1 == *f2 {
|
||||
for i in 1 .. n1 + 1 {
|
||||
pdl.push(Addr::HeapCell(a1 + i));
|
||||
pdl.push(Addr::HeapCell(a2 + i));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.fail = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_query_instr(&mut self, instr: &QueryInstruction) {
|
||||
match instr {
|
||||
&QueryInstruction::PutStructure(_, ref name, arity, reg) => {
|
||||
self.heap.push(HeapCellValue::Str(self.h + 1));
|
||||
self.heap.push(HeapCellValue::NamedStr(arity, name.clone()));
|
||||
|
||||
*self.register_mut(reg) = HeapCellRef::Str(self.h + 1);
|
||||
|
||||
self.h += 2;
|
||||
},
|
||||
&QueryInstruction::PutValue(norm, arg) =>
|
||||
self.registers[arg] = match norm {
|
||||
RegType::Temp(reg) => self.registers[reg],
|
||||
RegType::Perm(reg) => self.stack[reg]
|
||||
},
|
||||
&QueryInstruction::PutVariable(norm, arg) => {
|
||||
self.heap.push(HeapCellValue::Ref(self.h));
|
||||
|
||||
*self.register_mut(norm) = HeapCellRef::Ref(self.h);
|
||||
self.registers[arg] = HeapCellRef::Ref(self.h);
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
&QueryInstruction::SetVariable(reg) => {
|
||||
self.heap.push(HeapCellValue::Ref(self.h));
|
||||
*self.register_mut(reg) = HeapCellRef::Ref(self.h);
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
&QueryInstruction::SetValue(reg) => {
|
||||
let heap_val = self.lookup(Addr::from(reg));
|
||||
self.heap.push(HeapCellValue::from(heap_val));
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_fact_instr(&mut self, instr: &FactInstruction) {
|
||||
match instr {
|
||||
&FactInstruction::GetStructure(_, ref name, arity, reg) => {
|
||||
let addr = self.deref(Addr::from(reg));
|
||||
|
||||
match self.lookup(addr) {
|
||||
HeapCellRef::Str(a) => {
|
||||
let result = &self.heap[a];
|
||||
|
||||
if let &HeapCellValue::NamedStr(narity, ref str) = result {
|
||||
if narity == arity && *name == *str {
|
||||
self.s = a + 1;
|
||||
self.mode = MachineMode::Read;
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
HeapCellRef::Ref(_) => {
|
||||
self.heap.push(HeapCellValue::Str(self.h + 1));
|
||||
self.heap.push(HeapCellValue::NamedStr(arity, name.clone()));
|
||||
|
||||
let h = self.h;
|
||||
|
||||
self.bind(addr, h);
|
||||
|
||||
self.h += 2;
|
||||
self.mode = MachineMode::Write;
|
||||
}
|
||||
};
|
||||
},
|
||||
&FactInstruction::GetVariable(norm, arg) =>
|
||||
*self.register_mut(norm) = self.registers[arg],
|
||||
&FactInstruction::GetValue(norm, arg) =>
|
||||
self.unify(Addr::from(norm), Addr::RegNum(arg)),
|
||||
&FactInstruction::UnifyVariable(reg) => {
|
||||
match self.mode {
|
||||
MachineMode::Read =>
|
||||
*self.register_mut(reg) = self.heap[self.s].as_ref(self.s),
|
||||
MachineMode::Write => {
|
||||
self.heap.push(HeapCellValue::Ref(self.h));
|
||||
*self.register_mut(reg) = HeapCellRef::Ref(self.h);
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
},
|
||||
&FactInstruction::UnifyValue(reg) => {
|
||||
let s = self.s;
|
||||
|
||||
match self.mode {
|
||||
MachineMode::Read =>
|
||||
self.unify(Addr::from(reg), Addr::HeapCell(s)),
|
||||
MachineMode::Write => {
|
||||
let heap_val = self.lookup(Addr::from(reg));
|
||||
self.heap.push(HeapCellValue::from(heap_val));
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
|
||||
{
|
||||
match instr {
|
||||
&ControlInstruction::Allocate(num_cells) => {
|
||||
self.stack.push(self.cp, num_cells);
|
||||
self.p += 1;
|
||||
},
|
||||
&ControlInstruction::Call(ref name, arity) => {
|
||||
let compiled_tl_index = code_dir.get(&(name.clone(), arity))
|
||||
.map(|index| *index);
|
||||
|
||||
match compiled_tl_index {
|
||||
Some(compiled_tl_index) => {
|
||||
self.cp = self.p + 1;
|
||||
self.p = CodePtr::DirEntry(compiled_tl_index);
|
||||
},
|
||||
None => self.fail = true
|
||||
};
|
||||
},
|
||||
&ControlInstruction::Deallocate => {
|
||||
self.p = self.stack.get_cp();
|
||||
self.stack.pop();
|
||||
},
|
||||
&ControlInstruction::Proceed =>
|
||||
self.p = self.cp,
|
||||
};
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.h = 0;
|
||||
self.s = 0;
|
||||
self.p = CodePtr::TopLevel;
|
||||
self.cp = CodePtr::TopLevel;
|
||||
|
||||
self.fail = false;
|
||||
self.heap.clear();
|
||||
self.mode = MachineMode::Write;
|
||||
self.stack = Stack::new();
|
||||
self.registers = vec![HeapCellRef::Ref(0); 32];
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
pub mod ast;
|
||||
pub mod heapview;
|
||||
pub mod iterators;
|
||||
pub mod l2_parser;
|
||||
pub mod codegen;
|
||||
pub mod machine;
|
||||
pub mod stack;
|
||||
@@ -1,60 +0,0 @@
|
||||
use l2::ast::*;
|
||||
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::vec::Vec;
|
||||
|
||||
struct Frame {
|
||||
cp: CodePtr,
|
||||
perms: Vec<HeapCellRef>
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
fn new(cp: CodePtr, n: usize) -> Self {
|
||||
Frame {
|
||||
cp: cp,
|
||||
perms: vec![HeapCellRef::Ref(0); n]
|
||||
}
|
||||
}
|
||||
|
||||
fn read_pv(&self, i: usize) -> &HeapCellRef {
|
||||
self.perms.index(i)
|
||||
}
|
||||
|
||||
fn read_pv_mut(&mut self, i: usize) -> &mut HeapCellRef {
|
||||
self.perms.index_mut(i)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Stack(Vec<Frame>);
|
||||
|
||||
impl Stack {
|
||||
pub fn new() -> Self {
|
||||
Stack(Vec::new())
|
||||
}
|
||||
|
||||
pub fn push(&mut self, cp: CodePtr, n: usize) {
|
||||
self.0.push(Frame::new(cp, n));
|
||||
}
|
||||
|
||||
pub fn get_cp(&self) -> CodePtr {
|
||||
self.0.last().unwrap().cp
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) {
|
||||
self.0.pop();
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Stack {
|
||||
type Output = HeapCellRef;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.0.last().unwrap().read_pv(index - 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Stack {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.0.last_mut().unwrap().read_pv_mut(index - 1)
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
use l3::ast::*;
|
||||
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::vec::Vec;
|
||||
|
||||
pub struct Frame {
|
||||
pub global_index: usize,
|
||||
pub e: usize,
|
||||
pub cp: CodePtr,
|
||||
perms: Vec<Addr>
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
fn new(global_index: usize, e: usize, cp: CodePtr, n: usize) -> Self {
|
||||
Frame {
|
||||
global_index: global_index,
|
||||
e: e,
|
||||
cp: cp,
|
||||
perms: vec![Addr::HeapCell(0); n]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AndStack(Vec<Frame>);
|
||||
|
||||
impl AndStack {
|
||||
pub fn new() -> Self {
|
||||
AndStack(Vec::new())
|
||||
}
|
||||
|
||||
pub fn push(&mut self, global_index: usize, e: usize, cp: CodePtr, n: usize) {
|
||||
self.0.push(Frame::new(global_index, e, cp, n));
|
||||
}
|
||||
|
||||
pub fn top(&self) -> Option<&Frame> {
|
||||
self.0.last()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear()
|
||||
}
|
||||
|
||||
// drop the last n frames.
|
||||
pub fn drop_frames(&mut self, n: usize) {
|
||||
let len = self.0.len();
|
||||
self.0.truncate(len - n);
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for AndStack {
|
||||
type Output = Frame;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.0.index(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for AndStack {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.0.index_mut(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Frame {
|
||||
type Output = Addr;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.perms.index(index - 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Frame {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.perms.index_mut(index - 1)
|
||||
}
|
||||
}
|
||||
303
src/l3/ast.rs
303
src/l3/ast.rs
@@ -1,303 +0,0 @@
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Add, AddAssign};
|
||||
use std::vec::Vec;
|
||||
|
||||
pub type Var = String;
|
||||
|
||||
pub type Atom = String;
|
||||
|
||||
pub enum PredicateClause {
|
||||
Fact(Term),
|
||||
Rule(Rule)
|
||||
}
|
||||
|
||||
impl PredicateClause {
|
||||
pub fn name(&self) -> &Atom {
|
||||
match self {
|
||||
&PredicateClause::Fact(ref t) => t.name(),
|
||||
&PredicateClause::Rule(ref rule) => rule.head.0.name()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arity(&self) -> usize {
|
||||
match self {
|
||||
&PredicateClause::Fact(ref t) => t.arity(),
|
||||
&PredicateClause::Rule(ref rule) => rule.head.0.arity()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TopLevel {
|
||||
Fact(Term),
|
||||
Predicate(Vec<PredicateClause>),
|
||||
Query(Term),
|
||||
Rule(Rule)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Level {
|
||||
Deep, Shallow
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum RegType {
|
||||
Perm(usize),
|
||||
Temp(usize)
|
||||
}
|
||||
|
||||
impl RegType {
|
||||
pub fn reg_num(self) -> usize {
|
||||
match self {
|
||||
RegType::Perm(reg_num) | RegType::Temp(reg_num) => reg_num
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_perm(self) -> bool {
|
||||
match self {
|
||||
RegType::Perm(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum VarReg {
|
||||
ArgAndNorm(RegType, usize),
|
||||
Norm(RegType)
|
||||
}
|
||||
|
||||
impl VarReg {
|
||||
pub fn norm(self) -> RegType {
|
||||
match self {
|
||||
VarReg::ArgAndNorm(reg, _) | VarReg::Norm(reg) => reg
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_register(self) -> usize {
|
||||
match self {
|
||||
VarReg::ArgAndNorm(_, root) => root,
|
||||
VarReg::Norm(root) => root.reg_num()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Term {
|
||||
Atom(Cell<RegType>, Atom),
|
||||
Clause(Cell<RegType>, Atom, Vec<Box<Term>>),
|
||||
Var(Cell<VarReg>, Var)
|
||||
}
|
||||
|
||||
pub struct Rule {
|
||||
pub head: (Term, Term),
|
||||
pub clauses: Vec<Term>
|
||||
}
|
||||
|
||||
pub enum TermRef<'a> {
|
||||
Atom(Level, &'a Cell<RegType>, &'a Atom),
|
||||
Clause(Level, &'a Cell<RegType>, &'a Atom, &'a Vec<Box<Term>>),
|
||||
Var(Level, &'a Cell<VarReg>, &'a Var)
|
||||
}
|
||||
|
||||
pub enum FactInstruction {
|
||||
GetStructure(Level, Atom, usize, RegType),
|
||||
GetValue(RegType, usize),
|
||||
GetVariable(RegType, usize),
|
||||
UnifyVariable(RegType),
|
||||
UnifyValue(RegType)
|
||||
}
|
||||
|
||||
pub enum QueryInstruction {
|
||||
PutStructure(Level, Atom, usize, RegType),
|
||||
PutValue(RegType, usize),
|
||||
PutVariable(RegType, usize),
|
||||
SetVariable(RegType),
|
||||
SetValue(RegType)
|
||||
}
|
||||
|
||||
pub enum ChoiceInstruction {
|
||||
RetryMeElse(usize),
|
||||
TrustMe,
|
||||
TryMeElse(usize)
|
||||
}
|
||||
|
||||
pub enum ControlInstruction {
|
||||
Allocate(usize),
|
||||
Call(Atom, usize),
|
||||
Deallocate,
|
||||
Proceed
|
||||
}
|
||||
|
||||
pub type CompiledFact = Vec<FactInstruction>;
|
||||
|
||||
pub type CompiledQuery = Vec<QueryInstruction>;
|
||||
|
||||
pub enum Line {
|
||||
Choice(ChoiceInstruction),
|
||||
Control(ControlInstruction),
|
||||
Fact(CompiledFact),
|
||||
Query(CompiledQuery)
|
||||
}
|
||||
|
||||
pub enum LineOrCodeOffset<'a> {
|
||||
Instruction(&'a Line),
|
||||
Offset(usize)
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Line> for LineOrCodeOffset<'a> {
|
||||
fn from(line: &'a Line) -> Self {
|
||||
LineOrCodeOffset::Instruction(line)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Code = Vec<Line>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Addr {
|
||||
HeapCell(usize),
|
||||
StackCell(usize, usize),
|
||||
Str(usize)
|
||||
}
|
||||
|
||||
impl Addr {
|
||||
pub fn is_ref(self) -> bool {
|
||||
match self {
|
||||
Addr::HeapCell(_) | Addr::StackCell(_, _) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ref(self) -> Option<Ref> {
|
||||
match self {
|
||||
Addr::HeapCell(hc) => Some(Ref::HeapCell(hc)),
|
||||
Addr::StackCell(fr, sc) => Some(Ref::StackCell(fr, sc)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ref> for Addr {
|
||||
fn from(r: Ref) -> Self {
|
||||
match r {
|
||||
Ref::HeapCell(hc) => Addr::HeapCell(hc),
|
||||
Ref::StackCell(fr, sc) => Addr::StackCell(fr, sc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Ref {
|
||||
HeapCell(usize),
|
||||
StackCell(usize, usize)
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum HeapCellValue {
|
||||
NamedStr(usize, Atom),
|
||||
Ref(Ref),
|
||||
Str(usize)
|
||||
}
|
||||
|
||||
impl From<Addr> for HeapCellValue {
|
||||
fn from(addr: Addr) -> HeapCellValue {
|
||||
match addr {
|
||||
Addr::HeapCell(hc) =>
|
||||
HeapCellValue::Ref(Ref::HeapCell(hc)),
|
||||
Addr::StackCell(fr, sc) =>
|
||||
HeapCellValue::Ref(Ref::StackCell(fr, sc)),
|
||||
Addr::Str(hc) =>
|
||||
HeapCellValue::Str(hc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapCellValue {
|
||||
pub fn as_addr(&self, focus: usize) -> Addr {
|
||||
match self {
|
||||
&HeapCellValue::Ref(r) => Addr::from(r),
|
||||
&HeapCellValue::Str(s) => Addr::Str(s),
|
||||
&HeapCellValue::NamedStr(_, _) => Addr::Str(focus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CodePtr {
|
||||
DirEntry(usize),
|
||||
TopLevel
|
||||
}
|
||||
|
||||
impl Default for CodePtr {
|
||||
fn default() -> Self {
|
||||
CodePtr::TopLevel
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<usize> for CodePtr {
|
||||
type Output = CodePtr;
|
||||
|
||||
fn add(self, rhs: usize) -> Self::Output {
|
||||
match self {
|
||||
CodePtr::DirEntry(p) => CodePtr::DirEntry(p + rhs),
|
||||
CodePtr::TopLevel => CodePtr::TopLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<usize> for CodePtr {
|
||||
fn add_assign(&mut self, rhs: usize) {
|
||||
match self {
|
||||
&mut CodePtr::DirEntry(ref mut p) => *p += rhs,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Heap = Vec<HeapCellValue>;
|
||||
|
||||
pub type Registers = Vec<Addr>;
|
||||
|
||||
impl Term {
|
||||
pub fn subterms(&self) -> usize {
|
||||
match self {
|
||||
&Term::Clause(_, _, ref terms) => terms.len(),
|
||||
_ => 1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Atom {
|
||||
match self {
|
||||
&Term::Atom(_, ref atom)
|
||||
| &Term::Var(_, ref atom)
|
||||
| &Term::Clause(_, ref atom, _) => atom
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arity(&self) -> usize {
|
||||
match self {
|
||||
&Term::Atom(_, _) | &Term::Var(_, _) => 0,
|
||||
&Term::Clause(_, _, ref child_terms) => child_terms.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type HeapVarDict = HashMap<Var, Addr>;
|
||||
|
||||
pub enum EvalResult {
|
||||
EntryFailure,
|
||||
EntrySuccess,
|
||||
InitialQuerySuccess(HeapVarDict),
|
||||
QueryFailure,
|
||||
SubsequentQuerySuccess,
|
||||
}
|
||||
|
||||
impl EvalResult {
|
||||
#[allow(dead_code)]
|
||||
pub fn failed_query(&self) -> bool {
|
||||
if let &EvalResult::QueryFailure = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,447 +0,0 @@
|
||||
use l3::ast::*;
|
||||
use l3::iterators::{FactIterator, QueryIterator};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::max;
|
||||
use std::collections::HashMap;
|
||||
use std::vec::Vec;
|
||||
|
||||
trait CompilationTarget<'a> {
|
||||
type Iterator : Iterator<Item=TermRef<'a>>;
|
||||
|
||||
fn iter(&'a Term) -> Self::Iterator;
|
||||
|
||||
fn to_structure(Level, Atom, usize, RegType) -> Self;
|
||||
|
||||
fn argument_to_variable(RegType, usize) -> Self;
|
||||
fn argument_to_value(RegType, usize) -> Self;
|
||||
fn subterm_to_variable(RegType) -> Self;
|
||||
fn subterm_to_value(RegType) -> Self;
|
||||
|
||||
fn clause_arg_to_instr(RegType) -> Self;
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for FactInstruction {
|
||||
type Iterator = FactIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.breadth_first_iter()
|
||||
}
|
||||
|
||||
fn to_structure(lvl: Level, atom: Atom, arity: usize, reg: RegType) -> Self {
|
||||
FactInstruction::GetStructure(lvl, atom, arity, reg)
|
||||
}
|
||||
|
||||
fn argument_to_variable(arg: RegType, val: usize) -> Self {
|
||||
FactInstruction::GetVariable(arg, val)
|
||||
}
|
||||
|
||||
fn argument_to_value(arg: RegType, val: usize) -> Self {
|
||||
FactInstruction::GetValue(arg, val)
|
||||
}
|
||||
|
||||
fn subterm_to_variable(val: RegType) -> Self {
|
||||
FactInstruction::UnifyVariable(val)
|
||||
}
|
||||
|
||||
fn subterm_to_value(val: RegType) -> Self {
|
||||
FactInstruction::UnifyValue(val)
|
||||
}
|
||||
|
||||
fn clause_arg_to_instr(val: RegType) -> Self {
|
||||
FactInstruction::UnifyVariable(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CompilationTarget<'a> for QueryInstruction {
|
||||
type Iterator = QueryIterator<'a>;
|
||||
|
||||
fn iter(term: &'a Term) -> Self::Iterator {
|
||||
term.post_order_iter()
|
||||
}
|
||||
|
||||
fn to_structure(lvl: Level, atom: Atom, arity: usize, reg: RegType) -> Self {
|
||||
QueryInstruction::PutStructure(lvl, atom, arity, reg)
|
||||
}
|
||||
|
||||
fn argument_to_variable(arg: RegType, val: usize) -> Self {
|
||||
QueryInstruction::PutVariable(arg, val)
|
||||
}
|
||||
|
||||
fn argument_to_value(arg: RegType, val: usize) -> Self {
|
||||
QueryInstruction::PutValue(arg, val)
|
||||
}
|
||||
|
||||
fn subterm_to_variable(val: RegType) -> Self {
|
||||
QueryInstruction::SetVariable(val)
|
||||
}
|
||||
|
||||
fn subterm_to_value(val: RegType) -> Self {
|
||||
QueryInstruction::SetValue(val)
|
||||
}
|
||||
|
||||
fn clause_arg_to_instr(val: RegType) -> Self {
|
||||
QueryInstruction::SetValue(val)
|
||||
}
|
||||
}
|
||||
|
||||
struct TermMarker<'a> {
|
||||
bindings: HashMap<&'a Var, VarReg>,
|
||||
arg_c: usize,
|
||||
perm_c: usize,
|
||||
temp_c: usize
|
||||
}
|
||||
|
||||
impl<'a> TermMarker<'a> {
|
||||
fn new() -> TermMarker<'a> {
|
||||
TermMarker { bindings: HashMap::new(),
|
||||
arg_c: 1,
|
||||
perm_c: 1,
|
||||
temp_c: 1 }
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.bindings.clear();
|
||||
self.perm_c = 1;
|
||||
}
|
||||
|
||||
fn contains_var(&self, var: &'a Var) -> bool {
|
||||
self.bindings.contains_key(var)
|
||||
}
|
||||
|
||||
fn get(&self, var: &'a Var) -> VarReg {
|
||||
*self.bindings.get(var).unwrap()
|
||||
}
|
||||
|
||||
fn insert(&mut self, var: &'a Var, r: VarReg) {
|
||||
self.bindings.insert(var, r);
|
||||
}
|
||||
|
||||
fn mark_non_var(&mut self, lvl: Level, cell: &Cell<RegType>) {
|
||||
let reg_type = cell.get();
|
||||
|
||||
if reg_type.reg_num() == 0 {
|
||||
match lvl {
|
||||
Level::Deep if reg_type.is_perm() => {
|
||||
let perm = self.perm_c;
|
||||
self.perm_c += 1;
|
||||
cell.set(RegType::Perm(perm));
|
||||
},
|
||||
Level::Deep => {
|
||||
let temp = self.temp_c;
|
||||
self.temp_c += 1;
|
||||
cell.set(RegType::Temp(temp));
|
||||
},
|
||||
Level::Shallow if reg_type.is_perm() => {
|
||||
let arg = self.arg_c;
|
||||
self.arg_c += 1;
|
||||
cell.set(RegType::Perm(arg));
|
||||
},
|
||||
Level::Shallow => {
|
||||
let arg = self.arg_c;
|
||||
self.arg_c += 1;
|
||||
cell.set(RegType::Temp(arg));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_old_var(&mut self, lvl: Level, var: &'a Var) -> VarReg
|
||||
{
|
||||
let reg = self.get(var);
|
||||
|
||||
match lvl {
|
||||
Level::Deep => VarReg::Norm(reg.norm()),
|
||||
Level::Shallow => {
|
||||
let reg = VarReg::ArgAndNorm(reg.norm(), self.arg_c);
|
||||
|
||||
self.arg_c += 1;
|
||||
self.insert(var, reg);
|
||||
|
||||
reg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_new_var(&mut self, lvl: Level, var: &'a Var, reg: RegType) -> VarReg
|
||||
{
|
||||
let inner_reg = if reg.is_perm() {
|
||||
let perm = self.perm_c;
|
||||
self.perm_c += 1;
|
||||
RegType::Perm(perm)
|
||||
} else {
|
||||
let temp = self.temp_c;
|
||||
self.temp_c += 1;
|
||||
RegType::Temp(temp)
|
||||
};
|
||||
|
||||
let reg = match lvl {
|
||||
Level::Deep => VarReg::Norm(inner_reg),
|
||||
Level::Shallow => {
|
||||
let reg = VarReg::ArgAndNorm(inner_reg, self.arg_c);
|
||||
self.arg_c += 1;
|
||||
reg
|
||||
}
|
||||
};
|
||||
|
||||
self.insert(var, reg);
|
||||
reg
|
||||
}
|
||||
|
||||
fn advance_at_head(&mut self, term: &'a Term) {
|
||||
self.arg_c = 1;
|
||||
self.temp_c = max(term.subterms(), self.temp_c) + 1;
|
||||
}
|
||||
|
||||
fn advance(&mut self, term: &'a Term) {
|
||||
self.arg_c = 1;
|
||||
self.temp_c = term.subterms() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum TermStatus {
|
||||
New, Old, Recurrent
|
||||
}
|
||||
|
||||
pub struct CodeGenerator<'a> {
|
||||
marker: TermMarker<'a>
|
||||
}
|
||||
|
||||
type VariableFixture<'a> = (TermStatus, Vec<&'a Cell<VarReg>>);
|
||||
type VariableFixtures<'a> = HashMap<&'a Var, VariableFixture<'a>>;
|
||||
|
||||
impl<'a> CodeGenerator<'a> {
|
||||
pub fn new() -> Self {
|
||||
CodeGenerator { marker: TermMarker::new() }
|
||||
}
|
||||
|
||||
pub fn vars(&self) -> &HashMap<&Var, VarReg> {
|
||||
&self.marker.bindings
|
||||
}
|
||||
|
||||
fn to_structure<Target>(&mut self,
|
||||
lvl: Level,
|
||||
name: &'a Atom,
|
||||
cell: &'a Cell<RegType>,
|
||||
arity: usize)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
self.marker.mark_non_var(lvl, cell);
|
||||
Target::to_structure(lvl, name.clone(), arity, cell.get())
|
||||
}
|
||||
|
||||
fn var_term<Target>(&mut self,
|
||||
lvl: Level,
|
||||
cell: &'a Cell<VarReg>,
|
||||
var: &'a Var)
|
||||
-> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
if !self.marker.contains_var(var) {
|
||||
let reg = self.marker.mark_new_var(lvl, var, cell.get().norm());
|
||||
cell.set(reg);
|
||||
|
||||
match reg {
|
||||
VarReg::ArgAndNorm(arg, norm) =>
|
||||
Target::argument_to_variable(arg, norm),
|
||||
VarReg::Norm(norm) =>
|
||||
Target::subterm_to_variable(norm)
|
||||
}
|
||||
} else {
|
||||
let reg = self.marker.mark_old_var(lvl, var);
|
||||
cell.set(reg);
|
||||
|
||||
match reg {
|
||||
VarReg::ArgAndNorm(arg, norm) =>
|
||||
Target::argument_to_value(arg, norm),
|
||||
VarReg::Norm(norm) =>
|
||||
Target::subterm_to_value(norm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn non_var_subterm<Target>(&mut self, cell: &'a Cell<RegType>) -> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
self.marker.mark_non_var(Level::Deep, cell);
|
||||
Target::clause_arg_to_instr(cell.get())
|
||||
}
|
||||
|
||||
fn subterm_to_instr<Target>(&mut self, subterm: &'a Term) -> Target
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
match subterm {
|
||||
&Term::Atom(ref cell, _) | &Term::Clause(ref cell, _, _) =>
|
||||
self.non_var_subterm(cell),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
self.var_term(Level::Deep, cell, var)
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_target<Target>(&mut self, term: &'a Term) -> Vec<Target>
|
||||
where Target: CompilationTarget<'a>
|
||||
{
|
||||
let iter = Target::iter(term);
|
||||
let mut target = Vec::new();
|
||||
|
||||
for term in iter {
|
||||
match term {
|
||||
TermRef::Atom(lvl, term, atom) =>
|
||||
target.push(self.to_structure(lvl, atom, term, 0)),
|
||||
TermRef::Clause(lvl, term, atom, terms) => {
|
||||
target.push(self.to_structure(lvl, atom, term, terms.len()));
|
||||
|
||||
for subterm in terms {
|
||||
target.push(self.subterm_to_instr(subterm.as_ref()));
|
||||
}
|
||||
},
|
||||
TermRef::Var(lvl @ Level::Shallow, ref cell, ref var) =>
|
||||
target.push(self.var_term(lvl, cell, var)),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
target
|
||||
}
|
||||
|
||||
fn mark_vars_in_term<Iter>(iter: Iter, vs: &mut VariableFixtures<'a>)
|
||||
where Iter : Iterator<Item=TermRef<'a>>
|
||||
{
|
||||
for term in iter {
|
||||
if let TermRef::Var(_, reg_cell, var) = term {
|
||||
let mut status = vs.entry(var)
|
||||
.or_insert((TermStatus::New, Vec::new()));
|
||||
|
||||
status.1.push(reg_cell);
|
||||
|
||||
match status.0 {
|
||||
TermStatus::Old => status.0 = TermStatus::Recurrent,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for &mut (ref mut term_status, ref mut cb) in vs.values_mut() {
|
||||
match *term_status {
|
||||
TermStatus::New => *term_status = TermStatus::Old,
|
||||
TermStatus::Recurrent => {
|
||||
for cell_reg in cb.drain(0..) {
|
||||
cell_reg.set(VarReg::Norm(RegType::Perm(0)));
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_perm_vars(rule: &'a Rule) -> VariableFixtures {
|
||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||
let mut vfs = HashMap::new();
|
||||
|
||||
let iter = p0.breadth_first_iter().chain(p1.breadth_first_iter());
|
||||
|
||||
Self::mark_vars_in_term(iter, &mut vfs);
|
||||
|
||||
for term in clauses {
|
||||
Self::mark_vars_in_term(term.breadth_first_iter(), &mut vfs);
|
||||
}
|
||||
|
||||
vfs
|
||||
}
|
||||
|
||||
fn add_conditional_call(compiled_query: &mut Code, term: &Term) {
|
||||
match term {
|
||||
&Term::Atom(_, ref atom) => {
|
||||
let call = ControlInstruction::Call(atom.clone(), 0);
|
||||
compiled_query.push(Line::Control(call));
|
||||
},
|
||||
&Term::Clause(_, ref atom, ref terms) => {
|
||||
let call = ControlInstruction::Call(atom.clone(), terms.len());
|
||||
compiled_query.push(Line::Control(call));
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_rule(&mut self, rule: &'a Rule) -> Code {
|
||||
let vfs = Self::mark_perm_vars(&rule);
|
||||
let &Rule { head: (ref p0, ref p1), ref clauses } = rule;
|
||||
let mut perm_vars = 0;
|
||||
|
||||
for &(term_status, _) in vfs.values() {
|
||||
if let TermStatus::Recurrent = term_status {
|
||||
perm_vars += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut body = Vec::new();
|
||||
|
||||
body.push(Line::Control(ControlInstruction::Allocate(perm_vars)));
|
||||
|
||||
self.marker.advance(p0);
|
||||
body.push(Line::Fact(self.compile_target(p0)));
|
||||
|
||||
self.marker.advance_at_head(p1);
|
||||
body.push(Line::Query(self.compile_target(p1)));
|
||||
|
||||
Self::add_conditional_call(&mut body, p1);
|
||||
|
||||
body = clauses.iter()
|
||||
.map(|ref term| self.compile_query(term))
|
||||
.fold(body, |mut body, ref mut cqs| {
|
||||
body.append(cqs);
|
||||
body
|
||||
});
|
||||
|
||||
body.push(Line::Control(ControlInstruction::Deallocate));
|
||||
body
|
||||
}
|
||||
|
||||
pub fn compile_fact(&mut self, term: &'a Term) -> Code {
|
||||
self.marker.advance(term);
|
||||
|
||||
let mut compiled_fact = vec![Line::Fact(self.compile_target(term))];
|
||||
let proceed = Line::Control(ControlInstruction::Proceed);
|
||||
|
||||
compiled_fact.push(proceed);
|
||||
compiled_fact
|
||||
}
|
||||
|
||||
pub fn compile_query(&mut self, term: &'a Term) -> Code {
|
||||
self.marker.advance(term);
|
||||
|
||||
let mut compiled_query = vec![Line::Query(self.compile_target(term))];
|
||||
Self::add_conditional_call(&mut compiled_query, term);
|
||||
|
||||
compiled_query
|
||||
}
|
||||
|
||||
pub fn compile_predicate(&mut self, clauses: &'a Vec<PredicateClause>) -> Code
|
||||
{
|
||||
let mut code = Vec::new();
|
||||
|
||||
for (i, clause) in clauses.iter().enumerate() {
|
||||
self.marker.reset();
|
||||
|
||||
let mut clause_code = match clause {
|
||||
&PredicateClause::Fact(ref fact) =>
|
||||
self.compile_fact(fact),
|
||||
&PredicateClause::Rule(ref rule) =>
|
||||
self.compile_rule(rule)
|
||||
};
|
||||
|
||||
let choice = match i {
|
||||
0 => ChoiceInstruction::TryMeElse(clause_code.len() + 1),
|
||||
_ if i == clauses.len() - 1 => ChoiceInstruction::TrustMe,
|
||||
_ => ChoiceInstruction::RetryMeElse(clause_code.len() + 1)
|
||||
};
|
||||
|
||||
code.push(Line::Choice(choice));
|
||||
code.append(&mut clause_code);
|
||||
}
|
||||
|
||||
code
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
use l3::and_stack::*;
|
||||
use l3::ast::*;
|
||||
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HeapCellView<'a> {
|
||||
Str(usize, &'a Atom),
|
||||
HeapVar(usize),
|
||||
StackVar(usize, usize)
|
||||
}
|
||||
|
||||
pub struct HeapCellViewer<'a> {
|
||||
heap: &'a Heap,
|
||||
and_stack: &'a AndStack,
|
||||
state_stack: Vec<Addr>
|
||||
}
|
||||
|
||||
impl<'a> HeapCellViewer<'a> {
|
||||
pub fn new(heap: &'a Heap, and_stack: &'a AndStack, focus: Addr) -> Self {
|
||||
HeapCellViewer {
|
||||
heap: heap,
|
||||
and_stack: and_stack,
|
||||
state_stack: vec![focus]
|
||||
}
|
||||
}
|
||||
|
||||
fn follow_stack_ref(&mut self, mut fr: usize, mut sc: usize) -> HeapCellView<'a>
|
||||
{
|
||||
loop {
|
||||
match self.and_stack[fr][sc] {
|
||||
Addr::HeapCell(hc) | Addr::Str(hc) =>
|
||||
return self.follow_heap_ref(hc),
|
||||
Addr::StackCell(fr1, sc1) => {
|
||||
if fr1 == fr && sc1 == sc {
|
||||
return HeapCellView::StackVar(fr, sc);
|
||||
}
|
||||
|
||||
fr = fr1; sc = sc1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn follow_heap_ref(&mut self, mut focus: usize) -> HeapCellView<'a> {
|
||||
loop {
|
||||
match &self.heap[focus] {
|
||||
&HeapCellValue::NamedStr(arity, ref name) => {
|
||||
for i in (1 .. arity + 1).rev() {
|
||||
self.state_stack.push(Addr::HeapCell(focus + i));
|
||||
}
|
||||
|
||||
return HeapCellView::Str(arity, name);
|
||||
},
|
||||
&HeapCellValue::Ref(Ref::HeapCell(hc)) => {
|
||||
if focus == hc {
|
||||
return HeapCellView::HeapVar(hc);
|
||||
} else {
|
||||
focus = hc;
|
||||
}
|
||||
},
|
||||
&HeapCellValue::Ref(Ref::StackCell(fr, sc)) =>
|
||||
return self.follow_stack_ref(fr, sc),
|
||||
&HeapCellValue::Str(cell_num) =>
|
||||
focus = cell_num,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for HeapCellViewer<'a> {
|
||||
type Item = HeapCellView<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(addr) = self.state_stack.pop() {
|
||||
match addr {
|
||||
Addr::HeapCell(hc) | Addr::Str(hc) =>
|
||||
return Some(self.follow_heap_ref(hc)),
|
||||
Addr::StackCell(fr, sc) =>
|
||||
return Some(self.follow_stack_ref(fr, sc))
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
255
src/l3/io.rs
255
src/l3/io.rs
@@ -1,255 +0,0 @@
|
||||
use l3::ast::*;
|
||||
use l3::codegen::*;
|
||||
use l3::l3_parser::*;
|
||||
use l3::machine::*;
|
||||
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::input::TermRead;
|
||||
use termion::event::Key;
|
||||
|
||||
use std::io::{Write, stdin, stdout};
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for FactInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&FactInstruction::GetStructure(Level::Deep, ref name, ref arity, ref r) =>
|
||||
write!(f, "get_structure {}/{}, {}", name, arity, r),
|
||||
&FactInstruction::GetStructure(Level::Shallow, ref name, ref arity, ref r) =>
|
||||
write!(f, "get_structure {}/{}, A{}", name, arity, r.reg_num()),
|
||||
&FactInstruction::GetValue(ref x, ref a) =>
|
||||
write!(f, "get_value {}, A{}", x, a),
|
||||
&FactInstruction::GetVariable(ref x, ref a) =>
|
||||
write!(f, "get_variable {}, A{}", x, a),
|
||||
&FactInstruction::UnifyVariable(ref r) =>
|
||||
write!(f, "unify_variable {}", r),
|
||||
&FactInstruction::UnifyValue(ref r) =>
|
||||
write!(f, "unify_value {}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for QueryInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&QueryInstruction::PutStructure(Level::Deep, ref name, ref arity, ref r) =>
|
||||
write!(f, "put_structure {}/{}, {}", name, arity, r.reg_num()),
|
||||
&QueryInstruction::PutStructure(Level::Shallow, ref name, ref arity, ref r) =>
|
||||
write!(f, "put_structure {}/{}, A{}", name, arity, r.reg_num()),
|
||||
&QueryInstruction::PutValue(ref x, ref a) =>
|
||||
write!(f, "put_value {}, A{}", x, a),
|
||||
&QueryInstruction::PutVariable(ref x, ref a) =>
|
||||
write!(f, "put_variable {}, A{}", x, a),
|
||||
&QueryInstruction::SetVariable(ref r) =>
|
||||
write!(f, "set_variable {}", r),
|
||||
&QueryInstruction::SetValue(ref r) =>
|
||||
write!(f, "set_value {}", r),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ControlInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&ControlInstruction::Allocate(num_cells) =>
|
||||
write!(f, "allocate {}", num_cells),
|
||||
&ControlInstruction::Call(ref name, ref arity) =>
|
||||
write!(f, "call {}/{}", name, arity),
|
||||
&ControlInstruction::Deallocate =>
|
||||
write!(f, "deallocate"),
|
||||
&ControlInstruction::Proceed =>
|
||||
write!(f, "proceed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ChoiceInstruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&ChoiceInstruction::TryMeElse(offset) =>
|
||||
write!(f, "try_me_else {}", offset),
|
||||
&ChoiceInstruction::RetryMeElse(offset) =>
|
||||
write!(f, "retry_me_else {}", offset),
|
||||
&ChoiceInstruction::TrustMe =>
|
||||
write!(f, "trust_me")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Level {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Level::Shallow => write!(f, "A"),
|
||||
&Level::Deep => write!(f, "X")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VarReg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&VarReg::Norm(RegType::Perm(reg)) => write!(f, "Y{}", reg),
|
||||
&VarReg::Norm(RegType::Temp(reg)) => write!(f, "X{}", reg),
|
||||
&VarReg::ArgAndNorm(RegType::Perm(reg), arg) =>
|
||||
write!(f, "Y{} A{}", reg, arg),
|
||||
&VarReg::ArgAndNorm(RegType::Temp(reg), arg) =>
|
||||
write!(f, "X{} A{}", reg, arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RegType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&RegType::Perm(val) => write!(f, "Y{}", val),
|
||||
&RegType::Temp(val) => write!(f, "X{}", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn is_consistent(predicate: &Vec<PredicateClause>) -> bool {
|
||||
let name = predicate.first().unwrap().name();
|
||||
let arity = predicate.first().unwrap().arity();
|
||||
|
||||
for clause in predicate.iter().skip(1) {
|
||||
if !(name == clause.name() && arity == clause.arity()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn print_code(code: &Code) {
|
||||
for clause in code {
|
||||
match clause {
|
||||
&Line::Fact(ref fact) =>
|
||||
for fact_instr in fact {
|
||||
println!("{}", fact_instr);
|
||||
},
|
||||
&Line::Choice(ref choice) =>
|
||||
println!("{}", choice),
|
||||
&Line::Control(ref control) =>
|
||||
println!("{}", control),
|
||||
&Line::Query(ref query) =>
|
||||
for query_instr in query {
|
||||
println!("{}", query_instr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read() -> String {
|
||||
let _ = stdout().flush();
|
||||
|
||||
let mut buffer = String::new();
|
||||
let mut result = String::new();
|
||||
|
||||
let stdin = stdin();
|
||||
stdin.read_line(&mut buffer).unwrap();
|
||||
|
||||
if &*buffer.trim() == ":{" {
|
||||
buffer.clear();
|
||||
|
||||
stdin.read_line(&mut buffer).unwrap();
|
||||
|
||||
while &*buffer.trim() != "}:" {
|
||||
result += buffer.as_str();
|
||||
buffer.clear();
|
||||
stdin.read_line(&mut buffer).unwrap();
|
||||
}
|
||||
} else {
|
||||
result = buffer;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn eval(wam: &mut Machine, buffer: &str) -> EvalResult
|
||||
{
|
||||
let result = parse_TopLevel(buffer);
|
||||
let mut cg = CodeGenerator::new();
|
||||
|
||||
match &result {
|
||||
&Ok(TopLevel::Predicate(ref clauses)) => {
|
||||
if is_consistent(clauses) {
|
||||
let compiled_pred = cg.compile_predicate(clauses);
|
||||
wam.add_predicate(clauses, compiled_pred);
|
||||
|
||||
EvalResult::EntrySuccess
|
||||
} else {
|
||||
let msg = r"Error: predicate is inconsistent.
|
||||
Each predicate must have the same name and arity.";
|
||||
|
||||
println!("{}", msg);
|
||||
EvalResult::EntryFailure
|
||||
}
|
||||
},
|
||||
&Ok(TopLevel::Fact(ref fact)) => {
|
||||
let compiled_fact = cg.compile_fact(&fact);
|
||||
wam.add_fact(fact, compiled_fact);
|
||||
EvalResult::EntrySuccess
|
||||
},
|
||||
&Ok(TopLevel::Rule(ref rule)) => {
|
||||
let compiled_rule = cg.compile_rule(&rule);
|
||||
wam.add_rule(rule, compiled_rule);
|
||||
EvalResult::EntrySuccess
|
||||
},
|
||||
&Ok(TopLevel::Query(ref query)) => {
|
||||
let compiled_query = cg.compile_query(&query);
|
||||
wam.run_query(compiled_query, &cg)
|
||||
},
|
||||
&Err(_) => {
|
||||
println!("Grammatical error of some kind!");
|
||||
EvalResult::EntryFailure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(wam: &mut Machine, result: EvalResult) {
|
||||
match result {
|
||||
EvalResult::InitialQuerySuccess(heap_locs) => {
|
||||
println!("yes");
|
||||
|
||||
'outer: loop {
|
||||
let mut result = EvalResult::QueryFailure;
|
||||
let bindings = wam.heap_view(&heap_locs);
|
||||
|
||||
let stdin = stdin();
|
||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||
|
||||
write!(stdout, "{}\n\r", bindings).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
if !wam.or_stack_is_empty() {
|
||||
write!(stdout, "Press ; to continue or A to abort.\n\r").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
for c in stdin.keys() {
|
||||
match c.unwrap() {
|
||||
Key::Char(';') => {
|
||||
result = wam.continue_query();
|
||||
break;
|
||||
},
|
||||
Key::Char('a') | Key::Char('A') =>
|
||||
break 'outer,
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
if let &EvalResult::QueryFailure = &result {
|
||||
write!(stdout, "no\n\r").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
EvalResult::QueryFailure => println!("no"),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
use l3::ast::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::vec::Vec;
|
||||
|
||||
enum IteratorState<'a> {
|
||||
Atom(Level, &'a Cell<RegType>, &'a Atom),
|
||||
Clause(Level, usize, &'a Cell<RegType>, &'a Atom, &'a Vec<Box<Term>>),
|
||||
IsolatedAtom(&'a Cell<RegType>, &'a Atom),
|
||||
IsolatedVar(&'a Cell<VarReg>, &'a Var),
|
||||
RootClause(usize, &'a Vec<Box<Term>>),
|
||||
Var(Level, &'a Cell<VarReg>, &'a Var)
|
||||
}
|
||||
|
||||
impl<'a> IteratorState<'a> {
|
||||
fn to_state(lvl: Level, term: &'a Term) -> IteratorState<'a>
|
||||
{
|
||||
match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
IteratorState::Atom(lvl, cell, atom),
|
||||
&Term::Clause(ref cell, ref atom, ref child_terms) =>
|
||||
IteratorState::Clause(lvl, 0, cell, atom, child_terms),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
IteratorState::Var(lvl, cell, var)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryIterator<'a> {
|
||||
state_stack: Vec<IteratorState<'a>>
|
||||
}
|
||||
|
||||
impl<'a> QueryIterator<'a> {
|
||||
fn push_clause(&mut self,
|
||||
lvl: Level,
|
||||
child_num: usize,
|
||||
cell: &'a Cell<RegType>,
|
||||
name: &'a Atom,
|
||||
child_terms: &'a Vec<Box<Term>>)
|
||||
{
|
||||
self.state_stack.push(IteratorState::Clause(lvl,
|
||||
child_num,
|
||||
cell,
|
||||
name,
|
||||
child_terms));
|
||||
}
|
||||
|
||||
fn push_root_clause(&mut self,
|
||||
child_num: usize,
|
||||
child_terms: &'a Vec<Box<Term>>)
|
||||
{
|
||||
self.state_stack.push(IteratorState::RootClause(child_num, child_terms));
|
||||
}
|
||||
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_stack.push(IteratorState::to_state(lvl, term));
|
||||
}
|
||||
|
||||
fn new(term: &'a Term) -> QueryIterator<'a> {
|
||||
let state = match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
IteratorState::IsolatedAtom(cell, atom),
|
||||
&Term::Clause(_, _, ref terms) =>
|
||||
IteratorState::RootClause(0, terms),
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
IteratorState::IsolatedVar(cell, var)
|
||||
};
|
||||
|
||||
QueryIterator { state_stack: vec![state] }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for QueryIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(iter_state) = self.state_stack.pop() {
|
||||
match iter_state {
|
||||
IteratorState::Atom(lvl, cell, atom) =>
|
||||
return Some(TermRef::Atom(lvl, cell, atom)),
|
||||
IteratorState::Clause(lvl, child_num, cell, atom, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
return Some(TermRef::Clause(lvl, cell, atom, child_terms));
|
||||
} else {
|
||||
self.push_clause(lvl, child_num + 1, cell, atom, child_terms);
|
||||
self.push_subterm(Level::Deep, child_terms[child_num].as_ref());
|
||||
}
|
||||
},
|
||||
IteratorState::IsolatedAtom(cell, atom) =>
|
||||
return Some(TermRef::Atom(Level::Shallow, cell, atom)),
|
||||
IteratorState::IsolatedVar(cell, var) =>
|
||||
return Some(TermRef::Var(Level::Shallow, cell, var)),
|
||||
IteratorState::RootClause(child_num, child_terms) => {
|
||||
if child_num == child_terms.len() {
|
||||
return None;
|
||||
} else {
|
||||
self.push_root_clause(child_num + 1, child_terms);
|
||||
self.push_subterm(Level::Shallow, child_terms[child_num].as_ref());
|
||||
}
|
||||
},
|
||||
IteratorState::Var(lvl, cell, var) =>
|
||||
return Some(TermRef::Var(lvl, cell, var))
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FactIterator<'a> {
|
||||
state_queue: VecDeque<IteratorState<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> FactIterator<'a> {
|
||||
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
|
||||
self.state_queue.push_back(IteratorState::to_state(lvl, term));
|
||||
}
|
||||
|
||||
fn new(term: &'a Term) -> FactIterator<'a> {
|
||||
let states = match term {
|
||||
&Term::Atom(ref cell, ref atom) =>
|
||||
vec![IteratorState::IsolatedAtom(cell, atom)],
|
||||
&Term::Clause(_, _, ref terms) =>
|
||||
vec![IteratorState::RootClause(0, terms)],
|
||||
&Term::Var(ref cell, ref var) =>
|
||||
vec![IteratorState::IsolatedVar(cell, var)]
|
||||
};
|
||||
|
||||
FactIterator { state_queue: VecDeque::from(states) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FactIterator<'a> {
|
||||
type Item = TermRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(state) = self.state_queue.pop_front() {
|
||||
match state {
|
||||
IteratorState::Atom(lvl, cell, atom) =>
|
||||
return Some(TermRef::Atom(lvl, cell, atom)),
|
||||
IteratorState::Clause(lvl, _, cell, atom, child_terms) => {
|
||||
for child_term in child_terms {
|
||||
self.push_subterm(Level::Deep, child_term);
|
||||
}
|
||||
|
||||
return Some(TermRef::Clause(lvl, cell, atom, child_terms));
|
||||
},
|
||||
IteratorState::IsolatedAtom(cell, atom) =>
|
||||
return Some(TermRef::Atom(Level::Shallow, cell, atom)),
|
||||
IteratorState::IsolatedVar(cell, var) =>
|
||||
return Some(TermRef::Var(Level::Shallow, cell, var)),
|
||||
IteratorState::RootClause(_, child_terms) => {
|
||||
for child_term in child_terms {
|
||||
self.push_subterm(Level::Shallow, child_term);
|
||||
}
|
||||
},
|
||||
IteratorState::Var(lvl, cell, var) =>
|
||||
return Some(TermRef::Var(lvl, cell, var))
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn post_order_iter(&self) -> QueryIterator {
|
||||
QueryIterator::new(self)
|
||||
}
|
||||
|
||||
pub fn breadth_first_iter(&self) -> FactIterator {
|
||||
FactIterator::new(self)
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
use l3::ast::*;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
grammar;
|
||||
|
||||
pub TopLevel: TopLevel = {
|
||||
"?-" <t:Term> "." => TopLevel::Query(t),
|
||||
<Predicate> => TopLevel::Predicate(<>),
|
||||
<Rule> "." => TopLevel::Rule(<>),
|
||||
<Term> "." => TopLevel::Fact(<>)
|
||||
};
|
||||
|
||||
Atom : Atom = {
|
||||
r"[a-z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
|
||||
BoxedTerm : Box<Term> = {
|
||||
<t:Term> => Box::new(t)
|
||||
};
|
||||
|
||||
Clause : Term = {
|
||||
<a:Atom> "(" <ts: (<BoxedTerm> ",")*> <t:BoxedTerm> ")" => {
|
||||
let mut ts = ts;
|
||||
ts.push(t);
|
||||
Term::Clause(Cell::new(RegType::Temp(0)), a, ts)
|
||||
}
|
||||
};
|
||||
|
||||
Predicate : Vec<PredicateClause> = {
|
||||
<pcs: (<PredicateClause>)+> <pc: PredicateClause> => {
|
||||
let mut pcs = pcs;
|
||||
pcs.push(pc);
|
||||
pcs
|
||||
}
|
||||
};
|
||||
|
||||
PredicateClause : PredicateClause = {
|
||||
<Rule> "." => PredicateClause::Rule(<>),
|
||||
<Term> "." => PredicateClause::Fact(<>)
|
||||
};
|
||||
|
||||
Rule : Rule = {
|
||||
<c:Clause> ":-" <h:Term> <cs: ("," <Term>)*> =>
|
||||
Rule { head: (c, h), clauses: cs },
|
||||
<a:Atom> ":-" <h:Term> <cs: ("," <Term>)*> =>
|
||||
Rule { head: (Term::Atom(Cell::new(RegType::Temp(0)), a), h),
|
||||
clauses: cs }
|
||||
};
|
||||
|
||||
Term : Term = {
|
||||
<Clause> => <>,
|
||||
<Atom> => Term::Atom(Cell::new(RegType::Temp(0)), <>),
|
||||
<Var> => Term::Var(Cell::new(VarReg::Norm(RegType::Temp(0))), <>)
|
||||
};
|
||||
|
||||
Var : Var = {
|
||||
r"[A-Z][a-z0-9_]*" => <>.trim().to_string(),
|
||||
};
|
||||
2177
src/l3/l3_parser.rs
2177
src/l3/l3_parser.rs
File diff suppressed because it is too large
Load Diff
@@ -1,677 +0,0 @@
|
||||
use l3::ast::*;
|
||||
use l3::codegen::*;
|
||||
use l3::heapview::*;
|
||||
use l3::and_stack::*;
|
||||
use l3::or_stack::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::vec::Vec;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum MachineMode {
|
||||
Read,
|
||||
Write
|
||||
}
|
||||
|
||||
struct MachineState {
|
||||
h: usize,
|
||||
s: usize,
|
||||
p: CodePtr,
|
||||
b: usize,
|
||||
e: usize,
|
||||
num_of_args: usize,
|
||||
cp: CodePtr,
|
||||
fail: bool,
|
||||
heap: Heap,
|
||||
mode: MachineMode,
|
||||
and_stack: AndStack,
|
||||
or_stack: OrStack,
|
||||
registers: Registers,
|
||||
trail: Vec<Ref>,
|
||||
tr: usize,
|
||||
hb: usize
|
||||
}
|
||||
|
||||
type CodeDir = HashMap<(Atom, usize), usize>;
|
||||
|
||||
impl Index<RegType> for MachineState {
|
||||
type Output = Addr;
|
||||
|
||||
fn index(&self, reg: RegType) -> &Self::Output {
|
||||
match reg {
|
||||
RegType::Temp(temp) => &self.registers[temp],
|
||||
RegType::Perm(perm) => {
|
||||
let e = self.e;
|
||||
&self.and_stack[e][perm]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<RegType> for MachineState {
|
||||
fn index_mut(&mut self, reg: RegType) -> &mut Self::Output {
|
||||
match reg {
|
||||
RegType::Temp(temp) => &mut self.registers[temp],
|
||||
RegType::Perm(perm) => {
|
||||
let e = self.e;
|
||||
&mut self.and_stack[e][perm]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Machine {
|
||||
ms: MachineState,
|
||||
code: Code,
|
||||
code_dir: CodeDir
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
pub fn new() -> Self {
|
||||
Machine {
|
||||
ms: MachineState::new(),
|
||||
code: Vec::new(),
|
||||
code_dir: HashMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn failed(&self) -> bool {
|
||||
self.ms.fail
|
||||
}
|
||||
|
||||
pub fn add_fact(&mut self, fact: &Term, mut code: Code) {
|
||||
let p = self.code.len();
|
||||
let name = fact.name().clone();
|
||||
let arity = fact.arity();
|
||||
|
||||
self.code.append(&mut code);
|
||||
self.code_dir.insert((name, arity), p);
|
||||
}
|
||||
|
||||
pub fn add_rule(&mut self, rule: &Rule, mut code: Code) {
|
||||
let p = self.code.len();
|
||||
let name = rule.head.0.name().clone();
|
||||
let arity = rule.head.0.arity();
|
||||
|
||||
self.code.append(&mut code);
|
||||
self.code_dir.insert((name, arity), p);
|
||||
}
|
||||
|
||||
pub fn add_predicate(&mut self, pred: &Vec<PredicateClause>, mut code: Code)
|
||||
{
|
||||
let p = self.code.len();
|
||||
let name = pred.first().unwrap().name().clone();
|
||||
let arity = pred.first().unwrap().arity();
|
||||
|
||||
self.code.append(&mut code);
|
||||
self.code_dir.insert((name, arity), p);
|
||||
}
|
||||
|
||||
fn execute_instr<'a>(&mut self, instr_src: LineOrCodeOffset<'a>) -> bool
|
||||
{
|
||||
let mut instr = match instr_src {
|
||||
LineOrCodeOffset::Instruction(instr) => instr,
|
||||
LineOrCodeOffset::Offset(p) => &self.code[p]
|
||||
};
|
||||
|
||||
loop {
|
||||
match instr {
|
||||
&Line::Choice(ref choice_instr) =>
|
||||
self.ms.execute_choice_instr(choice_instr),
|
||||
&Line::Fact(ref fact) => {
|
||||
for fact_instr in fact {
|
||||
self.ms.execute_fact_instr(&fact_instr);
|
||||
}
|
||||
self.ms.p += 1;
|
||||
},
|
||||
&Line::Query(ref query) => {
|
||||
for query_instr in query {
|
||||
self.ms.execute_query_instr(&query_instr);
|
||||
}
|
||||
self.ms.p += 1;
|
||||
},
|
||||
&Line::Control(ref control_instr) =>
|
||||
self.ms.execute_ctrl_instr(&self.code_dir, control_instr),
|
||||
}
|
||||
|
||||
if self.failed() {
|
||||
let p = self.ms
|
||||
.or_stack
|
||||
.top()
|
||||
.map(|fr| fr.bp)
|
||||
.unwrap_or_default();
|
||||
|
||||
if let CodePtr::TopLevel = p {
|
||||
return false;
|
||||
} else {
|
||||
self.ms.fail = false;
|
||||
self.ms.p = p;
|
||||
}
|
||||
}
|
||||
|
||||
match self.ms.p {
|
||||
CodePtr::DirEntry(p) if p < self.code.len() =>
|
||||
instr = &self.code[p],
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn heap_view(&self, var_dir: &HeapVarDict) -> String {
|
||||
let mut result = String::new();
|
||||
|
||||
for (var, addr) in var_dir {
|
||||
let mut arities = Vec::new();
|
||||
let viewer = HeapCellViewer::new(&self.ms.heap,
|
||||
&self.ms.and_stack,
|
||||
*addr);
|
||||
|
||||
if result != "" {
|
||||
result += "\n\r";
|
||||
}
|
||||
|
||||
result += var.as_str();
|
||||
result += " = ";
|
||||
|
||||
for view in viewer {
|
||||
match arities.pop() {
|
||||
Some(n) => arities.push(n-1),
|
||||
None => {}
|
||||
}
|
||||
|
||||
if !(arities.is_empty() || result.ends_with("(")) {
|
||||
result += ", ";
|
||||
}
|
||||
|
||||
match view {
|
||||
HeapCellView::Str(arity, ref name) => {
|
||||
result += name.as_str();
|
||||
|
||||
if arity > 0 {
|
||||
arities.push(arity);
|
||||
result += "(";
|
||||
}
|
||||
},
|
||||
HeapCellView::HeapVar(cell_num) => {
|
||||
result += "_";
|
||||
result += cell_num.to_string().as_str();
|
||||
},
|
||||
HeapCellView::StackVar(fr, sc) => {
|
||||
result += "_s_";
|
||||
result += fr.to_string().as_str();
|
||||
result += "_";
|
||||
result += sc.to_string().as_str();
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(&0) = arities.last() {
|
||||
result += ")";
|
||||
arities.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn run_query(&mut self, code: Code, cg: &CodeGenerator) -> EvalResult
|
||||
{
|
||||
let mut succeeded = true;
|
||||
let mut heap_locs = HashMap::new();
|
||||
|
||||
for instr in code.iter().take(1) {
|
||||
succeeded = self.execute_instr(LineOrCodeOffset::from(instr));
|
||||
}
|
||||
|
||||
if succeeded {
|
||||
for (var, vr) in cg.vars() {
|
||||
let addr = self.ms.registers[vr.root_register()];
|
||||
heap_locs.insert((*var).clone(), addr);
|
||||
}
|
||||
|
||||
for instr in code.iter().skip(1) {
|
||||
succeeded = self.execute_instr(LineOrCodeOffset::from(instr));
|
||||
if !succeeded {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if succeeded {
|
||||
EvalResult::InitialQuerySuccess(heap_locs)
|
||||
} else {
|
||||
EvalResult::QueryFailure
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or_stack_is_empty(&self) -> bool {
|
||||
self.ms.or_stack.is_empty()
|
||||
}
|
||||
|
||||
pub fn continue_query(&mut self) -> EvalResult
|
||||
{
|
||||
if !self.or_stack_is_empty() {
|
||||
let b = self.ms.b;
|
||||
self.ms.p = self.ms.or_stack[b].bp;
|
||||
|
||||
let succeeded = if let CodePtr::DirEntry(p) = self.ms.p {
|
||||
self.execute_instr(LineOrCodeOffset::Offset(p))
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if succeeded {
|
||||
EvalResult::SubsequentQuerySuccess
|
||||
} else {
|
||||
EvalResult::QueryFailure
|
||||
}
|
||||
} else {
|
||||
EvalResult::QueryFailure
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.ms.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineState {
|
||||
fn new() -> MachineState {
|
||||
MachineState { h: 0,
|
||||
s: 0,
|
||||
p: CodePtr::TopLevel,
|
||||
b: 0,
|
||||
e: 0,
|
||||
num_of_args: 0,
|
||||
cp: CodePtr::TopLevel,
|
||||
fail: false,
|
||||
heap: Vec::with_capacity(256),
|
||||
mode: MachineMode::Write,
|
||||
and_stack: AndStack::new(),
|
||||
or_stack: OrStack::new(),
|
||||
registers: vec![Addr::HeapCell(0); 32],
|
||||
trail: Vec::new(),
|
||||
tr: 0,
|
||||
hb: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn num_frames(&self) -> usize {
|
||||
self.and_stack.len() + self.or_stack.len()
|
||||
}
|
||||
|
||||
fn store(&self, a: Addr) -> Addr {
|
||||
match a {
|
||||
Addr::HeapCell(r) => self.heap[r].as_addr(r),
|
||||
Addr::StackCell(fr, sc) => self.and_stack[fr][sc],
|
||||
addr => addr
|
||||
}
|
||||
}
|
||||
|
||||
fn deref(&self, a: Addr) -> Addr {
|
||||
let mut a = a;
|
||||
|
||||
loop {
|
||||
let value = self.store(a);
|
||||
|
||||
if value.is_ref() && value != a {
|
||||
a = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
return a;
|
||||
};
|
||||
}
|
||||
|
||||
fn bind(&mut self, r1: Ref, a2: Addr) {
|
||||
let t2 = self.store(a2);
|
||||
|
||||
match r1 {
|
||||
Ref::StackCell(fr, sc) =>
|
||||
self.and_stack[fr][sc] = t2,
|
||||
Ref::HeapCell(hc) =>
|
||||
self.heap[hc] = HeapCellValue::from(t2)
|
||||
};
|
||||
|
||||
self.trail(r1);
|
||||
}
|
||||
|
||||
fn unify(&mut self, a1: Addr, a2: Addr) {
|
||||
let mut pdl = vec![a1, a2];
|
||||
|
||||
self.fail = false;
|
||||
|
||||
while !(pdl.is_empty() || self.fail) {
|
||||
let d1 = self.deref(pdl.pop().unwrap());
|
||||
let d2 = self.deref(pdl.pop().unwrap());
|
||||
|
||||
if d1 != d2 {
|
||||
match (self.store(d1), self.store(d2)) {
|
||||
(Addr::HeapCell(hc), _) =>
|
||||
self.bind(Ref::HeapCell(hc), d2),
|
||||
(_, Addr::HeapCell(hc)) =>
|
||||
self.bind(Ref::HeapCell(hc), d1),
|
||||
(Addr::StackCell(fr, sc), _) =>
|
||||
self.bind(Ref::StackCell(fr, sc), d2),
|
||||
(_, Addr::StackCell(fr, sc)) =>
|
||||
self.bind(Ref::StackCell(fr, sc), d1),
|
||||
(Addr::Str(a1), Addr::Str(a2)) => {
|
||||
let r1 = &self.heap[a1];
|
||||
let r2 = &self.heap[a2];
|
||||
|
||||
if let &HeapCellValue::NamedStr(n1, ref f1) = r1 {
|
||||
if let &HeapCellValue::NamedStr(n2, ref f2) = r2 {
|
||||
if n1 == n2 && *f1 == *f2 {
|
||||
for i in 1 .. n1 + 1 {
|
||||
pdl.push(Addr::HeapCell(a1 + i));
|
||||
pdl.push(Addr::HeapCell(a2 + i));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.fail = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trail(&mut self, r: Ref) {
|
||||
match r {
|
||||
Ref::HeapCell(hc) => {
|
||||
if hc < self.hb {
|
||||
self.trail.push(r);
|
||||
self.tr += 1;
|
||||
}
|
||||
},
|
||||
Ref::StackCell(fr, _) => {
|
||||
let fr_gi = self.and_stack[fr].global_index;
|
||||
let b_gi = if !self.or_stack.is_empty() {
|
||||
self.or_stack[self.b].global_index
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if fr_gi < b_gi {
|
||||
self.trail.push(r);
|
||||
self.tr += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unwind_trail(&mut self, a1: usize, a2: usize) {
|
||||
for i in a1 .. a2 {
|
||||
match self.trail[i] {
|
||||
Ref::HeapCell(r) =>
|
||||
self.heap[r] = HeapCellValue::Ref(self.trail[i]),
|
||||
Ref::StackCell(fr, sc) =>
|
||||
self.and_stack[fr][sc] = Addr::StackCell(fr, sc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_query_instr(&mut self, instr: &QueryInstruction) {
|
||||
match instr {
|
||||
&QueryInstruction::PutStructure(_, ref name, arity, reg) => {
|
||||
self.heap.push(HeapCellValue::Str(self.h + 1));
|
||||
self.heap.push(HeapCellValue::NamedStr(arity, name.clone()));
|
||||
|
||||
self[reg] = Addr::Str(self.h + 1);
|
||||
|
||||
self.h += 2;
|
||||
},
|
||||
&QueryInstruction::PutValue(norm, arg) =>
|
||||
self.registers[arg] = self[norm],
|
||||
&QueryInstruction::PutVariable(norm, arg) => {
|
||||
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(self.h)));
|
||||
|
||||
self[norm] = Addr::HeapCell(self.h);
|
||||
self.registers[arg] = Addr::HeapCell(self.h);
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
&QueryInstruction::SetVariable(reg) => {
|
||||
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(self.h)));
|
||||
self[reg] = Addr::HeapCell(self.h);
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
&QueryInstruction::SetValue(reg) => {
|
||||
let heap_val = self[reg];
|
||||
self.heap.push(HeapCellValue::from(heap_val));
|
||||
|
||||
self.h += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_fact_instr(&mut self, instr: &FactInstruction) {
|
||||
match instr {
|
||||
&FactInstruction::GetStructure(_, ref name, arity, reg) => {
|
||||
let addr = self.deref(self[reg]);
|
||||
|
||||
match self.store(addr) {
|
||||
Addr::Str(a) => {
|
||||
let result = &self.heap[a];
|
||||
|
||||
if let &HeapCellValue::NamedStr(narity, ref str) = result {
|
||||
if narity == arity && *name == *str {
|
||||
self.s = a + 1;
|
||||
self.mode = MachineMode::Read;
|
||||
} else {
|
||||
self.fail = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Addr::HeapCell(_) | Addr::StackCell(_, _) => {
|
||||
self.heap.push(HeapCellValue::Str(self.h + 1));
|
||||
self.heap.push(HeapCellValue::NamedStr(arity, name.clone()));
|
||||
|
||||
let h = self.h;
|
||||
|
||||
self.bind(addr.as_ref().unwrap(), Addr::HeapCell(h));
|
||||
|
||||
self.h += 2;
|
||||
self.mode = MachineMode::Write;
|
||||
}
|
||||
};
|
||||
},
|
||||
&FactInstruction::GetVariable(norm, arg) =>
|
||||
self[norm] = self.registers[arg],
|
||||
&FactInstruction::GetValue(norm, arg) => {
|
||||
let norm_addr = self[norm];
|
||||
let reg_addr = self.registers[arg];
|
||||
|
||||
self.unify(norm_addr, reg_addr);
|
||||
},
|
||||
&FactInstruction::UnifyVariable(reg) => {
|
||||
match self.mode {
|
||||
MachineMode::Read =>
|
||||
self[reg] = self.heap[self.s].as_addr(self.s),
|
||||
MachineMode::Write => {
|
||||
self.heap.push(HeapCellValue::Ref(Ref::HeapCell(self.h)));
|
||||
self[reg] = Addr::HeapCell(self.h);
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
},
|
||||
&FactInstruction::UnifyValue(reg) => {
|
||||
let s = self.s;
|
||||
|
||||
match self.mode {
|
||||
MachineMode::Read => {
|
||||
let reg_addr = self[reg];
|
||||
self.unify(reg_addr, Addr::HeapCell(s));
|
||||
},
|
||||
MachineMode::Write => {
|
||||
let heap_val = self.store(self[reg]);
|
||||
self.heap.push(HeapCellValue::from(heap_val));
|
||||
self.h += 1;
|
||||
}
|
||||
};
|
||||
|
||||
self.s += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn execute_ctrl_instr(&mut self, code_dir: &CodeDir, instr: &ControlInstruction)
|
||||
{
|
||||
match instr {
|
||||
&ControlInstruction::Allocate(num_cells) => {
|
||||
let num_frames = self.num_frames();
|
||||
|
||||
self.and_stack.push(num_frames + 1, self.e, self.cp, num_cells);
|
||||
|
||||
self.e = self.and_stack.len() - 1;
|
||||
self.p += 1;
|
||||
},
|
||||
&ControlInstruction::Call(ref name, arity) => {
|
||||
let compiled_tl_index = code_dir.get(&(name.clone(), arity))
|
||||
.map(|index| *index);
|
||||
|
||||
match compiled_tl_index {
|
||||
Some(compiled_tl_index) => {
|
||||
self.cp = self.p + 1;
|
||||
self.num_of_args = arity;
|
||||
self.p = CodePtr::DirEntry(compiled_tl_index);
|
||||
},
|
||||
None => self.fail = true
|
||||
};
|
||||
},
|
||||
&ControlInstruction::Deallocate => {
|
||||
let e = self.e;
|
||||
|
||||
let num_frame_e = self.and_stack.top().unwrap().global_index;
|
||||
let num_frame_b = self.or_stack
|
||||
.top()
|
||||
.map(|fr| fr.global_index)
|
||||
.unwrap_or(0);
|
||||
|
||||
self.p = self.and_stack[e].cp;
|
||||
self.e = self.and_stack[e].e;
|
||||
|
||||
if num_frame_e > num_frame_b {
|
||||
let top_e = self.and_stack.top().unwrap().e;
|
||||
self.and_stack.drop_frames(top_e - self.e + 1);
|
||||
}
|
||||
},
|
||||
&ControlInstruction::Proceed =>
|
||||
self.p = self.cp,
|
||||
};
|
||||
}
|
||||
|
||||
fn execute_choice_instr(&mut self, instr: &ChoiceInstruction)
|
||||
{
|
||||
match instr {
|
||||
&ChoiceInstruction::TryMeElse(offset) => {
|
||||
let n = self.num_of_args;
|
||||
let num_frames = self.num_frames();
|
||||
|
||||
self.or_stack.push(num_frames + 1,
|
||||
self.e,
|
||||
self.cp,
|
||||
self.b,
|
||||
self.p + offset,
|
||||
self.tr,
|
||||
self.h,
|
||||
self.num_of_args);
|
||||
|
||||
self.b = self.or_stack.len() - 1;
|
||||
let b = self.b;
|
||||
|
||||
for i in 1 .. n + 1 {
|
||||
self.or_stack[b][i] = self.registers[i];
|
||||
}
|
||||
|
||||
self.hb = self.h;
|
||||
self.p += 1;
|
||||
},
|
||||
&ChoiceInstruction::RetryMeElse(offset) => {
|
||||
let b = self.b;
|
||||
let n = self.or_stack[b].num_args();
|
||||
|
||||
for i in 1 .. n + 1 {
|
||||
self.registers[i] = self.or_stack[b][i];
|
||||
}
|
||||
|
||||
self.e = self.or_stack[b].e;
|
||||
self.cp = self.or_stack[b].cp;
|
||||
|
||||
self.or_stack[b].bp = self.p + offset;
|
||||
|
||||
let old_tr = self.or_stack[b].tr;
|
||||
let curr_tr = self.tr;
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
self.tr = self.or_stack[b].tr;
|
||||
|
||||
self.trail.truncate(self.tr);
|
||||
self.heap.truncate(self.or_stack[b].h);
|
||||
|
||||
self.h = self.or_stack[b].h;
|
||||
self.hb = self.h;
|
||||
|
||||
self.p += 1;
|
||||
},
|
||||
&ChoiceInstruction::TrustMe => {
|
||||
let b = self.b;
|
||||
let n = self.or_stack[b].num_args();
|
||||
|
||||
for i in 1 .. n + 1 {
|
||||
self.registers[i] = self.or_stack[b][i];
|
||||
}
|
||||
|
||||
self.e = self.or_stack[b].e;
|
||||
self.cp = self.or_stack[b].cp;
|
||||
|
||||
let old_tr = self.or_stack[b].tr;
|
||||
let curr_tr = self.tr;
|
||||
|
||||
self.unwind_trail(old_tr, curr_tr);
|
||||
|
||||
self.tr = self.or_stack[b].tr;
|
||||
self.trail.truncate(self.tr);
|
||||
|
||||
self.h = self.or_stack[b].h;
|
||||
self.heap.truncate(self.h);
|
||||
|
||||
self.b = self.or_stack[b].b;
|
||||
|
||||
self.or_stack.pop();
|
||||
|
||||
self.hb = self.h;
|
||||
self.p += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.h = 0;
|
||||
self.hb = 0;
|
||||
self.e = 0;
|
||||
self.b = 0;
|
||||
self.s = 0;
|
||||
self.tr = 0;
|
||||
self.p = CodePtr::TopLevel;
|
||||
self.cp = CodePtr::TopLevel;
|
||||
self.num_of_args = 0;
|
||||
|
||||
self.fail = false;
|
||||
self.trail.clear();
|
||||
self.heap.clear();
|
||||
self.mode = MachineMode::Write;
|
||||
self.and_stack.clear();
|
||||
self.or_stack.clear();
|
||||
self.registers = vec![Addr::HeapCell(0); 32];
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
pub mod and_stack;
|
||||
pub mod ast;
|
||||
pub mod codegen;
|
||||
pub mod heapview;
|
||||
pub mod io;
|
||||
pub mod iterators;
|
||||
pub mod l3_parser;
|
||||
pub mod machine;
|
||||
pub mod or_stack;
|
||||
@@ -1,112 +0,0 @@
|
||||
use l3::ast::*;
|
||||
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::vec::Vec;
|
||||
|
||||
pub struct Frame {
|
||||
pub global_index: usize,
|
||||
pub e: usize,
|
||||
pub cp: CodePtr,
|
||||
pub b: usize,
|
||||
pub bp: CodePtr,
|
||||
pub tr: usize,
|
||||
pub h: usize,
|
||||
args: Vec<Addr>
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
fn new(global_index: usize,
|
||||
e: usize,
|
||||
cp: CodePtr,
|
||||
b: usize,
|
||||
bp: CodePtr,
|
||||
tr: usize,
|
||||
h: usize,
|
||||
n: usize)
|
||||
-> Self
|
||||
{
|
||||
Frame {
|
||||
global_index: global_index,
|
||||
e: e,
|
||||
cp: cp,
|
||||
b: b,
|
||||
bp: bp,
|
||||
tr: tr,
|
||||
h: h,
|
||||
args: vec![Addr::HeapCell(0); n]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_args(&self) -> usize {
|
||||
self.args.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OrStack(Vec<Frame>);
|
||||
|
||||
impl OrStack {
|
||||
pub fn new() -> Self {
|
||||
OrStack(Vec::new())
|
||||
}
|
||||
|
||||
pub fn push(&mut self,
|
||||
global_index: usize,
|
||||
e: usize,
|
||||
cp: CodePtr,
|
||||
b: usize,
|
||||
bp: CodePtr,
|
||||
tr: usize,
|
||||
h: usize,
|
||||
n: usize)
|
||||
{
|
||||
self.0.push(Frame::new(global_index, e, cp, b, bp, tr, h, n));
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear()
|
||||
}
|
||||
|
||||
pub fn top(&self) -> Option<&Frame> {
|
||||
self.0.last()
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) {
|
||||
self.0.pop();
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for OrStack {
|
||||
type Output = Frame;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.0.index(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for OrStack {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.0.index_mut(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for Frame {
|
||||
type Output = Addr;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
self.args.index(index - 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for Frame {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
self.args.index_mut(index - 1)
|
||||
}
|
||||
}
|
||||
60
src/lib.rs
Normal file
60
src/lib.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
#![recursion_limit = "4112"]
|
||||
|
||||
#[macro_use]
|
||||
extern crate static_assertions;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate maplit;
|
||||
|
||||
#[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;
|
||||
#[cfg(feature = "ffi")]
|
||||
mod ffi;
|
||||
mod forms;
|
||||
mod heap_iter;
|
||||
pub mod heap_print;
|
||||
#[cfg(feature = "http")]
|
||||
mod http;
|
||||
mod indexing;
|
||||
mod variable_records;
|
||||
#[macro_use]
|
||||
pub mod instructions {
|
||||
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
|
||||
}
|
||||
mod iterators;
|
||||
pub mod machine;
|
||||
mod raw_block;
|
||||
pub mod read;
|
||||
#[cfg(feature = "repl")]
|
||||
mod repl_helper;
|
||||
mod targets;
|
||||
pub mod types;
|
||||
|
||||
use instructions::instr;
|
||||
|
||||
mod rcu;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[wasm_bindgen]
|
||||
pub fn eval_code(s: &str) -> String {
|
||||
use machine::mock_wam::*;
|
||||
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
let mut wam = Machine::with_test_streams();
|
||||
let bytes = wam.test_load_string(s);
|
||||
String::from_utf8_lossy(&bytes).to_string()
|
||||
}
|
||||
163
src/lib/arithmetic.pl
Normal file
163
src/lib/arithmetic.pl
Normal file
@@ -0,0 +1,163 @@
|
||||
/** Arithmetic predicates
|
||||
|
||||
These predicates are additions to standard the arithmetic functions provided by `is/2`.
|
||||
*/
|
||||
|
||||
:- module(arithmetic, [expmod/4, lcm/3, lsb/2, msb/2, number_to_rational/2,
|
||||
number_to_rational/3, popcount/2,
|
||||
rational_numerator_denominator/3]).
|
||||
|
||||
:- use_module(library(charsio), [write_term_to_chars/3]).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(lists), [append/3, member/2]).
|
||||
|
||||
|
||||
%% expmod(+Base, +Expo, +Mod, -R).
|
||||
%
|
||||
% Modular exponentiation. Base, Expo and Mod must be integers.
|
||||
expmod(Base, Expo, Mod, R) :-
|
||||
( member(N, [Base, Expo, Mod]), var(N) -> instantiation_error(expmod/4)
|
||||
; member(N, [Base, Expo, Mod]), \+ integer(N) ->
|
||||
type_error(integer, N, expmod/4)
|
||||
; Expo < 0 -> domain_error(not_less_than_zero, Expo, expmod/4)
|
||||
; expmod_(Base, Expo, Mod, 1, R)
|
||||
).
|
||||
|
||||
expmod_(_, _, 1, _, 0) :- !.
|
||||
expmod_(_, 0, _, R, R) :- !.
|
||||
expmod_(Base0, Expo0, Mod, C0, R) :-
|
||||
Expo0 /\ 1 =:= 1,
|
||||
C is (C0 * Base0) mod Mod,
|
||||
!,
|
||||
Expo is Expo0 >> 1,
|
||||
Base is (Base0 * Base0) mod Mod,
|
||||
expmod_(Base, Expo, Mod, C, R).
|
||||
expmod_(Base0, Expo0, Mod, C, R) :-
|
||||
Expo is Expo0 >> 1,
|
||||
Base is (Base0 * Base0) mod Mod,
|
||||
expmod_(Base, Expo, Mod, C, R).
|
||||
|
||||
%% lcm(+A, +B, -Lcm) is det.
|
||||
%
|
||||
% Calculates the Least common multiple for A and B: the smallest positive integer
|
||||
% that is divisible by both A and B.
|
||||
%
|
||||
% A and B need to be integers.
|
||||
lcm(A, B, X) :-
|
||||
builtins:must_be_number(A, lcm/2),
|
||||
builtins:must_be_number(B, lcm/2),
|
||||
( \+ integer(A) -> type_error(integer, A, lcm/2)
|
||||
; \+ integer(B) -> type_error(integer, B, lcm/2)
|
||||
; (A = 0, B = 0) -> X = 0
|
||||
; builtins:can_be_number(X, lcm/2),
|
||||
X is abs(B) // gcd(A,B) * abs(A)
|
||||
).
|
||||
|
||||
%% lsb(+X, -N).
|
||||
%
|
||||
% True iff N is the least significat bit of integer X
|
||||
lsb(X, N) :-
|
||||
builtins:must_be_number(X, lsb/2),
|
||||
( \+ integer(X) -> type_error(integer, X, lsb/2)
|
||||
; X < 1 -> domain_error(not_less_than_one, X, lsb/2)
|
||||
; builtins:can_be_number(N, lsb/2),
|
||||
X1 is X /\ (-X),
|
||||
msb_(X1, -1, N)
|
||||
).
|
||||
|
||||
%% msb(+X, -N).
|
||||
%
|
||||
% True iff N is the most significant bit of integer X
|
||||
msb(X, N) :-
|
||||
builtins:must_be_number(X, msb/2),
|
||||
( \+ integer(X) -> type_error(integer, X, msb/2)
|
||||
; X < 1 -> domain_error(not_less_than_one, X, msb/2)
|
||||
; builtins:can_be_number(N, msb/2),
|
||||
X1 is X >> 1,
|
||||
msb_(X1, 0, N)
|
||||
).
|
||||
|
||||
msb_(0, N, N) :- !.
|
||||
msb_(X, M, N) :-
|
||||
X1 is X >> 1,
|
||||
M1 is M + 1,
|
||||
msb_(X1, M1, N).
|
||||
|
||||
%% number_to_rational(+Real, -Fraction).
|
||||
%
|
||||
% True iff given a number Real, Fraction is the same number represented as a fraction.
|
||||
number_to_rational(Real, Fraction) :-
|
||||
( var(Real) -> instantiation_error(number_to_rational/2)
|
||||
; integer(Real) -> Fraction is Real rdiv 1
|
||||
; (rational(Real) ; float(Real)) ->
|
||||
number_to_rational(1.0e-6, Real, Fraction)
|
||||
; type_error(number, Real, number_to_rational/2)
|
||||
).
|
||||
|
||||
% If 0 <= Eps0 <= 1e-16 then the search is for "infinite" precision.
|
||||
number_to_rational(Eps0, Real0, Fraction) :-
|
||||
( var(Eps0) -> instantiation_error(number_to_rational/3)
|
||||
; \+ number(Eps0) -> type_error(number, Eps0, number_to_rational/3)
|
||||
; Eps0 < 0 -> domain_error(not_less_than_zero, Eps0, number_to_rational/3)
|
||||
; Eps_ is Eps0 rdiv 1,
|
||||
rational_numerator_denominator(Eps_, EpsN, EpsD),
|
||||
Eps = EpsN/EpsD
|
||||
),
|
||||
( var(Real0) -> instantiation_error(number_to_rational/3)
|
||||
; \+ number(Real0) -> type_error(number, Eps0, number_to_rational/3)
|
||||
; Real_ is Real0 rdiv 1,
|
||||
rational_numerator_denominator(Real_, RealN, RealD),
|
||||
Real = RealN/RealD
|
||||
),
|
||||
E0/E1 = Eps,
|
||||
P0/Q0 = Real,
|
||||
( P0 < 0 -> I1 is -1 + P0 // Q0
|
||||
; I1 is P0 // Q0
|
||||
),
|
||||
P1 is P0 mod Q0,
|
||||
Q1 = Q0,
|
||||
( P1 =:= 0 -> Fraction is I1 + 0 rdiv 1
|
||||
; Qn1n is max(P1 * E1 - Q1 * E0, 0),
|
||||
Qn1d is Q1 * E1,
|
||||
Qn1 = Qn1n/Qn1d,
|
||||
Qp1n is P1 * E1 + Q1 * E0,
|
||||
Qp1d = Qn1d,
|
||||
Qp1 = Qp1n/Qp1d,
|
||||
stern_brocot_(Qn1, Qp1, 0/1, 1/0, P2/Q2),
|
||||
Fraction is I1 + P2 rdiv Q2
|
||||
),
|
||||
!.
|
||||
|
||||
stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, C/D, Fraction) :-
|
||||
Fn1 is A + C,
|
||||
Fd1 is B + D,
|
||||
simplify_fraction(Fn1/Fd1, Fn/Fd),
|
||||
S1 is sign(Fn * Qnd - Fd * Qnn),
|
||||
S2 is sign(Fn * Qpd - Fd * Qpn),
|
||||
( S1 < 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, Fn/Fd, C/D, Fraction)
|
||||
; S2 > 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, Fn/Fd, Fraction)
|
||||
; Fraction = Fn/Fd
|
||||
).
|
||||
|
||||
simplify_fraction(A0/B0, A/B) :-
|
||||
G is gcd(A0, B0),
|
||||
A is A0 // G,
|
||||
B is B0 // G.
|
||||
|
||||
%% rational_numerator_denominator(+Fraction, -Numerator, -Denominator).
|
||||
%
|
||||
% True iff given a fraction Fraction, Numerator is the numerator of that fraction
|
||||
% and Denominator the denominator.
|
||||
rational_numerator_denominator(R, N, D) :-
|
||||
write_term_to_chars(R, [], Cs),
|
||||
append(Ns, [' ', r, d, i, v, ' '|Ds], Cs),
|
||||
number_chars(N, Ns),
|
||||
number_chars(D, Ds).
|
||||
|
||||
%% popcount(+Number, -Bits1).
|
||||
%
|
||||
% True iff given an integer Number, Bits1 is the amount of 1 bits the binary representation
|
||||
% of that number has.
|
||||
popcount(X, N) :-
|
||||
must_be(integer, X),
|
||||
'$popcount'(X, N).
|
||||
@@ -54,31 +54,27 @@
|
||||
|
||||
:- use_module(library(lists)).
|
||||
|
||||
/** <module> Binary associations
|
||||
/** Binary associations
|
||||
|
||||
Assocs are Key-Value associations implemented as a balanced binary tree
|
||||
(AVL tree).
|
||||
|
||||
@see library(pairs), library(rbtrees)
|
||||
@author R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
|
||||
Authors: R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
|
||||
*/
|
||||
|
||||
/*
|
||||
:- meta_predicate
|
||||
map_assoc(1, ?),
|
||||
map_assoc(2, ?, ?).
|
||||
*/
|
||||
|
||||
%! empty_assoc(?Assoc) is semidet.
|
||||
:- meta_predicate map_assoc(1, ?).
|
||||
:- meta_predicate map_assoc(2, ?, ?).
|
||||
|
||||
%% empty_assoc(?Assoc) is semidet.
|
||||
%
|
||||
% Is true if Assoc is the empty association list.
|
||||
% Is true if Assoc is the empty association list.
|
||||
|
||||
empty_assoc(t).
|
||||
|
||||
%! assoc_to_list(+Assoc, -Pairs) is det.
|
||||
%% assoc_to_list(+Assoc, -Pairs) is det.
|
||||
%
|
||||
% Translate Assoc to a list Pairs of Key-Value pairs. The keys
|
||||
% in Pairs are sorted in ascending order.
|
||||
% Translate Assoc to a list Pairs of Key-Value pairs. The keys
|
||||
% in Pairs are sorted in ascending order.
|
||||
|
||||
assoc_to_list(Assoc, List) :-
|
||||
assoc_to_list(Assoc, List, []).
|
||||
@@ -89,10 +85,10 @@ assoc_to_list(t(Key,Val,_,L,R), List, Rest) :-
|
||||
assoc_to_list(t, List, List).
|
||||
|
||||
|
||||
%! assoc_to_keys(+Assoc, -Keys) is det.
|
||||
%% assoc_to_keys(+Assoc, -Keys) is det.
|
||||
%
|
||||
% True if Keys is the list of keys in Assoc. The keys are sorted
|
||||
% in ascending order.
|
||||
% True if Keys is the list of keys in Assoc. The keys are sorted
|
||||
% in ascending order.
|
||||
|
||||
assoc_to_keys(Assoc, List) :-
|
||||
assoc_to_keys(Assoc, List, []).
|
||||
@@ -103,11 +99,11 @@ assoc_to_keys(t(Key,_,_,L,R), List, Rest) :-
|
||||
assoc_to_keys(t, List, List).
|
||||
|
||||
|
||||
%! assoc_to_values(+Assoc, -Values) is det.
|
||||
%% assoc_to_values(+Assoc, -Values) is det.
|
||||
%
|
||||
% True if Values is the list of values in Assoc. Values are
|
||||
% ordered in ascending order of the key to which they were
|
||||
% associated. Values may contain duplicates.
|
||||
% True if Values is the list of values in Assoc. Values are
|
||||
% ordered in ascending order of the key to which they were
|
||||
% associated. Values may contain duplicates.
|
||||
|
||||
assoc_to_values(Assoc, List) :-
|
||||
assoc_to_values(Assoc, List, []).
|
||||
@@ -117,12 +113,12 @@ assoc_to_values(t(_,Value,_,L,R), List, Rest) :-
|
||||
assoc_to_values(R, More, Rest).
|
||||
assoc_to_values(t, List, List).
|
||||
|
||||
%! is_assoc(+Assoc) is semidet.
|
||||
%% is_assoc(+Assoc) is semidet.
|
||||
%
|
||||
% True if Assoc is an association list. This predicate checks
|
||||
% that the structure is valid, elements are in order, and tree
|
||||
% is balanced to the extent guaranteed by AVL trees. I.e.,
|
||||
% branches of each subtree differ in depth by at most 1.
|
||||
% True if Assoc is an association list. This predicate checks
|
||||
% that the structure is valid, elements are in order, and tree
|
||||
% is balanced to the extent guaranteed by AVL trees. I.e.,
|
||||
% branches of each subtree differ in depth by at most 1.
|
||||
|
||||
is_assoc(Assoc) :-
|
||||
is_assoc(Assoc, _Min, _Max, _Depth).
|
||||
@@ -154,12 +150,10 @@ balance(=,-).
|
||||
balance(<,<).
|
||||
balance(>,>).
|
||||
|
||||
%! gen_assoc(?Key, +Assoc, ?Value) is nondet.
|
||||
%% gen_assoc(?Key, +Assoc, ?Value) is nondet.
|
||||
%
|
||||
% True if Key-Value is an association in Assoc. Enumerates keys in
|
||||
% ascending order on backtracking.
|
||||
%
|
||||
% @see get_assoc/3.
|
||||
% True if Key-Value is an association in Assoc. Enumerates keys in
|
||||
% ascending order on backtracking.
|
||||
|
||||
gen_assoc(Key, Assoc, Value) :-
|
||||
( ground(Key)
|
||||
@@ -174,11 +168,11 @@ gen_assoc_(Key, t(_,_,_,_,R), Val) :-
|
||||
gen_assoc_(Key, R, Val).
|
||||
|
||||
|
||||
%! get_assoc(+Key, +Assoc, -Value) is semidet.
|
||||
%% get_assoc(+Key, +Assoc, -Value) is semidet.
|
||||
%
|
||||
% True if Key-Value is an association in Assoc.
|
||||
% True if Key-Value is an association in Assoc.
|
||||
%
|
||||
% @error type_error(assoc, Assoc) if Assoc is not an association list.
|
||||
% Throws error: `type_error(assoc, Assoc)` if Assoc is not an association list.
|
||||
|
||||
get_assoc(Key, Assoc, Val) :-
|
||||
must_be(assoc, Assoc),
|
||||
@@ -204,9 +198,9 @@ get_assoc(>, Key, _, _, Tree, Val) :-
|
||||
% :- endif.
|
||||
|
||||
|
||||
%! get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet.
|
||||
%% get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet.
|
||||
%
|
||||
% True if Key-Val0 is in Assoc0 and Key-Val is in Assoc.
|
||||
% True if Key-Val0 is in Assoc0 and Key-Val is in Assoc.
|
||||
|
||||
get_assoc(Key, t(K,V,B,L,R), Val, t(K,NV,B,NL,NR), NVal) :-
|
||||
compare(Rel, Key, K),
|
||||
@@ -219,12 +213,12 @@ get_assoc(>, Key, V, L, R, Val, V, L, NR, NVal) :-
|
||||
get_assoc(Key, R, Val, NR, NVal).
|
||||
|
||||
|
||||
%! list_to_assoc(+Pairs, -Assoc) is det.
|
||||
%% list_to_assoc(+Pairs, -Assoc) is det.
|
||||
%
|
||||
% Create an association from a list Pairs of Key-Value pairs. List
|
||||
% must not contain duplicate keys.
|
||||
% Create an association from a list Pairs of Key-Value pairs. List
|
||||
% must not contain duplicate keys.
|
||||
%
|
||||
% @error domain_error(unique_key_pairs, List) if List contains duplicate keys
|
||||
% Throws error: `domain_error(unique_key_pairs, List)` if List contains duplicate keys
|
||||
|
||||
list_to_assoc(List, Assoc) :-
|
||||
( List = [] -> Assoc = t
|
||||
@@ -249,13 +243,13 @@ list_to_assoc(N, List, More, Depth, t(K,V,Balance,L,R)) :-
|
||||
compare(B, RDepth, LDepth),
|
||||
balance(B, Balance).
|
||||
|
||||
%! ord_list_to_assoc(+Pairs, -Assoc) is det.
|
||||
%% ord_list_to_assoc(+Pairs, -Assoc) is det.
|
||||
%
|
||||
% Assoc is created from an ordered list Pairs of Key-Value
|
||||
% pairs. The pairs must occur in strictly ascending order of
|
||||
% their keys.
|
||||
% Assoc is created from an ordered list Pairs of Key-Value
|
||||
% pairs. The pairs must occur in strictly ascending order of
|
||||
% their keys.
|
||||
%
|
||||
% @error domain_error(key_ordered_pairs, List) if pairs are not ordered.
|
||||
% Throws error: `domain_error(key_ordered_pairs, List)` if pairs are not ordered.
|
||||
|
||||
ord_list_to_assoc(Sorted, Assoc) :-
|
||||
( Sorted = [] -> Assoc = t
|
||||
@@ -266,9 +260,9 @@ ord_list_to_assoc(Sorted, Assoc) :-
|
||||
)
|
||||
).
|
||||
|
||||
%! ord_pairs(+Pairs) is semidet
|
||||
%% ord_pairs(+Pairs) is semidet
|
||||
%
|
||||
% True if Pairs is a list of Key-Val pairs strictly ordered by key.
|
||||
% True if Pairs is a list of Key-Val pairs strictly ordered by key.
|
||||
|
||||
ord_pairs([K-_V|Rest]) :-
|
||||
ord_pairs(Rest, K).
|
||||
@@ -277,9 +271,9 @@ ord_pairs([K-_V|Rest], K0) :-
|
||||
K0 @< K,
|
||||
ord_pairs(Rest, K).
|
||||
|
||||
%! map_assoc(:Pred, +Assoc) is semidet.
|
||||
%% map_assoc(:Pred, +Assoc) is semidet.
|
||||
%
|
||||
% True if Pred(Value) is true for all values in Assoc.
|
||||
% True if Pred(Value) is true for all values in Assoc.
|
||||
|
||||
map_assoc(Pred, T) :-
|
||||
map_assoc_(T, Pred).
|
||||
@@ -290,10 +284,10 @@ map_assoc_(t(_,Val,_,L,R), Pred) :-
|
||||
call(Pred, Val),
|
||||
map_assoc_(R, Pred).
|
||||
|
||||
%! map_assoc(:Pred, +Assoc0, ?Assoc) is semidet.
|
||||
%% map_assoc(:Pred, +Assoc0, ?Assoc) is semidet.
|
||||
%
|
||||
% Map corresponding values. True if Assoc is Assoc0 with Pred
|
||||
% applied to all corresponding pairs of of values.
|
||||
% Map corresponding values. True if Assoc is Assoc0 with Pred
|
||||
% applied to all corresponding pairs of of values.
|
||||
|
||||
map_assoc(Pred, T0, T) :-
|
||||
map_assoc_(T0, Pred, T).
|
||||
@@ -305,9 +299,9 @@ map_assoc_(t(Key,Val,B,L0,R0), Pred, t(Key,Ans,B,L1,R1)) :-
|
||||
map_assoc_(R0, Pred, R1).
|
||||
|
||||
|
||||
%! max_assoc(+Assoc, -Key, -Value) is semidet.
|
||||
%% max_assoc(+Assoc, -Key, -Value) is semidet.
|
||||
%
|
||||
% True if Key-Value is in Assoc and Key is the largest key.
|
||||
% True if Key-Value is in Assoc and Key is the largest key.
|
||||
|
||||
max_assoc(t(K,V,_,_,R), Key, Val) :-
|
||||
max_assoc(R, K, V, Key, Val).
|
||||
@@ -317,9 +311,9 @@ max_assoc(t(K,V,_,_,R), _, _, Key, Val) :-
|
||||
max_assoc(R, K, V, Key, Val).
|
||||
|
||||
|
||||
%! min_assoc(+Assoc, -Key, -Value) is semidet.
|
||||
%% min_assoc(+Assoc, -Key, -Value) is semidet.
|
||||
%
|
||||
% True if Key-Value is in assoc and Key is the smallest key.
|
||||
% True if Key-Value is in assoc and Key is the smallest key.
|
||||
|
||||
min_assoc(t(K,V,_,L,_), Key, Val) :-
|
||||
min_assoc(L, K, V, Key, Val).
|
||||
@@ -329,10 +323,10 @@ min_assoc(t(K,V,_,L,_), _, _, Key, Val) :-
|
||||
min_assoc(L, K, V, Key, Val).
|
||||
|
||||
|
||||
%! put_assoc(+Key, +Assoc0, +Value, -Assoc) is det.
|
||||
%% put_assoc(+Key, +Assoc0, +Value, -Assoc) is det.
|
||||
%
|
||||
% Assoc is Assoc0, except that Key is associated with
|
||||
% Value. This can be used to insert and change associations.
|
||||
% Assoc is Assoc0, except that Key is associated with
|
||||
% Value. This can be used to insert and change associations.
|
||||
|
||||
put_assoc(Key, A0, Value, A) :-
|
||||
insert(A0, Key, Value, A, _).
|
||||
@@ -364,11 +358,11 @@ table(< , right , - , no , no ) :- !.
|
||||
table(> , left , - , no , no ) :- !.
|
||||
table(> , right , - , no , yes ) :- !.
|
||||
|
||||
%! del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
|
||||
%% del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
|
||||
%
|
||||
% True if Key-Value is in Assoc0 and Key is the smallest key.
|
||||
% Assoc is Assoc0 with Key-Value removed. Warning: This will
|
||||
% succeed with _no_ bindings for Key or Val if Assoc0 is empty.
|
||||
% True if Key-Value is in Assoc0 and Key is the smallest key.
|
||||
% Assoc is Assoc0 with Key-Value removed. Warning: This will
|
||||
% succeed with _no_ bindings for Key or Val if Assoc0 is empty.
|
||||
|
||||
del_min_assoc(Tree, Key, Val, NewTree) :-
|
||||
del_min_assoc(Tree, Key, Val, NewTree, _DepthChanged).
|
||||
@@ -378,11 +372,11 @@ del_min_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :-
|
||||
del_min_assoc(L, Key, Val, NewL, LeftChanged),
|
||||
deladjust(LeftChanged, t(K,V,B,NewL,R), left, NewTree, Changed).
|
||||
|
||||
%! del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
|
||||
%% del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
|
||||
%
|
||||
% True if Key-Value is in Assoc0 and Key is the greatest key.
|
||||
% Assoc is Assoc0 with Key-Value removed. Warning: This will
|
||||
% succeed with _no_ bindings for Key or Val if Assoc0 is empty.
|
||||
% True if Key-Value is in Assoc0 and Key is the greatest key.
|
||||
% Assoc is Assoc0 with Key-Value removed. Warning: This will
|
||||
% succeed with _no_ bindings for Key or Val if Assoc0 is empty.
|
||||
|
||||
del_max_assoc(Tree, Key, Val, NewTree) :-
|
||||
del_max_assoc(Tree, Key, Val, NewTree, _DepthChanged).
|
||||
@@ -392,10 +386,10 @@ del_max_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :-
|
||||
del_max_assoc(R, Key, Val, NewR, RightChanged),
|
||||
deladjust(RightChanged, t(K,V,B,L,NewR), right, NewTree, Changed).
|
||||
|
||||
%! del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet.
|
||||
%% del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet.
|
||||
%
|
||||
% True if Key-Value is in Assoc0. Assoc is Assoc0 with
|
||||
% Key-Value removed.
|
||||
% True if Key-Value is in Assoc0. Assoc is Assoc0 with
|
||||
% Key-Value removed.
|
||||
|
||||
del_assoc(Key, A0, Value, A) :-
|
||||
delete(A0, Key, Value, A, _).
|
||||
114
src/lib/atts.pl
Normal file
114
src/lib/atts.pl
Normal file
@@ -0,0 +1,114 @@
|
||||
:- module(atts, [op(1199, fx, attribute),
|
||||
term_attributed_variables/2]).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(terms)).
|
||||
|
||||
/* represent the list of attributes belonging to a variable,
|
||||
of a particular module, as a list of terms of the form
|
||||
Module:put_atts(V, ListOfAtts). */
|
||||
'$default_attr_list'(Module, V) -->
|
||||
( { Module:get_atts(V, Attributes) } ->
|
||||
'$default_attr_list'(Attributes, Module, V)
|
||||
; []
|
||||
).
|
||||
|
||||
'$default_attr_list'([PG | PGs], Module, AttrVar) -->
|
||||
[Module:put_atts(AttrVar, PG)],
|
||||
'$default_attr_list'(PGs, Module, AttrVar).
|
||||
'$default_attr_list'([], _, _) --> [].
|
||||
|
||||
'$absent_attr'(V, Module, Attr) :-
|
||||
( '$get_from_attr_list'(V, Module, Attr) ->
|
||||
false
|
||||
; true
|
||||
).
|
||||
|
||||
'$copy_attr_list'(L, _Module, []) :- var(L), !.
|
||||
'$copy_attr_list'([Module0:Att|Atts], Module, CopiedAtts) :-
|
||||
( Module0 == Module ->
|
||||
CopiedAtts = [Att|CopiedAtts0],
|
||||
'$copy_attr_list'(Atts, Module, CopiedAtts0)
|
||||
; '$copy_attr_list'(Atts, Module, CopiedAtts)
|
||||
).
|
||||
|
||||
user:term_expansion(Term0, Terms) :-
|
||||
nonvar(Term0),
|
||||
Term0 = (:- attribute Atts),
|
||||
nonvar(Atts),
|
||||
prolog_load_context(module, Module),
|
||||
phrase(expand_terms(Atts, Module), Terms).
|
||||
|
||||
expand_terms(Atts, Module) -->
|
||||
put_attrs_var_check,
|
||||
put_attrs(Atts, Module),
|
||||
get_attrs_var_check(Module),
|
||||
get_attrs(Atts, Module).
|
||||
|
||||
put_attrs_var_check -->
|
||||
[(put_atts(Var, Attr) :- nonvar(Var),
|
||||
throw(error(uninstantiation_error(Var), put_atts/2))),
|
||||
(put_atts(Var, Attr) :- var(Attr),
|
||||
throw(error(instantiation_error, put_atts/2)))].
|
||||
|
||||
get_attrs_var_check(Module) -->
|
||||
[(get_atts(Var, Attr) :- nonvar(Var),
|
||||
throw(error(uninstantiation_error(Var), get_atts/2))),
|
||||
(get_atts(Var, Attr) :- var(Attr),
|
||||
!,
|
||||
'$get_attr_list'(Var, Ls),
|
||||
nonvar(Ls),
|
||||
atts:'$copy_attr_list'(Ls, Module, Attr))].
|
||||
|
||||
put_attrs(Name/Arity, Module) -->
|
||||
put_attr(Name, Arity, Module),
|
||||
[(put_atts(Var, Attr) :- lists:maplist(Module:put_atts(Var), Attr), !)].
|
||||
put_attrs((Name/Arity, Atts), Module) -->
|
||||
{ nonvar(Atts) },
|
||||
put_attr(Name, Arity, Module),
|
||||
put_attrs(Atts, Module).
|
||||
|
||||
get_attrs(Name/Arity, Module) -->
|
||||
get_attr(Name, Arity, Module).
|
||||
get_attrs((Name/Arity, Atts), Module) -->
|
||||
{ nonvar(Atts) },
|
||||
get_attr(Name, Arity, Module),
|
||||
get_attrs(Atts, Module).
|
||||
|
||||
put_attr(Name, Arity, Module) -->
|
||||
{ functor(Attr, Name, Arity) },
|
||||
[(put_atts(V, +Attr) :-
|
||||
!,
|
||||
'$put_to_attr_list'(V, Module, Attr)),
|
||||
(put_atts(V, Attr) :-
|
||||
!,
|
||||
'$put_to_attr_list'(V, Module, Attr)),
|
||||
(put_atts(V, -Attr) :-
|
||||
!,
|
||||
'$del_from_attr_list'(V, Module, Attr))].
|
||||
|
||||
get_attr(Name, Arity, Module) -->
|
||||
{ functor(Attr, Name, Arity) },
|
||||
[(get_atts(V, +Attr) :-
|
||||
!,
|
||||
functor(Attr, _, _),
|
||||
atts:'$get_from_attr_list'(V, Module, Attr)),
|
||||
(get_atts(V, Attr) :-
|
||||
!,
|
||||
functor(Attr, _, _),
|
||||
atts:'$get_from_attr_list'(V, Module, Attr)),
|
||||
(get_atts(V, -Attr) :-
|
||||
!,
|
||||
functor(Attr, _, _),
|
||||
atts:'$absent_attr'(V, Module, Attr))].
|
||||
|
||||
user:goal_expansion(Term, M:put_atts(Var, Attr)) :-
|
||||
nonvar(Term),
|
||||
Term = put_atts(Var, M, Attr).
|
||||
user:goal_expansion(Term, M:get_atts(Var, Attr)) :-
|
||||
nonvar(Term),
|
||||
Term = get_atts(Var, M, Attr).
|
||||
|
||||
term_attributed_variables(Term, Vars) :-
|
||||
'$term_attributed_variables'(Term, Vars).
|
||||
@@ -1,3 +1,10 @@
|
||||
/** Predicates that generate integers
|
||||
|
||||
These predicates can be used to reason about integers in a reduced domain that
|
||||
follow some property. `library(clpz)` provides another way of reasoning about
|
||||
integers that may also be interesting.
|
||||
*/
|
||||
|
||||
:- module(between, [between/3, gen_int/1, gen_nat/1, numlist/2, numlist/3, repeat/1]).
|
||||
|
||||
%% TODO: numlist/5.
|
||||
@@ -5,24 +12,52 @@
|
||||
:- use_module(library(lists), [length/2]).
|
||||
:- use_module(library(error)).
|
||||
|
||||
%% between(+Lower, +Upper, -X).
|
||||
%
|
||||
% Given Lower and Upper are both integer numbers, true iff X is an integer so that _Lower =< X =< Upper_.
|
||||
% Can be used both to check if X is between Lower and Upper or to generate an integer between
|
||||
% Lower and Upper.
|
||||
%
|
||||
% Examples:
|
||||
%
|
||||
% ```
|
||||
% ?- between(10, 20, 15).
|
||||
% true.
|
||||
% ?- between(10, 20, 25).
|
||||
% false.
|
||||
% ?- between(3, 5, X).
|
||||
% X = 3
|
||||
% ; X = 4
|
||||
% ; X = 5.
|
||||
% ```
|
||||
between(Lower, Upper, X) :-
|
||||
must_be(integer, Lower),
|
||||
must_be(integer, Upper),
|
||||
can_be(integer, X),
|
||||
between_(Lower, Upper, X).
|
||||
( nonvar(X) ->
|
||||
Lower =< X,
|
||||
X =< Upper
|
||||
; Lower =< Upper,
|
||||
between_(Lower, Upper, X)
|
||||
).
|
||||
|
||||
between_(Lower, Upper, Lower) :-
|
||||
Lower =< Upper.
|
||||
between_(Lower1, Upper, X) :-
|
||||
Lower1 < Upper,
|
||||
Lower2 is Lower1 + 1,
|
||||
between_(Lower2, Upper, X).
|
||||
between_(Lower, Upper, Lower1) :-
|
||||
Lower < Upper,
|
||||
!,
|
||||
( Lower1 = Lower
|
||||
; Lower0 is Lower + 1,
|
||||
between_(Lower0, Upper, Lower1)
|
||||
).
|
||||
between_(Lower, Lower, Lower).
|
||||
|
||||
enumerate_nats(I, I).
|
||||
enumerate_nats(I0, N) :-
|
||||
I1 is I0 + 1,
|
||||
enumerate_nats(I1, N).
|
||||
|
||||
%% gen_nat(?N)
|
||||
%
|
||||
% True iff N is a natural number.
|
||||
gen_nat(N) :-
|
||||
can_be(integer, N),
|
||||
( var(N) -> enumerate_nats(0, N)
|
||||
@@ -37,6 +72,9 @@ enumerate_ints(I0, N) :-
|
||||
I1 is I0 + 1,
|
||||
enumerate_ints(I1, N).
|
||||
|
||||
%% gen_int(?N)
|
||||
%
|
||||
% True iff N is an integer.
|
||||
gen_int(N) :-
|
||||
can_be(integer, N),
|
||||
( var(N) -> enumerate_ints(0, N)
|
||||
@@ -48,9 +86,24 @@ repeat_integer(N) :-
|
||||
repeat_integer(N0) :-
|
||||
N0 > 0, N1 is N0 - 1, repeat_integer(N1).
|
||||
|
||||
%% repeat(+N)
|
||||
%
|
||||
% Succeeds N times. This predicate is only included for compatibility and *should not be used*
|
||||
% because it lacks a declarative interpretation.
|
||||
repeat(N) :-
|
||||
must_be(integer, N), repeat_integer(N).
|
||||
|
||||
%% numlist(?Upper, ?List)
|
||||
%
|
||||
% True iff List is the list of integers _[1, ..., Upper]_. Example:
|
||||
%
|
||||
% ```
|
||||
% ?- numlist(X, Y).
|
||||
% X = 1, Y = [1],
|
||||
% ; X = 2, Y = [1,2]
|
||||
% ; X = 3, Y = [1,2,3]
|
||||
% ; ... .
|
||||
% ```
|
||||
numlist(Upper, List) :-
|
||||
( integer(Upper) -> findall(X, between(1, Upper, X), List)
|
||||
; List = [_|_], length(List, Upper), findall(X, between(1, Upper, X), List)
|
||||
@@ -99,5 +152,14 @@ gen_ints(L, U) :-
|
||||
),
|
||||
L =< U.
|
||||
|
||||
%% numlist(?Lower, ?Upper, ?List).
|
||||
%
|
||||
% True iff List is a list of the form _[Lower, ..., Upper]_.
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% ?- numlist(5, 10, X).
|
||||
% X = [5,6,7,8,9,10].
|
||||
% ```
|
||||
numlist(Lower, Upper, List) :-
|
||||
gen_ints(Lower, Upper), findall(X, between(Lower, Upper, X), List).
|
||||
2261
src/lib/builtins.pl
Normal file
2261
src/lib/builtins.pl
Normal file
File diff suppressed because it is too large
Load Diff
395
src/lib/charsio.pl
Normal file
395
src/lib/charsio.pl
Normal file
@@ -0,0 +1,395 @@
|
||||
/** High-level predicates to work with chars and strings
|
||||
|
||||
This module contains predicates that relates strings of chars
|
||||
to other representations, as well as high-level predicates to
|
||||
read and write chars.
|
||||
|
||||
*/
|
||||
|
||||
:- module(charsio, [char_type/2,
|
||||
chars_utf8bytes/2,
|
||||
get_single_char/1,
|
||||
get_n_chars/3,
|
||||
get_line_to_chars/3,
|
||||
read_from_chars/2,
|
||||
read_term_from_chars/3,
|
||||
write_term_to_chars/3,
|
||||
chars_base64/3]).
|
||||
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(lists)).
|
||||
:- use_module(library(between)).
|
||||
:- use_module(library(iso_ext), [partial_string/1,partial_string/3]).
|
||||
|
||||
fabricate_var_name(VarType, VarName, N) :-
|
||||
char_code('A', AC),
|
||||
LN is N mod 26 + AC,
|
||||
char_code(LC, LN),
|
||||
NN is N // 26,
|
||||
( NN =:= 0 ->
|
||||
( VarType == fabricated ->
|
||||
atom_chars(VarName, ['_', LC])
|
||||
; VarType == numbervars ->
|
||||
atom_chars(VarName, [LC])
|
||||
)
|
||||
; number_chars(NN, NNChars),
|
||||
( VarType == fabricated ->
|
||||
atom_chars(VarName, ['_', LC | NNChars])
|
||||
; VarType == numbervars ->
|
||||
atom_chars(VarName, [LC | NNChars])
|
||||
)
|
||||
).
|
||||
|
||||
var_list_contains_name([VarName = _ | VarList], VarName0) :-
|
||||
( VarName == VarName0 -> true
|
||||
; var_list_contains_name(VarList, VarName0)
|
||||
).
|
||||
|
||||
var_list_contains_variable([_ = Var | VarList], Var0) :-
|
||||
( Var == Var0 -> true
|
||||
; var_list_contains_variable(VarList, Var0)
|
||||
).
|
||||
|
||||
make_new_var_name(VarType, V, VarName, N, N1, VarList) :-
|
||||
fabricate_var_name(VarType, VarName0, N),
|
||||
( var_list_contains_name(VarList, VarName0) ->
|
||||
N0 is N + 1,
|
||||
make_new_var_name(VarType, V, VarName, N0, N1, VarList)
|
||||
; VarName = VarName0,
|
||||
N1 is N + 1
|
||||
).
|
||||
|
||||
extend_var_list(Vars, VarList, NewVarList, VarType) :-
|
||||
extend_var_list_(Vars, 0, VarList, NewVarList0, VarType),
|
||||
append(VarList, NewVarList0, NewVarList).
|
||||
|
||||
extend_var_list_([], _, _, [], _).
|
||||
extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
|
||||
( var_list_contains_variable(VarList, V) ->
|
||||
extend_var_list_(Vs, N, VarList, NewVarList, VarType)
|
||||
; make_new_var_name(VarType, V, VarName, N, N1, VarList),
|
||||
NewVarList = [VarName = V | NewVarList0],
|
||||
extend_var_list_(Vs, N1, VarList, NewVarList0, VarType)
|
||||
).
|
||||
|
||||
|
||||
%% char_type(?Char, ?Type).
|
||||
%
|
||||
% Type is one of the categories that Char fits in.
|
||||
% At least one of the arguments must be ground.
|
||||
% Possible categories are:
|
||||
%
|
||||
% - `alnum`
|
||||
% - `alpha`
|
||||
% - `alphabetic`
|
||||
% - `alphanumeric`
|
||||
% - `ascii`
|
||||
% - `ascii_graphic`
|
||||
% - `ascii_punctuation`
|
||||
% - `binary_digit`
|
||||
% - `control`
|
||||
% - `decimal_digit`
|
||||
% - `exponent`
|
||||
% - `graphic`
|
||||
% - `graphic_token`
|
||||
% - `hexadecimal_digit`
|
||||
% - `layout`
|
||||
% - `lower`
|
||||
% - `meta`
|
||||
% - `numeric`
|
||||
% - `octal_digit`
|
||||
% - `octet`
|
||||
% - `prolog`
|
||||
% - `sign`
|
||||
% - `solo`
|
||||
% - `symbolic_control`
|
||||
% - `symbolic_hexadecimal`
|
||||
% - `upper`
|
||||
% - `lower(Lower)`
|
||||
% - `upper(Upper)`
|
||||
% - `whitespace`
|
||||
%
|
||||
% An example:
|
||||
%
|
||||
% ```
|
||||
% ?- char_type(a, Type).
|
||||
% Type = alnum
|
||||
% ; Type = alpha
|
||||
% ; Type = alphabetic
|
||||
% ; Type = alphanumeric
|
||||
% ; Type = ascii
|
||||
% ; Type = ascii_graphic
|
||||
% ; Type = hexadecimal_digit
|
||||
% ; Type = lower
|
||||
% ; Type = octet
|
||||
% ; Type = prolog
|
||||
% ; Type = symbolic_control
|
||||
% ; Type = lower("a")
|
||||
% ; Type = upper("A")
|
||||
% ; false.
|
||||
% ```
|
||||
%
|
||||
% Note that uppercase and lowercase transformations use a string. This is because
|
||||
% some characters do not map 1:1 between lowercase and uppercase.
|
||||
char_type(Char, Type) :-
|
||||
can_be(character, Char),
|
||||
( \+ ctype(Type) ->
|
||||
domain_error(char_type, Type, char_type/2)
|
||||
; true
|
||||
),
|
||||
( ground(Char) ->
|
||||
ctype(Type),
|
||||
'$char_type'(Char, Type)
|
||||
; ground(Type) ->
|
||||
ccode(Code),
|
||||
char_code(Char, Code),
|
||||
'$char_type'(Char, Type)
|
||||
; must_be(character, Char)
|
||||
).
|
||||
|
||||
|
||||
% 0xD800 to 0xDFFF are surrogate code points used by UTF-16.
|
||||
|
||||
ccode(Code) :- between(0, 0xD7FF, Code).
|
||||
ccode(Code) :- between(0xE000, 0x10FFFF, Code).
|
||||
|
||||
ctype(alnum).
|
||||
ctype(alpha).
|
||||
ctype(alphabetic).
|
||||
ctype(alphanumeric).
|
||||
ctype(ascii).
|
||||
ctype(ascii_graphic).
|
||||
ctype(ascii_punctuation).
|
||||
ctype(binary_digit).
|
||||
ctype(control).
|
||||
ctype(decimal_digit).
|
||||
ctype(exponent).
|
||||
ctype(graphic).
|
||||
ctype(graphic_token).
|
||||
ctype(hexadecimal_digit).
|
||||
ctype(layout).
|
||||
ctype(lower).
|
||||
ctype(meta).
|
||||
ctype(numeric).
|
||||
ctype(octal_digit).
|
||||
ctype(octet).
|
||||
ctype(prolog).
|
||||
ctype(sign).
|
||||
ctype(solo).
|
||||
ctype(symbolic_control).
|
||||
ctype(symbolic_hexadecimal).
|
||||
ctype(lower(_)).
|
||||
ctype(upper(_)).
|
||||
ctype(upper).
|
||||
ctype(whitespace).
|
||||
|
||||
|
||||
%% get_single_char(-Char).
|
||||
%
|
||||
% Gets a single char from the current input stream.
|
||||
get_single_char(C) :-
|
||||
( var(C) -> '$get_single_char'(C)
|
||||
; atom_length(C, 1) -> '$get_single_char'(C)
|
||||
; type_error(in_character, C, get_single_char/1)
|
||||
).
|
||||
|
||||
%% read_from_chars(+Chars, -Term).
|
||||
%
|
||||
% Given a string made of chars which contains a representation of
|
||||
% a Prolog term, Term is the Prolog term represented. Example:
|
||||
%
|
||||
% ```
|
||||
% ?- read_from_chars("f(x,y).", X).
|
||||
% X = f(x,y).
|
||||
% ```
|
||||
read_from_chars(Chars, Term) :-
|
||||
must_be(chars, Chars),
|
||||
must_be(var, Term),
|
||||
'$read_from_chars'(Chars, Term).
|
||||
|
||||
%% read_term_from_chars(+Chars, -Term, +Options).
|
||||
%
|
||||
% Like `read_from_chars`, except the reader is configured according to
|
||||
% `Options` which are those of `read_term`.
|
||||
%
|
||||
% ```
|
||||
% ?- read_term_from_chars("f(X,y).", T, [variable_names(['X'=X])]).
|
||||
% T = f(X,y).
|
||||
% ```
|
||||
read_term_from_chars(Chars, Term, Options) :-
|
||||
must_be(chars, Chars),
|
||||
must_be(var, Term),
|
||||
builtins:parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term_from_chars/3),
|
||||
'$read_term_from_chars'(Chars, Term, Singletons, Variables, VariableNames).
|
||||
|
||||
%% write_term_to_chars(+Term, +Options, -Chars).
|
||||
%
|
||||
% Given a Term which is a Prolog term and a set of options, Chars is
|
||||
% string representation of that term. Options available are:
|
||||
%
|
||||
% * `ignore_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false`
|
||||
% (default), operators do not use that generic term representation.
|
||||
% * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses.
|
||||
% If N = 0 (default), there's no limit.
|
||||
% * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false.
|
||||
% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog syntax, are quoted. Default is false.
|
||||
% * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`.
|
||||
% * `double_quotes(+Boolean)` if true, strings are printed in double quotes rather than with list notation. Default is false.
|
||||
write_term_to_chars(_, Options, _) :-
|
||||
var(Options), instantiation_error(write_term_to_chars/3).
|
||||
write_term_to_chars(Term, Options, Chars) :-
|
||||
builtins:parse_write_options(Options,
|
||||
[DoubleQuotes, IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames],
|
||||
write_term_to_chars/3),
|
||||
( nonvar(Chars) ->
|
||||
throw(error(uninstantiation_error(Chars), write_term_to_chars/3))
|
||||
;
|
||||
true
|
||||
),
|
||||
term_variables(Term, Vars),
|
||||
extend_var_list(Vars, VNNames, NewVarNames, numbervars),
|
||||
'$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth, DoubleQuotes).
|
||||
|
||||
% Encodes Ch character to list of Bytes.
|
||||
char_utf8bytes(Ch, Bytes) :-
|
||||
char_code(Ch, Code),
|
||||
phrase(code_to_utf8(Code), Bytes).
|
||||
|
||||
code_to_utf8(Code) --> {Code @< 0x80}, [Code], !.
|
||||
code_to_utf8(Code) --> {Code @< 0x800}, encode(Code, 0xC0, 2), !.
|
||||
code_to_utf8(Code) --> {Code @< 0x10000}, encode(Code, 0xE0, 3), !.
|
||||
code_to_utf8(Code) --> {Code @< 0x110000}, encode(Code, 0xF0, 4), !.
|
||||
|
||||
encode(_, _, 0) --> !.
|
||||
encode(Code, Prefix, Nb) -->
|
||||
{ Nb1 is Nb - 1, Byte is Prefix \/ ((Code >> (6 * Nb1)) /\ 0x3F) },
|
||||
[Byte], encode(Code, 0x80, Nb1).
|
||||
|
||||
% Maps characters and UTF-8 bytes.
|
||||
% If Cs is a variable, parses Bs as a list of UTF-8 bytes.
|
||||
% Otherwise, transform the list of characters Cs to UTF-8 bytes.
|
||||
|
||||
%% chars_utf8bytes(?Chars, ?Bytes).
|
||||
%
|
||||
% Maps a string made of chars with a list of UTF-8 bytes. Some examples:
|
||||
%
|
||||
% ```
|
||||
% ?- chars_utf8bytes("Prolog", X).
|
||||
% X = [80,114,111,108,111,103].
|
||||
% ?- chars_utf8bytes(X, [226, 136, 145]).
|
||||
% X = "∑".
|
||||
% ```
|
||||
chars_utf8bytes(Cs, Bs) :-
|
||||
var(Cs), must_be(list, Bs) ->
|
||||
once(phrase(decode_utf8(Cs), Bs))
|
||||
; (must_be(list, Cs),
|
||||
maplist(must_be(atom), Cs),
|
||||
maplist(char_utf8bytes, Cs, Bss),
|
||||
append(Bss, Bs)).
|
||||
|
||||
decode_utf8([]) --> [].
|
||||
decode_utf8(Chars) --> leading(Nb, Code), continuation(Code, Chars, Nb).
|
||||
|
||||
leading(1, Byte) --> [Byte], {Byte /\ 0x80 =:= 0}.
|
||||
leading(2, Code) --> [Byte], {Byte /\ 0xE0 =:= 0xC0, Code is Byte - 0xC0}.
|
||||
leading(3, Code) --> [Byte], {Byte /\ 0xF0 =:= 0xE0, Code is Byte - 0xE0}.
|
||||
leading(4, Code) --> [Byte], {Byte /\ 0xF8 =:= 0xF0, Code is Byte - 0xF0}.
|
||||
leading(1, 0xFFFD) --> [_]. % invalid first byte
|
||||
|
||||
continuation(Code, [H|T], 1) --> {char_code(H, Code)}, decode_utf8(T).
|
||||
continuation(Code, Chars, Nb) --> [Byte],
|
||||
{Nb1 is Nb - 1, Byte /\ 0xC0 =:= 0x80, NextCode is (Code << 6) \/ (Byte - 0x80)},
|
||||
continuation(NextCode, Chars, Nb1).
|
||||
|
||||
% invalid continuation byte
|
||||
% each remaining continuation byte (if any) will raise 0xFFFD too
|
||||
continuation(_, ['\xFFFD\'|T], _) --> [_], decode_utf8(T).
|
||||
|
||||
%% get_line_to_chars(+Stream, -Chars, +InitialChars).
|
||||
%
|
||||
% Reads chars from stream Stream until it finds a `\n` character.
|
||||
% InitialChars will be appended at the end of Chars
|
||||
get_line_to_chars(Stream, Cs0, Cs) :-
|
||||
'$get_n_chars'(Stream, 1, Char), % this also works for binary streams
|
||||
( Char == [] -> Cs0 = Cs
|
||||
; Char = [C],
|
||||
Cs0 = [C|Rest],
|
||||
( C == '\n' -> Rest = Cs
|
||||
; get_line_to_chars(Stream, Rest, Cs)
|
||||
)
|
||||
).
|
||||
|
||||
%% get_n_chars(+Stream, ?N, -Chars).
|
||||
%
|
||||
% Read N chars from stream Stream. N can be an integer, in that case
|
||||
% only N chars are read, or a variable, unifying N with the number of chars
|
||||
% read until it found EOF.
|
||||
get_n_chars(Stream, N, Cs) :-
|
||||
can_be(integer, N),
|
||||
( var(N) ->
|
||||
get_to_eof(Stream, Cs),
|
||||
length(Cs, N)
|
||||
; N >= 0,
|
||||
'$get_n_chars'(Stream, N, Cs)
|
||||
).
|
||||
|
||||
get_n_chars_wrapper(Stream, N, Cs) :-
|
||||
'$get_n_chars'(Stream, N, Cs).
|
||||
|
||||
get_to_eof(Stream, Cs) :-
|
||||
catch(get_n_chars_wrapper(Stream, 512, Cs0),
|
||||
error(syntax_error(unexpected_end_of_file), _),
|
||||
Cs0 = []),
|
||||
( Cs0 == [] -> Cs = []
|
||||
; partial_string(Cs0, Cs, Rest),
|
||||
get_to_eof(Stream, Rest)
|
||||
).
|
||||
|
||||
%% chars_base64(?Chars, ?Base64, +Options).
|
||||
%
|
||||
% Relation between a list of characters Cs and its Base64 encoding Bs,
|
||||
% also a list of characters.
|
||||
%
|
||||
% At least one of the arguments must be instantiated.
|
||||
%
|
||||
% Options are:
|
||||
%
|
||||
% - `padding(Boolean)`
|
||||
% Whether to use padding: true (the default) or false.
|
||||
% - `charset(C)`
|
||||
% Either 'standard' (RFC 4648 §4, the default) or 'url' (RFC 4648 §5).
|
||||
%
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% ?- chars_base64("hello", Bs, []).
|
||||
% Bs = "aGVsbG8=".
|
||||
% ```
|
||||
|
||||
chars_base64(Cs, Bs, Options) :-
|
||||
must_be(list, Options),
|
||||
( member(O, Options), var(O) ->
|
||||
instantiation_error(chars_base64/3)
|
||||
; ( member(padding(Padding), Options) -> true
|
||||
; Padding = true
|
||||
),
|
||||
( member(charset(Charset), Options) -> true
|
||||
; Charset = standard
|
||||
)
|
||||
),
|
||||
must_be(boolean, Padding),
|
||||
must_be(atom, Charset),
|
||||
( member(Charset, [standard,url]) -> true
|
||||
; domain_error(charset, Charset, chars_base64/3)
|
||||
),
|
||||
( var(Cs) ->
|
||||
must_be(chars, Bs),
|
||||
'$chars_base64'(Cs, Bs, Padding, Charset)
|
||||
; must_be(chars, Cs),
|
||||
( '$first_non_octet'(Cs, N) ->
|
||||
domain_error(octet_character, N, chars_base64/3)
|
||||
; '$chars_base64'(Cs, Bs, Padding, Charset)
|
||||
)
|
||||
).
|
||||
1964
src/lib/clpb.pl
Normal file
1964
src/lib/clpb.pl
Normal file
File diff suppressed because it is too large
Load Diff
8006
src/lib/clpz.pl
Normal file
8006
src/lib/clpz.pl
Normal file
File diff suppressed because it is too large
Load Diff
32
src/lib/cont.pl
Normal file
32
src/lib/cont.pl
Normal file
@@ -0,0 +1,32 @@
|
||||
:- module(cont, [reset/3, shift/1]).
|
||||
|
||||
:- meta_predicate reset(0, ?, ?).
|
||||
|
||||
reset(Goal, Ball, Cont) :-
|
||||
call(Goal),
|
||||
'$reset_cont_marker',
|
||||
'$bind_from_register'(Cont, 3),
|
||||
'$bind_from_register'(Ball, 4).
|
||||
|
||||
shift(Ball) :-
|
||||
'$nextEP'(first, E, P),
|
||||
get_chunks(E, P, L),
|
||||
( L == [] ->
|
||||
Cont = cont(true)
|
||||
; Cont = cont(cont:call_continuation(L))
|
||||
),
|
||||
'$write_cont_and_term'(_, _, Cont, Ball),
|
||||
'$unwind_environments'.
|
||||
|
||||
get_chunks(E, P, L) :-
|
||||
( '$points_to_cont_reset_marker'(P) ->
|
||||
L = []
|
||||
; '$get_cont_chunk'(E,P,TB),
|
||||
L = [TB|Rest],
|
||||
'$nextEP'(E, NextE, NextP),
|
||||
get_chunks(NextE, NextP, Rest)
|
||||
).
|
||||
|
||||
call_continuation(L) :- '$call_continuation'(L).
|
||||
|
||||
'$write_cont_and_term'(_, _, _, _).
|
||||
932
src/lib/crypto.pl
Normal file
932
src/lib/crypto.pl
Normal file
@@ -0,0 +1,932 @@
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Written 2020-2024 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_seed_keypair/2, % +Seed, -KeyPair
|
||||
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.
|
||||
%
|
||||
% - `hmac(+Key)`
|
||||
% Compute a hash-based message authentication code (HMAC) using
|
||||
% Key, a list of bytes. This option is currently supported for
|
||||
% algorithms `sha256`, `sha384` and `sha512`.
|
||||
%
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% ?- 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)
|
||||
),
|
||||
( member(HMAC, Options0), nonvar(HMAC), HMAC = hmac(Ks) ->
|
||||
must_be_bytes(Ks, crypto_data_hash/3),
|
||||
hmac_algorithm(A),
|
||||
'$crypto_hmac'(Data, Encoding, Ks, HashBytes, A)
|
||||
; '$crypto_data_hash'(Data, Encoding, HashBytes, A)
|
||||
),
|
||||
hex_bytes(Hash, HashBytes).
|
||||
|
||||
hmac_algorithm(sha256).
|
||||
hmac_algorithm(sha384).
|
||||
hmac_algorithm(sha512).
|
||||
|
||||
options_data_chars(Options, Data, Chars, Encoding) :-
|
||||
option(encoding(Encoding), Options, utf8),
|
||||
must_be(atom, Encoding),
|
||||
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_seed_keypair(+Seed, -Pair)
|
||||
%
|
||||
% Use Seed to deterministically generate an Ed25519 key pair Pair, a
|
||||
% list of characters. Seed must be a list of 32 bytes. It can be
|
||||
% chosen at random (using for example `crypto_n_random_bytes/2`) or
|
||||
% derived from input keying material (IKM) using for example
|
||||
% `crypto_data_hkdf/4`. The pair contains the private key and must be
|
||||
% kept absolutely secret. Pair can be used for signing. Its public
|
||||
% key can be obtained with `ed25519_keypair_public_key/2`.
|
||||
|
||||
ed25519_seed_keypair(Seed, Pair) :-
|
||||
must_be_bytes(Seed, ed25519_keypair_from_seed/2),
|
||||
length(Seed, 32),
|
||||
'$ed25519_seed_to_public_key'(Seed, Public),
|
||||
maplist(char_code, Public, PublicBytes),
|
||||
phrase(ed25519_PKCS8v2(Seed,PublicBytes), DERs),
|
||||
maplist(char_code, Pair, DERs).
|
||||
|
||||
% DER (and hence BER) encoding of an Ed25519 private key and
|
||||
% corresponding public key in PKCS#8v2 format (RFC 5958) as specified
|
||||
% in RFC 8410.
|
||||
|
||||
ed25519_PKCS8v2(Seed, PublicBytes) -->
|
||||
[0x30,81], % a SEQUENCE of 81 bytes follows
|
||||
|
||||
% the publicKey is present, hence we set version to v2
|
||||
[2,1,1], % the integer 1 denoting version 2 (awesome design!)
|
||||
|
||||
% privateKeyAlgorithm: SEQUENCE
|
||||
[0x30,5], % a SEQUENCE of 5 bytes follows
|
||||
[6,3], % an OBJECT IDENTIFIER of 3 bytes follows
|
||||
[43,101,112], % OID of Ed25519
|
||||
|
||||
% privateKey: OCTET STRING
|
||||
[4,34], % an OCTET STRING of 34 bytes follows
|
||||
[4,32], % an OCTET STRING of 32 bytes follows
|
||||
seq(Seed), % the seed is the private key
|
||||
|
||||
% publicKey: [1] IMPLICIT BIT STRING; context-specific, hence bit 7 set
|
||||
[0b10000001], % the public key follows
|
||||
[33], % a BIT STRING of length 33 follows
|
||||
[0], % 32 bytes is divisible by 8, hence 0 unused bits
|
||||
seq(PublicBytes).
|
||||
|
||||
%% ed25519_new_keypair(-Pair)
|
||||
%
|
||||
% Yields a new Ed25519 key pair Pair, a list of characters. The
|
||||
% 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_new_keypair(Pair) :-
|
||||
crypto_n_random_bytes(32, Bytes),
|
||||
ed25519_seed_keypair(Bytes, Pair).
|
||||
|
||||
%% 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_keypair_public_key(Pair, PublicKey) :-
|
||||
must_be_octet_chars(Pair, ed25519_keypair_public_key/2),
|
||||
reverse(Pair, RPs),
|
||||
length(RPublicKey, 32),
|
||||
phrase((seq(RPublicKey),...), RPs),
|
||||
reverse(RPublicKey, PublicKey).
|
||||
|
||||
%% ed25519_sign(+Key, +Data, -Signature, +Options)
|
||||
%
|
||||
% 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_sign(KeyPair, Data0, Signature, Options) :-
|
||||
must_be_octet_chars(KeyPair, ed25519_sign/4),
|
||||
length(Prefix, 16),
|
||||
length(PrivateKeyChars, 32),
|
||||
phrase((seq(Prefix),seq(PrivateKeyChars),...), KeyPair),
|
||||
maplist(char_code, PrivateKeyChars, PrivateKey),
|
||||
options_data_chars(Options, Data0, Data, Encoding),
|
||||
'$ed25519_sign_raw'(PrivateKey, Data, Encoding, Signature0),
|
||||
hex_bytes(Signature, Signature0).
|
||||
|
||||
%% 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_verify(Key, Data0, Signature0, Options) :-
|
||||
must_be_octet_chars(Key, ed25519_verify/4),
|
||||
options_data_chars(Options, Data0, Data, Encoding),
|
||||
hex_bytes(Signature0, Signature),
|
||||
'$ed25519_verify_raw'(Key, Data, Encoding, Signature).
|
||||
|
||||
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
X25519: ECDH key exchange over Curve25519
|
||||
=========================================
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||||
|
||||
%% curve25519_generator(-Gs)
|
||||
%
|
||||
% Points on Curve25519 are represented as lists of characters that
|
||||
% denote the u-coordinate of the Montgomery curve. Gs is the
|
||||
% generator point of Curve25519.
|
||||
|
||||
curve25519_generator(Gs) :-
|
||||
length(Gs0, 32),
|
||||
Gs0 = [9|Zs],
|
||||
maplist(=(0), Zs),
|
||||
maplist(char_code, Gs, Gs0).
|
||||
|
||||
%% 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_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)
|
||||
),
|
||||
must_be(chars, Point),
|
||||
length(Point, 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)
|
||||
%
|
||||
% Yields the generator point G of Curve.
|
||||
|
||||
crypto_curve_generator(curve(_,_,_,_,G,_,_,_), G).
|
||||
|
||||
%% crypto_curve_order(+Curve, -Order)
|
||||
%
|
||||
% Yields the order of Curve.
|
||||
|
||||
crypto_curve_order(curve(_,_,_,_,_,Order,_,_), Order).
|
||||
|
||||
%% crypto_curve_scalar_mult(+Curve, +Scalar, +Point, -Result)
|
||||
%
|
||||
% Computes the point _Result = Scalar*Point_. Scalar must be an
|
||||
% integer, and Point must be a point on Curve. This operation can be
|
||||
% used to negotiate a shared secret over a public channel. Consider
|
||||
% using `curve25519_scalar_mult/3` instead for more desirable
|
||||
% security properties.
|
||||
|
||||
crypto_curve_scalar_mult(Curve, Scalar, point(X,Y), point(RX, RY)) :-
|
||||
must_be(integer, Scalar),
|
||||
must_be_on_curve(Curve, point(X,Y)),
|
||||
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(+Name, -Curve)
|
||||
%
|
||||
% Yields a representation of the elliptic curve with name Name.
|
||||
% Currently, the only supported name is `secp256k1`, a Koblitz curve
|
||||
% regarded as secure.
|
||||
|
||||
crypto_name_curve(secp256k1,
|
||||
curve(secp256k1,
|
||||
0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
|
||||
0x0,
|
||||
0x7,
|
||||
point(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
|
||||
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8),
|
||||
0x00fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
|
||||
32,
|
||||
1)).
|
||||
251
src/lib/csv.pl
Normal file
251
src/lib/csv.pl
Normal file
@@ -0,0 +1,251 @@
|
||||
/** Predicates for parsing CSV data
|
||||
|
||||
## Read CSV files.
|
||||
|
||||
Only two options with default values:
|
||||
|
||||
- `token_separator(',')`
|
||||
- `with_header(true)`
|
||||
|
||||
### Examples:
|
||||
|
||||
Parsing a CSV string:
|
||||
|
||||
```
|
||||
?- use_module(library(csv)).
|
||||
?- use_module(library(dcgs)).
|
||||
?- phrase(parse_csv(Data), "col1,col2,col3,col4\none,2,,three").
|
||||
Data = frame(["col1","col2","col3","col4"],[["one",2,[],"three"]]).
|
||||
```
|
||||
|
||||
With some options:
|
||||
|
||||
```
|
||||
?- phrase(parse_csv(Data, [with_header(false), token_separator(';')]), "one;2;;three").
|
||||
Data = frame([],[["one",2,[],"three"]]).
|
||||
```
|
||||
|
||||
Parsing a CSV file:
|
||||
|
||||
```
|
||||
?- use_module(library(csv)).
|
||||
?- use_module(library(pio)).
|
||||
?- phrase_from_file(parse_csv(frame(Header, Rows)), './test.csv').
|
||||
```
|
||||
|
||||
## Write CSV files
|
||||
|
||||
Four options with default values :
|
||||
|
||||
- `line_separator('\n')`
|
||||
- `token_separator(',')`
|
||||
- `with_header(true)`
|
||||
- `null_value(empty)`
|
||||
|
||||
### Examples
|
||||
|
||||
Writing a CSV file:
|
||||
|
||||
```
|
||||
?- use_module(library(csv)).
|
||||
?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]])).
|
||||
```
|
||||
|
||||
With some options
|
||||
|
||||
```
|
||||
?- use_module(library(csv)).
|
||||
?- write_csv('./test.csv', frame(
|
||||
["col1","col2","col3","col4"],
|
||||
[["one",2,[],"three"]]
|
||||
),
|
||||
[with_header(false), line_separator('\r\n'), token_separator(';'), null_value('\\N')]).
|
||||
```
|
||||
*/
|
||||
|
||||
:- module(csv, [
|
||||
parse_csv//1,
|
||||
parse_csv//2,
|
||||
write_csv/2,
|
||||
write_csv/3
|
||||
]).
|
||||
|
||||
:- use_module(library(format)).
|
||||
:- use_module(library(dcgs)).
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(lists)).
|
||||
|
||||
|
||||
option(W, O) :-
|
||||
( member(W, O) -> true
|
||||
; throw(error(domain_error(csv_option, W), option/2))).
|
||||
|
||||
|
||||
option_extends([], Opt, Opt).
|
||||
option_extends([X | Y], Opt0, Opt) :-
|
||||
functor(X, Name, 1),
|
||||
F0 =.. [Name, _],
|
||||
( select(F0, Opt0, R) ->
|
||||
option_extends(Y, [X | R], Opt)
|
||||
; option_extends(Y, [X | Opt0], Opt) ).
|
||||
|
||||
|
||||
%% -- write --
|
||||
|
||||
|
||||
escaped_field([], []).
|
||||
escaped_field(['"' | Y], ['"', '"' | R]) :-
|
||||
escaped_field(Y, R).
|
||||
escaped_field([X | Y], [X | R]) :-
|
||||
X \== '"',
|
||||
escaped_field(Y, R).
|
||||
|
||||
|
||||
ensure_escaped(Field, Field) :-
|
||||
(atom(Field); integer(Field); float(Field)).
|
||||
ensure_escaped([X | Y], Field) :-
|
||||
escaped_field([X | Y], Field).
|
||||
|
||||
|
||||
write_field(Out, Field, Opt) :-
|
||||
( Field \== [] ->
|
||||
ensure_escaped(Field, Field0),
|
||||
format(Out, "~w", [Field0])
|
||||
; option(null_value(Null_Value), Opt),
|
||||
( Null_Value == empty -> true
|
||||
; format(Out, "~w", [Null_Value]))).
|
||||
|
||||
|
||||
write_row(Out, [Field], Opt) :-
|
||||
write_field(Out, Field, Opt).
|
||||
write_row(Out, [Field, X | Y], Opt) :-
|
||||
write_field(Out, Field, Opt),
|
||||
option(token_separator(Tk_Sep), Opt),
|
||||
format(Out, "~w", [Tk_Sep]),
|
||||
write_row(Out, [X | Y], Opt).
|
||||
|
||||
|
||||
write_rows(Out, [Row], Opt) :-
|
||||
write_row(Out, Row, Opt).
|
||||
write_rows(Out, [Row, X | Y], Opt) :-
|
||||
option(line_separator(Line_Sep), Opt),
|
||||
write_row(Out, Row, Opt),
|
||||
format(Out, "~w", [Line_Sep]),
|
||||
write_rows(Out, [X | Y], Opt).
|
||||
|
||||
|
||||
write_csv_(Out, frame(Header, Rows), Opt) :-
|
||||
option(with_header(With_Header), Opt),
|
||||
( With_Header == true ->
|
||||
write_row(Out, Header, Opt),
|
||||
option(line_separator(Line_Sep), Opt),
|
||||
format(Out, "~w", [Line_Sep])
|
||||
; true),
|
||||
write_rows(Out, Rows, Opt).
|
||||
|
||||
|
||||
write_csv(File_Name, Frm, Opt) :-
|
||||
option_extends(Opt, [
|
||||
null_value(empty),
|
||||
token_separator(','),
|
||||
with_header(true),
|
||||
line_separator('\n')
|
||||
], Opt0),
|
||||
setup_call_cleanup(
|
||||
open(File_Name, write, Out),
|
||||
write_csv_(Out, Frm, Opt0),
|
||||
close(Out)).
|
||||
write_csv(File_Name, Frm) :-
|
||||
write_csv(File_Name, Frm, []).
|
||||
|
||||
|
||||
%% -- read --
|
||||
|
||||
|
||||
tokens([], Opt), [Tk_Sep] -->
|
||||
{ option(token_separator(Tk_Sep), Opt) },
|
||||
[Tk_Sep],
|
||||
!.
|
||||
tokens([], _), "\r\n" -->
|
||||
"\r\n",
|
||||
!.
|
||||
tokens([], _), "\n" -->
|
||||
"\n",
|
||||
!.
|
||||
tokens([], _), "\r" -->
|
||||
"\r",
|
||||
!.
|
||||
tokens([X | Y], Opt) -->
|
||||
[X],
|
||||
!,
|
||||
tokens(Y, Opt).
|
||||
tokens([], _) --> [].
|
||||
|
||||
|
||||
field(R, Opt) -->
|
||||
"\"",
|
||||
!,
|
||||
string_tokens(R, Opt).
|
||||
field(R, Opt) -->
|
||||
tokens(R0, Opt),
|
||||
{ R0 \== [],
|
||||
catch(number_chars(R, R0), _, R = R0)
|
||||
}.
|
||||
field([], _) --> [].
|
||||
|
||||
|
||||
string_tokens(R, Opt) -->
|
||||
[X],
|
||||
( { X == '"' } ->
|
||||
( "\"" ->
|
||||
{ R = [X | Y] },
|
||||
string_tokens(Y, Opt)
|
||||
; { R = [] })
|
||||
; { R = [X | Y] },
|
||||
string_tokens(Y, Opt)).
|
||||
|
||||
|
||||
end_token --> "\r\n".
|
||||
end_token --> "\n".
|
||||
end_token --> "\r".
|
||||
end_token --> [].
|
||||
|
||||
|
||||
separator(Opt) -->
|
||||
{ option(token_separator(Tk_Sep), Opt) },
|
||||
[Tk_Sep].
|
||||
|
||||
|
||||
row([X | Y], Opt) -->
|
||||
field(X, Opt),
|
||||
!,
|
||||
( separator(Opt) ->
|
||||
row(Y, Opt)
|
||||
; end_token,
|
||||
{ Y = [] }).
|
||||
|
||||
|
||||
rows(R, Opt) -->
|
||||
row(X, Opt),
|
||||
!,
|
||||
( { X \== [[]] } ->
|
||||
rows(Y, Opt),
|
||||
{ R = [X | Y] }
|
||||
; { R = [] }).
|
||||
|
||||
|
||||
parse_csv(frame(Header, Rows), Opt) -->
|
||||
{ option_extends(Opt, [
|
||||
with_header(true),
|
||||
token_separator(',')
|
||||
], Opt0)
|
||||
},
|
||||
( { option(with_header(With_Header), Opt0),
|
||||
With_Header == true } ->
|
||||
row(Header, Opt0),
|
||||
{ Header \== [[]] },
|
||||
end_token
|
||||
; { Header = [] }),
|
||||
rows(Rows, Opt0).
|
||||
parse_csv(R) -->
|
||||
parse_csv(R, []).
|
||||
270
src/lib/dcgs.pl
Normal file
270
src/lib/dcgs.pl
Normal file
@@ -0,0 +1,270 @@
|
||||
/** Support for Definite Clause Grammars.
|
||||
|
||||
A Prolog definite clause grammar (DCG) describes a sequence. Operationally, DCGs
|
||||
can be used to parse, generate, complete and check sequences manifested as lists.
|
||||
|
||||
Check [The Power of Prolog chapter on DCGs](https://www.metalevel.at/prolog/dcg)
|
||||
to learn more about them.
|
||||
*/
|
||||
|
||||
|
||||
:- module(dcgs,
|
||||
[op(1105, xfy, '|'),
|
||||
phrase/2,
|
||||
phrase/3,
|
||||
phrase/4,
|
||||
phrase/5,
|
||||
seq//1,
|
||||
seqq//1,
|
||||
... //0,
|
||||
(-->)/2
|
||||
]).
|
||||
|
||||
:- use_module(library(error)).
|
||||
:- use_module(library(iso_ext)).
|
||||
:- use_module(library(lists), [append/3, member/2]).
|
||||
:- use_module(library(loader), [strip_module/3]).
|
||||
|
||||
:- meta_predicate phrase(2, ?).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?, ?).
|
||||
|
||||
:- meta_predicate phrase(2, ?, ?, ?, ?).
|
||||
|
||||
%% phrase(+Body, ?Ls).
|
||||
%
|
||||
% True iff Body describes the list Ls. Body must be a DCG body.
|
||||
% It is equivalent to `phrase(Body, Ls, [])`.
|
||||
%
|
||||
% Examples:
|
||||
%
|
||||
% ```
|
||||
% as --> [].
|
||||
% as --> [a], as.
|
||||
%
|
||||
% ?- phrase(as, Ls).
|
||||
% Ls = []
|
||||
% ; Ls = "a"
|
||||
% ; Ls = "aa"
|
||||
% ; Ls = "aaa"
|
||||
% ; ... .
|
||||
%
|
||||
% ?- phrase(as, "aaa").
|
||||
% true.
|
||||
% ```
|
||||
|
||||
phrase(GRBody, S0) :-
|
||||
phrase(GRBody, S0, []).
|
||||
|
||||
%% phrase(+Body, ?Ls, ?Ls0).
|
||||
%
|
||||
% True iff Body describes part of the list Ls and the rest of Ls is Ls0.
|
||||
%
|
||||
% Example:
|
||||
%
|
||||
% ```
|
||||
% ?- phrase(seq(X), "aaa", Y).
|
||||
% X = [], Y = "aaa"
|
||||
% ; X = "a", Y = "aa"
|
||||
% ; X = "aa", Y = "a"
|
||||
% ; X = "aaa", Y = [].
|
||||
% ```
|
||||
phrase(GRBody, S0, S) :-
|
||||
strip_module(GRBody, M, GRBody1),
|
||||
( var(GRBody) ->
|
||||
instantiation_error(phrase/3)
|
||||
; nonvar(GRBody1),
|
||||
dcg_constr(GRBody1),
|
||||
dcg_body(GRBody1, S0, S, GRBody2) ->
|
||||
call(M:GRBody2)
|
||||
; call(M:GRBody1, S0, S)
|
||||
).
|
||||
|
||||
phrase(GRBody, Arg, S0, S) :-
|
||||
strip_module(GRBody, M, GRBody1),
|
||||
( var(GRBody) ->
|
||||
instantiation_error(phrase/4)
|
||||
; nonvar(GRBody1),
|
||||
GRBody1 =.. GRBodys1,
|
||||
append(GRBodys1, [Arg], GRBodys2),
|
||||
GRBody2 =.. GRBodys2,
|
||||
dcg_constr(GRBody2),
|
||||
dcg_body(GRBody2, S0, S, GRBody3) ->
|
||||
call(M:GRBody3)
|
||||
; call(M:GRBody1, Arg, S0, S)
|
||||
).
|
||||
|
||||
phrase(GRBody, Arg1, Arg2, S0, S) :-
|
||||
strip_module(GRBody, M, GRBody1),
|
||||
( var(GRBody) ->
|
||||
instantiation_error(phrase/5)
|
||||
; nonvar(GRBody1),
|
||||
GRBody1 =.. GRBodys1,
|
||||
append(GRBodys1, [Arg1,Arg2], GRBodys2),
|
||||
GRBody2 =.. GRBodys2,
|
||||
dcg_constr(GRBody2),
|
||||
dcg_body(GRBody2, S0, S, GRBody3) ->
|
||||
call(M:GRBody3)
|
||||
; call(M:GRBody1, Arg1, Arg2, S0, S)
|
||||
).
|
||||
|
||||
% The same version of the below two dcg_rule clauses, but with module scoping.
|
||||
dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :-
|
||||
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||
dcg_body(GRBody, S0, S1, Goal1),
|
||||
dcg_terminals(Terminals, S, S1, Goal2),
|
||||
Body = ( Goal1, Goal2 ).
|
||||
dcg_rule(( M:NonTerminal --> GRBody ), ( M:Head :- Body )) :-
|
||||
NonTerminal \= ( _, _ ),
|
||||
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||
dcg_body(GRBody, S0, S, Body).
|
||||
|
||||
% This program uses append/3 as defined in the Prolog prologue.
|
||||
% Expands a DCG rule into a Prolog rule, when no error condition applies.
|
||||
dcg_rule(( NonTerminal, Terminals --> GRBody ), ( Head :- Body )) :-
|
||||
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||
dcg_body(GRBody, S0, S1, Goal1),
|
||||
dcg_terminals(Terminals, S, S1, Goal2),
|
||||
Body = ( Goal1, Goal2 ).
|
||||
dcg_rule(( NonTerminal --> GRBody ), ( Head :- Body )) :-
|
||||
NonTerminal \= ( _, _ ),
|
||||
dcg_non_terminal(NonTerminal, S0, S, Head),
|
||||
dcg_body(GRBody, S0, S, Body).
|
||||
|
||||
dcg_non_terminal(NonTerminal, S0, S, Goal) :-
|
||||
NonTerminal =.. NonTerminalUniv,
|
||||
append(NonTerminalUniv, [S0, S], GoalUniv),
|
||||
( callable(NonTerminal) ->
|
||||
Goal =.. GoalUniv
|
||||
; Goal = NonTerminal % let call/N throw an error instead of throwing one here.
|
||||
).
|
||||
|
||||
dcg_terminals(Terminals, S0, S, S0 = List) :-
|
||||
append(Terminals, S, List).
|
||||
|
||||
dcg_body(Var, S0, S, Body) :-
|
||||
var(Var),
|
||||
Body = phrase(Var, S0, S).
|
||||
dcg_body(GRBody, S0, S, Body) :-
|
||||
nonvar(GRBody),
|
||||
dcg_constr(GRBody),
|
||||
dcg_cbody(GRBody, S0, S, Body).
|
||||
dcg_body(NonTerminal, S0, S, Goal1) :-
|
||||
nonvar(NonTerminal),
|
||||
\+ dcg_constr(NonTerminal),
|
||||
loader:strip_module(NonTerminal, M, NonTerminal0),
|
||||
dcg_non_terminal(NonTerminal0, S0, S, Goal0),
|
||||
( functor(NonTerminal, (:), 2) ->
|
||||
Goal1 = M:Goal0
|
||||
; Goal1 = Goal0
|
||||
).
|
||||
|
||||
% The following constructs in a grammar rule body
|
||||
% are defined in the corresponding subclauses.
|
||||
dcg_constr([]). % 7.14.1
|
||||
dcg_constr([_|_]). % 7.14.2 - terminal sequence
|
||||
dcg_constr(( _, _ )). % 7.14.3 - concatenation
|
||||
dcg_constr(( _ ; _ )). % 7.14.4 - alternative
|
||||
dcg_constr(( _'|'_ )). % 7.14.6 - alternative
|
||||
dcg_constr({_}). % 7.14.7
|
||||
dcg_constr(call(_)). % 7.14.8
|
||||
dcg_constr(phrase(_)). % 7.14.9
|
||||
dcg_constr(phrase(_,_)). % extension of 7.14.9
|
||||
dcg_constr(phrase(_,_,_)). % extension of 7.14.9
|
||||
dcg_constr(!). % 7.14.10
|
||||
dcg_constr(\+ G_0) :- % 7.14.11 - not (existence implementation def.)
|
||||
throw(error(representation_error(dcg_body), [culprit- (\+ G_0)])).
|
||||
dcg_constr((If->Then)) :- % 7.14.12 - if-then (existence implementation def.)
|
||||
throw(error(representation_error(dcg_body), [culprit- (If->Then)])).
|
||||
|
||||
% The principal functor of the first argument indicates
|
||||
% the construct to be expanded.
|
||||
dcg_cbody([], S0, S, S0 = S).
|
||||
dcg_cbody([T|Ts], S0, S, Goal) :-
|
||||
must_be(list, [T|Ts]),
|
||||
dcg_terminals([T|Ts], S0, S, Goal).
|
||||
dcg_cbody(( GRFirst, GRSecond ), S0, S, ( First, Second )) :-
|
||||
dcg_body(GRFirst, S0, S1, First),
|
||||
dcg_body(GRSecond, S1, S, Second).
|
||||
dcg_cbody(( GREither ; GROr ), S0, S, ( Either ; Or )) :-
|
||||
\+ subsumes_term(( _ -> _ ), GREither),
|
||||
dcg_body(GREither, S0, S, Either),
|
||||
dcg_body(GROr, S0, S, Or).
|
||||
dcg_cbody(( GRCond ; GRElse ), S0, S, ( Cond ; Else )) :-
|
||||
subsumes_term(( _GRIf -> _GRThen ), GRCond),
|
||||
dcg_cbody(GRCond, S0, S, Cond),
|
||||
dcg_body(GRElse, S0, S, Else).
|
||||
dcg_cbody(( GREither '|' GROr ), S0, S, ( Either ; Or )) :-
|
||||
dcg_body(GREither, S0, S, Either),
|
||||
dcg_body(GROr, S0, S, Or).
|
||||
dcg_cbody({Goal}, S0, S, ( Goal, S0 = S )).
|
||||
dcg_cbody(call(Cont), S0, S, call(Cont, S0, S)).
|
||||
dcg_cbody(phrase(Body), S0, S, phrase(Body, S0, S)).
|
||||
dcg_cbody(phrase(Body, Arg), S0, S, phrase(Body, Arg, S0, S)).
|
||||
dcg_cbody(phrase(Body, Arg1, Arg2), S0, S, phrase(Body, Arg1, Arg2, S0, S)).
|
||||
dcg_cbody(!, S0, S, ( !, S0 = S )).
|
||||
% dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )).
|
||||
dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :-
|
||||
dcg_body(GRIf, S0, S1, If),
|
||||
dcg_body(GRThen, S1, S, Then).
|
||||
|
||||
user:term_expansion(Term0, Term) :-
|
||||
nonvar(Term0),
|
||||
dcg_rule(Term0, Term).
|
||||
|
||||
|
||||
%% seq(Seq)//
|
||||
%
|
||||
% Describes a sequence
|
||||
seq(Xs, Cs0,Cs) :-
|
||||
var(Xs),
|
||||
Cs0 == [],
|
||||
!,
|
||||
Xs = [],
|
||||
Cs0 = Cs.
|
||||
seq([]) --> [].
|
||||
seq([E|Es]) --> [E], seq(Es).
|
||||
|
||||
%% seqq(SeqOfSeqs)//
|
||||
%
|
||||
% Describes a sequence of sequences
|
||||
seqq([]) --> [].
|
||||
seqq([Es|Ess]) --> seq(Es), seqq(Ess).
|
||||
|
||||
%% ...//
|
||||
%
|
||||
% Describes an arbitrary number of elements
|
||||
...(Cs0,Cs) :-
|
||||
Cs0 == [],
|
||||
!,
|
||||
Cs0 = Cs.
|
||||
... --> [] | [_], ... .
|
||||
|
||||
error_goal(error(E, must_be/2), error(E, must_be/2)).
|
||||
error_goal(error(E, (=..)/2), error(E, (=..)/2)).
|
||||
error_goal(error(representation_error(dcg_body), Context),
|
||||
error(representation_error(dcg_body), Context)).
|
||||
error_goal(E, _) :- throw(E).
|
||||
|
||||
user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
|
||||
loader:strip_module(GRBody, M, GRBody0),
|
||||
nonvar(GRBody0),
|
||||
catch(dcgs:dcg_body(GRBody0, S, S0, GRBody1),
|
||||
E,
|
||||
dcgs:error_goal(E, GRBody1)
|
||||
),
|
||||
( GRBody = (_:_) ->
|
||||
GRBody2 = M:GRBody1
|
||||
; GRBody2 = GRBody1
|
||||
).
|
||||
|
||||
user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])).
|
||||
|
||||
|
||||
% (-->)/2 behaves as if it didn't exist. We export (and define) it
|
||||
% only so that clauses for (-->)/2 cannot be asserted when
|
||||
% library(dcgs) is loaded.
|
||||
|
||||
(_-->_) :- throw(error(existence_error(procedure,(-->)/2),(-->)/2)).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user