1538 Commits

Author SHA1 Message Date
Mark Thom
2d19243b3b Merge branch 'rebis-dev' into 0.9.0 release
Some checks failed
Test / build (macos-10.15, beta) (push) Has been cancelled
Test / build (macos-10.15, stable) (push) Has been cancelled
Test / build (ubuntu-20.04, beta) (push) Has been cancelled
Test / build (ubuntu-20.04, stable) (push) Has been cancelled
2022-03-06 09:45:05 -07:00
Mark Thom
0c19c56909 mark variables in ArithmeticEvaluator (#690) 2022-03-05 17:57:10 -07:00
Mark Thom
de35baadf3 use must_be(chars) in read_from_chars/2 (#1305) 2022-03-05 17:39:13 -07:00
Mark Thom
66c209f9e4 reset ReadlineStream from read_query_line (#1317, #1285) 2022-03-05 17:39:13 -07:00
Mark Thom
d7a3ed2d4a change domain_error(integer, ..) to type_error(integer, ..) in random.pl (#1310) 2022-03-05 17:39:13 -07:00
Mark Thom
0c25ffc26e dereference list link in delete_attribute (#1148) 2022-03-05 17:39:03 -07:00
Markus Triska
9f864574de ADDED: "f" to enumerate the next 5 solutions, similar to GUPU. 2022-03-02 21:05:49 +01:00
Mark Thom
a38f7c8524 save arguments to delayed goal before calling verify_attrs (#1304) 2022-03-01 23:59:41 -07:00
Mark Thom
bf85cd404c Merge pull request #1313 from triska/all_solutions
ADDED: "a" to enumerate all solutions
2022-03-01 15:34:21 -07:00
Markus Triska
102adb3544 ADDED: "a" to enumerate all solutions
Example:

    ?- member(X, "hello").
    %@    X = h   <-- press "a"
    %@ ;  X = e
    %@ ;  X = l
    %@ ;  X = l
    %@ ;  X = o
    %@ ;  false.
2022-03-01 23:52:51 +01:00
Mark Thom
c2b360d03e Merge pull request #1312 from triska/str_loc_as_cell
FIXED: load_html/3: use str_loc_as_cell! to store attribute structures
2022-03-01 12:09:42 -07:00
Markus Triska
1bec1b7002 FIXED: load_html/3: use str_loc_as_cell! to store attribute structures 2022-03-01 20:21:08 +01:00
Mark Thom
1b4b4807a5 refer to structures using str_loc_as_cell! (#1311) 2022-02-28 18:28:13 -07:00
Mark Thom
d4d135f2a9 fix unexpected error in length/2 (#1303) 2022-02-27 11:38:48 -07:00
Mark Thom
96faad1c01 add proper error throwing for call_with_inference_limit/3 (#1296) 2022-02-27 11:38:44 -07:00
Mark Thom
d55d5f15ae compare '[]' in pstr_comparator of compare_term_test (#1299) 2022-02-25 23:57:27 -07:00
Mark Thom
26e4560429 pop pstr offsets from iterator stack in printer (#1290) 2022-02-25 23:36:26 -07:00
Mark Thom
775bd3a08b unify '' to [] in unify_complete_string (#1292, #1288) 2022-02-25 23:01:08 -07:00
Mark Thom
d92d8bce89 recognize [] as equivalent to "" in unify_atom (#1288) 2022-02-22 23:45:58 -07:00
Mark Thom
1b91663244 Merge pull request #1287 from triska/rebis-dev-sgml
FIXED: library(sgml): load_html/3 and load_xml/3 now both work again.
2022-02-22 15:55:03 -07:00
Markus Triska
8fb673e93e use newly available get_n_chars/3 from library(charsio) 2022-02-23 00:03:36 +01:00
Markus Triska
d1372d9b3b FIXED: library(sgml): load_html/3 and load_xml/3 now both work again.
This addresses #1249.
2022-02-23 00:03:27 +01:00
Mark Thom
063f0da565 correct '$skip_max_list/4' for non-lists (#1276) 2022-02-20 23:44:18 -07:00
Mark Thom
c893247107 update setup_call_cleanup tests 2022-02-19 01:00:02 -07:00
Mark Thom
a5adcfff4c adjust dynamic external indices upon retraction, reset dynamic_mode to Next regardless of success or failure 2022-02-19 01:00:02 -07:00
Mark Thom
d3583276b3 throw instantiation_error on variables from (#1283) 2022-02-19 01:00:02 -07:00
Mark Thom
55d8de1b23 delay callable errors in control predicates (#1282) 2022-02-19 00:59:59 -07:00
Mark Thom
5b829636cb build CStr's to vars in write_literal_to_var (#1284) 2022-02-15 22:33:29 -07:00
Mark Thom
68ac92a616 don't unify module-qualified variables to ! (#1281) 2022-02-14 18:46:15 -07:00
Mark Thom
db20cda27a use VecDeque for generating indices during code generation (#1053) 2022-02-11 19:23:43 -07:00
Mark Thom
60dd47c696 add missing fact to beginning of tmember_t/3 (#1275) 2022-02-11 17:25:01 -07:00
Mark Thom
42da543980 group predicate subsequences by first instantiated arg (#1053) 2022-02-08 23:34:42 -07:00
Mark Thom
e4ea547623 print bounded depth partial strings correctly 2022-02-07 14:09:22 -07:00
Mark Thom
33a6c81a07 update setup_call_cleanup tests 2022-02-06 21:46:06 -07:00
Mark Thom
9ba503cd6b use printer cycle detection when printing partial strings (#1263), refactor skip_max_list functions 2022-02-06 21:36:49 -07:00
Mark Thom
f3f3dccf8e reference partial strings properly in read_term (#1271) 2022-02-06 15:14:18 -07:00
Mark Thom
2f0718e885 fix write_canonical on strings (#1233) 2022-02-05 20:40:28 -07:00
Mark Thom
7b8001d060 fix '$skip_max_list'/4 (#1260) 2022-02-05 18:33:30 -07:00
Mark Thom
3c022e4332 fix float formatting (#1233, #1258) 2022-02-05 18:33:27 -07:00
Mark Thom
37e34c5209 reset instruction pointers with calls, make HCPrinter::range_from safe (#1233) 2022-01-31 21:24:19 -07:00
Mark Thom
4b71607215 count cycle lengths in skip_max_list_cycle (#1260) 2022-01-31 18:23:16 -07:00
Mark Thom
e62875ac90 tag length/2 as the source of finite_memory errors in list.pl (#1259) 2022-01-31 18:08:00 -07:00
Mark Thom
835345b89e Merge pull request #1257 from mthom/rebis-dev_improved-length
Improved length/2 and arg/3 range checking
2022-01-30 11:30:16 -07:00
Mark Thom
4ddacc707d add range check to arg/3 (#1250) 2022-01-29 12:50:09 -07:00
Mark Thom
0d653a2ce6 improve '$skip_max_list'/4 and length/2 (#1023, #110) 2022-01-29 12:47:26 -07:00
Mark Thom
3208260ef8 Merge pull request #1254 from triska/valid_dotdotdot
ENHANCED: Make toplevel output a Prolog term that can be read back also for "...".
2022-01-28 11:55:52 -07:00
Markus Triska
2e57789d10 ENHANCED: Make toplevel output a Prolog term that can be read back also for "...".
This addresses #1240.
2022-01-28 18:47:55 +01:00
Mark Thom
b3006f6fd8 inline store & deref calls in '' (#1176) 2022-01-26 23:03:19 -07:00
Mark Thom
142ddcd57a use expand_goal directly for existential-qualified goals in all-solutions predicates (#1246) 2022-01-26 17:34:36 -07:00
Mark Thom
a562793fce don't read into the heap while incrementing self.s (#1233, #1245) 2022-01-25 20:37:29 -07:00
Mark Thom
e6c4ecfc10 lazily throw exceptions of dcg_body from goal-expanded phrase/{2,3} (#1244) 2022-01-22 14:55:18 -07:00
Mark Thom
5ff579f793 recognize []/N as callable (#1241, #989) 2022-01-21 08:10:59 -07:00
Mark Thom
8df346f377 expand phrase/{2,3} using dcg_body whenever possible 2022-01-21 08:10:55 -07:00
Mark Thom
af76068297 module-qualify callables more liberally but not for built-ins (#1243) 2022-01-20 17:40:45 -07:00
Mark Thom
5dddf0a460 dereference car of Lis in HeapPStRIter::step (#1238) 2022-01-19 20:51:23 -07:00
Mark Thom
1854338ff4 throw errors from phrase/{2,3} where expected (#1220) 2022-01-19 20:19:47 -07:00
Mark Thom
11b96875e0 streamline and fix phrase/{2,3} (#1237) 2022-01-19 20:07:15 -07:00
Mark Thom
6462c524f4 Merge pull request #1236 from triska/omit-module-prefix
omit module prefix, because the goals are already module-qualified
2022-01-19 17:22:44 -07:00
Markus Triska
b703303dd4 omit module prefix, because the goals are already module-qualified 2022-01-20 00:48:30 +01:00
Mark Thom
ab77b6b28e Merge pull request #1235 from triska/better-initialization-warning
ENHANCED: more specific warning when initialization/1 fails (#1149)
2022-01-19 16:41:43 -07:00
Markus Triska
0da9d1c036 ENHANCED: more specific warning when initialization/1 fails (#1149) 2022-01-20 00:04:09 +01:00
Mark Thom
88436b8627 Merge pull request #1234 from aarroyoc/ctrld
Fix Ctrl-D in toplevel
2022-01-18 21:34:30 -07:00
Mark Thom
f6116510a1 print -0.0 as 0.0 (#1164) 2022-01-18 21:31:14 -07:00
Mark Thom
5a132aaff4 throw error instead of overwriting builtin export (#1225) 2022-01-18 19:16:14 -07:00
Mark Thom
59992c8af2 remove number/3 from arithmetic.pl (#1170) 2022-01-18 17:38:45 -07:00
Adrián Arroyo Calle
ff3ec78df9 Fix Ctrl-D (#1168) 2022-01-18 19:27:27 +01:00
Mark Thom
b980ae1e8c fix floor for f64 (#1223) 2022-01-17 22:04:10 -07:00
Mark Thom
97b9d488d4 include library(error) in toplevel.pl (#1226) 2022-01-17 21:53:52 -07:00
Mark Thom
3ebf8d5db9 clear alias in close before resetting the stream to null (#1231) 2022-01-17 17:58:14 -07:00
Mark Thom
89e33903b2 Merge pull request #1232 from triska/toplevel_read_from_chars
FIXED: toplevel to use (newly renamed) read_from_chars/2
2022-01-17 17:44:58 -07:00
Markus Triska
6a610ac57d FIXED: toplevel to use (newly renamed) read_from_chars/2 2022-01-18 01:12:47 +01:00
Mark Thom
413f797155 Merge pull request #1228 from triska/charsio-read-renames
MODIFIED: Rename two predicates in library(charsio)
2022-01-17 17:03:46 -07:00
Mark Thom
6c8dc49216 Merge pull request #1229 from triska/faster_decryption
ENHANCED: library(crypto): Retain a compact internal representation of the ciphertext throughout decryption.
2022-01-17 17:03:29 -07:00
Mark Thom
9ded1447ec Merge pull request #1230 from josd/patch-1
Update clpz.pl
2022-01-17 17:03:03 -07:00
Markus Triska
516ed1fd5b ENHANCED: library(crypto): Retain a compact internal representation of the ciphertext throughout decryption.
This allows efficient decryption also of very large files and long
strings.

It is usually highly advisable to stick to common and portable library
predicates such as append/3. However, since append/3 does not yet
recognize this opportunity for improvement, I apply it manually in
this case, so that also very large files can be efficiently decrypted
using the compact internal string representation.

Without this change, decrypting a 1 GB file takes 48 GB of RAM,
whereas with this change, it only takes 2 GB (one for the string, one
for its copy with the appended tag).
2022-01-17 23:02:56 +01:00
Jos De Roo
1a4f8f992b Update clpz.pl
While running with scryer-prolog the resource control file should be ~/.scryerrc
2022-01-17 22:35:31 +01:00
Markus Triska
23adfef281 MODIFIED: library(charsio): read_n_chars/3 is now called get_n_chars/3
This is because "read" is associated with general Prolog terms.
2022-01-17 20:37:55 +01:00
Markus Triska
20b6816562 MODIFIED: library(charsio): read_term_from_chars/2 is now called read_from_chars/2
This is for compatibility with SICStus Prolog and GNU Prolog.
2022-01-17 20:21:18 +01:00
Mark Thom
c6400550e1 dereference second argument of '/2 2022-01-17 00:00:23 -07:00
Mark Thom
f26749ad79 Merge pull request #1224 from triska/working_directory
correct reference to '$working_directory'/2
2022-01-16 15:46:11 -07:00
Markus Triska
177c98fa95 correct reference to '$working_directory'/2 2022-01-16 23:20:43 +01:00
Mark Thom
7baa187863 Merge pull request #1222 from triska/retain_string_of_nil
retain the string "[]" as is, instead of converting it to '[]' (i.e., "")
2022-01-16 11:58:15 -07:00
Markus Triska
11d504e8f4 retain the string "[]" as is, instead of converting it to '[]' (i.e., "")
This addresses #1215.
2022-01-16 17:50:20 +01:00
Mark Thom
83378e4373 deduplicate but do not sort anonymous variables in toplevel (#1196) 2022-01-15 19:24:22 -07:00
Mark Thom
06abe2302b add missing cases to retract_clause (#1214) 2022-01-15 00:51:38 -07:00
Mark Thom
f3e5f7879d allow late dynamic declaration for predicates (#1205) 2022-01-14 16:31:49 -07:00
Mark Thom
bc5125b719 put_complete_string should push the empty list to the heap for empty strings (#1209) 2022-01-14 16:31:49 -07:00
Mark Thom
c6d23f9a0b fix off-by-one error in DynamicIndexedChoice (#1210) 2022-01-14 16:31:22 -07:00
Mark Thom
3b8afce7c7 fix syntax regressions (#1198, #1164) 2022-01-13 23:54:55 -07:00
Mark Thom
39e28f1f2a write plaintext and ciphertext directly to atoms (#1193) 2022-01-13 08:47:56 -07:00
Mark Thom
08363b1d73 Merge pull request #1208 from triska/mention-rebis-dev
add links to the rebis-dev branch and its announcement
2022-01-13 08:03:25 -07:00
Markus Triska
91aa0f200e add links to the rebis-dev branch and its announcement 2022-01-13 08:46:24 +01:00
Mark Thom
d9e190096c delimit partial strings around \x0\ 2022-01-12 22:10:59 -07:00
Mark Thom
4dca86d5cf serialize call_0 test (#1206) 2022-01-12 21:42:26 -07:00
Mark Thom
d3b628bb24 Merge pull request #1207 from triska/semi-deterministic
deterministic --> semi-deterministic
2022-01-12 15:30:24 -07:00
Markus Triska
cfbb05fb1b deterministic --> semi-deterministic 2022-01-12 21:58:23 +01:00
Mark Thom
340d428c88 Merge pull request #1199 from Skgland/do_not_gen_in_src
Generate into $OUT_DIR instead of src/
2022-01-11 23:01:59 -07:00
Mark Thom
d22bbc54c7 Merge pull request #1195 from triska/rebis-dev-format-U
ADDED: format specifier ~NU, using underscores to separate groups of digits
2022-01-11 22:49:38 -07:00
Mark Thom
c5cda99f9d evaluate floats referenced from the heap by tagged pointers (#1203) 2022-01-11 18:42:01 -07:00
Mark Thom
67854e0720 execute trust instead of retry at the end of dynamic choice blocks (#1204, #1173) 2022-01-11 18:24:26 -07:00
Mark Thom
cfe257495a recognize characters as separate from atoms in ''/7 (#1192) 2022-01-10 21:18:11 -07:00
Mark Thom
5f4e701461 render [] as empty string in AtomOrString::as_str() (#1193) 2022-01-10 20:46:00 -07:00
Mark Thom
e3622e0860 improve between_/3 implementation (#1186, #1191) 2022-01-10 18:34:25 -07:00
Mark Thom
f0e6b8ca47 imitate Rc-style equality for TypedArenaPtr (#1190) 2022-01-10 18:25:30 -07:00
Mark Thom
ef0b239e70 correctly print [] functors (#1189) 2022-01-10 17:26:57 -07:00
Skgland
590d80268e only rerun build.rs when something in src/ has changed
by default (when no rerun-if is emitted) build.rs is rerun if something in the package changes
2022-01-10 21:20:07 +01:00
Skgland
a5de329712 don't genrate code in src/
[Outputs of Build Script](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script) states that only files in $OUT_DIR may be modified
2022-01-10 21:19:18 +01:00
Markus Triska
2889a4438b ADDED: format specifier ~NL to limit the number of digits per line
Example:

    %?- format("~65L", [2^1000]).
    %@ 10715086071862673209484250490600018105614048117055336074437503883_
    %@ 70351051124936122493198378815695858127594672917553146825187145285_
    %@ 69231404359845775746985748039345677748242309854210746050623711418_
    %@ 77954182153046474983581941267398767559165543946077062914571196477_
    %@ 686542167660429831652624386837205668069376   true.
2022-01-10 17:52:42 +01:00
Markus Triska
f5952088e3 ADDED: format specifier ~NU, using underscores to separate groups of digits
Example:

    ?- format("~2U", [10^12]).
    %@ 10_000_000_000.00   true.
2022-01-10 16:07:05 +01:00
Mark Thom
bcacb49c05 compare bigints by value not by pointer (#1183) 2022-01-09 23:33:26 -07:00
Mark Thom
976b8e426d Merge pull request #1160 from aarroyoc/add-ugraphs-library
Add ugraphs library (ported from SWI)
2022-01-09 12:43:46 -07:00
Mark Thom
90ecd34cfd Merge pull request #1182 from aarroyoc/sockets-arity
fix socket_client_open/3
2022-01-09 12:24:39 -07:00
Adrián Arroyo Calle
35670c8654 remove TLS option in socket_client_open/3 2022-01-09 17:04:54 +01:00
Adrián Arroyo Calle
07bc97fb31 fix socket_client_open/3 2022-01-08 23:31:32 +01:00
Mark Thom
d01806ee2d correct heap offsets in setup_call_cleanup tests 2022-01-08 12:50:09 -07:00
Mark Thom
a54e42961c unify current time as a complete string, not an atom (#1175) 2022-01-08 11:47:35 -07:00
Mark Thom
2e46aa33c0 correct read/2 arity to 2 (#1171) 2022-01-08 00:19:17 -07:00
Mark Thom
d90cf6384c complete handling of control operators in interpreted (,)/2 (#1172) 2022-01-08 00:04:04 -07:00
Adrián Arroyo Calle
af3f676683 Minor fixes 2022-01-07 19:28:07 +01:00
Adrián Arroyo Calle
abf980d603 Merge remote-tracking branch 'upstream/rebis-dev' into add-ugraphs-library 2022-01-07 19:14:34 +01:00
Mark Thom
693b6d2547 add version dependency information to README 2022-01-07 07:52:56 -07:00
Mark Thom
06313cac64 update README.md 2022-01-07 00:17:17 -07:00
Mark Thom
cf1e315d72 add parser tests 2022-01-06 22:09:38 -07:00
Mark Thom
955e1799c8 flatten the instruction dispatch loop 2022-01-06 22:09:36 -07:00
Mark Thom
3db86f1e25 relocate most instruction routines from MachineState to Machine 2022-01-06 21:46:23 -07:00
Mark Thom
6f9b6a29c4 remove LocalCodePtr::IndexingBuf 2022-01-06 21:44:41 -07:00
Mark Thom
b056d40eba module-qualify incomplete goals in expand_goal/3 2022-01-06 21:44:41 -07:00
Mark Thom
708c3bc3ce dereference first argument of (is)/2 2022-01-06 21:44:41 -07:00
Mark Thom
39efc12dc8 generate heap_loc cells to refer to 0-arity atoms in read.rs 2022-01-06 21:44:41 -07:00
Mark Thom
4211151fe6 fix unify_float 2022-01-06 21:44:41 -07:00
Mark Thom
3974b6e6cd dereference variables when looking for string terminators 2022-01-06 21:44:41 -07:00
Mark Thom
b24df6e195 properly iterate over read terms in MachineState::read_term 2022-01-06 21:44:41 -07:00
Mark Thom
bcd33dc8e3 get test suite working 2022-01-06 21:44:41 -07:00
Mark Thom
6c66c236fb fix printing of CStr's 2022-01-06 21:44:41 -07:00
Mark Thom
520121b2b2 remove skeletons from replaced modules 2022-01-06 21:44:41 -07:00
Mark Thom
48c1d05151 use heap_loc to refer to 0-arity atom instead of str_loc 2022-01-06 21:44:41 -07:00
Mark Thom
8ba61a1da1 defer installation of inference counter for call_with_inference_limit til after goals expanded 2022-01-06 21:44:41 -07:00
Mark Thom
cd129e32a7 fix bugs revealed by src/tests/builtins.pl 2022-01-06 21:44:41 -07:00
Mark Thom
2b3e43f160 start to retune testing infrastructure 2022-01-06 21:44:41 -07:00
Mark Thom
d7bf04d2c0 fix off by one bug in system_calls 2022-01-06 21:44:41 -07:00
Mark Thom
4cde8cd501 fix bug in error_form 2022-01-06 21:44:41 -07:00
Mark Thom
fb4e627e62 fix various partial string bugs (copying using them in arg/3) 2022-01-06 21:44:41 -07:00
Mark Thom
ef3a97cedd detect attributed variables by properly detecting them in copy_term/2 2022-01-06 21:44:41 -07:00
Mark Thom
1a86ad5cec upgrade rustyline 2022-01-06 21:44:41 -07:00
Mark Thom
529c401eee don't allow [] as a stream alias, restore domain error on source sink for open/{3,4} 2022-01-06 21:44:41 -07:00
Mark Thom
a87236fea2 keep cursor position after writing to byte stream 2022-01-06 21:44:41 -07:00
Mark Thom
975c1ca62c fix stream errors 2022-01-06 21:44:41 -07:00
Mark Thom
d02b9d848c break from loop upon successful peek in peek_char/1 and peek_code/1 2022-01-06 21:44:41 -07:00
Mark Thom
f340f9ac94 break from loop in get_code upon successful character read 2022-01-06 21:44:41 -07:00
Mark Thom
1b3f290037 throw instantiation error if Arity is a variable in abolish/1 2022-01-06 21:44:41 -07:00
Mark Thom
b551ef315f use strip module in retract/1, a meta-predicate 2022-01-06 21:44:41 -07:00
Mark Thom
10bb6ab3bb restore old thread_goals to fix top-level solutions printing bug 2022-01-06 21:44:41 -07:00
Mark Thom
4af57b0dd3 fix HeapPStrIter bug not recognizing nil focus if PStrLoc points to PStrOffset to CStr 2022-01-06 21:44:41 -07:00
Mark Thom
bc613eeff9 fix number not recognizing floats 2022-01-06 21:44:41 -07:00
Mark Thom
7507e88406 detect module resolved cut in interpreted (,) 2022-01-06 21:44:41 -07:00
Mark Thom
073f281f1e fix arg/3 bug 2022-01-06 21:44:41 -07:00
Mark Thom
3355b49724 add bounds check for attributed variables slice 2022-01-06 21:44:41 -07:00
Mark Thom
0e2db4a23e tag DCG constructs with module names for proper resolution 2022-01-06 21:44:41 -07:00
Mark Thom
addc817cca break from get_char loop after successful char unification 2022-01-06 21:44:41 -07:00
Mark Thom
a0a86d0f62 unmark cell bits in occurs check 2022-01-06 21:44:41 -07:00
Mark Thom
b24e7cce38 support comparison and unification of cyclic partial strings 2022-01-06 21:44:41 -07:00
Mark Thom
ffd1b7069f greatly reduce the number of goal expansions done in callable if/then/else 2022-01-06 21:44:41 -07:00
Mark Thom
0404c3bd94 use new heap term representation 2022-01-06 21:44:38 -07:00
Mark Thom
d0b74a95f4 Merge pull request #1156 from mthom/proper_var_list_order_issue_1121
Correct order of variables in read_term/3 equation and variable lists
2022-01-06 20:46:59 -07:00
Adrián Arroyo Calle
0288d5dc19 Add ugraphs library 2021-12-31 12:56:24 +01:00
Mark Thom
9d06229cba create read_term/3 variable lists in order of variable occurrence within terms 2021-12-23 13:31:49 -07:00
Mark Thom
c60ada8421 Merge pull request #1132 from triska/tls_library
ADDED: library(tls) for negotiating TLS connections
2021-12-13 19:11:04 -07:00
Mark Thom
74a1d5cf38 Merge pull request #1141 from triska/layout_text_sequence
ENHANCED: Support also comments in integers with underscores
2021-12-13 19:09:37 -07:00
Mark Thom
43532e5322 Merge pull request #1142 from triska/s
report time with "s" instead of "seconds", in analogy to time(1)
2021-12-13 19:09:20 -07:00
Mark Thom
68cd1d6631 Merge pull request #1143 from heydtn/patch-1
Fix typo in abnf_lwsp/2
2021-12-13 19:04:46 -07:00
Mark Thom
afc18cc390 Merge pull request #1147 from triska/pio_improvements
ENHANCED: Use newly available fast chars test from library(error).
2021-12-13 19:04:27 -07:00
Markus Triska
57d15936cb ENHANCED: Use newly available fast chars test from library(error). 2021-12-12 17:43:17 +01:00
Nate Heydt
14406dbf76 Fix typo in abnf_lwsp/2 2021-12-09 07:42:48 -08:00
Markus Triska
12c561cee0 report time with "s" instead of "seconds", in analogy to time(1)
This addresses https://github.com/mthom/scryer-prolog/pull/1131#issuecomment-988670049 .
2021-12-08 16:13:54 +01:00
Markus Triska
f79b8a1ca5 ENHANCED: support full layout text sequence (also comments) in integers with underscores
Example:

    ?- X = 1_/**/2.
    %@    X = 12.

Following the comment in https://github.com/mthom/scryer-prolog/pull/1112#issuecomment-981540485
2021-12-08 10:42:19 +01:00
Markus Triska
637daa5bda remove redundant test: '\n' is a layout char. 2021-12-08 10:37:37 +01:00
Mark Thom
2eae6b4be7 Merge pull request #1131 from triska/cpu_time
walltime --> CPU time
2021-12-07 20:30:00 -07:00
Markus Triska
dc5e935ecd use newly available predicates from library(tls) for HTTPS 2021-12-05 18:16:47 +01:00
Markus Triska
538085169a ADDED: library(tls), providing all predicates for TLS connections.
The currently available predicates for TLS-connections can be extended
with predicates to load and reason about certificates etc.
2021-12-05 18:16:47 +01:00
Markus Triska
7e8a635e7e implement tls_client_negotiate/3 for explicit negotiation 2021-12-05 17:39:22 +01:00
Markus Triska
74e76b6f97 MODIFIED: Remove TLS-related predicates from library(sockets).
They will become available in a new library, library(tls).
2021-12-05 17:39:22 +01:00
Markus Triska
cb6309d370 walltime --> CPU time 2021-12-05 00:56:16 +01:00
Mark Thom
8065889862 Merge pull request #1129 from triska/tls_servers
ADDED: Support for creating TLS servers.
2021-12-04 16:23:43 -07:00
Mark Thom
e7a8950d09 Merge pull request #1124 from triska/test_corrections
several corrections to test cases
2021-12-04 16:23:31 -07:00
Mark Thom
88e9dc2177 Merge pull request #1127 from triska/retract_correction
Correct retract/1 issues due to module qualification
2021-12-04 16:23:00 -07:00
Mark Thom
2c555c969e Merge pull request #1122 from triska/better_time
ENHANCED: Better reporting for time/1.
2021-12-04 16:22:14 -07:00
Markus Triska
77c04c3a14 ADDED: Support for creating TLS servers.
The new predicates tls_server_context/2 and tls_server_negotiate/3 can
be used to negotiate TLS connections with clients for encrypted and
authenticated communication.
2021-12-03 16:45:27 +01:00
Markus Triska
48cea6efdf FIXED: retract/1 issues due to module qualification
This addresses #1125.
2021-12-01 21:46:12 +01:00
Markus Triska
89a90d9522 ENHANCED: Better reporting for time/1.
This addresses #378.
2021-12-01 21:23:00 +01:00
Markus Triska
0026f3fdef use anonymous variable 2021-12-01 20:54:06 +01:00
Markus Triska
ee054fd99c several corrections to test cases 2021-12-01 19:45:36 +01:00
Mark Thom
7abdade7c1 Merge pull request #1120 from triska/char_type_correction
ENHANCED: Correct type error for char_type/2.
2021-11-29 19:38:30 -05:00
Markus Triska
79a50de697 ENHANCED: Correct type error for char_type/2.
This addresses #905.
2021-11-29 19:33:22 +01:00
Mark Thom
e47aef5615 Merge pull request #1116 from aarroyoc/fix-open-lis
Allow all kinds of string to be processed in open/4
2021-11-28 16:18:02 -05:00
Mark Thom
b96ae1bac0 Merge pull request #1119 from mthom/expand_call_goals_as_call_goals
Expand call goals as call goals so that goals in control constructs are qualified properly
2021-11-28 16:16:47 -05:00
Mark Thom
7dafcb0860 expand call goals as call goals (#1114) 2021-11-28 10:27:50 -07:00
Mark Thom
5a6a686f42 Merge pull request #1117 from triska/indicate_failed_initialization
ENHANCED: indicate failed initialization/1 goal
2021-11-28 11:53:54 -05:00
Mark Thom
b576bb55ef Merge pull request #1115 from triska/get_char_type_test
ENHANCED: Character type checks for get_char/[1,2].
2021-11-28 11:53:15 -05:00
Markus Triska
fc8205d375 ENHANCED: indicate failed initialization/1 goal
This addresses #1109.
2021-11-28 14:50:42 +01:00
Adrián Arroyo Calle
7eb0669de5 Fix use_module import errors 2021-11-28 13:20:09 +01:00
Adrián Arroyo Calle
86161ccf2a Allow all kinds of string to be processed in open/4 2021-11-28 12:37:16 +01:00
Markus Triska
5e55732cb0 ENHANCED: Character type checks for get_char/[1,2].
This addresses #906.
2021-11-28 10:49:40 +01:00
Mark Thom
a87f0481b8 Merge pull request #1096 from aarroyoc/number_chars_fix
Prevents panic in some strange cases. Seen in #721
2021-11-27 22:37:30 -05:00
Mark Thom
c3d61361f8 Merge pull request #1112 from triska/underscores_in_integers
ENHANCED: Support '_' followed by layout text sequence in integers.
2021-11-27 20:29:49 -05:00
Mark Thom
a48934e31c Merge pull request #1113 from triska/seq
Use newly available seq//1 and ... //0 in several libraries
2021-11-27 20:29:31 -05:00
Mark Thom
ac75f67e2a Merge pull request #1111 from triska/fast_list_test
Use '$skip_max_list'/4 for fast list tests in can_be/2 and must_be/2
2021-11-27 20:29:17 -05:00
Markus Triska
45cfa6c8d8 use seq//1 2021-11-27 16:32:45 +01:00
Markus Triska
25a06b0bea use seq//1 and ... //0 which are now available in library(dcgs) 2021-11-27 16:32:44 +01:00
Markus Triska
91af72e8b1 use seq//1 2021-11-27 16:17:45 +01:00
Markus Triska
fd9b354c70 use seq//1 which is now available in library(dcgs) 2021-11-27 16:17:15 +01:00
Markus Triska
1647e67dd4 ENHANCED: Support '_' followed by layout text sequence in integers.
This is handy for writing large integers, for example in cryptographic
applications.

This addresses #994.

Examples:

    ?- X = 1_000_000.
    %@    X = 1000000.

    ?- X = 1_
    000_
    000.
    %@    X = 1000000.
2021-11-27 10:09:39 +01:00
Markus Triska
803e120a51 use must_be(list, ...) earlier, since it is now faster 2021-11-26 17:11:51 +01:00
Markus Triska
6ba374c62b ENHANCED: use '$skip_max_list'/4 for fast list tests in can_be/2 and must_be/2
This also (partially) addresses #1108.
2021-11-26 17:11:35 +01:00
Mark Thom
ca28f24e42 Merge pull request #1107 from triska/fast_chars_test
New type tests: must_be(chars, ...) and can_be(chars, ...)
2021-11-25 19:01:03 -05:00
Markus Triska
0d4c38138a use newly available must_be(chars, ...) for fast string tests 2021-11-22 20:00:06 +01:00
Markus Triska
fe291e90f0 use newly available must_be(chars, ...) for fast string tests 2021-11-22 19:58:36 +01:00
Markus Triska
220e1e8d83 ADDED: must_be(chars, ...) and can_be(chars, ...) for string tests.
Internal features of Scryer Prolog are used to make these tests fast.
2021-11-22 19:55:34 +01:00
Mark Thom
2c0fb3adb0 Merge pull request #1098 from triska/format_improvements
Various smaller improvements to library(format)
2021-11-21 15:28:32 -05:00
Mark Thom
b851eefb03 Merge pull request #1099 from aarroyoc/uninstantiation_error-missing
Fix uninstantiation_error missing. Fix #1031
2021-11-21 15:27:38 -05:00
Mark Thom
eaad2d8b1f Merge pull request #1100 from aarroyoc/phrase-to-file-options
Add phrase_to_file/3 (phrase_to_file with Options)
2021-11-21 15:27:17 -05:00
Mark Thom
575d235296 Merge pull request #1102 from triska/meta_predicate_corrections
Meta predicate corrections
2021-11-21 15:27:03 -05:00
Markus Triska
faf74519dc use round brackets for meta_predicate/1 declaration 2021-11-20 22:43:53 +01:00
Markus Triska
66becaf91c FIXED: meta_predicate/1 declaration of cond_t/3. 2021-11-20 22:43:33 +01:00
Markus Triska
6610ba67c4 FIXED: meta_predicate/1 declaration of (',')/3 and (;)/3. 2021-11-20 22:43:07 +01:00
Markus Triska
e05dd5ebb5 FIXED: meta_predicate/1 declaration for tfilter/3. 2021-11-20 22:39:38 +01:00
Markus Triska
cf77f29988 FIXED: meta_predicate/1 declaration for tmember_t/3. 2021-11-20 22:38:27 +01:00
Markus Triska
2be8ed886b FIXED: meta_predicate/1 declaration for tmember/2. 2021-11-20 22:37:59 +01:00
Markus Triska
21e5b0ab52 FIXED: meta_predicate/1 declaration for tpartition/4.
This addresses #1101.
2021-11-20 22:37:31 +01:00
Markus Triska
d72cb74ffa mention phrase_to_file/3 which is being prepared in #1100 2021-11-17 21:33:15 +01:00
Markus Triska
3c988d555f 'f' --> f 2021-11-17 21:20:40 +01:00
Markus Triska
5981a65cc2 better layout 2021-11-17 21:20:40 +01:00
Markus Triska
9900762747 use newly available seq//1 from library(dcgs)
I am using seq(Ls) instead of plain Ls so that DCGs can be debugged
declaratively, by generalizing away nonterminals in rules.
2021-11-17 21:20:40 +01:00
Adrián Arroyo Calle
0db2ca6659 Fix mistake in phrase_to_file/2 definition 2021-11-17 08:36:39 +01:00
Adrián Arroyo Calle
8e68626f14 Add phrase_to_file/3 (phrase_to_file with Options) 2021-11-16 23:54:04 +01:00
Adrián Arroyo Calle
72a88d1f91 Fix uninstantiation_error missing. Fix #1031 2021-11-16 23:22:24 +01:00
Markus Triska
bd75e9c184 more elegant solution for parsing numeric arguments, using a DCG 2021-11-16 18:25:35 +01:00
Markus Triska
23df16ecf9 mention the format("~s", [Ls]) pattern and new library(pio) predicates 2021-11-16 18:20:58 +01:00
Adrián Arroyo Calle
387732d2f9 Prevents panic in some strange cases. Seen in #721 2021-11-15 00:17:40 +01:00
Mark Thom
75b01d5021 Merge pull request #1082 from pmoura/fix_format_predicates_missing_numbervars_option_on_w_and_q_formats
Fix the format/2-3 predicates missing the numbervars(true) option in the ~w and ~q formats
2021-11-13 00:05:26 -05:00
Mark Thom
b7fa5db570 Merge pull request #1087 from Skgland/fix_beta_check
fix failing lint warnings in beta build
2021-11-13 00:04:51 -05:00
Mark Thom
297f126f66 Merge pull request #1078 from triska/pio_improvements
Various improvements for pure output
2021-11-13 00:03:57 -05:00
Mark Thom
bd6400f17f Merge pull request #1086 from Skgland/pass_-f_flag_in_cargo_test
pass -f to scryer-prolog in cargo test so that we ignore .scryerrc
2021-11-13 00:03:38 -05:00
Mark Thom
21e55023a4 Merge pull request #1088 from triska/call_nth_metapredicate
FIXED: meta_predicate/1 directive for call_nth/2.
2021-11-13 00:03:26 -05:00
Skgland
49e024bdd8 remove unused fields instead, prompting more dead_code removal 2021-11-11 19:32:35 +01:00
Markus Triska
57877107d8 use functional notation for all meta_predicate/1 directives 2021-11-11 19:29:32 +01:00
Markus Triska
e9b4a99c8f FIXED: meta_predicate/1 directive for call_nth/2.
This addresses #1021.
2021-11-11 19:26:27 +01:00
Skgland
7bc4876071 fix failing lint warnings in beta build 2021-11-11 18:53:08 +01:00
Skgland
e7899ee15e pass -f to scryer-prolog in cargo test so that we ignore .scryerrc
fixes issue #1085
2021-11-11 11:37:05 +01:00
Paulo Moura
7ea20d9ce9 Fix the format/2-3 predicates missing the numbervars(true) option in the ~w and ~q formats 2021-11-08 22:00:02 +00:00
Markus Triska
3045f327d5 FIXED: instantiation errors for alias(Var) in open/4 options
This addresses #1030.
2021-11-08 22:41:04 +01:00
Markus Triska
2d1f182c49 FIXED: instantiation errors for type(Var) in open/4 options
This addresses #1030.
2021-11-08 22:37:46 +01:00
Markus Triska
0677f51717 add motivation and comments about library(pio) predicates 2021-11-08 22:18:30 +01:00
Markus Triska
a9bfeb0e96 use newly available library predicate read_n_chars/3 2021-11-08 21:51:03 +01:00
Markus Triska
c44d27fb7f mention pure output predicates in the README 2021-11-08 20:29:39 +01:00
Markus Triska
a0a01b8ba7 ADDED: library(format): DCG nonterminal portray_clause_//1, in analogy to format_//2. 2021-11-07 17:47:05 +01:00
Markus Triska
e32215e19a express format/3 and portray_clause/2 in terms of phrase_to_stream/2 2021-11-07 17:47:05 +01:00
Markus Triska
893fb0e3cc ADDED: phrase_to_stream/2, writing a list of characters to a stream. 2021-11-07 17:44:45 +01:00
Mark Thom
1d2a838717 Merge pull request #1077 from pmoura/fix_get_code_predicates_at_end_of_file
Fix the get_code/1-2 predicates to return -1 at the end of stream as per standard
2021-11-07 09:40:29 -05:00
Mark Thom
8457a19792 Merge pull request #1071 from triska/read_n_chars
ADDED: read_n_chars/3, reading N characters from a stream.
2021-11-07 09:40:03 -05:00
Mark Thom
0b2638b201 Merge pull request #1070 from triska/open_stream
ENHANCED: open/4 to allow opening a stream by specifying stream(S).
2021-11-07 09:39:41 -05:00
Mark Thom
4dce57a1b6 Merge pull request #1069 from triska/crypto_improvements
Performance improvements for library(crypto)
2021-11-07 09:39:13 -05:00
Paulo Moura
658b835a70 Fix the get_code/1-2 predicates to return -1 at the end of stream 2021-11-06 19:41:25 +00:00
Markus Triska
52af955463 import also partial_string/3 (used in #1071) 2021-11-04 18:50:40 +01:00
Markus Triska
09e886db0b ADDED: read_n_chars/3, reading N characters from a stream. 2021-11-04 18:36:27 +01:00
Markus Triska
f668640e3d move error handling to Prolog by using the new '$first_non_octet'/2 2021-11-04 18:35:22 +01:00
Markus Triska
afcd44deaa ENHANCED: open/4 to allow opening a stream by specifying stream(S).
This allows switching standard output to binary, using for example:

  ?- current_output(S0), open(stream(S0), write, S, [type(binary)]).

format/3 can then be used to write binary data to S. This is needed
for example when piping binary data to other programs.

This addresses #614, please read the discussion for more information.

The current implementation is very preliminary: Specifically, it works
by destructively modifiying the parameters of the underlying stream,
making it no longer usable in its original mode. Currently, if the
type of standard output is set to binary, then the toplevel no longer
works. Therefore, after writing binary output to standard output,
the program should either halt, or set the stream type back to text.
2021-11-04 18:33:40 +01:00
Markus Triska
8c8c21c63b use partial_string/1 for much quicker test 2021-11-04 00:12:00 +01:00
Markus Triska
55dabbe16a use '$first_non_octet'/2 for much faster domain check 2021-11-04 00:12:08 +01:00
Markus Triska
494bd7b79c ADDED: '$first_non_octet'/2, for much more efficient domain checks in library(crypto).
'$first_non_octet'(Cs, C) <=> C is the leftmost character in the
string Cs whose character code is not in 0..255.
2021-11-04 00:12:00 +01:00
Markus Triska
320ee072e6 ENHANCED: Much faster Base64 encoding/decoding, using partial_string/1 for a quick check. 2021-11-03 22:07:20 +01:00
Mark Thom
67ef5fe8e6 Merge pull request #1065 from aarroyoc/pio-string
Disable check of atom and leverage to open/4 the domain_error
2021-11-03 13:27:45 -02:30
Mark Thom
7c5c700bb8 Merge pull request #1066 from aarroyoc/rust-2021
Rust 2021
2021-11-03 13:27:18 -02:30
Adrián Arroyo Calle
dd51908439 Rust 2021 2021-11-01 23:03:51 +01:00
Adrián Arroyo Calle
6a0f638d40 Disable check of atom and leverage to open/4 the domain_error 2021-10-29 23:52:08 +02:00
Adrián Arroyo Calle
2480c6633c Disable check of atom and leverage to open/4 the domain_error 2021-10-29 23:51:30 +02:00
Mark Thom
ca62e54652 Merge pull request #1052 from aarroyoc/shell-predicate
Implement shell/1 and shell/2
2021-10-17 11:58:28 -06:00
Adrián Arroyo Calle
44dd9ae9fb Go back and accept only list of chars 2021-10-15 17:22:15 +02:00
Adrián Arroyo Calle
3f4d62952a Accept atom or shell 2021-10-14 22:52:29 +02:00
Adrián Arroyo Calle
391cda919f Implement shell/1 and shell/2 2021-10-11 22:35:05 +02:00
Mark Thom
44fc61ed86 Merge pull request #1051 from aarroyoc/close-stream
Throw exception if closing a closed TCP stream #1046
2021-10-07 17:09:16 -06:00
Adrián Arroyo Calle
f70685d53c Close Stream test feedback 2021-09-30 23:46:52 +02:00
Adrián Arroyo Calle
807abfef4f Close Stream bug files 2021-09-30 23:01:25 +02:00
Adrián Arroyo Calle
810d4c51f9 Close Stream bug files 2021-09-30 22:59:15 +02:00
Mark Thom
1a0684cd20 Merge pull request #1049 from pmoura/add_preliminary_current_module_predicate_definition
Add preliminary current_module/1 predicate definition requiring a bound argument
2021-09-30 09:19:09 -06:00
Adrián Arroyo Calle
47d811c250 use shutdown error values to throw exceptions in close stream 2021-09-30 00:10:03 +02:00
Adrián Arroyo Calle
4d0998ef72 launch exception if closing a closed TCP stream #1046 2021-09-28 22:48:51 +02:00
Mark Thom
5d2b0377d8 reset attributed variables queue when backtracking (#678, etc.) 2021-09-27 19:12:24 -06:00
Mark Thom
f6d69b6051 reset attributed variables queue after successful verify_attributes interrupt (#669, etc) 2021-09-26 18:58:00 -06:00
Paulo Moura
490496f381 Add preliminary current_module/1 predicate definition requiring a bound argument 2021-09-24 10:13:00 +01:00
Mark Thom
bc4f719931 Merge pull request #1045 from triska/phrase_to_file
ADDED: Rudimentary version of phrase_to_file/2 in library(pio)
2021-09-05 23:31:57 -06:00
Markus Triska
ded4a75b9c use round brackets for meta_predicate declarations
This is because meta_predicate is not a standard operator.
2021-09-04 22:02:17 +02:00
Markus Triska
9e75eb35a0 ADDED: Rudimentary version of phrase_to_file/2 in library(pio)
See #691 for more information.
2021-09-04 21:13:51 +02:00
Mark Thom
326fb99849 Merge pull request #1033 from aarroyoc/fix-1022
Add a -f switch to load alternative initialization files
2021-08-23 22:26:15 -06:00
Mark Thom
fe1ced9f58 Merge pull request #1032 from aarroyoc/fix-1009
Fix #1009 by providing a naive num-bigint implementation of count_ones
2021-08-23 22:25:44 -06:00
Adrián Arroyo Calle
00d0502cbd Rename init_file to disable_init_file 2021-08-23 09:53:15 +02:00
Adrián Arroyo Calle
c30bd51b91 use consult instead of use_module 2021-08-22 11:44:17 +02:00
Adrián Arroyo Calle
9391dd9d51 Rename to disable_init_file and change description 2021-08-22 11:36:27 +02:00
Adrián Arroyo Calle
3cd112b60f PR feedback 2021-08-21 17:53:20 +02:00
Adrián Arroyo Calle
dce0c43e26 Read scryerrc if goals specified but not -f specified 2021-08-20 14:15:16 +02:00
Adrián Arroyo Calle
80e228f236 Clean the code 2021-08-20 14:06:57 +02:00
Adrián Arroyo Calle
8ee2545b05 Add a -f switch to load alternative initialization files 2021-08-20 14:00:32 +02:00
Adrián Arroyo Calle
9bcc56e337 Make it line 2021-08-20 12:40:27 +02:00
Adrián Arroyo Calle
3c5a94452a Fix #1009 by providing a naive num-bigint implementation of count_ones 2021-08-20 12:38:15 +02:00
Mark Thom
fce7f8aa68 Merge pull request #1029 from triska/format
ENHANCED: Domain error non_empty_list for empty arguments, addressing #1024
2021-08-15 08:59:06 -06:00
Markus Triska
9e13f18463 ENHANCED: Domain error non_empty_list for empty arguments, addressing #1024.
Example:

    ?- format("~d", []).
    %@ caught: error(domain_error(non_empty_list,[]),format_//2)
2021-08-15 12:29:28 +02:00
Mark Thom
2905f6b465 Merge pull request #1027 from triska/format
ENHANCED: format_string domain error for invalid radix in format string
2021-08-14 23:06:28 -06:00
Markus Triska
9d3f3eb013 ENHANCED: format_string domain error for invalid radix in format string
This addresses #1024.

Example:

    ?- format("hello ~0r!", [12]).
    %@ caught: error(domain_error(format_string,"~0r"),format_//2)
2021-08-14 20:04:07 +02:00
Mark Thom
75b5afa759 Merge pull request #1026 from triska/format
ENHANCED: If N is omitted in ~Nr or ~NR, it defaults to 8.
2021-08-14 09:15:30 -06:00
Markus Triska
78c2f19e72 ENHANCED: If N is omitted in ~Nr or ~NR, it defaults to 8.
This is for compatibility with Quintus, SICStus and GNU Prolog,
addressing #1024.

Example:

    ?- format("~r", [10]).
    %@ 12   true.
2021-08-14 13:48:21 +02:00
Mark Thom
9a66a626f7 run user-level and module-level initialization goals from loader.pl (#1012) 2021-08-01 09:25:55 -06:00
Mark Thom
f42b7f4efa Merge pull request #1001 from triska/master
ADDED: ... //0, describing an arbitrary number of elements
2021-07-08 14:44:29 -06:00
Markus Triska
bf9654e138 ADDED: ... //0, describing an arbitrary number of elements
This is a very versatile nonterminal. For instance, repeated elements:

    ?- phrase((...,[E],...,[E],...), "hello!!").
    %@    E = l
    %@ ;  E = !
    %@ ;  false.
2021-07-03 09:54:01 +02:00
Mark Thom
4decd1d784 Merge pull request #996 from triska/master
address #995: wrong results for popcount/1
2021-06-22 11:56:56 -06:00
Markus Triska
3e7cd24814 address #995: wrong results for popcount/1
Many thanks to @notoria and @flexoron for very useful test cases!
2021-06-21 21:19:37 +02:00
Mark Thom
3e0bece53a Merge pull request #993 from triska/master
ADDED: popcount(+Integer)
2021-06-18 18:13:30 -06:00
Mark Thom
c1b89b3b06 Merge pull request #986 from Regan-Koopmans/981
Add popcount/2
2021-06-18 18:13:18 -06:00
Regan-Koopmans
f5eadb2957 981 Prefer must_be/2 2021-06-18 08:22:43 +02:00
Markus Triska
ee393c66dd ADDED: popcount(+Integer)
Example:

    ?- X #= popcount(3+5).
       X = 1.

This builds on #986 provided by @Regan-Koopmans. Many thanks!
2021-06-17 20:18:36 +02:00
Regan-Koopmans
72a7765b58 981 Reimplement as library system call 2021-06-17 12:10:25 +02:00
Mark Thom
176daeec03 Merge pull request #983 from triska/master
check for correct domain of integers in reifiable expressions also in the expanded code
2021-06-03 18:32:48 -06:00
Markus Triska
066f740819 check for correct domain of integers in reifiable expressions also in the expanded code
This is to preserve domain errors instead of failing silently. Example:

   ?- 5 #> 3 #<==> 2.
   %@ caught: error(domain_error(clpz_reifiable_expression,2),unknown(2)-1)
2021-06-03 22:57:58 +02:00
Mark Thom
04121644a3 Merge pull request #978 from Skgland/master
fix beta build and tests
2021-06-03 12:38:18 -06:00
Mark Thom
1e37a0d9e6 Merge pull request #982 from triska/master
ENHANCED: Goal expansion for reified constraints.
2021-06-03 12:35:31 -06:00
Markus Triska
fc8d33a98a ENHANCED: Goal expansion for reified constraints.
Used in package precautionary by @dcnorris.
2021-06-03 20:26:44 +02:00
Skgland
2089727c7f ignore setup_call_cleanup tests as their output is apparently non-deterministic 2021-06-02 17:41:14 +02:00
Skgland
fbbf705d10 fix tests 2021-06-02 17:15:31 +02:00
Skgland
e08c302756 fix beta build 2021-06-02 16:43:10 +02:00
Mark Thom
87ef3519d9 Merge pull request #972 from pmoura/add_pid_predicate
Add pid/1 predicate to the os library
2021-05-19 14:09:21 -06:00
Mark Thom
9002c33046 leave the expansion of incomplete goals in meta-predicates to call/N (#967, #970) 2021-05-19 14:07:46 -06:00
Paulo Moura
b9ad6f4fd2 Add pid/1 predicate to the os library 2021-05-18 20:05:30 +01:00
Mark Thom
57db17853e Merge pull request #971 from pmoura/add_rename_file_predicate
Add rename_file/2 predicate to the files library
2021-05-18 12:49:52 -06:00
Paulo Moura
58555d598b Add rename_file/2 predicate to the files library 2021-05-18 18:08:30 +01:00
Mark Thom
0eeae24049 Merge pull request #968 from pmoura/add_make_directory_path_predicate
Add make_directory_path/1 predicate to the files library
2021-05-18 10:26:06 -06:00
Mark Thom
3def7c66ad Merge pull request #969 from pmoura/add_delete_directory_predicate
Add delete_directory/1 predicate to the files library
2021-05-18 10:25:52 -06:00
Paulo Moura
eeac3bc436 Add delete_directory/1 predicate to the files library 2021-05-18 08:41:19 +01:00
Paulo Moura
bdebc7f32e Add make_directory_path/1 predicate to the files library 2021-05-18 00:21:11 +01:00
Mark Thom
e7d6811948 Merge pull request #965 from pmoura/fix_current_predicate_error_checking
Fix current_predicate/1 predicate error checking
2021-05-16 23:37:20 -06:00
Paulo Moura
0e263dd753 Fix current_predicate/1 predicate error checking 2021-05-16 23:49:21 +01:00
Mark Thom
56bbcb1cb3 Merge pull request #956 from pmoura/fix_subsumes_term_predicate
Fix subsumes_term/2 predicate to use unify_with_occurs_check/2 instead of (=)/2
2021-05-13 23:18:58 -06:00
Mark Thom
f02648022a Merge pull request #960 from triska/master
FIXED: correct propagation of tuples_in/2 (#929)
2021-05-13 23:18:47 -06:00
Mark Thom
9bb1f4e6f8 Merge pull request #958 from pmoura/fixes_for_retract_predicates
Fixes for retract predicates
2021-05-13 23:18:36 -06:00
Mark Thom
d219f0bdc6 Merge pull request #957 from pmoura/fix_at_end_of_stream_predicate_missing_check_for_instantiation_error
Fix at_end_of_stream/1 predicate missing check for instantiation error
2021-05-13 23:17:59 -06:00
Mark Thom
c8d0f6ff20 Merge pull request #955 from pmoura/fix_abolish_predicate_throw_of_instantiation_error
Fix abolish/1 predicate throw of instantiation_error
2021-05-13 23:17:30 -06:00
Mark Thom
2813c75292 Merge pull request #954 from pmoura/fix_set_stream_position_exception_term_missing_argument
Fix set_stream_position/2 predicate missing argument in exception term
2021-05-13 23:17:12 -06:00
Markus Triska
581e055359 FIXED: correct propagation of tuples_in/2 (#929)
Many thanks to @notoria for producing a very short test case.
2021-05-13 21:48:43 +02:00
Paulo Moura
88a2cfc5e1 Fix retract_clause/2 internal predicate to correctly handle module qualification and no lose body bindings 2021-05-12 23:48:12 +01:00
Paulo Moura
fb29830521 Fix retractall/1 predicate to retract both facts and rules 2021-05-12 23:46:05 +01:00
Paulo Moura
6540fea4da Fix at_end_of_stream/1 predicate missing check for instantiation error 2021-05-12 21:17:11 +01:00
Paulo Moura
4fd33b015e Fix subsumes_term/2 predicate to use unify_with_occurs_check/2 instead of (=)/2 2021-05-12 20:02:09 +01:00
Paulo Moura
d2fcdb3b6c Fix abolish/1 predicate throw of instantiation_error 2021-05-12 19:39:20 +01:00
Paulo Moura
c2fe0876c2 Fix set_stream_position/2 predicate missing argument in exception term 2021-05-12 19:32:24 +01:00
Mark Thom
39aebd7144 Merge pull request #952 from panasenco/dockerfile
Made rust version in Dockerfile static
2021-05-12 11:02:38 -06:00
panasenco
6a7d0b8c0a Made rust version in Dockerfile static, this seems to have really fixed the cache issues 2021-05-11 22:46:11 -07:00
Mark Thom
7b92bee596 Merge pull request #951 from pmoura/add_max_arity_prolog_flag
Add max_arity Prolog flag
2021-05-11 19:22:38 -06:00
Paulo Moura
f7eda362c7 Add max_arity Prolog flag 2021-05-12 00:34:47 +01:00
Mark Thom
0fb8459bc1 Merge pull request #948 from pmoura/fix_bounded_and_integer_rounding_function_flags_for_current_prolog_flag
Fix querying bounded and integer_rounding_function flag values
2021-05-11 16:55:36 -06:00
Paulo Moura
42d9b57733 Fix querying bounded and integer_rounding_function flag values 2021-05-11 12:08:11 +01:00
Mark Thom
ebe143d9c9 Merge pull request #946 from pmoura/fix_char_code_predicate_error_term_on_bould_non_atom_first_argument
Fix char_code/2 predicate wrong error term when the first argument is bound but is not an atom
2021-05-10 14:42:13 -06:00
Paulo Moura
8acbdfbf1d Fix char_code/2 predicate wrong error term when the first argument is bound but is not an atom 2021-05-10 21:27:04 +01:00
Mark Thom
6eb36226b1 Merge pull request #934 from panasenco/dockerfile
Dockerfile update: Switched from using @LukeMathWalker's image to using the official Rust image.
2021-05-10 11:10:39 -06:00
Mark Thom
21984f0bbe Merge pull request #938 from pmoura/fix_bitwise_shift_functions_type_error_in_second_argument
Fix bitwise shift functions type error in the second argument
2021-05-10 11:10:20 -06:00
Mark Thom
5eac945bc0 Merge pull request #937 from pmoura/fix_open_predicate_error_term_on_bound_stream_argument
Fix open/3-4 predicates error term when the stream argument is bound
2021-05-10 11:10:12 -06:00
Mark Thom
2736b4198a Merge pull request #936 from pmoura/fix_throw_1_control_construct_with_var_argument
Fix throw/1 control construct to throw an instantiation error when the argument is not bound
2021-05-10 11:09:59 -06:00
Mark Thom
a4f2f7faa6 Merge pull request #939 from pmoura/fix_missing_argument_in_current_op_3_predicate_exception_term
Fix missing argument in current_op/3 predicate exception term
2021-05-10 11:09:44 -06:00
Mark Thom
94923175a8 Merge pull request #940 from pmoura/fix_power_infix_operator_type_as_per_standard
Fix (**)/2 operator type
2021-05-10 11:09:23 -06:00
Mark Thom
f1c629e7bf Merge pull request #944 from triska/master
FIXED: module qualification for between/3 in goal expansion (#941)
2021-05-10 11:09:09 -06:00
Mark Thom
5d0c60bd24 Merge pull request #945 from pmoura/fix_char_code_error_checking
Fix char_code/2 predicate failure instead of exception on non-integer second argument
2021-05-10 11:08:58 -06:00
Mark Thom
59f08cd651 Merge pull request #942 from pmoura/fix_op_3_predicate_operator_priority_domain_error
Fix op/3 predicate operator_priority domain error
2021-05-10 11:07:04 -06:00
Paulo Moura
63db194140 Fix char_code/2 predicate failure instead of exception on non-integer second argument 2021-05-10 00:37:09 +01:00
Markus Triska
38db8d4e1a FIXED: module qualification for between/3 in goal expansion (#941)
Many thanks to @dcnorris for reporting this issue!
2021-05-09 23:54:37 +02:00
Paulo Moura
c2e8cbb846 Fix op/3 predicate operator_priority domain error 2021-05-09 22:25:37 +01:00
Paulo Moura
3c567b895f Fix (**)/2 operator type 2021-05-09 21:02:44 +01:00
Paulo Moura
673c75fb72 Fix missing argument in current_op/3 predicate exception term 2021-05-09 20:51:41 +01:00
Paulo Moura
7a576ee36e Fix bitwise shift functions type error in the second argument 2021-05-09 19:58:35 +01:00
Paulo Moura
73c09e3365 Fix open/3-4 predicates error term when the stream argument is bound 2021-05-09 19:21:21 +01:00
Paulo Moura
07dd2b279e Fix throw/1 control construct to throw an instantiation error when the argument is not bound 2021-05-09 12:42:51 +01:00
panasenco
c7943d2521 Switched from using @LukeMathWalker's image to using the official Rust image. Was experiencing cache issues before that, either due to the image, or due to Docker cache being weird... 2021-05-08 17:06:52 -07:00
Mark Thom
9b483d381f Merge pull request #928 from panasenco/permutation
Ported permutation/2 from SWI library(lists)
2021-05-08 15:09:01 -06:00
Mark Thom
4e25337779 Merge pull request #933 from pmoura/add_epsilon_arithmetic_constant
Add epsilon/0 arithmetic constant
2021-05-08 15:08:13 -06:00
panasenco
1e5041b8b0 Moved MIT header to top of lists.pl 2021-05-08 13:32:30 -07:00
Paulo Moura
3f4bbe9b9e Add epsilon/0 arithmetic constant 2021-05-08 21:23:54 +01:00
Mark Thom
56eaf883dc Merge pull request #932 from pmoura/fix_abolish_bug_with_no_existing_predicates
Fix abolish/1 to succeed on non-existing predicates as per standard
2021-05-08 12:39:28 -06:00
Paulo Moura
d8c0af7ded Fix abolish/1 to succeed on non-existing predicates as per standard 2021-05-08 11:13:08 +01:00
panasenco
8724a6b6e2 Ported permutation/2 from SWI library(lists) 2021-05-07 00:29:48 -07:00
Mark Thom
b968b2c438 add missing default RevJmpBy arm to thread_choice_instr_at_to (prompted by #922) 2021-05-05 11:16:39 -06:00
Mark Thom
533d1ea9ab set self.dynamic_mode to FirstOrNext::Next independently of succeed or failure in dynamic indexing instructions (prompted by #922) 2021-05-04 20:56:25 -06:00
Mark Thom
c9e32c449a correct assertz/1 bugs (#922) 2021-05-04 17:53:02 -06:00
Mark Thom
d9e42bfcba fix incremental compilation and multifile bugs 2021-05-03 16:59:12 -06:00
Mark Thom
f552564fc1 import builtins to dynamically generated modules unless builtins is being defined 2021-05-02 11:01:17 -06:00
Mark Thom
b21a096516 add read/2 and nl/1 (#896) 2021-05-01 16:12:00 -06:00
Mark Thom
d0b25de554 add user_error stream (#917) 2021-04-30 21:58:03 -06:00
Mark Thom
bba836dc31 fail on undefined multifile/discontiguous/dynamic predicates (#915) 2021-04-30 21:46:50 -06:00
Mark Thom
14fc8e2efa support full paths in load_context_path/2 (#916) 2021-04-30 20:38:26 -06:00
Mark Thom
d8bd4fbea6 fail when retracting undefined dynamic predicates (#918) 2021-04-30 20:36:56 -06:00
Mark Thom
7cc89da879 Merge pull request #920 from panasenco/serialize
Character logic cleanup and extension
2021-04-29 13:11:13 -06:00
panasenco
baed12f45e Oops, we want to keep digits as characters in library(abnf), but as numbers in library(json). 2021-04-28 23:05:02 -07:00
panasenco
dfbf291725 Restructuring and improving documentation. 2021-04-28 22:22:20 -07:00
panasenco
cb07074246 Replaced object() with pairs() in library(json), and made key type explicit. 2021-04-28 16:09:38 -07:00
panasenco
0198fe90b6 Added ABNF grammar to library(charsio) as well as octet character type that it depends on. Modified library(json) to use the new ABNF grammar. 2021-04-27 22:48:03 -07:00
panasenco
061221b073 Alphabetized character logic in macros.rs and system_calls.rs 2021-04-27 14:01:49 -07:00
Mark Thom
7e4cfede7d fix use of local skeletons to reload predicates (#919) 2021-04-27 02:01:00 -06:00
Mark Thom
48225c3c0a Merge pull request #913 from panasenco/json
Another thorough rewrite of library(json) to better preserve the complete set of answers as much as possible
2021-04-26 13:34:04 -06:00
panasenco
02b89362b2 Replaced raw_chars//1 in library(pio) with seq//1 in library(dcgs). 2021-04-25 14:53:37 -07:00
panasenco
e0e812f95c Added aw_chars//1 to library(pio). Began using setup_call_cleanup/3 as suggested by @triska. 2021-04-24 21:29:50 -07:00
panasenco
2cbc7e4f9a Another thorough rewrite of library(json) to better preserve the complete set of answers as much as possible and thereby write more elegant and simple code. Some performance gains too! 2021-04-23 10:30:38 -07:00
Mark Thom
7bec37bfd8 Merge pull request #912 from panasenco/json
library(json) beta
2021-04-22 17:44:44 -06:00
panasenco
828550687c Rewrote many parts of library(json) to leave no choicepoints when generating JSON. However, the generating performance actually worsened slightly... 2021-04-22 15:21:27 -07:00
panasenco
d3caba4073 Now preserving distinction between ints and floats 2021-04-20 19:59:01 -07:00
panasenco
7e802706fe Seem to be generating JSON correctly and at a good speed too! 2021-04-20 17:33:30 -07:00
panasenco
c87e3eac08 Removed CLP(Z) from library(json), achieving a 100x speedup in JSON parsing. 2021-04-20 17:30:41 -07:00
panasenco
5b1b12ec9e Fixed forward slash special case issue, added a bunch of test files that were successfully read! 2021-04-20 17:30:41 -07:00
panasenco
c0c6f13d44 Fixed defaulty representations of json_members//1 and json_elements//1 2021-04-20 17:30:41 -07:00
panasenco
20f192daa1 Changed formatting to match Markus Triska's as much as I can tell 2021-04-20 17:30:41 -07:00
panasenco
0e73b53803 Complete reordering and partial rewrite to match the official McKeeman form of the JSON specification 2021-04-20 17:30:41 -07:00
panasenco
e2923c378e Added objects and type distinction based on the principal functor 2021-04-20 17:30:41 -07:00
panasenco
216d4da85a Parsing and generating JSON numbers works perfectly now as far as I can tell 2021-04-20 17:30:24 -07:00
panasenco
407e775282 Began working on json library (fresh start after thinking about instantiation in terms of search strategies) 2021-04-20 17:29:36 -07:00
Mark Thom
e71afa63b0 Merge pull request #909 from triska/master
add a new section about support and discussions
2021-04-17 10:34:48 -06:00
Markus Triska
30fd99679a add a new section about support and discussions 2021-04-17 13:45:41 +02:00
Mark Thom
a9ef15cdfa Merge pull request #900 from triska/master
remove rectractall/1 (built-in since 8188e3d0cf).
2021-04-14 15:28:59 -06:00
Mark Thom
b08442b46f assertz/1, asserta/1, retract/1, retractall/1 are meta-predicates (#902, #903) 2021-04-14 15:28:16 -06:00
Markus Triska
f56e2fc48e remove rectractall/1 (built-in since 8188e3d0cf).
This resolves #899.
2021-04-14 20:18:50 +02:00
Mark Thom
5452b55e38 Merge pull request #897 from panasenco/master
Switched to cargo-chef for preparing the Docker image
2021-04-12 21:55:58 -06:00
panasenco
bec8d36961 Switched to cargo-chef for preparing the Docker image 2021-04-12 17:35:20 -07:00
Mark Thom
8188e3d0cf add missing standard predicates (#896) 2021-04-12 17:26:11 -06:00
Mark Thom
10e92eec32 call '$erase_ball' before running cleanup code in setup_call_cleanup (#886) 2021-03-24 16:56:53 -06:00
Mark Thom
37f2336eee stop unifying ! to free variables in control_functor/1 (#887) 2021-03-24 15:05:59 -06:00
Mark Thom
290cb1b517 make retract_local_clauses safe, remove warning about overwriting warning in do_not_duplicate_path_components test 2021-03-23 12:37:43 -06:00
Mark Thom
7520fe7000 use occurs check when enabled in UnifyValue (#885) 2021-03-22 22:05:03 -06:00
Mark Thom
9f861dfe89 reorder declaration matching in loader.pl 2021-03-22 18:13:01 -06:00
Mark Thom
fffb87d013 flush loader term queue before compiling declarations, declare '/1' as dynamic before asserting 2021-03-22 16:53:23 -06:00
Mark Thom
0cb731c584 remove redundant retraction records for incremental compilation 2021-03-22 15:27:45 -06:00
Mark Thom
233faea200 compare the tails of partial string prefixes in compare_pstr_prefixes (#883) 2021-03-22 12:44:58 -06:00
Mark Thom
1b9015a049 copies only those attributes planted by the calling module 2021-03-21 13:00:29 -06:00
Mark Thom
111de1462c remove vestigial attribute goals struct field and related predicates 2021-03-21 12:06:53 -06:00
Mark Thom
7fb0b4a8df rename issue841-occure-check.pl to issue841-occurs-check.pl 2021-03-19 21:39:28 -06:00
Mark Thom
627c49c5db recognize cuts in (->)/(,)/(;) calling contexts (#878) 2021-03-19 16:34:55 -06:00
Mark Thom
665b1ad58a Merge pull request #872 from notoria/charsio
Extend alnum, add forgotten graphic_token
2021-03-18 14:51:36 -06:00
Mark Thom
4623e9d7fc fix findall/4 (#873) 2021-03-18 12:50:12 -06:00
Mark Thom
a3f0290432 restore deleted parts of number_chars/2 and number_codes/2 (#721) 2021-03-17 20:23:58 -06:00
Mark Thom
69b1798af7 print floating point zero as 0.0 (#859) 2021-03-17 20:17:08 -06:00
Mark Thom
10af206024 make chars callable (#713) 2021-03-17 19:50:41 -06:00
Mark Thom
6c23d7aec8 always print variable bindings before names (#865) 2021-03-17 19:27:53 -06:00
Mark Thom
7937ccee30 check that a load context path is a filename before potentially returning it as a string 2021-03-17 18:58:33 -06:00
Mark Thom
b98e8c34eb findall/4 should be declared as a meta-predicate (#873) 2021-03-17 18:13:06 -06:00
Mark Thom
5c2059b4e8 issue an imcomplete reduction error if stream ends with tokens remaining to be parsed (#871) 2021-03-17 14:23:34 -06:00
notoria
f4be0cf4b3 Extend alnum, add forgotten graphic_token 2021-03-17 20:47:09 +01:00
Mark Thom
5f7abda22d remove operators declared at user-level from files when reloading 2021-03-16 18:30:34 -06:00
Mark Thom
51424aed32 clear user-level definitions when reloading a file (#455) 2021-03-16 02:30:09 -06:00
Mark Thom
914fb09ed0 don't warn about overwriting a clause asserted to a dynamic predicate whose clauses have all been retracted (#870) 2021-03-15 14:22:54 -06:00
Mark Thom
7ea7e5c951 remove intra-module predicates from module code directory when module is being re-defined (#848) 2021-03-13 23:22:24 -07:00
Mark Thom
c69807b416 Merge pull request #866 from Skgland/add_tests
Add tests for some recently closed Issues
2021-03-13 00:39:01 -07:00
Skgland
08320119f1 fix expected output for syntax_error test 2021-03-11 17:01:26 +01:00
Skgland
49dcd9bb4a address problems brought up in review 2021-03-11 16:16:31 +01:00
Skgland
8f41603101 add more tests
for some of the recently closed issues
2021-03-11 01:52:22 +01:00
Skgland
d437609365 improve comment 2021-03-11 01:52:21 +01:00
Skgland
e1c681fffe clean up tests/
give helper function a more descriptive name
2021-03-11 01:52:21 +01:00
Mark Thom
d4d47182b4 fix is_cyclic_term (#864) 2021-03-10 16:11:48 -07:00
Mark Thom
bc2d0191ff Merge pull request #861 from notoria/mod
Small improvement for mod in CLP(Z)
2021-03-10 13:52:48 -07:00
notoria
bd222ed2bf Added a test for mod in CLP(Z) 2021-03-10 21:20:18 +01:00
Mark Thom
ceac824e2b Merge pull request #862 from notoria/xor
Implemented a propagator for xor in CLP(Z)
2021-03-09 12:33:06 -07:00
notoria
93b835ae0d Use functional notation in CLP(Z) for compatibility 2021-03-07 20:59:25 +01:00
notoria
aa2d57cf37 Implemented a propagator for xor in CLP(Z) 2021-03-07 18:52:10 +01:00
notoria
f6d4821a68 Small improvement for mod in CLP(Z) 2021-03-07 18:38:04 +01:00
Mark Thom
fa025bcf39 Merge pull request #858 from triska/master
CLP(B) correction
2021-03-05 12:57:20 -07:00
Markus Triska
842176a595 is is --> is 2021-03-05 18:36:00 +01:00
Markus Triska
0fb74b56b3 FIXED: CLP(B): weighted_maximum/3 with repeated variables
Many thanks to @jburse for reporting this issue:

    https://github.com/triska/clpz/issues/15

Example:

    ?- sat(~(Y*X)), sat(Z=:=Y), weighted_maximum([7,2,5],[X,Y,Z],W).
       W = 7, Y = 0, X = 1, Z = 0
    ;  W = 7, Y = 1, X = 0, Z = 1
    ;  false.
2021-03-05 18:35:19 +01:00
Markus Triska
87abcd6a52 adapt comment to Scryer Prolog 2021-03-05 18:16:38 +01:00
Mark Thom
0a71e40030 qualify '$print_project_attributes_exception' call (#857) 2021-03-05 01:14:56 -07:00
Mark Thom
1b9db035ba call project_attributes from top-level (#846) 2021-03-04 16:05:06 -07:00
Mark Thom
34c3035c2b Merge pull request #856 from triska/master
Improvements for library(format)
2021-03-04 13:53:25 -07:00
Markus Triska
d92951ba5b ENHANCED: more readable indentation of nested disjunctions
Example:

    ?- portray_clause((h :- a ; b ; c)).
    h :-
       (  a
       ;  b
       ;  c
       ).
       true.
2021-03-04 21:40:24 +01:00
Markus Triska
c5749cbbb1 remove unnecessary argument 2021-03-04 21:40:24 +01:00
Mark Thom
a2e5bcc137 Merge pull request #854 from triska/master
Better indentation for nested ( If -> Then ; Else ) constructs
2021-03-03 16:11:03 -07:00
Markus Triska
761d707b69 use the new portray_clause/1 output for generated definitions 2021-03-03 22:34:09 +01:00
Markus Triska
d327a05e12 separate side-effects from declarative description to facilitate test cases etc. 2021-03-03 22:34:09 +01:00
Markus Triska
6477d21e24 ENHANCED: more readable indentation of nested ( If -> Then ; Else ) constructs
Example:

    ?- portray_clause((h :- ( a -> b ; c -> d ; e, f))).
    h :-
       (  a ->
          b
       ;  c ->
          d
       ;  e,
          f
       ).
       true.
2021-03-03 22:34:09 +01:00
Mark Thom
d3612e956e catch exceptions thrown from initialization goals (#852) 2021-03-03 13:56:13 -07:00
Mark Thom
5a3ee3a46e has_tail must be false for use as a filename from open/{3,4} 2021-03-03 12:16:47 -07:00
Mark Thom
23cb743ce2 Merge pull request #851 from Skgland/fix_num_test2
Workaround to make num tests work
2021-03-02 23:36:59 -07:00
Skgland
40e5a2dc35 workaround to make num tests work 2021-03-03 01:18:24 +01:00
Mark Thom
73eb4079ef Merge pull request #849 from triska/master
Remove duplicated definitions
2021-03-02 11:59:22 -07:00
Markus Triska
5b60c8aa7e remove min_/3 and max_/3 which are also defined elsewhere 2021-03-02 19:59:57 +01:00
Markus Triska
8c5a688566 remove group_pairs_by_key/2, which is now provided by library(pairs) 2021-03-02 19:58:20 +01:00
Mark Thom
7ea9706c94 warn when overwriting a predicate (#848) 2021-03-02 11:43:47 -07:00
Mark Thom
5976e2d873 do not check for non-unifiability in dif:attribute_goals, which is sure to succeed, resulting in no goals being printed at top-level (#844) 2021-03-02 10:34:25 -07:00
Mark Thom
d32d452583 Merge pull request #845 from triska/master
explain the new occurs_check flag
2021-03-01 11:45:53 -07:00
Markus Triska
b77bdabca6 explain the new occurs_check flag 2021-03-01 19:42:16 +01:00
Mark Thom
e8971e0d8b use atom_chars to convert .scryerrc path string to atom 2021-02-28 23:26:22 -07:00
Mark Thom
498c4660d0 use plain ! while interpreting control functors in metacalls (#724) 2021-02-28 19:12:35 -07:00
Mark Thom
54c142fc0d apply expand_module_names to call(A) goals 2021-02-28 19:01:36 -07:00
Mark Thom
6079402dc4 suppress tabled/2 existence errors in tabling library, do not print evaluation errors generated by term and goal expansion in loader.pl 2021-02-28 18:56:24 -07:00
Mark Thom
3d4a7f97e1 Merge pull request #843 from mthom/rebis-dev
Merge rebis-dev into master
2021-02-28 18:33:24 -07:00
Mark Thom
d6e04beb95 check that Module is an atom before calling Module:goal_expansion/2 2021-02-28 18:28:12 -07:00
Mark Thom
9a225e6244 wrap variable goals in module-expanded call/1 2021-02-28 18:15:33 -07:00
Mark Thom
a03f00628b do not print error messages if .scryerrc[.pl] does not exist (#842) 2021-02-28 17:57:35 -07:00
Mark Thom
360485d830 print errors arising from term and goal expansion calls from loader.pl 2021-02-28 16:19:37 -07:00
Mark Thom
3f1cfd2995 introduce bind_fn, use in applicable Fact/Query instructions (#841) 2021-02-28 16:19:06 -07:00
Mark Thom
08e2b601f1 pop non-composite terms from iter stack in is_cyclic_term 2021-02-28 12:28:19 -07:00
Mark Thom
101ed9a633 add error value to occurs_check prolog flag (#783) 2021-02-28 11:44:23 -07:00
Mark Thom
81ecd17b93 Merge pull request #838 from Skgland/librarify
Split main.rs into lib.rs and bin/scryer-prolog.rs
2021-02-28 11:39:24 -07:00
Skgland
5a78f02dcb revert removal of PartialEq 2021-02-28 19:21:32 +01:00
Skgland
0a08464d4f move the signal handler to the binary 2021-02-28 19:21:31 +01:00
Skgland
a638d42d92 remove #[allow(dead_code)]
only commenting out the unused variant as `OptArgIndexKeyType`
as it appears to mirror `OptArgIndexKey` and there List is not unused
so this appears to be not yet used rather than obsolete
2021-02-28 19:21:31 +01:00
Skgland
90256ea2f5 fix CI lib build with beta compiler 2021-02-28 19:21:31 +01:00
Skgland
90aefd35f0 adjust ci 2021-02-28 19:21:30 +01:00
Skgland
2f428b7261 split into lib and bin
* makes most pub things in src/ pub(crate) as not to expose things accidentally
  * only those things needed by src/bin/scryer-prolog.rs and tests/scryer.rs
    should be pub
* split src/main.rs into src/lib.rs and src/bin/scryer-prolog.rs
* add tests folder and run most of the files in src/tests with cargo test
  added bytes method to Stream in src/machine/streams.rs to check if stdout is as expected
2021-02-28 19:21:30 +01:00
Mark Thom
f935060b2b restore .scryerrc loading 2021-02-28 11:03:55 -07:00
Mark Thom
a367812348 add occurs_check flag (#309) 2021-02-28 10:41:32 -07:00
Mark Thom
8e6a89b279 enable op declarations with lists of operands using term expansion (#839) 2021-02-28 09:15:03 -07:00
Mark Thom
0747697d10 add lines read from devour_whitespace (#836) 2021-02-28 02:09:53 -07:00
Mark Thom
8c7494885f set compilation_target of assert loader to match that of predicate qualifier 2021-02-28 00:27:50 -07:00
Mark Thom
590a0b8077 update the parser with the number of lines already read by the underlying stream (#836) 2021-02-27 23:56:58 -07:00
Mark Thom
064d261357 implement logical update semantics for dynamic database predicates 2021-02-27 23:33:10 -07:00
Mark Thom
adb96710bf Merge pull request #837 from triska/rebis-dev
Small improvements for Rebis
2021-02-25 15:17:16 -07:00
Markus Triska
dd64268bc7 add entry for library(lambda) 2021-02-25 23:12:56 +01:00
Markus Triska
aecbd0eda3 omit internal residual goals for global_cardinality/2 2021-02-25 23:12:36 +01:00
Markus Triska
2c1b7e3b14 remove unused attribute_goal/2 2021-02-25 23:12:36 +01:00
Mark Thom
3eb6357892 Merge pull request #835 from triska/lambda
ADDED: library(lambda) by Ulrich Neumerkel
2021-02-25 14:26:41 -07:00
Markus Triska
e0c98a5c79 adapt libraries and toplevel output to Scryer Prolog 2021-02-25 22:08:25 +01:00
Markus Triska
dfb60ec2ed use individual meta_predicate/1 declarations 2021-02-25 22:08:25 +01:00
Markus Triska
b85e260b92 ADDED: library(lambda) by Ulrich Neumerkel.
Source: http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/lambda.pl

More information:

    http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord
2021-02-25 22:08:00 +01:00
Mark Thom
4d72845c58 add copy_term_nat/2 (#795) 2021-02-22 22:39:34 -07:00
Mark Thom
9067f76bd6 no longer delay verify_attributes for cut instructions (#807) 2021-02-22 18:33:35 -07:00
Mark Thom
6c447da730 no longer generate a choice point in bb_b_put/2 (#742) 2021-02-22 18:27:30 -07:00
Mark Thom
1a9f6f06df consume whitespace before reading a term in loader.pl, modify prolog_load_context at file key to better reflect loading context, use it when reporting singleton variables (#812) 2021-02-22 13:23:22 -07:00
Mark Thom
5f8bdc564b extend existing local predicate skeletons in LoadState::compile 2021-02-22 02:00:25 -07:00
Mark Thom
f6498f2a7b anonymize singleton variables in iso_ext.pl, charsio.pl 2021-02-21 19:20:42 -07:00
Mark Thom
0ef5f7f9b1 issue singleton variable warnings from loader.pl (#812) 2021-02-21 19:20:42 -07:00
Mark Thom
a323a4dd8e wrap stream instances in one Rc<RefCell<..>>, use Ref/RefMut to access stream options 2021-02-21 19:20:42 -07:00
Mark Thom
d5581ffd05 Merge pull request #834 from aarroyoc/rebis-http
Fix HTTP server in rebis
2021-02-21 13:32:09 -07:00
Adrián Arroyo Calle
77968ab33a Fix typo 2021-02-21 21:08:03 +01:00
Adrián Arroyo Calle
265a5955d7 Use notoria's comment 2021-02-21 20:22:08 +01:00
Mark Thom
465ab2fa34 Merge pull request #830 from aarroyoc/rebis-http
Fix HTTP server on Rebis. Breaking change
2021-02-21 12:10:09 -07:00
Mark Thom
d60bfef924 tweak bimetatrans_tests.pl 2021-02-21 12:08:36 -07:00
Mark Thom
5225587ff5 add set_prolog_flag to declarations recognized by loader.pl 2021-02-21 12:08:14 -07:00
Mark Thom
fec790e108 correct comment before call meta_predicate declarations 2021-02-20 19:10:34 -07:00
Mark Thom
eb5ea28bcd add 'e' as evaluable functor for Euler's constant (#832) 2021-02-20 18:32:04 -07:00
Mark Thom
a9fe2ab5c4 use env::current_dir in Machine::current_dir (#833) 2021-02-20 18:25:34 -07:00
Mark Thom
f2db0886dd move to a local num-rug-adapter crate addressing #822 2021-02-19 17:35:10 -07:00
Mark Thom
f6926a9642 remove nl after top-level use_module (#818) 2021-02-19 14:36:30 -07:00
Mark Thom
d3442bb08f qualify command-line specified goals with user: (#820) 2021-02-19 14:12:34 -07:00
Mark Thom
a08f5c3016 treat LocalCodePtr::Halt received at lookup_local_instr as an interrupt (#823) 2021-02-19 13:44:22 -07:00
Mark Thom
eff892ccb8 add qualified imports use_module library modules 2021-02-18 17:58:16 -07:00
Adrián Arroyo Calle
b565eaaf5a Fix HTTP server to work on Rebis branch. Breaking change 2021-02-19 00:13:00 +01:00
Mark Thom
3831a371a4 Merge pull request #829 from aarroyoc/uuidv4
UUIDv4 library
2021-02-18 14:29:06 -07:00
Adrián Arroyo Calle
77ec8ebfc3 UUIDv4 generation 2021-02-18 22:16:24 +01:00
Mark Thom
c272e4d1e8 use add_dynamically_generated_module in get_or_insert_local_code_index 2021-02-18 14:15:28 -07:00
Mark Thom
d69b7f41f2 add '$remove_module_exports' to loader.rs 2021-02-18 14:08:25 -07:00
Mark Thom
c8a47d839a Merge pull request #828 from triska/rebis-dev
enable meta_predicate/1 declarations
2021-02-18 12:21:21 -07:00
Markus Triska
b3db8913c6 enable meta_predicate/1 declarations 2021-02-18 20:19:23 +01:00
Mark Thom
e8f8f34a76 Merge pull request #827 from triska/rebis-dev
add appropriate meta_predicate/1 declarations
2021-02-18 12:15:12 -07:00
Markus Triska
5bec2c87cb add appropriate meta_predicate/1 declarations 2021-02-18 20:15:04 +01:00
Mark Thom
d4d283480f Merge pull request #826 from triska/rebis-dev
add appropriate meta_predicate/1 declarations
2021-02-18 11:17:00 -07:00
Markus Triska
71a524662d add appropriate meta_predicate/1 declarations 2021-02-18 19:16:11 +01:00
Mark Thom
172aef7e26 Merge pull request #825 from tniessen/readme-typo-fix
Fix typo in README
2021-02-18 11:15:50 -07:00
Tobias Nießen
395b5faa2d instantianted -> instantiated 2021-02-18 16:53:10 +01:00
Tobias Nießen
f4a765c5ed Fix typo in README 2021-02-18 15:41:18 +01:00
Mark Thom
f5ad845d57 try to canonicalize to absolute path in current_dir(), use it in setting LoadContext directories (#821) 2021-02-17 18:49:51 -07:00
Mark Thom
2a70ca375c restore support for tabling and delimited continuations (#779) 2021-02-17 16:18:49 -07:00
Mark Thom
396c589743 add second term expansion to capture the addition of hook predicates to the expansion process in loader.pl, loader.rs 2021-02-17 15:39:52 -07:00
Mark Thom
7670b81633 remove VNs variable from loader.pl 2021-02-17 13:31:58 -07:00
Mark Thom
f9b98f97b6 remove unnecessary cause of failure in parser, qualify call to op_/3 in builtins.pl 2021-02-17 11:56:58 -07:00
Mark Thom
00bf39204d add missing variable checks in various places 2021-02-17 11:12:19 -07:00
Mark Thom
e9ba3ad223 handle user:goal_expansion, but not Target:goal_expansion, explicitly in loader.pl 2021-02-17 01:19:51 -07:00
Mark Thom
6b6666be47 support discontiguous and multifile declarations 2021-02-16 22:08:32 -07:00
Mark Thom
e2a413df78 compile scoped clauses from loader.pl 2021-02-13 19:41:04 -07:00
Mark Thom
b24f68eee7 remove vestigial uses of constant_eq 2021-02-11 19:22:03 -07:00
Mark Thom
72c1a0222c Box LoadStatePayload when storing it in the heap 2021-02-11 19:21:20 -07:00
Mark Thom
bd97083268 remove unused is_internal function from indexing.rs 2021-02-11 18:51:21 -07:00
Mark Thom
e7cf5d4f3d use machine-generated PartialEq instance for hashing Constant (#817) 2021-02-11 18:40:23 -07:00
Mark Thom
2429971bcd update call/N indices in user-level code_dir in addition to those of pre-loaded modules 2021-02-11 15:38:30 -07:00
Mark Thom
170a71bd02 offset SwitchOnTerm's Internal indices only if the proper instructions were emitted 2021-02-11 15:38:16 -07:00
Mark Thom
14efbb1356 roll back extent of heap when loads are complete in loader.pl 2021-02-11 15:37:43 -07:00
Mark Thom
20cd0dd77a use IndexMap over HashMap in prolog_parser 2021-02-11 15:35:14 -07:00
Mark Thom
2db1fac1eb break out of the first inner loop if constants contains no key in remove_constant_indices (#816) 2021-02-10 14:13:37 -07:00
Mark Thom
ce8490ed41 Merge pull request #813 from triska/rebis-dev
~| now incorporates the number of characters described so far
2021-02-09 17:57:08 -07:00
Mark Thom
7e208984de reset merging_ptr offset before insertion of each overlapping constant (#816) 2021-02-09 17:18:00 -07:00
Mark Thom
4d29a3ae3c check for control functors (,/;/->) before jumping to internal interpretation (#815) 2021-02-09 16:40:25 -07:00
Mark Thom
547c63b28e qualify exception handling code in loader.pl (#809) 2021-02-09 15:47:14 -07:00
Mark Thom
9004aae597 Merge branch 'rebis-dev' of https://github.com/mthom/scryer-prolog into rebis-dev 2021-02-09 14:30:09 -07:00
Markus Triska
1bdfc5a1a4 ~| now incorporates the number of characters described so far
Therefore, subsequent uses of ~N| now yield the specified column,
instead of potentially overshooting.

Example:

    ?- format("hello~|~t~8|!", []).
    hello   !   true.

Such cases are probably rather uncommon: Relative positioning with ~N+
is more likely to be used in such cases, and that worked as intended
also previously as it does now:

    ?- format("hello~|~t~3+!", []).
    hello   !   true.

In fact, if absolute positions are used, then ~| can be omitted entirely:

    ?- format("hello~t~8|!", []).
    hello   !   true.
2021-02-09 20:55:46 +01:00
Mark Thom
195273f01d throw exceptions when modules do not contain claimed exports 2021-02-09 12:17:34 -07:00
Mark Thom
605aea2211 Merge pull request #811 from triska/master
link to announcement of Rebis Development Branch, call for testing
2021-02-09 11:59:59 -07:00
Markus Triska
d1afcb941f link to announcement of Rebis Development Branch, call for testing 2021-02-09 19:14:25 +01:00
Mark Thom
cc7e21170f correct faulty removal of clauses in abolish/1 2021-02-08 19:02:26 -07:00
Mark Thom
30602c0849 clean up implementation of (->)/2, (;)/2, and (,)/2 2021-02-07 21:13:27 -07:00
Mark Thom
1dabe95899 remove tidy_trail 2021-02-07 15:39:48 -07:00
Mark Thom
4995c0ed94 append .pl to names of files that do not exist in loader.pl 2021-02-07 15:38:03 -07:00
Mark Thom
df82dbe5f5 use parent() instead of ancestors().next() in load_context_directory() 2021-02-07 13:51:01 -07:00
Mark Thom
de9c74e1d9 Merge branch 'Skgland-clean_up' into rebis-dev 2021-02-07 00:34:57 -07:00
Bennet Bleßmann
d89eb9ff0e change reposity url 2021-02-06 22:38:40 +01:00
Skgland
5041042925 rename prolog_parser_rebis to prolog_parser
also remove remaining extern crate statements that slipped through earlier
2021-02-06 22:13:34 +01:00
Skgland
0d983e63a1 fix incorrect rug path 2021-02-06 22:13:06 +01:00
Skgland
3f7a60d84b more clippy lints 2021-02-06 22:13:06 +01:00
Skgland
1eb9fcf521 more clippy lints 2021-02-06 22:13:06 +01:00
Skgland
1571690bbb fix clippy lint match|if let looks like matches! macro 2021-02-06 22:13:05 +01:00
Skgland
eb2133e648 fix clippy lint Manual RangeInclusive::contains implementation 2021-02-06 22:13:05 +01:00
Skgland
9107b3ddbe fix some clippy lint warnings 2021-02-06 22:13:05 +01:00
Skgland
64433bd8dd fix macro hygien 2021-02-06 22:13:04 +01:00
Skgland
bc6159c538 change prolog_parse_rebis to use 2018 edition rust
so all crates use the same edition
2021-02-06 22:13:04 +01:00
Skgland
8e5954f36f remove use statements from main.rs that temporary replaced extern crate and fixed paths that previously used them 2021-02-06 22:13:04 +01:00
Skgland
b53ef148a0 remove extern crate declaration and fix outfall (macros now need to be imported into scope)
using use declarations in main.rs so that use paths don't need to be updated as well, this will be done in a later commit
2021-02-06 22:12:31 +01:00
Mark Thom
3f950490f9 set rustyline to check the cursor position (#802), don't add .pl extension to files in loader.pl (#800) 2021-02-06 13:47:26 -07:00
Mark Thom
49b1c1368e remove dynamic clause declaration for clause internal 2021-02-05 23:16:42 -07:00
Mark Thom
bdb5df104a shed module names from arguments of the (->)/2, (,)/2, (;)/2 predicates 2021-02-05 22:50:57 -07:00
Mark Thom
700778e574 collate paths as atoms in loader.pl (#778) 2021-02-05 20:18:56 -07:00
Mark Thom
a104b35cea gather initialization goals and call them once load succeeds (#792) 2021-02-05 18:42:30 -07:00
Mark Thom
bbbf95705b implement abolish/1 2021-02-05 18:35:06 -07:00
Mark Thom
927871d73d Merge branch 'rebis-dev' of https://github.com/mthom/scryer-prolog into rebis-dev 2021-02-05 17:17:14 -07:00
Mark Thom
62419e975f bootstrap the top-level by calling ''/1 as in master (#790) 2021-02-05 17:16:42 -07:00
Mark Thom
3fc2c4223b delay premature set_switch_var_offset_* calls in retract_clause, add module resolution for asserta/assertz/retract/clause 2021-02-05 17:12:25 -07:00
Mark Thom
84da884211 Merge pull request #798 from triska/rebis-dev
CLP(ℤ) enhancements for Rebis
2021-02-05 13:24:53 -07:00
Mark Thom
1bc8e9aebf make load_context_path/2 handle .pl file extensions, remove commented out code 2021-02-05 13:13:54 -07:00
Markus Triska
a228e46a39 ENHANCED: Enable goal expansion for CLP(ℤ).
This is now possible since #445 is resolved.
2021-02-05 21:06:08 +01:00
Markus Triska
81913a5987 Revert "preliminary workaround for #788"
This reverts commit 164b993064,
which was made obsolete by 75a52f032b.
2021-02-05 20:59:47 +01:00
Mark Thom
2d7f31a60d implement dynamic, multifile, and discontiguous properties and callable type checking for predicate_property/2 2021-02-05 02:16:14 -07:00
Mark Thom
804858d736 remove defunct reset function (#782) 2021-02-04 21:02:37 -07:00
Mark Thom
222be9cf6c modify rightmost_power/2 to ignore module qualification on (^)/2 (#794) 2021-02-04 20:51:55 -07:00
Mark Thom
75a52f032b perform goal expansion in call/N (#788, #797) 2021-02-04 20:40:48 -07:00
Mark Thom
a9e0a51059 Merge pull request #793 from triska/rebis-dev
preliminary workaround for #788
2021-02-04 12:51:09 -07:00
Markus Triska
164b993064 preliminary workaround for #788
This change can be reverted once the issue is resolved.
2021-02-04 20:48:09 +01:00
Mark Thom
62b103fa23 Merge branch 'rebis-dev' of https://github.com/mthom/scryer-prolog into rebis-dev 2021-02-03 22:34:34 -07:00
Mark Thom
e17eb01f76 respect module context in expand_module_names (#788) 2021-02-03 16:47:22 -07:00
Mark Thom
2fe55e4715 resolve calls to tranpose_ and same_length in lists.pl (#790) 2021-02-03 16:38:46 -07:00
Mark Thom
a8a82e45a0 introduce cuts over indexed (:)/2 of dcgs:phrase_/3 (#786) 2021-02-03 16:03:15 -07:00
Mark Thom
fcba140ffe Merge pull request #787 from triska/rebis-dev
add meta_predicate declaration for with_local_attributes/3
2021-02-03 15:51:15 -07:00
Markus Triska
b96ff781bc add meta_predicate declaration for must_succeed/1 2021-02-03 23:49:28 +01:00
Mark Thom
94392f248c enable phrase_ rules for module resolved grammars (#785) 2021-02-03 15:48:12 -07:00
Markus Triska
24e6c31c44 add meta_predicate declaration for with_local_attributes/3
This is needed for all_distinct/1 etc.

Example:

    ?- all_distinct([X,Y,Z]).
       clpz:all_distinct([X,Y,Z])
    ;  false.
2021-02-03 23:45:21 +01:00
Mark Thom
cab0e4395a manually expand module names in higher-order predicates of lists.pl and give call_residue_vars/2 a meta-predicate definition 2021-02-03 15:37:16 -07:00
Markus Triska
ae66e299e6 Use term expansion for generated predicates. 2021-02-03 15:21:39 -07:00
Mark Thom
88ad2ee103 restore cut after dcg_constr check (#784) 2021-02-03 15:21:20 -07:00
Mark Thom
48f3f4ca37 add meta-predicate declarations to reif library 2021-02-03 12:46:33 -07:00
Mark Thom
f3ab17a3c0 Merge branch 'master' into rebis-dev 2021-02-03 12:00:00 -07:00
Mark Thom
5749d43fb6 restore prolog_parser test files (#782) 2021-02-03 11:20:55 -07:00
Mark Thom
f5d808e68f do not traverse data structures when binding to local variables in unify_with_occurs_check (#781) 2021-02-03 11:11:22 -07:00
Mark Thom
88f1160e2b use find_inner_choice_instr when appending an indexed clause to an indexed subsequence 2021-02-03 01:22:13 -07:00
Mark Thom
b7218d2279 Merge branch 'rebis-dev' of https://github.com/mthom/scryer-prolog into rebis-dev 2021-02-02 22:59:28 -07:00
Mark Thom
e928a0aaea expand attributed variable goals 2021-02-02 21:47:59 -07:00
Mark Thom
a2447ecaa3 replace Char '!' with QueryTerm::BlockedCut 2021-02-02 21:03:26 -07:00
Mark Thom
09a089d5f1 set target_pos' clause_start correctly in append_compiled_clause 2021-02-02 21:02:08 -07:00
Mark Thom
137b0cd837 Merge pull request #774 from brightly-salty/re-add-prolog_parser
Add prolog_parser to scryer_prolog
2021-02-02 17:56:26 -07:00
Caden Haustein
4ffdd9eb15 Add prolog_parser 2021-02-02 18:35:41 -06:00
Mark Thom
4e29099ed9 export loader's public predicates from builtins, handle module resolution from DCGs 2021-02-02 15:43:52 -07:00
Mark Thom
8900df6f13 add built_in to predicate_property 2021-02-02 15:03:14 -07:00
Mark Thom
814c034683 add meta-predicate declarations before their definitions 2021-02-01 19:26:50 -07:00
Mark Thom
e1ec4bee75 move term expansion logic entirely into loader.pl 2021-02-01 16:50:05 -07:00
Mark Thom
6ed7767512 add module scoping to predicate_property, expand subgoals in module resolved predicates 2021-02-01 16:09:19 -07:00
Mark Thom
08b18af8a0 call attribute variable goals with module resolution 2021-02-01 14:48:13 -07:00
Mark Thom
75908ab88f replace (:) with 0 in meta-predicate declarations, remove support for (:) in meta-predicate declarations 2021-01-31 18:00:10 -07:00
Mark Thom
fcba33997f Merge branch 'master' into rebis-dev 2021-01-31 12:41:18 -07:00
Mark Thom
134be307d3 Merge pull request #771 from notoria/args
Handled the empty case (#770)
2021-01-31 12:11:57 -07:00
Caden Haustein
092f8e6482 Fix errors, move prolog_parser 2021-01-31 12:43:39 -06:00
Caden Haustein
cf5960afad Add prolog_parser to scryer_prolog 2021-01-31 11:40:20 -06:00
notoria
e5204e55d3 Handled the empty case (#770) 2021-01-31 11:03:17 +01:00
Mark Thom
61b14d1cd9 fix bug in append_compiled_clause, generalize merging indexed subsequences of clauses 2021-01-31 01:18:45 -07:00
Mark Thom
c2e3b47d29 properly thread functors whose first subterm is a variable in thread_goals/4 2021-01-30 23:37:23 -07:00
Mark Thom
a239007db0 target prolog_parser_rebis on crates.io 2021-01-30 22:03:07 -07:00
Mark Thom
c5e5eb8b86 explain motive for Rebis development branch in README.md 2021-01-30 21:29:41 -07:00
Mark Thom
a4d15bfb88 move to a predicate-based module system, move to loader.[rs|pl]-based compilation, add support for incremental clause compilation 2021-01-30 21:07:51 -07:00
Mark Thom
b33158b92e Merge pull request #766 from aarroyoc/http-form
HTTP Server: urlencoded forms and some syntax improvements
2021-01-30 13:55:40 -07:00
Mark Thom
3b30c4d487 Merge pull request #769 from notoria/between
Removed redundant choicepoint from between/3
2021-01-30 13:55:05 -07:00
notoria
dfd7ac633a Removed redundant choicepoint from between/3 2021-01-30 17:45:55 +01:00
Adrián Arroyo Calle
213f8a604e Test against macOS 10.15 2021-01-27 23:40:26 +01:00
Adrián Arroyo Calle
13499352fd urlencoded forms and some syntax improvements 2021-01-27 23:34:38 +01:00
Mark Thom
70aa4934c5 Merge pull request #762 from aarroyoc/gh-action
GitHub Actions
2021-01-23 13:31:02 -07:00
Adrián Arroyo Calle
1d538ee70c Fix Beta 2021-01-16 22:32:50 +01:00
Adrián Arroyo Calle
2ed2e6d62f Disable Windows 2021-01-16 22:00:05 +01:00
Adrián Arroyo Calle
aea0570d2b Minor fix 2021-01-16 19:12:53 +01:00
Adrián Arroyo Calle
def33a77da Delete Travis CI 2021-01-16 19:11:07 +01:00
Adrián Arroyo Calle
29db677a9e GitHub Actions 2021-01-16 19:10:50 +01:00
Mark Thom
d362ca3a6d Merge pull request #747 from triska/determinism
reflect better determinism thanks to #732
2020-12-29 14:43:30 -07:00
Markus Triska
3796792421 reflect better determinism thanks to #732 2020-12-29 22:24:56 +01:00
Mark Thom
b8020db029 Merge pull request #726 from aarroyoc/http-server
HTTP Server
2020-12-29 13:07:59 -07:00
Adrián Arroyo Calle
a90030ca2d PR feedback 2020-12-28 22:55:32 +01:00
Adrián Arroyo Calle
3f8d3afe5c URL Decode 2020-12-28 14:42:20 +01:00
Adrián Arroyo Calle
155004bdbb Log time 2020-12-27 22:36:24 +01:00
Adrián Arroyo Calle
534c74b67c Improve docs & Route matching 2020-12-27 20:14:21 +01:00
Adrián Arroyo Calle
5428935ab1 File response 2020-12-27 14:48:05 +01:00
Adrián Arroyo Calle
fc14d089b1 Use phrase instead of forall 2020-12-27 14:11:09 +01:00
Adrián Arroyo Calle
4e543238e1 Queries 2020-12-27 13:23:38 +01:00
Adrián Arroyo Calle
6579a542b8 Merge branch 'master' into http-server 2020-12-27 12:18:05 +01:00
Adrián Arroyo Calle
27e3dcea6c Redirections, default status code, rout matching 1.0 2020-12-27 12:16:46 +01:00
Mark Thom
01f73b11d8 fix bug in code walker causing entire clauses to be skipped (#736) 2020-12-26 22:35:28 -07:00
Mark Thom
dd07226ab4 Merge pull request #743 from triska/determinism
reflect determinism improvement thanks to the latest changes
2020-12-23 13:22:02 -07:00
Markus Triska
67d856e4b7 reflect determinism improvement thanks to the latest changes 2020-12-23 18:06:35 +01:00
Mark Thom
6ad1e57123 Merge pull request #741 from triska/determinism
reflect improved determinism thanks to the improvements by @notoria
2020-12-22 19:18:46 -07:00
Markus Triska
27a52ef56a reflect improved determinism thanks to the improvements by @notoria 2020-12-23 00:05:29 +01:00
Mark Thom
d290596c1d Merge pull request #740 from triska/indexing
small documentation improvements related to the new indexing strategy
2020-12-22 15:39:17 -07:00
Mark Thom
342b4a3b52 Merge pull request #734 from triska/deterministic_format
ENHANCED: format_//2, format/[2,3], portray_clause/1 are now deterministic
2020-12-22 14:51:56 -07:00
Mark Thom
19652e6367 Merge pull request #732 from notoria/indexing
Implemented a different way to index clauses
2020-12-22 14:51:10 -07:00
Markus Triska
1c76c869ef small documentation improvements related to the new indexing strategy 2020-12-22 21:59:11 +01:00
notoria
706d842102 Renamed find_optimal_index to first_instantiated_index 2020-12-21 20:27:53 +01:00
notoria
2889631b61 Merge pull request #6 from triska/indexing
document first instantiated argument indexing
2020-12-21 20:02:22 +01:00
Markus Triska
5b9f0f45a9 document first instantiated argument indexing
Many thanks to @notoria for this brilliant idea and implementation!
2020-12-21 19:58:54 +01:00
notoria
ecc059bf8b Organized changes into find_optimal_index 2020-12-21 12:19:21 +01:00
Adrián Arroyo Calle
e1f4a50e65 HTTP Body 2020-12-21 00:15:00 +01:00
notoria
ec2c5a9b2c Simplified the code, removed first_arg, the Option 2020-12-19 21:04:17 +01:00
Markus Triska
4efbc20a4f ENHANCED: format_//2, format/[2,3], portray_clause/1 are now deterministic.
This works as soon as #732 is merged, since then maplist/N and foldl/N
are deterministic in the required cases.

This also resolves the extra choicepoint of time/1 (see #378).

Many thanks to @notoria for implementing better indexing in #732,
which allowed me to find this opportunity for improvement!
2020-12-19 18:18:14 +01:00
notoria
657e4f12bb Implemented a different way to index clauses 2020-12-19 14:52:38 +01:00
Mark Thom
22ffb1f53f Merge pull request #729 from brightly-salty/replace-dirs
Replace dirs with dirs-next
2020-12-15 10:04:46 -07:00
brightly-salty
4854da4805 Replace dirs with dirs-next 2020-12-15 06:59:26 -06:00
Adrián Arroyo Calle
33a0df20c3 Read body 2020-12-14 22:57:53 +01:00
Adrián Arroyo Calle
e4d1e4b7a0 Request and Response headers 2020-12-12 21:28:33 +01:00
Adrián Arroyo Calle
dbc193ab2a WIP http_server 2020-12-09 23:40:00 +01:00
Mark Thom
7bde174b40 Merge pull request #722 from aarroyoc/history-file
History file support (Rustyline 7.0) (#152)
2020-12-06 15:50:52 -07:00
Mark Thom
cf8014582b Merge pull request #720 from aarroyoc/max-min-list
Add list_max & list_min to lists module
2020-12-06 15:39:42 -07:00
Adrián Arroyo Calle
e2796dd351 History file support (Rustyline 7.0) 2020-12-06 23:16:25 +01:00
Adrián Arroyo Calle
0696a18e0b Change name to list_max, list_min as PR feedback 2020-12-06 21:58:37 +01:00
Adrián Arroyo Calle
0cc9388af0 Use foldl as PR feedback suggested 2020-12-06 17:31:42 +01:00
Adrián Arroyo Calle
a73529969a Add max_list & min_list to lists 2020-12-05 14:20:50 +01:00
Mark Thom
c55de17ca8 Merge pull request #710 from triska/clpz-reif
ADDED: Provisional support for (#=)/3 and (#<)/3.
2020-11-08 14:40:32 -07:00
Markus Triska
41a751e7f5 ADDED: Provisional support for (#=)/3 and (#<)/3.
These predicates are intended for constructs from library(reif), such
as if_/3. For the time being, they are defined here. If you have any
recommendations or comments regarding the best location of these
predicates, or need more of them, please file an issue.

Suggested by @Qqwy in https://github.com/triska/clpz/issues/12.

Many thanks!
2020-11-08 09:26:50 +01:00
Mark Thom
b9a53e441e support number/1 2020-11-03 23:38:55 -07:00
Mark Thom
add62c2093 Merge branch 'master' of https://github.com/mthom/rusty-wam into master 2020-10-22 00:24:30 -06:00
Mark Thom
de01ac233c Merge pull request #706 from malbarbo/fix-num
Fix build with num features
2020-10-20 21:43:03 -06:00
Mark Thom
c9e926f451 Merge branch 'master' of https://github.com/mthom/rusty-wam into master 2020-10-20 15:25:38 -06:00
Marco A L Barbosa
8d405849be Update num-rug-adapter to fix build with num feature. 2020-10-18 19:20:45 -03:00
Mark Thom
5fbdb1af9f Merge pull request #702 from triska/showterm
Add showterm.el and showterm.pl to the distribution
2020-10-03 12:20:02 -06:00
Markus Triska
83350f8866 use "r" interactive specifier 2020-10-03 17:13:17 +02:00
Markus Triska
b4ccd889af refer to new tools directory 2020-10-03 09:59:31 +02:00
Markus Triska
35a3f2dc91 ADDED: showterm.el and showterm.pl to draw terms as trees in Emacs 2020-10-03 09:56:26 +02:00
Mark Thom
ed7a4514de Merge pull request #700 from triska/development_environment
DOC: Development environment recommendations.
2020-10-01 13:41:52 -06:00
Markus Triska
2e295e354a DOC: Development environment recommendations. 2020-10-01 21:15:40 +02:00
Mark Thom
d71d9cc8ef Merge branch 'master' of https://github.com/mthom/rusty-wam into master 2020-09-14 10:46:11 -06:00
Mark Thom
108b62d839 Merge pull request #698 from triska/file_must_exist
ENHANCED: library(files): Error handling if requested files do not exist.
2020-09-13 10:30:50 -06:00
Markus Triska
46dfaa5b28 ENHANCED: library(files): Error handling if requested files do not exist.
We now throw exceptions instead of failing silently, or even crashing
when using file_size/2 etc. with nonexistent files.
2020-09-13 10:23:23 +02:00
Mark Thom
323e9c3eb3 refactor SystemClauseType::Open in anticipation of #614 2020-09-05 18:57:03 -06:00
Mark Thom
d677d3d2ef preface the 'spelling error' with an explainer comment (#687) 2020-09-05 18:07:58 -06:00
Mark Thom
c39b239d78 Merge pull request #696 from triska/format_distinct_variables
FIXED: format_//2: Keep different variables distinct.
2020-09-05 08:50:07 -06:00
Markus Triska
5f3ab823fd FIXED: format_//2: Keep different variables distinct.
Example:

        ?- phrase(format_("~w~w", [_,_]), Ls).
        %@    Ls = "AB"
        %@ ;  false.
2020-09-05 09:58:49 +02:00
Mark Thom
03d1da4bf2 Merge pull request #694 from triska/write_all
FIXED: reliably write all characters when using format/3
2020-09-02 12:52:53 -06:00
Markus Triska
78656d220b FIXED: reliably write all characters when using format/3
This addresses #693.

Many thanks to @notoria for a brilliant test case, and the
suggestion of this correction!
2020-09-02 19:29:04 +02:00
Mark Thom
7e7aa7992a Merge pull request #689 from matteoredaelli/patch-1
Update README.md
2020-08-25 12:27:13 -03:00
Matteo Redaelli
a53b4df8b6 Update README.md
wrong link to source file dcgs.pl
2020-08-25 17:19:45 +02:00
Mark Thom
3ba2b8ade6 Merge pull request #686 from triska/call_nth
ADDED: Preliminary version of call_nth/2.
2020-08-24 16:07:40 -03:00
Markus Triska
79cb4cd6a5 ADDED: First version of call_nth/2.
A thread-safe implementation is possible for example with
'$nb_setarg'/3 (see #390).
2020-08-24 19:29:08 +02:00
Mark Thom
d3ab4b5def flatten passage of cut point B in (,)/2, (->)/2 and (;)/2 (#685, #683) 2020-08-23 22:49:10 -03:00
Mark Thom
cdeb07520f Merge pull request #682 from triska/dif_improvements
Improvements to dif/2
2020-08-22 20:17:32 -03:00
Markus Triska
cc77ef680d dif/2: Omit entailed residual goals, if the arguments are not unifiable.
This addresses #135.
2020-08-22 17:50:15 +02:00
Markus Triska
e75ebd9b6e dif/2: Succeed unconditionally if the arguments are not unifiable.
This addresses one part of #135.
2020-08-22 17:50:15 +02:00
Mark Thom
62c9b8390b Merge pull request #677 from notoria/clpz_mod
Made mod stronger and addressed #675
2020-08-15 13:55:48 -03:00
notoria
0724c044d6 Use nonterminal fd_put//3 in mod from clpz 2020-08-15 17:56:07 +02:00
notoria
bed4afe74f Simplified the code 2020-08-15 17:39:34 +02:00
notoria
d4263cc8b9 Using queue_goal//1 to correctly bind variables 2020-08-15 17:06:12 +02:00
notoria
b4b11465a1 Fixed variable bindings in mod from clpz 2020-08-15 13:37:33 +02:00
notoria
daaebc59cb Made mod stronger and addressed #675 2020-08-15 12:53:28 +02:00
Mark Thom
79a74038ac Merge pull request #674 from notoria/clpz_mod
Improved mod in clpz
2020-08-14 00:21:19 -03:00
notoria
314baabf1d More improvement on mod from CLP(Z) 2020-08-13 21:36:36 +02:00
notoria
a24fbb8f61 Improved mod in CLP(Z)
This is still a work in progress.
2020-08-13 10:33:36 +02:00
Mark Thom
32eaab0783 Merge pull request #671 from triska/clpb_restriction
FIXED: CLP(B): Delay BDD restriction until after the instantiation.
2020-08-12 16:59:51 -03:00
Markus Triska
e185b626bd FIXED: CLP(B): Delay BDD restriction until after the instantiation.
This is necessary to actually take the new value into account.

Example:

    ?- sat(A*B>=C*D), A=1,B=0,C=1,D=1.
    false.

This addresses #670.
2020-08-12 19:51:25 +02:00
Mark Thom
74dc94f6bc Merge pull request #668 from triska/xbrl
library(sgml): Correctly parse XML leaf nodes that are not text nodes.
2020-08-11 19:53:02 -03:00
Markus Triska
099d9aaca6 library(sgml): Correctly parse XML leaf nodes that are not text nodes.
Example:

    ?- load_xml("<schemaRef type=\"simple\"/>", Node, []).
       Node = [element(schemaRef,[type="simple"],[])].

This is necessary for example to parse XBRL files. See #665.
2020-08-11 21:20:33 +02:00
Mark Thom
5fa3b9f2fb Merge pull request #667 from triska/clpb_taut
omit internal attributes in residual goals when using taut/2
2020-08-11 15:54:34 -03:00
Markus Triska
ad8e2ad4f6 omit internal attributes in residual goals when using taut/2
Examples:

    ?- taut(X=:=X,1).
       clpb:sat(X=:=X)
    ;  false.

    ?- taut(X=\=X,0).
       clpb:sat(X=:=X)
    ;  false.
2020-08-11 20:49:10 +02:00
Mark Thom
8203eff47b update prolog_parser version (#657) 2020-08-08 16:29:35 -06:00
Mark Thom
ad333047d9 Merge branch 'master' of https://github.com/mthom/scryer-prolog 2020-08-08 13:12:09 -06:00
Mark Thom
6e5d2d6a36 fix bugs in PausedPrologStream (#661) 2020-08-08 13:11:09 -06:00
Mark Thom
4b610b6293 Merge pull request #656 from notoria/try_from_number
Fixed some conversion issues
2020-08-08 12:24:29 -03:00
Mark Thom
2bd998e82f Merge pull request #660 from triska/shorter_crypto
Shorten and improve implementation of several cryptographic routines
2020-08-08 12:23:40 -03:00
Markus Triska
c55cc3c472 ensure proper lengths of key and initialization vector
This avoids crashes when using unsuitable lengths.
2020-08-06 23:27:49 +02:00
Markus Triska
674483a4c6 remove entailed constraint 2020-08-06 23:17:08 +02:00
Markus Triska
a16f84560d use self.deref(...) (see #653) 2020-08-06 20:12:57 +02:00
Markus Triska
1b4500339e use atom_argument_to_string 2020-08-06 20:12:23 +02:00
notoria
3143468751 Fixed some conversion issues 2020-08-06 11:39:55 +02:00
Mark Thom
f627b32355 fix crash in CryptoPasswordHash (#653), conversion of Rational integers in number_chars/2 (#655) 2020-08-06 00:17:55 -06:00
Mark Thom
10ba6fb773 Merge pull request #654 from triska/aad
ADDED: library(crypto): Support for additional authenticated data (AAD).
2020-08-06 02:40:51 -03:00
Markus Triska
2d3f1e51ec shorten, and increase readability 2020-08-05 21:49:46 +02:00
Markus Triska
1c23336cff use "octet" as a string literal, reducing the number of arguments 2020-08-05 20:21:46 +02:00
Markus Triska
a622ffddfe ADDED: library(crypto): Support for additional authenticated data (AAD).
Additional authenticated data can now be specified with the new
aad(Chars) option for encryption and decryption. It is authenticated,
but not encrypted.
2020-08-05 20:09:07 +02:00
Mark Thom
79cf0c63c4 update prolog_parser to 0.8.67 (#648) 2020-08-04 23:35:10 -06:00
Mark Thom
d57a592273 unify '[]' with proper [] in atom_chars/2 (#569) 2020-08-03 19:29:53 -06:00
Mark Thom
4f15802fbc reconcile '[]' and [] (#644), solve remaining conformity issues (#648), move (-->) from dcgs.pl to builtins.pl (#649) 2020-08-02 23:35:53 -06:00
Mark Thom
357ae7a8aa Merge pull request #647 from notoria/newline
Improved readline
2020-07-31 18:19:12 -03:00
notoria
ac00ae4daf Improved readline 2020-07-31 21:16:33 +02:00
Mark Thom
249f034a38 Merge pull request #645 from triska/curve25519
ADDED: library(crypto): ECDH key exchange over Curve25519 (X25519)
2020-07-30 10:29:20 -03:00
Markus Triska
32c612b747 ADDED: library(crypto): ECDH key exchange over Curve25519 (X25519) 2020-07-29 23:51:11 +02:00
Mark Thom
386f7cf65d the indexer now categorizes partial strings only as lists (#623, #642) 2020-07-27 16:43:43 -06:00
Mark Thom
8dc07882b6 try to build strings from list as potential matches in SwitchOnConstant (#623) 2020-07-26 19:16:05 -06:00
Mark Thom
320802633f Merge pull request #639 from triska/flush_output
ENHANCED: flush output for impure I/O (format/[2,3], portray_clause/1 etc.)
2020-07-25 13:20:45 -03:00
Markus Triska
dcc4b91b35 ENHANCED: flush output for impure I/O (format/[2,3], portray_clause/1 etc.)
Without this, we do not get a (timely) prompt in cases like:

    ?- format("press a key: ", []), get_single_char(C).

whereas for example write/1 already works in such cases:

    ?- write('press a key: '), get_single_char(C).
    press a key:
2020-07-25 09:49:02 +02:00
Mark Thom
e7c27ed999 Merge pull request #638 from triska/base64
ADDED: chars_base64/3 for efficient bidirectional Base64 conversion.
2020-07-23 19:49:12 -03:00
Markus Triska
928c9c9aad throw an error if a character cannot be encoded 2020-07-23 00:35:23 +02:00
Markus Triska
4a90f13dee better error handling for options 2020-07-23 00:27:31 +02:00
Markus Triska
61dddcee3f use newly available chars_base64/3 2020-07-22 20:35:48 +02:00
Markus Triska
4c510001ce ADDED: chars_base64/3 for efficient bidirectional Base64 conversion. 2020-07-22 20:35:48 +02:00
Mark Thom
82dfa690d2 Merge pull request #636 from triska/path_segments
ADDED: path_segments/2 for portable reasoning about path components.
2020-07-19 13:16:27 -03:00
Markus Triska
2768beec4c ADDED: path_segments/2 for portable reasoning about path components.
The platform-specific directory separator is taken into account.
The clean representation of segments as a list simplifies reasoning.
2020-07-19 12:32:01 +02:00
Mark Thom
bd0aa16322 Merge pull request #633 from triska/file_time
ADDED: file_creation_time/2 and file_access_time/2.
2020-07-18 12:40:53 -03:00
Mark Thom
a1746a5bf1 Merge pull request #631 from cduret/lib_csv
add write_csv & change skip_header to with_header option
2020-07-18 12:40:28 -03:00
Mark Thom
fb502fb66b Merge pull request #635 from triska/more_links
DOC: add more links to libraries, and include iso_ext in the enumeration
2020-07-18 12:40:09 -03:00
Markus Triska
c9a69263b6 DOC: add more links to libraries, and include iso_ext in the enumeration 2020-07-18 08:12:26 +02:00
Mark Thom
f0c8056334 look up operator precedence when arity does not match what the atom records, don't print brackets around outermost '+' (#629) 2020-07-17 11:02:58 -06:00
Markus Triska
17dd9239b6 ADDED: file_creation_time/2 and file_access_time/2.
The code can be simplified once if- and while-let-chains are available:

    https://github.com/rust-lang/rust/issues/53667
2020-07-17 18:48:39 +02:00
Mark Thom
6c691d9223 Merge pull request #632 from triska/path_canonical
ADDED: path_canonical/2, obtaining the canonical absolute path.
2020-07-16 21:03:13 -03:00
Cyrille Duret
920644afaf set domain_error exception & use format/3 with setup_call_cleanup/3 for stream writing 2020-07-17 01:21:40 +02:00
Markus Triska
8668aee7fd ENHANCED: Reorder fields in time stamps so that (@<)/2 is meaningful. 2020-07-16 23:40:03 +02:00
Markus Triska
4f386b3e5e ADDED: file_modification_time/2, obtaining a time stamp.
This addresses a remaining aspect of #511.
2020-07-16 23:40:03 +02:00
Markus Triska
0f4667d942 ADDED: path_canonical/2, obtaining the canonical absolute path.
This addresses a remaining aspect of #511.
2020-07-15 20:43:34 +02:00
Cyrille Duret
5705c42820 set throw() to follow ISO recommendations 2020-07-15 18:26:57 +02:00
794404
f5f0d00a75 add write_csv & change skip_header to with_header option 2020-07-15 13:02:30 +02:00
Mark Thom
ec75e21898 Merge pull request #630 from triska/delete_file
ADDED: delete_file/1, addressing a remaining aspect of #511
2020-07-14 20:26:51 -03:00
Markus Triska
94e9e17c79 ADDED: working_directory/2, addressing a remaining aspect of #511 2020-07-14 23:05:29 +02:00
Markus Triska
5134e64cae ADDED: delete_file/1, addressing a remaining aspect of #511 2020-07-14 22:21:46 +02:00
Mark Thom
53d39ccb4b Merge pull request #626 from triska/compact_crypto
ENHANCED: library(crypto): Retain the compact representation of strings.
2020-07-12 12:17:58 -03:00
Markus Triska
b227e160f0 mention symmetric encryption and ChaCha20-Poly1305 2020-07-12 11:59:34 +02:00
Markus Triska
88a2b82f7e ENHANCED: library(crypto): Retain the compact representation of strings.
This avoids the costly (in terms of space requirements!) conversion of
compact lists of characters to lists of integers, making hashing, HKDF,
encryption, decryption, signing and signature verification an order of
magnitude more efficient (primarily in terms of space, also in time).

This makes library(crypto) suitable to process also very large files.
2020-07-12 11:54:37 +02:00
Mark Thom
0016365446 Merge pull request #625 from triska/os
ADDED: library(os), reasoning about environment variables.
2020-07-11 23:50:01 -03:00
Markus Triska
0fa8feb475 use newly available character type in must_be/2 2020-07-11 21:00:25 +02:00
Markus Triska
eb872f8fca ADDED: library(os), reasoning about environment variables.
Together with library(files), this addresses #511.
2020-07-11 21:00:25 +02:00
Mark Thom
5fd78320dd slight update to the BiMetaTrans README 2020-07-10 15:22:15 -06:00
Mark Thom
3195042c7d update bimetatrans tests 2020-07-10 10:26:48 -06:00
Mark Thom
b0234fa8b7 add bimetatrans README 2020-07-09 20:53:13 -06:00
Mark Thom
2a629fee37 Merge pull request #621 from notoria/tabling
Tabling
2020-07-03 14:40:52 -03:00
notoria
bf13700a95 Enhanced the implementation
The restriction has been removed by using read_library_path from
cfd15f4e02.
2020-07-01 13:14:12 +02:00
notoria
83268f5ff6 Allowing paths in use_module(library(..)) (#604)
In a module, the path is restricted in being an atom.
2020-07-01 10:22:17 +02:00
notoria
1eb42654e0 Revert "allow paths in use_module(library(..)) (#604)"
This reverts commit cfd15f4e02.
2020-07-01 10:07:26 +02:00
notoria
e05c34d1ee Revert "build.rs recursively delves into directories during library bake stage, correct misnamed file at machine/mod.rs:455 (#617)"
This reverts commit c1b577f2c7.
2020-07-01 10:06:20 +02:00
Mark Thom
8325bb11e2 Merge pull request #606 from triska/files
ADDED: library(files), for reasoning about files and directories.
2020-06-30 13:34:39 -03:00
Mark Thom
c1b577f2c7 build.rs recursively delves into directories during library bake stage, correct misnamed file at machine/mod.rs:455 (#617) 2020-06-29 00:58:10 -06:00
Mark Thom
29a61db275 Merge pull request #613 from cduret/csv_lib
add csv library
2020-06-27 16:28:10 -03:00
Cyrille Duret
e2c61485a1 add csv library 2020-06-25 21:13:29 +02:00
Markus Triska
65f200e02c directory_files/2: throw representation error if into_string() fails
Suggested by @notoria in #606. Many thanks!
2020-06-24 23:30:40 +02:00
Markus Triska
aa9432e24f comment on indexing, addressing a comment by @pmoura in #606
If indexing is needed for lists of characters, it should be added to
the engine.
2020-06-24 22:29:34 +02:00
Markus Triska
30d1585468 ADDED: library(files), for reasoning about files and directories. 2020-06-24 22:29:34 +02:00
Mark Thom
376c5263f0 Merge pull request #608 from triska/xml
ADDED: load_xml/3 to load XML files from streams, files and strings.
2020-06-22 12:21:35 -03:00
Markus Triska
bcd6d4e8a7 library(format) is not used in this example 2020-06-21 09:46:05 +02:00
Markus Triska
32eb896c56 ADDED: load_xml/3 to load XML files from streams, files and strings. 2020-06-21 09:45:37 +02:00
Mark Thom
4603163e88 Merge pull request #607 from triska/xpath
ADDED: library(xpath) for convenient analysis of HTML and XML documents.
2020-06-20 14:46:52 -03:00
Markus Triska
b3c9fb2fea import member/2 from library(lists)
This is needed when processing redirects.
2020-06-20 19:07:43 +02:00
Markus Triska
d66ea07ba4 ADDED: library(xpath) for convenient analysis of HTML and XML documents. 2020-06-20 18:08:11 +02:00
Mark Thom
496279c92d Merge pull request #596 from triska/sgml
ADDED: library(sgml), providing load_html/3 to parse HTML documents
2020-06-19 12:59:06 -03:00
Mark Thom
e0ae925ca3 Merge pull request #605 from notoria/debug
Added library(debug)
2020-06-19 12:58:26 -03:00
notoria
1799c19933 Added an entry in README 2020-06-19 17:48:55 +02:00
notoria
94eddd3b42 Added library(debug) 2020-06-19 17:18:59 +02:00
Markus Triska
79829cb6d2 ADDED: library(sgml), providing load_html/3 to parse HTML documents 2020-06-19 16:40:01 +02:00
Mark Thom
0d20ca0bf2 Merge pull request #592 from triska/http_open
ADDED: library(http/http_open), opening HTTP and HTTPS streams for reading
2020-06-19 11:30:23 -03:00
Mark Thom
3ede5ea28f Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-06-19 00:58:43 -06:00
Mark Thom
cfd15f4e02 allow paths in use_module(library(..)) (#604) 2020-06-19 00:57:56 -06:00
Mark Thom
1f9ff2dd88 clarify meaning of AssertItems and QueryItems in BiMetaTrans source-level documentation 2020-06-18 23:47:54 -06:00
Mark Thom
7740a39a4d Merge pull request #602 from triska/put_char
put_char/[1,2]: throw type errors for atoms that are not characters
2020-06-18 12:16:47 -03:00
Markus Triska
5eda2dfe0e put_char/[1,2]: throw type errors for atoms that are not characters
Example:

    ?- put_char(hello).
    %@ caught: error(type_error(character,hello),put_char/2)
2020-06-18 00:15:00 +02:00
Mark Thom
1509c6a471 Merge pull request #597 from triska/faster_format
ENHANCED: much faster format/3 for text streams
2020-06-17 18:48:49 -03:00
Markus Triska
19acd608a5 ADDED: library(http/http_open), opening HTTP and HTTPS streams for reading
Example:

   ?- http_open("https://github.com/mthom/scryer-prolog", Stream, []),
      length(Ls, 10),
      maplist(get_char(Stream), Ls).

Yielding:

   %@    Stream = '$stream'(0x7fba4c59ef10), Ls = "\n\n\n\n\n<!DOC"
   %@ ;  false.
2020-06-17 20:10:22 +02:00
Mark Thom
bc81ff28d8 cover Addr::Fixnum when not referenced as an Addr::Con, undefine dynamic predicate definitions from within modules too (#588) 2020-06-16 19:03:07 -06:00
Mark Thom
ba867e95c3 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-06-16 18:47:53 -06:00
Mark Thom
53f99ca434 fix copy_partial_string bug (#572) 2020-06-16 18:47:39 -06:00
Markus Triska
8958ea036e ENHANCED: much faster format/3 for text streams
This is now also used by format/2 to emit output on the terminal,
and significantly speeds up toplevel output of long strings.
2020-06-16 21:13:03 +02:00
Mark Thom
d3563c30e7 Merge pull request #590 from triska/read_line_to_chars
ADDED: read_line_to_chars/3, reading up to and including "\n" from a stream
2020-06-16 00:06:01 -03:00
Mark Thom
0f017339b7 change the plurality of AssertItem(s), QueryItem(s) in bimetatrans.pl 2020-06-15 16:38:27 -06:00
Mark Thom
4d3a3d806f Merge pull request #595 from triska/faster_format
ENHANCED: faster format/3 when writing to binary streams
2020-06-14 12:37:06 -03:00
Markus Triska
10a0f708b9 use format/3 to benefit from efficiency improvements 2020-06-14 10:51:24 +02:00
Markus Triska
409d3c168d ENHANCED: faster format/3 when writing to binary streams
This is also more secure, since it does not change the atom table
and therefore leaves little trace of what was processed.
2020-06-14 10:43:25 +02:00
Mark Thom
005220b6d4 Merge pull request #589 from triska/lazy_pio
ENHANCED: Read more characters at once, significantly speeding up library(pio)
2020-06-13 21:09:19 -03:00
Mark Thom
8462b039d3 Merge pull request #593 from triska/portray_clause
ADDED: portray_clause/2, writing to a specified stream.
2020-06-13 21:08:54 -03:00
Mark Thom
2e3cabce6b Merge pull request #594 from triska/scryerrc
explain .scryerrc configuration file
2020-06-13 17:55:32 -03:00
Mark Thom
2c6b615b8c fix broken links in README (#591) 2020-06-13 10:46:53 -06:00
Markus Triska
f910e013a6 use take() for extra fast processing of binary files
Suggested by @notoria in #589. Many thanks!
2020-06-13 16:47:46 +02:00
Markus Triska
5f1b03261c better naming: str --> string
Suggested by @notoria in #589. Many thanks!
2020-06-13 16:47:46 +02:00
Markus Triska
862197fc96 explain .scryerrc configuration file 2020-06-13 16:47:02 +02:00
Markus Triska
44ebd894d5 ADDED: portray_clause/2, writing to a specified stream. 2020-06-13 16:45:47 +02:00
Markus Triska
b85ff7db36 ENHANCED: Read more characters at once, significantly speeding up library(pio). 2020-06-13 16:25:41 +02:00
Markus Triska
22fd216de5 ADDED: read_line_to_chars/3, reading up to and including "\n" from a stream, yielding a list difference.
This works for both binary and text streams.
2020-06-13 09:14:44 +02:00
Mark Thom
2f02c428cc rename bimetatrans example files and directory 2020-06-12 18:32:48 -06:00
Mark Thom
1732413ee1 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-06-12 18:28:07 -06:00
Mark Thom
33325f1574 remove vestigial prolog/ directory (#444) 2020-06-12 18:26:38 -06:00
Mark Thom
4f288c6fd9 Merge pull request #587 from triska/lazy_pio
ENHANCED: phrase_from_file/[2,3] now read the file lazily.
2020-06-12 11:24:52 -03:00
Mark Thom
5ffb4597b3 add missing ! in int_pow (#580) 2020-06-11 22:28:05 -06:00
Mark Thom
d9de38b935 adjust the tail created by CreatePartialString to point to the last cell it pushed to the heap (#586) 2020-06-11 21:25:20 -06:00
Mark Thom
ad459b83c0 credit Ulrich Neumerkel for partial strings in README 2020-06-11 21:24:20 -06:00
Markus Triska
d96ea6b815 include library(pio) and library(charsio) in the itemization 2020-06-12 00:02:41 +02:00
Markus Triska
40c3e31955 ENHANCED: phrase_from_file/[2,3] now read the file lazily.
This allows processing extremely large files. In addition, the
efficient string representation helps to reduce memory usage.

These features unleash the full power of Prolog for text processing,
the exact use case Prolog was designed for.
2020-06-12 00:02:41 +02:00
Mark Thom
f84c831cda remove most cuts from bimetatrans_ruleml.pl 2020-06-11 12:22:35 -06:00
Mark Thom
904cf97f7d fix printing of atoms like '.x' (#585) 2020-06-11 11:11:04 -06:00
Mark Thom
791e22dc24 eliminate unnecessary find_min_time_ predicate in least_time.pl example 2020-06-10 22:25:23 -06:00
Mark Thom
c82a0b3d2d Merge pull request #582 from triska/format_time
ADDED: format_time//2 for describing strings involving dates and times
2020-06-10 20:21:44 -03:00
Markus Triska
2fc887724b ADDED: format_time//2 for describing strings involving dates and times
current_time/1 yields the current system time as an opaque time stamp.
2020-06-11 00:55:38 +02:00
Mark Thom
e16762a137 scryer and prolog_parser version bump (#581) 2020-06-10 14:29:22 -06:00
Mark Thom
4a07ffac69 prolog_parser version bump, scryer version bump 2020-06-09 22:15:11 -06:00
Mark Thom
9331c4ce9e Merge pull request #579 from triska/master
ENHANCED: throw Prolog exception on expired certificates
2020-06-09 18:56:29 -03:00
Markus Triska
184ef94e7e ENHANCED: throw Prolog exception on expired certificates
Example:

    ?- socket_client_open('expired.badssl.com':443, S, [tls(true)]).
    caught: error(permission_error(open,source_sink,'expired.badssl.com'),socket_client_open/3)
2020-06-09 23:01:28 +02:00
Mark Thom
d67740d44d Merge pull request #577 from triska/tls
ADDED: Encrypted client connections in library(sockets) via new option tls/1.
2020-06-09 10:56:17 -03:00
Markus Triska
7b30daf88d ADDED: Encrypted client connections in library(sockets) via new option tls/1.
Use tls(true) to negotiate an encrypted network connection via TLS.
2020-06-09 07:43:29 +02:00
Markus Triska
d5e76da475 ADDED: type checks for type boolean 2020-06-08 00:41:47 +02:00
Mark Thom
5d91c1af81 Merge pull request #578 from triska/format_improvements
Various improvements for format_//2
2020-06-07 19:36:16 -03:00
Mark Thom
bb013e78d2 Merge pull request #576 from triska/crypto_curve_scalar_mult
ENHANCED: Faster Rust-based crypto_curve_scalar_mult/4 using OpenSSL.
2020-06-07 19:34:12 -03:00
Markus Triska
c22816de08 type check for ~d and ~D specifiers 2020-06-08 00:09:55 +02:00
Markus Triska
9bb851d437 ENHANCED: format_//2 now evaluates arithmetic expressions for ~d, ~D, ~f, ~r and ~R
Example:

    ?- phrase(format_("~d", [1+2]), Cs).
       Cs = "3"
    ;  false.
2020-06-08 00:09:55 +02:00
Markus Triska
c8a9f8dcf5 better domain error in format_//2
Example:

    ?- format("", [hello]).
    caught: error(domain_error(empty_list,[hello]),format_//2)
2020-06-07 23:59:37 +02:00
Markus Triska
e8653ffe32 add type check for scalar argument in crypto_curve_scalar_mult/4
Suggested by @notoria in #576. Many thanks!
2020-06-07 18:51:13 +02:00
Markus Triska
5499df418a use secp256k1 in the example 2020-06-07 11:04:24 +02:00
Markus Triska
b586618411 ENHANCED: Faster Rust-based crypto_curve_scalar_mult/4 using OpenSSL. 2020-06-07 11:04:24 +02:00
Mark Thom
3b5730be54 s/prolog-implementation/prolog-system (cargo does not allow keywords to exceed 20 characters) 2020-06-04 23:29:48 -06:00
Mark Thom
961c4a1b9c bump version number 2020-06-04 23:25:21 -06:00
Mark Thom
72c5908f09 update prolog_parser version, correct atom_chars/2 (#379) 2020-06-04 23:23:49 -06:00
Mark Thom
2a588ff943 remove double_quotes checks from unnecessary places in system_calls.rs and elsewhere (#513, #542) 2020-06-04 21:03:41 -06:00
Mark Thom
24f11b5ae9 remove unreachable branch in increment_s_ptr (#575) 2020-06-03 22:05:46 -06:00
Mark Thom
1d44d99875 correct unexpected bad variables while ensuring better names are generated in order (#571) 2020-05-30 00:17:34 -06:00
Mark Thom
eebcdfb24f Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-27 11:11:37 -06:00
Mark Thom
526b56a650 order generation of variable names in attributed goals after toplevel variables (#465) 2020-05-27 11:11:26 -06:00
Mark Thom
20675f56e6 plug missing variables in equations (#488), exclude/3 regression (#452), incorrect answer substitution (#326) 2020-05-26 23:46:09 -06:00
Mark Thom
e5fbdeb3a9 Merge pull request #568 from triska/logo
How about an actual raven for a logo?
2020-05-27 00:19:41 -03:00
Markus Triska
7cc92ec1c1 ADDED: Logo.
Motivation and considerations:

- A Scryer certainly needs a raven. Ravens are among the most
  intelligent animals on earth.
- When writing Prolog programs, a few strokes are often enough.
- Purple is the colour most associated with magic and royalty,
  indicating the almost surreal and pure features provided by Scryer,
  in the lineage of Marseille Prolog.
- The shape of the silhouette looks a bit like an "S" for "Scryer".
- A square which surrounds the image is traditionally associated with
  finishing a proof (square, Latin "quadratum", has the same starting
  letters as "quod erat demonstrandum").

I call this raven: *Cryer*. Cryer is an archaic spelling for "crier",
an officer who makes public announcements in a court of justice.
The Prolog top-level likewise makes announcements about what is true.
2020-05-26 22:13:51 +02:00
Mark Thom
fe0bf6f2ef Merge pull request #524 from matt2xu/decode_utf8
Add decoding of UTF-8 sequences
2020-05-26 13:42:42 -03:00
Mark Thom
828c19d478 Merge pull request #563 from triska/master
ADDED: halt/1, halting with specified exit code
2020-05-23 16:02:05 -03:00
Mark Thom
15db77558c throw evaluable type error for unrecognized atoms in arith_eval_by_metacall 2020-05-23 13:33:05 -06:00
Mark Thom
18cf4a3bd3 Add type errors where appropriate in arith_eval_by_metacall (#392) 2020-05-23 13:07:40 -06:00
Mark Thom
e0378acfc2 throw type_error(number, E) in arith_eval_by_metacall (#392) 2020-05-23 12:39:29 -06:00
Mark Thom
53cb2af202 (**)/2 should always evaluate to floating point (#558) 2020-05-23 12:24:00 -06:00
Mark Thom
f74005a166 update (^)/2 in light of Draft Technical Corrigendum 3 (#559) 2020-05-23 12:13:48 -06:00
Markus Triska
b0df981b01 ADDED: halt/1, halting with specified exit code 2020-05-23 19:28:17 +02:00
Mark Thom
888d844ec5 address arity discrepancies in builtins.n and call/N (#525) 2020-05-22 23:57:32 -06:00
Mark Thom
3a7eb9fc0c Merge pull request #560 from notoria/cargo
Added some informations in Cargo.toml
2020-05-22 23:43:06 -03:00
Mark Thom
46276aacd9 Merge pull request #557 from mthom/docker
Merge Master with Docker branch
2020-05-22 23:42:55 -03:00
notoria
3a7a29868f Added some informations in Cargo.toml 2020-05-23 02:03:24 +02:00
Mark Thom
0060c8988a use -1 as eof_code for binary streams (#555) 2020-05-22 15:58:07 -06:00
Mark Thom
458f482c2a Merge pull request #556 from panasenco/docker
Valid Dockerfile and README
2020-05-22 18:54:10 -03:00
Matthieu Wipliez
cc9d6587a3 Implementation of UTF-8 bytes to chars 2020-05-22 23:33:12 +02:00
panasenco
dadf1b6ff9 Phrasing 2020-05-22 14:01:10 -07:00
panasenco
db56bb92f0 Fixed link in Dockerfile documentation. Updated README with working Docker link. Made general improvements to the Docker section of the README now that I'm not in a sleep-deprived zombie state. 2020-05-22 13:57:48 -07:00
panasenco
e82e36f51e Revert "Revert "Created and Tested Dockerfile""
This reverts commit e00d864199.
2020-05-22 13:57:48 -07:00
Mark Thom
f9aad34d28 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-22 14:50:51 -06:00
Mark Thom
b2d720b853 correct get_byte/1 not emitted -1 upon discovery of end_of_stream position (#555) 2020-05-22 14:50:40 -06:00
Mark Thom
c10c05e924 Merge pull request #552 from mthom/master
Scryer Dockerfile
2020-05-22 16:04:03 -03:00
Mark Thom
8909246725 Merge pull request #554 from triska/format3
ADDED: format/3, writing formatted output to a stream
2020-05-22 14:32:44 -03:00
Markus Triska
48c0b0ab3c ENHANCED: Faster format/3 for binary streams.
This speeds up web servers considerably when sending binary files.
2020-05-22 17:30:29 +02:00
Markus Triska
cbbed310c5 ADDED: format/3, writing formatted output to a stream
Both binary and text streams are supported.
2020-05-22 17:23:51 +02:00
Mark Thom
9e9c3b6342 modify int_pow so that a power of -1 is valid (#548) 2020-05-21 22:33:22 -06:00
Mark Thom
fefd262c2e Merge pull request #545 from triska/ed25519
ADDED: Public key signatures and signature verification with Ed25519
2020-05-21 11:30:47 -03:00
Markus Triska
1108c99688 document that lists of integers can be specified if encoding(octet) is used
This seems to be a good compromise: The API now strongly encourages
lists of characters, which are ideally suited to represent text, and
also allows lists of bytes if the encoding(octet) option is used.
2020-05-21 13:41:57 +02:00
Markus Triska
93da831ef0 correct mode indication for ed25519_verify/4
Noted by @notoria in #545. Many thanks!
2020-05-21 01:27:06 +02:00
Markus Triska
72bf45912e "PrivateKey" --> "KeyPair" 2020-05-21 00:14:15 +02:00
Markus Triska
5d94276c4f remove several mentions of list of bytes in predicate descriptions
This is to focus more on the intended core representation of text: In
Prolog, text is ideally represented as a list of characters, and this
is what we want to encourage, especially given Scryer's compact
representation for strings.

For the time being, library(crypto) still also supports lists of bytes in
several predicates. This will likely be removed at some point in the
future. Please use lists of characters to make your code future-proof.
2020-05-21 00:07:42 +02:00
Markus Triska
b43f27030e require PKCS#8 v2 format for better security
Notably, this format requires that the public key also be present.
This format is what ed25519_new_keypair/1 generates, and it is
strongly encouraged for higher security.
2020-05-20 23:55:21 +02:00
Markus Triska
2845f55157 ADDED: ed25519_keypair_public_key/2, relating a key pair to its public key 2020-05-20 23:43:28 +02:00
Markus Triska
e254d84710 clarify format of public key 2020-05-20 23:17:42 +02:00
Markus Triska
0ef7b5488d ADDED: ed25519_new_keypair/1 to dynamically create a new Ed25519 key pair 2020-05-20 23:11:56 +02:00
Markus Triska
56b04f8df8 use LessSafeKey to simplify the implementation of authenticated encryption
The nonce is explicitly specified, and the application programmer
must (and always had to) ensure that it is unique for a given key.
2020-05-20 22:32:39 +02:00
Markus Triska
2d036c6b25 "codes" --> "bytes"
Support for lists of bytes may be dropped from library(crypto). Use
lists of characters to make your code future-proof. Lists of
characters will always be supported due to their compactness,
and because Prolog applications should move towards characters.
2020-05-20 22:32:39 +02:00
Markus Triska
ad3be5c848 ADDED: Public key signatures and signature verification with Ed25519 2020-05-20 22:32:39 +02:00
Mark Thom
a0544cc345 revert printing of characters 2020-05-19 17:34:55 -06:00
Mark Thom
3245261162 Merge pull request #537 from triska/master
import member/2 from library(lists)
2020-05-19 15:16:38 -03:00
Markus Triska
21b7348c6a import member/2 from library(lists) 2020-05-19 20:13:06 +02:00
Mark Thom
54d4dc6e1e Merge pull request #536 from mthom/revert-517-docker
Revert "Created and Tested Dockerfile"
2020-05-19 15:00:29 -03:00
Mark Thom
e00d864199 Revert "Created and Tested Dockerfile" 2020-05-19 11:59:06 -06:00
Mark Thom
ad6fc89a24 Merge pull request #533 from triska/master
ADDED: Support for SHA-3 algorithms in crypto_data_hash/3
2020-05-19 14:23:12 -03:00
Mark Thom
fc4823ce8e Merge pull request #534 from triska/phrase_from_file_3
ADDED: phrase_from_file/3 in library(pio)
2020-05-19 14:22:59 -03:00
Markus Triska
0bd9831eec ADDED: phrase_from_file/3 in library(pio)
This allows us to specify type(binary), and read from binary files
with DCGs.
2020-05-19 18:52:18 +02:00
Markus Triska
dec2ef70c7 better error handling in crypto_data_hkdf/4
Noted by @notoria in #533. Many thanks!
2020-05-19 18:22:25 +02:00
Markus Triska
d19d8ea770 crypto_data_hkdf/4: do not crash for length > usize::max_value()
For now, we fail silently in such cases.

Noted by @notoria in #533. Many thanks!
2020-05-19 17:31:36 +02:00
Markus Triska
34f7752c0f use more fixnums in cryptographic routines 2020-05-19 17:03:02 +02:00
Markus Triska
da4d061067 correct option processing in crypto_data_decrypt/6 2020-05-19 17:02:33 +02:00
Markus Triska
85155439be use Fixnums for bytes in hashing.
Suggested by @notoria in #533. Many thanks!
2020-05-19 16:43:44 +02:00
Markus Triska
dd32e69061 use matching to select the hashing algorithm
Suggested by @notoria in #533. Many thanks!
2020-05-19 16:37:50 +02:00
Mark Thom
1c0c660c32 Merge pull request #517 from panasenco/docker
Created and Tested Dockerfile
2020-05-19 11:30:08 -03:00
Mark Thom
2eac902c33 correct fixnum overflow on negation (#528) 2020-05-19 10:45:54 -06:00
Markus Triska
9f5322d309 ADDED: Support for BLAKE2 algorithms in crypto_data_hash/3. 2020-05-19 12:44:10 +02:00
Markus Triska
f5c2f6f9e9 ADDED: Support for SHA-3 algorithms in crypto_data_hash/3 2020-05-19 12:10:46 +02:00
Mark Thom
e0e3b180e7 accomodate \0\ in partial strings, print null as \0\ (#267, #526), update prolog parser, version bump 2020-05-18 23:45:34 -06:00
Mark Thom
041dc039d4 add Addr::Lis as case in PartialStringTail (#530) 2020-05-18 12:27:26 -06:00
Mark Thom
ac755b0ada Merge pull request #527 from triska/master
Add type checks to increase robustness of library(crypto)
2020-05-18 11:18:58 -03:00
Mark Thom
c14259060c fix partial_string_tail panic (#530) 2020-05-18 11:14:27 -06:00
Markus Triska
a423eb5323 stronger validation of input lists for cryptographic routines
Example:

    ?- crypto_data_hkdf(Var, 32, Bs, []).
    caught: error(instantiation_error,must_be/2)

Reported by @notoria in #527. Many thanks!
2020-05-18 13:28:52 +02:00
Markus Triska
fd732550d8 crypto_data_hkdf/4: Fail if the length is too long.
Due to the way the counter is constructed in the HKDF specification,
the requested output length can be at most 255 times the size of the
digest algorithm's output.

Reported by @notoria in #527. Many thanks!
2020-05-18 13:21:38 +02:00
Markus Triska
fac7ba70c8 crypto_password_hash/3: fail if the number of iterations is too high
Discussed in #527.
2020-05-18 13:11:39 +02:00
Markus Triska
e9f8b35918 centralize reasoning about encoding 2020-05-18 11:29:35 +02:00
Markus Triska
23034dd4f5 raise instantiation errors for variable encoding
Reported by notoria in #527.

Note that from a declarative perspective, it would indeed be valid to
give answers for both available encodings.
2020-05-18 11:21:38 +02:00
Markus Triska
70ad44adfd type check for length argument in crypto_data_hkdf/4
Reported by @notoria in #527.
2020-05-18 11:12:59 +02:00
Markus Triska
95ca8a2630 throw instantiation error if the list of options contains a variable (#523)
Many thanks to @notoria for the test case!
2020-05-18 09:58:09 +02:00
panasenco
a7fd3c50e5 Updated README with Docker install instructions 2020-05-17 20:50:34 -07:00
panasenco
5639f40994 Managed to create a 77MB scryer-prolog Docker image 2020-05-17 20:50:34 -07:00
panasenco
33a8262334 New Dockerfile based on Fredrik Park's blog post - compilation fails with linker error 2020-05-17 20:50:34 -07:00
panasenco
9b4f7ad696 Created initial working Docker deployment 2020-05-17 20:50:33 -07:00
Mark Thom
eecd809d9a fix atom_codes (#521) 2020-05-17 16:34:52 -06:00
Mark Thom
240d3cb8f1 add Cargo.lock 2020-05-17 15:20:02 -06:00
Mark Thom
5720b7b94c eliminate Addr::CharCode (#519), fix atom_code panic (#521), fix failure involving lists of character codes (#520), bump version number 2020-05-17 15:19:27 -06:00
Markus Triska
3855d7ea02 type test for salt in crypto_password_hash/3 2020-05-17 22:01:22 +02:00
Mark Thom
9f3351469d solve overflow of left arithmetic shift (#518) 2020-05-17 12:20:22 -06:00
Mark Thom
2c4a0cf65b update Cargo.lock 2020-05-16 23:13:57 -06:00
Mark Thom
a8f360c8fd scryer version bump 2020-05-16 23:12:43 -06:00
Mark Thom
b3cff6555f propagate syntax errors from read_term when they're not UnexpectedEOF (#507) 2020-05-16 23:10:38 -06:00
Mark Thom
69706ecab0 fix sign error in shl on fixnum with n >= 63 (#499) 2020-05-16 23:00:37 -06:00
Mark Thom
05d3b97eae correct for second argument when (is)/2 is a tail call 2020-05-16 20:22:17 -06:00
Mark Thom
a38fb41e37 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-16 14:51:19 -06:00
Mark Thom
204ae377e3 restore old clpz.pl 2020-05-16 14:51:04 -06:00
notoria
3e5a652874 Enhanced Travis-CI (#512) 2020-05-16 11:51:53 -06:00
Markus Triska
862ae26631 Authenticated encryption in library(crypto), new encoding/1 option for hashes (#515)
* shorten n_newlines//1

* remove unneeded variable

* ADDED: authenticated encryption and decryption with ChaCha20-Poly1305

* ADDED: encoding/1 option for crypto_data_hash/3 and crypto_data_hkdf/4
2020-05-16 11:51:41 -06:00
Mark Thom
9e0208ec71 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-15 22:51:24 -06:00
Mark Thom
510530973c correct mishandled Addr::CharCode case in eq_test (#505) 2020-05-15 22:51:18 -06:00
Markus Triska
4e69301562 ADDED: Password-based key derivation (PBKDF2) (#509)
The new predicates crypto_password_hash/[2,3] let you store
passwords safely, and easily verify passwords later.
2020-05-15 10:49:23 -06:00
Matthieu Wipliez
df06d4b9a2 Work in progress: add char_utf8bytes (#493)
* Add char_utf8bytes to library

* Improved implementation

* Improve code + add chars predicate

* Update example

* Renamed string_utf8bytes to char_utf8bytes
2020-05-15 10:05:04 -06:00
Mark Thom
b60561c3bb fix existence_error in atom_chars/2, atom_codes/2 (#504, #506) 2020-05-14 22:33:28 -06:00
Mark Thom
fffa503968 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-14 22:25:50 -06:00
Mark Thom
8754157810 implement (div)/2 properly (#435) 2020-05-14 22:25:39 -06:00
Mark Thom
0763686892 Merge pull request #502 from triska/hkdf
ADDED: HMAC-based key derivation (HKDF) via crypto_data_hkdf/4
2020-05-14 15:13:32 -03:00
Markus Triska
50776748a7 ADDED: HMAC-based key derivation (HKDF) via crypto_data_hkdf/4
This is useful to generate keys and initialization vectors
from suitable input keying material, so that future predicates
for symmetric encryption can be used with appropriate parameters.
2020-05-14 20:07:59 +02:00
Mark Thom
c07274a6fa Merge pull request #501 from triska/elliptic_curves
ADDED: Reasoning about elliptic curves in library(crypto).
2020-05-13 20:58:39 -03:00
Markus Triska
4084005ee7 ADDED: Reasoning about elliptic curves in library(crypto).
This is useful to establish shared secrets, using ECDH key exchange.

Note that CLP(ℤ) goal expansion is currently disabled due to #445,
and this slows down the computations considerably for the time being.
2020-05-13 22:44:27 +02:00
Mark Thom
40fce574cf Merge pull request #500 from triska/master
ADDED: ripemd160 digest algorithm
2020-05-13 17:39:24 -03:00
Markus Triska
fd761735d4 ADDED: ripemd160 digest algorithm
This is used for example for Bitcoin address generation.
2020-05-13 22:36:40 +02:00
Mark Thom
9cb98bdcdc Merge pull request #496 from triska/master
ADDED: crypto_data_hash/3, computing cryptographically secure digests
2020-05-13 17:32:46 -03:00
Mark Thom
b5b1f7ff8a Merge pull request #498 from notoria/between
Enhanced between/3
2020-05-13 17:32:31 -03:00
notoria
a86019d53b Enhanced between/3 2020-05-13 21:21:46 +02:00
Markus Triska
8cd7d08573 ADDED: crypto_data_hash/3, computing cryptographically secure digests 2020-05-13 19:23:09 +02:00
Mark Thom
6d6f6ed8fc resolve warning messages (#495) 2020-05-12 20:36:21 -06:00
Mark Thom
c9aed1381f Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-12 20:28:42 -06:00
Mark Thom
49ba5f30c5 throw existence_error on failed put_byte write (#492) 2020-05-12 20:28:39 -06:00
Mark Thom
3c6149fd27 do not unwrap stream write, TcpStream::shutdown results (#492) 2020-05-12 18:51:42 -06:00
Mark Thom
23f804eda3 return true on unmatching partial strings in eq_test to indicate failure (#491) 2020-05-12 18:22:41 -06:00
Mark Thom
4e321db189 Merge pull request #494 from triska/master
ADDED: crypto_n_random_bytes/2, creating cryptographically secure random bytes
2020-05-12 19:44:07 -03:00
Markus Triska
716fde5784 ADDED: crypto_n_random_bytes/2, creating cryptographically secure random bytes
The ring crate is used since it will be needed also for future
predicates in library(crypto).
2020-05-13 00:36:27 +02:00
Mark Thom
c70b873397 reset current input in ReadQueryTerm 2020-05-11 15:29:28 -06:00
Mark Thom
4c0da691b8 record names of variables at root in write_term_to_heap (#487) 2020-05-10 21:48:38 -06:00
Mark Thom
d1d36f9af0 fix at_end_of_stream/1 (#479) 2020-05-10 16:06:15 -06:00
Mark Thom
56efb4ffba change VarNames to VNNames (#476) 2020-05-10 15:54:53 -06:00
Mark Thom
540bc71873 use heap_pstr_iter in AtomChars (#482) 2020-05-10 15:46:51 -06:00
Mark Thom
c1df7ead1a set past end of stream, set Byte to -1, Char and Code to end_of_file, at end of stream (#479) 2020-05-10 14:50:17 -06:00
Mark Thom
d170c807e3 deref addr's in list of atom_codes (#484) 2020-05-10 14:33:56 -06:00
Mark Thom
50c0abc786 correct failed partial string matching (#483) 2020-05-10 14:00:49 -06:00
Mark Thom
c5057127ee eliminate duplicates in term_variables/2, term_attributed_variables/2 (#481) 2020-05-10 13:43:39 -06:00
Mark Thom
ea7b1a9592 fix position property in stream_property/2 (#477) 2020-05-10 13:35:48 -06:00
Mark Thom
26d483f2d7 place cut after infinitely expanding VarList in write_term_to_chars/3 (#476) 2020-05-10 13:30:30 -06:00
Mark Thom
67fce58708 change wrong at_end_of_stream value 'end' to 'at' (#479) 2020-05-10 13:24:45 -06:00
Mark Thom
ea5771c442 detect source_sink domain error in open/4 (#480) 2020-05-10 13:19:14 -06:00
Mark Thom
fa8a1faeb3 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-09 20:08:09 -06:00
Mark Thom
d60a000af6 increase prolog_parser version 2020-05-09 20:07:57 -06:00
Mark Thom
3d68e2b74f Merge pull request #475 from notoria/debug
Debug & warnings
2020-05-09 18:35:16 -03:00
Mark Thom
42f6d48abf Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-09 15:05:09 -06:00
Mark Thom
5587957602 halt on Ctrl-D in toplevel (#473) 2020-05-09 15:04:36 -06:00
notoria
1d313ebf25 Removed the warnings 2020-05-09 22:30:39 +02:00
notoria
cf92349c1d Implemented Debug for the new data structures 2020-05-09 22:28:32 +02:00
Mark Thom
b0ae44bc25 Merge branch 'sockets-develop' 2020-05-09 14:26:29 -06:00
Mark Thom
0c94124fdf Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-09 14:26:22 -06:00
Mark Thom
22298ddb41 update README.md 2020-05-09 14:26:08 -06:00
Mark Thom
6f927b9941 add set_stream_position/2 2020-05-09 14:20:32 -06:00
Mark Thom
dc5626f34d Merge pull request #474 from triska/radix
ADDED: ~Nr and ~NR are now available for radix conversions
2020-05-09 15:09:14 -03:00
Mark Thom
2717ec2759 Merge pull request #471 from triska/master
ADDED: Initial version of library(crypto).
2020-05-09 14:33:58 -03:00
Markus Triska
d75449ea63 ADDED: ~Nr and ~NR are now available for radix conversions 2020-05-09 19:19:25 +02:00
Mark Thom
034f5b16bd add at_end_of_stream/{0,1} 2020-05-09 01:38:23 -06:00
Mark Thom
ff420f1e67 carry past_end_of_stream in WrappedStreamInstance 2020-05-09 01:34:56 -06:00
Mark Thom
5be771842f add stream_property/2 2020-05-09 00:56:17 -06:00
Markus Triska
a131c93a14 ADDED: Initial version of library(crypto). 2020-05-08 22:33:49 +02:00
Mark Thom
3da3546ef8 Merge pull request #468 from notoria/cont
Enhanced shift/1
2020-05-07 14:26:34 -03:00
notoria
47f5223c37 Enhanced shift/1 2020-05-07 19:10:21 +02:00
Mark Thom
b8c50eb0fe add peek_char/{1,2}, peek_byte/{1,2}, peek_code/{1,2} 2020-05-06 01:05:03 -06:00
Mark Thom
fe05082ddc add sockets library, use new type_error function from sockets system calls 2020-05-05 22:57:06 -06:00
Mark Thom
414acda9e0 add put_code/{1,2}, get_code/{1,2}, improve get_* predicates 2020-05-05 22:42:35 -06:00
Mark Thom
47e3a5e75a add put_byte/{1,2}, put_char/{1,2} 2020-05-05 17:42:18 -06:00
Mark Thom
573df892bc cleanup on reading predicates, add get_char/{1,2} 2020-05-05 16:54:40 -06:00
Mark Thom
bfced59949 clean up stream error handling, add get_byte/{1,2} 2020-05-05 16:09:17 -06:00
Mark Thom
9a63b1751b change EOFCode eof_action from end_of_stream to end_of_file, add echo_server.pl example 2020-05-05 14:15:16 -06:00
Mark Thom
53ae8b9bd1 add flush_output/{0,1}, past_end_of_stream 2020-05-05 14:10:49 -06:00
Mark Thom
bf3cc5a367 Merge pull request #460 from notoria/get_single_char
Fixed ctrl-c issue with get_single_char/1
2020-05-05 14:10:24 -03:00
notoria
ac00bc6b70 Fixed ctrl-c issue with get_single_char/1 2020-05-05 18:47:00 +02:00
Mark Thom
ab62603c5a add close/{1,2}, better EOF action handling in read_term 2020-05-05 00:38:21 -06:00
Mark Thom
dd247cd541 adapt write_term family, add handling for EOF actions. 2020-05-04 21:17:06 -06:00
Mark Thom
0d7a9f32d6 add open/3, write_term/3 2020-05-04 14:50:26 -06:00
Mark Thom
0d85ef4404 Merge pull request #453 from triska/master
disable goal expansion until #445 is resolved
2020-05-04 14:32:26 -03:00
Mark Thom
8e9273a96a test 2020-05-04 11:18:13 -06:00
Markus Triska
2dfdaaa4ba disable goal expansion until #445 is resolved 2020-05-04 18:55:52 +02:00
Mark Thom
407094cdcb merge 2020-05-03 23:36:26 -06:00
Mark Thom
0111db6744 improve answer substitutions at top-level 2020-05-03 23:35:21 -06:00
Mark Thom
a49230c0e1 Merge branch 'master' into sockets-develop 2020-05-03 23:09:13 -06:00
Mark Thom
d04abc2021 improve answer substitutions at top-level 2020-05-03 23:08:53 -06:00
Mark Thom
b5368207d6 merge with master 2020-05-03 18:49:15 -06:00
Mark Thom
b8400e0c92 make term{_attributed_}variables/2 order {attributed} variables by left-to-right occurrence 2020-05-03 18:43:23 -06:00
Mark Thom
ef6b6cd279 Merge pull request #450 from triska/master
do not project queue/2 attributes to residual goals
2020-05-03 20:34:47 -03:00
Mark Thom
e5903d8d6e use select/3 in gather_equations for detecting whether variables are distinct. 2020-05-03 20:31:12 -06:00
Mark Thom
e2a3db1b82 use copy_term/3 as source of attribute goals 2020-05-03 19:34:18 -06:00
Mark Thom
9e220e8416 restore previous focus upon character match failure in compare_pstr_to_string (#441) 2020-05-03 17:03:59 -06:00
Markus Triska
605c233753 do not project queue/2 attributes to residual goals 2020-05-04 00:54:43 +02:00
Mark Thom
aafe18c2b5 Merge pull request #449 from notoria/read
Completed read/1, ctrl-d is end_of_file
2020-05-03 15:27:46 -03:00
Mark Thom
262e8af813 version bump for prolog_parser 2020-05-03 13:46:34 -06:00
notoria
75aa4ce8a0 Completed read/1, ctrl-d is end_of_file 2020-05-03 19:54:17 +02:00
Mark Thom
a494cdaf92 use complete strings in FileToChars (#430) 2020-05-03 02:54:49 -06:00
Mark Thom
848425418b Project goals of query variables (#362) 2020-05-02 22:27:32 -06:00
Mark Thom
989bed88fe wrap support code in modules 2020-05-02 21:26:36 -06:00
Mark Thom
99f66200f7 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-02 18:47:53 -06:00
Mark Thom
b642bd2cfd add term_attributed_variables/2 (#422) 2020-05-02 18:46:29 -06:00
Mark Thom
c062b7c9da Merge pull request #443 from notoria/arithmetic
Implemented expmod/4
2020-05-02 12:00:44 -03:00
notoria
7df7901532 Implemented expmod/4 2020-05-02 14:56:07 +02:00
Mark Thom
9dabce819a remove now unnecessary dependencies, correct ordering of ! in (;)/3 2020-05-01 23:38:44 -03:00
Mark Thom
a019109857 Merge pull request #440 from notoria/arithmetic
Enhanced number_to_rational/2 and number_to_rational/3
2020-05-01 21:48:55 -03:00
Mark Thom
46cdf14ecc Merge pull request #439 from notoria/cli2
Enhanced command line?
2020-05-01 21:45:03 -03:00
Mark Thom
fd5ff20b68 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-05-01 21:40:45 -06:00
Mark Thom
81f9666e82 cut after compound check in (;)/2 only after semicolon_compound_selector succeeds 2020-05-01 21:40:36 -06:00
Mark Thom
aa8659d5c7 insert cuts where needed into (;)/3 (#434) 2020-05-01 18:52:01 -06:00
notoria
9df2df0f9f Enhanced number_to_rational/2 and number_to_rational/3 2020-05-02 02:36:23 +02:00
notoria
98d38d763b Added $scryer_prolog_version/1 2020-05-02 02:19:22 +02:00
notoria
319a4622b3 Enhanced command line 2020-05-02 01:02:06 +02:00
Mark Thom
59b4c6dacb Merge pull request #437 from triska/master
use ℤ
2020-05-01 19:08:45 -03:00
Markus Triska
ec6d725587 use ℤ
This is now possible thanks to the great contribution by @matt2xu.

Many thanks!
2020-05-02 00:06:16 +02:00
Mark Thom
6f19649186 Merge pull request #436 from mthom/revert-419-cli
Revert "Enhanced command line"
2020-05-01 18:43:37 -03:00
Mark Thom
6a583b9b5b Revert "Enhanced command line" 2020-05-01 15:43:20 -06:00
Mark Thom
6617b79cd8 Merge pull request #419 from notoria/cli
Enhanced command line
2020-05-01 18:32:02 -03:00
Mark Thom
46b695d093 accept any and all characters in comment chars 2020-05-01 16:39:27 -06:00
notoria
de8a017e80 Enhanced command line #2 2020-05-01 18:52:41 +02:00
Mark Thom
b71ec91f57 Merge pull request #432 from triska/master
correct overeager CLP(ℤ) goal expansion
2020-05-01 13:41:43 -03:00
Mark Thom
1b7c226779 correct failing character match in compare_pstr_to_string (#397) 2020-05-01 10:29:10 -06:00
Markus Triska
ca5a5b4392 correct overeager CLP(ℤ) goal expansion
For instance, consider:

    t(X) :- X #= 1.

We *cannot* expand this to:

    ?- listing(t/1).
    t(A) :-
       (  integer(A) ->
          A=:=A
       ;  (  var(A) ->
             true
          ;  true,
             clpz:clpz_equal(A,A)
          )
       ).

Also, a new binding *must not* be dragged outside of disjunctions,
since the code may look for example like this:

        i(X) :-
            (   X #= 3
            ;   X #= 4
            ).

This commit fixes such issues, and still rewrites CLP(ℤ) expressions
as far as possible already at compilation time.

For example:

    n(X) :- X #= 1+3.

This is now compiled to (note that 1+3 is evaluated to 4):

    ?- listing(n/1).
    n(A) :-
       (  integer(A) ->
          A=:=4
       ;  (  var(A) ->
             A=4
          ;  B=4,
             clpz:clpz_equal(A,B)
          )
       ).

Ideally, it should be compiled to:

    n(4).
2020-05-01 18:29:00 +02:00
notoria
6ae7ae0210 Enhanced version 2020-05-01 16:46:31 +02:00
notoria
b911d2fda4 Enhanced command line 2020-05-01 16:19:06 +02:00
Mark Thom
847a92f580 map single character atoms down to characters in constant_index 2020-05-01 01:34:09 -06:00
Mark Thom
a346493de1 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-30 18:20:51 -06:00
Mark Thom
efd5d6efab use slightly better names in indexing functions 2020-04-30 18:20:42 -06:00
Mark Thom
b6df5a4b7f only add expanded goals to dynamic clause code (#416) 2020-04-30 17:53:26 -06:00
Mark Thom
34ae56e319 correct queue_len += queue_len; to queue_len += queue.len(); (#416) 2020-04-30 15:31:01 -06:00
Mark Thom
5870824d01 Merge pull request #425 from triska/master
enable goal expansion for CLP(ℤ) goals
2020-04-30 18:20:27 -03:00
Markus Triska
1dbadbfc35 enable goal expansion for CLP(ℤ) goals
Example:

    integer_successor(I0, I) :- I #= I0 + 1.

Yielding:

    ?- listing(integer_successor/2).
    integer_successor(A,B) :-
       (  integer(B) ->
          (  integer(A) ->
             B=:=A+1
          ;  C is B,
             clpz:clpz_equal(C,A+1)
          )
       ;  (  integer(A) ->
             (  var(B) ->
                B is A+1
             ;  C is A+1,
                clpz:clpz_equal(B,C)
             )
          ;  clpz:clpz_equal(B,A+1)
          )
       ).

Thus, fast low-level arithmetic is used whenever possible.
2020-04-30 23:09:15 +02:00
Mark Thom
d3f1cd7411 index atoms with operators against the same atom with no operator (#387) 2020-04-30 15:09:05 -06:00
Mark Thom
a821daadd3 Merge pull request #420 from triska/master
correct verify_attributes/3 for variables that have no CLP(ℤ) attribute attached (#373)
2020-04-30 13:32:21 -03:00
Markus Triska
a39f4b4487 correct verify_attributes/3 for variables that have no CLP(ℤ) attribute attached (#373)
Example:

    ?- freeze(B, queen_value_truth(C,N,B)), Q #= N #<==> B.
       clpz:(Q#=N#<==>B), clpz:(B in 0..1), freeze:freeze(B,queen_value_truth(C,N,B))
    ;  false.
2020-04-30 18:10:09 +02:00
Mark Thom
f7b401b9b6 fix user:goal_expansion 2020-04-30 09:28:11 -06:00
Mark Thom
c342d18f92 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-30 00:01:12 -06:00
Mark Thom
e0e52a3090 fix 'drain lower bound was too large', store user-level expansions to modules (#416) 2020-04-30 00:01:00 -06:00
Mark Thom
6f92480315 Merge pull request #418 from notoria/mediants
Documentation for arithmetic.pl
2020-04-29 18:53:00 -03:00
notoria
490d008edf Merge pull request #4 from triska/mediants
include library(arithmetic) in the overview
2020-04-29 23:48:07 +02:00
notoria
ccee17c573 Merge branch 'mediants' into mediants 2020-04-29 23:47:05 +02:00
Mark Thom
dd4832e40b add ordsets.pl to README 2020-04-29 18:42:17 -03:00
Mark Thom
ff5b870a9f Merge pull request #417 from notoria/mediants
Implemented predicate mediants/2 with Stern-Brocot tree
2020-04-29 18:33:35 -03:00
notoria
a371580201 Add rational_numerator_denominator/3, number_to_rational/2 and renamed stern_brocot/3 to number_to_rational/3 2020-04-29 23:30:14 +02:00
Markus Triska
b499c575bc include library(arithmetic) in the overview
Many thanks to @notoria for these very useful predicates!
2020-04-29 22:06:49 +02:00
notoria
f5a6268cef Add rational_numerator_denominator/3, number_to_rational/2 and renamed stern_brocot/3 to number_to_rational/3 2020-04-29 21:52:01 +02:00
Mark Thom
2df4083602 use select to filter variable lists when printing equations of two variables 2020-04-29 11:05:10 -03:00
Mark Thom
2495fb796d select variable names in write_eq based on whether RHS is a variable (#326) 2020-04-29 10:56:17 -03:00
notoria
f7b49740c1 Removed predicate mediants/2 and added stern_brocot/3 2020-04-29 14:18:20 +02:00
Mark Thom
7e765fe726 correct answer substitution order, equating variables to themselves (#326) 2020-04-29 01:45:42 -06:00
notoria
1d339f74d1 Implemented predicate mediants/2 with Stern-Brocot tree 2020-04-29 02:44:13 +02:00
Mark Thom
a3bb288f01 check that lower bound on term expansion drain is below the len of the term expansion vector, queue (#416) 2020-04-28 19:22:20 -06:00
Mark Thom
0e1226573a print rational numbers using the rdiv operator when defined (#413) 2020-04-28 18:03:04 -06:00
Mark Thom
8151b65d12 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-28 17:20:25 -06:00
Mark Thom
eefd36b50d do length check in PartialString::range_from (#412) 2020-04-28 17:20:14 -06:00
Mark Thom
6f4d769187 Merge pull request #414 from triska/master
Small corrections, additions and improvements
2020-04-28 16:54:39 -03:00
Markus Triska
20b76a703a mention portray_clause/1 and listing/1 2020-04-28 18:27:46 +02:00
Markus Triska
dcc09b7bb5 mention backtrackable and non-backtrackable global variables 2020-04-28 18:22:18 +02:00
Markus Triska
4ea0dee90a use new nth0/3 from library(lists) 2020-04-28 17:59:49 +02:00
Markus Triska
e61116d35a use singleton variable, correct a mistake in maplist/8 2020-04-28 17:59:49 +02:00
Markus Triska
f7256c75d5 ADDED: nth0/3, relating indices to list elements 2020-04-28 17:59:49 +02:00
Markus Triska
2696fd1291 small typographic corrections 2020-04-28 17:59:49 +02:00
Markus Triska
b139620fba use new predicates from library(error) to throw type and domain errors 2020-04-28 17:59:49 +02:00
Mark Thom
97115a9c1c Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-28 01:41:11 -06:00
Mark Thom
36134c61b2 erase unnecessary stub from block of FileToChars 2020-04-28 01:40:59 -06:00
Mark Thom
77cf0fd87b don't append lists of attributes when binding attributed variables (#353) 2020-04-28 01:40:11 -06:00
Mark Thom
ab77a1cbc0 modify domain.pl example (#347) 2020-04-28 01:39:34 -06:00
Mark Thom
8490892493 Merge pull request #409 from triska/clp
add more information about Constraint Logic Programming (CLP)
2020-04-27 15:05:15 -03:00
Markus Triska
5f35dffa34 if_ --> if_/3 2020-04-27 19:53:25 +02:00
Markus Triska
89a4b5a6ff add more information about Constraint Logic Programming (CLP) 2020-04-27 19:48:46 +02:00
Mark Thom
931de7e39c Merge pull request #408 from triska/master
Introduce and use new predicates for throwing ISO errors
2020-04-27 14:35:17 -03:00
Markus Triska
ff41d6aef9 include the new library(random) in the overview
Many thanks to @notoria for this contribution!
2020-04-27 18:44:59 +02:00
Markus Triska
8e1ba58551 use new predicates from library(error) 2020-04-27 18:44:59 +02:00
Markus Triska
2b1692a860 ADDED: library(error): instantiation_error/1, domain_error/3, type_error/3
These predicates simplify throwing ISO errors.
2020-04-27 18:44:59 +02:00
Markus Triska
7a2981e21c include usage advice about probabilistic predicates 2020-04-27 18:44:59 +02:00
Mark Thom
f8717359d7 Merge pull request #406 from notoria/random
Created the library random and updated library clpb
2020-04-27 09:14:01 -03:00
Mark Thom
fe395e3a8c publish bimetatrans_ruleml_tests, move both files to own directory 2020-04-26 20:32:16 -06:00
Mark Thom
eb9fe851af Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-26 20:15:27 -06:00
notoria
d61851ad02 Added predicate random_integer and updated clpb 2020-04-27 01:52:19 +02:00
notoria
de7a408903 Created the library random and moved some predicates from iso_ext 2020-04-26 23:37:22 +02:00
Mark Thom
4b0f60fe71 Merge pull request #405 from triska/master
Two small documentation additions
2020-04-26 17:20:58 -03:00
Markus Triska
a167bcff5e add library(cont) to the list 2020-04-26 21:57:37 +02:00
Markus Triska
fb812e6335 add more information about tabling 2020-04-26 21:57:37 +02:00
Mark Thom
3fd557c74a Merge pull request #402 from notoria/pio
Small patch for library pio
2020-04-26 12:17:09 -03:00
notoria
9b1272da51 Small patch for library pio 2020-04-26 14:54:20 +02:00
Mark Thom
ae2402f4a7 Merge pull request #399 from notoria/debug
Implemented the Debug trait for all data structures
2020-04-25 23:35:27 -03:00
notoria
c98e869564 Implemented the Debug trait for all data structures 2020-04-26 02:20:24 +02:00
Mark Thom
e1a019ec56 Merge pull request #398 from triska/master
Several smaller improvements
2020-04-25 17:20:24 -03:00
Mark Thom
2d7fcbf23d use proper module name in bimetatrans_ruleml 2020-04-25 12:55:36 -06:00
Markus Triska
b604177ca1 include library(pio) and library(charsio) in the description 2020-04-25 20:12:54 +02:00
Markus Triska
f27597cab5 update toplevel interaction 2020-04-25 20:12:54 +02:00
Markus Triska
40619c8184 ENHANCED: Throw domain errors in char_type/2 for wrong types
This is especially important because a few of our names diverge from
ctype(3), and we better inform programmers when the type they are
using is not available at all.
2020-04-25 20:12:54 +02:00
Markus Triska
b0a5df2838 use an anonymous variable 2020-04-25 19:59:46 +02:00
Mark Thom
ae9232a2cb Merge pull request #396 from notoria/phrase_from_file
Implemented phrase_from_file/2 and /2
2020-04-25 14:17:24 -03:00
notoria
ad28d0025a No trimming of the buffer 2020-04-25 18:20:20 +02:00
notoria
d6772c5ca5 Implemented phrase_from_file/2 and /2 2020-04-25 18:04:13 +02:00
Mark Thom
a2400767ed Merge pull request #391 from notoria/msb_lsb2
Implemented lsb/2 and msb/2
2020-04-25 12:58:03 -03:00
notoria
1873b655a8 Implemented lsb/2 and msb/2 2020-04-24 23:52:32 +02:00
Mark Thom
e27c2b59c8 Merge pull request #380 from notoria/char_type
Implementation of the predicate char_type for a subset
2020-04-23 21:57:03 -03:00
notoria
4b50608642 Implementation of the predicate char_type for a subset 2020-04-23 16:09:06 +02:00
notoria
e3a7f9eb7a Draft of an implementation of char_type 2020-04-22 12:57:18 +02:00
Mark Thom
c130b3a92e Merge pull request #381 from triska/master
ADDED: library(format) now provides a rudimentary version of listing/1
2020-04-21 21:16:40 -03:00
Markus Triska
20fb6d6669 ADDED: library(format) now provides a rudimentary version of listing/1
Example:

    :- dynamic(a/1).

    a(X) :- X = true, b(X).

Yielding:

    ?- listing(a/1).
    %@ a(A) :-
    %@    A=true,
    %@    b(A).
    %@    true.

listing/1 only works for predicates and DCGs that are declared dynamic/1.
2020-04-22 01:53:20 +02:00
Mark Thom
caf20cd43d Merge pull request #377 from triska/master
portray_clause/1 now correctly tracks variable names (#376)
2020-04-20 19:16:12 -03:00
Markus Triska
608d661355 portray_clause/1 now correctly tracks variable names (#376)
Example:

    ?- portray_clause((A :- B)).
    %@ A :-
    %@    B.
    %@    true
    %@ ;  ...

Also, change the indentation from 8 to 3 at the express request of @UWN.
2020-04-20 22:53:15 +02:00
Mark Thom
31c868818b Merge pull request #375 from notoria/sleep
Changed the error being thrown by sleep/1
2020-04-20 11:52:25 -03:00
Mark Thom
443c4b6cd0 speed unification of pairs of partial strings 2020-04-20 11:37:32 -06:00
Mark Thom
ee425bb7f0 change CPU_now to CpuNow 2020-04-20 10:37:00 -06:00
Mark Thom
9d1c026231 check for num in comparisons, bump num-rug-adapter to 0.1.3 2020-04-20 10:35:30 -06:00
notoria
6d7612ead6 Corrected the error being thrown by sleep/1 2020-04-20 16:36:57 +02:00
notoria
b3e31094ff Changed the error being thrown by sleep/1 2020-04-20 11:27:28 +02:00
Mark Thom
c9750fab2c Merge pull request #371 from notoria/sleep
Added the sleep predicate
2020-04-19 21:41:33 -03:00
Mark Thom
5a1348e4d1 do casts between different arithmetic types for comparison and equality (#372) 2020-04-19 21:34:59 -06:00
Mark Thom
b64d45a74e speed comparisons of partial strings 2020-04-19 20:58:53 -06:00
Mark Thom
ba5a4c3945 use default machine policies in term expansions 2020-04-19 19:54:23 -06:00
Mark Thom
9ba37a932b Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-19 16:43:32 -06:00
Mark Thom
8ce377d033 update toplevel comments, add mutable stack to zipped acyclic iterators 2020-04-19 16:01:35 -06:00
notoria
a7f3dafd81 Undo a workaround 2020-04-19 23:26:16 +02:00
notoria
fb38b4b906 Corrected bound 2020-04-19 23:04:51 +02:00
notoria
f0a1a90b0e Merge pull request #3 from triska/sleep
update comment
2020-04-19 22:44:48 +02:00
notoria
fb331d39a6 Thrown an error if T < 0 or T > 0xfffffffffffffbff in sleep(T) 2020-04-19 22:43:13 +02:00
Markus Triska
7a3e6e19b1 update comment 2020-04-19 22:36:27 +02:00
notoria
5a063c541d Added the sleep predicate 2020-04-19 21:35:49 +02:00
Mark Thom
eef25ec638 Merge pull request #370 from triska/master
ENHANCED: time(Goal) now also reports the time if Goal fails
2020-04-19 16:33:51 -03:00
Markus Triska
7141fb18c1 ENHANCED: time(Goal) now also reports the time if Goal fails 2020-04-19 21:13:15 +02:00
Mark Thom
2c92be5e3d Merge pull request #367 from triska/master
ADDED: library(time), providing time/1 for benchmarking
2020-04-19 14:29:09 -03:00
Mark Thom
cae0733149 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-19 12:58:54 -06:00
Mark Thom
5d6ca7a88e throw '' whenever an interrupt is made (#365, #366) 2020-04-19 12:58:44 -06:00
Markus Triska
23b8a4d364 add entry for time/1 2020-04-19 19:24:22 +02:00
Markus Triska
c0df8cb578 update answer to use the latest toplevel improvements 2020-04-19 19:18:47 +02:00
Markus Triska
4693c23a49 ADDED: library(time), providing time/1 for benchmarking
sleep/1 and predicates for reasoning about time stamps should also
be added to this library.
2020-04-19 19:18:47 +02:00
Mark Thom
86364ff278 Merge pull request #364 from triska/master
Documentation addition, and correction of error term
2020-04-18 22:54:17 -03:00
Markus Triska
60f1f1cb3d Restore the type_error, which was implemented correctly by @notoria.
This corrects f28e87b240:
in_character is a valid type by 7.12.2 Error classification!
2020-04-19 02:15:59 +02:00
Markus Triska
ab3c520006 document ~| format modifier to place a tab stop at this position
This works best when used in tandem with ~N+, since it is currently
equivalent to ~0| and does not take the actual text position into account.
However, when using relative positions, this works as intended.
2020-04-19 01:59:16 +02:00
Mark Thom
3798dcb98d Merge pull request #363 from triska/master
Delay toplevel output until after the goal succeeds
2020-04-18 20:37:32 -03:00
Mark Thom
9daf290432 remove SCCCutPolicy panic, revise (;)/2 so that comma'ed cuts are handled properly (#361) 2020-04-18 20:10:53 -06:00
Mark Thom
3438f88c1e drop $ naming convention inside '' when unnecessary 2020-04-18 20:06:52 -06:00
Mark Thom
2c8858ff2d use correct signs in fixnum remainders 2020-04-18 17:37:54 -06:00
Markus Triska
45ed9405b0 use the most recent improvements 2020-04-19 01:34:13 +02:00
Markus Triska
075ca78dd2 ENHANCED: Delay toplevel output until after the goal succeeds.
This is to accommodate goals that are used for their side-effects,
when we are interested in their output.

Examples:

    ?- portray_clause((a :- a)).
    a :-
            a.
       true
    ;  false.

    ?- format("hello~w~n", [!]).
    hello!
       true
    ;  false.
2020-04-19 01:34:03 +02:00
Mark Thom
1d964ef921 add domain and type errors for compare/3 (#296) 2020-04-18 14:37:14 -06:00
Mark Thom
0c6d9b1d3b Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-18 14:17:13 -06:00
Markus Triska
1713f1222b reintroduce "n" as a synonym for ";" and " " 2020-04-18 14:16:46 -06:00
Markus Triska
450591d5a9 ENHANCED: the toplevel interaction now supports RETURN as a synonym for "."
This is made possible due to the recent improvements by @notoria.
2020-04-18 14:16:46 -06:00
Markus Triska
5c4863dfb1 small simplifications 2020-04-18 14:16:46 -06:00
notoria
c6a82cbc07 Removed a check in get_single_char 2020-04-18 14:16:46 -06:00
notoria
cc8f074d33 Added Space for continuation 2020-04-18 14:16:46 -06:00
notoria
196d74c3a0 Fixed the predicate name when error is thrown 2020-04-18 14:16:46 -06:00
Markus Triska
f28e87b240 instead of a type error, use a domain error
The preceding use of atom_length/2 already ensures that C has the
correct type (i.e., atom). However, its domain may still be wrong,
if its length is greater than 1.
2020-04-18 14:16:46 -06:00
Markus Triska
2d77ef4245 use get_single_char/1 2020-04-18 14:16:46 -06:00
notoria
3f578e24b5 Removed $raw_input_read_char 2020-04-18 14:16:46 -06:00
notoria
fb5ee5e190 get_single_char reads Tab as \t 2020-04-18 14:16:46 -06:00
notoria
63e8378310 get_single_char reads Enter as \n 2020-04-18 14:16:46 -06:00
notoria
c24ebaf506 Moved get_single_char from builtins.pl to charsio.pl 2020-04-18 14:16:46 -06:00
notoria
d3a1f11dfe Added predicate for reading a single character 2020-04-18 14:16:46 -06:00
Mark Thom
f96e694ff8 bracket graphic token chars in toplevel.pl (#357) 2020-04-18 14:16:40 -06:00
Mark Thom
d5d635e50f slight tweaks to bimetatrans 2020-04-18 14:16:40 -06:00
Mark Thom
1ad4ce9822 index Constant::Usize (#355) 2020-04-18 14:16:40 -06:00
Mark Thom
3f971174e4 quote single dots only 2020-04-18 14:16:40 -06:00
Markus Triska
16e257ea32 ADDED: list_to_set/2, using the first occurrence of each element
Example:

    ?- list_to_set([B,a,b,a,B,A,b,A], Ls).
       Ls = [B,a,b,A]
    ;  false.
2020-04-18 14:16:40 -06:00
Mark Thom
37f1f8ac14 allow characters as atoms in Machine::use_module and Machine::use_qualified_module 2020-04-18 14:16:40 -06:00
Mark Thom
474fa9b3ec remove underscores in printed variables produced by write_term_to_chars (#340) 2020-04-18 14:16:40 -06:00
Mark Thom
6d7b14ceb5 don't count the terminator in PStrIter len 2020-04-18 14:16:40 -06:00
Mark Thom
af14832b6c quote dot when quoted(true) (#349) 2020-04-18 14:16:40 -06:00
Mark Thom
9ef185978f privilege new op declarations over old ones (#346) 2020-04-18 14:16:40 -06:00
Mark Thom
38bb44664c interrupt running queries and return to toplevel (#323) 2020-04-18 14:16:40 -06:00
Mark Thom
124d924ff5 write [a,b,c] correctly with write_canonical (#349) 2020-04-18 14:16:40 -06:00
Markus Triska
e8091d8add update the toplevel description, incorporating the latest changes 2020-04-18 14:16:40 -06:00
Mark Thom
500bd53b6a Merge pull request #360 from notoria/master
Add get_single_char predicate.
2020-04-18 14:10:26 -03:00
notoria
6996ca832d Merge pull request #2 from triska/get_single_char
New feature: RET can now be used as a synonym for "." in the interaction
2020-04-18 18:51:37 +02:00
notoria
2e15ab44ab Removed a check in get_single_char 2020-04-18 18:28:26 +02:00
Markus Triska
2ae5472872 reintroduce "n" as a synonym for ";" and " " 2020-04-18 18:09:55 +02:00
Markus Triska
98a32790cd ENHANCED: the toplevel interaction now supports RETURN as a synonym for "."
This is made possible due to the recent improvements by @notoria.
2020-04-18 17:56:37 +02:00
Markus Triska
6dcefcfb71 small simplifications 2020-04-18 17:52:43 +02:00
notoria
988366e37f Added Space for continuation 2020-04-18 16:57:45 +02:00
notoria
98a37905b8 Fixed the predicate name when error is thrown 2020-04-18 15:21:19 +02:00
notoria
bd832d0fbb Merge pull request #1 from triska/get_single_char
use get_single_char/1
2020-04-18 14:50:32 +02:00
Markus Triska
1f7e18f2a9 instead of a type error, use a domain error
The preceding use of atom_length/2 already ensures that C has the
correct type (i.e., atom). However, its domain may still be wrong,
if its length is greater than 1.
2020-04-18 14:39:30 +02:00
Markus Triska
105e9c8e88 use get_single_char/1 2020-04-18 14:33:11 +02:00
notoria
99e1a5f117 Removed $raw_input_read_char 2020-04-18 13:54:18 +02:00
notoria
b35b49f7b3 get_single_char reads Tab as \t 2020-04-18 13:33:42 +02:00
notoria
5d064b18e6 get_single_char reads Enter as \n 2020-04-18 13:30:52 +02:00
notoria
11ea92288d Moved get_single_char from builtins.pl to charsio.pl 2020-04-18 12:52:49 +02:00
notoria
1ea6ae9fd9 Added predicate for reading a single character 2020-04-18 11:49:28 +02:00
Mark Thom
57d739673a index Constant::Usize (#355) 2020-04-15 14:11:45 -06:00
Mark Thom
4d8f76e922 quote single dots only 2020-04-15 13:45:35 -06:00
Mark Thom
fb41d4ab0f Merge pull request #354 from triska/master
ADDED: list_to_set/2, using the first occurrence of each element
2020-04-15 13:19:54 -03:00
Mark Thom
023ee688d3 allow characters as atoms in Machine::use_module and Machine::use_qualified_module 2020-04-15 10:32:23 -06:00
Mark Thom
5f8f0d8573 remove underscores in printed variables produced by write_term_to_chars (#340) 2020-04-15 10:27:20 -06:00
Mark Thom
f56e0a6586 don't count the terminator in PStrIter len 2020-04-15 10:07:44 -06:00
Markus Triska
2c96420bc4 ADDED: list_to_set/2, using the first occurrence of each element
Example:

    ?- list_to_set([B,a,b,a,B,A,b,A], Ls).
       Ls = [B,a,b,A]
    ;  false.
2020-04-15 18:04:01 +02:00
Mark Thom
8d95eb57fa quote dot when quoted(true) (#349) 2020-04-15 00:54:32 -06:00
Mark Thom
a9a5e79e85 privilege new op declarations over old ones (#346) 2020-04-15 00:37:00 -06:00
Mark Thom
2b720b8d0b interrupt running queries and return to toplevel (#323) 2020-04-15 00:08:35 -06:00
Mark Thom
93a0ab7f0a write [a,b,c] correctly with write_canonical (#349) 2020-04-14 23:25:15 -06:00
Mark Thom
18dd55d1f4 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-14 23:15:01 -06:00
Mark Thom
c9074d8d96 properly iterate through UTF-8 strings (#350) 2020-04-14 23:14:47 -06:00
Mark Thom
f6c995157e clean up print_list_like 2020-04-14 14:26:30 -06:00
Mark Thom
a059a9fbdc print lists as strings when possible (#295) 2020-04-14 13:28:51 -06:00
Mark Thom
2ac6596ba9 Merge pull request #348 from triska/master
update the toplevel description, incorporating the latest changes
2020-04-14 14:29:40 -03:00
Markus Triska
2d1f57e839 update the toplevel description, incorporating the latest changes 2020-04-14 19:24:13 +02:00
Mark Thom
7799ed36f0 provide better variable names in write_term_to_chars/3 (#340) 2020-04-14 10:39:10 -06:00
Mark Thom
db5ed345bc improve writeX performance (#290) 2020-04-14 10:25:05 -06:00
Mark Thom
082c6a9e64 copy operator exports to impromptu modules (#307) 2020-04-14 01:03:49 -06:00
Mark Thom
b74a4d5225 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-14 00:40:57 -06:00
Mark Thom
16a572c250 change from xsi to iso in bimetatrans, correct atomic/1 failure (#343) 2020-04-14 00:40:51 -06:00
Mark Thom
6a8a2d6f56 Merge pull request #345 from triska/master
ADDED: group_pairs_by_key/2
2020-04-13 19:24:52 -03:00
Markus Triska
237c855f10 ADDED: group_pairs_by_key/2
Pairs must be keysorted and may also contain variables as keys.

Examples:

    ?-  group_pairs_by_key([1-a,1-b,2-c], Ps).
       Ps = [1-[a,b],2-[c]].

    ?-  group_pairs_by_key([X-a,X-b,2-c], Ps).
       Ps = [X-[a,b],2-[c]].
2020-04-14 00:10:33 +02:00
Mark Thom
15d18484e3 version bump 2020-04-13 11:17:50 -06:00
Mark Thom
378e9b5539 Merge pull request #327 from srenatus/sr/pass-argv-as-partial-strings
use partial strings for passing argv to toplevel.pl
2020-04-13 11:17:08 -03:00
Mark Thom
7a135bd87c Merge pull request #342 from srenatus/sr/fix-cargo-install
main: fix version reporting without git
2020-04-13 11:16:55 -03:00
Stephan Renatus
9e907e36e2 main: fix version reporting without git
Fixes #339.

At least I hope so. I didn't come up with an idea how to test this with
little effort.

Signed-off-by: Stephan Renatus <srenatus@chef.io>
2020-04-13 15:50:33 +02:00
Mark Thom
9ac71f304d Merge pull request #341 from triska/master
the workaround is no longer necessary, since #336 is now corrected
2020-04-13 09:13:17 -03:00
Markus Triska
ab9b604d02 the workaround is no longer necessary, since #336 is now corrected 2020-04-13 11:32:06 +02:00
Stephan Renatus
155645307a use partial strings for passing argv to toplevel.pl
In run_top_level: rename variables, make them partial strings

The rename is mostly cosmetic: all of argv is passed, and if argv
contains flags, goals, etc, it's more than filenames.

Using partial strings instead of atom came from discussions about how to
pass goals in the CLI, and can thus be considered preliminary work for
that.

In toplevel.pl: convert partial string to atom before passing it to
`use_module`.

Signed-off-by: Stephan Renatus <srenatus@chef.io>
2020-04-13 10:10:10 +02:00
Mark Thom
4ca7ac4851 use prolog_parser 0.8.50 (#336) 2020-04-13 00:32:41 -06:00
Mark Thom
02eb24cd57 correct partial string copying by resetting threshold to redirected partial string stubs to 0 2020-04-12 23:33:07 -06:00
Mark Thom
787fbe1bb6 use partial_string/1 in place of the removed string/1 in bimetatrans_ruleml.pl 2020-04-12 22:27:53 -06:00
Mark Thom
7678858bab update Cargo.lock 2020-04-12 22:01:59 -06:00
Mark Thom
bf7a28f07c bump version to 0.8.119 2020-04-12 21:59:22 -06:00
Mark Thom
9e54b1406c Merge pull request #338 from triska/master
also include a newline in portray_clause/1
2020-04-12 20:35:48 -03:00
Markus Triska
f52c9772a7 also include a newline in portray_clause/1 2020-04-13 01:24:15 +02:00
Mark Thom
ea5203d8c3 Merge pull request #337 from triska/master
ADDED: provide a rudimentary version of portray_clause/1
2020-04-12 20:10:21 -03:00
Mark Thom
0fad2f9953 make write_term_to_chars work with variable terms (#335) 2020-04-12 18:10:45 -06:00
Mark Thom
d3a9392421 provide read_term_from_chars/2 (#334) 2020-04-12 17:26:11 -06:00
Markus Triska
208927544d ADDED: provide a rudimentary version of portray_clause/1
At the moment, library(format) seems to be a fitting place.
In the eventual library organization, portray_clause/1 and
related predicates may be moved to their own dedicated library.
2020-04-13 01:04:52 +02:00
Mark Thom
7621155a89 change wording of QueryCannotBePostedAsFact to QueryCannotBeDefinedAsFact, remove old commented code in read.rs 2020-04-12 16:01:53 -06:00
Mark Thom
4ddc4ebf46 Merge pull request #333 from triska/master
incorporate the latest improvements in the description of partial strings
2020-04-12 16:00:01 -03:00
Markus Triska
786f0b5ca2 incorporate the latest improvements in the description of partial strings
As of 6e4b76a3b4, the use of
partial_string/3 can be replaced by (=)/2 in this example.
2020-04-12 20:46:22 +02:00
Mark Thom
b4ff429bfa Merge pull request #332 from triska/master
ENHANCED: ~w now supports compound terms, and ~q is now also available
2020-04-12 15:00:20 -03:00
Mark Thom
f2cf0ec459 move write_term_to_chars/3 to src/prolog/lib/charsio.pl 2020-04-12 14:42:39 -06:00
Mark Thom
0380d70a46 add write_term_to_chars/3 to iso_ext.pl (#288) 2020-04-12 14:29:28 -06:00
Mark Thom
28099b9812 correct misleading error for modules (#300) 2020-04-12 12:51:59 -06:00
Mark Thom
2ce4f602c0 be more judicious with at_cdr in print_string (#295) 2020-04-12 12:18:15 -06:00
Mark Thom
5e81fb4754 throw exception when attempting to post query as goal (#329) 2020-04-12 12:07:41 -06:00
Markus Triska
c9f1a95343 ENHANCED: ~w now supports compound terms, and ~q is now also available 2020-04-12 19:56:01 +02:00
Mark Thom
97d8d07947 recognize empty lists as partial strings (#328) 2020-04-11 23:08:15 -06:00
Mark Thom
4f0adad78e remove string/1, use a more general test for the partial_string/1 type test (#328) 2020-04-11 22:47:52 -06:00
Mark Thom
0499005db5 throw error when trying to use partial lists as DCG terminal-sequence (#286) 2020-04-11 22:40:29 -06:00
Mark Thom
aedffb3cfe remove unnecessary use of partial_string in bimetatrans example 2020-04-11 22:02:17 -06:00
Mark Thom
fff96e69db Merge pull request #310 from triska/bimeta_example
Bimeta example
2020-04-11 22:03:10 -03:00
Mark Thom
abc99c9dd8 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-04-11 21:59:25 -06:00
Mark Thom
6e4b76a3b4 compile special instructions for partial strings when recognized 2020-04-11 21:53:28 -06:00
Mark Thom
92d8642133 do not accept [X|Xs] patterns as DCG non-terminals (#286) 2020-04-11 21:52:45 -06:00
Mark Thom
184124586b Merge pull request #320 from triska/master
add library(freeze) to make zcompare/3 work
2020-04-09 12:26:31 -03:00
Markus Triska
4d32b6976a add library(freeze) to make zcompare/3 work 2020-04-08 22:09:59 +02:00
Mark Thom
45a99e576d Merge pull request #318 from triska/master
extend description of strings and partial strings
2020-04-08 16:59:13 -03:00
Markus Triska
fac6d54986 extend description of strings and partial strings
Also, explain in more detail what this feature means to Prolog
application programmers, and the strategic direction of Scryer.
2020-04-08 21:07:00 +02:00
Mark Thom
036c35729c Merge pull request #316 from triska/master
clpz_monotonic/0 --> monotonic/0
2020-04-08 13:18:42 -03:00
Markus Triska
e442fddc66 tuples_in/2 now works 2020-04-08 17:49:06 +02:00
Mark Thom
a3c3afba48 fix list index offset bug (#314) 2020-04-06 16:15:36 -06:00
Markus Triska
794ceac440 clpz_monotonic/0 --> monotonic/0 2020-04-06 23:05:57 +02:00
Mark Thom
6299d41cda mark clp(X) item as completed, restore clp(Z) library description 2020-04-05 21:26:27 -03:00
Mark Thom
2e5ce0249e Merge pull request #312 from triska/master
ADDED: CLP(ℤ), Constraint Logic Programming over Integers
2020-04-05 21:14:11 -03:00
Mark Thom
560298655b consolidate Cargo.lock 2020-04-05 20:34:49 -06:00
Mark Thom
4086df27b2 merge with master 2020-04-05 20:33:45 -06:00
Mark Thom
d76ae413c4 use fixnums in place of bignums where possible 2020-04-05 20:32:16 -06:00
Markus Triska
8b1df2e9ca ADDED: CLP(ℤ), Constraint Logic Programming over Integers
library(clpz) implements declarative integer arithmetic.

The most important predicates for reasoning about integers are:

    (#=)/2    equality
    (#\=)/2   disequality
    (#<)/2    less than
    (#>)/2    greater than
    (#=<)/2   less than or equal to
    (#>=)/2   greater than or equal to

In addition, the library provides several global constraints, such as
all_distinct/1 and global_cardinality/2, and reification predicates
that reflect the truth values of constraints into integer variables.

Enumeration predicates such label/1 and labeling/2 can be used to
search for solutions over finite domains.

Almost all Prolog programs also reason about integers. Therefore, I
recommend to add this library to your .scryerrc configuration file so
that declarative integer arithmetic is available in all your programs.

More information about CLP(ℤ):

    https://www.metalevel.at/prolog/clpz

Enjoy!
2020-04-06 01:59:41 +02:00
Mark Thom
c8855f97e8 allocate empty partial strings 2020-04-05 02:55:30 -06:00
Mark Thom
bbf1aa1668 use copy_term/3 to print residual goals (#254) 2020-04-05 02:55:04 -06:00
Mark Thom
141f3bcec3 harmonize partial strings with complete strings (#276), make Addr a copyable type 2020-04-03 10:22:46 -06:00
Mark Thom
8f11fd7c62 Merge pull request #306 from srenatus/sr/version-argument
main: add -v/--version flag
2020-04-03 00:35:51 -03:00
Mark Thom
9f960c25a5 add bimetatrans_ruleml.pl to examples 2020-03-30 20:00:08 -06:00
Markus Triska
11a616917a introduce and use nonterminal list//1
Example:

    ?- phrase(list(Ls), Ls0).
       Ls = [], Ls0 = []
    ;  Ls = [_A], Ls0 = [_A]
    ;  Ls = [_A,_B], Ls0 = [_A,_B]
    ;  Ls = [_A,_B,_C], Ls0 = [_A,_B,_C]
    ;  ...
2020-03-31 01:28:16 +02:00
Markus Triska
a89f03f27d embed literal lists directly in DCGs
Example use of direct embedding:

    ?- phrase("abc", Ls0, Ls).
       Ls0 = [a,b,c|Ls].
2020-03-31 01:24:15 +02:00
Markus Triska
3faf36440c naming: partial_list_ws//1 --> list_ws//1
A DCG describes a list, so "list" is an appropriate name.
2020-03-31 01:23:00 +02:00
Stephan Renatus
45d4f46024 main: skip args[0]
This is a bit pedantic, it's unlikely that the executable would be
called `-v` or `--version`. However, it seems slightly more correct to
disregard argv[0] when checking flags.
Signed-off-by: Stephan Renatus <srenatus@chef.io>
2020-03-28 09:07:44 +01:00
Stephan Renatus
896f2aeb74 main: add -v/--version flag
Added a new dependency to take care of pulling in the version from git
at build time.

Args handling is minimalistic, but there wasn't any before, so I figured
this might do for now. (Eventually, some proper `--help` and usage
output might come in handy?)

Signed-off-by: Stephan Renatus <srenatus@chef.io>
2020-03-27 19:59:50 +01:00
Mark Thom
7de39611f3 Merge pull request #305 from srenatus/sr/typo
examples/least_time.pl: fix typo
2020-03-27 14:35:17 -03:00
Stephan Renatus
7e6be23287 examples/least_time.pl: fix typo
Signed-off-by: Stephan Renatus <srenatus@chef.io>
2020-03-27 18:29:17 +01:00
Mark Thom
cac76d4739 Merge branch 'master' into develop 2020-03-26 22:01:25 -06:00
Mark Thom
194e5dc94e initial commit for merge 2020-03-26 22:01:23 -06:00
Mark Thom
f7629362d7 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-03-19 20:42:37 -06:00
Mark Thom
ea0c396114 add least_time example 2020-03-19 20:42:11 -06:00
Mark Thom
057e2d8056 provide contextual information in the return value of try_in_situ_result 2020-03-19 20:41:58 -06:00
Mark Thom
487fe21e25 #278 2020-03-19 17:49:17 -06:00
Mark Thom
54dce9b60e Merge pull request #294 from triska/format
ENHANCED: format_//2 now supports ~a, ~d, ~Nd, ~D, ~ND, ~Nn, ~i and ~*
2020-03-19 20:00:40 -03:00
Markus Triska
f09a3142a6 ENHANCED: format_//2 now supports ~a, ~d, ~Nd, ~D, ~ND, ~Nn, ~i and ~* 2020-03-19 21:51:53 +01:00
Mark Thom
121c8d8a48 Merge pull request #293 from triska/documentation
update toplevel interaction
2020-03-17 13:55:05 -03:00
Markus Triska
b9561ec8db update toplevel interaction 2020-03-17 17:42:57 +01:00
Mark Thom
b1d8eff019 remove Addr::PStrTail (#276) 2020-03-17 02:03:29 -06:00
Mark Thom
f9c4a40d60 add set_input/1, set_output/1 2020-03-15 23:29:21 -06:00
Mark Thom
8e318f76a7 correct contextual quoting of chars 2020-03-15 21:15:42 -06:00
Mark Thom
7d21ea93a6 Merge pull request #289 from triska/documentation
include a short overview of notable libraries
2020-03-15 12:09:44 -03:00
Mark Thom
8630ba4a2d Merge pull request #291 from triska/toplevel
Two small toplevel features
2020-03-15 12:09:25 -03:00
Markus Triska
17a448e045 support 'h' to print help message during toplevel interaction 2020-03-15 13:42:51 +01:00
Markus Triska
7ff11dce35 support 'p' to reprint answer with max depth, allowing w -> p -> w ... 2020-03-15 13:26:49 +01:00
Markus Triska
e2a4854bcf include a short overview of notable libraries 2020-03-15 11:43:32 +01:00
Mark Thom
bef96e231d use max_depth at toplevel, allowing full printing of terms with the 'w' keypress (#287), put disjunctions on new lines (#278) 2020-03-15 00:35:59 -06:00
Mark Thom
325df8a6e3 add provisional max_depth option to write_term, speed printing of non-cyclic terms 2020-03-15 00:09:20 -06:00
Mark Thom
3620f72b6c output strings more efficiently 2020-03-14 18:17:11 -06:00
Mark Thom
42a90b2d40 don't emit overwriting warnings for term_expansion/2 or goal_expansion/2 2020-03-14 14:18:01 -06:00
Mark Thom
97aab6950c correct DCG descriptions of lists (#286) 2020-03-14 14:14:26 -06:00
Mark Thom
31258247b3 Give new answer variables readable names (#279) 2020-03-14 03:04:11 -06:00
Mark Thom
beed4e8aa8 print strings as strings only in the term expander 2020-03-13 21:05:25 -06:00
Mark Thom
c60a0bc062 print strings as strings (#285) 2020-03-13 18:24:13 -06:00
Mark Thom
733c2d4152 eliminate inner_numbervar_count 2020-03-13 17:12:35 -06:00
Mark Thom
79b5857f72 Merge pull request #284 from triska/master
ADDED: library(format), describing strings with format_//2
2020-03-13 17:06:27 -03:00
Mark Thom
7260eba26a allow module export of nonterminals (#281) 2020-03-13 15:40:03 -06:00
Mark Thom
7e7a1b2caf properly handle '\n' when quoted and not (#283, #280) 2020-03-13 15:32:56 -06:00
Mark Thom
99181e3302 fix -names (#275) 2020-03-13 13:49:12 -06:00
Mark Thom
d57d2b96d6 Revert "fix $VAR-names (#275) and properly print single control chars (#280)"
This reverts commit d3eb31e4c3.
2020-03-13 13:47:58 -06:00
Markus Triska
24bed8c548 ADDED: library(format), describing strings with format_//2
This library provides the nonterminal format_//2 to describe
formatted strings. format/2 is provided for impure output.

Usage:
======

phrase(format_(FormatString, Arguments), Ls)

format_//2 describes a list of characters Ls that are formatted
according to FormatString. FormatString is a string (i.e.,
a list of characters) that specifies the layout of Ls.
The characters in FormatString are used literally, except
for the following tokens with special meaning:

  ~w    use the next available argument from Arguments here,
        which must be atomic (a current limitation)
  ~f    use the next argument here, a floating point number
  ~Nf   where N is an integer: format the float argument
        using N digits after the decimal point
  ~s    use the next argument here, which must be a string
  ~N|   where N is an integer: place a tab stop at text column N
  ~N+   where N is an integer: place a tab stop N characters
        after the previous tab stop (or start of line)
  ~t    distribute spaces evenly between the two closest tabstops
  ~`Ct  like ~t, use character C instead of spaces to fill the space
  ~n    newline
  ~~    the literal ~

The predicate format/2 is like format_//2, except that it outputs
the text on the terminal instead of describing it declaratively.

If at all possible, format_//2 should be used, to stress pure parts
that enable easy testing etc. If necessary, you can emit the list Ls
with maplist(write, Ls).

The entire library only works if the Prolog flag double_quotes
is set to chars, the default value in Scryer Prolog. This should
also stay that way, to encourage a sensible environment.

Example:

?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs).
%@ Cs = [h,e,l,l,o,'\n','.','.','.','.','.','.',t,h,e,r,e,!] ;
%@ false.
2020-03-13 20:46:53 +01:00
Mark Thom
6c9e704aa8 sort variables in findall_with_existential before computing set difference (#282), trawl back changes of (#279) 2020-03-13 13:18:47 -06:00
Mark Thom
d3eb31e4c3 fix $VAR-names (#275) and properly print single control chars (#280) 2020-03-13 02:48:40 -06:00
Mark Thom
698b01f85a give new answer variables readable names (#279) 2020-03-13 02:40:06 -06:00
Mark Thom
4c88e97330 add current_output, printing of stream terms 2020-03-11 00:38:01 -06:00
Mark Thom
25babff827 add streams.rs, consume them in place of the old PrologStream 2020-03-09 11:56:16 -06:00
Mark Thom
23e833c69e update README.md 2020-03-08 18:02:06 -06:00
Mark Thom
22bb3fe876 replace termion with crossterm 2020-03-08 18:01:54 -06:00
Mark Thom
eee10d3ee7 fix broken conformity tests, quote left paren 2020-03-08 16:49:47 -06:00
Mark Thom
c7591045af represent the current_dir as an absolute path, not a relative one. (#262) 2020-03-07 16:00:10 -07:00
Mark Thom
6ddfef6383 count single character atoms as characters when generating first argument indices 2020-03-06 21:19:53 -07:00
Mark Thom
9852e30b39 compare characters directly in term comparison tests 2020-03-05 12:50:11 -07:00
Mark Thom
2d3ce32314 switch to representation error for partial strings (#267) 2020-03-01 23:21:35 -07:00
Mark Thom
d8e0bea110 correct handling of strings and partial strings as lists, print bar operator with surrounding spaces (#274) 2020-03-01 00:00:40 -07:00
Mark Thom
b2cf6a6ea8 update README 2020-02-29 15:23:48 -07:00
Mark Thom
2ebcd673c8 add s pointers for strings and partial strings, put_unsafe_value calls store earlier to avoid unnecessary heap writes (#273) 2020-02-28 23:42:40 -07:00
Mark Thom
aa5f186a96 allow set_prolog_flag in declarations 2020-02-28 21:42:22 -07:00
Mark Thom
934f73f696 remove cyclic_term/1 2020-02-27 16:19:48 -07:00
Mark Thom
993c6f0e7b actually do lco, and mark unsafe variables before the goals where they last occur, not just in the last goal 2020-02-27 16:17:20 -07:00
Mark Thom
1d79e22468 mention rustup.rs in README (#261) 2020-02-23 13:22:56 -07:00
Mark Thom
8ba92d8b99 update prolog_parser version, allow ('|') operator in DCGs (#274) 2020-02-23 13:16:14 -07:00
Mark Thom
af23258152 remove setof/3, bagof/3 non-determinism (#270) 2020-02-22 15:13:01 -07:00
Mark Thom
b2e50b42a3 rename non_iso.pl to iso_ext.pl (#265) 2020-02-22 00:58:37 -07:00
Mark Thom
003b4d0218 add comparisons between partial strings and string constants (#263) 2020-02-22 00:51:27 -07:00
Mark Thom
545472f9c4 correct misreported error in toplevel loop (#272) 2020-02-21 21:09:59 -07:00
Mark Thom
8f781c27f9 remove same_length/2 from clpb.pl 2020-02-21 17:21:15 -07:00
Mark Thom
589dc34b99 make partial strings accept characters (#268) 2020-02-21 13:08:03 -07:00
Mark Thom
564622e38b partial_string/3 should accept empty lists (#264) 2020-02-21 12:55:33 -07:00
Mark Thom
d3744d6f66 identify ground partial strings under (==) (#269) 2020-02-21 12:51:23 -07:00
Mark Thom
2613ef0633 adopt compatibility predicates from clpb and clpz into lists and pairs libraries 2020-02-21 12:48:15 -07:00
Mark Thom
be3cdcd71a fix #255 (I think?) 2020-02-21 01:02:52 -07:00
Mark Thom
d48ac09e25 correct PStrLocation offset bug in copier.rs 2020-02-20 10:59:42 -07:00
Mark Thom
e5a0ba7bf3 revert to older between.pl 2020-02-20 10:07:45 -07:00
Mark Thom
eb1980d286 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-02-19 21:44:18 -07:00
Mark Thom
424af312e5 update Cargo.lock and toml version 2020-02-19 21:42:39 -07:00
Mark Thom
a2a4c78c9a update README 2020-02-19 21:38:19 -07:00
Mark Thom
0457b38e41 add redone partial strings (#24, #95) 2020-02-19 21:34:09 -07:00
Mark Thom
617f803355 update between/3 to allow inf as upper bound 2020-02-19 15:55:56 -07:00
Mark Thom
d3da2683cb stop applying (=..)/2 to variables in tabling/trie.pl 2020-02-19 14:43:06 -07:00
Mark Thom
d97c670fcd Merge pull request #258 from aarroyoc/master
add n as an option to get the next solution
2020-02-19 12:10:48 -04:00
Mark Thom
8902bcb5ab use raw_block.rs for the heap backend 2020-02-16 16:30:36 -07:00
Mark Thom
5f51e264f2 change raw_vec to raw_block to avoid clashes with liballoc package 2020-02-13 20:05:46 -07:00
Mark Thom
1c4e2c0ed6 allow the frontier of the RawVec to be offset by a trait function 2020-02-12 23:52:30 -07:00
Mark Thom
5a6333129b move bump allocation logic to its own module 2020-02-12 23:34:05 -07:00
Mark Thom
969bd8f82c remove partial strings, but represent strings as lists when warranted by double_quotes 2020-02-12 22:12:42 -07:00
Adrián Arroyo Calle
a732bc9dc4 add n as an option to get the next solution 2020-02-10 17:51:26 +01:00
Mark Thom
1b5cf493d6 correct improper use of multifile directive in tabling/wrapper.pl 2020-02-06 10:12:57 -07:00
Mark Thom
0be8abd38c print extended characters recently added to alpha char set 2020-02-04 23:39:49 -07:00
Mark Thom
b71e8082d2 prolog_parser version bump 2020-02-04 23:35:03 -07:00
Mark Thom
034af296ff recommit Cargo.lock 2020-02-04 17:49:20 -07:00
Mark Thom
554b08a7d2 version bump, README.md tweak 2020-02-04 17:47:57 -07:00
Mark Thom
00b38549f5 enable cross-query memoization in the tabling library 2020-02-02 22:52:17 -07:00
Mark Thom
62b41ddb93 remove tabling/SLG resolution from nice-to-have list 2020-02-02 22:34:54 -07:00
Mark Thom
d83c5cf5f1 add tabling library, update README, make Cargo.toml non-local 2020-02-02 22:24:48 -07:00
Mark Thom
740bd528c4 add multifile and module scoped predicates 2020-02-02 22:13:14 -07:00
Mark Thom
668c9489c9 update README 2020-01-26 20:19:44 -07:00
Mark Thom
dcf2eadcf2 add preliminary multifile declaration support 2020-01-26 20:17:52 -07:00
Mark Thom
9c1064c1a3 use atom_concat in gensym.pl 2020-01-22 22:08:42 -07:00
Mark Thom
40bf5bc75f don't hold on to popped or frames 2020-01-20 20:59:30 -07:00
Mark Thom
6be2d8ccf2 correct bug in bb_b_put, correct stack truncation bug resulting in erroneous failures (affecting issue #255 and the in-progress tabling library) 2020-01-19 20:56:11 -07:00
Mark Thom
9958ad6f34 add prefixes to gensym keys 2020-01-13 21:01:09 -07:00
Mark Thom
728be8c259 add simple gensym library 2020-01-13 20:30:58 -07:00
Mark Thom
9a80d398e8 add listing sources to modules 2020-01-13 20:17:22 -07:00
Mark Thom
242c47bb92 Merge branch 'master' of https://github.com/mthom/rusty-wam 2020-01-03 00:47:56 -07:00
Mark Thom
85e37a9977 implement less defaulty interface in cont.pl 2019-12-23 12:19:18 -07:00
Mark Thom
dded56af78 remove l* languages 2019-12-23 11:49:20 -07:00
Mark Thom
3bf1cbbe6c push of preliminary delimited continuations library cont.pl (#136) 2019-12-20 22:27:49 -07:00
Mark Thom
4ef8ab6e76 merge with master 2019-12-19 20:04:30 -04:00
Mark Thom
837ae4cb17 preliminary cont work 2019-12-19 20:02:10 -04:00
Mark Thom
a4e352ba0d use Addr::StackCell(0,0) to indicate non-live local variables 2019-12-12 20:34:50 -07:00
Mark Thom
02d8b1441d expand goals inside (\+)/1 2019-12-11 21:42:15 -07:00
Mark Thom
196784bd09 change goal and term expansions, change call/N to use goal expansions 2019-12-10 21:36:02 -07:00
Mark Thom
b0fa45cd33 correct odd accidental change to clpb.pl 2019-12-08 11:44:01 -07:00
Mark Thom
923c9d4ad8 remove zeroing out from Stack::truncate 2019-12-08 11:43:22 -07:00
Mark Thom
2be7070e1a revert arithmetic code 2019-12-08 00:17:55 -07:00
Mark Thom
f16c2a6e99 update tests 2019-12-07 19:21:09 -07:00
Mark Thom
d6e75110a5 reconcile latest changes against stack changes 2019-12-07 18:59:03 -07:00
Mark Thom
3d2c4f0801 add backtracking of attributed variable data 2019-12-06 20:52:40 -04:00
Mark Thom
5ccd334555 resolve panic caused by lingering attribute goals (#253) 2019-12-06 15:22:28 -04:00
Mark Thom
90e1c990e5 print equations between variables (#228, #252) 2019-12-06 10:30:16 -04:00
Mark Thom
018b076835 binding attributed variables more eagerly after each implementation of verify_attributes/3 has been called (#248) 2019-12-05 00:33:46 -07:00
Mark Thom
9ae029b04d pop AND stack frames after unwinding the trail (#250) 2019-12-03 23:11:57 -07:00
Mark Thom
43b39538ff correct attributed variables bugs 2019-12-03 22:59:51 -07:00
Mark Thom
738ea59e23 fix copy_term/3 infinite looping on cyclic terms 2019-12-03 22:01:38 -07:00
Mark Thom
33c60d3434 Merge branch 'master' of https://github.com/mthom/rusty-wam 2019-12-02 19:38:15 -07:00
Mark Thom
607673ddcb Merge branch 'master' of https://github.com/mthom/scryer-prolog 2019-12-02 17:06:28 -04:00
Mark Thom
fc8e55c582 correct copying of cyclic lists in copier.rs 2019-12-02 17:06:12 -04:00
Mark Thom
943e5eeb35 correct misprinting of attributed variables done by printer 2019-12-01 21:46:04 -07:00
Mark Thom
52488b875a add predicates to lists.pl 2019-12-01 19:30:40 -07:00
Mark Thom
c362cc6d34 fix list copying 2019-12-01 15:28:47 -07:00
Mark Thom
406d3520f1 delete freeze attribute in freeze::attribute_goals//1 2019-12-01 14:43:27 -07:00
Mark Thom
9b71866b54 properly copy attributed variables (#247)" 2019-12-01 14:42:59 -07:00
Mark Thom
e48f87fcf0 clear ball before setting it (#246) 2019-12-01 03:04:01 -07:00
Mark Thom
77e83a390c add sumlist/2 to lists.pl 2019-11-30 14:26:15 -07:00
Mark Thom
27b659c401 add sumlist/2 to lists.pl 2019-11-30 14:22:59 -07:00
Mark Thom
a4cacaeab2 compress the definition of freeze:attribute_goals//1 2019-11-30 14:12:33 -07:00
Mark Thom
0eb20a5d8e pop AND frames when safe to do so, suspend resizing of AND frames until a proper GC is implemented (#244) 2019-11-30 14:08:02 -07:00
Mark Thom
3e49db1a29 backtrack attributed variable bindings after failure (#242) 2019-11-29 13:47:22 -04:00
Mark Thom
2d719ab6b7 create a list of module-prefixed goals in copy_term/3 2019-11-29 10:59:20 -04:00
Mark Thom
34745f6242 clone attribute goals from copy_term/3, fetch attribute goals should be a move 2019-11-29 00:59:31 -07:00
Mark Thom
9c9c484ee4 add copy_term/3 (#232) 2019-11-29 00:44:23 -07:00
Mark Thom
335202b9d9 reset attributed variable state between toplevel queries (#242) 2019-11-28 19:08:08 -07:00
Mark Thom
f998d6a99f merge latest commit 2019-11-28 21:22:59 -04:00
Mark Thom
5723edd765 unsafe stack transition 2019-11-28 21:22:03 -04:00
Mark Thom
7eb0de7f52 preserve heap contents in between goal expansions (#240, #241) 2019-11-28 00:45:13 -07:00
Mark Thom
9ae901bd0d terms containing attributed variables are not ground (#239) 2019-11-27 14:13:18 -04:00
Mark Thom
1dec482e22 use in situ code directory from metacall if conventional lookup fails (#238) 2019-11-27 00:52:30 -07:00
Mark Thom
0a665b79f2 add (:)/{3..12} to enable metacalls on module-prefixed predicates 2019-11-26 20:51:59 -07:00
Mark Thom
c3c53017f9 add ambiguity check for period printing, remove extraneous space between last goal and period (#237) 2019-11-26 19:38:43 -07:00
Mark Thom
834c57466f add operator exports to module declarations, treat them separately from predicate exports (#230)" 2019-11-25 23:09:49 -07:00
Mark Thom
4e887e3a87 generate module-level expansion code along with rest of module code 2019-11-24 15:47:34 -07:00
Mark Thom
7bf6a230f3 record module-level term_expansion and goal_expansion as inner predicates (#228) 2019-11-24 12:50:53 -07:00
Mark Thom
24ffdc2724 correct bug, dead code in toplevel.pl 2019-11-22 20:38:03 -07:00
Mark Thom
6899f051ac Merge branch 'master' of https://github.com/mthom/rusty-wam 2019-11-21 18:05:09 -07:00
Mark Thom
d9d77b3022 Merge branch 'master' of https://github.com/mthom/scryer-prolog 2019-11-21 16:26:13 -04:00
Mark Thom
b31b23e41c add conditional bracketing to equations printed by toplevel 2019-11-21 16:26:00 -04:00
Mark Thom
16418a47a8 Merge branch 'master' of https://github.com/mthom/rusty-wam 2019-11-20 23:50:54 -07:00
Mark Thom
1139b3b4c1 small test updates 2019-11-20 23:50:51 -07:00
Mark Thom
3277ecd13d Merge pull request #226 from malbarbo/num
Add feature num to use num crate in place of rug.
2019-11-20 23:51:55 -04:00
Marco A L Barbosa
37ff4a821c Add feature num to use num crate in place of rug. 2019-11-20 17:51:08 -03:00
Mark Thom
f82c6f3fed avoid overwriting IndexPtr's for clauses already declared dynamic (#227) 2019-11-20 10:50:33 -04:00
Mark Thom
d3842bb070 Merge pull request #234 from XVilka/patch-2
Fix README formatting
2019-11-19 20:14:00 -04:00
Mark Thom
2ab745016a Merge branch 'master' of https://github.com/mthom/rusty-wam 2019-11-19 20:09:44 -07:00
Mark Thom
5aaba8534f bump prolog_parser to version 0.8.35 2019-11-19 20:09:28 -07:00
Mark Thom
2b625b1094 commit Cargo.lock 2019-11-19 20:08:52 -07:00
Mark Thom
17f9695ab2 remove tests.rs module from main.rs 2019-11-19 10:13:59 -04:00
Anton Kochkov
c0a49b6a66 Fix README formatting 2019-11-19 18:48:46 +08:00
Mark Thom
66bebff01e add setup_call_cleanup tests, expand builtins tests 2019-11-18 23:17:56 -07:00
Mark Thom
6f9477f0bb rollback changes in snapshot handling 2019-11-17 21:07:16 -04:00
Mark Thom
de88fdc4dc correct failure to observe last call position in queries' 2019-11-17 20:18:11 -04:00
Mark Thom
17b2510480 update version number 2019-11-16 19:18:41 -07:00
Mark Thom
bcfd7cbf8d begin migrating tests to pure prolog, correct bug in toplevel 2019-11-16 19:15:07 -07:00
Mark Thom
cee3dbc453 quote values and goals in equations, eliminate dead code, correct retract/1 2019-11-16 17:15:25 -07:00
Mark Thom
c219d9ad5f Merge branch 'master' of https://github.com/mthom/scryer-prolog 2019-11-16 19:53:29 -04:00
Mark Thom
15927d7054 transition to unsafe and/or stack 2019-11-16 19:51:53 -04:00
Mark Thom
ed985c3cfe move more of the toplevel from rust into prolog 2019-11-16 00:26:15 -07:00
Mark Thom
4ad62e4606 add warnings when initialization goals fail (#168) 2019-10-30 00:25:17 -06:00
Mark Thom
d34811aa36 handle asserts in modules a little better (#223, #224) 2019-10-30 00:14:36 -06:00
Mark Thom
b90d69a45b install asserted predicates into modules from initialization directives (#222) 2019-10-29 00:06:51 -06:00
Mark Thom
81f220a4d2 replace \n\r and \r\n by \n (#221) 2019-10-27 20:50:21 -06:00
Mark Thom
e594ade84d finish #214, add needed ambiguity checks if 0 is the final character 2019-10-27 19:23:35 -06:00
Mark Thom
0dcd9e5805 correct sign/1 (#216) 2019-10-27 14:18:32 -06:00
Mark Thom
67a00e4deb update to prolog_parser v0.8.34 with increased MAX_ARITY (#218) 2019-10-27 13:10:32 -06:00
Mark Thom
e656e7fbae add sign/1 (#216) and gcd/2 (#217) as evaluable functors, update the README 2019-10-27 13:05:05 -06:00
Mark Thom
0fdf0d8d06 correct #215 by fixing bug in ambiguity_check 2019-10-27 12:32:18 -06:00
Mark Thom
7cdbd2f16c add consult/1 and shorthand for consult/1 (#214) 2019-10-27 11:33:49 -06:00
Mark Thom
ccfcc654a5 remove module from toplevel in unqualified use_module 2019-10-26 12:39:42 -06:00
Mark Thom
2eea0528b1 remove all mention of [clear] from the README 2019-10-26 01:35:26 -06:00
Mark Thom
a358431d30 reload files properly (re: #213) 2019-10-26 01:34:41 -06:00
Mark Thom
e9c1556c32 remove scratch comments from clpb.pl 2019-10-22 21:19:01 -06:00
Mark Thom
1b1879a6fa fix attributed variables bug causing weighted_maximum/3 example to omit a variable binding 2019-10-20 14:50:46 -06:00
Mark Thom
24e5e39c28 add order preserving tidy_trail, fix random_labeling/2 2019-10-19 00:29:50 -06:00
Mark Thom
ab9a14cc6a add randomness predicates, small but consequential changes to TrailRef 2019-10-17 00:21:21 -06:00
Mark Thom
a641822a1b Merge pull request #206 from triska/master
weighted_maximum/3 now works
2019-10-16 16:17:05 -03:00
Markus Triska
145fee0d36 weighted_maximum/3 now works 2019-10-16 19:18:38 +02:00
Markus Triska
567af2648c support must_be(var, ...) 2019-10-16 19:18:38 +02:00
Mark Thom
42a3bdc357 eliminate lingering attribute goals 2019-10-16 11:38:33 -03:00
255 changed files with 82614 additions and 37556 deletions

6
.dockerignore Executable file
View File

@@ -0,0 +1,6 @@
target
Dockerfile
README.md
.git
.gitignore
.gitmodules

40
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Test
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-10.15]
rust-version: [stable, beta]
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust-version }}
override: true
- name: Build lib
uses: actions-rs/cargo@v1
with:
command: rustc
args: --verbose --lib -- -D warnings
- name: Build bin
uses: actions-rs/cargo@v1
with:
command: rustc
args: --verbose --bin scryer-prolog -- -D warnings
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
args: --verbose --all
- name: Num tests
uses: actions-rs/cargo@v1
continue-on-error: true
with:
command: test
args: --verbose --all --no-default-features --features num

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
src/static_atoms.rs
target/
Cargo.lock

View File

@@ -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

2053
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,75 @@
[package]
name = "scryer-prolog"
version = "0.8.111"
version = "0.9.0"
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.rs"
[workspace]
members = ["crates/num-rug-adapter",
"crates/static-string-indexing",
"crates/instructions-template",
"crates/to-syn-value",
"crates/to-syn-value_derive"]
[features]
num = ["num-rug-adapter"]
# no default features to make num tests work
# workaround for --no-default-features and --features not working intuitively for workspaces with a root package
# see rust-lang/cargo#7160
default = ["rug"]
[build-dependencies]
indexmap = "1.0.2"
static-string-indexing = { path = "./crates/static-string-indexing" }
instructions-template = { path = "./crates/instructions-template" }
proc-macro2 = "*"
[dependencies]
dirs = "2.0.2"
downcast = "0.10.0"
cpu-time = "1.0.0"
crossterm = "0.16.0"
dirs-next = "2.0.0"
divrem = "0.1.0"
fxhash = "0.2.1"
git-version = "0.3.4"
hostname = "0.3.1"
indexmap = "1.0.2"
lazy_static = "1.4.0"
lexical = "5.2.2"
libc = "0.2.62"
modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" } # modular-bitfield = "0.11.2"
nix = "0.15.0"
ordered-float = "0.5.0"
prolog_parser = "0.8.33"
num-rug-adapter = { optional = true, path = "./crates/num-rug-adapter" }
ordered-float = "2.1.1"
phf = { version = "0.9", features = ["macros"] }
ref_thread_local = "0.0.0"
rug = "1.4.0"
rustyline = "5.0.3"
rug = { version = "1.12.0", optional = true }
rustyline = "9.0.0"
ring = "0.16.13"
ripemd160 = "0.8.0"
sha3 = "0.8.2"
blake2 = "0.8.1"
openssl = { version = "0.10.29", features = ["vendored"] }
native-tls = "0.2.4"
chrono = "0.4.11"
select = "0.4.3"
roxmltree = "0.11.0"
base64 = "0.12.3"
smallvec = "*"
sodiumoxide = "0.2.6"
static_assertions = "1.1.0"
slice-deque = "0.3.0"
[dependencies.termion]
version = "1.4.0"
[dev-dependencies]
assert_cmd = "1.0.3"
predicates-core = "1.0.2"
serial_test = "0.5.1"
[profile.release]
debug = true

26
Dockerfile Executable file
View File

@@ -0,0 +1,26 @@
# See https://github.com/LukeMathWalker/cargo-chef
ARG RUST_VERSION=1.52.1-buster
FROM rust:${RUST_VERSION} as planner
WORKDIR /scryer-prolog
RUN cargo install cargo-chef
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM rust:${RUST_VERSION} as cacher
WORKDIR /scryer-prolog
RUN cargo install cargo-chef
COPY --from=planner /scryer-prolog/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
FROM rust:${RUST_VERSION} as builder
WORKDIR /scryer-prolog
COPY . .
# Copy over the cached dependencies
COPY --from=cacher /scryer-prolog/target target
COPY --from=cacher $CARGO_HOME $CARGO_HOME
RUN cargo build --release --bin scryer-prolog
FROM debian:stable-slim
COPY --from=builder /scryer-prolog/target/release/scryer-prolog /usr/local/bin
ENV RUST_BACKTRACE=1
ENTRYPOINT ["/usr/local/bin/scryer-prolog"]

696
README.md
View File

@@ -5,6 +5,8 @@ source industrial strength production environment that is also a
testbed for bleeding edge research in logic and constraint
programming, which is itself written in a high-level language.
![Scryer Logo: Cryer](logo/scryer.png)
## Phase 1
Produce an implementation of the Warren Abstract Machine in Rust, done
@@ -30,10 +32,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,7 +42,7 @@ 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
- [x] `if_/3` and related predicates, following the developments of the
paper "Indexing `dif/2`".
- [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`).
- [x] Clause creation and destruction (`asserta/1`, `assertz/1`,
@@ -50,11 +50,26 @@ Extend Scryer Prolog to include the following, among other features:
- [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").
- [x] Tabling library based on delimited continuations
(documented in "Tabling as a Library with Delimited Control").
- [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.
- [ ] Greatly reducing the number of instructions used to compile disjunctives.
- [ ] Storing short atoms to heap cells without writing them to the atom table.
- [ ] A compacting garbage collector satisfying the five properties of
"Precise Garbage Collection in Prolog." (_in progress_)
- [ ] Mode declarations.
- [ ] Extensions for clp(FD).
## Phase 3
@@ -79,218 +94,130 @@ Programming?"
unum implementation or an ad hoc one. Unums are described in
Gustafson's book "The End of Error."
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
### Native Install (Unix Only)
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.
Scryer Prolog can be installed with cargo, like so:
```
$> cargo install scryer-prolog
```
cargo will download and install the libraries Scryer Prolog uses
automatically. You can find the `scryer-prolog` executable in
`~/.cargo/bin`.
automatically from crates.io. You can find the `scryer-prolog`
executable in `~/.cargo/bin`.
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.
Publishing Rust crates to crates.io and pushing to git are entirely
distinct, independent processes, so to be sure you have the latest
commit, it is recommended to clone directly from this git repository,
which can be done as follows:
## Built-in predicates
```
$> git clone https://github.com/mthom/scryer-prolog
$> cd scryer-prolog
$> cargo run [--release]
```
The following predicates are built-in to Scryer.
The optional `--release` flag will perform various optimizations,
producing a faster executable.
* 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.57 and up**.
### Docker Install (All Platforms)
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&nbsp;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&nbsp;input and consults the entered Prolog&nbsp;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&nbsp;`.` to abort the search and return to the
toplevel&nbsp;prompt. Press&nbsp;`f` to see the next 5 answers, and
`a` to see all answers. Press&nbsp;`h` to show a help message.
To quit Scryer Prolog, use the standard predicate `halt/0`:
```
?- halt.
```
@@ -301,42 +228,342 @@ 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&nbsp;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&nbsp;indexing naturally arises as a
special case of this strategy, if the first argument is instantiated
in any clause of a predicate's definition.
### Strings and partial strings
In Scryer Prolog, the default value of the Prolog flag `double_quotes`
is `chars`, which is also the recommended setting. This means that
double-quoted strings are interpreted as lists of *characters*, in the
tradition of Marseille&nbsp;Prolog.
For example, the following query succeeds:
```
?- "abc" = [a,b,c].
true.
```
Internally, strings are represented very compactly in packed
UTF-8&nbsp;encoding. A naive representation of strings as lists of
characters would use one memory&nbsp;cell per character, one
memory&nbsp;cell per list constructor, and one memory&nbsp;cell for
each tail that occurs in the list. Since one memory&nbsp;cell takes
8&nbsp;bytes on 64-bit machines, the packed representation used by
Scryer&nbsp;Prolog yields an up&nbsp;to **24-fold&nbsp;reduction** of
memory usage, and corresponding reduction of memory&nbsp;accesses when
creating and processing strings.
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&nbsp;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&nbsp;list with tail&nbsp;`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&nbsp;=&nbsp;[a,b,c|Ls]</tt> yields
the exact same internal representation, and has the advantage that
only the standard predicate&nbsp;`(=)/2` is used.
Definite clause grammars as provided by
[`library(dcgs)`](src/lib/dcgs.pl), and the predicates from
[`library(lists)`](src/lib/lists.pl), are ideally suited for reasoning
about strings.
Partial strings were first proposed by Ulrich Neumerkel in issue
[#95](https://github.com/mthom/scryer-prolog/issues/95).
### Occurs check and cyclic terms
The *occurs&nbsp;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&nbsp;check* is omitted by default
in Scryer&nbsp;Prolog and many other Prolog systems.
In Scryer Prolog, performing unifications which succeed only if the
*occurs&nbsp;check* is omitted yield *cyclic&nbsp;terms*, also called
*rational&nbsp;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&nbsp;check* enabled, or let Prolog throw an error if enabling
the *occurs&nbsp;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&nbsp;check*. This is the default.
- `true`
Perform all unifications with the *occurs&nbsp;check* enabled.
- `error`
Yield an error if a unification is performed that the
*occurs&nbsp;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&nbsp;check* is enabled.
### Tabling (SLG resolution)
One of the foremost attractions of Prolog is that logical consequences
of pure&nbsp;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&nbsp;execution and
SLG&nbsp;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&nbsp;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&nbsp;et.&nbsp;al.
### Constraint Logic Programming (CLP)
Scryer Prolog provides excellent support for Constraint Logic
Programming&nbsp;(CLP), which is the amalgamation of
Logic&nbsp;Programming&nbsp;(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&nbsp;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&nbsp;solvers.
These features make Scryer Prolog an ideal platform for teaching,
learning and developing portable CLP&nbsp;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&nbsp;Prolog are also called
*library*&nbsp;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&nbsp;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&nbsp;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&nbsp;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&nbsp;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&nbsp;resolution).
* [`format`](src/lib/format.pl)
The nonterminal `format_//2` is used to describe formatted output,
arranging arguments according to a given format&nbsp;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&nbsp;trees which ensure
*O*(log(*N*))&nbsp;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&nbsp;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&nbsp;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&nbsp;servers. HTTPS is also supported.
* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.0 web server.
* [`sgml`](src/lib/sgml.pl)
`load_html/3` and `load_xml/3` represent HTML and XML&nbsp;documents
as Prolog&nbsp;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&nbsp;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&nbsp;variables.
* [`iso_ext`](src/lib/iso_ext.pl)
Conforming extensions to and candidates for inclusion in the Prolog
ISO&nbsp;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&nbsp;(HKDF), password-based key derivation&nbsp;(PBKDF2),
public key signatures and signature verification with&nbsp;Ed25519,
ECDH key&nbsp;exchange over Curve25519 (X25519), authenticated symmetric
encryption with ChaCha20-Poly1305, and reasoning about elliptic curves.
* [`uuid`](src/lib/uuid.pl) UUIDv4 generation and hex representation
* [`tls`](src/lib/tls.pl)
Predicates for negotiating TLS connections explicitly.
* [`ugraphs`](src/lib/ugraphs.pl) Graph manipulation library
To use predicates provided by the `lists` library, write:
```
?- use_module(library(lists)).
@@ -371,7 +598,6 @@ REPL:
```
?- [user].
(type Enter + Ctrl-D to terminate the stream when finished)
:- module(test, [local_member/2]).
:- use_module(library(lists)).
@@ -379,4 +605,56 @@ 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&nbsp;Emacs](https://www.gnu.org/software/emacs/) with the
[Prolog&nbsp;mode](https://bruda.ca/emacs/prolog_mode_for_emacs)
maintained by Stefan Bruda.
Use [ediprolog](https://www.metalevel.at/ediprolog/) to consult
Prolog&nbsp;code and evaluate Prolog queries in arbitrary
Emacs&nbsp;buffers.
Emacs definitions that show Prolog terms as trees are available
in&nbsp;[tools](tools).
To *debug* Prolog code, we recommend the predicates from
[**`library(debug)`**](src/lib/debug.pl), most notably:
- `(*)/1` to *"generalize&nbsp;away"* a Prolog goal. Use it to debug
unexpected failures by generalizing your definitions until they
succeed. Simply place&nbsp;`*` 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&nbsp;`$` in front of a goal
to emit this information for that goal.
This way of debugging Prolog code has several major benefits, such as:
It stays close to the actual Prolog code under consideration, it does
not need additional tools and formalisms for its application, and
further, it encourages declarative reasoning that can in principle
also be performed automatically.
## Support and discussions
If Scryer Prolog crashes or yields unexpected errors, consider filing
an&nbsp;[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)!

106
build.rs
View File

@@ -1,56 +1,88 @@
extern crate indexmap;
use indexmap::IndexSet;
use static_string_indexing::index_static_strings;
use instructions_template::generate_instructions_rs;
use std::env;
use std::fs::{File, copy, read_dir};
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;
fn main()
{
fn find_prolog_files(libraries: &mut File, prefix: &str, current_dir: &Path) {
let entries = match current_dir.read_dir() {
Ok(entries) => entries,
Err(_) => return,
};
for entry in entries.filter_map(Result::ok).map(|e| e.path()) {
if entry.is_dir() {
if let Some(file_name) = entry.file_name() {
let new_prefix = prefix.to_owned() + file_name.to_str().unwrap() + "/";
find_prolog_files(libraries, &new_prefix, &entry);
}
} else if entry.is_file() {
let ext = std::ffi::OsStr::new("pl");
if entry.extension() == Some(ext) {
let contain = String::from_utf8(fs::read(&entry).unwrap()).unwrap();
let name = entry.file_stem().unwrap().to_str().unwrap();
let line = format!(
" m.insert(\"{}\",\n{:?});\n",
prefix.to_owned() + name,
contain
);
libraries.write_all(line.as_bytes()).unwrap();
}
}
}
}
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("libraries.rs");
let mut libraries = File::create(&dest_path).unwrap();
let mut library_index = IndexSet::new();
let lib_path = Path::new("src/lib");
let paths = read_dir("./src/prolog/lib").unwrap();
libraries
.write_all(
b"ref_thread_local::ref_thread_local! {
pub(crate) static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
let mut m = IndexMap::new();\n",
)
.unwrap();
for item in paths {
let item = item.unwrap().path();
find_prolog_files(&mut libraries, "", &lib_path);
libraries.write_all(b"\n m\n };\n}\n").unwrap();
if let Some(file_name) = item.file_name() {
if let Some(ext) = item.extension() {
if ext == "pl" {
let file_stem = item.file_stem().unwrap();
let file_str = file_stem.to_string_lossy().to_uppercase();
let dest = Path::new(&out_dir).join(file_name);
let instructions_path = Path::new(&out_dir).join("instructions.rs");
let mut instructions_file = File::create(&instructions_path).unwrap();
match copy(&item, dest) {
Ok(_) => {},
Err(e) => panic!("die: {:?}", e)
};
let quoted_output = generate_instructions_rs();
let include_line = format!("static {}: &str = include_str!(\"{}.pl\");\n",
file_str, file_stem.to_string_lossy());
instructions_file
.write_all(quoted_output.to_string().as_bytes())
.unwrap();
libraries.write_all(include_line.as_bytes()).unwrap();
library_index.insert(file_stem.to_string_lossy().to_string());
}
}
}
}
Command::new("rustfmt")
.arg(instructions_path.as_os_str())
.spawn().unwrap()
.wait().unwrap();
libraries.write_all(b"\nref_thread_local! {
pub static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
let mut m = IndexMap::new();\n").unwrap();
let static_atoms_path = Path::new(&out_dir).join("static_atoms.rs");
let mut static_atoms_file = File::create(&static_atoms_path).unwrap();
for item in library_index {
let line = format!("\n m.insert(\"{}\", {});", item, item.to_uppercase());
libraries.write_all(line.as_bytes()).unwrap();
}
let quoted_output = index_static_strings(&instructions_path);
libraries.write_all(b"\n\n m\n };
}").unwrap();
static_atoms_file
.write_all(quoted_output.to_string().as_bytes())
.unwrap();
Command::new("rustfmt")
.arg(static_atoms_path.as_os_str())
.spawn().unwrap()
.wait().unwrap();
println!("cargo:rerun-if-changed=src/");
}

129
crates/instructions-template/Cargo.lock generated Normal file
View File

@@ -0,0 +1,129 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "indexmap"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "instructions-template"
version = "0.1.0"
dependencies = [
"indexmap",
"proc-macro2",
"quote",
"strum",
"strum_macros",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "392a54546fda6b7cc663379d0e6ce8b324cf88aecc5a499838e1be9781bdce2e"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustversion"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]]
name = "strum"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb"
[[package]]
name = "strum_macros"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "1.0.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "to-syn-value"
version = "0.1.0"
dependencies = [
"syn",
"to-syn-value_derive",
]
[[package]]
name = "to-syn-value_derive"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"

View File

@@ -0,0 +1,14 @@
[package]
name = "instructions-template"
version = "0.1.0"
edition = "2021"
[dependencies]
indexmap = "*"
proc-macro2 = "*"
quote = "*"
strum = "0.23"
strum_macros = "0.23"
syn = { version = "*", features = ['full', 'visit', 'extra-traits'] }
to-syn-value = { path = "../to-syn-value" }
to-syn-value_derive = { path = "../to-syn-value_derive" }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
[package]
name = "num-rug-adapter"
version = "0.1.5"
authors = ["Marco A L Barbosa <malbarbo@gmail.com>"]
edition = "2021"
description = "An adapter to use num crate where rug is needed."
license = "MIT/Apache-2.0"
repository = "https://github.com/malbarbo/num-rug-adapter"
keywords = ["mathematics", "numerics", "bignum"]
categories = ["api-bindings", "science"]
readme = "README.md"
[dependencies]
libc = "0.2"
num-bigint = "0.2"
num-integer = "0.1.41"
num-rational = "0.2"
num-traits = "0.2"

View File

@@ -0,0 +1,888 @@
use num_bigint::{BigInt, ParseBigIntError};
use num_integer::Integer as _;
use num_rational::BigRational;
use num_traits::{FromPrimitive, Num, Signed, ToPrimitive};
use num_traits::identities::One;
use std::cmp::Ordering;
use std::fmt::{self, Display, Formatter};
use std::ops::*;
use std::str::FromStr;
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Integer(BigInt);
impl Integer {
#[inline]
pub fn new() -> Self {
Integer(BigInt::default())
}
#[inline]
pub fn from_str_radix(s: &str, radix: u32) -> Result<Self, ParseBigIntError> {
BigInt::from_str_radix(s, radix).map(Integer)
}
#[inline]
pub fn to_u8(&self) -> Option<u8> {
self.0.to_u8()
}
#[inline]
pub fn to_u32(&self) -> Option<u32> {
self.0.to_u32()
}
#[inline]
pub fn to_u64(&self) -> Option<u64> {
self.0.to_u64()
}
#[inline]
pub fn to_usize(&self) -> Option<usize> {
self.0.to_usize()
}
#[inline]
pub fn to_i32(&self) -> Option<i32> {
self.0.to_i32()
}
#[inline]
pub fn to_isize(&self) -> Option<isize> {
self.0.to_isize()
}
#[inline]
pub fn to_f64(&self) -> f64 {
self.0.to_f64().unwrap()
}
#[inline]
pub fn abs(&self) -> Self {
Integer(self.0.abs())
}
#[inline]
pub fn abs_ref(&self) -> Self {
Integer(self.0.abs())
}
#[inline]
pub fn div_rem(&self, other: Self) -> (Self, Self) {
let (a, b) = num_integer::Integer::div_rem(&self.0, &other.0);
(Integer(a), Integer(b))
}
#[inline]
pub fn div_rem_ref(&self, other: &Self) -> (Self, Self) {
let (a, b) = num_integer::Integer::div_rem(&self.0, &other.0);
(Integer(a), Integer(b))
}
#[inline]
pub fn div_rem_floor(&self, other: Self) -> (Self, Self) {
let (a, b) = num_integer::Integer::div_mod_floor(&self.0, &other.0);
(Integer(a), Integer(b))
}
#[inline]
pub fn div_rem_floor_ref(&self, other: &Self) -> (Self, Self) {
let (a, b) = num_integer::Integer::div_mod_floor(&self.0, &other.0);
(Integer(a), Integer(b))
}
#[inline]
pub fn mod_u(&self, modulo: u32) -> u32 {
(self.0.abs() % modulo).to_u32().unwrap()
}
#[inline]
pub fn is_odd(&self) -> bool {
num_integer::Integer::is_odd(&self.0)
}
#[inline]
pub fn from_f64(v: f64) -> Option<Self> {
BigInt::from_f64(v).map(Integer)
}
#[inline]
pub fn gcd_ref(&self, other: &Self) -> Self {
Integer(num_integer::Integer::gcd(&self.0, &other.0))
}
#[inline]
pub fn gcd(&self, other: &Self) -> Self {
Integer(num_integer::Integer::gcd(&self.0, &other.0))
}
#[inline]
pub fn count_ones(&self) -> Option<u32> {
Some(self.0.to_u32_digits().1.iter().map(|&d| d.count_ones()).sum())
}
}
impl From<&Integer> for Integer {
#[inline]
fn from(s: &Integer) -> Self {
s.clone()
}
}
impl From<i32> for Integer {
#[inline]
fn from(s: i32) -> Self {
Integer(BigInt::from(s))
}
}
impl From<isize> for Integer {
#[inline]
fn from(s: isize) -> Self {
Integer(BigInt::from(s))
}
}
impl From<u8> for Integer {
#[inline]
fn from(s: u8) -> Self {
Integer(BigInt::from(s))
}
}
impl From<u32> for Integer {
#[inline]
fn from(s: u32) -> Self {
Integer(BigInt::from(s))
}
}
impl From<u64> for Integer {
#[inline]
fn from(s: u64) -> Self {
Integer(BigInt::from(s))
}
}
impl From<usize> for Integer {
#[inline]
fn from(s: usize) -> Self {
Integer(BigInt::from(s))
}
}
impl Mul for Integer {
type Output = Integer;
#[inline]
fn mul(self, other: Integer) -> Self::Output {
Integer(self.0 * other.0)
}
}
impl Mul<u32> for Integer {
type Output = Integer;
#[inline]
fn mul(self, other: u32) -> Self::Output {
Integer(self.0 * other)
}
}
impl Mul<&Integer> for Integer {
type Output = Integer;
fn mul(self, other: &Integer) -> Self::Output {
Integer(self.0 * &other.0)
}
}
impl MulAssign<&Integer> for Integer {
#[inline]
fn mul_assign(&mut self, other: &Integer) {
self.0 *= &other.0;
}
}
impl Add for Integer {
type Output = Integer;
#[inline]
fn add(self, other: Integer) -> Self::Output {
Integer(self.0 + other.0)
}
}
impl Add<Integer> for &Integer {
type Output = Integer;
#[inline]
fn add(self, other: Integer) -> Self::Output {
Integer(&self.0 + other.0)
}
}
impl Add<&Integer> for Integer {
type Output = Integer;
#[inline]
fn add(self, other: &Integer) -> Self::Output {
Integer(self.0 + &other.0)
}
}
impl Add<&Integer> for &Integer {
type Output = Integer;
#[inline]
fn add(self, other: &Integer) -> Self::Output {
Integer(&self.0 + &other.0)
}
}
impl AddAssign<i64> for Integer {
#[inline]
fn add_assign(&mut self, other: i64) {
self.0 += other;
}
}
impl AddAssign<&Integer> for Integer {
#[inline]
fn add_assign(&mut self, other: &Integer) {
self.0 += &other.0;
}
}
impl Div for Integer {
type Output = Integer;
#[inline]
fn div(self, other: Integer) -> Integer {
Integer(self.0 / other.0)
}
}
impl Div<&Integer> for Integer {
type Output = Integer;
#[inline]
fn div(self, other: &Integer) -> Integer {
Integer(self.0 / &other.0)
}
}
impl Div<Integer> for &Integer {
type Output = Integer;
#[inline]
fn div(self, other: Integer) -> Integer {
Integer(&self.0 / &other.0)
}
}
impl Shr<u32> for Integer {
type Output = Integer;
#[inline]
fn shr(self, rhs: u32) -> Self::Output {
Integer(self.0 >> rhs as usize)
}
}
impl Shr<u32> for &Integer {
type Output = Integer;
#[inline]
fn shr(self, rhs: u32) -> Self::Output {
Integer(&self.0 >> rhs as usize)
}
}
impl ShrAssign<u32> for Integer {
#[inline]
fn shr_assign(&mut self, rhs: u32) {
self.0 >>= rhs as usize;
}
}
impl Shl<u32> for Integer {
type Output = Integer;
#[inline]
fn shl(self, rhs: u32) -> Self::Output {
Integer(self.0 << rhs as usize)
}
}
impl Shl<u32> for &Integer {
type Output = Integer;
#[inline]
fn shl(self, rhs: u32) -> Self::Output {
Integer(&self.0 << rhs as usize)
}
}
impl Not for Integer {
type Output = Integer;
#[inline]
fn not(self) -> Self::Output {
Integer(!self.0)
}
}
impl Not for &Integer {
type Output = Integer;
#[inline]
fn not(self) -> Self::Output {
Integer(!&self.0)
}
}
impl Rem for Integer {
type Output = Integer;
#[inline]
fn rem(self, other: Integer) -> Self::Output {
Integer(self.0.mod_floor(&other.0))
}
}
impl Rem<&Integer> for Integer {
type Output = Integer;
#[inline]
fn rem(self, other: &Integer) -> Self::Output {
Integer(self.0.mod_floor(&other.0))
}
}
impl Rem<&Integer> for &Integer {
type Output = Integer;
#[inline]
fn rem(self, other: &Integer) -> Self::Output {
Integer(self.0.mod_floor(&other.0))
}
}
impl Rem<Integer> for &Integer {
type Output = Integer;
#[inline]
fn rem(self, other: Integer) -> Self::Output {
Integer(self.0.mod_floor(&other.0))
}
}
impl BitAnd for Integer {
type Output = Integer;
#[inline]
fn bitand(self, other: Integer) -> Self::Output {
Integer(self.0 & &other.0)
}
}
impl BitAnd<&Integer> for Integer {
type Output = Integer;
#[inline]
fn bitand(self, other: &Integer) -> Self::Output {
Integer(self.0 & &other.0)
}
}
impl BitAnd<Integer> for &Integer {
type Output = Integer;
#[inline]
fn bitand(self, other: Integer) -> Self::Output {
Integer(&self.0 & other.0)
}
}
impl BitAnd for &Integer {
type Output = Integer;
#[inline]
fn bitand(self, other: &Integer) -> Self::Output {
Integer(&self.0 & &other.0)
}
}
impl BitOr for Integer {
type Output = Integer;
#[inline]
fn bitor(self, other: Integer) -> Self::Output {
Integer(self.0 | other.0)
}
}
impl BitOr<&Integer> for Integer {
type Output = Integer;
#[inline]
fn bitor(self, other: &Integer) -> Self::Output {
Integer(self.0 | &other.0)
}
}
impl BitOr<Integer> for &Integer {
type Output = Integer;
#[inline]
fn bitor(self, other: Integer) -> Self::Output {
Integer(&self.0 | other.0)
}
}
impl BitOr for &Integer {
type Output = Integer;
#[inline]
fn bitor(self, other: &Integer) -> Self::Output {
Integer(&self.0 | &other.0)
}
}
impl BitXor for Integer {
type Output = Integer;
#[inline]
fn bitxor(self, other: Integer) -> Self::Output {
Integer(self.0 ^ other.0)
}
}
impl BitXor<&Integer> for Integer {
type Output = Integer;
#[inline]
fn bitxor(self, other: &Integer) -> Self::Output {
Integer(self.0 ^ &other.0)
}
}
impl BitXor<Integer> for &Integer {
type Output = Integer;
#[inline]
fn bitxor(self, other: Integer) -> Self::Output {
Integer(&self.0 ^ other.0)
}
}
impl BitXor<&Integer> for &Integer {
type Output = Integer;
#[inline]
fn bitxor(self, other: &Integer) -> Self::Output {
Integer(&self.0 ^ &other.0)
}
}
impl PartialEq<i32> for Integer {
#[inline]
fn eq(&self, other: &i32) -> bool {
self.0 == BigInt::from(*other)
}
}
impl PartialEq<i64> for Integer {
#[inline]
fn eq(&self, other: &i64) -> bool {
self.0 == BigInt::from(*other)
}
}
impl PartialEq<isize> for Integer {
#[inline]
fn eq(&self, other: &isize) -> bool {
self.0 == BigInt::from(*other)
}
}
impl PartialEq<usize> for Integer {
#[inline]
fn eq(&self, other: &usize) -> bool {
self.0 == BigInt::from(*other)
}
}
impl PartialEq<Integer> for isize {
#[inline]
fn eq(&self, other: &Integer) -> bool {
other.0 == BigInt::from(*self)
}
}
impl PartialOrd<i32> for Integer {
#[inline]
fn partial_cmp(&self, other: &i32) -> Option<Ordering> {
self.0.partial_cmp(&BigInt::from(*other))
}
}
impl PartialOrd<i64> for Integer {
#[inline]
fn partial_cmp(&self, other: &i64) -> Option<Ordering> {
self.0.partial_cmp(&BigInt::from(*other))
}
}
impl PartialOrd<isize> for Integer {
#[inline]
fn partial_cmp(&self, other: &isize) -> Option<Ordering> {
self.0.partial_cmp(&BigInt::from(*other))
}
}
impl PartialOrd<usize> for Integer {
#[inline]
fn partial_cmp(&self, other: &usize) -> Option<Ordering> {
self.0.partial_cmp(&BigInt::from(*other))
}
}
impl FromStr for Integer {
type Err = <BigInt as FromStr>::Err;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Integer(s.parse()?))
}
}
impl Neg for Integer {
type Output = Integer;
#[inline]
fn neg(self) -> Self {
Integer(-self.0)
}
}
impl Display for Integer {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
// Rational
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Rational(BigRational);
impl Rational {
#[inline]
pub fn new() -> Self {
Rational(BigRational::from(BigInt::default()))
}
#[inline]
pub fn from_f64(v: f64) -> Option<Self> {
BigRational::from_f64(v).map(Rational)
}
#[inline]
pub fn to_f64(&self) -> f64 {
self.0.numer().to_f64().unwrap() / self.0.denom().to_f64().unwrap()
}
#[inline]
pub fn numer(&self) -> &Integer {
unsafe { ::std::mem::transmute(self.0.numer()) }
}
#[inline]
pub fn denom(&self) -> &Integer {
unsafe { ::std::mem::transmute(self.0.denom()) }
}
#[inline]
pub fn abs(self) -> Self {
Rational(self.0.abs())
}
#[inline]
pub fn abs_ref(&self) -> Self {
Rational(self.0.abs())
}
#[inline]
pub fn fract_floor_ref(&self) -> &Self {
panic!()
}
}
impl From<isize> for Rational {
#[inline]
fn from(s: isize) -> Self {
Rational(BigRational::new_raw(BigInt::from(s), One::one()))
}
}
impl From<&Integer> for Rational {
#[inline]
fn from(s: &Integer) -> Self {
Rational::from(s.clone())
}
}
impl From<&Rational> for Rational {
#[inline]
fn from(s: &Rational) -> Self {
s.clone()
}
}
impl From<Integer> for Rational {
#[inline]
fn from(i: Integer) -> Self {
Rational(BigRational::from(i.0))
}
}
impl Add for Rational {
type Output = Rational;
#[inline]
fn add(self, other: Rational) -> Self::Output {
Rational(self.0 + other.0)
}
}
impl Add<&Rational> for Rational {
type Output = Rational;
#[inline]
fn add(self, other: &Rational) -> Self::Output {
Rational(self.0 + &other.0)
}
}
impl PartialEq<i32> for Rational {
#[inline]
fn eq(&self, other: &i32) -> bool {
self.0 == BigRational::from(BigInt::from(*other))
}
}
impl PartialEq<i64> for Rational {
#[inline]
fn eq(&self, other: &i64) -> bool {
self.0 == BigRational::from(BigInt::from(*other))
}
}
impl PartialEq<isize> for Rational {
#[inline]
fn eq(&self, other: &isize) -> bool {
self == &Rational::from(*other)
}
}
impl PartialEq<Rational> for isize {
#[inline]
fn eq(&self, other: &Rational) -> bool {
other == &Rational::from(*self)
}
}
impl PartialOrd<isize> for Rational {
#[inline]
fn partial_cmp(&self, other: &isize) -> Option<Ordering> {
self.0.partial_cmp(&BigRational::from(BigInt::from(*other)))
}
}
impl PartialOrd<i64> for Rational {
#[inline]
fn partial_cmp(&self, other: &i64) -> Option<Ordering> {
self.0.partial_cmp(&BigRational::from(BigInt::from(*other)))
}
}
impl PartialOrd<i32> for Rational {
#[inline]
fn partial_cmp(&self, other: &i32) -> Option<Ordering> {
self.0.partial_cmp(&BigRational::from(BigInt::from(*other)))
}
}
impl Neg for Rational {
type Output = Rational;
#[inline]
fn neg(self) -> Self {
Rational(-self.0)
}
}
impl Mul for Rational {
type Output = Rational;
#[inline]
fn mul(self, other: Rational) -> Self::Output {
Rational(self.0 * other.0)
}
}
impl Mul<&Rational> for Rational {
type Output = Rational;
fn mul(self, other: &Rational) -> Self::Output {
Rational(self.0 * &other.0)
}
}
impl Div for Rational {
type Output = Rational;
#[inline]
fn div(self, other: Rational) -> Self::Output {
Rational(self.0 / other.0)
}
}
impl Div<&Rational> for &Rational {
type Output = Rational;
#[inline]
fn div(self, other: &Rational) -> Self::Output {
Rational(&self.0 / &other.0)
}
}
impl Display for Rational {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
pub trait Assign<Src = Self> {
fn assign(&mut self, src: Src);
}
impl Assign<&Rational> for (&mut Rational, &mut Integer) {
fn assign(&mut self, _src: &Rational) {
panic!()
}
}
pub mod ops {
use super::{Integer, Rational};
pub trait Pow<Rhs> {
type Output;
fn pow(self, rhs: Rhs) -> Self::Output;
}
impl Pow<u32> for Integer {
type Output = Integer;
fn pow(self, rhs: u32) -> Self::Output {
Integer(num_traits::Pow::pow(&self.0, rhs))
}
}
pub trait PowAssign<Rhs> {
fn pow_assign(&mut self, rhs: Rhs);
}
impl PowAssign<u32> for Integer {
fn pow_assign(&mut self, rhs: u32) {
// FIXME: make it efficient
self.0 = num_traits::Pow::pow(&self.0, rhs);
}
}
pub trait NegAssign {
fn neg_assign(&mut self);
}
impl NegAssign for Integer {
fn neg_assign(&mut self) {
self.0 = -std::mem::replace(self, Integer::new()).0;
}
}
impl NegAssign for Rational {
#[inline]
fn neg_assign(&mut self) {
self.0 = -std::mem::replace(self, Rational::new()).0;
}
}
}
pub mod rand {
use super::Integer;
use std::marker::PhantomData;
pub struct RandState<'a>{
_marker: PhantomData<&'a ()>,
}
impl<'a> RandState<'a> {
pub fn new() -> Self {
unsafe { libc::srand(libc::time(std::ptr::null_mut()) as _) };
RandState { _marker: PhantomData }
}
pub fn borrow_mut(&self) -> &Self {
self
}
pub fn bits(&mut self, bits: u32) -> u32 {
assert!(bits <= 32);
(unsafe { libc::rand() } as u32) & (u32::max_value() >> (32 - bits))
}
pub fn seed(&mut self, seed: &Integer) {
unsafe { libc::srand(seed.to_f64() as _)}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::ops::NegAssign;
#[test]
fn bits() {
let mut rand = rand::RandState::new();
for bits in 1..32 {
for _ in 0..100 {
let r = rand.bits(bits);
let max = 1 << bits;
assert!(max > r, "{} > {}", max, r);
}
}
}
#[test]
fn neg_rational() {
let mut x = Rational::from_f64(5.0).unwrap();
let x_neg = Rational::from_f64(-5.0).unwrap();
x.neg_assign();
assert_eq!(x, x_neg);
}
#[test]
fn neg_integer() {
let mut x = Integer::from(5);
let x_neg = Integer::from(-5);
x.neg_assign();
assert_eq!(x, x_neg);
}
}

View File

@@ -0,0 +1,11 @@
[package]
name = "static-string-indexing"
version = "0.1.0"
edition = "2021"
[dependencies]
proc-macro2 = "*"
syn = { version = "*", features = ['full', 'visit', 'extra-traits'] }
indexmap = "*"
walkdir = "2"
quote = "*"

View File

@@ -0,0 +1,177 @@
use proc_macro2::TokenStream;
use syn::*;
use syn::parse::*;
use syn::visit::*;
use indexmap::IndexSet;
struct StaticStrVisitor {
static_strs: IndexSet<String>,
}
impl StaticStrVisitor {
fn new() -> Self {
Self { static_strs: IndexSet::new() }
}
}
struct MacroFnArgs {
args: Vec<Expr>,
}
struct ReadHeapCellExprAndArms {
expr: Expr,
arms: Vec<Arm>,
}
impl Parse for ReadHeapCellExprAndArms {
fn parse(input: ParseStream) -> Result<Self> {
let mut arms = vec![];
let expr = input.parse()?;
input.parse::<Token![,]>()?;
arms.push(input.parse()?);
while !input.is_empty() {
if let Ok(_) = input.parse::<Token![,]>() {}
arms.push(input.parse()?);
}
Ok(ReadHeapCellExprAndArms { expr, arms })
}
}
impl Parse for MacroFnArgs {
fn parse(input: ParseStream) -> Result<Self> {
let mut args = vec![];
if !input.is_empty() {
args.push(input.parse()?);
}
while !input.is_empty() {
if let Ok(_) = input.parse::<Token![,]>() {}
args.push(input.parse()?);
}
Ok(MacroFnArgs { args })
}
}
impl<'ast> Visit<'ast> for StaticStrVisitor {
fn visit_macro(&mut self, m: &'ast Macro) {
let Macro { path, .. } = m;
if path.is_ident("atom") {
if let Some(Lit::Str(string)) = m.parse_body::<Lit>().ok() {
self.static_strs.insert(string.value());
}
} else if path.is_ident("read_heap_cell") {
if let Some(m) = m.parse_body::<ReadHeapCellExprAndArms>().ok() {
self.visit_expr(&m.expr);
for e in m.arms {
self.visit_arm(&e);
}
}
} else {
if let Some(m) = m.parse_body::<MacroFnArgs>().ok() {
for e in m.args {
self.visit_expr(&e);
}
}
}
}
}
pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStream {
use quote::*;
use std::ffi::OsStr;
use std::fs::File;
use std::io::Read;
use walkdir::WalkDir;
fn filter_rust_files(e: &walkdir::DirEntry) -> bool {
if e.path().is_dir() {
return true;
}
e.path().extension().and_then(OsStr::to_str) == Some("rs")
}
let mut visitor = StaticStrVisitor::new();
fn process_filepath(path: &std::path::Path) -> std::result::Result<syn::File, ()> {
let mut src = String::new();
let mut file = match File::open(path) {
Ok(file) => file,
Err(_) => return Err(()),
};
match file.read_to_string(&mut src) {
Ok(_) => {}
Err(e) => {
panic!("error reading file: {:?}", e);
}
}
let syntax = match syn::parse_file(&src) {
Ok(s) => s,
Err(e) => {
panic!("parse error: {} in file {:?}", e, path);
}
};
Ok(syntax)
}
for entry in WalkDir::new("src/")
.into_iter()
.filter_entry(filter_rust_files)
{
let entry = entry.unwrap();
if entry.path().is_dir() {
continue;
}
let syntax = match process_filepath(entry.path()) {
Ok(syntax) => syntax,
Err(_) => continue,
};
visitor.visit_file(&syntax);
}
match process_filepath(instruction_rs_path) {
Ok(syntax) => visitor.visit_file(&syntax),
Err(_) => {}
}
let indices = (0..visitor.static_strs.len()).map(|i| i << 3);
let indices_iter = indices.clone();
let static_strs_len = visitor.static_strs.len();
let static_strs: &Vec<_> = &visitor.static_strs.into_iter().collect();
quote! {
use phf;
static STRINGS: [&'static str; #static_strs_len] = [
#(
#static_strs,
)*
];
#[macro_export]
macro_rules! atom {
#((#static_strs) => { Atom { index: #indices_iter } };)*
}
static STATIC_ATOMS_MAP: phf::Map<&'static str, Atom> = phf::phf_map! {
#(#static_strs => { Atom { index: #indices } },)*
};
}
}

View File

@@ -0,0 +1,10 @@
[package]
name = "to-syn-value"
version = "0.1.0"
authors = ["Mark Thom <markjordanthom@gmail.com>"]
edition = "2021"
publish = false
[dependencies]
syn = { version = "*", features = ['full', 'visit', 'extra-traits'] }
to-syn-value_derive = { path = "../to-syn-value_derive" }

View File

@@ -0,0 +1,3 @@
pub trait ToDeriveInput {
fn to_derive_input() -> syn::DeriveInput;
}

View File

@@ -0,0 +1,14 @@
[package]
name = "to-syn-value_derive"
version = "0.1.0"
authors = ["Mark Thom <markjordanthom@gmail.com>"]
edition = "2021"
publish = false
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "*"
syn = { version = "*", features = ['full', 'visit', 'extra-traits'] }
quote = "*"

View File

@@ -0,0 +1,20 @@
use syn::*;
use quote::*;
#[proc_macro_derive(ToDeriveInput)]
pub fn derive_to_derive_input(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
let ty_name = derive_input.ident.clone();
quote! {
use to_syn_value::*;
impl ToDeriveInput for #ty_name {
fn to_derive_input() -> syn::DeriveInput {
syn::parse_quote! {
#derive_input
}
}
}
}.into()
}

BIN
logo/scryer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

896
logo/scryer.svg Normal file
View 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

View File

@@ -1,41 +1,57 @@
use prolog_parser::ast::*;
use crate::parser::ast::*;
use crate::temp_v;
use crate::prolog::fixtures::*;
use crate::prolog::forms::*;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::targets::*;
use crate::fixtures::*;
use crate::forms::*;
use crate::instructions::*;
use crate::machine::machine_indices::*;
use crate::targets::*;
use std::cell::Cell;
use std::rc::Rc;
pub trait Allocator<'a> {
pub(crate) trait Allocator {
fn new() -> Self;
fn mark_anon_var<Target>(&mut self, _: Level, _: GenContext, _: &mut Vec<Target>)
where
Target: CompilationTarget<'a>;
fn mark_non_var<Target>(&mut self, _: Level, _: GenContext, _: &'a Cell<RegType>, _: &mut Vec<Target>)
where
Target: CompilationTarget<'a>;
fn mark_reserved_var<Target>(
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
&mut self,
_: Rc<Var>,
_: Level,
_: &'a Cell<VarReg>,
_: GenContext,
_: &mut Vec<Target>,
_: RegType,
_: bool,
) where
Target: CompilationTarget<'a>;
fn mark_var<Target>(&mut self, _: Rc<Var>, _: Level, _: &'a Cell<VarReg>, _: GenContext, _: &mut Vec<Target>)
where
Target: CompilationTarget<'a>;
lvl: Level,
context: GenContext,
code: &mut Code,
);
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
&mut self,
lvl: Level,
context: GenContext,
cell: &'a Cell<RegType>,
code: &mut Code,
);
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
&mut self,
var_name: Rc<String>,
lvl: Level,
cell: &'a Cell<VarReg>,
term_loc: GenContext,
code: &mut Code,
r: RegType,
is_new_var: bool,
);
fn mark_var<'a, Target: CompilationTarget<'a>>(
&mut self,
var_name: Rc<String>,
lvl: Level,
cell: &'a Cell<VarReg>,
context: GenContext,
code: &mut Code,
);
fn reset(&mut self);
fn reset_contents(&mut self) {}
fn reset_arg(&mut self, _: usize);
fn reset_at_head(&mut self, _: &Vec<Box<Term>>);
fn reset_arg(&mut self, arg_num: usize);
fn reset_at_head(&mut self, args: &Vec<Term>);
fn advance_arg(&mut self);
@@ -44,10 +60,10 @@ pub trait Allocator<'a> {
fn take_bindings(self) -> AllocVarDict;
fn drain_var_data(
fn drain_var_data<'a>(
&mut self,
vs: VariableFixtures<'a>,
num_of_chunks: usize
num_of_chunks: usize,
) -> VariableFixtures<'a> {
let mut perm_vs = VariableFixtures::new();
@@ -71,17 +87,17 @@ pub trait Allocator<'a> {
perm_vs
}
fn get(&self, var: Rc<Var>) -> RegType {
fn get(&self, var: Rc<String>) -> RegType {
self.bindings()
.get(&var)
.map_or(temp_v!(0), |v| v.as_reg_type())
}
fn is_unbound(&self, var: Rc<Var>) -> bool {
fn is_unbound(&self, var: Rc<String>) -> bool {
self.get(var).reg_num() == 0
}
fn record_register(&mut self, var: Rc<Var>, r: RegType) {
fn record_register(&mut self, var: Rc<String>, r: RegType) {
match self.bindings_mut().get_mut(&var).unwrap() {
&mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(),
&mut VarData::Perm(ref mut s) => *s = r.reg_num(),

801
src/arena.rs Normal file
View File

@@ -0,0 +1,801 @@
use crate::machine::loader::LiveLoadState;
use crate::machine::machine_indices::*;
use crate::machine::streams::*;
use crate::read::*;
use modular_bitfield::prelude::*;
use ordered_float::OrderedFloat;
use rug::{Integer, Rational};
use std::alloc;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::net::TcpListener;
use std::ops::{Deref, DerefMut};
use std::ptr;
#[macro_export]
macro_rules! arena_alloc {
($e:expr, $arena:expr) => {{
let result = $e;
#[allow(unused_unsafe)]
unsafe { $arena.alloc(result) }
}};
}
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq)]
#[bits = 7]
pub enum ArenaHeaderTag {
F64 = 0b01,
Integer = 0b10,
Rational = 0b11,
OssifiedOpDir = 0b0000100,
LiveLoadState = 0b0001000,
InactiveLoadState = 0b1011000,
InputFileStream = 0b10000,
OutputFileStream = 0b10100,
NamedTcpStream = 0b011100,
NamedTlsStream = 0b100000,
ReadlineStream = 0b110000,
StaticStringStream = 0b110100,
ByteStream = 0b111000,
StandardOutputStream = 0b1100,
StandardErrorStream = 0b11000,
NullStream = 0b111100,
TcpListener = 0b1000000,
Dropped = 0b1000100,
}
#[bitfield]
#[derive(Copy, Clone, Debug)]
pub struct ArenaHeader {
size: B56,
m: bool,
tag: ArenaHeaderTag,
}
const_assert!(mem::size_of::<ArenaHeader>() == 8);
impl ArenaHeader {
#[inline]
pub fn build_with(size: u64, tag: ArenaHeaderTag) -> Self {
ArenaHeader::new()
.with_size(size)
.with_tag(tag)
.with_m(false)
}
#[inline]
pub fn get_tag(self) -> ArenaHeaderTag {
self.tag()
}
}
#[derive(Debug, PartialOrd, Ord)]
pub struct TypedArenaPtr<T: ?Sized>(ptr::NonNull<T>);
impl<T: ?Sized + PartialEq> PartialEq for TypedArenaPtr<T> {
fn eq(&self, other: &TypedArenaPtr<T>) -> bool {
self.0 == other.0 || &**self == &**other
}
}
impl<T: ?Sized + PartialEq> Eq for TypedArenaPtr<T> {}
impl<T: ?Sized + Hash> Hash for TypedArenaPtr<T> {
#[inline(always)]
fn hash<H: Hasher>(&self, hasher: &mut H) {
(&*self as &T).hash(hasher)
}
}
impl<T: ?Sized> Clone for TypedArenaPtr<T> {
fn clone(&self) -> Self {
TypedArenaPtr(self.0)
}
}
impl<T: ?Sized> Copy for TypedArenaPtr<T> {}
impl<T: ?Sized> Deref for TypedArenaPtr<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref() }
}
}
impl<T: ?Sized> DerefMut for TypedArenaPtr<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.0.as_mut() }
}
}
impl<T: fmt::Display> fmt::Display for TypedArenaPtr<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", **self)
}
}
impl<T: ?Sized> TypedArenaPtr<T> {
#[inline]
pub const fn new(data: *mut T) -> Self {
unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }
}
#[inline]
pub fn as_ptr(&self) -> *mut T {
self.0.as_ptr()
}
#[inline]
pub fn header_ptr(&self) -> *const ArenaHeader {
let mut ptr = self.as_ptr() as *const u8 as usize;
ptr -= mem::size_of::<*const ArenaHeader>();
ptr as *const ArenaHeader
}
#[inline]
fn header_ptr_mut(&mut self) -> *mut ArenaHeader {
let mut ptr = self.as_ptr() as *const u8 as usize;
ptr -= mem::size_of::<*const ArenaHeader>();
ptr as *mut ArenaHeader
}
#[inline]
pub fn get_mark_bit(&self) -> bool {
unsafe { (*self.header_ptr()).m() }
}
#[inline]
pub fn set_tag(&mut self, tag: ArenaHeaderTag) {
unsafe { (*self.header_ptr_mut()).set_tag(tag); }
}
#[inline]
pub fn get_tag(&self) -> ArenaHeaderTag {
unsafe { (*self.header_ptr()).get_tag() }
}
#[inline]
pub fn mark(&mut self) {
unsafe {
(*self.header_ptr_mut()).set_m(true);
}
}
#[inline]
pub fn unmark(&mut self) {
unsafe {
(*self.header_ptr_mut()).set_m(false);
}
}
}
pub trait ArenaAllocated {
type PtrToAllocated;
fn tag() -> ArenaHeaderTag;
fn size(&self) -> usize;
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated
where
Self: Sized;
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct F64Ptr(pub TypedArenaPtr<OrderedFloat<f64>>);
impl fmt::Display for F64Ptr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", *self)
}
}
impl Deref for F64Ptr {
type Target = TypedArenaPtr<OrderedFloat<f64>>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for F64Ptr {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl ArenaAllocated for OrderedFloat<f64> {
type PtrToAllocated = F64Ptr;
#[inline]
fn tag() -> ArenaHeaderTag {
ArenaHeaderTag::F64
}
#[inline]
fn size(&self) -> usize {
mem::size_of::<Self>()
}
#[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe {
ptr::write(dst, self);
F64Ptr(TypedArenaPtr::new(dst as *mut Self))
}
}
}
impl ArenaAllocated for Integer {
type PtrToAllocated = TypedArenaPtr<Integer>;
#[inline]
fn tag() -> ArenaHeaderTag {
ArenaHeaderTag::Integer
}
#[inline]
fn size(&self) -> usize {
mem::size_of::<Self>()
}
#[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe {
ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self)
}
}
}
impl ArenaAllocated for Rational {
type PtrToAllocated = TypedArenaPtr<Rational>;
#[inline]
fn tag() -> ArenaHeaderTag {
ArenaHeaderTag::Rational
}
#[inline]
fn size(&self) -> usize {
mem::size_of::<Self>()
}
#[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe {
ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self)
}
}
}
impl ArenaAllocated for OssifiedOpDir {
type PtrToAllocated = TypedArenaPtr<OssifiedOpDir>;
#[inline]
fn tag() -> ArenaHeaderTag {
ArenaHeaderTag::OssifiedOpDir
}
#[inline]
fn size(&self) -> usize {
mem::size_of::<Self>()
}
#[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe {
ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self)
}
}
}
impl ArenaAllocated for LiveLoadState {
type PtrToAllocated = TypedArenaPtr<LiveLoadState>;
#[inline]
fn tag() -> ArenaHeaderTag {
ArenaHeaderTag::LiveLoadState
}
#[inline]
fn size(&self) -> usize {
mem::size_of::<Self>()
}
#[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe {
ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self)
}
}
}
impl ArenaAllocated for TcpListener {
type PtrToAllocated = TypedArenaPtr<TcpListener>;
#[inline]
fn tag() -> ArenaHeaderTag {
ArenaHeaderTag::TcpListener
}
#[inline]
fn size(&self) -> usize {
mem::size_of::<Self>()
}
#[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe {
ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self)
}
}
}
#[derive(Clone, Copy, Debug)]
struct AllocSlab {
next: *mut AllocSlab,
header: ArenaHeader,
}
#[derive(Debug)]
pub struct Arena(*mut AllocSlab);
unsafe impl Send for Arena {}
unsafe impl Sync for Arena {}
impl Arena {
#[inline]
pub fn new() -> Self {
Arena(ptr::null_mut())
}
pub unsafe fn alloc<T: ArenaAllocated>(&mut self, value: T) -> T::PtrToAllocated {
let size = value.size() + mem::size_of::<AllocSlab>();
let align = mem::align_of::<AllocSlab>();
let layout = alloc::Layout::from_size_align_unchecked(size, align);
let slab = alloc::alloc(layout) as *mut AllocSlab;
(*slab).next = self.0;
(*slab).header = ArenaHeader::build_with(value.size() as u64, T::tag());
let offset = (*slab).payload_offset();
let result = value.copy_to_arena(offset as *mut T);
self.0 = slab;
result
}
}
unsafe fn drop_slab_in_place(value: &mut AllocSlab) {
use crate::parser::char_reader::CharReader;
match value.header.tag() {
ArenaHeaderTag::Integer => {
ptr::drop_in_place(value.payload_offset::<Integer>());
}
ArenaHeaderTag::Rational => {
ptr::drop_in_place(value.payload_offset::<Rational>());
}
ArenaHeaderTag::InputFileStream => {
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<InputFileStream>>>());
}
ArenaHeaderTag::OutputFileStream => {
ptr::drop_in_place(value.payload_offset::<StreamLayout<OutputFileStream>>());
}
ArenaHeaderTag::NamedTcpStream => {
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<NamedTcpStream>>>());
}
ArenaHeaderTag::NamedTlsStream => {
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<NamedTlsStream>>>());
}
ArenaHeaderTag::ReadlineStream => {
ptr::drop_in_place(value.payload_offset::<StreamLayout<ReadlineStream>>());
}
ArenaHeaderTag::StaticStringStream => {
ptr::drop_in_place(value.payload_offset::<StreamLayout<StaticStringStream>>());
}
ArenaHeaderTag::ByteStream => {
ptr::drop_in_place(value.payload_offset::<StreamLayout<CharReader<ByteStream>>>());
}
ArenaHeaderTag::OssifiedOpDir => {
ptr::drop_in_place(value.payload_offset::<OssifiedOpDir>());
}
ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => {
ptr::drop_in_place(value.payload_offset::<LiveLoadState>());
}
ArenaHeaderTag::Dropped => {
}
ArenaHeaderTag::TcpListener => {
ptr::drop_in_place(value.payload_offset::<TcpListener>());
}
ArenaHeaderTag::StandardOutputStream => {
ptr::drop_in_place(value.payload_offset::<StreamLayout<StandardOutputStream>>());
}
ArenaHeaderTag::StandardErrorStream => {
ptr::drop_in_place(value.payload_offset::<StreamLayout<StandardErrorStream>>());
}
ArenaHeaderTag::F64 | ArenaHeaderTag::NullStream => {
}
}
}
impl Drop for Arena {
fn drop(&mut self) {
let mut ptr = self.0;
while !ptr.is_null() {
unsafe {
let ptr_r = &*ptr;
let layout = alloc::Layout::from_size_align_unchecked(
ptr_r.slab_size(),
mem::align_of::<AllocSlab>(),
);
drop_slab_in_place(&mut *ptr);
let next_ptr = ptr_r.next;
alloc::dealloc(ptr as *mut u8, layout);
ptr = next_ptr;
}
}
self.0 = ptr::null_mut();
}
}
const_assert!(mem::size_of::<AllocSlab>() == 16);
impl AllocSlab {
#[inline]
fn slab_size(&self) -> usize {
self.header.size() as usize + mem::size_of::<AllocSlab>()
}
fn payload_offset<T>(&self) -> *mut T {
let mut ptr = (self as *const AllocSlab) as usize;
ptr += mem::size_of::<AllocSlab>();
ptr as *mut T
}
}
const_assert!(mem::size_of::<OrderedFloat<f64>>() == 8);
#[cfg(test)]
mod tests {
use crate::machine::mock_wam::*;
use crate::machine::partial_string::*;
use ordered_float::OrderedFloat;
use rug::{Integer, Rational};
#[test]
fn float_ptr_cast() {
let mut wam = MockWAM::new();
let f = OrderedFloat(0f64);
let mut fp = arena_alloc!(f, &mut wam.machine_st.arena);
let cell = HeapCellValue::from(fp);
assert_eq!(cell.get_tag(), HeapCellValueTag::F64);
assert_eq!(fp.get_mark_bit(), false);
assert_eq!(**fp, f);
fp.mark();
assert_eq!(fp.get_mark_bit(), true);
read_heap_cell!(cell,
(HeapCellValueTag::F64, ptr) => {
assert_eq!(**ptr, f)
}
_ => { unreachable!() }
);
}
#[test]
fn heap_cell_value_const_cast() {
let mut wam = MockWAM::new();
let const_value = HeapCellValue::from(ConsPtr::build_with(
0x0000_5555_ff00_0431 as *const _,
ConsPtrMaskTag::Cons,
));
match const_value.to_untyped_arena_ptr() {
Some(arena_ptr) => {
assert_eq!(arena_ptr.into_bytes(), const_value.into_bytes());
}
None => {
assert!(false);
}
}
let stream = Stream::from_static_string("test", &mut wam.machine_st.arena);
let stream_cell =
HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons));
match stream_cell.to_untyped_arena_ptr() {
Some(arena_ptr) => {
assert_eq!(arena_ptr.into_bytes(), stream_cell.into_bytes());
}
None => {
assert!(false);
}
}
}
#[test]
fn heap_put_literal_tests() {
let mut wam = MockWAM::new();
// integer
let big_int = 2 * Integer::from(1u64 << 63);
let big_int_ptr: TypedArenaPtr<Integer> = arena_alloc!(big_int, &mut wam.machine_st.arena);
assert!(!big_int_ptr.as_ptr().is_null());
let cell = HeapCellValue::from(Literal::Integer(big_int_ptr));
assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
let untyped_arena_ptr = match cell.to_untyped_arena_ptr() {
Some(ptr) => ptr,
None => {
assert!(false);
unreachable!()
}
};
match_untyped_arena_ptr!(untyped_arena_ptr,
(ArenaHeaderTag::Integer, n) => {
assert_eq!(&*n, &(2 * Integer::from(1u64 << 63)))
}
_ => unreachable!()
);
read_heap_cell!(cell,
(HeapCellValueTag::Cons, cons_ptr) => {
match_untyped_arena_ptr!(cons_ptr,
(ArenaHeaderTag::Integer, n) => {
assert_eq!(&*n, &(2 * Integer::from(1u64 << 63)))
}
_ => { unreachable!() }
)
}
_ => { unreachable!() }
);
// rational
let big_rat = 2 * Rational::from(1u64 << 63);
let big_rat_ptr: TypedArenaPtr<Rational> = arena_alloc!(big_rat, &mut wam.machine_st.arena);
assert!(!big_rat_ptr.as_ptr().is_null());
let rat_cell = typed_arena_ptr_as_cell!(big_rat_ptr);
assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
match rat_cell.to_untyped_arena_ptr() {
Some(untyped_arena_ptr) => {
assert_eq!(
Some(big_rat_ptr.header_ptr()),
Some(untyped_arena_ptr.into()),
);
}
None => {
assert!(false); // we fail.
}
}
// assert_eq!(wam.machine_st.heap[1usize].get_tag(), HeapCellValueTag::Cons);
read_heap_cell!(rat_cell,
(HeapCellValueTag::Cons, cons_ptr) => {
match_untyped_arena_ptr!(cons_ptr,
(ArenaHeaderTag::Rational, n) => {
assert_eq!(&*n, &(2 * Rational::from(1u64 << 63)));
}
_ => unreachable!()
)
}
_ => { unreachable!() }
);
// atom
let f_atom = atom!("f");
let g_atom = atom!("g");
assert_eq!(f_atom.as_str(), "f");
assert_eq!(g_atom.as_str(), "g");
let f_atom_cell = atom_as_cell!(f_atom);
let g_atom_cell = atom_as_cell!(g_atom);
assert_eq!(f_atom_cell.get_tag(), HeapCellValueTag::Atom);
match f_atom_cell.to_atom() {
Some(atom) => {
assert_eq!(f_atom, atom);
assert_eq!(atom.as_str(), "f");
}
None => {
assert!(false);
}
}
read_heap_cell!(f_atom_cell,
(HeapCellValueTag::Atom, (atom, arity)) => {
assert_eq!(f_atom, atom);
assert_eq!(arity, 0);
assert_eq!(atom.as_str(), "f");
}
_ => { unreachable!() }
);
read_heap_cell!(g_atom_cell,
(HeapCellValueTag::Atom, (atom, arity)) => {
assert_eq!(g_atom, atom);
assert_eq!(arity, 0);
assert_eq!(atom.as_str(), "g");
}
_ => { unreachable!() }
);
// complete string
let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "ronan", &mut wam.machine_st.atom_tbl);
let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
assert_eq!(pstr_cell.get_tag(), HeapCellValueTag::PStr);
match pstr_cell.to_pstr() {
Some(pstr) => {
assert_eq!(pstr.as_str_from(0), "ronan");
}
None => {
assert!(false);
}
}
read_heap_cell!(pstr_cell,
(HeapCellValueTag::PStr, pstr_atom) => {
let pstr = PartialString::from(pstr_atom);
assert_eq!(pstr.as_str_from(0), "ronan");
}
_ => { unreachable!() }
);
// fixnum
let fixnum_cell = fixnum_as_cell!(Fixnum::build_with(3));
assert_eq!(fixnum_cell.get_tag(), HeapCellValueTag::Fixnum);
match fixnum_cell.to_fixnum() {
Some(n) => assert_eq!(n.get_num(), 3),
None => assert!(false),
}
read_heap_cell!(fixnum_cell,
(HeapCellValueTag::Fixnum, n) => {
assert_eq!(n.get_num(), 3);
}
_ => { unreachable!() }
);
let fixnum_b_cell = fixnum_as_cell!(Fixnum::build_with(1 << 55));
assert_eq!(fixnum_b_cell.get_tag(), HeapCellValueTag::Fixnum);
match fixnum_b_cell.to_fixnum() {
Some(n) => assert_eq!(n.get_num(), 1 << 55),
None => assert!(false),
}
match Fixnum::build_with_checked(1 << 57) {
Ok(_) => assert!(false),
_ => assert!(true),
}
match Fixnum::build_with_checked(i64::MAX) {
Ok(_) => assert!(false),
_ => assert!(true),
}
match Fixnum::build_with_checked(i64::MIN) {
Ok(_) => assert!(false),
_ => assert!(true),
}
match Fixnum::build_with_checked(-1) {
Ok(n) => assert_eq!(n.get_num(), -1),
_ => assert!(false),
}
match Fixnum::build_with_checked((1 << 56) - 1) {
Ok(n) => assert_eq!(n.get_num(), (1 << 56) - 1),
_ => assert!(false),
}
match Fixnum::build_with_checked(-(1 << 56)) {
Ok(n) => assert_eq!(n.get_num(), -(1 << 56)),
_ => assert!(false),
}
match Fixnum::build_with_checked(-(1 << 56) - 1) {
Ok(_n) => assert!(false),
_ => assert!(true),
}
match Fixnum::build_with_checked(-1) {
Ok(n) => assert_eq!(-n, Fixnum::build_with(1)),
_ => assert!(false),
}
// float
let float = OrderedFloat(3.1415926f64);
let float_ptr = arena_alloc!(float, &mut wam.machine_st.arena);
assert!(!float_ptr.as_ptr().is_null());
let float_cell = typed_arena_ptr_as_cell!(float_ptr);
assert_eq!(cell.get_tag(), HeapCellValueTag::Cons);
match float_cell.to_untyped_arena_ptr() {
Some(untyped_arena_ptr) => {
assert_eq!(Some(float_ptr.header_ptr()), Some(untyped_arena_ptr.into()),);
}
None => {
assert!(false); // we fail.
}
}
// char
let c = 'c';
let char_cell = char_as_cell!(c);
read_heap_cell!(char_cell,
(HeapCellValueTag::Char, c) => {
assert_eq!(c, 'c');
}
_ => { unreachable!() }
);
let c = 'Ћ';
let cyrillic_char_cell = char_as_cell!(c);
read_heap_cell!(cyrillic_char_cell,
(HeapCellValueTag::Char, c) => {
assert_eq!(c, 'Ћ');
}
_ => { unreachable!() }
);
// empty list
let cell = empty_list_as_cell!();
read_heap_cell!(cell,
(HeapCellValueTag::Atom, (el, _arity)) => {
assert_eq!(el.flat_index() as usize, empty_list_as_cell!().get_value());
assert_eq!(el.as_str(), "[]");
}
_ => { unreachable!() }
);
}
}

722
src/arithmetic.rs Normal file
View File

@@ -0,0 +1,722 @@
use crate::allocator::*;
use crate::arena::*;
use crate::atom_table::*;
use crate::fixtures::*;
use crate::forms::*;
use crate::instructions::*;
use crate::iterators::*;
use crate::types::*;
use crate::parser::ast::*;
use crate::parser::rug::ops::PowAssign;
use crate::parser::rug::{Assign, Integer, Rational};
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use ordered_float::*;
use std::cell::Cell;
use std::cmp::{max, min, Ordering};
use std::convert::TryFrom;
use std::f64;
use std::num::FpCategory;
use std::ops::Div;
use std::rc::Rc;
use std::vec::Vec;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ArithmeticTerm {
Reg(RegType),
Interm(usize),
Number(Number),
}
impl ArithmeticTerm {
pub(crate) fn interm_or(&self, interm: usize) -> usize {
if let &ArithmeticTerm::Interm(interm) = self {
interm
} else {
interm
}
}
}
impl Default for ArithmeticTerm {
fn default() -> Self {
ArithmeticTerm::Number(Number::default())
}
}
#[derive(Debug)]
pub(crate) struct ArithInstructionIterator<'a> {
state_stack: Vec<TermIterState<'a>>,
}
pub(crate) type ArithCont = (Code, Option<ArithmeticTerm>);
impl<'a> ArithInstructionIterator<'a> {
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
self.state_stack
.push(TermIterState::subterm_to_state(lvl, term));
}
fn from(term: &'a Term) -> Result<Self, ArithmeticError> {
let state = match term {
Term::AnonVar => return Err(ArithmeticError::UninstantiatedVar),
Term::Clause(cell, name, terms) => match ClauseType::from(*name, terms.len()) {
ct @ ClauseType::Named(..) => {
Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms))
}
ClauseType::Inlined(InlinedClauseType::IsFloat(_)) => {
let ct = ClauseType::Named(1, atom!("float"), CodeIndex::default());
Ok(TermIterState::Clause(Level::Shallow, 0, cell, ct, terms))
}
_ => Err(ArithmeticError::NonEvaluableFunctor(
Literal::Atom(*name),
terms.len(),
)),
}?,
Term::Literal(cell, cons) => TermIterState::Literal(Level::Shallow, cell, cons),
Term::Cons(..) | Term::PartialString(..) => {
return Err(ArithmeticError::NonEvaluableFunctor(
Literal::Atom(atom!(".")),
2,
))
}
Term::Var(cell, var) => TermIterState::Var(Level::Shallow, cell, var.clone()),
};
Ok(ArithInstructionIterator {
state_stack: vec![state],
})
}
}
#[derive(Debug)]
pub(crate) enum ArithTermRef<'a> {
Literal(&'a Literal),
Op(Atom, usize), // name, arity.
Var(Level, &'a Cell<VarReg>, Rc<String>),
}
impl<'a> Iterator for ArithInstructionIterator<'a> {
type Item = Result<ArithTermRef<'a>, ArithmeticError>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(iter_state) = self.state_stack.pop() {
match iter_state {
TermIterState::AnonVar(_) => return Some(Err(ArithmeticError::UninstantiatedVar)),
TermIterState::Clause(lvl, child_num, cell, ct, subterms) => {
let arity = subterms.len();
if child_num == arity {
return Some(Ok(ArithTermRef::Op(ct.name(), arity)));
} else {
self.state_stack.push(TermIterState::Clause(
lvl,
child_num + 1,
cell,
ct,
subterms,
));
self.push_subterm(lvl, &subterms[child_num]);
}
}
TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))),
TermIterState::Var(lvl, cell, var) => {
// the expression is the second argument of an
// is/2 but the iterator can't see that, so the
// level needs to be demoted manually.
return Some(Ok(ArithTermRef::Var(lvl.child_level(), cell, var.clone())));
}
_ => {
return Some(Err(ArithmeticError::NonEvaluableFunctor(
Literal::Atom(atom!(".")),
2,
)));
}
};
}
None
}
}
#[derive(Debug)]
pub(crate) struct ArithmeticEvaluator<'a, TermMarker> {
marker: &'a mut TermMarker,
interm: Vec<ArithmeticTerm>,
interm_c: usize,
}
pub(crate) trait ArithmeticTermIter<'a> {
type Iter: Iterator<Item = Result<ArithTermRef<'a>, ArithmeticError>>;
fn iter(self) -> Result<Self::Iter, ArithmeticError>;
}
impl<'a> ArithmeticTermIter<'a> for &'a Term {
type Iter = ArithInstructionIterator<'a>;
fn iter(self) -> Result<Self::Iter, ArithmeticError> {
ArithInstructionIterator::from(self)
}
}
fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), ArithmeticError> {
match c {
Literal::Fixnum(n) => interm.push(ArithmeticTerm::Number(Number::Fixnum(*n))),
Literal::Integer(n) => interm.push(ArithmeticTerm::Number(Number::Integer(*n))),
Literal::Float(n) => interm.push(ArithmeticTerm::Number(Number::Float(***n))),
Literal::Rational(n) => interm.push(ArithmeticTerm::Number(Number::Rational(*n))),
Literal::Atom(name) if name == &atom!("e") => interm.push(ArithmeticTerm::Number(
Number::Float(OrderedFloat(f64::consts::E)),
)),
Literal::Atom(name) if name == &atom!("pi") => interm.push(ArithmeticTerm::Number(
Number::Float(OrderedFloat(f64::consts::PI)),
)),
Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
Number::Float(OrderedFloat(f64::EPSILON)),
)),
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
}
Ok(())
}
impl<'a, TermMarker: Allocator> ArithmeticEvaluator<'a, TermMarker> {
pub(crate) fn new(marker: &'a mut TermMarker, target_int: usize) -> Self {
ArithmeticEvaluator {
marker,
interm: Vec::new(),
interm_c: target_int,
}
}
fn get_unary_instr(
&self,
name: Atom,
a1: ArithmeticTerm,
t: usize,
) -> Result<Instruction, ArithmeticError> {
match name {
atom!("abs") => Ok(Instruction::Abs(a1, t)),
atom!("-") => Ok(Instruction::Neg(a1, t)),
atom!("+") => Ok(Instruction::Plus(a1, t)),
atom!("cos") => Ok(Instruction::Cos(a1, t)),
atom!("sin") => Ok(Instruction::Sin(a1, t)),
atom!("tan") => Ok(Instruction::Tan(a1, t)),
atom!("log") => Ok(Instruction::Log(a1, t)),
atom!("exp") => Ok(Instruction::Exp(a1, t)),
atom!("sqrt") => Ok(Instruction::Sqrt(a1, t)),
atom!("acos") => Ok(Instruction::ACos(a1, t)),
atom!("asin") => Ok(Instruction::ASin(a1, t)),
atom!("atan") => Ok(Instruction::ATan(a1, t)),
atom!("float") => Ok(Instruction::Float(a1, t)),
atom!("truncate") => Ok(Instruction::Truncate(a1, t)),
atom!("round") => Ok(Instruction::Round(a1, t)),
atom!("ceiling") => Ok(Instruction::Ceiling(a1, t)),
atom!("floor") => Ok(Instruction::Floor(a1, t)),
atom!("sign") => Ok(Instruction::Sign(a1, t)),
atom!("\\") => Ok(Instruction::BitwiseComplement(a1, t)),
_ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 1)),
}
}
fn get_binary_instr(
&self,
name: Atom,
a1: ArithmeticTerm,
a2: ArithmeticTerm,
t: usize,
) -> Result<Instruction, ArithmeticError> {
match name {
atom!("+") => Ok(Instruction::Add(a1, a2, t)),
atom!("-") => Ok(Instruction::Sub(a1, a2, t)),
atom!("/") => Ok(Instruction::Div(a1, a2, t)),
atom!("//") => Ok(Instruction::IDiv(a1, a2, t)),
atom!("max") => Ok(Instruction::Max(a1, a2, t)),
atom!("min") => Ok(Instruction::Min(a1, a2, t)),
atom!("div") => Ok(Instruction::IntFloorDiv(a1, a2, t)),
atom!("rdiv") => Ok(Instruction::RDiv(a1, a2, t)),
atom!("*") => Ok(Instruction::Mul(a1, a2, t)),
atom!("**") => Ok(Instruction::Pow(a1, a2, t)),
atom!("^") => Ok(Instruction::IntPow(a1, a2, t)),
atom!(">>") => Ok(Instruction::Shr(a1, a2, t)),
atom!("<<") => Ok(Instruction::Shl(a1, a2, t)),
atom!("/\\") => Ok(Instruction::And(a1, a2, t)),
atom!("\\/") => Ok(Instruction::Or(a1, a2, t)),
atom!("xor") => Ok(Instruction::Xor(a1, a2, t)),
atom!("mod") => Ok(Instruction::Mod(a1, a2, t)),
atom!("rem") => Ok(Instruction::Rem(a1, a2, t)),
atom!("gcd") => Ok(Instruction::Gcd(a1, a2, t)),
atom!("atan2") => Ok(Instruction::ATan2(a1, a2, t)),
_ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 2)),
}
}
fn incr_interm(&mut self) -> usize {
let temp = self.interm_c;
self.interm.push(ArithmeticTerm::Interm(temp));
self.interm_c += 1;
temp
}
fn instr_from_clause(
&mut self,
name: Atom,
arity: usize,
) -> Result<Instruction, ArithmeticError> {
match arity {
1 => {
let a1 = self.interm.pop().unwrap();
let ninterm = if a1.interm_or(0) == 0 {
self.incr_interm()
} else {
self.interm.push(a1.clone());
a1.interm_or(0)
};
self.get_unary_instr(name, a1, ninterm)
}
2 => {
let a2 = self.interm.pop().unwrap();
let a1 = self.interm.pop().unwrap();
let min_interm = min(a1.interm_or(0), a2.interm_or(0));
let ninterm = if min_interm == 0 {
let max_interm = max(a1.interm_or(0), a2.interm_or(0));
if max_interm == 0 {
self.incr_interm()
} else {
self.interm.push(ArithmeticTerm::Interm(max_interm));
self.interm_c = max_interm + 1;
max_interm
}
} else {
self.interm.push(ArithmeticTerm::Interm(min_interm));
self.interm_c = min_interm + 1;
min_interm
};
self.get_binary_instr(name, a1, a2, ninterm)
}
_ => Err(ArithmeticError::NonEvaluableFunctor(
Literal::Atom(name),
arity,
)),
}
}
pub(crate) fn eval(
&mut self,
src: &'a Term,
term_loc: GenContext,
) -> Result<ArithCont, ArithmeticError>
{
let mut code = vec![];
let mut iter = src.iter()?;
while let Some(term_ref) = iter.next() {
match term_ref? {
ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?,
ArithTermRef::Var(lvl, cell, name) => {
let r = if cell.get().norm().reg_num() == 0 {
let mut getter = || {
use crate::targets::QueryInstruction;
loop {
match self.marker.bindings().get(&name) {
Some(&VarData::Temp(_, t, _)) if t != 0 =>
return RegType::Temp(t),
Some(&VarData::Perm(p)) if p != 0 =>
return RegType::Perm(p),
_ => {
self.marker.mark_var::<QueryInstruction>(
name.clone(),
lvl,
cell,
term_loc,
&mut code,
);
}
}
}
};
getter()
/*
_ => return Err(ArithmeticError::UninstantiatedVar),
*/
} else {
cell.get().norm()
};
self.interm.push(ArithmeticTerm::Reg(r));
}
ArithTermRef::Op(name, arity) => {
code.push(self.instr_from_clause(name, arity)?);
}
}
}
Ok((code, self.interm.pop()))
}
}
// integer division rounding function -- 9.1.3.1.
pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number {
match n {
&Number::Integer(_) | &Number::Fixnum(_) => *n,
&Number::Float(OrderedFloat(f)) => fixnum!(Number, f.floor() as i64, arena),
&Number::Rational(ref r) => {
let r_ref = r.fract_floor_ref();
let (mut fract, mut floor) = (Rational::new(), Integer::new());
(&mut fract, &mut floor).assign(r_ref);
Number::Integer(arena_alloc!(floor, arena))
}
}
}
impl From<Fixnum> for Integer {
#[inline]
fn from(n: Fixnum) -> Integer {
Integer::from(n.get_num())
}
}
// floating point rounding function -- 9.1.4.1.
pub(crate) fn rnd_f(n: &Number) -> f64 {
match n {
&Number::Fixnum(n) => n.get_num() as f64,
&Number::Integer(ref n) => n.to_f64(),
&Number::Float(OrderedFloat(f)) => f,
&Number::Rational(ref r) => r.to_f64(),
}
}
// floating point result function -- 9.1.4.2.
pub(crate) fn result_f<Round>(n: &Number, round: Round) -> Result<f64, EvalError>
where
Round: Fn(&Number) -> f64,
{
let f = rnd_f(n);
classify_float(f, round)
}
fn classify_float<Round>(f: f64, round: Round) -> Result<f64, EvalError>
where
Round: Fn(&Number) -> f64,
{
match f.classify() {
FpCategory::Normal | FpCategory::Zero => Ok(round(&Number::Float(OrderedFloat(f)))),
FpCategory::Infinite => {
let f = round(&Number::Float(OrderedFloat(f)));
if OrderedFloat(f) == OrderedFloat(f64::MAX) {
Ok(f)
} else {
Err(EvalError::FloatOverflow)
}
}
FpCategory::Nan => Err(EvalError::Undefined),
_ => Ok(round(&Number::Float(OrderedFloat(f)))),
}
}
#[inline]
pub(crate) fn float_fn_to_f(n: i64) -> Result<f64, EvalError> {
classify_float(n as f64, rnd_f)
}
#[inline]
pub(crate) fn float_i_to_f(n: &Integer) -> Result<f64, EvalError> {
classify_float(n.to_f64(), rnd_f)
}
#[inline]
pub(crate) fn float_r_to_f(r: &Rational) -> Result<f64, EvalError> {
classify_float(r.to_f64(), rnd_f)
}
#[inline]
pub(crate) fn add_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
Ok(OrderedFloat(classify_float(f1 + f2, rnd_f)?))
}
#[inline]
pub(crate) fn mul_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
Ok(OrderedFloat(classify_float(f1 * f2, rnd_f)?))
}
#[inline]
fn div_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
if FpCategory::Zero == f2.classify() {
Err(EvalError::ZeroDivisor)
} else {
Ok(OrderedFloat(classify_float(f1 / f2, rnd_f)?))
}
}
impl Div<Number> for Number {
type Output = Result<Number, EvalError>;
fn div(self, rhs: Number) -> Self::Output {
match (self, rhs) {
(Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
float_fn_to_f(n1.get_num())?,
float_fn_to_f(n2.get_num())?,
)?)),
(Number::Fixnum(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
float_fn_to_f(n1.get_num())?,
float_i_to_f(&n2)?,
)?)),
(Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
float_i_to_f(&n1)?,
float_fn_to_f(n2.get_num())?,
)?)),
(Number::Fixnum(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
float_fn_to_f(n1.get_num())?,
float_r_to_f(&n2)?,
)?)),
(Number::Rational(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
float_r_to_f(&n1)?,
float_fn_to_f(n2.get_num())?,
)?)),
(Number::Fixnum(n1), Number::Float(OrderedFloat(n2))) => {
Ok(Number::Float(div_f(float_fn_to_f(n1.get_num())?, n2)?))
}
(Number::Float(OrderedFloat(n1)), Number::Fixnum(n2)) => {
Ok(Number::Float(div_f(n1, float_fn_to_f(n2.get_num())?)?))
}
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
float_i_to_f(&n1)?,
float_i_to_f(&n2)?,
)?)),
(Number::Integer(n1), Number::Float(OrderedFloat(n2))) => {
Ok(Number::Float(div_f(float_i_to_f(&n1)?, n2)?))
}
(Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
Ok(Number::Float(div_f(n2, float_i_to_f(&n1)?)?))
}
(Number::Integer(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
float_i_to_f(&n1)?,
float_r_to_f(&n2)?,
)?)),
(Number::Rational(n2), Number::Integer(n1)) => Ok(Number::Float(div_f(
float_r_to_f(&n2)?,
float_i_to_f(&n1)?,
)?)),
(Number::Rational(n1), Number::Float(OrderedFloat(n2))) => {
Ok(Number::Float(div_f(float_r_to_f(&n1)?, n2)?))
}
(Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
Ok(Number::Float(div_f(n2, float_r_to_f(&n1)?)?))
}
(Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
Ok(Number::Float(div_f(f1, f2)?))
}
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Float(div_f(
float_r_to_f(&r1)?,
float_r_to_f(&r2)?,
)?)),
}
}
}
impl PartialEq for Number {
fn eq(&self, rhs: &Self) -> bool {
match (self, rhs) {
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.eq(&n2),
(&Number::Fixnum(n1), &Number::Integer(ref n2)) => n1.get_num().eq(&**n2),
(&Number::Integer(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2.get_num()),
(&Number::Fixnum(n1), &Number::Rational(ref n2)) => n1.get_num().eq(&**n2),
(&Number::Rational(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2.get_num()),
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).eq(&n2),
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2.get_num() as f64)),
(&Number::Integer(ref n1), &Number::Integer(ref n2)) => n1.eq(n2),
(&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(n2),
(&Number::Float(n1), &Number::Integer(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64())),
(&Number::Integer(ref n1), &Number::Rational(ref n2)) => {
#[cfg(feature = "num")]
{
&Rational::from(&**n1) == &**n2
}
#[cfg(not(feature = "num"))]
{
&**n1 == &**n2
}
}
(&Number::Rational(ref n1), &Number::Integer(ref n2)) => {
#[cfg(feature = "num")]
{
&**n1 == &Rational::from(&**n2)
}
#[cfg(not(feature = "num"))]
{
&**n1 == &**n2
}
}
(&Number::Rational(ref n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64()).eq(&n2),
(&Number::Float(n1), &Number::Rational(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64())),
(&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2),
(&Number::Rational(ref r1), &Number::Rational(ref r2)) => r1.eq(&r2),
}
}
}
impl Eq for Number {}
impl PartialOrd<usize> for Number {
#[inline]
fn partial_cmp(&self, rhs: &usize) -> Option<Ordering> {
match self {
Number::Fixnum(n) => {
let n = n.get_num();
if n < 0i64 {
Some(Ordering::Less)
} else {
(n as usize).partial_cmp(rhs)
}
}
Number::Integer(n) => (&**n).partial_cmp(rhs),
Number::Rational(r) => (&**r).partial_cmp(rhs),
Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)),
}
}
}
impl PartialEq<usize> for Number {
#[inline]
fn eq(&self, rhs: &usize) -> bool {
match self {
Number::Fixnum(n) => {
let n = n.get_num();
if n < 0i64 {
false
} else {
(n as usize).eq(rhs)
}
}
Number::Integer(n) => (&**n).eq(rhs),
Number::Rational(r) => (&**r).eq(rhs),
Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)),
}
}
}
impl PartialOrd for Number {
fn partial_cmp(&self, rhs: &Number) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
impl Ord for Number {
fn cmp(&self, rhs: &Number) -> Ordering {
match (self, rhs) {
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.get_num().cmp(&n2.get_num()),
(&Number::Fixnum(n1), Number::Integer(n2)) => Integer::from(n1.get_num()).cmp(&*n2),
(Number::Integer(n1), &Number::Fixnum(n2)) => (&**n1).cmp(&Integer::from(n2.get_num())),
(&Number::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1.get_num()).cmp(&*n2),
(Number::Rational(n1), &Number::Fixnum(n2)) => {
(&**n1).cmp(&Rational::from(n2.get_num()))
}
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).cmp(&n2),
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2.get_num() as f64)),
(&Number::Integer(n1), &Number::Integer(n2)) => (*n1).cmp(&*n2),
(&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(n2),
(&Number::Float(n1), &Number::Integer(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64())),
(&Number::Integer(n1), &Number::Rational(n2)) => {
#[cfg(feature = "num")]
{
Rational::from(&**n1).cmp(n2)
}
#[cfg(not(feature = "num"))]
{
(&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less)
}
}
(&Number::Rational(n1), &Number::Integer(n2)) => {
#[cfg(feature = "num")]
{
(&**n1).cmp(&Rational::from(&**n2))
}
#[cfg(not(feature = "num"))]
{
(&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less)
}
}
(&Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(&n2),
(&Number::Float(n1), &Number::Rational(n2)) => n1.cmp(&OrderedFloat(n2.to_f64())),
(&Number::Float(f1), &Number::Float(f2)) => f1.cmp(&f2),
(&Number::Rational(r1), &Number::Rational(r2)) => (*r1).cmp(&*r2),
}
}
}
impl TryFrom<HeapCellValue> for Number {
type Error = ();
#[inline]
fn try_from(value: HeapCellValue) -> Result<Number, Self::Error> {
read_heap_cell!(value,
(HeapCellValueTag::Cons, c) => {
match_untyped_arena_ptr!(c,
(ArenaHeaderTag::F64, n) => {
Ok(Number::Float(*n))
}
(ArenaHeaderTag::Integer, n) => {
Ok(Number::Integer(n))
}
(ArenaHeaderTag::Rational, n) => {
Ok(Number::Rational(n))
}
_ => {
Err(())
}
)
}
(HeapCellValueTag::F64, n) => {
Ok(Number::Float(**n))
}
(HeapCellValueTag::Fixnum, n) => {
Ok(Number::Fixnum(n))
}
_ => {
Err(())
}
)
}
}
// Computes n ^ power. Ignores the sign of power.
pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer {
let mut power = Integer::from(power.abs_ref());
if power == 0 {
return Integer::from(1);
}
let mut oddand = Integer::from(1);
while power > 1 {
if power.is_odd() {
oddand *= &n;
}
n.pow_assign(2);
power >>= 1;
}
n * oddand
}

366
src/atom_table.rs Normal file
View File

@@ -0,0 +1,366 @@
use crate::parser::ast::MAX_ARITY;
use crate::raw_block::*;
use crate::types::*;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ptr;
use std::slice;
use std::str;
use indexmap::IndexSet;
use modular_bitfield::prelude::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Atom {
pub index: usize,
}
const_assert!(mem::size_of::<Atom>() == 8);
include!(concat!(env!("OUT_DIR"), "/static_atoms.rs"));
impl<'a> From<&'a Atom> for Atom {
#[inline]
fn from(atom: &'a Atom) -> Self {
*atom
}
}
impl From<bool> for Atom {
#[inline]
fn from(value: bool) -> Self {
if value { atom!("true") } else { atom!("false") }
}
}
#[cfg(test)]
use std::cell::RefCell;
const ATOM_TABLE_INIT_SIZE: usize = 1 << 16;
const ATOM_TABLE_ALIGN: usize = 8;
#[cfg(test)]
thread_local! {
static ATOM_TABLE_BUF_BASE: RefCell<*const u8> = RefCell::new(ptr::null_mut());
}
#[cfg(not(test))]
static mut ATOM_TABLE_BUF_BASE: *const u8 = ptr::null_mut();
#[cfg(test)]
fn set_atom_tbl_buf_base(ptr: *const u8) {
ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| {
*atom_table_buf_base.borrow_mut() = ptr;
});
}
#[cfg(test)]
pub(crate) fn get_atom_tbl_buf_base() -> *const u8 {
ATOM_TABLE_BUF_BASE.with(|atom_table_buf_base| *atom_table_buf_base.borrow())
}
#[cfg(not(test))]
fn set_atom_tbl_buf_base(ptr: *const u8) {
unsafe {
ATOM_TABLE_BUF_BASE = ptr;
}
}
#[cfg(not(test))]
pub(crate) fn get_atom_tbl_buf_base() -> *const u8 {
unsafe { ATOM_TABLE_BUF_BASE }
}
impl RawBlockTraits for AtomTable {
#[inline]
fn init_size() -> usize {
ATOM_TABLE_INIT_SIZE
}
#[inline]
fn align() -> usize {
ATOM_TABLE_ALIGN
}
}
#[bitfield]
#[derive(Copy, Clone, Debug)]
struct AtomHeader {
#[allow(unused)] m: bool,
len: B50,
#[allow(unused)] padding: B13,
}
impl AtomHeader {
fn build_with(len: u64) -> Self {
AtomHeader::new().with_len(len).with_m(false)
}
}
impl Borrow<str> for Atom {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl Hash for Atom {
#[inline]
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.as_str().hash(hasher)
// hasher.write_usize(self.index)
}
}
#[macro_export]
macro_rules! is_char {
($s:expr) => {
!$s.is_empty() && $s.chars().nth(1).is_none()
};
}
impl Atom {
#[inline]
pub fn buf(self) -> *const u8 {
let ptr = self.as_ptr();
if ptr.is_null() {
return ptr::null();
}
(ptr as usize + mem::size_of::<AtomHeader>()) as *const u8
}
#[inline(always)]
pub fn is_static(self) -> bool {
self.index < STRINGS.len() << 3
}
#[inline(always)]
pub fn as_ptr(self) -> *const u8 {
if self.is_static() {
ptr::null()
} else {
(get_atom_tbl_buf_base() as usize + self.index - (STRINGS.len() << 3)) as *const u8
}
}
#[inline(always)]
pub fn from(index: usize) -> Self {
Self { index }
}
#[inline(always)]
pub fn len(self) -> usize {
if self.is_static() {
STRINGS[self.index >> 3].len()
} else {
unsafe { ptr::read(self.as_ptr() as *const AtomHeader).len() as _ }
}
}
#[inline(always)]
pub fn flat_index(self) -> u64 {
(self.index >> 3) as u64
}
pub fn as_char(self) -> Option<char> {
let s = self.as_str();
let mut it = s.chars();
let c1 = it.next();
let c2 = it.next();
if c2.is_none() { c1 } else { None }
}
#[inline]
pub fn chars(&self) -> str::Chars {
self.as_str().chars()
}
#[inline]
pub fn as_str(&self) -> &str {
unsafe {
let ptr = self.as_ptr();
if ptr.is_null() {
return STRINGS[self.index >> 3];
}
let header = ptr::read::<AtomHeader>(ptr as *const _);
let len = header.len() as usize;
let buf = (ptr as usize + mem::size_of::<AtomHeader>()) as *mut u8;
str::from_utf8_unchecked(slice::from_raw_parts(buf, len))
}
}
pub fn defrock_brackets(&self, atom_tbl: &mut AtomTable) -> Self {
let s = self.as_str();
let s = if s.starts_with('(') && s.ends_with(')') {
&s['('.len_utf8()..s.len() - ')'.len_utf8()]
} else {
return *self;
};
atom_tbl.build_with(s)
}
}
unsafe fn write_to_ptr(string: &str, ptr: *mut u8) {
ptr::write(ptr as *mut _, AtomHeader::build_with(string.len() as u64));
let str_ptr = (ptr as usize + mem::size_of::<AtomHeader>()) as *mut u8;
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr as *mut u8, string.len());
}
impl PartialOrd for Atom {
#[inline]
fn partial_cmp(&self, other: &Atom) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Atom {
#[inline]
fn cmp(&self, other: &Atom) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
#[derive(Debug)]
pub struct AtomTable {
block: RawBlock<AtomTable>,
pub table: IndexSet<Atom>,
}
impl Drop for AtomTable {
fn drop(&mut self) {
self.block.deallocate();
}
}
impl AtomTable {
#[inline]
pub fn new() -> Self {
let table = Self {
block: RawBlock::new(),
table: IndexSet::new(),
};
set_atom_tbl_buf_base(table.block.base);
table
}
#[inline]
pub fn buf(&self) -> *const u8 {
self.block.base as *const u8
}
#[inline]
pub fn top(&self) -> *const u8 {
self.block.top
}
#[inline(always)]
fn lookup_str(&self, string: &str) -> Option<Atom> {
STATIC_ATOMS_MAP.get(string).or_else(|| self.table.get(string)).cloned()
}
pub fn build_with(&mut self, string: &str) -> Atom {
if let Some(atom) = self.lookup_str(string) {
return atom;
}
unsafe {
let size = mem::size_of::<AtomHeader>() + string.len();
let align_offset = 8 * mem::align_of::<AtomHeader>();
let size = (size & !(align_offset - 1)) + align_offset;
let len_ptr = {
let mut ptr;
loop {
ptr = self.block.alloc(size);
if ptr.is_null() {
self.block.grow();
set_atom_tbl_buf_base(self.block.base);
} else {
break;
}
}
ptr
};
let ptr_base = self.block.base as usize;
write_to_ptr(string, len_ptr);
let atom = Atom {
index: (STRINGS.len() << 3) + len_ptr as usize - ptr_base,
};
self.table.insert(atom);
atom
}
}
}
#[bitfield]
#[repr(u64)]
#[derive(Copy, Clone, Debug)]
pub struct AtomCell {
name: B46,
arity: B10,
#[allow(unused)] f: bool,
#[allow(unused)] m: bool,
#[allow(unused)] tag: B6,
}
impl AtomCell {
#[inline]
pub fn build_with(name: u64, arity: u16, tag: HeapCellValueTag) -> Self {
if arity > 0 {
debug_assert!(arity as usize <= MAX_ARITY);
AtomCell::new()
.with_name(name)
.with_arity(arity)
.with_f(false)
.with_tag(tag as u8)
} else {
AtomCell::new()
.with_name(name)
.with_f(false)
.with_tag(tag as u8)
}
}
#[inline]
pub fn get_index(self) -> usize {
self.name() as usize
}
#[inline]
pub fn get_name(self) -> Atom {
Atom::from(self.get_index() << 3)
}
#[inline]
pub fn get_arity(self) -> usize {
self.arity() as usize
}
#[inline]
pub fn get_name_and_arity(self) -> (Atom, usize) {
(Atom::from(self.get_index() << 3), self.get_arity())
}
}

20
src/bin/scryer-prolog.rs Normal file
View File

@@ -0,0 +1,20 @@
fn main() {
use nix::sys::signal;
use scryer_prolog::*;
let handler = signal::SigHandler::Handler(handle_sigint);
unsafe { signal::signal(signal::Signal::SIGINT, handler) }.unwrap();
let mut wam = machine::Machine::new();
wam.run_top_level();
}
pub extern "C" fn handle_sigint(signal: libc::c_int) {
use nix::sys::signal;
use std::sync::atomic::Ordering;
let signal = signal::Signal::from_c_int(signal).unwrap();
if signal == signal::Signal::SIGINT {
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
}
}

1165
src/codegen.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +1,40 @@
use indexmap::IndexMap;
use prolog_parser::ast::*;
use crate::allocator::*;
use crate::fixtures::*;
use crate::forms::Level;
use crate::instructions::*;
use crate::machine::machine_indices::*;
use crate::parser::ast::*;
use crate::targets::CompilationTarget;
use crate::prolog::allocator::*;
use crate::prolog::fixtures::*;
use crate::prolog::forms::*;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::targets::*;
use crate::temp_v;
use fxhash::FxBuildHasher;
use std::cell::Cell;
use std::collections::BTreeSet;
use std::rc::Rc;
pub struct DebrayAllocator {
bindings: IndexMap<Rc<Var>, VarData>,
#[derive(Debug)]
pub(crate) struct DebrayAllocator {
bindings: IndexMap<Rc<String>, VarData, FxBuildHasher>,
arg_c: usize,
temp_lb: usize,
arity: usize, // 0 if not at head.
contents: IndexMap<usize, Rc<Var>>,
contents: IndexMap<usize, Rc<String>, FxBuildHasher>,
in_use: BTreeSet<usize>,
}
impl DebrayAllocator {
fn is_curr_arg_distinct_from(&self, var: &Var) -> bool {
fn is_curr_arg_distinct_from(&self, var: &String) -> bool {
match self.contents.get(&self.arg_c) {
Some(t_var) if **t_var != *var => true,
_ => false,
}
}
fn occurs_shallowly_in_head(&self, var: &Var, r: usize) -> bool {
fn occurs_shallowly_in_head(&self, var: &String, r: usize) -> bool {
match self.bindings.get(var).unwrap() {
&VarData::Temp(_, _, ref tvd) => tvd.use_set.contains(&(GenContext::Head, r)),
_ => false,
@@ -42,7 +47,7 @@ impl DebrayAllocator {
in_use_range || self.in_use.contains(&r)
}
fn alloc_with_cr(&self, var: &Var) -> usize {
fn alloc_with_cr(&self, var: &String) -> usize {
match self.bindings.get(var) {
Some(&VarData::Temp(_, _, ref tvd)) => {
for &(_, reg) in tvd.use_set.iter() {
@@ -68,7 +73,7 @@ impl DebrayAllocator {
}
}
fn alloc_with_ca(&self, var: &Var) -> usize {
fn alloc_with_ca(&self, var: &String) -> usize {
match self.bindings.get(var) {
Some(&VarData::Temp(_, _, ref tvd)) => {
for &(_, reg) in tvd.use_set.iter() {
@@ -96,7 +101,7 @@ impl DebrayAllocator {
}
}
fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Rc<Var>, usize)> {
fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(Rc<String>, usize)> {
// we want to allocate a register to the k^{th} parameter, par_k.
// par_k may not be a temporary variable.
let k = self.arg_c;
@@ -121,10 +126,11 @@ impl DebrayAllocator {
}
}
fn evacuate_arg<'a, Target>(&mut self, chunk_num: usize, target: &mut Vec<Target>)
where
Target: CompilationTarget<'a>,
{
fn evacuate_arg<'a, Target: CompilationTarget<'a>>(
&mut self,
chunk_num: usize,
code: &mut Code,
) {
match self.alloc_in_last_goal_hint(chunk_num) {
Some((var, r)) => {
let k = self.arg_c;
@@ -132,7 +138,7 @@ impl DebrayAllocator {
if r != k {
let r = RegType::Temp(r);
target.push(Target::move_to_register(r, k));
code.push(Target::move_to_register(r, k));
self.contents.swap_remove(&k);
self.contents.insert(r.reg_num(), var.clone());
@@ -147,10 +153,10 @@ impl DebrayAllocator {
fn alloc_reg_to_var<'a, Target>(
&mut self,
var: &Var,
var: &String,
lvl: Level,
term_loc: GenContext,
target: &mut Vec<Target>,
target: &mut Vec<Instruction>,
) -> usize
where
Target: CompilationTarget<'a>,
@@ -158,7 +164,7 @@ impl DebrayAllocator {
match term_loc {
GenContext::Head => {
if let Level::Shallow = lvl {
self.evacuate_arg(0, target);
self.evacuate_arg::<Target>(0, target);
self.alloc_with_cr(var)
} else {
self.alloc_with_ca(var)
@@ -167,7 +173,7 @@ impl DebrayAllocator {
GenContext::Mid(_) => self.alloc_with_ca(var),
GenContext::Last(chunk_num) => {
if let Level::Shallow = lvl {
self.evacuate_arg(chunk_num, target);
self.evacuate_arg::<Target>(chunk_num, target);
self.alloc_with_cr(var)
} else {
self.alloc_with_ca(var)
@@ -191,7 +197,7 @@ impl DebrayAllocator {
final_index
}
fn in_place(&self, var: &Var, term_loc: GenContext, r: RegType, k: usize) -> bool {
fn in_place(&self, var: &String, term_loc: GenContext, r: RegType, k: usize) -> bool {
match term_loc {
GenContext::Head if !r.is_perm() => r.reg_num() == k,
_ => match self.bindings().get(var).unwrap() {
@@ -202,49 +208,49 @@ impl DebrayAllocator {
}
}
impl<'a> Allocator<'a> for DebrayAllocator {
impl Allocator for DebrayAllocator {
fn new() -> DebrayAllocator {
DebrayAllocator {
arity: 0,
arg_c: 1,
temp_lb: 1,
bindings: IndexMap::new(),
contents: IndexMap::new(),
bindings: IndexMap::with_hasher(FxBuildHasher::default()),
contents: IndexMap::with_hasher(FxBuildHasher::default()),
in_use: BTreeSet::new(),
}
}
fn mark_anon_var<Target>(&mut self, lvl: Level, term_loc: GenContext, target: &mut Vec<Target>)
where
Target: CompilationTarget<'a>,
{
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
&mut self,
lvl: Level,
term_loc: GenContext,
code: &mut Code,
) {
let r = RegType::Temp(self.alloc_reg_to_non_var());
match lvl {
Level::Deep => target.push(Target::subterm_to_variable(r)),
Level::Deep => code.push(Target::subterm_to_variable(r)),
Level::Root | Level::Shallow => {
let k = self.arg_c;
if let GenContext::Last(chunk_num) = term_loc {
self.evacuate_arg(chunk_num, target);
self.evacuate_arg::<Target>(chunk_num, code);
}
self.arg_c += 1;
target.push(Target::argument_to_variable(r, k));
code.push(Target::argument_to_variable(r, k));
}
};
}
fn mark_non_var<Target>(
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
&mut self,
lvl: Level,
term_loc: GenContext,
cell: &Cell<RegType>,
target: &mut Vec<Target>,
) where
Target: CompilationTarget<'a>,
{
cell: &'a Cell<RegType>,
code: &mut Code,
) {
let r = cell.get();
let r = match lvl {
@@ -252,7 +258,7 @@ impl<'a> Allocator<'a> for DebrayAllocator {
let k = self.arg_c;
if let GenContext::Last(chunk_num) = term_loc {
self.evacuate_arg(chunk_num, target);
self.evacuate_arg::<Target>(chunk_num, code);
}
self.arg_c += 1;
@@ -268,20 +274,18 @@ impl<'a> Allocator<'a> for DebrayAllocator {
cell.set(r);
}
fn mark_var<Target>(
fn mark_var<'a, Target: CompilationTarget<'a>>(
&mut self,
var: Rc<Var>,
var: Rc<String>,
lvl: Level,
cell: &'a Cell<VarReg>,
term_loc: GenContext,
target: &mut Vec<Target>,
) where
Target: CompilationTarget<'a>,
{
code: &mut Code,
) {
let (r, is_new_var) = match self.get(var.clone()) {
RegType::Temp(0) => {
// here, r is temporary *and* unassigned.
let o = self.alloc_reg_to_var(&var, lvl, term_loc, target);
let o = self.alloc_reg_to_var::<Target>(&var, lvl, term_loc, code);
cell.set(VarReg::Norm(RegType::Temp(o)));
(RegType::Temp(o), true)
@@ -295,27 +299,25 @@ impl<'a> Allocator<'a> for DebrayAllocator {
r => (r, false),
};
self.mark_reserved_var(var, lvl, cell, term_loc, target, r, is_new_var);
self.mark_reserved_var::<Target>(var, lvl, cell, term_loc, code, r, is_new_var);
}
fn mark_reserved_var<Target>(
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
&mut self,
var: Rc<Var>,
var: Rc<String>,
lvl: Level,
cell: &'a Cell<VarReg>,
term_loc: GenContext,
target: &mut Vec<Target>,
code: &mut Code,
r: RegType,
is_new_var: bool,
) where
Target: CompilationTarget<'a>,
{
) {
match lvl {
Level::Root | Level::Shallow => {
let k = self.arg_c;
if self.is_curr_arg_distinct_from(&var) {
self.evacuate_arg(term_loc.chunk_num(), target);
self.evacuate_arg::<Target>(term_loc.chunk_num(), code);
}
self.arg_c += 1;
@@ -324,24 +326,24 @@ impl<'a> Allocator<'a> for DebrayAllocator {
if !self.in_place(&var, term_loc, r, k) {
if is_new_var {
target.push(Target::argument_to_variable(r, k));
code.push(Target::argument_to_variable(r, k));
} else {
target.push(Target::argument_to_value(r, k));
code.push(Target::argument_to_value(r, k));
}
}
}
Level::Deep if is_new_var => {
if let GenContext::Head = term_loc {
if self.occurs_shallowly_in_head(&var, r.reg_num()) {
target.push(Target::subterm_to_value(r));
code.push(Target::subterm_to_value(r));
} else {
target.push(Target::subterm_to_variable(r));
code.push(Target::subterm_to_variable(r));
}
} else {
target.push(Target::subterm_to_variable(r));
code.push(Target::subterm_to_variable(r));
}
}
Level::Deep => target.push(Target::subterm_to_value(r)),
Level::Deep => code.push(Target::subterm_to_value(r)),
};
if !r.is_perm() {
@@ -380,12 +382,12 @@ impl<'a> Allocator<'a> for DebrayAllocator {
self.bindings
}
fn reset_at_head(&mut self, args: &Vec<Box<Term>>) {
fn reset_at_head(&mut self, args: &Vec<Term>) {
self.reset_arg(args.len());
self.arity = args.len();
for (idx, arg) in args.iter().enumerate() {
if let &Term::Var(_, ref var) = arg.as_ref() {
if let &Term::Var(_, ref var) = arg {
let r = self.get(var.clone());
if !r.is_perm() && r.reg_num() == 0 {

View 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)))].
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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 = [],

View 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)
).

View 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).

View File

@@ -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
View 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).

View File

@@ -1,9 +1,8 @@
use prolog_parser::ast::*;
use crate::parser::ast::*;
use crate::prolog::allocator::*;
use crate::prolog::forms::*;
use crate::prolog::instructions::*;
use crate::prolog::iterators::*;
use crate::forms::*;
use crate::instructions::*;
use crate::iterators::*;
use indexmap::{IndexMap, IndexSet};
@@ -14,22 +13,24 @@ use std::rc::Rc;
use std::vec::Vec;
// labeled with chunk numbers.
pub enum VarStatus {
#[derive(Debug)]
pub(crate) enum VarStatus {
Perm(usize),
Temp(usize, TempVarData), // Perm(chunk_num) | Temp(chunk_num, _)
}
pub type OccurrenceSet = BTreeSet<(GenContext, usize)>;
pub(crate) type OccurrenceSet = BTreeSet<(GenContext, usize)>;
// Perm: 0 initially, a stack register once processed.
// Temp: labeled with chunk_num and temp offset (unassigned if 0).
pub enum VarData {
#[derive(Debug)]
pub(crate) enum VarData {
Perm(usize),
Temp(usize, usize, TempVarData),
}
impl VarData {
pub fn as_reg_type(&self) -> RegType {
pub(crate) fn as_reg_type(&self) -> RegType {
match self {
&VarData::Temp(_, r, _) => RegType::Temp(r),
&VarData::Perm(r) => RegType::Perm(r),
@@ -37,15 +38,16 @@ impl VarData {
}
}
pub struct TempVarData {
pub last_term_arity: usize,
pub use_set: OccurrenceSet,
pub no_use_set: BTreeSet<usize>,
pub conflict_set: BTreeSet<usize>,
#[derive(Debug)]
pub(crate) struct TempVarData {
pub(crate) last_term_arity: usize,
pub(crate) use_set: OccurrenceSet,
pub(crate) no_use_set: BTreeSet<usize>,
pub(crate) conflict_set: BTreeSet<usize>,
}
impl TempVarData {
pub fn new(last_term_arity: usize) -> Self {
pub(crate) fn new(last_term_arity: usize) -> Self {
TempVarData {
last_term_arity: last_term_arity,
use_set: BTreeSet::new(),
@@ -54,7 +56,7 @@ impl TempVarData {
}
}
pub fn uses_reg(&self, reg: usize) -> bool {
pub(crate) fn uses_reg(&self, reg: usize) -> bool {
for &(_, nreg) in self.use_set.iter() {
if reg == nreg {
return true;
@@ -64,7 +66,7 @@ impl TempVarData {
return false;
}
pub fn populate_conflict_set(&mut self) {
pub(crate) fn populate_conflict_set(&mut self) {
if self.last_term_arity > 0 {
let arity = self.last_term_arity;
let mut conflict_set: BTreeSet<usize> = (1..arity).collect();
@@ -80,30 +82,30 @@ impl TempVarData {
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
pub struct VariableFixtures<'a>{
perm_vars: IndexMap<Rc<Var>, VariableFixture<'a>>,
last_chunk_temp_vars: IndexSet<Rc<Var>>
#[derive(Debug)]
pub(crate) struct VariableFixtures<'a> {
perm_vars: IndexMap<Rc<String>, VariableFixture<'a>>,
last_chunk_temp_vars: IndexSet<Rc<String>>,
}
impl<'a> VariableFixtures<'a> {
pub fn new() -> Self {
pub(crate) fn new() -> Self {
VariableFixtures {
perm_vars: IndexMap::new(),
last_chunk_temp_vars: IndexSet::new()
last_chunk_temp_vars: IndexSet::new(),
}
}
pub fn insert(&mut self, var: Rc<Var>, vs: VariableFixture<'a>) {
pub(crate) fn insert(&mut self, var: Rc<String>, vs: VariableFixture<'a>) {
self.perm_vars.insert(var, vs);
}
pub fn insert_last_chunk_temp_var(&mut self, var: Rc<Var>) {
pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Rc<String>) {
self.last_chunk_temp_vars.insert(var);
}
// computes no_use and conflict sets for all temp vars.
pub fn populate_restricting_sets(&mut self) {
pub(crate) fn populate_restricting_sets(&mut self) {
// three stages:
// 1. move the use sets of each variable to a local IndexMap, use_set
// (iterate mutably, swap mutable refs).
@@ -113,7 +115,7 @@ impl<'a> VariableFixtures<'a> {
// Compute the conflict set of u.
// 1.
let mut use_sets: IndexMap<Rc<Var>, OccurrenceSet> = IndexMap::new();
let mut use_sets: IndexMap<Rc<String>, OccurrenceSet> = IndexMap::new();
for (var, &mut (ref mut var_status, _)) in self.iter_mut() {
if let &mut VarStatus::Temp(_, ref mut var_data) = var_status {
@@ -151,11 +153,11 @@ impl<'a> VariableFixtures<'a> {
}
}
fn get_mut(&mut self, u: Rc<Var>) -> Option<&mut VariableFixture<'a>> {
fn get_mut(&mut self, u: Rc<String>) -> Option<&mut VariableFixture<'a>> {
self.perm_vars.get_mut(&u)
}
fn iter_mut(&mut self) -> indexmap::map::IterMut<Rc<Var>, VariableFixture<'a>> {
fn iter_mut(&mut self) -> indexmap::map::IterMut<Rc<String>, VariableFixture<'a>> {
self.perm_vars.iter_mut()
}
@@ -168,7 +170,7 @@ impl<'a> VariableFixtures<'a> {
};
}
pub fn vars_above_threshold(&self, index: usize) -> usize {
pub(crate) fn vars_above_threshold(&self, index: usize) -> usize {
let mut var_count = 0;
for &(ref var_status, _) in self.values() {
@@ -182,7 +184,7 @@ impl<'a> VariableFixtures<'a> {
var_count
}
pub fn mark_vars_in_chunk<I>(&mut self, iter: I, lt_arity: usize, term_loc: GenContext)
pub(crate) fn mark_vars_in_chunk<I>(&mut self, iter: I, lt_arity: usize, term_loc: GenContext)
where
I: Iterator<Item = TermRef<'a>>,
{
@@ -191,7 +193,7 @@ impl<'a> VariableFixtures<'a> {
for term_ref in iter {
if let &TermRef::Var(lvl, cell, ref var) = &term_ref {
let mut status = self.perm_vars.remove(var).unwrap_or((
let mut status = self.perm_vars.swap_remove(var).unwrap_or((
VarStatus::Temp(chunk_num, TempVarData::new(lt_arity)),
Vec::new(),
));
@@ -216,19 +218,19 @@ impl<'a> VariableFixtures<'a> {
}
}
pub fn into_iter(self) -> indexmap::map::IntoIter<Rc<Var>, VariableFixture<'a>> {
pub(crate) fn into_iter(self) -> indexmap::map::IntoIter<Rc<String>, VariableFixture<'a>> {
self.perm_vars.into_iter()
}
fn values(&self) -> indexmap::map::Values<Rc<Var>, VariableFixture<'a>> {
fn values(&self) -> indexmap::map::Values<Rc<String>, VariableFixture<'a>> {
self.perm_vars.values()
}
pub fn size(&self) -> usize {
pub(crate) fn size(&self) -> usize {
self.perm_vars.len()
}
pub fn set_perm_vals(&self, has_deep_cuts: bool) {
pub(crate) fn set_perm_vals(&self, has_deep_cuts: bool) {
let mut values_vec: Vec<_> = self
.values()
.filter_map(|ref v| match &v.0 {
@@ -249,68 +251,67 @@ impl<'a> VariableFixtures<'a> {
}
}
pub struct UnsafeVarMarker {
pub unsafe_vars: IndexMap<RegType, bool>,
#[derive(Debug)]
pub(crate) struct UnsafeVarMarker {
pub(crate) unsafe_vars: IndexMap<RegType, usize>,
pub(crate) safe_vars: IndexSet<RegType>,
}
impl UnsafeVarMarker {
pub fn new() -> Self {
pub(crate) fn new() -> Self {
UnsafeVarMarker {
unsafe_vars: IndexMap::new(),
safe_vars: IndexSet::new(),
}
}
pub fn record_unsafe_vars<'a, Alloc: Allocator<'a>>(
&mut self,
fixtures: &VariableFixtures,
marker: &Alloc
) {
for &(_, ref cb) in fixtures.values() {
if let Some(index) = cb.first() {
if !self.unsafe_vars.contains_key(&index.get().norm()) {
self.unsafe_vars.insert(index.get().norm(), false);
}
}
}
for var in fixtures.last_chunk_temp_vars.iter().cloned() {
let r = marker.get(var);
self.unsafe_vars.insert(r, false);
pub(crate) fn from_safe_vars(safe_vars: IndexSet<RegType>) -> Self {
UnsafeVarMarker {
unsafe_vars: IndexMap::new(),
safe_vars,
}
}
pub fn mark_safe_vars(&mut self, query_instr: &QueryInstruction) {
pub(crate) fn mark_safe_vars(&mut self, query_instr: &Instruction) -> bool {
match query_instr {
QueryInstruction::PutVariable(RegType::Temp(r), _) => {
if let Some(found) = self.unsafe_vars.get_mut(&RegType::Temp(*r)) {
*found = true;
}
&Instruction::PutVariable(r @ RegType::Temp(_), _) |
&Instruction::SetVariable(r) => {
self.safe_vars.insert(r);
true
}
QueryInstruction::SetVariable(reg) => {
if let Some(found) = self.unsafe_vars.get_mut(reg) {
*found = true;
}
_ => false,
}
}
pub(crate) fn mark_phase(&mut self, query_instr: &Instruction, phase: usize) {
match query_instr {
&Instruction::PutValue(r @ RegType::Perm(_), _) |
&Instruction::SetValue(r) => {
let p = self.unsafe_vars.entry(r).or_insert(0);
*p = phase;
}
_ => {}
}
}
pub fn mark_unsafe_vars(&mut self, query_instr: &mut QueryInstruction) {
pub(crate) fn mark_unsafe_vars(&mut self, query_instr: &mut Instruction, phase: usize) {
match query_instr {
&mut QueryInstruction::PutValue(RegType::Perm(i), arg) => {
if let Some(found) = self.unsafe_vars.get_mut(&RegType::Perm(i)) {
if !*found {
*found = true;
*query_instr = QueryInstruction::PutUnsafeValue(i, arg);
&mut Instruction::PutValue(RegType::Perm(i), arg) => {
if let Some(p) = self.unsafe_vars.swap_remove(&RegType::Perm(i)) {
if p == phase {
*query_instr = Instruction::PutUnsafeValue(i, arg);
self.safe_vars.insert(RegType::Perm(i));
} else {
self.unsafe_vars.insert(RegType::Perm(i), p);
}
}
}
&mut QueryInstruction::SetValue(reg) => {
if let Some(found) = self.unsafe_vars.get_mut(&reg) {
if !*found {
*found = true;
*query_instr = QueryInstruction::SetLocalValue(reg);
}
&mut Instruction::SetValue(r) => {
if !self.safe_vars.contains(&r) {
*query_instr = Instruction::SetLocalValue(r);
self.safe_vars.insert(r);
self.unsafe_vars.remove(&r);
}
}
_ => {}

885
src/forms.rs Normal file
View File

@@ -0,0 +1,885 @@
use crate::arena::*;
use crate::atom_table::*;
use crate::instructions::*;
use crate::machine::heap::*;
use crate::machine::loader::PredicateQueue;
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use crate::parser::ast::*;
use crate::parser::parser::CompositeOpDesc;
use crate::parser::rug::{Integer, Rational};
use crate::types::*;
use fxhash::FxBuildHasher;
use indexmap::{IndexMap, IndexSet};
use ordered_float::OrderedFloat;
use slice_deque::*;
use std::cell::Cell;
use std::convert::TryFrom;
use std::fmt;
use std::ops::AddAssign;
use std::path::PathBuf;
use std::rc::Rc;
use crate::{is_infix, is_postfix};
pub type PredicateKey = (Atom, usize); // name, arity.
pub type Predicate = Vec<PredicateClause>;
// vars of predicate, toplevel offset. Vec<Term> is always a vector
// of vars (we get their adjoining cells this way).
pub type JumpStub = Vec<Term>;
#[derive(Debug, Clone)]
pub enum TopLevel {
Fact(Term), // Term, line_num, col_num
Predicate(Predicate),
Query(Vec<QueryTerm>),
Rule(Rule), // Rule, line_num, col_num
}
#[derive(Debug, Clone, Copy)]
pub enum AppendOrPrepend {
Append,
Prepend,
}
impl AppendOrPrepend {
#[inline]
pub(crate) fn is_append(self) -> bool {
match self {
AppendOrPrepend::Append => true,
AppendOrPrepend::Prepend => false,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Level {
Deep,
Root,
Shallow,
}
impl Level {
pub(crate) fn child_level(self) -> Level {
match self {
Level::Root => Level::Shallow,
_ => Level::Deep,
}
}
}
#[derive(Debug, Clone)]
pub enum QueryTerm {
// register, clause type, subterms, use default call policy.
Clause(Cell<RegType>, ClauseType, Vec<Term>, bool),
BlockedCut, // a cut which is 'blocked by letters', like the P term in P -> Q.
UnblockedCut(Cell<VarReg>),
GetLevelAndUnify(Cell<VarReg>, Rc<String>),
Jump(JumpStub),
}
impl QueryTerm {
pub(crate) fn set_default_caller(&mut self) {
match self {
&mut QueryTerm::Clause(_, _, _, ref mut use_default_cp) => *use_default_cp = true,
_ => {}
}
}
pub(crate) fn arity(&self) -> usize {
match self {
&QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(),
&QueryTerm::BlockedCut | &QueryTerm::UnblockedCut(..) => 0,
&QueryTerm::Jump(ref vars) => vars.len(),
&QueryTerm::GetLevelAndUnify(..) => 1,
}
}
}
#[derive(Debug, Clone)]
pub struct Rule {
pub(crate) head: (Atom, Vec<Term>, QueryTerm),
pub(crate) clauses: Vec<QueryTerm>,
}
#[derive(Clone, Debug, Hash)]
pub enum ListingSource {
DynamicallyGenerated,
File(Atom, PathBuf), // filename, path
User,
}
impl ListingSource {
pub(crate) fn from_file_and_path(filename: Atom, path_buf: PathBuf) -> Self {
ListingSource::File(filename, path_buf)
}
}
pub trait ClauseInfo {
fn is_consistent(&self, clauses: &PredicateQueue) -> bool {
match clauses.first() {
Some(cl) => {
self.name() == ClauseInfo::name(cl) && self.arity() == ClauseInfo::arity(cl)
}
None => true,
}
}
fn name(&self) -> Option<Atom>;
fn arity(&self) -> usize;
}
impl ClauseInfo for PredicateKey {
#[inline]
fn name(&self) -> Option<Atom> {
Some(self.0)
}
#[inline]
fn arity(&self) -> usize {
self.1
}
}
impl ClauseInfo for Term {
fn name(&self) -> Option<Atom> {
//, atom_tbl: &AtomTable) -> Option<StringBuffer> {
match self {
Term::Clause(_, name, terms) => {
// let str_buf = StringBuffer::from(*name, atom_tbl);
match name.as_str() {
// str_buf.as_str() {
":-" => {
match terms.len() {
1 => None, // a declaration.
2 => terms[0].name(), //.map(|name| StringBuffer::from(name, atom_tbl)),
_ => Some(*name),
}
}
_ => Some(*name), //str_buf),
}
}
Term::Literal(_, Literal::Atom(name)) => Some(*name), //Some(StringBuffer::from(*name, atom_tbl)),
_ => None,
}
}
fn arity(&self) -> usize {
match self {
Term::Clause(_, name, terms) => match name.as_str() {
":-" => match terms.len() {
1 => 0,
2 => terms[0].arity(),
_ => terms.len(),
},
_ => terms.len(),
},
_ => 0,
}
}
}
impl ClauseInfo for Rule {
fn name(&self) -> Option<Atom> {
Some(self.head.0)
}
fn arity(&self) -> usize {
self.head.1.len()
}
}
impl ClauseInfo for PredicateClause {
fn name(&self) -> Option<Atom> {
match self {
&PredicateClause::Fact(ref term, ..) => term.name(),
&PredicateClause::Rule(ref rule, ..) => rule.name(),
}
}
fn arity(&self) -> usize {
match self {
&PredicateClause::Fact(ref term, ..) => term.arity(),
&PredicateClause::Rule(ref rule, ..) => rule.arity(),
}
}
}
#[derive(Debug, Clone)]
pub enum PredicateClause {
Fact(Term),
Rule(Rule),
}
impl PredicateClause {
pub(crate) fn args(&self) -> Option<&[Term]> {
match self {
PredicateClause::Fact(term, ..) => match term {
Term::Clause(_, _, args) => Some(&args),
_ => None,
},
PredicateClause::Rule(rule, ..) => {
if rule.head.1.is_empty() {
None
} else {
Some(&rule.head.1)
}
}
}
}
}
#[derive(Debug)]
pub struct ClauseSpan {
pub left: usize,
pub right: usize,
pub instantiated_arg_index: usize,
}
#[derive(Debug, Clone)]
pub enum ModuleSource {
Library(Atom),
File(Atom),
}
impl ModuleSource {
pub(crate) fn as_functor_stub(&self) -> MachineStub {
match self {
ModuleSource::Library(name) => {
functor!(atom!("library"), [atom(name)])
}
ModuleSource::File(name) => {
functor!(name)
}
}
}
}
#[derive(Clone, Copy, Hash, Debug)]
pub enum MetaSpec {
Minus,
Plus,
Either,
RequiresExpansionWithArgument(usize),
}
#[derive(Debug, Clone)]
pub enum Declaration {
Dynamic(Atom, usize),
MetaPredicate(Atom, Atom, Vec<MetaSpec>), // module name, name, meta-specs
Module(ModuleDecl),
NonCountedBacktracking(Atom, usize), // name, arity
Op(OpDecl),
UseModule(ModuleSource),
UseQualifiedModule(ModuleSource, IndexSet<ModuleExport>),
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub struct OpDecl {
pub(crate) op_desc: OpDesc,
pub(crate) name: Atom,
}
#[inline(always)]
pub(crate) fn fixity(spec: u32) -> Fixity {
match spec {
XFY | XFX | YFX => Fixity::In,
XF | YF => Fixity::Post,
FX | FY => Fixity::Pre,
_ => unreachable!(),
}
}
impl OpDecl {
#[inline]
pub(crate) fn new(op_desc: OpDesc, name: Atom) -> Self {
Self { op_desc, name }
}
#[inline]
pub(crate) fn remove(&mut self, op_dir: &mut OpDir) {
let prec = self.op_desc.get_prec();
self.op_desc.set(0, self.op_desc.get_spec());
self.insert_into_op_dir(op_dir);
self.op_desc.set(prec, self.op_desc.get_spec());
}
pub(crate) fn insert_into_op_dir(&self, op_dir: &mut OpDir) -> Option<OpDesc> {
let key = (self.name, fixity(self.op_desc.get_spec() as u32));
match op_dir.get_mut(&key) {
Some(cell) => {
let (old_prec, old_spec) = cell.get();
cell.set(self.op_desc.get_prec(), self.op_desc.get_spec());
return Some(OpDesc::build_with(old_prec, old_spec));
}
None => {}
}
op_dir.insert(key, self.op_desc)
}
pub(crate) fn submit(
&self,
existing_desc: Option<CompositeOpDesc>,
op_dir: &mut OpDir,
) -> Result<(), SessionError> {
let (spec, name) = (self.op_desc.get_spec(), self.name.clone());
if is_infix!(spec as u32) {
if let Some(desc) = existing_desc {
if desc.post > 0 {
return Err(SessionError::OpIsInfixAndPostFix(name));
}
}
}
if is_postfix!(spec as u32) {
if let Some(desc) = existing_desc {
if desc.inf > 0 {
return Err(SessionError::OpIsInfixAndPostFix(name));
}
}
}
self.insert_into_op_dir(op_dir);
Ok(())
}
}
#[derive(Debug)]
pub enum AtomOrString {
Atom(Atom),
String(String),
}
impl AtomOrString {
#[inline]
pub fn as_str(&self) -> &str {
match self {
AtomOrString::Atom(atom) if atom == &atom!("[]") => "",
AtomOrString::Atom(atom) => atom.as_str(),
AtomOrString::String(string) => string.as_str(),
}
}
#[inline]
pub fn to_string(self) -> String {
match self {
AtomOrString::Atom(atom) => {
atom.as_str().to_owned()
}
AtomOrString::String(string) => {
string
}
}
}
}
pub(crate) fn fetch_atom_op_spec(
name: Atom,
spec: Option<OpDesc>,
op_dir: &OpDir,
) -> Option<OpDesc> {
fetch_op_spec_from_existing(name, 2, spec, op_dir)
.or_else(|| fetch_op_spec_from_existing(name, 1, spec, op_dir))
}
pub(crate) fn fetch_op_spec_from_existing(
name: Atom,
arity: usize,
op_desc: Option<OpDesc>,
op_dir: &OpDir,
) -> Option<OpDesc> {
if let Some(ref op_desc) = &op_desc {
if op_desc.arity() != arity {
/* it's possible to extend operator functors with
* additional terms. When that happens,
* void the op_spec by returning None. */
return None;
}
}
op_desc.or_else(|| fetch_op_spec(name, arity, op_dir))
}
pub(crate) fn fetch_op_spec(name: Atom, arity: usize, op_dir: &OpDir) -> Option<OpDesc> {
match arity {
2 => op_dir.get(&(name, Fixity::In)).and_then(|op_desc| {
if op_desc.get_prec() > 0 {
Some(*op_desc)
} else {
None
}
}),
1 => {
if let Some(op_desc) = op_dir.get(&(name.clone(), Fixity::Pre)) {
if op_desc.get_prec() > 0 {
return Some(*op_desc);
}
}
op_dir.get(&(name, Fixity::Post)).and_then(|op_desc| {
if op_desc.get_prec() > 0 {
Some(*op_desc)
} else {
None
}
})
}
0 => {
fetch_atom_op_spec(name, None, op_dir)
}
_ => None,
}
}
pub(crate) type ModuleDir = IndexMap<Atom, Module, FxBuildHasher>;
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
pub enum ModuleExport {
OpDecl(OpDecl),
PredicateKey(PredicateKey),
}
#[derive(Debug, Clone)]
pub struct ModuleDecl {
pub(crate) name: Atom,
pub(crate) exports: Vec<ModuleExport>,
}
#[derive(Debug)]
pub struct Module {
pub(crate) module_decl: ModuleDecl,
pub(crate) code_dir: CodeDir,
pub(crate) op_dir: OpDir,
pub(crate) meta_predicates: MetaPredicateDir,
pub(crate) extensible_predicates: ExtensiblePredicates,
pub(crate) local_extensible_predicates: LocalExtensiblePredicates,
pub(crate) listing_src: ListingSource,
}
// Module's and related types are defined in forms.
impl Module {
pub(crate) fn new(
module_decl: ModuleDecl,
listing_src: ListingSource,
) -> Self {
Module {
module_decl,
code_dir: CodeDir::with_hasher(FxBuildHasher::default()),
op_dir: default_op_dir(),
meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()),
extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()),
local_extensible_predicates: LocalExtensiblePredicates::with_hasher(
FxBuildHasher::default(),
),
listing_src,
}
}
pub(crate) fn new_in_situ(module_decl: ModuleDecl) -> Self {
Module {
module_decl,
code_dir: CodeDir::with_hasher(FxBuildHasher::default()),
op_dir: OpDir::with_hasher(FxBuildHasher::default()),
meta_predicates: MetaPredicateDir::with_hasher(FxBuildHasher::default()),
extensible_predicates: ExtensiblePredicates::with_hasher(FxBuildHasher::default()),
local_extensible_predicates: LocalExtensiblePredicates::with_hasher(
FxBuildHasher::default()
),
listing_src: ListingSource::DynamicallyGenerated,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum Number {
Float(OrderedFloat<f64>),
Integer(TypedArenaPtr<Integer>),
Rational(TypedArenaPtr<Rational>),
Fixnum(Fixnum),
}
impl Default for Number {
fn default() -> Self {
Number::Fixnum(Fixnum::build_with(0))
}
}
impl fmt::Display for Number {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Number::Float(fl) => write!(f, "{}", fl),
Number::Integer(n) => write!(f, "{}", n),
Number::Rational(r) => write!(f, "{}", r),
Number::Fixnum(n) => write!(f, "{}", n.get_num()),
}
}
}
pub trait ArenaFrom<T> {
fn arena_from(value: T, arena: &mut Arena) -> Self;
}
impl ArenaFrom<Integer> for Number {
#[inline]
fn arena_from(value: Integer, arena: &mut Arena) -> Number {
Number::Integer(arena_alloc!(value, arena))
}
}
impl ArenaFrom<Rational> for Number {
#[inline]
fn arena_from(value: Rational, arena: &mut Arena) -> Number {
Number::Rational(arena_alloc!(value, arena))
}
}
impl ArenaFrom<usize> for Number {
#[inline]
fn arena_from(value: usize, arena: &mut Arena) -> Number {
match i64::try_from(value) {
Ok(value) => Fixnum::build_with_checked(value)
.map(Number::Fixnum)
.unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena))),
Err(_) => Number::Integer(arena_alloc!(Integer::from(value), arena)),
}
}
}
impl ArenaFrom<u64> for Number {
#[inline]
fn arena_from(value: u64, arena: &mut Arena) -> Number {
match i64::try_from(value) {
Ok(value) => Fixnum::build_with_checked(value)
.map(Number::Fixnum)
.unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena))),
Err(_) => Number::Integer(arena_alloc!(Integer::from(value), arena)),
}
}
}
impl ArenaFrom<i64> for Number {
#[inline]
fn arena_from(value: i64, arena: &mut Arena) -> Number {
Fixnum::build_with_checked(value)
.map(Number::Fixnum)
.unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena)))
}
}
impl ArenaFrom<isize> for Number {
#[inline]
fn arena_from(value: isize, arena: &mut Arena) -> Number {
Fixnum::build_with_checked(value as i64)
.map(Number::Fixnum)
.unwrap_or_else(|_| Number::Integer(arena_alloc!(Integer::from(value), arena)))
}
}
impl ArenaFrom<u32> for Number {
#[inline]
fn arena_from(value: u32, _arena: &mut Arena) -> Number {
Number::Fixnum(Fixnum::build_with(value as i64))
}
}
impl ArenaFrom<i32> for Number {
#[inline]
fn arena_from(value: i32, _arena: &mut Arena) -> Number {
Number::Fixnum(Fixnum::build_with(value as i64))
}
}
impl ArenaFrom<Number> for Literal {
#[inline]
fn arena_from(value: Number, arena: &mut Arena) -> Literal {
match value {
Number::Fixnum(n) => Literal::Fixnum(n),
Number::Integer(n) => Literal::Integer(n),
Number::Float(f) => Literal::Float(arena_alloc!(f, arena)),
Number::Rational(r) => Literal::Rational(r),
}
}
}
impl ArenaFrom<Number> for HeapCellValue {
#[inline]
fn arena_from(value: Number, arena: &mut Arena) -> HeapCellValue {
match value {
Number::Fixnum(n) => fixnum_as_cell!(n),
Number::Integer(n) => typed_arena_ptr_as_cell!(n),
Number::Float(n) => typed_arena_ptr_as_cell!(arena_alloc!(n, arena)),
Number::Rational(n) => typed_arena_ptr_as_cell!(n),
}
}
}
impl Number {
#[inline]
pub(crate) fn is_positive(&self) -> bool {
match self {
&Number::Fixnum(n) => n.get_num() > 0,
&Number::Integer(ref n) => &**n > &0,
&Number::Float(f) => f.is_sign_positive(),
&Number::Rational(ref r) => &**r > &0,
}
}
#[inline]
pub(crate) fn is_negative(&self) -> bool {
match self {
&Number::Fixnum(n) => n.get_num() < 0,
&Number::Integer(ref n) => &**n < &0,
&Number::Float(OrderedFloat(f)) => f.is_sign_negative() && OrderedFloat(f) != -0f64,
&Number::Rational(ref r) => &**r < &0,
}
}
#[inline]
pub(crate) fn is_zero(&self) -> bool {
match self {
&Number::Fixnum(n) => n.get_num() == 0,
&Number::Integer(ref n) => &**n == &0,
&Number::Float(f) => f == OrderedFloat(0f64) || f == OrderedFloat(-0f64),
&Number::Rational(ref r) => &**r == &0,
}
}
#[inline]
pub(crate) fn is_integer(&self) -> bool {
match self {
Number::Fixnum(_) | Number::Integer(_) => true,
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub(crate) enum OptArgIndexKey {
Literal(usize, usize, Literal, Vec<Literal>), // index, IndexingCode location, opt arg, alternatives
List(usize, usize), // index, IndexingCode location
None,
Structure(usize, usize, Atom, usize), // index, IndexingCode location, name, arity
}
impl OptArgIndexKey {
#[inline]
pub(crate) fn take(&mut self) -> OptArgIndexKey {
std::mem::replace(self, OptArgIndexKey::None)
}
#[inline]
pub(crate) fn arg_num(&self) -> usize {
match &self {
OptArgIndexKey::Literal(arg_num, ..)
| OptArgIndexKey::Structure(arg_num, ..)
| OptArgIndexKey::List(arg_num, _) => {
// these are always at least 1.
*arg_num
}
OptArgIndexKey::None => 0,
}
}
#[inline]
pub(crate) fn is_some(&self) -> bool {
self.switch_on_term_loc().is_some()
}
#[inline]
pub(crate) fn switch_on_term_loc(&self) -> Option<usize> {
match &self {
OptArgIndexKey::Literal(_, loc, ..)
| OptArgIndexKey::Structure(_, loc, ..)
| OptArgIndexKey::List(_, loc) => Some(*loc),
OptArgIndexKey::None => None,
}
}
#[inline]
pub(crate) fn set_switch_on_term_loc(&mut self, value: usize) {
match self {
OptArgIndexKey::Literal(_, ref mut loc, ..)
| OptArgIndexKey::Structure(_, ref mut loc, ..)
| OptArgIndexKey::List(_, ref mut loc) => {
*loc = value;
}
OptArgIndexKey::None => {}
}
}
}
impl AddAssign<usize> for OptArgIndexKey {
#[inline]
fn add_assign(&mut self, n: usize) {
match self {
OptArgIndexKey::Literal(_, ref mut o, ..)
| OptArgIndexKey::List(_, ref mut o)
| OptArgIndexKey::Structure(_, ref mut o, ..) => {
*o += n;
}
OptArgIndexKey::None => {}
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct ClauseIndexInfo {
pub(crate) clause_start: usize,
pub(crate) opt_arg_index_key: OptArgIndexKey,
}
impl ClauseIndexInfo {
#[inline]
pub(crate) fn new(clause_start: usize) -> Self {
Self {
clause_start,
opt_arg_index_key: OptArgIndexKey::None,
// index_locs: vec![],
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct PredicateInfo {
pub(crate) is_extensible: bool,
pub(crate) is_discontiguous: bool,
pub(crate) is_dynamic: bool,
pub(crate) is_multifile: bool,
pub(crate) has_clauses: bool,
}
impl Default for PredicateInfo {
#[inline]
fn default() -> Self {
PredicateInfo {
is_extensible: false,
is_discontiguous: false,
is_dynamic: false,
is_multifile: false,
has_clauses: false,
}
}
}
impl PredicateInfo {
#[inline]
pub(crate) fn compile_incrementally(&self) -> bool {
let base = self.is_extensible && self.has_clauses;
base && (self.is_discontiguous || self.is_multifile)
}
#[inline]
pub(crate) fn must_retract_local_clauses(&self) -> bool {
self.is_extensible && self.has_clauses && !self.is_discontiguous
}
}
#[derive(Clone, Debug)]
pub(crate) struct LocalPredicateSkeleton {
pub(crate) is_discontiguous: bool,
pub(crate) is_dynamic: bool,
pub(crate) is_multifile: bool,
pub(crate) clause_clause_locs: SliceDeque<usize>,
pub(crate) clause_assert_margin: usize,
pub(crate) retracted_dynamic_clauses: Option<Vec<ClauseIndexInfo>>, // always None if non-dynamic.
}
impl LocalPredicateSkeleton {
#[inline]
pub(crate) fn new() -> Self {
Self {
is_discontiguous: false,
is_dynamic: false,
is_multifile: false,
clause_clause_locs: sdeq![],
clause_assert_margin: 0,
retracted_dynamic_clauses: Some(vec![]),
}
}
#[inline]
pub(crate) fn predicate_info(&self) -> PredicateInfo {
PredicateInfo {
is_extensible: true,
is_discontiguous: self.is_discontiguous,
is_dynamic: self.is_dynamic,
is_multifile: self.is_multifile,
has_clauses: !self.clause_clause_locs.is_empty(),
}
}
#[inline]
pub(crate) fn reset(&mut self) {
self.clause_clause_locs.clear();
self.clause_assert_margin = 0;
}
#[inline]
pub(crate) fn add_retracted_dynamic_clause_info(&mut self, clause_info: ClauseIndexInfo) {
debug_assert_eq!(self.is_dynamic, true);
if self.retracted_dynamic_clauses.is_none() {
self.retracted_dynamic_clauses = Some(vec![]);
}
self.retracted_dynamic_clauses
.as_mut()
.unwrap()
.push(clause_info);
}
}
#[derive(Clone, Debug)]
pub(crate) struct PredicateSkeleton {
pub(crate) core: LocalPredicateSkeleton,
pub(crate) clauses: SliceDeque<ClauseIndexInfo>,
}
impl PredicateSkeleton {
#[inline]
pub(crate) fn new() -> Self {
PredicateSkeleton {
core: LocalPredicateSkeleton::new(),
clauses: sdeq![],
}
}
#[inline]
pub(crate) fn predicate_info(&self) -> PredicateInfo {
PredicateInfo {
is_extensible: true,
is_discontiguous: self.core.is_discontiguous,
is_dynamic: self.core.is_dynamic,
is_multifile: self.core.is_multifile,
has_clauses: !self.clauses.is_empty(),
}
}
pub(crate) fn target_pos_of_clause_clause_loc(
&self,
clause_clause_loc: usize,
) -> Option<usize> {
let search_result = self.core.clause_clause_locs[0..self.core.clause_assert_margin]
.binary_search_by(|loc| clause_clause_loc.cmp(&loc));
match search_result {
Ok(loc) => Some(loc),
Err(_) => self.core.clause_clause_locs[self.core.clause_assert_margin..]
.binary_search_by(|loc| loc.cmp(&clause_clause_loc))
.map(|loc| loc + self.core.clause_assert_margin)
.ok(),
}
}
}

2593
src/heap_iter.rs Normal file

File diff suppressed because it is too large Load Diff

1837
src/heap_print.rs Normal file

File diff suppressed because it is too large Load Diff

1564
src/indexing.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,74 +1,73 @@
use prolog_parser::ast::*;
use crate::prolog::clause_types::*;
use crate::prolog::forms::*;
use crate::prolog::machine::machine_indices::*;
use crate::atom_table::*;
use crate::forms::*;
use crate::instructions::*;
use crate::machine::machine_indices::*;
use crate::parser::ast::*;
use std::cell::Cell;
use std::collections::VecDeque;
use std::fmt;
use std::iter::*;
use std::rc::Rc;
use std::vec::Vec;
#[derive(Clone)]
pub enum TermRef<'a> {
#[derive(Debug, Clone)]
pub(crate) enum TermRef<'a> {
AnonVar(Level),
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
Constant(Level, &'a Cell<RegType>, &'a Constant),
Clause(Level, &'a Cell<RegType>, ClauseType, &'a Vec<Box<Term>>),
Var(Level, &'a Cell<VarReg>, Rc<Var>),
Literal(Level, &'a Cell<RegType>, &'a Literal),
Clause(Level, &'a Cell<RegType>, ClauseType, &'a Vec<Term>),
PartialString(Level, &'a Cell<RegType>, Atom, &'a Option<Box<Term>>),
Var(Level, &'a Cell<VarReg>, Rc<String>),
}
impl<'a> TermRef<'a> {
pub fn level(self) -> Level {
pub(crate) fn level(self) -> Level {
match self {
TermRef::AnonVar(lvl)
| TermRef::Cons(lvl, ..)
| TermRef::Constant(lvl, ..)
| TermRef::Var(lvl, ..)
| TermRef::Clause(lvl, ..) => lvl,
| TermRef::Cons(lvl, ..)
| TermRef::Literal(lvl, ..)
| TermRef::Var(lvl, ..)
| TermRef::Clause(lvl, ..) => lvl,
TermRef::PartialString(lvl, ..) => lvl,
}
}
}
pub enum TermIterState<'a> {
#[derive(Debug)]
pub(crate) enum TermIterState<'a> {
AnonVar(Level),
Constant(Level, &'a Cell<RegType>, &'a Constant),
Clause(
Level,
usize,
&'a Cell<RegType>,
ClauseType,
&'a Vec<Box<Term>>,
),
Literal(Level, &'a Cell<RegType>, &'a Literal),
Clause(Level, usize, &'a Cell<RegType>, ClauseType, &'a Vec<Term>),
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
FinalCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
Var(Level, &'a Cell<VarReg>, Rc<Var>),
InitialPartialString(Level, &'a Cell<RegType>, Atom, &'a Option<Box<Term>>),
FinalPartialString(Level, &'a Cell<RegType>, Atom, &'a Option<Box<Term>>),
Var(Level, &'a Cell<VarReg>, Rc<String>),
}
impl<'a> TermIterState<'a> {
pub fn subterm_to_state(lvl: Level, term: &'a Term) -> TermIterState<'a> {
pub(crate) fn subterm_to_state(lvl: Level, term: &'a Term) -> TermIterState<'a> {
match term {
&Term::AnonVar => TermIterState::AnonVar(lvl),
&Term::Clause(ref cell, ref name, ref subterms, ref spec) => {
let ct = if let Some(spec) = spec {
ClauseType::Op(name.clone(), spec.clone(), CodeIndex::default())
} else {
ClauseType::Named(name.clone(), subterms.len(), CodeIndex::default())
};
Term::AnonVar => TermIterState::AnonVar(lvl),
Term::Clause(cell, name, subterms) => {
let ct = ClauseType::Named(subterms.len(), *name, CodeIndex::default());
TermIterState::Clause(lvl, 0, cell, ct, subterms)
}
&Term::Cons(ref cell, ref head, ref tail) => {
Term::Cons(cell, head, tail) => {
TermIterState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref())
}
&Term::Constant(ref cell, ref constant) => TermIterState::Constant(lvl, cell, constant),
&Term::Var(ref cell, ref var) => TermIterState::Var(lvl, cell, var.clone()),
Term::Literal(cell, constant) => TermIterState::Literal(lvl, cell, constant),
Term::PartialString(cell, string_buf, tail) => {
TermIterState::InitialPartialString(lvl, cell, *string_buf, tail)
}
Term::Var(cell, var) => TermIterState::Var(lvl, cell, var.clone()),
}
}
}
pub struct QueryIterator<'a> {
#[derive(Debug)]
pub(crate) struct QueryIterator<'a> {
state_stack: Vec<TermIterState<'a>>,
}
@@ -78,11 +77,11 @@ impl<'a> QueryIterator<'a> {
.push(TermIterState::subterm_to_state(lvl, term));
}
fn from_rule_head_clause(terms: &'a Vec<Box<Term>>) -> Self {
fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
let state_stack = terms
.iter()
.rev()
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt.as_ref()))
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
.collect();
QueryIterator { state_stack }
@@ -90,30 +89,19 @@ impl<'a> QueryIterator<'a> {
fn from_term(term: &'a Term) -> Self {
let state = match term {
&Term::AnonVar => {
Term::AnonVar | Term::Cons(..) | Term::Literal(..) | Term::PartialString(..) => {
return QueryIterator {
state_stack: vec![],
}
}
&Term::Clause(ref r, ref name, ref terms, ref fixity) => TermIterState::Clause(
Term::Clause(r, name, terms) => TermIterState::Clause(
Level::Root,
0,
r,
ClauseType::from(name.clone(), terms.len(), fixity.clone()),
ClauseType::from(*name, terms.len()),
terms,
),
&Term::Cons(..) => {
return QueryIterator {
state_stack: vec![],
}
}
&Term::Constant(_, _) => {
return QueryIterator {
state_stack: vec![],
}
}
&Term::Var(ref cell, ref var) =>
TermIterState::Var(Level::Root, cell, (*var).clone()),
Term::Var(cell, var) => TermIterState::Var(Level::Root, cell, var.clone()),
};
QueryIterator {
@@ -123,8 +111,8 @@ impl<'a> QueryIterator<'a> {
fn new(term: &'a QueryTerm) -> Self {
match term {
&QueryTerm::Clause(ref cell, ClauseType::CallN, ref terms, _) => {
let state = TermIterState::Clause(Level::Root, 1, cell, ClauseType::CallN, terms);
&QueryTerm::Clause(ref cell, ClauseType::CallN(arity), ref terms, _) => {
let state = TermIterState::Clause(Level::Root, 1, cell, ClauseType::CallN(arity), terms);
QueryIterator {
state_stack: vec![state],
}
@@ -136,7 +124,7 @@ impl<'a> QueryIterator<'a> {
}
}
&QueryTerm::UnblockedCut(ref cell) => {
let state = TermIterState::Var(Level::Root, cell, rc_atom!("!"));
let state = TermIterState::Var(Level::Root, cell, Rc::new("!".to_string()));
QueryIterator {
state_stack: vec![state],
}
@@ -169,20 +157,24 @@ impl<'a> Iterator for QueryIterator<'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::AnonVar(lvl) => {
return Some(TermRef::AnonVar(lvl));
}
TermIterState::Clause(lvl, child_num, cell, ct, child_terms) => {
if child_num == child_terms.len() {
match ct {
ClauseType::CallN => {
self.push_subterm(Level::Shallow, child_terms[0].as_ref())
ClauseType::CallN(_) => {
self.push_subterm(Level::Shallow, &child_terms[0]);
}
ClauseType::Named(..) | ClauseType::Op(..) => {
ClauseType::Named(..) => {
return match lvl {
Level::Root => None,
lvl => Some(TermRef::Clause(lvl, cell, ct, child_terms)),
}
}
_ => return None,
_ => {
return None;
}
};
} else {
self.state_stack.push(TermIterState::Clause(
@@ -192,23 +184,35 @@ impl<'a> Iterator for QueryIterator<'a> {
ct,
child_terms,
));
self.push_subterm(lvl.child_level(), child_terms[child_num].as_ref());
self.push_subterm(lvl.child_level(), &child_terms[child_num]);
}
}
TermIterState::InitialCons(lvl, cell, head, tail) => {
self.state_stack
.push(TermIterState::FinalCons(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));
if let Some(tail) = tail {
self.push_subterm(lvl.child_level(), tail);
}
}
TermIterState::FinalPartialString(lvl, cell, string, tail) => {
return Some(TermRef::PartialString(lvl, cell, string, tail));
}
TermIterState::FinalCons(lvl, cell, head, tail) => {
return Some(TermRef::Cons(lvl, cell, head, tail))
return Some(TermRef::Cons(lvl, cell, head, tail));
}
TermIterState::Constant(lvl, cell, constant) => {
return Some(TermRef::Constant(lvl, cell, constant))
TermIterState::Literal(lvl, cell, constant) => {
return Some(TermRef::Literal(lvl, cell, constant));
}
TermIterState::Var(lvl, cell, var) => {
return Some(TermRef::Var(lvl, cell, var));
}
TermIterState::Var(lvl, cell, var) => return Some(TermRef::Var(lvl, cell, var)),
};
}
@@ -216,7 +220,8 @@ impl<'a> Iterator for QueryIterator<'a> {
}
}
pub struct FactIterator<'a> {
#[derive(Debug)]
pub(crate) struct FactIterator<'a> {
state_queue: VecDeque<TermIterState<'a>>,
iterable_root: bool,
}
@@ -227,10 +232,10 @@ impl<'a> FactIterator<'a> {
.push_back(TermIterState::subterm_to_state(lvl, term));
}
pub fn from_rule_head_clause(terms: &'a Vec<Box<Term>>) -> Self {
pub(crate) fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
let state_queue = terms
.iter()
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt.as_ref()))
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
.collect();
FactIterator {
@@ -241,21 +246,31 @@ impl<'a> FactIterator<'a> {
fn new(term: &'a Term, iterable_root: bool) -> Self {
let states = match term {
&Term::AnonVar => vec![TermIterState::AnonVar(Level::Root)],
&Term::Clause(ref cell, ref name, ref terms, ref fixity) => {
let ct = ClauseType::from(name.clone(), terms.len(), fixity.clone());
Term::AnonVar => {
vec![TermIterState::AnonVar(Level::Root)]
}
Term::Clause(cell, name, terms) => {
let ct = ClauseType::from(*name, terms.len());
vec![TermIterState::Clause(Level::Root, 0, cell, ct, terms)]
}
&Term::Cons(ref cell, ref head, ref tail) => vec![TermIterState::InitialCons(
Term::Cons(cell, head, tail) => vec![TermIterState::InitialCons(
Level::Root,
cell,
head.as_ref(),
tail.as_ref(),
)],
&Term::Constant(ref cell, ref constant) => {
vec![TermIterState::Constant(Level::Root, cell, constant)]
Term::PartialString(cell, string_buf, tail_opt) => {
vec![TermIterState::InitialPartialString(
Level::Root,
cell,
*string_buf,
tail_opt,
)]
}
&Term::Var(ref cell, ref var) => {
Term::Literal(cell, constant) => {
vec![TermIterState::Literal(Level::Root, cell, constant)]
}
Term::Var(cell, var) => {
vec![TermIterState::Var(Level::Root, cell, var.clone())]
}
};
@@ -273,7 +288,9 @@ impl<'a> Iterator for FactIterator<'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::AnonVar(lvl) => {
return Some(TermRef::AnonVar(lvl));
}
TermIterState::Clause(lvl, _, cell, ct, child_terms) => {
for child_term in child_terms {
self.push_subterm(lvl.child_level(), child_term);
@@ -290,10 +307,19 @@ impl<'a> Iterator for FactIterator<'a> {
return Some(TermRef::Cons(lvl, cell, head, tail));
}
TermIterState::Constant(lvl, cell, constant) => {
return Some(TermRef::Constant(lvl, cell, constant))
TermIterState::InitialPartialString(lvl, cell, string_buf, tail_opt) => {
if let Some(tail) = tail_opt {
self.push_subterm(Level::Deep, tail);
}
return Some(TermRef::PartialString(lvl, cell, string_buf, tail_opt));
}
TermIterState::Literal(lvl, cell, constant) => {
return Some(TermRef::Literal(lvl, cell, constant))
}
TermIterState::Var(lvl, cell, var) => {
return Some(TermRef::Var(lvl, cell, var));
}
TermIterState::Var(lvl, cell, var) => return Some(TermRef::Var(lvl, cell, var)),
_ => {}
}
}
@@ -302,27 +328,28 @@ impl<'a> Iterator for FactIterator<'a> {
}
}
pub fn post_order_iter(term: &Term) -> QueryIterator {
pub(crate) fn post_order_iter<'a>(term: &'a Term) -> QueryIterator<'a> {
QueryIterator::from_term(term)
}
pub fn breadth_first_iter(term: &Term, iterable_root: bool) -> FactIterator {
pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: bool) -> FactIterator<'a> {
FactIterator::new(term, iterable_root)
}
pub enum ChunkedTerm<'a> {
HeadClause(ClauseName, &'a Vec<Box<Term>>),
#[derive(Debug)]
pub(crate) enum ChunkedTerm<'a> {
HeadClause(Atom, &'a Vec<Term>),
BodyTerm(&'a QueryTerm),
}
pub fn query_term_post_order_iter<'a>(query_term: &'a QueryTerm) -> QueryIterator<'a> {
pub(crate) fn query_term_post_order_iter<'a>(query_term: &'a QueryTerm) -> QueryIterator<'a> {
QueryIterator::new(query_term)
}
impl<'a> ChunkedTerm<'a> {
pub fn post_order_iter(&self) -> QueryIterator<'a> {
pub(crate) fn post_order_iter(&self) -> QueryIterator<'a> {
match self {
&ChunkedTerm::BodyTerm(ref qt) => QueryIterator::new(qt),
&ChunkedTerm::BodyTerm(qt) => QueryIterator::new(qt),
&ChunkedTerm::HeadClause(_, terms) => QueryIterator::from_rule_head_clause(terms),
}
}
@@ -340,18 +367,30 @@ fn contains_cut_var<'a, Iter: Iterator<Item = &'a Term>>(terms: Iter) -> bool {
false
}
pub struct ChunkedIterator<'a> {
pub chunk_num: usize,
pub(crate) struct ChunkedIterator<'a> {
pub(crate) chunk_num: usize,
iter: Box<dyn Iterator<Item = ChunkedTerm<'a>> + 'a>,
deep_cut_encountered: bool,
cut_var_in_head: bool,
}
impl<'a> fmt::Debug for ChunkedIterator<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ChunkedIterator")
.field("chunk_num", &self.chunk_num)
// Hacky solution.
.field("iter", &"Box<dyn Iterator<Item = ChunkedTerm<'a>> + 'a>")
.field("deep_cut_encountered", &self.deep_cut_encountered)
.field("cut_var_in_head", &self.cut_var_in_head)
.finish()
}
}
type ChunkedIteratorItem<'a> = (usize, usize, Vec<ChunkedTerm<'a>>);
type RuleBodyIteratorItem<'a> = (usize, usize, Vec<&'a QueryTerm>);
impl<'a> ChunkedIterator<'a> {
pub fn rule_body_iter(self) -> Box<dyn Iterator<Item = RuleBodyIteratorItem<'a>> + 'a> {
pub(crate) fn rule_body_iter(self) -> Box<dyn Iterator<Item = RuleBodyIteratorItem<'a>> + 'a> {
Box::new(self.filter_map(|(cn, lt_arity, terms)| {
let filtered_terms: Vec<_> = terms
.into_iter()
@@ -368,17 +407,17 @@ impl<'a> ChunkedIterator<'a> {
}
}))
}
pub fn from_term_sequence(terms: &'a [QueryTerm]) -> Self {
ChunkedIterator {
chunk_num: 0,
iter: Box::new(terms.iter().map(|t| ChunkedTerm::BodyTerm(t))),
deep_cut_encountered: false,
cut_var_in_head: false,
/*
pub(crate) fn from_term_sequence(terms: &'a [QueryTerm]) -> Self {
ChunkedIterator {
chunk_num: 0,
iter: Box::new(terms.iter().map(|t| ChunkedTerm::BodyTerm(t))),
deep_cut_encountered: false,
cut_var_in_head: false,
}
}
}
pub fn from_rule_body(p1: &'a QueryTerm, clauses: &'a Vec<QueryTerm>) -> Self {
*/
pub(crate) fn from_rule_body(p1: &'a QueryTerm, clauses: &'a Vec<QueryTerm>) -> Self {
let inner_iter = Box::new(once(ChunkedTerm::BodyTerm(p1)));
let iter = inner_iter.chain(clauses.iter().map(|t| ChunkedTerm::BodyTerm(t)));
@@ -390,7 +429,7 @@ impl<'a> ChunkedIterator<'a> {
}
}
pub fn from_rule(rule: &'a Rule) -> Self {
pub(crate) fn from_rule(rule: &'a Rule) -> Self {
let &Rule {
head: (ref name, ref args, ref p1),
ref clauses,
@@ -408,7 +447,7 @@ impl<'a> ChunkedIterator<'a> {
}
}
pub fn encountered_deep_cut(&self) -> bool {
pub(crate) fn encountered_deep_cut(&self) -> bool {
self.deep_cut_encountered
}
@@ -420,7 +459,7 @@ impl<'a> ChunkedIterator<'a> {
while let Some(term) = item {
match term {
ChunkedTerm::HeadClause(_, terms) => {
if contains_cut_var(terms.iter().map(|t| t.as_ref())) {
if contains_cut_var(terms.iter()) {
self.cut_var_in_head = true;
}
@@ -450,13 +489,16 @@ impl<'a> ChunkedIterator<'a> {
arity = 1;
break;
}
ChunkedTerm::BodyTerm(&QueryTerm::UnblockedCut(..)) => result.push(term),
ChunkedTerm::BodyTerm(&QueryTerm::UnblockedCut(..)) => {
self.deep_cut_encountered = true;
result.push(term);
}
ChunkedTerm::BodyTerm(&QueryTerm::Clause(_, ClauseType::Inlined(_), ..)) => {
result.push(term)
}
ChunkedTerm::BodyTerm(&QueryTerm::Clause(
_,
ClauseType::CallN,
ClauseType::CallN(_),
ref subterms,
_,
)) => {

View File

@@ -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)
};
}
}

View File

@@ -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
}

View File

@@ -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 }
}
}

View File

@@ -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), <>),
};

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}

View File

@@ -1,7 +0,0 @@
mod l0_parser;
pub mod ast;
pub mod iterators;
pub mod parser;
pub mod codegen;
pub mod machine;

View File

@@ -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
}

View File

@@ -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()
}
}
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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)), <>)
};

File diff suppressed because it is too large Load Diff

View File

@@ -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];
}
}

View File

@@ -1,5 +0,0 @@
pub mod ast;
pub mod iterators;
pub mod l1_parser;
pub mod codegen;
pub mod machine;

View File

@@ -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()
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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(),
};

File diff suppressed because it is too large Load Diff

View File

@@ -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];
}
}

View File

@@ -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;

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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"),
_ => {}
};
}

View File

@@ -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)
}
}

View File

@@ -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(),
};

File diff suppressed because it is too large Load Diff

View File

@@ -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];
}
}

View File

@@ -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;

View File

@@ -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)
}
}

34
src/lib.rs Normal file
View File

@@ -0,0 +1,34 @@
#![recursion_limit = "4112"]
#[macro_use]
extern crate static_assertions;
#[macro_use]
pub mod macros;
#[macro_use]
pub mod atom_table;
#[macro_use]
pub mod arena;
#[macro_use]
pub mod parser;
mod allocator;
mod arithmetic;
pub mod codegen;
mod debray_allocator;
mod fixtures;
mod forms;
mod heap_iter;
pub mod heap_print;
mod indexing;
#[macro_use]
pub mod instructions {
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
}
mod iterators;
pub mod machine;
mod raw_block;
pub mod read;
mod targets;
pub mod types;
use instructions::instr;

121
src/lib/arithmetic.pl Normal file
View File

@@ -0,0 +1,121 @@
:- module(arithmetic, [expmod/4, lsb/2, msb/2, number_to_rational/2,
number_to_rational/3, popcount/2,
rational_numerator_denominator/3]).
:- use_module(library(charsio), [write_term_to_chars/3]).
:- use_module(library(error)).
:- use_module(library(lists), [append/3, member/2]).
expmod(Base, Expo, Mod, R) :-
( member(N, [Base, Expo, Mod]), var(N) -> instantiation_error(expmod/4)
; member(N, [Base, Expo, Mod]), \+ integer(N) ->
type_error(integer, N, expmod/4)
; Expo < 0 -> domain_error(not_less_than_zero, Expo, expmod/4)
; expmod_(Base, Expo, Mod, 1, R)
).
expmod_(_, _, 1, _, 0) :- !.
expmod_(_, 0, _, R, R) :- !.
expmod_(Base0, Expo0, Mod, C0, R) :-
Expo0 /\ 1 =:= 1,
C is (C0 * Base0) mod Mod,
!,
Expo is Expo0 >> 1,
Base is (Base0 * Base0) mod Mod,
expmod_(Base, Expo, Mod, C, R).
expmod_(Base0, Expo0, Mod, C, R) :-
Expo is Expo0 >> 1,
Base is (Base0 * Base0) mod Mod,
expmod_(Base, Expo, Mod, C, R).
lsb(X, N) :-
builtins:must_be_number(X, lsb/2),
( \+ integer(X) -> type_error(integer, X, lsb/2)
; X < 1 -> domain_error(not_less_than_one, X, lsb/2)
; builtins:can_be_number(N, lsb/2),
X1 is X /\ (-X),
msb_(X1, -1, N)
).
msb(X, N) :-
builtins:must_be_number(X, msb/2),
( \+ integer(X) -> type_error(integer, X, msb/2)
; X < 1 -> domain_error(not_less_than_one, X, msb/2)
; builtins:can_be_number(N, msb/2),
X1 is X >> 1,
msb_(X1, 0, N)
).
msb_(0, N, N) :- !.
msb_(X, M, N) :-
X1 is X >> 1,
M1 is M + 1,
msb_(X1, M1, N).
number_to_rational(Real, Fraction) :-
( var(Real) -> instantiation_error(number_to_rational/2)
; integer(Real) -> Fraction is Real rdiv 1
; (rational(Real) ; float(Real)) ->
number_to_rational(1.0e-6, Real, Fraction)
; type_error(number, Real, number_to_rational/2)
).
% If 0 <= Eps0 <= 1e-16 then the search is for "infinite" precision.
number_to_rational(Eps0, Real0, Fraction) :-
( var(Eps0) -> instantiation_error(number_to_rational/3)
; \+ number(Eps0) -> type_error(number, Eps0, number_to_rational/3)
; Eps0 < 0 -> domain_error(not_less_than_zero, Eps0, number_to_rational/3)
; Eps_ is Eps0 rdiv 1,
rational_numerator_denominator(Eps_, EpsN, EpsD),
Eps = EpsN/EpsD
),
( var(Real0) -> instantiation_error(number_to_rational/3)
; \+ number(Real0) -> type_error(number, Eps0, number_to_rational/3)
; Real_ is Real0 rdiv 1,
rational_numerator_denominator(Real_, RealN, RealD),
Real = RealN/RealD
),
E0/E1 = Eps,
P0/Q0 = Real,
( P0 < 0 -> I1 is -1 + P0 // Q0
; I1 is P0 // Q0
),
P1 is P0 mod Q0,
Q1 = Q0,
( P1 =:= 0 -> Fraction is I1 + 0 rdiv 1
; Qn1n is max(P1 * E1 - Q1 * E0, 0),
Qn1d is Q1 * E1,
Qn1 = Qn1n/Qn1d,
Qp1n is P1 * E1 + Q1 * E0,
Qp1d = Qn1d,
Qp1 = Qp1n/Qp1d,
stern_brocot_(Qn1, Qp1, 0/1, 1/0, P2/Q2),
Fraction is I1 + P2 rdiv Q2
),
!.
stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, C/D, Fraction) :-
Fn1 is A + C,
Fd1 is B + D,
simplify_fraction(Fn1/Fd1, Fn/Fd),
S1 is sign(Fn * Qnd - Fd * Qnn),
S2 is sign(Fn * Qpd - Fd * Qpn),
( S1 < 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, Fn/Fd, C/D, Fraction)
; S2 > 0 -> stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, Fn/Fd, Fraction)
; Fraction = Fn/Fd
).
simplify_fraction(A0/B0, A/B) :-
G is gcd(A0, B0),
A is A0 // G,
B is B0 // G.
rational_numerator_denominator(R, N, D) :-
write_term_to_chars(R, [], Cs),
append(Ns, [' ', r, d, i, v, ' '|Ds], Cs),
number_chars(N, Ns),
number_chars(D, Ds).
popcount(X, N) :-
must_be(integer, X),
'$popcount'(X, N).

View File

@@ -63,12 +63,9 @@ Assocs are Key-Value associations implemented as a balanced binary tree
@author R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
*/
/*
:- meta_predicate
map_assoc(1, ?),
map_assoc(2, ?, ?).
*/
:- meta_predicate map_assoc(1, ?).
:- meta_predicate map_assoc(2, ?, ?).
%! empty_assoc(?Assoc) is semidet.
%
% Is true if Assoc is the empty association list.

196
src/lib/atts.pl Normal file
View File

@@ -0,0 +1,196 @@
:- module(atts, [op(1199, fx, attribute),
call_residue_vars/2,
term_attributed_variables/2]).
:- use_module(library(dcgs)).
:- use_module(library(terms)).
/* represent the list of attributes belonging to a variable,
of a particular module, as a list of terms of the form
Module:put_atts(V, ListOfAtts). */
'$default_attr_list'(Module, V) -->
( { Module:get_atts(V, Attributes) } ->
'$default_attr_list'(Attributes, Module, V)
; []
).
'$default_attr_list'([PG | PGs], Module, AttrVar) -->
[Module:put_atts(AttrVar, PG)],
'$default_attr_list'(PGs, Module, AttrVar).
'$default_attr_list'([], _, _) --> [].
'$absent_attr'(V, Attr) :-
'$get_attr_list'(V, Ls),
'$absent_from_list'(Ls, Attr).
'$absent_from_list'(X, Attr) :-
( var(X) ->
true
; X = [L|Ls],
L \= Attr ->
'$absent_from_list'(Ls, Attr)
).
'$get_attr'(V, Attr) :-
'$get_attr_list'(V, Ls),
nonvar(Ls),
'$get_from_list'(Ls, V, Attr).
'$get_from_list'([L|Ls], V, Attr) :-
nonvar(L),
( L \= Attr ->
nonvar(Ls),
'$get_from_list'(Ls, V, Attr)
; L = Attr,
'$enqueue_attr_var'(V)
).
'$put_attr'(V, Attr) :-
'$get_attr_list'(V, Ls),
'$add_to_list'(Ls, V, Attr).
'$add_to_list'(Ls, V, Attr) :-
( var(Ls) ->
Ls = [Attr | _],
'$enqueue_attr_var'(V)
; Ls = [_ | Ls0],
'$add_to_list'(Ls0, V, Attr)
).
'$del_attr'(Ls0, _, _) :-
var(Ls0),
!.
'$del_attr'(Ls0, V, Attr) :-
Ls0 = [Att | Ls1],
nonvar(Att),
( Att \= Attr ->
'$del_attr_buried'(Ls0, Ls1, V, Attr)
; '$enqueue_attr_var'(V),
'$del_attr_head'(V),
'$del_attr'(Ls1, V, Attr)
).
'$del_attr_step'(Ls1, V, Attr) :-
( nonvar(Ls1) ->
Ls1 = [_ | Ls2],
'$del_attr_buried'(Ls1, Ls2, V, Attr)
; true
).
%% assumptions: Ls0 is a list, Ls1 is its tail;
%% the head of Ls0 can be ignored.
'$del_attr_buried'(Ls0, Ls1, V, Attr) :-
( var(Ls1) -> true
; Ls1 = [Att | Ls2] ->
( Att \= Attr ->
'$del_attr_buried'(Ls1, Ls2, V, Attr)
; '$enqueue_attr_var'(V),
'$del_attr_non_head'(Ls0), %% set tail of Ls0 = tail of Ls1. can be undone by backtracking.
'$del_attr_step'(Ls1, V, Attr)
)
).
'$copy_attr_list'(L, _Module, []) :- var(L), !.
'$copy_attr_list'([Module0:Att|Atts], Module, CopiedAtts) :-
( Module0 == Module ->
CopiedAtts = [Att|CopiedAtts0],
'$copy_attr_list'(Atts, Module, CopiedAtts0)
; '$copy_attr_list'(Atts, Module, CopiedAtts)
).
user:term_expansion(Term0, Terms) :-
nonvar(Term0),
Term0 = (:- attribute Atts),
nonvar(Atts),
prolog_load_context(module, Module),
phrase(expand_terms(Atts, Module), Terms).
expand_terms(Atts, Module) -->
put_attrs_var_check,
put_attrs(Atts, Module),
get_attrs_var_check(Module),
get_attrs(Atts, Module).
put_attrs_var_check -->
[(put_atts(Var, Attr) :- nonvar(Var),
throw(error(uninstantiation_error(Var), put_atts/2))),
(put_atts(Var, Attr) :- var(Attr),
throw(error(instantiation_error, put_atts/2)))].
get_attrs_var_check(Module) -->
[(get_atts(Var, Attr) :- nonvar(Var),
throw(error(uninstantiation_error(Var), get_atts/2))),
(get_atts(Var, Attr) :- var(Attr),
!,
'$get_attr_list'(Var, Ls),
nonvar(Ls),
atts:'$copy_attr_list'(Ls, Module, Attr))].
put_attrs(Name/Arity, Module) -->
put_attr(Name, Arity, Module),
[(put_atts(Var, Attr) :- lists:maplist(Module:put_atts(Var), Attr), !)].
put_attrs((Name/Arity, Atts), Module) -->
{ nonvar(Atts) },
put_attr(Name, Arity, Module),
put_attrs(Atts, Module).
get_attrs(Name/Arity, Module) -->
get_attr(Name, Arity, Module).
get_attrs((Name/Arity, Atts), Module) -->
{ nonvar(Atts) },
get_attr(Name, Arity, Module),
get_attrs(Atts, Module).
put_attr(Name, Arity, Module) -->
{ functor(Attr, Name, Arity) },
[(put_atts(V, +Attr) :-
!,
functor(Attr, Head, Arity),
functor(AttrForm, Head, Arity),
'$get_attr_list'(V, Ls),
atts:'$del_attr'(Ls, V, Module:AttrForm),
atts:'$put_attr'(V, Module:Attr)),
(put_atts(V, Attr) :-
!,
functor(Attr, Head, Arity),
functor(AttrForm, Head, Arity),
'$get_attr_list'(V, Ls),
atts:'$del_attr'(Ls, V, Module:AttrForm),
atts:'$put_attr'(V, Module:Attr)),
(put_atts(V, -Attr) :-
!,
functor(Attr, _, _),
'$get_attr_list'(V, Ls),
atts:'$del_attr'(Ls, V, Module:Attr))].
get_attr(Name, Arity, Module) -->
{ functor(Attr, Name, Arity) },
[(get_atts(V, +Attr) :-
!,
functor(Attr, _, _),
atts:'$get_attr'(V, Module:Attr)),
(get_atts(V, Attr) :-
!,
functor(Attr, _, _),
atts:'$get_attr'(V, Module:Attr)),
(get_atts(V, -Attr) :-
!,
functor(Attr, _, _),
atts:'$absent_attr'(V, Module:Attr))].
user:goal_expansion(Term, M:put_atts(Var, Attr)) :-
nonvar(Term),
Term = put_atts(Var, M, Attr).
user:goal_expansion(Term, M:get_atts(Var, Attr)) :-
nonvar(Term),
Term = get_atts(Var, M, Attr).
:- meta_predicate call_residue_vars(0, ?).
call_residue_vars(Goal, Vars) :-
'$get_attr_var_queue_delim'(B),
call(Goal),
'$get_attr_var_queue_beyond'(B, Vars).
term_attributed_variables(Term, Vars) :-
'$term_attributed_variables'(Term, Vars).

View File

@@ -9,14 +9,21 @@ 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) :-

1699
src/lib/builtins.pl Normal file

File diff suppressed because it is too large Load Diff

257
src/lib/charsio.pl Normal file
View File

@@ -0,0 +1,257 @@
:- module(charsio, [char_type/2,
chars_utf8bytes/2,
get_single_char/1,
get_n_chars/3,
read_line_to_chars/3,
read_from_chars/2,
write_term_to_chars/3,
chars_base64/3]).
:- use_module(library(dcgs)).
:- use_module(library(iso_ext)).
:- use_module(library(error)).
:- use_module(library(lists)).
:- use_module(library(iso_ext), [partial_string/1,partial_string/3]).
fabricate_var_name(VarType, VarName, N) :-
char_code('A', AC),
LN is N mod 26 + AC,
char_code(LC, LN),
NN is N // 26,
( NN =:= 0 ->
( VarType == fabricated ->
atom_chars(VarName, ['_', LC])
; VarType == numbervars ->
atom_chars(VarName, [LC])
)
; number_chars(NN, NNChars),
( VarType == fabricated ->
atom_chars(VarName, ['_', LC | NNChars])
; VarType == numbervars ->
atom_chars(VarName, [LC | NNChars])
)
).
var_list_contains_name([VarName = _ | VarList], VarName0) :-
( VarName == VarName0 -> true
; var_list_contains_name(VarList, VarName0)
).
var_list_contains_variable([_ = Var | VarList], Var0) :-
( Var == Var0 -> true
; var_list_contains_variable(VarList, Var0)
).
make_new_var_name(VarType, V, VarName, N, N1, VarList) :-
fabricate_var_name(VarType, VarName0, N),
( var_list_contains_name(VarList, VarName0) ->
N0 is N + 1,
make_new_var_name(VarType, V, VarName, N0, N1, VarList)
; VarName = VarName0,
N1 is N + 1
).
extend_var_list(Vars, VarList, NewVarList, VarType) :-
extend_var_list_(Vars, 0, VarList, NewVarList0, VarType),
append(VarList, NewVarList0, NewVarList).
extend_var_list_([], _, _, [], _).
extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
( var_list_contains_variable(VarList, V) ->
extend_var_list_(Vs, N, VarList, NewVarList, VarType)
; make_new_var_name(VarType, V, VarName, N, N1, VarList),
NewVarList = [VarName = V | NewVarList0],
extend_var_list_(Vs, N1, VarList, NewVarList0, VarType)
).
char_type(Char, Type) :-
must_be(character, Char),
( ground(Type) ->
( ctype(Type) ->
'$char_type'(Char, Type)
; domain_error(char_type, Type, char_type/2)
)
; ctype(Type),
'$char_type'(Char, Type)
).
ctype(alnum).
ctype(alpha).
ctype(alphabetic).
ctype(alphanumeric).
ctype(ascii).
ctype(ascii_graphic).
ctype(ascii_punctuation).
ctype(binary_digit).
ctype(control).
ctype(decimal_digit).
ctype(exponent).
ctype(graphic).
ctype(graphic_token).
ctype(hexadecimal_digit).
ctype(layout).
ctype(lower).
ctype(meta).
ctype(numeric).
ctype(octal_digit).
ctype(octet).
ctype(prolog).
ctype(sign).
ctype(solo).
ctype(symbolic_control).
ctype(symbolic_hexadecimal).
ctype(upper).
ctype(whitespace).
get_single_char(C) :-
( var(C) -> '$get_single_char'(C)
; atom_length(C, 1) -> '$get_single_char'(C)
; type_error(in_character, C, get_single_char/1)
).
read_from_chars(Chars, Term) :-
must_be(chars, Chars),
'$read_term_from_chars'(Chars, Term).
write_term_to_chars(_, Options, _) :-
var(Options), instantiation_error(write_term_to_chars/3).
write_term_to_chars(Term, Options, Chars) :-
builtins:parse_write_options(Options,
[IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames],
write_term_to_chars/3),
( nonvar(Chars) ->
throw(error(uninstantiation_error(Chars), write_term_to_chars/3))
;
true
),
term_variables(Term, Vars),
extend_var_list(Vars, VNNames, NewVarNames, numbervars),
'$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth).
% Encodes Ch character to list of Bytes.
char_utf8bytes(Ch, Bytes) :-
char_code(Ch, Code),
phrase(code_to_utf8(Code), Bytes).
code_to_utf8(Code) --> {Code @< 0x80}, [Code], !.
code_to_utf8(Code) --> {Code @< 0x800}, encode(Code, 0xC0, 2), !.
code_to_utf8(Code) --> {Code @< 0x10000}, encode(Code, 0xE0, 3), !.
code_to_utf8(Code) --> {Code @< 0x110000}, encode(Code, 0xF0, 4), !.
encode(_, _, 0) --> !.
encode(Code, Prefix, Nb) -->
{ Nb1 is Nb - 1, Byte is Prefix \/ ((Code >> (6 * Nb1)) /\ 0x3F) },
[Byte], encode(Code, 0x80, Nb1).
% Maps characters and UTF-8 bytes.
% If Cs is a variable, parses Bs as a list of UTF-8 bytes.
% Otherwise, transform the list of characters Cs to UTF-8 bytes.
chars_utf8bytes(Cs, Bs) :-
var(Cs), must_be(list, Bs) ->
once(phrase(decode_utf8(Cs), Bs))
; (must_be(list, Cs),
maplist(must_be(atom), Cs),
maplist(char_utf8bytes, Cs, Bss),
append(Bss, Bs)).
decode_utf8([]) --> [].
decode_utf8(Chars) --> leading(Nb, Code), continuation(Code, Chars, Nb).
leading(1, Byte) --> [Byte], {Byte /\ 0x80 =:= 0}.
leading(2, Code) --> [Byte], {Byte /\ 0xE0 =:= 0xC0, Code is Byte - 0xC0}.
leading(3, Code) --> [Byte], {Byte /\ 0xF0 =:= 0xE0, Code is Byte - 0xE0}.
leading(4, Code) --> [Byte], {Byte /\ 0xF8 =:= 0xF0, Code is Byte - 0xF0}.
leading(1, 0xFFFD) --> [_]. % invalid first byte
continuation(Code, [H|T], 1) --> {char_code(H, Code)}, decode_utf8(T).
continuation(Code, Chars, Nb) --> [Byte],
{Nb1 is Nb - 1, Byte /\ 0xC0 =:= 0x80, NextCode is (Code << 6) \/ (Byte - 0x80)},
continuation(NextCode, Chars, Nb1).
% invalid continuation byte
% each remaining continuation byte (if any) will raise 0xFFFD too
continuation(_, ['\xFFFD\'|T], _) --> [_], decode_utf8(T).
read_line_to_chars(Stream, Cs0, Cs) :-
'$get_n_chars'(Stream, 1, Char), % this also works for binary streams
( Char == [] -> Cs0 = Cs
; Char = [C],
Cs0 = [C|Rest],
( C == '\n' -> Rest = Cs
; read_line_to_chars(Stream, Rest, Cs)
)
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Read N characters from Stream.
If N is a variable, read until EOF, unifying N with the number of
characters read.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
get_n_chars(Stream, N, Cs) :-
can_be(integer, N),
( var(N) ->
read_to_eof(Stream, Cs),
length(Cs, N)
; N >= 0,
'$get_n_chars'(Stream, N, Cs)
).
read_to_eof(Stream, Cs) :-
'$get_n_chars'(Stream, 512, Cs0),
( Cs0 == [] -> Cs = []
; partial_string(Cs0, Cs, Rest),
read_to_eof(Stream, Rest)
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Relation between a list of characters Cs and its Base64 encoding Bs,
also a list of characters.
At least one of the arguments must be instantiated.
Options are:
- padding(Boolean)
Whether to use padding: true (the default) or false.
- charset(C)
Either 'standard' (RFC 4648 §4, the default) or 'url' (RFC 4648 §5).
Example:
?- chars_base64("hello", Bs, []).
Bs = "aGVsbG8=".
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
chars_base64(Cs, Bs, Options) :-
must_be(list, Options),
( member(O, Options), var(O) ->
instantiation_error(chars_base64/3)
; ( member(padding(Padding), Options) -> true
; Padding = true
),
( member(charset(Charset), Options) -> true
; Charset = standard
)
),
must_be(boolean, Padding),
must_be(atom, Charset),
( member(Charset, [standard,url]) -> true
; domain_error(charset, Charset, chars_base64/3)
),
( var(Cs) ->
must_be(chars, Bs),
'$chars_base64'(Cs, Bs, Padding, Charset)
; must_be(chars, Cs),
( '$first_non_octet'(Cs, N) ->
domain_error(byte_char, N, chars_base64/3)
; '$chars_base64'(Cs, Bs, Padding, Charset)
)
).

View File

@@ -16,11 +16,9 @@
Public operators.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- op(300, fy, ~).
:- op(500, yfx, #).
:- module(clpb, [
sat/1,
:- module(clpb, [op(300, fy, ~),
op(500, yfx, #),
sat/1,
taut/2,
labeling/1,
sat_count/2,
@@ -32,9 +30,11 @@
:- use_module(library(between)).
:- use_module(library(atts)).
:- use_module(library(lists)).
:- use_module(library(non_iso)).
:- use_module(library(iso_ext)).
:- use_module(library(random)).
:- use_module(library(pairs)).
:- use_module(library(dcgs)).
%:- use_module(library(types)).
:- use_module(library(error), [domain_error/3, type_error/3]).
:- attribute
clpb/1,
@@ -49,17 +49,6 @@
Compatibility predicates.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
group_pairs_by_key([], []).
group_pairs_by_key([M-N|T0], [M-[N|TN]|T]) :-
same_key(M, T0, TN, T1),
group_pairs_by_key(T1, T).
same_key(M0, [M-N|T0], [N|TN], T) :-
M0 == M,
!,
same_key(M, T0, TN, T).
same_key(_, L, [], L).
must_be(What, Term) :- must_be(What, unknown(Term)-1, Term).
must_be(acyclic, Where, Term) :- !,
@@ -77,8 +66,8 @@ must_be(list(What), Where, Term) :- !,
must_be(ground, _, Term) :- !,
functor(Term, _, _).
must_be(Type, Goal-Arg, Term) :-
must_be(Term, Type, Goal, Arg).
must_be(Type, _, Term) :-
error:must_be(Type, Term).
clpz_list(Nil, _) :- Nil == [].
clpz_list(Ls, Where) :-
@@ -87,7 +76,6 @@ clpz_list(Ls, Where) :-
; Ls = [_|Rest],
clpz_list(Rest, Where)
).
instantiation_error(Term) :- instantiation_error(Term, unknown(Term)-1).
@@ -99,72 +87,14 @@ instantiation_error(_, Goal-Arg) :-
domain_error(Expectation, Term) :-
domain_error(Expectation, Term, unknown(Term)-1).
domain_error(Expectation, Term, Goal-Arg) :-
throw(error(domain_error(Expectation, Term), domain_error(Goal, Arg, Expectation, Term))).
type_error(Expectation, Term) :-
type_error(Expectation, Term, unknown(Term)-1).
type_error(Expectation, Term, Goal-Arg) :-
throw(error(type_error(Expectation, Term), type_error(Goal, Arg, Expectation, Term))).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
foldl/4
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
foldl(Goal_3, Ls, A0, A) :-
foldl_(Ls, Goal_3, A0, A).
foldl_([], _, A, A).
foldl_([L|Ls], G_3, A0, A) :-
call(G_3, L, A0, A1),
foldl_(Ls, G_3, A1, A).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
foldl/5
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
foldl(Goal_4, Xs, Ys, A0, A) :-
foldl_(Xs, Ys, Goal_4, A0, A).
foldl_([], [], _, A, A).
foldl_([X|Xs], [Y|Ys], G_4, A0, A) :-
call(G_4, X, Y, A0, A1),
foldl_(Xs, Ys, G_4, A1, A).
partition(Pred, Ls0, As, Bs) :-
include(Pred, Ls0, As),
exclude(Pred, Ls0, Bs).
sum_list(Ls, S) :- sumlist(Ls, S).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Pairs.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
pairs_keys_values([], [], []).
pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :-
pairs_keys_values(ABs, As, Bs).
pairs_keys(Ps, Ks) :- pairs_keys_values(Ps, Ks, _).
pairs_values(Ps, Vs) :- pairs_keys_values(Ps, _, Vs).
map_list_to_pairs(Pred, Ls, Ps) :-
map_list_to_pairs2(Ls, Pred, Ps).
map_list_to_pairs2([], _, []).
map_list_to_pairs2([H|T0], Pred, [K-H|T]) :-
call(Pred, H, K),
map_list_to_pairs2(T0, Pred, T).
goal_expansion(get_attr(Var, Module, Value), (var(Var),get_atts(Var, Access))) :-
Access =.. [Module,Value].
@@ -728,10 +658,6 @@ existential(V, BDD, Node) :-
Counter network for card(Is,Fs).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
same_length([], []).
same_length([_|As], [_|Bs]) :-
same_length(As, Bs).
counter_network(Cs, Fs, Node) :-
same_length([_|Fs], Indicators),
fill_indicators(Indicators, 0, Cs),
@@ -905,9 +831,8 @@ verify_attributes(Var, Other, Gs) :-
( integer(Other) ->
( between(0, 1, Other) ->
root_get_formula_bdd(Root, Sat, BDD0),
bdd_restriction(BDD0, I, Other, BDD),
root_put_formula_bdd(Root, Sat, BDD),
Gs = [satisfiable_bdd(BDD)]
Gs = [bdd_restriction(BDD0,I,Other,BDD),satisfiable_bdd(BDD)]
; no_truth_value(Other)
)
; atom(Other) ->
@@ -1242,8 +1167,7 @@ bdd_count(Node, VNum, Count) :-
bdd_count(High, VNum, HCount),
bdd_pow(Low, V, VNum, LPow),
bdd_pow(High, V, VNum, HPow),
Count0 is LPow*LCount + HPow*HCount,
Count = Count0
Count is LPow*LCount + HPow*HCount
)
).
@@ -1352,13 +1276,18 @@ weighted_maximum(Ws, Vars, Max) :-
maplist(var_with_index, Vars, IVs),
pairs_keys_values(Pairs0, IVs, Ws),
keysort(Pairs0, Pairs1),
pairs_keys_values(Pairs1, IVs1, WeightsIndexOrder),
% sum linear combinations of repeated variables
group_pairs_by_key(Pairs1, Groups),
maplist(group_sumweights_pair, Groups, Pairs2),
pairs_keys_values(Pairs2, IVs1, WeightsIndexOrder),
pairs_values(IVs1, VarsIndexOrder),
% Pairs is a list of Var-Weight terms, in index order of Vars
pairs_keys_values(Pairs, VarsIndexOrder, WeightsIndexOrder),
bdd_maximum(BDD, Pairs, Max),
max_labeling(BDD, Pairs).
group_sumweights_pair((I-V)-Ws, (I-V)-W) :- sum_list(Ws, W).
max_labeling(1, Pairs) :- max_upto(Pairs, _, _).
max_labeling(node(_,Var,Low,High,Aux), Pairs0) :-
max_upto(Pairs0, Var, Pairs),
@@ -1593,8 +1522,8 @@ pairs_([], _) --> [].
pairs_([B|Bs], A) --> [A-B], pairs_(Bs, A).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Set the Prolog flag clpb_residuals to bdd to obtain the BDD nodes
as residuals. Note that they cannot be used as regular goals.
Assert clpb:clpb_residuals(bdd) to obtain the BDD nodes as
residuals. Note that they cannot be used as regular goals.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
nodes([]) --> [].
@@ -1622,9 +1551,10 @@ sats([]) --> [].
sats([A|As]) --> [clpb:sat(A)], sats(As).
booleans([]) --> [].
booleans([B|Bs]) --> boolean(B), { del_clpb(B) }, booleans(Bs).
booleans([B|Bs]) --> boolean(B), booleans(Bs).
boolean(Var) -->
{ del_clpb(Var) },
( { get_attr(Var, clpb_omit_boolean, true) } -> []
; [clpb:sat(Var =:= Var)]
).

7841
src/lib/clpz.pl Normal file

File diff suppressed because it is too large Load Diff

32
src/lib/cont.pl Normal file
View 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'(_, _, _, _).

840
src/lib/crypto.pl Normal file
View File

@@ -0,0 +1,840 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written 2020, 2021, 2022 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
Predicates for cryptographic applications.
This library assumes that the Prolog flag double_quotes is set to chars.
In Scryer Prolog, lists of characters are very efficiently represented,
and strings have the advantage that the atom table remains unmodified.
Especially for cryptographic applications, it as an advantage that
using strings leaves little trace of what was processed in the system.
For predicates that accept an encoding/1 option to specify the encoding
of the input data, if encoding(octet) is used, then the input can also
be specified as a list of bytes, i.e., integers between 0 and 255.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(crypto,
[hex_bytes/2, % ?Hex, ?Bytes
crypto_n_random_bytes/2, % +N, -Bytes
crypto_data_hash/3, % +Data, -Hash, +Options
crypto_data_hkdf/4, % +Data, +Length, -Bytes, +Options
crypto_password_hash/2, % +Password, ?Hash
crypto_password_hash/3, % +Password, -Hash, +Options
crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options
crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options
ed25519_new_keypair/1, % -KeyPair
ed25519_keypair_public_key/2, % +KeyPair, +PublicKey
ed25519_sign/4, % +KeyPair, +Data, -Signature, +Options
ed25519_verify/4, % +PublicKey, +Data, +Signature, +Options
curve25519_generator/1, % -Generator
curve25519_scalar_mult/3, % +Scalar, +Point, -Result
crypto_name_curve/2, % +Name, -Curve
crypto_curve_order/2, % +Curve, -Order
crypto_curve_generator/2, % +Curve, -Generator
crypto_curve_scalar_mult/4 % +Curve, +Scalar, +Point, -Result
]).
:- use_module(library(error)).
:- use_module(library(lists)).
:- use_module(library(between)).
:- use_module(library(dcgs)).
:- use_module(library(clpz)).
:- use_module(library(arithmetic)).
:- use_module(library(format)).
:- use_module(library(charsio)).
:- use_module(library(si)).
:- use_module(library(iso_ext), [partial_string/3]).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
hex_bytes(?Hex, ?Bytes) is det.
Relation between a hexadecimal sequence and a list of bytes. Hex
is a string of hexadecimal numbers. Bytes is a list of *integers*
between 0 and 255 that represent the sequence as a list of bytes.
At least one of the arguments must be instantiated.
Example:
?- hex_bytes("501ACE", Bs).
Bs = [80,26,206].
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
hex_bytes(Hs, Bytes) :-
( ground(Hs) ->
must_be(list, Hs),
maplist(must_be(atom), 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_byte_chars(Chars, Context) :-
must_be(chars, Chars),
( '$first_non_octet'(Chars, F) ->
domain_error(byte_char, F, Context)
; true
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cryptographically secure random numbers
=======================================
crypto_n_random_bytes(+N, -Bytes) is det
Bytes is unified with a list of N cryptographically secure
pseudo-random bytes. Each byte is an integer between 0 and 255. If
the internal pseudo-random number generator (PRNG) has not been
seeded with enough entropy to ensure an unpredictable byte
sequence, an exception is thrown.
One way to relate such a list of bytes to an _integer_ is to use
CLP() constraints as follows:
:- use_module(library(clpz)).
:- use_module(library(lists)).
bytes_integer(Bs, N) :-
foldl(pow, Bs, 0-0, N-_).
pow(B, N0-I0, N-I) :-
B in 0..255,
N #= N0 + B*256^I0,
I #= I0 + 1.
With this definition, we can generate a random 256-bit integer
_from_ a list of 32 random _bytes_:
?- crypto_n_random_bytes(32, Bs),
bytes_integer(Bs, I).
Bs = [146,166,162,210,242,7,25,132,64,94|...],
I = 337420085690608915485...(56 digits omitted).
The above relation also works in the other direction, letting you
translate an integer _to_ a list of bytes. In addition, you can
use hex_bytes/2 to convert bytes to _tokens_ that can be easily
exchanged in your applications.
?- crypto_n_random_bytes(12, Bs),
hex_bytes(Hex, Bs).
Bs = [34,25,50,72,58,63,50,172,32,46|...], Hex = "221932483a3f32ac202 ...".
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
crypto_n_random_bytes(N, Bs) :-
must_be(integer, N),
length(Bs, N),
maplist(crypto_random_byte, Bs).
crypto_random_byte(B) :- '$crypto_random_byte'(B).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Hashing
=======
crypto_data_hash(+Data, -Hash, +Options)
Where Data is a list of characters, and Hash is the computed hash
as a list of hexadecimal characters.
Options is a list of:
- algorithm(+A)
where A is one of ripemd160, sha256, sha384, sha512, sha512_256,
sha3_224, sha3_256, sha3_384, sha3_512, blake2s256, blake2b512,
or a variable. If A is a variable, then it is unified with the
default algorithm, which is an algorithm that is considered
cryptographically secure at the time of this writing.
- encoding(+Encoding)
The default encoding is utf8. The alternative is octet,
to treat the input as a list of raw bytes.
Example:
?- crypto_data_hash("abc", Hs, [algorithm(sha256)]).
Hs = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad".
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SHA256 is the current default for several hash-related predicates.
It is deemed sufficiently secure for the foreseeable future. Yet,
application programmers must be aware that the default may change in
future versions. The hash predicates all yield the algorithm they
used if a Prolog variable is used for the pertaining option.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
crypto_data_hash(Data0, Hash, Options0) :-
must_be(list, Options0),
options_data_chars(Options0, Data0, Data, Encoding),
functor_hash_options(algorithm, A, Options0, _),
( hash_algorithm(A) -> true
; domain_error(hash_algorithm, A, crypto_data_hash/3)
),
'$crypto_data_hash'(Data, Encoding, HashBytes, A),
hex_bytes(Hash, HashBytes).
options_data_chars(Options, Data, Chars, Encoding) :-
option(encoding(Encoding), Options, utf8),
must_be(atom, Encoding),
encoding_chars(Encoding, Data, Chars).
default_hash(sha256).
functor_hash_options(F, Hash, Options0, [Option|Options]) :-
Option =.. [F,Hash],
( select(Option, Options0, Options) ->
( var(Hash) ->
default_hash(Hash)
; must_be(atom, Hash)
)
; Options = Options0,
default_hash(Hash)
).
hash_algorithm(ripemd160).
hash_algorithm(sha256).
hash_algorithm(sha512).
hash_algorithm(sha384).
hash_algorithm(sha512_256).
hash_algorithm(sha3_224).
hash_algorithm(sha3_256).
hash_algorithm(sha3_384).
hash_algorithm(sha3_512).
hash_algorithm(blake2s256).
hash_algorithm(blake2b512).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
crypto_data_hkdf(+Data, +Length, -Bytes, +Options) is det.
Concentrate possibly dispersed entropy of Data and then expand it
to the desired length. Data is a list of characters.
Bytes is unified with a list of bytes of length Length, and is
suitable as input keying material and initialization vectors to
symmetric encryption algorithms.
Admissible options are:
- algorithm(+Algorithm)
One of sha256, sha384 or sha512. If you specify a variable,
then it is unified with the algorithm that was used, which is a
cryptographically secure algorithm by default.
- info(+Info)
Optional context and application specific information,
specified as a list of characters. The default is [].
- salt(+List)
Optionally, a list of bytes that are used as salt. The
default is all zeroes.
- encoding(+Encoding)
The default encoding is utf8. The alternative is octet,
to treat the input as a list of raw bytes.
The `info/1` option can be used to generate multiple keys from a
single master key, using for example values such as "key" and
"iv", or the name of a file that is to be encrypted.
See crypto_n_random_bytes/2 to obtain a suitable salt.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
crypto_data_hkdf(Data0, L, Bytes, Options0) :-
functor_hash_options(algorithm, Algorithm, Options0, Options),
( hkdf_algorithm(Algorithm) -> true
; domain_error(hkdf_algorithm, Algorithm, crypto_data_hkdf/4)
),
must_be(integer, L),
L #>= 0,
options_data_chars(Options, Data0, Data, Encoding),
option(salt(SaltBytes), Options, []),
must_be_bytes(SaltBytes, crypto_data_hkdf/4),
option(info(Info0), Options, []),
chars_bytes_(Info0, Info, crypto_data_hkdf/4),
'$crypto_data_hkdf'(Data, Encoding, SaltBytes, Info, Algorithm, L, Bytes).
hkdf_algorithm(sha256).
hkdf_algorithm(sha384).
hkdf_algorithm(sha512).
option(What, Options, Default) :-
( member(V, Options), var(V) ->
instantiation_error(option/3)
; true
),
( member(What, Options) -> true
; What =.. [_,Default]
).
chars_bytes_(Cs, Bytes, Context) :-
must_be(list, Cs),
( maplist(integer, Cs) -> Bytes = Cs
; chars_utf8bytes(Cs, Bytes)
),
must_be_bytes(Bytes, Context).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The so-called modular crypt format (MCF) is a standard for encoding
password hash strings. However, there's no official specification
document describing it. Nor is there a central registry of
identifiers or rules. This page describes what is known about it:
https://pythonhosted.org/passlib/modular_crypt_format.html
As of 2016, the MCF is deprecated in favor of the PHC String Format:
https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md
This is what we are using below. For the time being, it is best to
treat these hashes as opaque terms in applications. Please let me
know if you need to rely on any specifics of this format.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
crypto_password_hash(+Password, ?Hash) is semidet.
If Hash is instantiated, the predicate succeeds _iff_ the hash
matches the given password. Otherwise, the call is equivalent to
crypto_password_hash(Password, Hash, []) and computes a
password-based hash using the default options.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
crypto_password_hash(Password0, Hash) :-
( nonvar(Hash) ->
chars_bytes_(Password0, Password, crypto_password_hash/2),
must_be(list, Hash),
dollar_segments(Hash, [[],"pbkdf2-sha512",[t,=|CsIterations],SaltB64,HashB64]),
number_chars(Iterations, CsIterations),
bytes_base64(SaltBytes, SaltB64),
bytes_base64(HashBytes, HashB64),
'$crypto_password_hash'(Password, SaltBytes, Iterations, HashBytes)
; crypto_password_hash(Password0, Hash, [])
).
dollar_segments(Ls, Segments) :-
( append(Front, [$|Ds], Ls) ->
Segments = [Front|Rest],
dollar_segments(Ds, Rest)
; Segments = [Ls]
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
crypto_password_hash(+Password, -Hash, +Options) is det.
Derive Hash based on Password. This predicate is similar to
crypto_data_hash/3 in that it derives a hash from given data.
However, it is tailored for the specific use case of _passwords_.
One essential distinction is that for this use case, the derivation
of a hash should be _as slow as possible_ to counteract brute-force
attacks over possible passwords.
Another important distinction is that equal passwords must yield,
with very high probability, _different_ hashes. For this reason,
cryptographically strong random numbers are automatically added to
the password before a hash is derived.
Hash is unified with a string that contains the computed hash and
all parameters that were used, except for the password. Instead of
storing passwords, store these hashes. Later, you can verify the
validity of a password with crypto_password_hash/2, comparing the
then entered password to the stored hash. If you need to export this
atom, you should treat it as opaque ASCII data with up to 255 bytes
of length. The maximal length may increase in the future.
Admissible options are:
- algorithm(+Algorithm)
The algorithm to use. Currently, the only available algorithm
is 'pbkdf2-sha512', which is therefore also the default.
- cost(+C)
C is an integer, denoting the binary logarithm of the number
of _iterations_ used for the derivation of the hash. This
means that the number of iterations is set to 2^C. Currently,
the default is 17, and thus more than one hundred _thousand_
iterations. You should set this option as high as your server
and users can tolerate. The default is subject to change and
will likely increase in the future or adapt to new algorithms.
- salt(+Salt)
Use the given list of bytes as salt. By default,
cryptographically secure random numbers are generated for this
purpose. The default is intended to be secure, and constitutes
the typical use case of this predicate.
Currently, PBKDF2 with SHA-512 is used as the hash derivation
function, using 128 bits of salt. All default parameters, including
the algorithm, are subject to change, and other algorithms will also
become available in the future. Since computed hashes store all
parameters that were used during their derivation, such changes will
not affect the operation of existing deployments. Note though that
new hashes will then be computed with the new default parameters.
See crypto_data_hkdf/4 for generating keys from Hash.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
crypto_password_hash(Password0, Hash, Options) :-
chars_bytes_(Password0, Password, crypto_password_hash/3),
must_be(list, Options),
option(cost(C), Options, 17),
Iterations #= 2^C,
Algorithm = 'pbkdf2-sha512', % current default and only option
option(algorithm(Algorithm), Options, Algorithm),
( member(salt(SaltBytes), Options) ->
must_be_bytes(SaltBytes, crypto_password_hash/2)
; crypto_n_random_bytes(16, SaltBytes)
),
'$crypto_password_hash'(Password, SaltBytes, Iterations, HashBytes),
bytes_base64(HashBytes, HashB64),
bytes_base64(SaltBytes, SaltB64),
phrase(format_("$pbkdf2-sha512$t=~d$~s$~s", [Iterations,SaltB64,HashB64]), Hash).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Bidirectional Bytes <-> Base64 conversion *without padding*.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
bytes_base64(Bytes, Base64) :-
( var(Bytes) ->
chars_base64(Chars, Base64, [padding(false)]),
maplist(char_code, Chars, Bytes)
; maplist(char_code, Chars, Bytes),
chars_base64(Chars, Base64, [padding(false)])
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
crypto_data_encrypt(+PlainText,
+Algorithm,
+Key,
+IV,
-CipherText,
+Options).
Encrypt the given PlainText, using the symmetric algorithm
Algorithm, key Key, and initialization vector (or nonce) IV, to
give CipherText.
PlainText must be a list of characters, Key and IV must be lists of
bytes, and CipherText is created as a list of characters.
Keys and IVs can be chosen at random (using for example
crypto_n_random_bytes/2) or derived from input keying material (IKM)
using for example crypto_data_hkdf/4. This input is often a shared
secret, such as a negotiated point on an elliptic curve, or the hash
that was computed from a password via crypto_password_hash/3 with a
freshly generated and specified _salt_.
Reusing the same combination of Key and IV typically leaks at least
_some_ information about the plaintext. For example, identical
plaintexts will then correspond to identical ciphertexts. For some
algorithms, reusing an IV with the same Key has disastrous results
and can cause the loss of all properties that are otherwise
guaranteed. Especially in such cases, an IV is also called a
_nonce_ (number used once).
It is safe to store and transfer the used initialization vector (or
nonce) in plain text, but the key _must be kept secret_.
Currently, the only supported algorithm is 'chacha20-poly1305', a
powerful and efficient _authenticated_ encryption scheme, providing
secrecy and at the same time reliable protection against undetected
_modifications_ of the encrypted data. This is a very good choice
for virtually all use cases. It is a stream cipher and can encrypt
data of any length up to 256 GB. Further, the encrypted data has
exactly the same length as the original, and no padding is used.
Options:
- encoding(+Encoding)
Encoding to use for PlainText. Default is utf8. The alternative
is octet to treat PlainText as raw bytes.
- tag(-List)
For authenticated encryption schemes, List is unified with a
list of _bytes_ holding the tag. This tag must be provided for
decryption.
- aad(+Data)
Data is additional authenticated data (AAD), a list of
characters. It is authenticated in that it influences the tag,
but it is not encrypted. The encoding/1 option also specifies
the encoding of Data.
Here is an example encryption and decryption, using the ChaCha20
stream cipher with the Poly1305 authenticator. This cipher uses a
256-bit key and a 96-bit nonce, i.e., 32 and 12 _bytes_,
respectively:
?- Algorithm = 'chacha20-poly1305',
crypto_n_random_bytes(32, Key),
crypto_n_random_bytes(12, IV),
crypto_data_encrypt("this text is to be encrypted", Algorithm,
Key, IV, CipherText, [tag(Tag)]),
crypto_data_decrypt(CipherText, Algorithm,
Key, IV, RecoveredText, [tag(Tag)]).
Yielding:
Algorithm = 'chacha20-poly1305',
Key = [113,247,153,134,177,220,13,193,50,150|...],
IV = [135,20,149,153,63,35,68,114,247,171|...],
CipherText = "\x94\0Ej\x94\®Â\x95\óÑÆXÃn¾ð©b\x1c\ ...",
RecoveredText = "this text is to be ...",
Tag = [152,117,152,17,162,75,150,206,144,40|...]
In this example, we use crypto_n_random_bytes/2 to generate a key
and nonce from cryptographically secure random numbers. For
repeated applications, you must ensure that a nonce is only used
_once_ together with the same key. Note that for _authenticated_
encryption schemes, the _tag_ that was computed during encryption
is necessary for decryption. It is safe to store and transfer the
tag in plain text.
See also crypto_data_decrypt/6, and hex_bytes/2 for conversion
between bytes and hex encoding.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
crypto_data_encrypt(PlainText0, Algorithm, Key, IV, CipherText, Options) :-
options_data_chars(Options, PlainText0, PlainText, Encoding),
option(tag(Tag), Options, _),
( nonvar(Tag) ->
must_be_bytes(Tag, crypto_data_encrypt/6)
; true
),
option(aad(AAD0), Options, []),
encoding_chars(Encoding, AAD0, AAD),
must_be_bytes(Key, crypto_data_encrypt/6),
must_be_bytes(IV, crypto_data_encrypt/6),
must_be(atom, Algorithm),
( Algorithm = 'chacha20-poly1305' -> true
; domain_error('chacha20-poly1305', Algorithm, crypto_data_encrypt/6)
),
algorithm_key_iv(Algorithm, Key, IV),
'$crypto_data_encrypt'(PlainText, AAD, Encoding, Key, IV, Tag, CipherText).
algorithm_key_iv('chacha20-poly1305', Key, IV) :-
length(Key, 32),
length(IV, 12).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
crypto_data_decrypt(+CipherText,
+Algorithm,
+Key,
+IV,
-PlainText,
+Options).
Decrypt the given CipherText, using the symmetric algorithm
Algorithm, key Key, and initialization vector IV, to give
PlainText. CipherText must be a list of characters, and Key and IV
must be lists of bytes. PlainText is created as a list of
characters.
Currently, the only supported algorithm is 'chacha20-poly1305',
a very secure, fast and versatile authenticated encryption method.
Options is a list of:
- encoding(+Encoding)
Encoding to use for PlainText. The default is utf8. The
alternative is octet, which is used if the data are raw bytes.
- tag(+Tag)
For authenticated encryption schemes, the tag must be specified as
a list of bytes exactly as they were generated upon encryption.
- aad(+Data)
Any additional authenticated data (AAD) must be specified. The
encoding/1 option also specifies the encoding of Data.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
crypto_data_decrypt(CipherText0, Algorithm, Key, IV, PlainText, Options) :-
option(tag(Tag), Options, []),
must_be_bytes(Tag, crypto_data_decrypt/6),
must_be_bytes(Key, crypto_data_decrypt/6),
must_be_bytes(IV, crypto_data_decrypt/6),
must_be(atom, Algorithm),
option(encoding(Encoding), Options, utf8),
option(aad(AAD0), Options, []),
encoding_chars(Encoding, AAD0, AAD),
must_be(atom, Encoding),
member(Encoding, [utf8,octet]),
encoding_chars(octet, CipherText0, CipherText1),
maplist(char_code, TagChars, Tag),
% we append the tag very efficiently, retaining a compact
% internal string representation of the ciphertext
partial_string(CipherText1, CipherText, TagChars),
( Algorithm = 'chacha20-poly1305' -> true
; domain_error('chacha20-poly1305', Algorithm, crypto_data_decrypt/6)
),
algorithm_key_iv(Algorithm, Key, IV),
'$crypto_data_decrypt'(CipherText, AAD, Key, IV, Encoding, PlainText).
encoding_chars(octet, Bs, Cs) :-
must_be(list, Bs),
( maplist(integer, Bs) ->
maplist(char_code, Cs, Bs)
; Bs = Cs
),
must_be_byte_chars(Cs, crypto_encoding).
encoding_chars(utf8, Cs, Cs) :-
must_be(chars, Cs).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Digital signatures with Ed25519
===============================
- ed25519_new_keypair(-Pair)
Yields a new Ed25519 key pair Pair, a list of characters. The
pair contains the private key and must be kept absolutely secret.
Pair can be used for signing. Its public key can be obtained
with ed25519_keypair_public_key/2.
- ed25519_keypair_public_key(+Pair, -PublicKey)
PublicKey is the public key of the given key pair. The public key
can be used for signature verification, and can be shared freely.
The public key is represented as a list of characters.
- ed25519_sign(+Key, +Data, -Signature, +Options)
Key and Data must be lists of characters. Key is a key pair in
PKCS#8 v2 format as generated by ed25519_new_keypair/1. Sign Data
with Key, yielding Signature as a list of hexadecimal characters.
- ed25519_verify(+Key, +Data, +Signature, +Options)
Key and Data must be lists of characters. Key is a public key.
Succeeds if Data was signed with the private key corresponding to
Key, where Signature is a list of hexadecimal characters as
generated by ed25519_sign/4. Fails otherwise.
Currently, the only option for signing and verifying is:
- encoding(+Encoding)
The default encoding of Data is utf8. The alternative is octet,
which treats Data as a list of raw bytes.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
ed25519_new_keypair(Pair) :-
'$ed25519_new_keypair'(Pair).
ed25519_keypair_public_key(Pair, PublicKey) :-
must_be_byte_chars(Pair, ed25519_keypair_public_key),
'$ed25519_keypair_public_key'(Pair, PublicKey).
ed25519_sign(Key, Data0, Signature, Options) :-
must_be_byte_chars(Key, ed25519_sign),
options_data_chars(Options, Data0, Data, Encoding),
'$ed25519_sign'(Key, Data, Encoding, Signature0),
hex_bytes(Signature, Signature0).
ed25519_verify(Key, Data0, Signature0, Options) :-
must_be_byte_chars(Key, ed25519_verify),
options_data_chars(Options, Data0, Data, Encoding),
hex_bytes(Signature0, Signature),
'$ed25519_verify'(Key, Data, Encoding, Signature).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
X25519: ECDH key exchange over Curve25519
=========================================
Points on Curve25519 are represented as lists of characters that denote
the u-coordinate of the Montgomery curve.
- curve25519_generator(-Gs)
Gs is the generator point of Curve25519.
- curve25519_scalar_mult(+Scalar, +Ps, -Rs)
Scalar must be an integer between 0 and 2^256-1,
or a list of 32 bytes, and Ps must be a point on the curve.
Computes the point Rs = Scalar*Ps as mandated by X25519.
Alice and Bob can use this to establish a shared secret as follows,
where Gs is the generator point of Curve25519:
1. Alice creates a random integer a and sends As = a*Gs to Bob.
2. Bob creates a random integer b and sends Bs = b*Gs to Alice.
3. Alice computes Rs = a*Bs.
4. Bob computes Rs = b*As.
5. Alice and Bob use crypto_data_hkdf/4 on Rs with suitable
(same) parameters to obtain lists of bytes that can be used as
keys and initialization vectors for symmetric encryption.
If a and b are kept secret, this method is considered very secure.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
curve25519_generator(Gs) :-
length(Gs0, 32),
Gs0 = [9|Zs],
maplist(=(0), Zs),
maplist(char_code, Gs, Gs0).
curve25519_scalar_mult(Scalar, Point, Result) :-
( integer_si(Scalar) ->
length(ScalarBytes, 32),
bytes_integer(ScalarBytes, Scalar)
; ScalarBytes = Scalar,
must_be_bytes(ScalarBytes, curve25519_scalar_mult/3),
length(ScalarBytes, 32)
),
maplist(char_code, Point, PointBytes),
'$curve25519_scalar_mult'(ScalarBytes, PointBytes, Result).
bytes_integer(Bs, N) :-
foldl(pow, Bs, 0-0, N-_).
pow(B, N0-I0, N-I) :-
B in 0..255,
N #= N0 + B*256^I0,
I #= I0 + 1.
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Operations on Elliptic Curves
=============================
Sample use: Establishing a shared secret S, using ECDH key exchange.
?- crypto_name_curve(secp256k1, C),
crypto_curve_generator(C, Generator),
PrivateKey = 10,
crypto_curve_scalar_mult(C, PrivateKey, Generator, PublicKey),
Random = 12,
crypto_curve_scalar_mult(C, Random, Generator, R),
crypto_curve_scalar_mult(C, Random, PublicKey, S),
crypto_curve_scalar_mult(C, PrivateKey, R, S).
For better security, new code should use Curve25519 instead.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
An elliptic curve over a prime field F_p is represented as:
curve(Name,P,A,B,point(X,Y),Order,FieldLength,Cofactor).
First, we define suitable accessors.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
curve_name(curve(Name,_,_,_,_,_,_,_), Name).
curve_p(curve(_,P,_,_,_,_,_,_), P).
curve_a(curve(_,_,A,_,_,_,_,_), A).
curve_b(curve(_,_,_,B,_,_,_,_), B).
curve_field_length(curve(_,_,_,_,_,_,FieldLength,_), FieldLength).
crypto_curve_generator(curve(_,_,_,_,G,_,_,_), G).
crypto_curve_order(curve(_,_,_,_,_,Order,_,_), Order).
crypto_curve_scalar_mult(Curve, Scalar, point(X,Y), point(RX, RY)) :-
must_be(integer, Scalar),
must_be_on_curve(Curve, point(X,Y)),
curve_name(Curve, Name),
curve_field_length(Curve, L0),
L #= 2*L0, % for hex encoding
phrase(format_("04~|~`0t~16r~*+~`0t~16r~*+", [X,L,Y,L]), Hex),
hex_bytes(Hex, Bytes),
'$crypto_curve_scalar_mult'(Name, Scalar, Bytes, SX, SY),
number_chars(RX, SX),
number_chars(RY, SY).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- 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(secp112r1,
curve(secp112r1,
0x00db7c2abf62e35e668076bead208b,
0x00db7c2abf62e35e668076bead2088,
0x659ef8ba043916eede8911702b22,
point(0x09487239995a5ee76b55f9c2f098,
0xa89ce5af8724c0a23e0e0ff77500),
0x00db7c2abf62e35e7628dfac6561c5,
14,
1)).
crypto_name_curve(secp256k1,
curve(secp256k1,
0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
0x0,
0x7,
point(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8),
0x00fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
32,
1)).

238
src/lib/csv.pl Normal file
View File

@@ -0,0 +1,238 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Predicates for parsing CSV data
Read csv files
Only two options with default values :
- token_separator(',')
- with_header(true)
Examples
* parsing a csv string:
?- use_module(library(csv)).
?- use_module(library(dcgs)).
?- phrase(parse_csv(Data), "col1,col2,col3,col4\none,2,,three").
Data = frame(["col1","col2","col3","col4"],[["one",2,[],"three"]]).
* with some options:
?- phrase(parse_csv(Data, [with_header(false), token_separator(';')]), "one;2;;three").
Data = frame([],[["one",2,[],"three"]]).
* parsing a csv file:
?- use_module(library(csv)).
?- use_module(library(pio)).
?- phrase_from_file(parse_csv(frame(Header, Rows)), './test.csv').
Write csv files
Four options with default values :
- line_separator('\n')
- token_separator(',')
- with_header(true)
- null_value(empty)
Examples
* writing a csv file:
?- use_module(library(csv)).
?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]])).
* with some options
?- use_module(library(csv)).
?- write_csv('./test.csv', frame(["col1","col2","col3","col4"], [["one",2,[],"three"]]), [with_header(false), line_separator('\r\n'), token_separator(';'), null_value('\\N')]).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(csv, [
parse_csv//1,
parse_csv//2,
write_csv/2,
write_csv/3
]).
:- use_module(library(format)).
:- use_module(library(dcgs)).
:- use_module(library(iso_ext)).
:- use_module(library(lists)).
option(W, O) :-
( member(W, O) -> true
; throw(error(domain_error(csv_option, W), option/2))).
option_extends([], Opt, Opt).
option_extends([X | Y], Opt0, Opt) :-
functor(X, Name, 1),
F0 =.. [Name, _],
( select(F0, Opt0, R) ->
option_extends(Y, [X | R], Opt)
; option_extends(Y, [X | Opt0], Opt) ).
%% -- write --
escaped_field([], []).
escaped_field(['"' | Y], ['"', '"' | R]) :-
escaped_field(Y, R).
escaped_field([X | Y], [X | R]) :-
X \== '"',
escaped_field(Y, R).
ensure_escaped(Field, Field) :-
(atom(Field); integer(Field); float(Field)).
ensure_escaped([X | Y], Field) :-
escaped_field([X | Y], Field).
write_field(Out, Field, Opt) :-
( Field \== [] ->
ensure_escaped(Field, Field0),
format(Out, "~w", [Field0])
; option(null_value(Null_Value), Opt),
( Null_Value == empty -> true
; format(Out, "~w", [Null_Value]))).
write_row(Out, [Field], Opt) :-
write_field(Out, Field, Opt).
write_row(Out, [Field, X | Y], Opt) :-
write_field(Out, Field, Opt),
option(token_separator(Tk_Sep), Opt),
format(Out, "~w", [Tk_Sep]),
write_row(Out, [X | Y], Opt).
write_rows(Out, [Row], Opt) :-
write_row(Out, Row, Opt).
write_rows(Out, [Row, X | Y], Opt) :-
option(line_separator(Line_Sep), Opt),
write_row(Out, Row, Opt),
format(Out, "~w", [Line_Sep]),
write_rows(Out, [X | Y], Opt).
write_csv_(Out, frame(Header, Rows), Opt) :-
option(with_header(With_Header), Opt),
( With_Header == true ->
write_row(Out, Header, Opt),
option(line_separator(Line_Sep), Opt),
format(Out, "~w", [Line_Sep])
; true),
write_rows(Out, Rows, Opt).
write_csv(File_Name, Frm, Opt) :-
option_extends(Opt, [
null_value(empty),
token_separator(','),
with_header(true),
line_separator('\n')
], Opt0),
setup_call_cleanup(
open(File_Name, write, Out),
write_csv_(Out, Frm, Opt0),
close(Out)).
write_csv(File_Name, Frm) :-
write_csv(File_Name, Frm, []).
%% -- read --
tokens([], Opt), [Tk_Sep] -->
{ option(token_separator(Tk_Sep), Opt) },
[Tk_Sep],
!.
tokens([], _), "\r\n" -->
"\r\n",
!.
tokens([], _), "\n" -->
"\n",
!.
tokens([], _), "\r" -->
"\r",
!.
tokens([X | Y], Opt) -->
[X],
!,
tokens(Y, Opt).
tokens([], _) --> [].
field(R, Opt) -->
"\"",
!,
string_tokens(R, Opt).
field(R, Opt) -->
tokens(R0, Opt),
{ R0 \== [],
catch(number_chars(R, R0), _, R = R0)
}.
field([], _) --> [].
string_tokens(R, Opt) -->
[X],
( { X == '"' } ->
( "\"" ->
{ R = [X | Y] },
string_tokens(Y, Opt)
; { R = [] })
; { R = [X | Y] },
string_tokens(Y, Opt)).
end_token --> "\r\n".
end_token --> "\n".
end_token --> "\r".
end_token --> [].
separator(Opt) -->
{ option(token_separator(Tk_Sep), Opt) },
[Tk_Sep].
row([X | Y], Opt) -->
field(X, Opt),
!,
( separator(Opt) ->
row(Y, Opt)
; end_token ->
{ Y = [] }).
rows(R, Opt) -->
row(X, Opt),
!,
( { X \== [[]] } ->
rows(Y, Opt),
{ R = [X | Y] }
; { R = [] }).
parse_csv(frame(Header, Rows), Opt) -->
{ option_extends(Opt, [
with_header(true),
token_separator(',')
], Opt0)
},
( { option(with_header(With_Header), Opt0),
With_Header == true } ->
row(Header, Opt0),
{ Header \== [[]] },
end_token
; { Header = [] }),
rows(Rows, Opt0).
parse_csv(R) -->
parse_csv(R, []).

164
src/lib/dcgs.pl Normal file
View File

@@ -0,0 +1,164 @@
:- module(dcgs,
[op(1105, xfy, '|'),
phrase/2,
phrase/3,
seq//1,
seqq//1,
... //0
]).
:- use_module(library(error)).
:- use_module(library(lists), [append/3, member/2]).
:- use_module(library(loader), [strip_module/3]).
load_context(GRBody, Module, GRBody0) :-
strip_module(GRBody, Module, GRBody0),
( nonvar(Module) ->
true
; prolog_load_context(module, Module) ->
true
; true
).
:- meta_predicate phrase(2, ?).
:- meta_predicate phrase(2, ?, ?).
phrase(GRBody, S0) :-
phrase(GRBody, S0, []).
phrase(GRBody, S0, S) :-
load_context(GRBody, Module, GRBody0),
( var(GRBody0) ->
instantiation_error(phrase/3)
; dcg_body(GRBody0, S0, S, GRBody1, Module) ->
call(GRBody1)
; type_error(callable, GRBody0, phrase/3)
).
module_call_qualified(M, Call, Call1) :-
( nonvar(M) -> Call1 = M:Call
; Call = Call1
).
% The same version of the below two dcg_rule clauses, but with module scoping.
dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :-
dcg_non_terminal(NonTerminal, S0, S, Head),
dcg_body(GRBody, S0, S1, Goal1, _),
dcg_terminals(Terminals, S, S1, Goal2),
Body = ( Goal1, Goal2 ).
dcg_rule(( M:NonTerminal --> GRBody ), ( M:Head :- Body )) :-
NonTerminal \= ( _, _ ),
dcg_non_terminal(NonTerminal, S0, S, Head),
dcg_body(GRBody, S0, S, Body, _).
% This program uses append/3 as defined in the Prolog prologue.
% Expands a DCG rule into a Prolog rule, when no error condition applies.
dcg_rule(( NonTerminal, Terminals --> GRBody ), ( Head :- Body )) :-
dcg_non_terminal(NonTerminal, S0, S, Head),
dcg_body(GRBody, S0, S1, Goal1, _),
dcg_terminals(Terminals, S, S1, Goal2),
Body = ( Goal1, Goal2 ).
dcg_rule(( NonTerminal --> GRBody ), ( Head :- Body )) :-
NonTerminal \= ( _, _ ),
dcg_non_terminal(NonTerminal, S0, S, Head),
dcg_body(GRBody, S0, S, Body, _).
dcg_non_terminal(NonTerminal, S0, S, Goal) :-
NonTerminal =.. NonTerminalUniv,
append(NonTerminalUniv, [S0, S], GoalUniv),
Goal =.. GoalUniv.
dcg_terminals(Terminals, S0, S, S0 = List) :-
append(Terminals, S, List).
dcg_body(Var, S0, S, Body, M) :-
var(Var),
module_call_qualified(M, Var, Var1),
Body = phrase(Var1, S0, S).
dcg_body(GRBody, S0, S, Body, M) :-
nonvar(GRBody),
dcg_constr(GRBody),
dcg_cbody(GRBody, S0, S, Body, M).
dcg_body(NonTerminal, S0, S, Goal1, M) :-
nonvar(NonTerminal),
\+ dcg_constr(NonTerminal),
NonTerminal \= ( _ -> _ ),
NonTerminal \= ( \+ _ ),
module_call_qualified(M, Goal, Goal1),
dcg_non_terminal(NonTerminal, S0, S, Goal).
% The following constructs in a grammar rule body
% are defined in the corresponding subclauses.
dcg_constr([]). % 7.14.1
dcg_constr([_|_]). % 7.14.2 - terminal sequence
dcg_constr(( _, _ )). % 7.14.3 - concatenation
dcg_constr(( _ ; _ )). % 7.14.4 - alternative
dcg_constr(( _'|'_ )). % 7.14.6 - alternative
dcg_constr({_}). % 7.14.7
dcg_constr(call(_)). % 7.14.8
dcg_constr(phrase(_)). % 7.14.9
dcg_constr(!). % 7.14.10
%% dcg_constr(\+ _). % 7.14.11 - not (existence implementation dep.)
dcg_constr((_->_)). % 7.14.12 - if-then (existence implementation dep.)
% The principal functor of the first argument indicates
% the construct to be expanded.
dcg_cbody([], S0, S, S0 = S, _M).
dcg_cbody([T|Ts], S0, S, Goal, _M) :-
must_be(list, [T|Ts]),
dcg_terminals([T|Ts], S0, S, Goal).
dcg_cbody(( GRFirst, GRSecond ), S0, S, ( First, Second ), M) :-
dcg_body(GRFirst, S0, S1, First, M),
dcg_body(GRSecond, S1, S, Second, M).
dcg_cbody(( GREither ; GROr ), S0, S, ( Either ; Or ), M) :-
\+ subsumes_term(( _ -> _ ), GREither),
dcg_body(GREither, S0, S, Either, M),
dcg_body(GROr, S0, S, Or, M).
dcg_cbody(( GRCond ; GRElse ), S0, S, ( Cond ; Else ), M) :-
subsumes_term(( _GRIf -> _GRThen ), GRCond),
dcg_cbody(GRCond, S0, S, Cond, M),
dcg_body(GRElse, S0, S, Else, M).
dcg_cbody(( GREither '|' GROr ), S0, S, ( Either ; Or ), M) :-
dcg_body(GREither, S0, S, Either, M),
dcg_body(GROr, S0, S, Or, M).
dcg_cbody({Goal}, S0, S, ( Goal1, S0 = S ), M) :-
module_call_qualified(M, Goal, Goal1).
dcg_cbody(call(Cont), S0, S, call(Cont1, S0, S), M) :-
module_call_qualified(M, Cont, Cont1).
dcg_cbody(phrase(Body), S0, S, phrase(Body1, S0, S), M) :-
module_call_qualified(M, Body, Body1).
dcg_cbody(!, S0, S, ( !, S0 = S ), _M).
dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody1,S0,_), S0 = S ), M) :-
module_call_qualified(M, GRBody, GRBody1).
dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then ), M) :-
dcg_body(GRIf, S0, S1, If, M),
dcg_body(GRThen, S1, S, Then, M).
user:term_expansion(Term0, Term) :-
nonvar(Term0),
dcg_rule(Term0, Term).
% Describes a sequence
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
% Describes a sequence of sequences
seqq([]) --> [].
seqq([Es|Ess]) --> seq(Es), seqq(Ess).
% Describes an arbitrary number of elements
... --> [] | [_], ... .
user:goal_expansion(phrase(GRBody, S, S0), GRBody1) :-
load_context(GRBody, M, GRBody0),
nonvar(GRBody0),
catch(dcgs:dcg_body(GRBody0, S, S0, GRBody1, M),
error(E, must_be/2),
( GRBody1 = throw(error(E, must_be/2)) )
).
user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])).

26
src/lib/debug.pl Normal file
View File

@@ -0,0 +1,26 @@
% Source: https://stackoverflow.com/a/30791637
:- module(debug, [
op(900, fx, $),
op(900, fx, $-),
op(950, fy, *),
(*)/1,
($)/1,
($-)/1
]).
:- use_module(library(format), [portray_clause/1]).
:- meta_predicate *(0).
:- meta_predicate $(0).
:- meta_predicate $-(0).
$-(G_0) :-
catch(G_0, Ex, ( portray_clause(exception:Ex:G_0), throw(Ex) ) ).
$(G_0) :-
portray_clause(call:G_0),
$-G_0,
portray_clause(exit:G_0).
*(_).

24
src/lib/diag.pl Normal file
View File

@@ -0,0 +1,24 @@
:- module(diag, [wam_instructions/2]).
:- use_module(library(error)).
wam_instructions(Clause, Listing) :-
( nonvar(Clause) ->
( Clause = Name / Arity ->
fetch_instructions(user, Name, Arity, Listing)
; Clause = Module : (Name / Arity) ->
fetch_instructions(Module, Name, Arity, Listing)
)
; throw(error(instantiation_error, wam_instructions/2))
).
fetch_instructions(Module, Name, Arity, Listing) :-
must_be(atom, Module),
must_be(atom, Name),
must_be(integer, Arity),
( Arity >= 0 ->
'$wam_instructions'(Module, Name, Arity, Listing)
; throw(error(domain_error(not_less_than_zero, Arity), wam_instructions/2))
).

View File

@@ -8,8 +8,8 @@
put_dif_att(Var, X, Y) :-
( get_atts(Var, +dif(Z)) ->
sort([X \== Y | Z], NewZ),
put_atts(Var, +dif(NewZ))
sort([X \== Y | Z], NewZ),
put_atts(Var, +dif(NewZ))
; put_atts(Var, +dif([X \== Y]))
).
@@ -21,8 +21,8 @@ dif_set_variables([Var|Vars], X, Y) :-
append_goals([], _).
append_goals([Var|Vars], Goals) :-
( get_atts(Var, +dif(VarGoals)) ->
append(Goals, VarGoals, NewGoals0),
sort(NewGoals0, NewGoals)
append(Goals, VarGoals, NewGoals0),
sort(NewGoals0, NewGoals)
; NewGoals = Goals
),
put_atts(Var, +dif(NewGoals)),
@@ -30,25 +30,30 @@ append_goals([Var|Vars], Goals) :-
verify_attributes(Var, Value, Goals) :-
( get_atts(Var, +dif(Goals)) ->
term_variables(Value, ValueVars),
append_goals(ValueVars, Goals)
term_variables(Value, ValueVars),
append_goals(ValueVars, Goals)
; Goals = []
).
% Probably the world's worst dif/2 implementation. I'm open to
% suggestions for improvement.
dif(X, Y) :- X \== Y,
( term_variables(X, XVars), term_variables(Y, YVars),
dif_set_variables(XVars, X, Y),
dif_set_variables(YVars, X, Y)
).
dif(X, Y) :-
X \== Y,
( X \= Y -> true
; ( term_variables(X, XVars),
term_variables(Y, YVars),
dif_set_variables(XVars, X, Y),
dif_set_variables(YVars, X, Y)
)
).
gather_dif_goals([]) --> [].
gather_dif_goals([(X \== Y) | Goals]) -->
[dif(X, Y)],
[dif:dif(X, Y)],
gather_dif_goals(Goals).
attribute_goals(X) -->
{ get_atts(X, +dif(Goals)) },
gather_dif_goals(Goals).
gather_dif_goals(Goals),
{ put_atts(X, -dif(_)) }.

143
src/lib/error.pl Normal file
View File

@@ -0,0 +1,143 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written September 2018 by Markus Triska (triska@metalevel.at)
I place this code in the public domain. Use it in any way you want.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(error, [must_be/2,
can_be/2,
instantiation_error/1,
domain_error/3,
type_error/3
]).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
must_be(Type, Term)
This predicate is intended for type-checks of built-in predicates.
It asserts that Term is:
1) instantiated *and*
2) instantiated to an instance of the given Type.
It corresponds to usage mode +Term.
Currently, the following types are supported:
- atom
- boolean
- character
- chars
- integer
- list
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
must_be(Type, Term) :-
must_be_(type, Type),
must_be_(Type, Term).
must_be_(Type, _) :-
var(Type),
instantiation_error(must_be/2).
must_be_(var, Term) :-
( var(Term) -> true
; throw(error(uninstantiation_error(Term), must_be/2))
).
must_be_(integer, Term) :- check_(integer, integer, Term).
must_be_(atom, Term) :- check_(atom, atom, Term).
must_be_(character, T) :- check_(error:character, character, T).
must_be_(chars, Ls) :-
must_be(list, Ls),
( '$is_partial_string'(Ls) ->
% The expected case (success) uses a very fast test.
% We cannot use partial_string/1 from library(iso_ext),
% because that library itself imports library(error).
true
; all_characters(Ls)
).
must_be_(list, Term) :- check_(error:ilist, list, Term).
must_be_(type, Term) :- check_(error:type, type, Term).
must_be_(boolean, Term) :- check_(error:boolean, boolean, Term).
% We cannot use maplist(must_be(character), Cs), because library(lists)
% uses library(error), so importing it would create a cyclic dependency.
all_characters([]).
all_characters([C|Cs]) :-
must_be(character, C),
all_characters(Cs).
check_(Pred, Type, Term) :-
( var(Term) -> instantiation_error(must_be/2)
; call(Pred, Term) -> true
; type_error(Type, Term, must_be/2)
).
boolean(B) :- ( B == true ; B == false ).
character(C) :-
atom(C),
atom_length(C, 1).
ilist(Ls) :-
'$skip_max_list'(_, _, Ls, Rs),
( var(Rs) ->
instantiation_error(must_be/2)
; Rs == []
).
type(type).
type(integer).
type(atom).
type(character).
type(chars).
type(list).
type(var).
type(boolean).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
can_be(Type, Term)
This predicate is intended for type-checks of built-in predicates.
It asserts that there is a substitution which, if applied to Term,
makes it an instance of Type.
It corresponds to usage mode ?Term.
It supports the same types as must_be/2.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
can_be(Type, Term) :-
must_be(type, Type),
( var(Term) -> true
; can_(Type, Term) -> true
; type_error(Type, Term, can_be/2)
).
can_(integer, Term) :- integer(Term).
can_(atom, Term) :- atom(Term).
can_(character, T) :- character(T).
can_(chars, Ls) :- '$is_partial_string'(Ls).
can_(list, Term) :- list_or_partial_list(Term).
can_(boolean, Term) :- boolean(Term).
list_or_partial_list(Ls) :-
'$skip_max_list'(_, _, Ls, Rs),
( var(Rs) -> true
; Rs == []
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Shorthands for throwing ISO errors.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
instantiation_error(Context) :-
throw(error(instantiation_error, Context)).
domain_error(Type, Term, Context) :-
throw(error(domain_error(Type, Term), Context)).
type_error(Type, Term, Context) :-
throw(error(type_error(Type, Term), Context)).

228
src/lib/files.pl Normal file
View File

@@ -0,0 +1,228 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written June 2020 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
Predicates for reasoning about files and directories.
In this library, directories and files are represented as
*lists of characters*. This is an ideal representation:
-) Lists of characters can be conveniently reasoned about with DCGs
and built-in Prolog predicates from library(lists). This alone
is already a very compelling argument to use them.
-) Other Scryer libraries such as library(http/http_open) also already
use lists of characters to represent paths.
-) File names are mostly ephemeral, so it is good for efficiency
that they can quickly allocated transiently on the heap, leaving the
atom table mostly unaffected. Indexing is almost never needed
for file names. If needed, it should be added to the engine.
-) The previous point is also good for security, since the system
leaves little trace of which files were even accessed.
-) Scryer Prolog represents lists of characters extremely compactly.
Some Prolog programmers will likely find this representation quite
unusual, because files are represented as *atoms* in many systems.
However, the ISO standard only demands that sources and sinks be
*ground* terms, so lists of characters are completely admissible:
A source/sink is specified as an implementation defined
ground term in a call of open/4 (8.11.5). All subsequent
references to the source/sink are made by referring to a
stream-term (7.10.2) or alias (7.10.2.2).
I believe that with the advent of Scryer Prolog and its efficient
representation of strings as lists of characters, we should take
this opportunity for improvement, and in fact extend the use of
lists of characters also to other predicates like open/3.
Please note that we *cannot* simply accept *both* representations,
because that would invalidate the type errors raised by this library,
and make future extensions for type checking impossible.
So I ask you: Please try out this representation for a few months.
It simplifies working with files considerably. Once we have collected
more experience with this representations, please let us consider
how to proceed in such a way that we can call it an improvement.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(files, [directory_files/2,
file_size/2,
file_exists/1,
directory_exists/1,
delete_file/1,
rename_file/2,
delete_directory/1,
make_directory/1,
make_directory_path/1,
working_directory/2,
path_canonical/2,
path_segments/2,
file_modification_time/2,
file_creation_time/2,
file_access_time/2]).
:- use_module(library(error)).
:- use_module(library(lists)).
:- use_module(library(charsio)).
list_of_chars(Cs) :-
must_be(list, Cs),
maplist(must_be(character), Cs).
directory_files(Directory, Files) :-
list_of_chars(Directory),
can_be(list, Files),
'$directory_files'(Directory, Files).
file_size(File, Size) :-
file_must_exist(File, file_size/2),
list_of_chars(File),
can_be(integer, Size),
'$file_size'(File, Size).
file_exists(File) :-
list_of_chars(File),
'$file_exists'(File).
directory_exists(Directory) :-
list_of_chars(Directory),
'$directory_exists'(Directory).
make_directory(Directory) :-
list_of_chars(Directory),
'$make_directory'(Directory).
make_directory_path(Directory) :-
list_of_chars(Directory),
'$make_directory_path'(Directory).
delete_file(File) :-
file_must_exist(File, delete_file/1),
list_of_chars(File),
'$delete_file'(File).
rename_file(File, Renamed) :-
file_must_exist(File, rename_file/2),
list_of_chars(File),
list_of_chars(Renamed),
'$rename_file'(File, Renamed).
delete_directory(Directory) :-
directory_must_exist(Directory, delete_directory/1),
list_of_chars(Directory),
'$delete_directory'(Directory).
file_must_exist(File, Context) :-
( file_exists(File) -> true
; throw(error(existence_error(file, File), Context))
).
directory_must_exist(Directory, Context) :-
( directory_exists(Directory) -> true
; throw(error(existence_error(directory, Directory), Context))
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Dir0 is the current working directory, and the working directory
is changed to Dir.
Use working_directory(Ds, Ds) to determine the current working directory,
and leave it as is.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
working_directory(Dir0, Dir) :-
can_be(list, Dir0),
can_be(list, Dir),
'$working_directory'(Dir0, Dir).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
True iff Cs is the canonical, absolute path of Ps.
All intermediate components are normalized, and all symbolic links
are resolved.
The predicate fails in the following situations, though not
necessarily *only* in these cases:
1. Ps is a path that does not exist.
2. A non-final component in Ps is not a directory.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
path_canonical(Ps, Cs) :-
must_be(list, Ps),
maplist(must_be(character), Ps),
can_be(list, Cs),
'$path_canonical'(Ps, Cs).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
T is, respectively, the modification, access or creation time of File.
T is a time stamp, suitable for use in format_time//2 in library(time).
For two time stamps A and B, if A precedes B, then A @< B holds.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
file_modification_time(File, T) :-
file_time_(File, modification, T).
file_access_time(File, T) :-
file_time_(File, access, T).
file_creation_time(File, T) :-
file_time_(File, creation, T).
file_time_(File, Which, T) :-
file_must_exist(File, file_time_/3),
'$file_time'(File, Which, T0),
read_from_chars(T0, T).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
path_segments(Ps, Segments): True iff Segments are the segments of Ps.
Segments is the list of components of the path Ps that are
separated by the platform-specific directory separator. Each
segment is a list of characters.
At least one of the arguments must be instantiated.
Examples:
?- path_segments("/hello/there", Segments).
Segments = [[],"hello","there"].
?- path_segments(Path, ["hello","there"]).
Path = "hello/there".
To obtain the platform-specific directory separator, you can use:
?- path_segments(Separator, ["",""]).
Separator = "/".
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
path_segments(Path, Segments) :-
'$directory_separator'(Sep),
( var(Path) ->
must_be(list, Segments),
maplist(list_of_chars, Segments),
append_with_separator(Segments, Sep, Path)
; list_of_chars(Path),
path_to_segments(Path, Sep, Segments)
).
append_with_separator([], _, []).
append_with_separator([Segment|Segments], Sep, Path) :-
append_with_separator_(Segments, Segment, Sep, Path).
append_with_separator_([], Segment, _, Segment).
append_with_separator_([Segment|Segments], Prev, Sep, Path) :-
append(Prev, [Sep|Rest], Path),
append_with_separator_(Segments, Segment, Sep, Rest).
path_to_segments(Path, Sep, Segments) :-
( append(Front, [Sep|Ps], Path) ->
Segments = [Front|Rest],
path_to_segments(Ps, Sep, Rest)
; Segments = [Path]
).

636
src/lib/format.pl Normal file
View File

@@ -0,0 +1,636 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written 2020, 2021, 2022 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
This library provides the nonterminal format_//2 to describe
formatted strings. format/[2,3] are provided for impure output.
Usage:
======
phrase(format_(FormatString, Arguments), Ls)
format_//2 describes a list of characters Ls that are formatted
according to FormatString. FormatString is a string (i.e.,
a list of characters) that specifies the layout of Ls.
The characters in FormatString are used literally, except
for the following tokens with special meaning:
~w use the next available argument from Arguments here
~q use the next argument here, formatted as by writeq/1
~a use the next argument here, which must be an atom
~s use the next argument here, which must be a string
~d use the next argument here, which must be an integer
~f use the next argument here, a floating point number
~Nf where N is an integer: format the float argument
using N digits after the decimal point
~Nd like ~d, placing the last N digits after a decimal point;
if N is 0 or omitted, no decimal point is used.
~ND like ~Nd, separating digits to the left of the decimal point
in groups of three, using the character "," (comma)
~NU like ~ND, using "_" (underscore) to separate groups of digits
~NL format an integer so that at most N digits appear on a line.
If N is 0 or omitted, it defaults to 72.
~Nr where N is an integer between 2 and 36: format the
next argument, which must be an integer, in radix N.
The characters "a" to "z" are used for radices 10 to 36.
If N is omitted, it defaults to 8 (octal).
~NR like ~Nr, except that "A" to "Z" are used for radices > 9
~| place a tab stop at this position
~N| where N is an integer: place a tab stop at text column N
~N+ where N is an integer: place a tab stop N characters
after the previous tab stop (or start of line)
~t distribute spaces evenly between the two closest tab stops
~`Ct like ~t, use character C instead of spaces to fill the space
~n newline
~Nn N newlines
~i ignore the next argument
~~ the literal ~
Instead of ~N, you can write ~* to use the next argument from Arguments
as the numeric argument.
The predicate format/2 is like format_//2, except that it outputs
the text on the terminal instead of describing it declaratively.
format/3, used as format(Stream, FormatString, Arguments), outputs
the described string to the given Stream. If Stream is a binary
stream, then the code of each emitted character must be in 0..255.
If at all possible, format_//2 should be used, to stress pure parts
that enable easy testing etc. If necessary, you can emit the list Ls
with maplist(put_char, Ls) or, much faster, with format("~s", [Ls]).
Ideally, however, you use phrase_to_file/[2,3] or phrase_to_stream/2
from library(pio) to write the described list directly to a file
or stream, respectively: phrase_to_stream(format_(..., [...]), S).
The advantage of this is that an ideal implementation writes
the characters as they become known, without manifesting the list.
The entire library only works if the Prolog flag double_quotes
is set to chars, the default value in Scryer Prolog. This should
also stay that way, to encourage a sensible environment.
Example:
?- phrase(format_("~s~n~`.t~w!~12|", ["hello",there]), Cs).
%@ Cs = "hello\n......there!".
I place this code in the public domain. Use it in any way you want.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(format, [format_//2,
format/2,
format/3,
portray_clause_//1,
portray_clause/1,
portray_clause/2,
listing/1
]).
:- use_module(library(dcgs)).
:- use_module(library(lists)).
:- use_module(library(error)).
:- use_module(library(charsio)).
:- use_module(library(between)).
:- use_module(library(pio)).
format_(Fs, Args) -->
{ must_be(list, Fs),
must_be(list, Args),
unique_variable_names(Args, VNs),
phrase(cells(Fs,Args,0,[],VNs), Cells) },
format_cells(Cells).
format_cells([]) --> [].
format_cells([Cell|Cells]) -->
format_cell(Cell),
format_cells(Cells).
format_cell(newline) --> "\n".
format_cell(cell(From,To,Es)) -->
% distribute the space between the glue elements
{ phrase(elements_gluevars(Es, 0, Length), Vs),
( Vs = [] -> true
; Space is To - From - Length,
( Space =< 0 -> maplist(=(0), Vs)
; length(Vs, NumGlue),
Distr is Space // NumGlue,
Delta is Space - Distr*NumGlue,
( Delta =:= 0 ->
maplist(=(Distr), Vs)
; BigGlue is Distr + Delta,
reverse(Vs, [BigGlue|Rest]),
maplist(=(Distr), Rest)
)
)
) },
format_elements(Es).
format_elements([]) --> [].
format_elements([E|Es]) -->
format_element(E),
format_elements(Es).
format_element(chars(Cs)) --> seq(Cs).
format_element(glue(Fill,Num)) -->
{ length(Ls, Num),
maplist(=(Fill), Ls) },
seq(Ls).
elements_gluevars([], N, N) --> [].
elements_gluevars([E|Es], N0, N) -->
element_gluevar(E, N0, N1),
elements_gluevars(Es, N1, N).
element_gluevar(chars(Cs), N0, N) -->
{ length(Cs, L),
N is N0 + L }.
element_gluevar(glue(_,V), N, N) --> [V].
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Our key datastructure is a list of cells and newlines.
A cell has the shape cell(From,To,Elements), where
From and To denote the positions of surrounding tab stops.
Elements is a list of elements that occur in a cell,
namely terms of the form chars(Cs) and glue(Char, Var).
"glue" elements (TeX terminology) are evenly stretched
to fill the remaining whitespace in the cell. For each
glue element, the character Char is used for filling,
and Var is a free variable that is used when the
available space is distributed.
newline is used if ~n occurs in a format string.
It is used because a newline character does not
consume whitespace in the sense of format strings.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
cells([], Args, Tab, Es, _) --> !,
( { Args == [] } -> cell(Tab, Tab, Es)
; { domain_error(empty_list, Args, format_//2) }
).
cells([~,~|Fs], Args, Tab, Es, VNs) --> !,
cells(Fs, Args, Tab, [chars("~")|Es], VNs).
cells([~,w|Fs], [Arg|Args], Tab, Es, VNs) --> !,
{ write_term_to_chars(Arg, [numbervars(true),variable_names(VNs)], Chars) },
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
cells([~,q|Fs], [Arg|Args], Tab, Es, VNs) --> !,
{ write_term_to_chars(Arg, [quoted(true),numbervars(true),variable_names(VNs)], Chars) },
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
cells([~,a|Fs], [Arg|Args], Tab, Es, VNs) --> !,
{ atom_chars(Arg, Chars) },
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num, [d|Fs], Args0, [Arg0|Args]) },
!,
{ Arg is Arg0, % evaluate compound expression
must_be(integer, Arg),
number_chars(Arg, Cs0) },
( { Num =:= 0 } -> { Cs = Cs0 }
; { length(Cs0, L),
( L =< Num ->
Delta is Num - L,
length(Zs, Delta),
maplist(=('0'), Zs),
phrase(("0.",seq(Zs),seq(Cs0)), Cs)
; BeforeComma is L - Num,
length(Bs, BeforeComma),
append(Bs, Ds, Cs0),
phrase((seq(Bs),".",seq(Ds)), Cs)
) }
),
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num, ['D'|Fs], Args0, [Arg|Args]) },
!,
{ separate_digits_fractional(Arg, ',', Num, Cs) },
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num, ['U'|Fs], Args0, [Arg|Args]) },
!,
{ separate_digits_fractional(Arg, '_', Num, Cs) },
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num0, ['L'|Fs], Args0, [Arg|Args]) },
!,
{ ( Num0 =:= 0 ->
Num = 72
; Num = Num0
),
phrase(format_("~d", [Arg]), Cs0),
phrase(split_lines_width(Cs0, Num), Cs) },
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
cells([~,i|Fs], [_|Args], Tab, Es, VNs) --> !,
cells(Fs, Args, Tab, Es, VNs).
cells([~,n|Fs], Args, Tab, Es, VNs) --> !,
cell(Tab, Tab, Es),
n_newlines(1),
cells(Fs, Args, 0, [], VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num, [n|Fs], Args0, Args) },
!,
cell(Tab, Tab, Es),
n_newlines(Num),
cells(Fs, Args, 0, [], VNs).
cells([~,s|Fs], [Arg|Args], Tab, Es, VNs) --> !,
cells(Fs, Args, Tab, [chars(Arg)|Es], VNs).
cells([~,f|Fs], [Arg|Args], Tab, Es, VNs) --> !,
{ format_number_chars(Arg, Chars) },
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num, [f|Fs], Args0, [Arg|Args]) },
!,
{ format_number_chars(Arg, Cs0),
phrase(upto_what(Bs, .), Cs0, Cs),
( Num =:= 0 -> Chars = Bs
; ( Cs = ['.'|Rest] ->
length(Rest, L),
( Num < L ->
length(Ds, Num),
append(Ds, _, Rest)
; Num =:= L ->
Ds = Rest
; Num > L,
Delta is Num - L,
% we should look into the float with
% greater accuracy here, and use the
% actual digits instead of 0.
length(Zs, Delta),
maplist(=('0'), Zs),
append(Rest, Zs, Ds)
)
; length(Ds, Num),
maplist(=('0'), Ds)
),
append(Bs, ['.'|Ds], Chars)
) },
cells(Fs, Args, Tab, [chars(Chars)|Es], VNs).
cells([~,r|Fs], Args, Tab, Es, VNs) --> !,
cells([~,'8',r|Fs], Args, Tab, Es, VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num, [r|Fs], Args0, [Arg|Args]) },
!,
{ integer_to_radix(Arg, Num, lowercase, Cs) },
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
cells([~,'R'|Fs], Args, Tab, Es, VNs) --> !,
cells([~,'8','R'|Fs], Args, Tab, Es, VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num, ['R'|Fs], Args0, [Arg|Args]) },
!,
{ integer_to_radix(Arg, Num, uppercase, Cs) },
cells(Fs, Args, Tab, [chars(Cs)|Es], VNs).
cells([~,'`',Char,t|Fs], Args, Tab, Es, VNs) --> !,
cells(Fs, Args, Tab, [glue(Char,_)|Es], VNs).
cells([~,t|Fs], Args, Tab, Es, VNs) --> !,
cells(Fs, Args, Tab, [glue(' ',_)|Es], VNs).
cells([~,'|'|Fs], Args, Tab0, Es, VNs) --> !,
{ phrase(elements_gluevars(Es, 0, Width), _),
Tab is Tab0 + Width },
cell(Tab0, Tab, Es),
cells(Fs, Args, Tab, [], VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num, ['|'|Fs], Args0, Args) },
!,
cell(Tab, Num, Es),
cells(Fs, Args, Num, [], VNs).
cells([~|Fs0], Args0, Tab0, Es, VNs) -->
{ numeric_argument(Fs0, Num, [+|Fs], Args0, Args) },
!,
{ Tab is Tab0 + Num },
cell(Tab0, Tab, Es),
cells(Fs, Args, Tab, [], VNs).
cells([~|Cs], Args, _, _, _) -->
( { Args == [] } ->
{ domain_error(non_empty_list, [], format_//2) }
; { domain_error(format_string, [~|Cs], format_//2) }
).
cells(Fs0, Args, Tab, Es, VNs) -->
{ phrase(upto_what(Fs1, ~), Fs0, Fs),
Fs1 = [_|_] },
cells(Fs, Args, Tab, [chars(Fs1)|Es], VNs).
format_number_chars(N0, Chars) :-
N is N0, % evaluate compound expression
number_chars(N, Chars).
n_newlines(0) --> !.
n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- phrase(upto_what(Cs, ~), "abc~test", Rest).
Cs = [a,b,c], Rest = [~,t,e,s,t].
?- phrase(upto_what(Cs, ~), "abc", Rest).
Cs = [a,b,c], Rest = [].
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
separate_digits_fractional(Arg, Sep, Num, Cs) :-
number_chars(Num, NCs),
phrase(("~",seq(NCs),"d"), FStr),
phrase(format_(FStr, [Arg]), Cs0),
phrase(upto_what(Bs0, .), Cs0, Ds),
reverse(Bs0, Bs1),
phrase(groups_of_three(Bs1,Sep), Bs2),
reverse(Bs2, Bs),
append(Bs, Ds, Cs).
upto_what([], W), [W] --> [W], !.
upto_what([C|Cs], W) --> [C], !, upto_what(Cs, W).
upto_what([], _) --> [].
groups_of_three([A,B,C,D|Rs], Sep) --> !, [A,B,C,Sep], groups_of_three([D|Rs], Sep).
groups_of_three(Ls, _) --> seq(Ls).
split_lines_width(Cs, Num) -->
( { length(Prefix, Num),
append(Prefix, [R|Rs], Cs) } ->
seq(Prefix), "_\n",
split_lines_width([R|Rs], Num)
; seq(Cs)
).
cell(From, To, Es0) -->
( { Es0 == [] } -> []
; { reverse(Es0, Es) },
[cell(From,To,Es)]
).
%?- format:numeric_argument("2f", Num, [f|Fs], Args0, Args).
%?- format:numeric_argument("100b", Num, Rs, Args0, Args).
numeric_argument(Ds, Num, Rest, Args0, Args) :-
( Ds = [*|Rest] ->
Args0 = [Num|Args]
; phrase(numeric_argument_(Ds, Rest), Ns),
foldl(plus_times10, Ns, 0, Num),
Args0 = Args
).
numeric_argument_([D|Ds], Rest) -->
( { member(D, "0123456789") } ->
{ number_chars(N, [D]) },
[N],
numeric_argument_(Ds, Rest)
; { Rest = [D|Ds] }
).
plus_times10(D, N0, N) :- N is D + N0*10.
radix_error(lowercase, R) --> format_("~~~dr", [R]).
radix_error(uppercase, R) --> format_("~~~dR", [R]).
integer_to_radix(I0, R, Which, Cs) :-
I is I0, % evaluate compound expression
must_be(integer, I),
must_be(integer, R),
( \+ between(2, 36, R) ->
phrase(radix_error(Which,R), Es),
domain_error(format_string, Es, format_//2)
; true
),
digits(Which, Ds),
( I < 0 ->
Pos is abs(I),
phrase(integer_to_radix_(Pos, R, Ds), Cs0, "-")
; I =:= 0 -> Cs0 = "0"
; phrase(integer_to_radix_(I, R, Ds), Cs0)
),
reverse(Cs0, Cs).
integer_to_radix_(0, _, _) --> !.
integer_to_radix_(I0, R, Ds) -->
{ M is I0 mod R,
nth0(M, Ds, D),
I is I0 // R },
[D],
integer_to_radix_(I, R, Ds).
digits(lowercase, "0123456789abcdefghijklmnopqrstuvwxyz").
digits(uppercase, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Impure I/O, implemented as a small wrapper over format_//2.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
format(Fs, Args) :-
current_output(Stream),
format(Stream, Fs, Args).
format(Stream, Fs, Args) :-
phrase_to_stream(format_(Fs, Args), Stream),
flush_output(Stream).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- phrase(format:cells("hello", [], 0, [], []), Cs).
?- phrase(format:cells("hello~10|", [], 0, [], []), Cs).
?- phrase(format:cells("~ta~t~10|", [], 0, [], []), Cs).
?- phrase(format_("~`at~50|", []), Ls).
?- phrase(format:cells("~`at~50|", [], 0, [], []), Cs),
phrase(format:format_cells(Cs), Ls).
?- phrase(format:cells("~ta~t~tb~tc~21|", [], 0, [], []), Cs).
Cs = [cell(0,21,[glue(' ',_A),chars("a"),glue(' ',_B),glue(' ',_C),chars("b"),glue(' ',_D),chars("c ...")])]
?- phrase(format:cells("~ta~t~4|", [], 0, [], []), Cs).
Cs = [cell(0,4,[glue(' ',_A),chars("a"),glue(' ',_B)])]
?- phrase(format:format_cell(cell(0,1,[glue(a,_94)])), Ls).
?- phrase(format:format_cell(cell(0,50,[chars("hello")])), Ls).
?- phrase(format_("~`at~50|~n", []), Ls).
?- phrase(format_("hello~n~tthere~6|", []), Ls).
?- format("~ta~t~4|", []).
a true.
?- format("~ta~tb~tc~10|", []).
a b c true.
?- format("~tabc~3|", []).
?- format("~ta~t~4|", []).
?- format("~ta~t~tb~tc~20|", []).
a b c true.
?- format("~2f~n", [3]).
3.00
true.
?- format("~20f", [0.1]).
0.10000000000000000000 true.
?- X is atan(2), format("~7f~n", [X]).
1.1071487
X = 1.1071487177940906.
?- format("~`at~50|~n", []).
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
true
?- format("~t~N", []).
?- format("~q", [.]).
'.' true.
?- format("~12r", [300]).
210 true.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
We also provide rudimentary versions of portray_clause/1 and listing/1.
In the eventual library organization, portray_clause/1 and
related predicates may be placed in their own dedicated library.
portray_clause/1 is useful for printing solutions in such a way
that they can be read back with read/1.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
portray_clause(Term) :-
current_output(Out),
portray_clause(Out, Term).
portray_clause(Stream, Term) :-
phrase_to_stream(portray_clause_(Term), Stream),
flush_output(Stream).
portray_clause_(Term) -->
{ unique_variable_names(Term, VNs) },
portray_(Term, VNs), ".\n".
unique_variable_names(Term, VNs) :-
term_variables(Term, Vs),
foldl(var_name, Vs, VNs, 0, _).
var_name(V, Name=V, Num0, Num) :-
charsio:fabricate_var_name(numbervars, Name, Num0),
Num is Num0 + 1.
literal(Lit, VNs) -->
{ write_term_to_chars(Lit, [quoted(true),variable_names(VNs)], Ls) },
seq(Ls).
portray_(Var, VNs) --> { var(Var) }, !, literal(Var, VNs).
portray_((Head :- Body), VNs) --> !,
literal(Head, VNs), " :-\n",
body_(Body, 0, 3, VNs).
portray_((Head --> Body), VNs) --> !,
literal(Head, VNs), " -->\n",
body_(Body, 0, 3, VNs).
portray_(Any, VNs) --> literal(Any, VNs).
body_(Var, C, I, VNs) --> { var(Var) }, !,
indent_to(C, I),
literal(Var, VNs).
body_((A,B), C, I, VNs) --> !,
body_(A, C, I, VNs), ",\n",
body_(B, 0, I, VNs).
body_(Body, C, I, VNs) -->
{ body_if_then_else(Body, If, Then, Else) },
!,
indent_to(C, I),
"( ",
{ C1 is I + 3 },
body_(If, C1, C1, VNs), " ->\n",
body_(Then, 0, C1, VNs), "\n",
else_branch(Else, I, VNs).
body_((A;B), C, I, VNs) --> !,
indent_to(C, I),
"( ",
{ C1 is I + 3 },
body_(A, C1, C1, VNs), "\n",
else_branch(B, I, VNs).
body_(Goal, C, I, VNs) -->
indent_to(C, I), literal(Goal, VNs).
% True iff Body has the shape ( If -> Then ; Else ).
body_if_then_else(Body, If, Then, Else) :-
nonvar(Body),
Body = (A ; Else),
nonvar(A),
A = (If -> Then).
else_branch(Else, I, VNs) -->
indent_to(0, I),
"; ",
{ C is I + 3 },
( { body_if_then_else(Else, If, Then, NextElse) } ->
body_(If, C, C, VNs), " ->\n",
body_(Then, 0, C, VNs), "\n",
else_branch(NextElse, I, VNs)
; { nonvar(Else), Else = ( A ; B ) } ->
body_(A, C, C, VNs), "\n",
else_branch(B, I, VNs)
; body_(Else, C, C, VNs), "\n",
indent_to(0, I),
")"
).
indent_to(CurrentColumn, Indent) -->
format_("~t~*|", [Indent-CurrentColumn]).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- portray_clause(a).
a.
?- portray_clause((a :- b)).
a :-
b.
?- portray_clause((a :- b, c, d)).
a :-
b,
c,
d.
true.
?- portray_clause([a,b,c,d]).
"abcd".
?- portray_clause(X).
?- portray_clause((f(X) :- X)).
?- portray_clause((h :- ( a -> b; c))).
?- portray_clause((h :- ( (a -> x ; y) -> b; c))).
?- portray_clause((h(X) :- ( (a(X) ; y(A,B)) -> b; c))).
?- portray_clause((h :- (a,d;b,c) ; (b,e;d))).
?- portray_clause((a :- b ; c ; d)).
?- portray_clause((h :- L = '.')).
?- portray_clause(-->(a, (b, {t}, d))).
?- portray_clause((A :- B)).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
listing(PI) :-
nonvar(PI),
( PI = Name/Arity0 ->
Arity = Arity0
; PI = Name//Arity0 ->
Arity is Arity0 + 2
; type_error(predicate_indicator, PI, listing/1)
),
functor(Head, Name, Arity),
\+ \+ clause(Head, _), % only true if there is at least one clause
( clause(Head, Body),
( Body == true ->
portray_clause(Head)
; portray_clause((Head :- Body))
),
false
; true
).

View File

@@ -3,6 +3,8 @@
:- use_module(library(atts)).
:- use_module(library(dcgs)).
:- meta_predicate freeze(?, 0).
:- attribute frozen/1.
verify_attributes(Var, Other, Goals) :-
@@ -21,15 +23,8 @@ freeze(X, Goal) :-
put_atts(Fresh, frozen(Goal)),
Fresh = X.
gather_freeze_goals(Attrs, _) -->
{ var(Attrs) },
!.
gather_freeze_goals([frozen(X) | _], Var) -->
[freeze(Var, X)],
!.
gather_freeze_goals([_ | Attrs], Var) -->
gather_freeze_goals(Attrs, Var).
attribute_goals(Var) -->
{ get_atts(Var, frozen(Goals)),
put_atts(Var, -frozen(_)) },
[freeze(Var, Goals)].
attribute_goals(X) -->
{ '$get_attr_list'(X, Attrs) },
gather_freeze_goals(Attrs, X).

Some files were not shown because too many files have changed in this diff Show More