639 Commits

Author SHA1 Message Date
Mark Thom
3fc5be0d03 reconcile '[]' and [] (#644), solve remaining conformity issues (#648), move (-->) from dcgs.pl to builtins.pl (#649) 2020-08-02 23:33:37 -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
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
121 changed files with 40316 additions and 11788 deletions

6
.dockerignore Executable file
View File

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

View File

@@ -1,14 +1,31 @@
language: rust
rust:
- stable
- beta
- nightly
matrix:
allow_failures:
- rust: nightly
fast_finish: true
cache: cargo
os: linux
dist: xenial
script:
- cargo build --verbose --all
- cargo test --verbose --all
- cargo test --verbose --all --no-default-features --features num
before_script:
- cargo fetch
jobs:
allow_failures:
env:
- CAN_FAIL=true
include:
- stage: "Stable: Build"
rust: stable
script: cargo rustc --verbose -- -D warnings
name: "Build Stable"
- stage: "Stable: Tests"
rust: stable
script: cargo test --verbose --all
name: "Tests Stable"
- stage: "Features"
rust: stable
script: cargo test --verbose --all --no-default-features --features num
name: "num Tests"
env: CAN_FAIL=true
- stage: "Beta: Build"
# - #
rust: beta
script: cargo rustc --verbose -- -D warnings
name: "Build Beta"

1609
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,15 @@
[package]
name = "scryer-prolog"
version = "0.8.118"
version = "0.8.127"
authors = ["Mark Thom <markjordanthom@gmail.com>"]
build = "build.rs"
repository = "https://github.com/mthom/scryer-prolog"
description = "A modern Prolog implementation written mostly in Rust."
license = "BSD-3-Clause"
edition = "2018"
description = "A modern Prolog implementation written mostly in Rust."
readme = "README.md"
repository = "https://github.com/mthom/scryer-prolog"
license = "BSD-3-Clause"
keywords = ["prolog", "prolog-interpreter", "prolog-system"]
categories = ["command-line-utilities"]
build = "build.rs"
[build-dependencies]
indexmap = "1.0.2"
@@ -16,16 +19,32 @@ default = ["rug", "prolog_parser/rug"]
num = ["num-rug-adapter", "prolog_parser/num"]
[dependencies]
cpu-time = "1.0.0"
crossterm = "0.16.0"
dirs = "2.0.2"
divrem = "0.1.0"
downcast = "0.10.0"
git-version = "0.3.4"
hostname = "0.3.1"
indexmap = "1.0.2"
lazy_static = "1.4.0"
libc = "0.2.62"
nix = "0.15.0"
num-rug-adapter = { optional = true, version = "0.1.1" }
num-rug-adapter = { optional = true, version = "0.1.3" }
ordered-float = "0.5.0"
prolog_parser = { version = "0.8.40", default-features = false }
prolog_parser = { version = "0.8.65", default-features = false }
ref_thread_local = "0.0.0"
rug = { version = "1.4.0", optional = true }
rustyline = "5.0.3"
termion = "1.4.0"
rustyline = "6.0.0"
unicode_reader = "1.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"
sodiumoxide = "0.2.6"

30
Dockerfile Executable file
View File

@@ -0,0 +1,30 @@
# Based on https://hub.docker.com/_/rust?tab=description and https://blog.sedrik.se/posts/my-docker-setup-for-rust/
# The first container is for build purposes only.
FROM rust as builder
WORKDIR /usr/src/scryer-prolog
# Using a dummy build.rs and src/main.rs with your Cargo.toml lets Docker cache your Rust dependencies and not rebuild
# them every time.
COPY Cargo.toml .
COPY Cargo.lock .
RUN mkdir -p src
RUN echo "fn main() {}" > src/main.rs
RUN echo "fn main() {}" > build.rs
RUN cargo build --release
# We need to touch our real main.rs and build.rs files or else
# docker will use the cached ones.
COPY . .
RUN touch src/main.rs
RUN touch build.rs
RUN cargo build --release
RUN ls ./target/release
# Finally, copy the scryer-prolog executable to a slimmer container.
FROM debian:buster-slim
COPY --from=builder /usr/src/scryer-prolog/target/release/scryer-prolog /usr/local/bin/scryer-prolog
CMD ["scryer-prolog"]

405
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,7 +32,7 @@ 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
- [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.
@@ -40,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`,
@@ -51,12 +53,15 @@ Extend Scryer Prolog to include the following, among other features:
- [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 list of
chars, using a packed internal representation.
- [ ] clp(B) and clp() as builtin libraries (_in progress_).
- [ ] Streams and predicates for stream control (_in progress_).
- [ ] An incremental compacting garbage collector satisfying the five
(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.
- [ ] Incremental compilation and loading process, newly written,
primarily in Prolog. (_in progress_)
- [ ] A compacting garbage collector satisfying the five
properties of "Precise Garbage Collection in Prolog."
- [ ] Mode declarations.
@@ -90,10 +95,18 @@ strings.
## 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 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
@@ -117,69 +130,85 @@ $> cargo run [--release]
The optional `--release` flag will perform various optimizations,
producing a faster executable.
Note on compatibility: Scryer Prolog should work on Linux, Mac OS X,
and BSD variants on which Rust runs. Windows support hinges on
rustyline and Termion being functional in that environment, which to
my knowledge is not presently the case.
### 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:
```
?- hello(What).
What = declarative_world
; What = pure_world.
```
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;`h` to show a help message.
To quit Scryer Prolog, use the standard predicate `halt/0`:
To quit scryer-prolog, type
```
?- halt.
```
@@ -190,46 +219,247 @@ 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
### Strings and partial strings
Scryer has three specialized non-ISO predicates for handling so-called
"partial strings." Partial strings imitate difference lists of
characters, but their characters are packed in UTF-8 format, a much
more efficient alternative to how lists of characters are represented
in many other Prologs.
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.
To use partial strings, the `non_iso` library must be loaded:
For example, the following query succeeds:
`?- use_module(library(non_iso)).`
```
?- "abc" = [a,b,c].
true.
```
If `X` is a free variable, the query
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.
`?- partial_string("abc", X, _), X = [a, b, c | Y], partial_string(X),
partial_string_tail(X, Tail), Tail == Y.`
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:
will succeed, posting:
```
?- partial_string("abc", Ls0, Ls).
Ls0 = [a,b,c|Ls].
```
`Tail = Y, X = [a,b,c|Y].`
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.
By all appearances, partial strings are plain Prolog lists.
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/lists.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).
### 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.
* [`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.
* [`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
* [`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.
TLS negotiation is performed via the option `tls(true)` in
`socket_client_open/3`, yielding secure encrypted connections.
* [`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` 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.
To use predicates provided by the `lists` library, write:
```
?- use_module(library(lists)).
@@ -264,7 +494,6 @@ REPL:
```
?- [user].
(type Enter + Ctrl-D to terminate the stream when finished)
:- module(test, [local_member/2]).
:- use_module(library(lists)).
@@ -273,3 +502,17 @@ local_member(X, Xs) :- member(X, Xs).
The user listing can also be terminated by placing `end_of_file.` at
the end of the stream.
### Configuration file
At startup, Scryer Prolog consults the file `~/.scryerrc`, if the file
exists. This file is useful to automatically load libraries and define
predicates that you need often.
For example, a sensible starting point for `~/.scryerrc` is:
```
:- use_module(library(lists)).
:- use_module(library(dcgs)).
:- use_module(library(reif)).
```

View File

@@ -1,56 +1,55 @@
extern crate indexmap;
use indexmap::IndexSet;
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;
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();
for item in paths {
let item = item.unwrap().path();
if let Some(file_name) = item.file_name() {
if let Some(ext) = item.extension() {
if ext == "pl" {
let file_stem = item.file_stem().unwrap();
let file_str = file_stem.to_string_lossy().to_uppercase();
let dest = Path::new(&out_dir).join(file_name);
match copy(&item, dest) {
Ok(_) => {},
Err(e) => panic!("die: {:?}", e)
};
let include_line = format!("static {}: &str = include_str!(\"{}.pl\");\n",
file_str, file_stem.to_string_lossy());
libraries.write_all(include_line.as_bytes()).unwrap();
library_index.insert(file_stem.to_string_lossy().to_string());
}
}
}
}
libraries.write_all(b"\nref_thread_local! {
libraries
.write_all(
b"ref_thread_local! {
pub static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
let mut m = IndexMap::new();\n").unwrap();
for item in library_index {
let line = format!("\n m.insert(\"{}\", {});", item, item.to_uppercase());
libraries.write_all(line.as_bytes()).unwrap();
}
libraries.write_all(b"\n\n m\n };
}").unwrap();
let mut m = IndexMap::new();\n",
)
.unwrap();
find_prolog_files(&mut libraries, "", &lib_path);
libraries.write_all(b"\n m\n };\n}\n").unwrap();
}

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,9 +1,9 @@
use prolog_parser::ast::*;
use crate::prolog_parser::ast::*;
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::machine::machine_indices::*;
use crate::targets::*;
use std::cell::Cell;
use std::rc::Rc;

View File

@@ -1,26 +1,29 @@
use prolog_parser::ast::*;
use crate::prolog_parser::ast::*;
use crate::prolog::clause_types::*;
use crate::prolog::fixtures::*;
use crate::prolog::forms::*;
use crate::prolog::instructions::*;
use crate::prolog::iterators::*;
use crate::clause_types::*;
use crate::fixtures::*;
use crate::forms::*;
use crate::instructions::*;
use crate::iterators::*;
use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::machine_indices::*;
use crate::machine::heap::*;
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use crate::prolog::ordered_float::*;
use crate::prolog::rug::ops::PowAssign;
use crate::prolog::rug::{Assign, Integer, Rational};
use crate::ordered_float::*;
use crate::rug::ops::PowAssign;
use crate::rug::{Assign, Integer, Rational};
use std::cell::Cell;
use std::cmp::{max, min, Ordering};
use std::convert::TryFrom;
use std::f64;
use std::num::FpCategory;
use std::ops::{Add, Div, Mul, Neg, Sub};
use std::rc::Rc;
use std::vec::Vec;
#[derive(Debug)]
pub struct ArithInstructionIterator<'a> {
state_stack: Vec<TermIterState<'a>>,
}
@@ -66,6 +69,7 @@ impl<'a> ArithInstructionIterator<'a> {
}
}
#[derive(Debug)]
pub enum ArithTermRef<'a> {
Constant(&'a Constant),
Op(ClauseName, usize), // name, arity.
@@ -107,6 +111,7 @@ impl<'a> Iterator for ArithInstructionIterator<'a> {
}
}
#[derive(Debug)]
pub struct ArithmeticEvaluator<'a> {
bindings: &'a AllocVarDict,
interm: Vec<ArithmeticTerm>,
@@ -262,6 +267,9 @@ impl<'a> ArithmeticEvaluator<'a> {
fn push_constant(&mut self, c: &Constant) -> Result<(), ArithmeticError> {
match c {
&Constant::Fixnum(n) => self
.interm
.push(ArithmeticTerm::Number(Number::Fixnum(n))),
&Constant::Integer(ref n) => self
.interm
.push(ArithmeticTerm::Number(Number::Integer(n.clone()))),
@@ -316,18 +324,25 @@ impl<'a> ArithmeticEvaluator<'a> {
}
// integer division rounding function -- 9.1.3.1.
pub fn rnd_i<'a>(n: &'a Number) -> RefOrOwned<'a, Integer> {
pub fn rnd_i<'a>(n: &'a Number) -> RefOrOwned<'a, Number> {
match n {
&Number::Integer(ref n) => RefOrOwned::Borrowed(n),
&Number::Integer(_) => {
RefOrOwned::Borrowed(n)
}
&Number::Float(OrderedFloat(f)) => {
RefOrOwned::Owned(Integer::from_f64(f.floor()).unwrap_or_else(|| Integer::from(0)))
RefOrOwned::Owned(Number::from(
Integer::from_f64(f.floor()).unwrap_or_else(|| Integer::from(0))
))
}
&Number::Fixnum(n) => {
RefOrOwned::Owned(Number::from(n))
}
&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);
RefOrOwned::Owned(floor)
RefOrOwned::Owned(Number::from(floor))
}
}
}
@@ -335,6 +350,7 @@ pub fn rnd_i<'a>(n: &'a Number) -> RefOrOwned<'a, Integer> {
// floating point rounding function -- 9.1.4.1.
pub fn rnd_f(n: &Number) -> f64 {
match n {
&Number::Fixnum(n) => n as f64,
&Number::Integer(ref n) => n.to_f64(),
&Number::Float(OrderedFloat(f)) => f,
&Number::Rational(ref r) => r.to_f64(),
@@ -370,22 +386,32 @@ where
}
}
#[inline]
fn float_fn_to_f(n: isize) -> Result<f64, EvalError> {
classify_float(n as f64, rnd_f)
}
#[inline]
fn float_i_to_f(n: &Integer) -> Result<f64, EvalError> {
classify_float(n.to_f64(), rnd_f)
}
#[inline]
fn float_r_to_f(r: &Rational) -> Result<f64, EvalError> {
classify_float(r.to_f64(), rnd_f)
}
#[inline]
fn add_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
Ok(OrderedFloat(classify_float(f1 + f2, rnd_f)?))
}
#[inline]
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)
@@ -399,23 +425,46 @@ impl Add<Number> for Number {
fn add(self, rhs: Number) -> Self::Output {
match (self, rhs) {
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Integer(n1 + n2)), // add_i
(Number::Fixnum(n1), Number::Fixnum(n2)) => {
Ok(if let Some(result) = n1.checked_add(n2) {
Number::Fixnum(result)
} else {
Number::from(Integer::from(n1) + Integer::from(n2))
})
}
(Number::Fixnum(n1), Number::Integer(n2)) |
(Number::Integer(n2), Number::Fixnum(n1)) => {
Ok(Number::from(Integer::from(n1) + &*n2))
}
(Number::Fixnum(n1), Number::Rational(n2)) |
(Number::Rational(n2), Number::Fixnum(n1)) => {
Ok(Number::from(Rational::from(n1) + &*n2))
}
(Number::Fixnum(n1), Number::Float(OrderedFloat(n2))) |
(Number::Float(OrderedFloat(n2)), Number::Fixnum(n1)) => {
Ok(Number::Float(add_f(float_fn_to_f(n1)?, n2)?))
}
(Number::Integer(n1), Number::Integer(n2)) => {
Ok(Number::from(Integer::from(&*n1) + &*n2)) // add_i
}
(Number::Integer(n1), Number::Float(OrderedFloat(n2)))
| (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
| (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
Ok(Number::Float(add_f(float_i_to_f(&n1)?, n2)?))
}
(Number::Integer(n1), Number::Rational(n2))
| (Number::Rational(n2), Number::Integer(n1)) => {
Ok(Number::Rational(Rational::from(n1) + n2))
| (Number::Rational(n2), Number::Integer(n1)) => {
Ok(Number::from(Rational::from(&*n1) + &*n2))
}
(Number::Rational(n1), Number::Float(OrderedFloat(n2)))
| (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
| (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
Ok(Number::Float(add_f(float_r_to_f(&n1)?, n2)?))
}
(Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
Ok(Number::Float(add_f(f1, f2)?))
}
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Rational(r1 + r2)),
(Number::Rational(r1), Number::Rational(r2)) => {
Ok(Number::from(Rational::from(&*r1) + &*r2))
}
}
}
}
@@ -425,9 +474,15 @@ impl Neg for Number {
fn neg(self) -> Self::Output {
match self {
Number::Integer(n) => Number::Integer(-n),
Number::Fixnum(n) =>
if let Some(n) = n.checked_neg() {
Number::Fixnum(n)
} else {
Number::from(-Integer::from(n))
}
Number::Integer(n) => Number::Integer(Rc::new(-Integer::from(&*n))),
Number::Float(OrderedFloat(f)) => Number::Float(OrderedFloat(-f)),
Number::Rational(r) => Number::Rational(-r),
Number::Rational(r) => Number::Rational(Rc::new(-Rational::from(&*r))),
}
}
}
@@ -445,14 +500,35 @@ impl Mul<Number> for Number {
fn mul(self, rhs: Number) -> Self::Output {
match (self, rhs) {
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Integer(n1 * n2)), // mul_i
(Number::Fixnum(n1), Number::Fixnum(n2)) => {
Ok(if let Some(result) = n1.checked_mul(n2) {
Number::Fixnum(result)
} else {
Number::from(Integer::from(n1) * Integer::from(n2))
})
}
(Number::Fixnum(n1), Number::Integer(n2)) |
(Number::Integer(n2), Number::Fixnum(n1)) => {
Ok(Number::from(Integer::from(n1) * &*n2))
}
(Number::Fixnum(n1), Number::Rational(n2)) |
(Number::Rational(n2), Number::Fixnum(n1)) => {
Ok(Number::from(Rational::from(n1) * &*n2))
}
(Number::Fixnum(n1), Number::Float(OrderedFloat(n2))) |
(Number::Float(OrderedFloat(n2)), Number::Fixnum(n1)) => {
Ok(Number::Float(mul_f(float_fn_to_f(n1)?, n2)?))
}
(Number::Integer(n1), Number::Integer(n2)) => {
Ok(Number::Integer(Rc::new(Integer::from(&*n1) * &*n2))) // mul_i
}
(Number::Integer(n1), Number::Float(OrderedFloat(n2)))
| (Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
Ok(Number::Float(mul_f(float_i_to_f(&n1)?, n2)?))
}
(Number::Integer(n1), Number::Rational(n2))
| (Number::Rational(n2), Number::Integer(n1)) => {
Ok(Number::Rational(Rational::from(n1) * n2))
Ok(Number::Rational(Rc::new(Rational::from(&*n1) * &*n2)))
}
(Number::Rational(n1), Number::Float(OrderedFloat(n2)))
| (Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
@@ -461,7 +537,9 @@ impl Mul<Number> for Number {
(Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
Ok(Number::Float(mul_f(f1, f2)?))
}
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Rational(r1 * r2)),
(Number::Rational(r1), Number::Rational(r2)) => {
Ok(Number::Rational(Rc::new(Rational::from(&*r1) * &*r2)))
}
}
}
}
@@ -471,24 +549,72 @@ impl Div<Number> for Number {
fn div(self, rhs: Number) -> Self::Output {
match (self, rhs) {
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
float_i_to_f(&n1)?,
float_i_to_f(&n2)?,
)?)),
(Number::Fixnum(n1), Number::Fixnum(n2)) => {
Ok(Number::Float(div_f(
float_fn_to_f(n1)?,
float_fn_to_f(n2)?,
)?))
}
(Number::Fixnum(n1), Number::Integer(n2)) => {
Ok(Number::Float(div_f(
float_fn_to_f(n1)?,
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)?,
)?))
}
(Number::Fixnum(n1), Number::Rational(n2)) => {
Ok(Number::Float(div_f(
float_fn_to_f(n1)?,
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)?,
)?))
}
(Number::Fixnum(n1), Number::Float(OrderedFloat(n2))) => {
Ok(Number::Float(div_f(
float_fn_to_f(n1)?,
n2,
)?))
}
(Number::Float(OrderedFloat(n1)), Number::Fixnum(n2)) => {
Ok(Number::Float(div_f(
n1,
float_fn_to_f(n2)?,
)?))
}
(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::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)?))
}
@@ -506,41 +632,172 @@ impl Div<Number> for Number {
}
}
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.eq(&**n2),
(&Number::Integer(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2),
(&Number::Fixnum(n1), &Number::Rational(ref n2)) => n1.eq(&**n2),
(&Number::Rational(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2),
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1 as f64).eq(&n2),
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2 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 for Number {
fn partial_cmp(&self, rhs: &Number) -> Option<Ordering> {
match (self, rhs) {
(&Number::Integer(ref n1), &Number::Integer(ref n2)) => Some(n1.cmp(n2)),
(&Number::Integer(_), Number::Float(_)) => Some(Ordering::Greater),
(&Number::Float(_), &Number::Integer(_)) => Some(Ordering::Less),
(&Number::Integer(_), &Number::Rational(_)) => Some(Ordering::Greater),
(&Number::Rational(_), &Number::Integer(_)) => Some(Ordering::Less),
(&Number::Rational(_), Number::Float(_)) => Some(Ordering::Greater),
(&Number::Float(_), &Number::Rational(_)) => Some(Ordering::Less),
(&Number::Float(f1), &Number::Float(f2)) => Some(f1.cmp(&f2)),
(&Number::Rational(ref r1), &Number::Rational(ref r2)) => Some(r1.cmp(&r2)),
}
Some(self.cmp(rhs))
}
}
impl Ord for Number {
fn cmp(&self, rhs: &Number) -> Ordering {
match (self, rhs) {
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.cmp(&n2),
(&Number::Fixnum(n1), Number::Integer(n2)) => Integer::from(n1).cmp(&*n2),
(Number::Integer(n1), &Number::Fixnum(n2)) => (&**n1).cmp(&Integer::from(n2)),
(&Number::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1).cmp(&*n2),
(Number::Rational(n1), &Number::Fixnum(n2)) => (&**n1).cmp(&Rational::from(n2)),
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1 as f64).cmp(&n2),
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2 as f64)),
(&Number::Integer(ref n1), &Number::Integer(ref n2)) => n1.cmp(n2),
(&Number::Integer(_), Number::Float(_)) => Ordering::Greater,
(&Number::Float(_), &Number::Integer(_)) => Ordering::Less,
(&Number::Integer(_), &Number::Rational(_)) => Ordering::Greater,
(&Number::Rational(_), &Number::Integer(_)) => Ordering::Less,
(&Number::Rational(_), Number::Float(_)) => Ordering::Greater,
(&Number::Float(_), &Number::Rational(_)) => Ordering::Less,
(&Number::Integer(ref 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(ref n1), &Number::Rational(ref n2)) => {
#[cfg(feature = "num")]
{
Rational::from(&**n1).cmp(n2)
}
#[cfg(not(feature = "num"))]
{
(&**n1).partial_cmp(&**n2).unwrap_or(Ordering::Less)
}
}
(&Number::Rational(ref n1), &Number::Integer(ref n2)) => {
#[cfg(feature = "num")]
{
(&**n1).cmp(&Rational::from(&**n2))
}
#[cfg(not(feature = "num"))]
{
(&**n1).partial_cmp(&**n2).unwrap_or(Ordering::Less)
}
}
(&Number::Rational(ref n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64()).cmp(&n2),
(&Number::Float(n1), &Number::Rational(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64())),
(&Number::Float(f1), &Number::Float(f2)) => f1.cmp(&f2),
(&Number::Rational(ref r1), &Number::Rational(ref r2)) => r1.cmp(&r2),
}
}
}
impl<'a> TryFrom<(Addr, &'a Heap)> for Number {
type Error = ();
fn try_from((addr, heap): (Addr, &'a Heap)) -> Result<Number, Self::Error> {
match addr {
Addr::Fixnum(n) => {
Ok(Number::from(n))
}
Addr::Float(n) => {
Ok(Number::Float(n))
}
Addr::Usize(n) => {
if let Ok(n) = isize::try_from(n) {
Ok(Number::from(n))
} else {
Ok(Number::from(Integer::from(n)))
}
}
Addr::Con(h) => {
Number::try_from(&heap[h])
}
_ => {
Err(())
}
}
}
}
impl<'a> TryFrom<&'a HeapCellValue> for Number {
type Error = ();
fn try_from(value: &'a HeapCellValue) -> Result<Number, Self::Error> {
match value {
HeapCellValue::Addr(addr) => {
match addr {
&Addr::Fixnum(n) => {
Ok(Number::from(n))
}
&Addr::Float(n) => {
Ok(Number::Float(n))
}
&Addr::Usize(n) => {
if let Ok(n) = isize::try_from(n) {
Ok(Number::from(n))
} else {
Ok(Number::from(Integer::from(n)))
}
}
_ => {
Err(())
}
}
}
HeapCellValue::Integer(n) => {
Ok(Number::Integer(n.clone()))
}
HeapCellValue::Rational(n) => {
Ok(Number::Rational(n.clone()))
}
_ => {
Err(())
}
}
}
}
impl<'a> From<&'a Integer> for Number {
#[inline]
fn from(src: &'a Integer) -> Self {
Number::Integer(Rc::new(Integer::from(src)))
}
}
// Computes n ^ power. Ignores the sign of power.
pub fn binary_pow(mut n: Integer, power: Integer) -> Integer {
let mut power = power.abs();
pub fn binary_pow(mut n: Integer, power: &Integer) -> Integer {
let mut power = Integer::from(power.abs_ref());
if power == 0 {
return Integer::from(1);

View File

@@ -1,14 +1,14 @@
use prolog_parser::ast::*;
use crate::prolog_parser::ast::*;
use crate::prolog::forms::Number;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::rug::rand::RandState;
use crate::forms::Number;
use crate::machine::machine_indices::*;
use crate::rug::rand::RandState;
use ref_thread_local::RefThreadLocal;
use crate::ref_thread_local::RefThreadLocal;
use std::collections::BTreeMap;
#[derive(Clone, Copy, Eq, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum CompareNumberQT {
GreaterThan,
LessThan,
@@ -31,7 +31,7 @@ impl CompareNumberQT {
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompareTermQT {
LessThan,
LessThanOrEqual,
@@ -50,7 +50,7 @@ impl CompareTermQT {
}
}
#[derive(Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ArithmeticTerm {
Reg(RegType),
Interm(usize),
@@ -67,7 +67,7 @@ impl ArithmeticTerm {
}
}
#[derive(Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum InlinedClauseType {
CompareNumber(CompareNumberQT, ArithmeticTerm, ArithmeticTerm),
IsAtom(RegType),
@@ -75,7 +75,6 @@ pub enum InlinedClauseType {
IsCompound(RegType),
IsInteger(RegType),
IsRational(RegType),
IsString(RegType),
IsFloat(RegType),
IsNonVar(RegType),
IsVar(RegType),
@@ -105,7 +104,6 @@ ref_thread_local! {
m.insert(("compound", 1), ClauseType::Inlined(InlinedClauseType::IsCompound(r1)));
m.insert(("integer", 1), ClauseType::Inlined(InlinedClauseType::IsInteger(r1)));
m.insert(("rational", 1), ClauseType::Inlined(InlinedClauseType::IsRational(r1)));
m.insert(("string", 1), ClauseType::Inlined(InlinedClauseType::IsString(r1)));
m.insert(("float", 1), ClauseType::Inlined(InlinedClauseType::IsFloat(r1)));
m.insert(("nonvar", 1), ClauseType::Inlined(InlinedClauseType::IsNonVar(r1)));
m.insert(("var", 1), ClauseType::Inlined(InlinedClauseType::IsVar(r1)));
@@ -117,7 +115,6 @@ ref_thread_local! {
m.insert(("@>=", 2), ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(CompareTermQT::GreaterThanOrEqual)));
m.insert(("@=<", 2), ClauseType::BuiltIn(BuiltInClauseType::CompareTerm(CompareTermQT::LessThanOrEqual)));
m.insert(("copy_term", 2), ClauseType::BuiltIn(BuiltInClauseType::CopyTerm));
m.insert(("cyclic_term", 1), ClauseType::BuiltIn(BuiltInClauseType::CyclicTerm));
m.insert(("==", 2), ClauseType::BuiltIn(BuiltInClauseType::Eq));
m.insert(("functor", 3), ClauseType::BuiltIn(BuiltInClauseType::Functor));
m.insert(("ground", 1), ClauseType::BuiltIn(BuiltInClauseType::Ground));
@@ -141,7 +138,6 @@ impl InlinedClauseType {
&InlinedClauseType::IsCompound(..) => "compound",
&InlinedClauseType::IsInteger(..) => "integer",
&InlinedClauseType::IsRational(..) => "rational",
&InlinedClauseType::IsString(..) => "string",
&InlinedClauseType::IsFloat(..) => "float",
&InlinedClauseType::IsNonVar(..) => "nonvar",
&InlinedClauseType::IsVar(..) => "var",
@@ -149,7 +145,7 @@ impl InlinedClauseType {
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum SystemClauseType {
AbolishClause,
AbolishModuleClause,
@@ -160,18 +156,31 @@ pub enum SystemClauseType {
AtomCodes,
AtomLength,
BindFromRegister,
CallAttributeGoals,
CallContinuation,
CharCode,
CharType,
CharsToNumber,
ClearAttrVarBindings,
ClearAttributeGoals,
CloneAttributeGoals,
CodesToNumber,
CopyTermWithoutAttrVars,
CheckCutPoint,
Close,
CopyToLiftedHeap,
CreatePartialString,
CurrentHostname,
CurrentInput,
CurrentOutput,
DirectoryFiles,
FileSize,
FileExists,
DirectoryExists,
DirectorySeparator,
MakeDirectory,
DeleteFile,
WorkingDirectory,
PathCanonical,
FileTime,
DeleteAttribute,
DeleteHeadAttribute,
DynamicModuleResolution(usize),
@@ -179,10 +188,15 @@ pub enum SystemClauseType {
EnqueueAttributedVar,
ExpandGoal,
ExpandTerm,
FetchAttributeGoals,
FetchGlobalVar,
FetchGlobalVarWithOffset,
FirstStream,
FlushOutput,
GetByte,
GetChar,
GetNChars,
GetCode,
GetSingleChar,
ResetAttrVarState,
TruncateIfNoLiftedHeapGrowthDiff,
TruncateIfNoLiftedHeapGrowth,
@@ -217,8 +231,17 @@ pub enum SystemClauseType {
NumberToChars,
NumberToCodes,
OpDeclaration,
Open,
NextStream,
PartialStringTail,
PeekByte,
PeekChar,
PeekCode,
PointsToContinuationResetMarker,
PutByte,
PutChar,
PutChars,
PutCode,
REPL(REPLCodePtr),
ReadQueryTerm,
ReadTerm,
@@ -231,8 +254,12 @@ pub enum SystemClauseType {
RetractClause,
RestoreCutPolicy,
SetCutPoint(RegType),
SetInput,
SetOutput,
StoreGlobalVar,
StoreGlobalVarWithOffset,
StreamProperty,
SetStreamPosition,
InferenceLevel,
CleanUpBlock,
EraseBall,
@@ -243,7 +270,10 @@ pub enum SystemClauseType {
GetDoubleQuotes,
InstallNewBlock,
Maybe,
RawInputReadChar,
CpuNow,
CurrentTime,
QuotedToken,
ReadTermFromChars,
ResetBlock,
ReturnFromVerifyAttr,
SetBall,
@@ -251,7 +281,13 @@ pub enum SystemClauseType {
SetDoubleQuotes,
SetSeed,
SkipMaxList,
Sleep,
SocketClientOpen,
SocketServerOpen,
SocketServerAccept,
SocketServerClose,
Succeed,
TermAttributedVariables,
TermVariables,
TruncateLiftedHeapTo,
UnifyWithOccursCheck,
@@ -260,6 +296,26 @@ pub enum SystemClauseType {
Variant,
WAMInstructions,
WriteTerm,
WriteTermToChars,
ScryerPrologVersion,
CryptoRandomByte,
CryptoDataHash,
CryptoDataHKDF,
CryptoPasswordHash,
CryptoDataEncrypt,
CryptoDataDecrypt,
CryptoCurveScalarMult,
Ed25519Sign,
Ed25519Verify,
Ed25519NewKeyPair,
Ed25519KeyPairPublicKey,
Curve25519ScalarMult,
LoadHTML,
LoadXML,
GetEnv,
SetEnv,
UnsetEnv,
CharsBase64,
}
impl SystemClauseType {
@@ -274,9 +330,9 @@ impl SystemClauseType {
&SystemClauseType::AtomCodes => clause_name!("$atom_codes"),
&SystemClauseType::AtomLength => clause_name!("$atom_length"),
&SystemClauseType::BindFromRegister => clause_name!("$bind_from_register"),
&SystemClauseType::CallAttributeGoals => clause_name!("$call_attribute_goals"),
&SystemClauseType::CallContinuation => clause_name!("$call_continuation"),
&SystemClauseType::CharCode => clause_name!("$char_code"),
&SystemClauseType::CharType => clause_name!("$char_type"),
&SystemClauseType::CharsToNumber => clause_name!("$chars_to_number"),
&SystemClauseType::CheckCutPoint => clause_name!("$check_cp"),
&SystemClauseType::ClearAttributeGoals => clause_name!("$clear_attribute_goals"),
@@ -284,18 +340,31 @@ impl SystemClauseType {
&SystemClauseType::CodesToNumber => clause_name!("$codes_to_number"),
&SystemClauseType::CopyTermWithoutAttrVars => clause_name!("$copy_term_without_attr_vars"),
&SystemClauseType::CreatePartialString => clause_name!("$create_partial_string"),
&SystemClauseType::CurrentInput => clause_name!("$current_input"),
&SystemClauseType::CurrentHostname => clause_name!("$current_hostname"),
&SystemClauseType::CurrentOutput => clause_name!("$current_output"),
&SystemClauseType::DirectoryFiles => clause_name!("$directory_files"),
&SystemClauseType::FileSize => clause_name!("$file_size"),
&SystemClauseType::FileExists => clause_name!("$file_exists"),
&SystemClauseType::DirectoryExists => clause_name!("$directory_exists"),
&SystemClauseType::DirectorySeparator => clause_name!("$directory_separator"),
&SystemClauseType::MakeDirectory => clause_name!("$make_directory"),
&SystemClauseType::DeleteFile => clause_name!("$delete_file"),
&SystemClauseType::WorkingDirectory => clause_name!("$working_directory"),
&SystemClauseType::PathCanonical => clause_name!("$path_canonical"),
&SystemClauseType::FileTime => clause_name!("$file_time"),
&SystemClauseType::REPL(REPLCodePtr::CompileBatch) => clause_name!("$compile_batch"),
&SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"),
&SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule) => {
clause_name!("$use_qualified_module")
}
&SystemClauseType::REPL(REPLCodePtr::UseModuleFromFile) => {
clause_name!("$use_module_from_file")
}
&SystemClauseType::REPL(REPLCodePtr::UseQualifiedModuleFromFile) => {
clause_name!("$use_qualified_module_from_file")
}
&SystemClauseType::ClearAttrVarBindings => clause_name!("$clear_attr_var_bindings"),
&SystemClauseType::REPL(REPLCodePtr::UseModule) => clause_name!("$use_module"),
&SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule) => {
clause_name!("$use_qualified_module")
}
&SystemClauseType::REPL(REPLCodePtr::UseModuleFromFile) => {
clause_name!("$use_module_from_file")
}
&SystemClauseType::REPL(REPLCodePtr::UseQualifiedModuleFromFile) => {
clause_name!("$use_qualified_module_from_file")
}
&SystemClauseType::Close => clause_name!("$close"),
&SystemClauseType::CopyToLiftedHeap => clause_name!("$copy_to_lh"),
&SystemClauseType::DeleteAttribute => clause_name!("$del_attr_non_head"),
&SystemClauseType::DeleteHeadAttribute => clause_name!("$del_attr_head"),
@@ -304,12 +373,17 @@ impl SystemClauseType {
&SystemClauseType::EnqueueAttributedVar => clause_name!("$enqueue_attr_var"),
&SystemClauseType::ExpandTerm => clause_name!("$expand_term"),
&SystemClauseType::ExpandGoal => clause_name!("$expand_goal"),
&SystemClauseType::FetchAttributeGoals => clause_name!("$fetch_attribute_goals"),
&SystemClauseType::FetchGlobalVar => clause_name!("$fetch_global_var"),
&SystemClauseType::FetchGlobalVarWithOffset => {
clause_name!("$fetch_global_var_with_offset")
}
&SystemClauseType::FirstStream => clause_name!("$first_stream"),
&SystemClauseType::FlushOutput => clause_name!("$flush_output"),
&SystemClauseType::GetByte => clause_name!("$get_byte"),
&SystemClauseType::GetChar => clause_name!("$get_char"),
&SystemClauseType::GetNChars => clause_name!("$get_n_chars"),
&SystemClauseType::GetCode => clause_name!("$get_code"),
&SystemClauseType::GetSingleChar => clause_name!("$get_single_char"),
&SystemClauseType::ResetAttrVarState => clause_name!("$reset_attr_var_state"),
&SystemClauseType::TruncateIfNoLiftedHeapGrowth => {
clause_name!("$truncate_if_no_lh_growth")
@@ -338,15 +412,21 @@ impl SystemClauseType {
&SystemClauseType::GetSCCCleaner => clause_name!("$get_scc_cleaner"),
&SystemClauseType::Halt => clause_name!("$halt"),
&SystemClauseType::HeadIsDynamic => clause_name!("$head_is_dynamic"),
&SystemClauseType::OpDeclaration => clause_name!("$op$"),
&SystemClauseType::Open => clause_name!("$open"),
&SystemClauseType::OpDeclaration => clause_name!("$op"),
&SystemClauseType::InstallSCCCleaner => clause_name!("$install_scc_cleaner"),
&SystemClauseType::InstallInferenceCounter => {
clause_name!("$install_inference_counter")
}
&SystemClauseType::IsPartialString => clause_name!("$is_partial_string"),
&SystemClauseType::PartialStringTail => clause_name!("$partial_string_tail"),
&SystemClauseType::PeekByte => clause_name!("$peek_byte"),
&SystemClauseType::PeekChar => clause_name!("$peek_char"),
&SystemClauseType::PeekCode => clause_name!("$peek_code"),
&SystemClauseType::LiftedHeapLength => clause_name!("$lh_length"),
&SystemClauseType::Maybe => clause_name!("maybe"),
&SystemClauseType::CpuNow => clause_name!("$cpu_now"),
&SystemClauseType::CurrentTime => clause_name!("$current_time"),
&SystemClauseType::ModuleAssertDynamicPredicateToFront => {
clause_name!("$module_asserta")
}
@@ -356,19 +436,38 @@ impl SystemClauseType {
&SystemClauseType::ModuleHeadIsDynamic => clause_name!("$module_head_is_dynamic"),
&SystemClauseType::ModuleExists => clause_name!("$module_exists"),
&SystemClauseType::ModuleOf => clause_name!("$module_of"),
&SystemClauseType::NextStream => clause_name!("$next_stream"),
&SystemClauseType::NoSuchPredicate => clause_name!("$no_such_predicate"),
&SystemClauseType::NumberToChars => clause_name!("$number_to_chars"),
&SystemClauseType::NumberToCodes => clause_name!("$number_to_codes"),
&SystemClauseType::PointsToContinuationResetMarker => {
clause_name!("$points_to_cont_reset_marker")
}
&SystemClauseType::RawInputReadChar => clause_name!("$raw_input_read_char"),
&SystemClauseType::PutByte => {
clause_name!("$put_byte")
}
&SystemClauseType::PutChar => {
clause_name!("$put_char")
}
&SystemClauseType::PutChars => {
clause_name!("$put_chars")
}
&SystemClauseType::PutCode => {
clause_name!("$put_code")
}
&SystemClauseType::QuotedToken => {
clause_name!("$quoted_token")
}
&SystemClauseType::RedoAttrVarBinding => clause_name!("$redo_attr_var_binding"),
&SystemClauseType::RemoveCallPolicyCheck => clause_name!("$remove_call_policy_check"),
&SystemClauseType::RemoveInferenceCounter => clause_name!("$remove_inference_counter"),
&SystemClauseType::RestoreCutPolicy => clause_name!("$restore_cut_policy"),
&SystemClauseType::SetCutPoint(_) => clause_name!("$set_cp"),
&SystemClauseType::SetInput => clause_name!("$set_input"),
&SystemClauseType::SetOutput => clause_name!("$set_output"),
&SystemClauseType::SetSeed => clause_name!("$set_seed"),
&SystemClauseType::StreamProperty => clause_name!("$stream_property"),
&SystemClauseType::SetStreamPosition => clause_name!("$set_stream_position"),
&SystemClauseType::StoreGlobalVar => clause_name!("$store_global_var"),
&SystemClauseType::StoreGlobalVarWithOffset => {
clause_name!("$store_global_var_with_offset")
@@ -385,6 +484,7 @@ impl SystemClauseType {
&SystemClauseType::NextEP => clause_name!("$nextEP"),
&SystemClauseType::ReadQueryTerm => clause_name!("$read_query_term"),
&SystemClauseType::ReadTerm => clause_name!("$read_term"),
&SystemClauseType::ReadTermFromChars => clause_name!("$read_term_from_chars"),
&SystemClauseType::ResetGlobalVarAtKey => clause_name!("$reset_global_var_at_key"),
&SystemClauseType::ResetGlobalVarAtOffset => clause_name!("$reset_global_var_at_offset"),
&SystemClauseType::RetractClause => clause_name!("$retract_clause"),
@@ -395,7 +495,13 @@ impl SystemClauseType {
&SystemClauseType::SetCutPointByDefault(_) => clause_name!("$set_cp_by_default"),
&SystemClauseType::SetDoubleQuotes => clause_name!("$set_double_quotes"),
&SystemClauseType::SkipMaxList => clause_name!("$skip_max_list"),
&SystemClauseType::Sleep => clause_name!("$sleep"),
&SystemClauseType::SocketClientOpen => clause_name!("$socket_client_open"),
&SystemClauseType::SocketServerOpen => clause_name!("$socket_server_open"),
&SystemClauseType::SocketServerAccept => clause_name!("$socket_server_accept"),
&SystemClauseType::SocketServerClose => clause_name!("$socket_server_close"),
&SystemClauseType::Succeed => clause_name!("$succeed"),
&SystemClauseType::TermAttributedVariables => clause_name!("$term_attributed_variables"),
&SystemClauseType::TermVariables => clause_name!("$term_variables"),
&SystemClauseType::TruncateLiftedHeapTo => clause_name!("$truncate_lh_to"),
&SystemClauseType::UnifyWithOccursCheck => clause_name!("$unify_with_occurs_check"),
@@ -404,6 +510,26 @@ impl SystemClauseType {
&SystemClauseType::Variant => clause_name!("$variant"),
&SystemClauseType::WAMInstructions => clause_name!("$wam_instructions"),
&SystemClauseType::WriteTerm => clause_name!("$write_term"),
&SystemClauseType::WriteTermToChars => clause_name!("$write_term_to_chars"),
&SystemClauseType::ScryerPrologVersion => clause_name!("$scryer_prolog_version"),
&SystemClauseType::CryptoRandomByte => clause_name!("$crypto_random_byte"),
&SystemClauseType::CryptoDataHash => clause_name!("$crypto_data_hash"),
&SystemClauseType::CryptoDataHKDF => clause_name!("$crypto_data_hkdf"),
&SystemClauseType::CryptoPasswordHash => clause_name!("$crypto_password_hash"),
&SystemClauseType::CryptoDataEncrypt => clause_name!("$crypto_data_encrypt"),
&SystemClauseType::CryptoDataDecrypt => clause_name!("$crypto_data_decrypt"),
&SystemClauseType::CryptoCurveScalarMult => clause_name!("$crypto_curve_scalar_mult"),
&SystemClauseType::Ed25519Sign => clause_name!("$ed25519_sign"),
&SystemClauseType::Ed25519Verify => clause_name!("$ed25519_verify"),
&SystemClauseType::Ed25519NewKeyPair => clause_name!("$ed25519_new_keypair"),
&SystemClauseType::Ed25519KeyPairPublicKey => clause_name!("$ed25519_keypair_public_key"),
&SystemClauseType::Curve25519ScalarMult => clause_name!("$curve25519_scalar_mult"),
&SystemClauseType::LoadHTML => clause_name!("$load_html"),
&SystemClauseType::LoadXML => clause_name!("$load_xml"),
&SystemClauseType::GetEnv => clause_name!("$getenv"),
&SystemClauseType::SetEnv => clause_name!("$setenv"),
&SystemClauseType::UnsetEnv => clause_name!("$unsetenv"),
&SystemClauseType::CharsBase64 => clause_name!("$chars_base64"),
}
}
@@ -420,11 +546,10 @@ impl SystemClauseType {
("$module_assertz", 5) => Some(SystemClauseType::ModuleAssertDynamicPredicateToBack),
("$asserta", 4) => Some(SystemClauseType::AssertDynamicPredicateToFront),
("$assertz", 4) => Some(SystemClauseType::AssertDynamicPredicateToBack),
("$call_attribute_goals", 2) => Some(SystemClauseType::CallAttributeGoals),
("$call_continuation", 1) => Some(SystemClauseType::CallContinuation),
("$char_code", 2) => Some(SystemClauseType::CharCode),
("$char_type", 2) => Some(SystemClauseType::CharType),
("$chars_to_number", 2) => Some(SystemClauseType::CharsToNumber),
("$clear_attr_var_bindings", 0) => Some(SystemClauseType::ClearAttrVarBindings),
("$clear_attribute_goals", 0) => Some(SystemClauseType::ClearAttributeGoals),
("$clone_attribute_goals", 1) => Some(SystemClauseType::CloneAttributeGoals),
("$codes_to_number", 2) => Some(SystemClauseType::CodesToNumber),
@@ -433,6 +558,13 @@ impl SystemClauseType {
("$check_cp", 1) => Some(SystemClauseType::CheckCutPoint),
("$compile_batch", 0) => Some(SystemClauseType::REPL(REPLCodePtr::CompileBatch)),
("$copy_to_lh", 2) => Some(SystemClauseType::CopyToLiftedHeap),
("$close", 2) => Some(SystemClauseType::Close),
("$current_hostname", 1) => Some(SystemClauseType::CurrentHostname),
("$current_input", 1) => Some(SystemClauseType::CurrentInput),
("$current_output", 1) => Some(SystemClauseType::CurrentOutput),
("$first_stream", 1) => Some(SystemClauseType::FirstStream),
("$next_stream", 2) => Some(SystemClauseType::NextStream),
("$flush_output", 1) => Some(SystemClauseType::FlushOutput),
("$del_attr_non_head", 1) => Some(SystemClauseType::DeleteAttribute),
("$del_attr_head", 1) => Some(SystemClauseType::DeleteHeadAttribute),
("$get_next_db_ref", 2) => Some(SystemClauseType::GetNextDBRef),
@@ -443,16 +575,34 @@ impl SystemClauseType {
("$enqueue_attribute_goal", 1) => Some(SystemClauseType::EnqueueAttributeGoal),
("$enqueue_attr_var", 1) => Some(SystemClauseType::EnqueueAttributedVar),
("$partial_string_tail", 2) => Some(SystemClauseType::PartialStringTail),
("$peek_byte", 2) => Some(SystemClauseType::PeekByte),
("$peek_char", 2) => Some(SystemClauseType::PeekChar),
("$peek_code", 2) => Some(SystemClauseType::PeekCode),
("$is_partial_string", 1) => Some(SystemClauseType::IsPartialString),
("$expand_term", 2) => Some(SystemClauseType::ExpandTerm),
("$expand_goal", 2) => Some(SystemClauseType::ExpandGoal),
("$fetch_attribute_goals", 1) => Some(SystemClauseType::FetchAttributeGoals),
("$fetch_global_var", 2) => Some(SystemClauseType::FetchGlobalVar),
("$fetch_global_var_with_offset", 3) => Some(SystemClauseType::FetchGlobalVarWithOffset),
("$get_char", 1) => Some(SystemClauseType::GetChar),
("$get_byte", 2) => Some(SystemClauseType::GetByte),
("$get_char", 2) => Some(SystemClauseType::GetChar),
("$get_n_chars", 3) => Some(SystemClauseType::GetNChars),
("$get_code", 2) => Some(SystemClauseType::GetCode),
("$get_single_char", 1) => Some(SystemClauseType::GetSingleChar),
("$points_to_cont_reset_marker", 1) => {
Some(SystemClauseType::PointsToContinuationResetMarker)
}
("$put_byte", 2) => {
Some(SystemClauseType::PutByte)
}
("$put_char", 2) => {
Some(SystemClauseType::PutChar)
}
("$put_chars", 2) => {
Some(SystemClauseType::PutChars)
}
("$put_code", 2) => {
Some(SystemClauseType::PutCode)
}
("$reset_attr_var_state", 0) => Some(SystemClauseType::ResetAttrVarState),
("$truncate_if_no_lh_growth", 1) => {
Some(SystemClauseType::TruncateIfNoLiftedHeapGrowth)
@@ -468,12 +618,14 @@ impl SystemClauseType {
("$get_lh_from_offset_diff", 3) => Some(SystemClauseType::GetLiftedHeapFromOffsetDiff),
("$get_double_quotes", 1) => Some(SystemClauseType::GetDoubleQuotes),
("$get_scc_cleaner", 1) => Some(SystemClauseType::GetSCCCleaner),
("$halt", 0) => Some(SystemClauseType::Halt),
("$halt", 1) => Some(SystemClauseType::Halt),
("$head_is_dynamic", 1) => Some(SystemClauseType::HeadIsDynamic),
("$install_scc_cleaner", 2) => Some(SystemClauseType::InstallSCCCleaner),
("$install_inference_counter", 3) => Some(SystemClauseType::InstallInferenceCounter),
("$lh_length", 1) => Some(SystemClauseType::LiftedHeapLength),
("$maybe", 0) => Some(SystemClauseType::Maybe),
("$cpu_now", 1) => Some(SystemClauseType::CpuNow),
("$current_time", 1) => Some(SystemClauseType::CurrentTime),
("$module_exists", 1) => Some(SystemClauseType::ModuleExists),
("$module_of", 2) => Some(SystemClauseType::ModuleOf),
("$module_retract_clause", 5) => Some(SystemClauseType::ModuleRetractClause),
@@ -482,11 +634,16 @@ impl SystemClauseType {
("$number_to_chars", 2) => Some(SystemClauseType::NumberToChars),
("$number_to_codes", 2) => Some(SystemClauseType::NumberToCodes),
("$op", 3) => Some(SystemClauseType::OpDeclaration),
("$open", 7) => Some(SystemClauseType::Open),
("$redo_attr_var_binding", 2) => Some(SystemClauseType::RedoAttrVarBinding),
("$remove_call_policy_check", 1) => Some(SystemClauseType::RemoveCallPolicyCheck),
("$remove_inference_counter", 2) => Some(SystemClauseType::RemoveInferenceCounter),
("$restore_cut_policy", 0) => Some(SystemClauseType::RestoreCutPolicy),
("$set_cp", 1) => Some(SystemClauseType::SetCutPoint(temp_v!(1))),
("$set_input", 1) => Some(SystemClauseType::SetInput),
("$set_output", 1) => Some(SystemClauseType::SetOutput),
("$stream_property", 3) => Some(SystemClauseType::StreamProperty),
("$set_stream_position", 2) => Some(SystemClauseType::SetStreamPosition),
("$inference_level", 2) => Some(SystemClauseType::InferenceLevel),
("$clean_up_block", 1) => Some(SystemClauseType::CleanUpBlock),
("$erase_ball", 0) => Some(SystemClauseType::EraseBall),
@@ -498,10 +655,11 @@ impl SystemClauseType {
("$get_current_block", 1) => Some(SystemClauseType::GetCurrentBlock),
("$get_cp", 1) => Some(SystemClauseType::GetCutPoint),
("$install_new_block", 1) => Some(SystemClauseType::InstallNewBlock),
("$raw_input_read_char", 1) => Some(SystemClauseType::RawInputReadChar),
("$quoted_token", 1) => Some(SystemClauseType::QuotedToken),
("$nextEP", 3) => Some(SystemClauseType::NextEP),
("$read_query_term", 2) => Some(SystemClauseType::ReadQueryTerm),
("$read_term", 2) => Some(SystemClauseType::ReadTerm),
("$read_query_term", 5) => Some(SystemClauseType::ReadQueryTerm),
("$read_term", 5) => Some(SystemClauseType::ReadTerm),
("$read_term_from_chars", 2) => Some(SystemClauseType::ReadTermFromChars),
("$reset_block", 1) => Some(SystemClauseType::ResetBlock),
("$reset_cont_marker", 0) => Some(SystemClauseType::ResetContinuationMarker),
("$reset_global_var_at_key", 1) => Some(SystemClauseType::ResetGlobalVarAtKey),
@@ -513,35 +671,70 @@ impl SystemClauseType {
("$set_double_quotes", 1) => Some(SystemClauseType::SetDoubleQuotes),
("$set_seed", 1) => Some(SystemClauseType::SetSeed),
("$skip_max_list", 4) => Some(SystemClauseType::SkipMaxList),
("$sleep", 1) => Some(SystemClauseType::Sleep),
("$socket_client_open", 8) => Some(SystemClauseType::SocketClientOpen),
("$socket_server_open", 3) => Some(SystemClauseType::SocketServerOpen),
("$socket_server_accept", 7) => Some(SystemClauseType::SocketServerAccept),
("$socket_server_close", 1) => Some(SystemClauseType::SocketServerClose),
("$store_global_var", 2) => Some(SystemClauseType::StoreGlobalVar),
("$store_global_var_with_offset", 2) => Some(SystemClauseType::StoreGlobalVarWithOffset),
("$term_attributed_variables", 2) => Some(SystemClauseType::TermAttributedVariables),
("$term_variables", 2) => Some(SystemClauseType::TermVariables),
("$truncate_lh_to", 1) => Some(SystemClauseType::TruncateLiftedHeapTo),
("$unwind_environments", 0) => Some(SystemClauseType::UnwindEnvironments),
("$unwind_stack", 0) => Some(SystemClauseType::UnwindStack),
("$unify_with_occurs_check", 2) => Some(SystemClauseType::UnifyWithOccursCheck),
("$use_module", 1) => Some(SystemClauseType::REPL(REPLCodePtr::UseModule)),
("$use_module_from_file", 1) =>
Some(SystemClauseType::REPL(REPLCodePtr::UseModuleFromFile)),
("$use_qualified_module", 2) =>
Some(SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule)),
("$use_qualified_module_from_file", 2) =>
Some(SystemClauseType::REPL(REPLCodePtr::UseQualifiedModuleFromFile)),
("$directory_files", 2) => Some(SystemClauseType::DirectoryFiles),
("$file_size", 2) => Some(SystemClauseType::FileSize),
("$file_exists", 1) => Some(SystemClauseType::FileExists),
("$directory_exists", 1) => Some(SystemClauseType::DirectoryExists),
("$directory_separator", 1) => Some(SystemClauseType::DirectorySeparator),
("$make_directory", 1) => Some(SystemClauseType::MakeDirectory),
("$delete_file", 1) => Some(SystemClauseType::DeleteFile),
("$working_directory", 2) => Some(SystemClauseType::WorkingDirectory),
("$path_canonical", 2) => Some(SystemClauseType::PathCanonical),
("$file_time", 3) => Some(SystemClauseType::FileTime),
("$use_module", 1) => Some(SystemClauseType::REPL(REPLCodePtr::UseModule)),
("$use_module_from_file", 1) =>
Some(SystemClauseType::REPL(REPLCodePtr::UseModuleFromFile)),
("$use_qualified_module", 2) =>
Some(SystemClauseType::REPL(REPLCodePtr::UseQualifiedModule)),
("$use_qualified_module_from_file", 2) =>
Some(SystemClauseType::REPL(REPLCodePtr::UseQualifiedModuleFromFile)),
("$variant", 2) => Some(SystemClauseType::Variant),
("$write_term", 5) => Some(SystemClauseType::WriteTerm),
("$wam_instructions", 3) => Some(SystemClauseType::WAMInstructions),
("$write_term", 7) => Some(SystemClauseType::WriteTerm),
("$write_term_to_chars", 7) => Some(SystemClauseType::WriteTermToChars),
("$scryer_prolog_version", 1) => Some(SystemClauseType::ScryerPrologVersion),
("$crypto_random_byte", 1) => Some(SystemClauseType::CryptoRandomByte),
("$crypto_data_hash", 4) => Some(SystemClauseType::CryptoDataHash),
("$crypto_data_hkdf", 7) => Some(SystemClauseType::CryptoDataHKDF),
("$crypto_password_hash", 4) => Some(SystemClauseType::CryptoPasswordHash),
("$crypto_data_encrypt", 6) => Some(SystemClauseType::CryptoDataEncrypt),
("$crypto_data_decrypt", 6) => Some(SystemClauseType::CryptoDataDecrypt),
("$crypto_curve_scalar_mult", 5) => Some(SystemClauseType::CryptoCurveScalarMult),
("$ed25519_sign", 5) => Some(SystemClauseType::Ed25519Sign),
("$ed25519_verify", 5) => Some(SystemClauseType::Ed25519Verify),
("$ed25519_new_keypair", 1) => Some(SystemClauseType::Ed25519NewKeyPair),
("$ed25519_keypair_public_key", 3) => Some(SystemClauseType::Ed25519KeyPairPublicKey),
("$curve25519_scalar_mult", 3) => Some(SystemClauseType::Curve25519ScalarMult),
("$load_html", 3) => Some(SystemClauseType::LoadHTML),
("$load_xml", 3) => Some(SystemClauseType::LoadXML),
("$getenv", 2) => Some(SystemClauseType::GetEnv),
("$setenv", 2) => Some(SystemClauseType::SetEnv),
("$unsetenv", 1) => Some(SystemClauseType::UnsetEnv),
("$chars_base64", 4) => Some(SystemClauseType::CharsBase64),
_ => None,
}
}
}
#[derive(Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum BuiltInClauseType {
AcyclicTerm,
Arg,
Compare,
CompareTerm(CompareTermQT),
CyclicTerm,
CopyTerm,
Eq,
Functor,
@@ -554,7 +747,7 @@ pub enum BuiltInClauseType {
Sort,
}
#[derive(Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ClauseType {
BuiltIn(BuiltInClauseType),
CallN,
@@ -572,7 +765,6 @@ impl BuiltInClauseType {
&BuiltInClauseType::Arg => clause_name!("arg"),
&BuiltInClauseType::Compare => clause_name!("compare"),
&BuiltInClauseType::CompareTerm(qt) => clause_name!(qt.name()),
&BuiltInClauseType::CyclicTerm => clause_name!("cyclic_term"),
&BuiltInClauseType::CopyTerm => clause_name!("copy_term"),
&BuiltInClauseType::Eq => clause_name!("=="),
&BuiltInClauseType::Functor => clause_name!("functor"),
@@ -592,7 +784,6 @@ impl BuiltInClauseType {
&BuiltInClauseType::Arg => 3,
&BuiltInClauseType::Compare => 2,
&BuiltInClauseType::CompareTerm(_) => 2,
&BuiltInClauseType::CyclicTerm => 1,
&BuiltInClauseType::CopyTerm => 2,
&BuiltInClauseType::Eq => 2,
&BuiltInClauseType::Functor => 3,

View File

@@ -1,28 +1,30 @@
use prolog_parser::ast::*;
use crate::prolog_parser::ast::*;
use crate::prolog::allocator::*;
use crate::prolog::arithmetic::*;
use crate::prolog::clause_types::*;
use crate::prolog::fixtures::*;
use crate::prolog::forms::*;
use crate::prolog::indexing::*;
use crate::prolog::instructions::*;
use crate::prolog::iterators::*;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::targets::*;
use crate::allocator::*;
use crate::arithmetic::*;
use crate::clause_types::*;
use crate::fixtures::*;
use crate::forms::*;
use crate::indexing::*;
use crate::instructions::*;
use crate::iterators::*;
use crate::machine::machine_indices::*;
use crate::targets::*;
use indexmap::IndexMap;
use crate::indexmap::{IndexMap, IndexSet};
use std::cell::Cell;
use std::rc::Rc;
use std::vec::Vec;
#[derive(Debug)]
pub struct CodeGenerator<TermMarker> {
marker: TermMarker,
pub var_count: IndexMap<Rc<Var>, usize>,
non_counted_bt: bool,
}
#[derive(Debug)]
pub struct ConjunctInfo<'a> {
pub perm_vs: VariableFixtures<'a>,
pub num_of_chunks: usize,
@@ -50,56 +52,45 @@ impl<'a> ConjunctInfo<'a> {
self.has_deep_cut as usize
}
fn mark_unsafe_vars<Alloc: Allocator<'a>>(
fn mark_unsafe_vars(
&self,
mut unsafe_var_marker: UnsafeVarMarker,
marker: &Alloc,
code: &mut Code
code: &mut Code,
) {
// target the last goal of the rule for handling unsafe variables.
// we use this weird logic to find the last goal.
let right_index = if let Some(Line::Control(_)) = code.last() {
if code.len() >= 2 {
code.len() - 2
} else {
return;
}
} else {
if code.len() >= 1 {
code.len() - 1
} else {
return;
}
};
if code.is_empty() {
return;
}
let mut index = right_index;
let mut code_index = 0;
if let Line::Query(_) = &code[right_index] {
while let Line::Query(_) = &code[index] {
if index == 0 {
break;
} else {
index -= 1;
for phase in 0 .. {
while let Line::Query(ref query_instr) = &code[code_index] {
if !unsafe_var_marker.mark_safe_vars(query_instr) {
unsafe_var_marker.mark_phase(query_instr, phase);
}
code_index += 1;
}
if let Line::Query(_) = &code[index] {
if code_index + 1 < code.len() {
code_index += 1;
} else {
index += 1;
break;
}
}
code_index = 0;
for phase in 0 .. {
while let Line::Query(ref mut query_instr) = &mut code[code_index] {
unsafe_var_marker.mark_unsafe_vars(query_instr, phase);
code_index += 1;
}
unsafe_var_marker.record_unsafe_vars(&self.perm_vs, marker);
for line in code.iter() {
if let Line::Query(ref query_instr) = line {
unsafe_var_marker.mark_safe_vars(query_instr);
}
}
for index in index..right_index + 1 {
if let &mut Line::Query(ref mut query_instr) = &mut code[index] {
unsafe_var_marker.mark_unsafe_vars(query_instr);
}
if code_index + 1 < code.len() {
code_index += 1;
} else {
break;
}
}
}
@@ -131,31 +122,45 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
*self.var_count.get(var).unwrap()
}
fn mark_var_in_non_callable(
&mut self,
name: Rc<Var>,
term_loc: GenContext,
vr: &'a Cell<VarReg>,
code: &mut Code,
) -> RegType {
let mut target = Vec::new();
self.marker.mark_var(name, Level::Shallow, vr, term_loc, &mut target);
if !target.is_empty() {
code.extend(target.into_iter().map(Line::Query));
}
vr.get().norm()
}
fn mark_non_callable(
&mut self,
name: Rc<Var>,
arity: usize,
arg: usize,
term_loc: GenContext,
vr: &'a Cell<VarReg>,
code: &mut Code,
) -> RegType {
match self.marker.bindings().get(&name) {
Some(&VarData::Temp(_, t, _)) if t != 0 => RegType::Temp(t),
Some(&VarData::Perm(p)) if p != 0 => RegType::Perm(p),
_ => {
let mut target = Vec::new();
self.marker.reset_arg(arity);
self.marker
.mark_var(name, Level::Shallow, vr, term_loc, &mut target);
if !target.is_empty() {
for query_instr in target {
code.push(Line::Query(query_instr));
}
Some(&VarData::Temp(_, t, _)) if t != 0 => {
RegType::Temp(t)
}
Some(&VarData::Perm(p)) if p != 0 => {
if let GenContext::Last(_) = term_loc {
self.mark_var_in_non_callable(name.clone(), term_loc, vr, code);
temp_v!(arg)
} else {
RegType::Perm(p)
}
vr.get().norm()
}
_ => {
self.mark_var_in_non_callable(name, term_loc, vr, code)
}
}
}
@@ -174,40 +179,49 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
target.push(Target::to_void(1));
}
fn subterm_to_instr<Target>(
fn deep_var_instr<Target: CompilationTarget<'a>>(
&mut self,
cell: &'a Cell<VarReg>,
var: &'a Rc<Var>,
term_loc: GenContext,
is_exposed: bool,
target: &mut Vec<Target>,
) {
if is_exposed || self.get_var_count(var.as_ref()) > 1 {
self.marker.mark_var(var.clone(), Level::Deep, cell, term_loc, target);
} else {
Self::add_or_increment_void_instr(target);
}
}
fn subterm_to_instr<Target: CompilationTarget<'a>>(
&mut self,
subterm: &'a Term,
term_loc: GenContext,
is_exposed: bool,
target: &mut Vec<Target>,
) where
Target: CompilationTarget<'a>,
{
) {
match subterm {
&Term::AnonVar if is_exposed => {
self.marker.mark_anon_var(Level::Deep, term_loc, target)
self.marker.mark_anon_var(Level::Deep, term_loc, target);
}
&Term::AnonVar => {
Self::add_or_increment_void_instr(target);
}
&Term::AnonVar => Self::add_or_increment_void_instr(target),
&Term::Cons(ref cell, _, _) | &Term::Clause(ref cell, _, _, _) => {
self.marker
.mark_non_var(Level::Deep, term_loc, cell, target);
self.marker.mark_non_var(Level::Deep, term_loc, cell, target);
target.push(Target::clause_arg_to_instr(cell.get()));
}
&Term::Constant(_, ref constant) => {
target.push(Target::constant_subterm(constant.clone()))
target.push(Target::constant_subterm(constant.clone()));
}
&Term::Var(ref cell, ref var) => {
if is_exposed || self.get_var_count(var) > 1 {
self.marker
.mark_var(var.clone(), Level::Deep, cell, term_loc, target);
} else {
Self::add_or_increment_void_instr(target);
}
self.deep_var_instr(cell, var, term_loc, is_exposed, target);
}
};
}
fn compile_target<Target, Iter>(
fn compile_target<Target, Iter>(
&mut self,
iter: Iter,
term_loc: GenContext,
@@ -221,6 +235,13 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
for term in iter {
match term {
TermRef::AnonVar(lvl @ Level::Shallow) => {
if let GenContext::Head = term_loc {
self.marker.advance_arg();
} else {
self.marker.mark_anon_var(lvl, term_loc, &mut target);
}
}
TermRef::Clause(lvl, cell, ct, terms) => {
self.marker.mark_non_var(lvl, term_loc, cell, &mut target);
target.push(Target::to_structure(ct, terms.len(), cell.get()));
@@ -236,15 +257,22 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
self.subterm_to_instr(head, term_loc, is_exposed, &mut target);
self.subterm_to_instr(tail, term_loc, is_exposed, &mut target);
}
TermRef::Constant(lvl @ Level::Shallow, cell, Constant::String(ref string)) => {
self.marker.mark_non_var(lvl, term_loc, cell, &mut target);
target.push(Target::to_pstr(lvl, string.to_string(), cell.get(), false));
}
TermRef::Constant(lvl @ Level::Shallow, cell, constant) => {
self.marker.mark_non_var(lvl, term_loc, cell, &mut target);
target.push(Target::to_constant(lvl, constant.clone(), cell.get()));
}
TermRef::AnonVar(lvl @ Level::Shallow) => {
if let GenContext::Head = term_loc {
self.marker.advance_arg();
TermRef::PartialString(lvl, cell, string, tail) => {
self.marker.mark_non_var(lvl, term_loc, cell, &mut target);
if let Some(tail) = tail {
target.push(Target::to_pstr(lvl, string, cell.get(), true));
self.subterm_to_instr(tail, term_loc, is_exposed, &mut target);
} else {
self.marker.mark_anon_var(lvl, term_loc, &mut target);
target.push(Target::to_pstr(lvl, string, cell.get(), false));
}
}
TermRef::Var(lvl @ Level::Shallow, cell, ref var) if var.as_str() == "!" => {
@@ -263,14 +291,13 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
}
}
self.marker
.mark_var(var.clone(), lvl, cell, term_loc, &mut target);
self.marker.mark_var(var.clone(), lvl, cell, term_loc, &mut target);
}
TermRef::Var(lvl @ Level::Shallow, cell, var) => {
self.marker
.mark_var(var.clone(), lvl, cell, term_loc, &mut target)
self.marker.mark_var(var.clone(), lvl, cell, term_loc, &mut target);
}
_ => {
}
_ => {}
};
}
@@ -330,7 +357,9 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
&mut ControlInstruction::CallClause(_, _, _, ref mut last_call, _) => {
*last_call = true
}
&mut ControlInstruction::JmpBy(_, _, _, ref mut last_call) => *last_call = true,
&mut ControlInstruction::JmpBy(_, _, _, ref mut last_call) => {
*last_call = true
}
&mut ControlInstruction::Proceed => {}
_ => dealloc_index += 1,
},
@@ -350,33 +379,42 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
) -> Result<(), ParserError> {
match ct {
&InlinedClauseType::CompareNumber(cmp, ..) => {
if let &Term::Var(ref vr, ref name) = terms[0].as_ref() {
self.mark_non_callable(name.clone(), 2, term_loc, vr, code);
}
if let &Term::Var(ref vr, ref name) = terms[1].as_ref() {
self.mark_non_callable(name.clone(), 2, term_loc, vr, code);
}
self.marker.reset_arg(2);
let (mut lcode, at_1) = self.call_arith_eval(terms[0].as_ref(), 1)?;
let (mut rcode, at_2) = self.call_arith_eval(terms[1].as_ref(), 2)?;
let at_1 =
if let &Term::Var(ref vr, ref name) = terms[0].as_ref() {
ArithmeticTerm::Reg(
self.mark_non_callable(name.clone(), 1, term_loc, vr, code)
)
} else {
at_1.unwrap_or(interm!(1))
};
let at_2 =
if let &Term::Var(ref vr, ref name) = terms[1].as_ref() {
ArithmeticTerm::Reg(
self.mark_non_callable(name.clone(), 2, term_loc, vr, code)
)
} else {
at_2.unwrap_or(interm!(2))
};
code.append(&mut lcode);
code.append(&mut rcode);
code.push(compare_number_instr!(
cmp,
at_1.unwrap_or(interm!(1)),
at_2.unwrap_or(interm!(2))
));
code.push(compare_number_instr!(cmp, at_1, at_2));
}
&InlinedClauseType::IsAtom(..) => match terms[0].as_ref() {
&Term::Constant(_, Constant::Char(_))
| &Term::Constant(_, Constant::EmptyList)
| &Term::Constant(_, Constant::Atom(..)) => {
&Term::Constant(_, Constant::Char(_)) |
&Term::Constant(_, Constant::EmptyList) |
&Term::Constant(_, Constant::Atom(..)) => {
code.push(succeed!());
}
&Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1);
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_atom!(r));
}
@@ -392,6 +430,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
code.push(succeed!());
}
&Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1);
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_atomic!(r));
}
@@ -401,6 +440,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
code.push(succeed!());
}
&Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1);
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_compound!(r));
}
@@ -413,6 +453,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
code.push(succeed!());
}
&Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1);
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_rational!(r));
}
@@ -425,6 +466,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
code.push(succeed!());
}
&Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1);
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_float!(r));
}
@@ -432,23 +474,12 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
code.push(fail!());
}
},
&InlinedClauseType::IsString(..) => match terms[0].as_ref() {
&Term::Constant(_, Constant::String(..)) => {
code.push(succeed!());
}
&Term::Var(ref vr, ref name) => {
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_string!(r));
}
_ => {
code.push(fail!());
}
},
&InlinedClauseType::IsNonVar(..) => match terms[0].as_ref() {
&Term::AnonVar => {
code.push(fail!());
}
&Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1);
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_nonvar!(r));
}
@@ -457,10 +488,12 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
}
},
&InlinedClauseType::IsInteger(..) => match terms[0].as_ref() {
&Term::Constant(_, Constant::Integer(_)) => {
&Term::Constant(_, Constant::Integer(_)) |
&Term::Constant(_, Constant::Fixnum(_)) => {
code.push(succeed!());
}
&Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1);
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_integer!(r));
}
@@ -476,6 +509,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
code.push(succeed!());
}
&Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1);
let r = self.mark_non_callable(name.clone(), 1, term_loc, vr, code);
code.push(is_var!(r));
}
@@ -490,7 +524,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
term: &'a Term,
target_int: usize,
) -> Result<ArithCont, ArithmeticError> {
let mut evaluator = ArithmeticEvaluator::new(self.marker.bindings(), target_int);
let mut evaluator = ArithmeticEvaluator::new(&self.marker.bindings(), target_int);
evaluator.eval(term)
}
@@ -504,36 +538,27 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
let (mut acode, at) = self.call_arith_eval(terms[1].as_ref(), 1)?;
code.append(&mut acode);
Ok(match terms[0].as_ref() {
self.marker.reset_arg(2);
match terms[0].as_ref() {
&Term::Var(ref vr, ref name) => {
let mut target = vec![];
self.marker.reset_arg(2);
self.marker
.mark_var(name.clone(), Level::Shallow, vr, term_loc, &mut target);
self.marker.mark_var(name.clone(), Level::Shallow, vr, term_loc, &mut target);
if !target.is_empty() {
code.extend(target.into_iter().map(Line::Query));
}
if use_default_call_policy {
code.push(is_call_by_default!(temp_v!(1), at.unwrap_or(interm!(1))))
} else {
code.push(is_call!(temp_v!(1), at.unwrap_or(interm!(1))))
}
}
&Term::Constant(_, ref c @ Constant::Integer(_)) => {
&Term::Constant(_, ref c @ Constant::Integer(_)) |
&Term::Constant(_, ref c @ Constant::Fixnum(_)) => {
code.push(Line::Query(put_constant!(
Level::Shallow,
c.clone(),
temp_v!(1)
)));
if use_default_call_policy {
code.push(is_call_by_default!(temp_v!(1), at.unwrap_or(interm!(1))))
} else {
code.push(is_call!(temp_v!(1), at.unwrap_or(interm!(1))))
}
self.marker.advance_arg();
}
&Term::Constant(_, ref c @ Constant::Float(_)) => {
code.push(Line::Query(put_constant!(
@@ -542,11 +567,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
temp_v!(1)
)));
if use_default_call_policy {
code.push(is_call_by_default!(temp_v!(1), at.unwrap_or(interm!(1))))
} else {
code.push(is_call!(temp_v!(1), at.unwrap_or(interm!(1))))
}
self.marker.advance_arg();
}
&Term::Constant(_, ref c @ Constant::Rational(_)) => {
code.push(Line::Query(put_constant!(
@@ -555,13 +576,27 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
temp_v!(1)
)));
if use_default_call_policy {
code.push(is_call_by_default!(temp_v!(1), at.unwrap_or(interm!(1))))
} else {
code.push(is_call!(temp_v!(1), at.unwrap_or(interm!(1))))
}
self.marker.advance_arg();
}
_ => code.push(fail!()),
_ => {
code.push(fail!());
return Ok(());
}
}
let at =
if let &Term::Var(ref vr, ref name) = terms[1].as_ref() {
ArithmeticTerm::Reg(
self.mark_non_callable(name.clone(), 2, term_loc, vr, code)
)
} else {
at.unwrap_or(interm!(1))
};
Ok(if use_default_call_policy {
code.push(is_call_by_default!(temp_v!(1), at));
} else {
code.push(is_call!(temp_v!(1), at));
})
}
@@ -684,6 +719,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
head: (_, ref args, ref p1),
ref clauses,
} = rule;
let mut code = Vec::new();
self.marker.reset_at_head(args);
@@ -705,39 +741,31 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
let iter = ChunkedIterator::from_rule_body(p1, clauses);
self.compile_seq(iter, &conjunct_info, &mut code, false)?;
conjunct_info.mark_unsafe_vars(unsafe_var_marker, &self.marker, &mut code);
conjunct_info.mark_unsafe_vars(unsafe_var_marker, &mut code);
Self::compile_cleanup(&mut code, &conjunct_info, clauses.last().unwrap_or(p1));
Ok(code)
}
fn mark_unsafe_fact_vars(&self, fact: &mut CompiledFact) -> UnsafeVarMarker {
let mut unsafe_vars = IndexMap::new();
for var_status in self.marker.bindings().values() {
unsafe_vars.insert(var_status.as_reg_type(), false);
}
let mut safe_vars = IndexSet::new();
for fact_instr in fact.iter_mut() {
match fact_instr {
&mut FactInstruction::UnifyValue(reg) => {
if let Some(found) = unsafe_vars.get_mut(&reg) {
if !*found {
*found = true;
*fact_instr = FactInstruction::UnifyLocalValue(reg);
}
&mut FactInstruction::UnifyValue(r) => {
if !safe_vars.contains(&r) {
*fact_instr = FactInstruction::UnifyLocalValue(r);
safe_vars.insert(r);
}
}
&mut FactInstruction::UnifyVariable(reg) => {
if let Some(found) = unsafe_vars.get_mut(&reg) {
*found = true;
}
&mut FactInstruction::UnifyVariable(r) => {
safe_vars.insert(r);
}
_ => {}
};
}
}
UnsafeVarMarker { unsafe_vars }
UnsafeVarMarker::from_safe_vars(safe_vars)
}
pub fn compile_fact<'b: 'a>(&mut self, term: &'b Term) -> Code {
@@ -802,7 +830,7 @@ impl<'a, TermMarker: Allocator<'a>> CodeGenerator<TermMarker> {
let iter = ChunkedIterator::from_term_sequence(query);
self.compile_seq(iter, &conjunct_info, &mut code, true)?;
conjunct_info.mark_unsafe_vars(UnsafeVarMarker::new(), &self.marker, &mut code);
conjunct_info.mark_unsafe_vars(UnsafeVarMarker::new(), &mut code);
if let Some(query_term) = query.last() {
Self::compile_cleanup(&mut code, &conjunct_info, query_term);

View File

@@ -1,17 +1,18 @@
use indexmap::IndexMap;
use crate::indexmap::IndexMap;
use prolog_parser::ast::*;
use crate::prolog_parser::ast::*;
use crate::prolog::allocator::*;
use crate::prolog::fixtures::*;
use crate::prolog::forms::*;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::targets::*;
use crate::allocator::*;
use crate::fixtures::*;
use crate::forms::*;
use crate::machine::machine_indices::*;
use crate::targets::*;
use std::cell::Cell;
use std::collections::BTreeSet;
use std::rc::Rc;
#[derive(Debug)]
pub struct DebrayAllocator {
bindings: IndexMap<Rc<Var>, VarData>,
arg_c: usize,
@@ -292,7 +293,9 @@ impl<'a> Allocator<'a> for DebrayAllocator {
(pr, true)
}
r => (r, false),
r => {
(r, false)
}
};
self.mark_reserved_var(var, lvl, cell, term_loc, target, r, is_new_var);

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,57 @@
/* 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)).
permutation([], []).
permutation([X|Xs], Ys) :-
permutation(Xs, Yss),
select(X, Ys, Yss).
valid_time([H1,H2,M1,M2], T) :-
memberd_t(H1, [0,1,2], TH1),
memberd_t(H2, [0,1,2,3,4,5,6,7,8,9], TH2),
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,11 +1,10 @@
use prolog_parser::ast::*;
use crate::prolog_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};
use crate::indexmap::{IndexMap, IndexSet};
use std::cell::Cell;
use std::collections::BTreeSet;
@@ -14,6 +13,7 @@ use std::rc::Rc;
use std::vec::Vec;
// labeled with chunk numbers.
#[derive(Debug)]
pub enum VarStatus {
Perm(usize),
Temp(usize, TempVarData), // Perm(chunk_num) | Temp(chunk_num, _)
@@ -23,6 +23,7 @@ pub type OccurrenceSet = BTreeSet<(GenContext, usize)>;
// Perm: 0 initially, a stack register once processed.
// Temp: labeled with chunk_num and temp offset (unassigned if 0).
#[derive(Debug)]
pub enum VarData {
Perm(usize),
Temp(usize, usize, TempVarData),
@@ -37,6 +38,7 @@ impl VarData {
}
}
#[derive(Debug)]
pub struct TempVarData {
pub last_term_arity: usize,
pub use_set: OccurrenceSet,
@@ -80,6 +82,7 @@ impl TempVarData {
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
#[derive(Debug)]
pub struct VariableFixtures<'a>{
perm_vars: IndexMap<Rc<Var>, VariableFixture<'a>>,
last_chunk_temp_vars: IndexSet<Rc<Var>>
@@ -91,7 +94,7 @@ impl<'a> VariableFixtures<'a> {
perm_vars: IndexMap::new(),
last_chunk_temp_vars: IndexSet::new()
}
}
pub fn insert(&mut self, var: Rc<Var>, vs: VariableFixture<'a>) {
@@ -249,68 +252,69 @@ impl<'a> VariableFixtures<'a> {
}
}
#[derive(Debug)]
pub struct UnsafeVarMarker {
pub unsafe_vars: IndexMap<RegType, bool>,
pub unsafe_vars: IndexMap<RegType, usize>,
pub safe_vars: IndexSet<RegType>,
}
impl UnsafeVarMarker {
pub 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 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 fn mark_safe_vars(&mut self, query_instr: &QueryInstruction) -> bool {
match query_instr {
QueryInstruction::PutVariable(RegType::Temp(r), _) => {
if let Some(found) = self.unsafe_vars.get_mut(&RegType::Temp(*r)) {
*found = true;
}
&QueryInstruction::PutVariable(r @ RegType::Temp(_), _)
| &QueryInstruction::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 fn mark_phase(&mut self, query_instr: &QueryInstruction, phase: usize) {
match query_instr {
&QueryInstruction::PutValue(r @ RegType::Perm(_), _)
| &QueryInstruction::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 fn mark_unsafe_vars(&mut self, query_instr: &mut QueryInstruction, 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;
if let Some(p) = self.unsafe_vars.swap_remove(&RegType::Perm(i)) {
if p == phase {
*query_instr = QueryInstruction::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 QueryInstruction::SetValue(r) => {
if !self.safe_vars.contains(&r) {
*query_instr = QueryInstruction::SetLocalValue(r);
self.safe_vars.insert(r);
self.unsafe_vars.remove(&r);
}
}
_ => {}

View File

@@ -1,14 +1,14 @@
use prolog_parser::ast::*;
use prolog_parser::parser::OpDesc;
use prolog_parser::tabled_rc::*;
use crate::prolog_parser::ast::*;
use crate::prolog_parser::parser::OpDesc;
use crate::prolog_parser::tabled_rc::*;
use crate::prolog::clause_types::*;
use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::ordered_float::OrderedFloat;
use crate::prolog::rug::{Integer, Rational};
use crate::clause_types::*;
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use crate::ordered_float::OrderedFloat;
use crate::rug::{Integer, Rational};
use indexmap::IndexMap;
use crate::indexmap::IndexMap;
use std::cell::Cell;
use std::collections::VecDeque;
@@ -21,7 +21,7 @@ pub type PredicateKey = (ClauseName, usize); // name, arity.
// of vars (we get their adjoining cells this way).
pub type JumpStub = Vec<Term>;
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum TopLevel {
Declaration(Declaration),
Fact(Term, usize, usize), // Term, line_num, col_num
@@ -41,7 +41,7 @@ impl TopLevel {
}
}
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub enum Level {
Deep,
Root,
@@ -57,7 +57,7 @@ impl Level {
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum QueryTerm {
// register, clause type, subterms, use default call policy.
Clause(Cell<RegType>, ClauseType, Vec<Box<Term>>, bool),
@@ -85,13 +85,13 @@ impl QueryTerm {
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct Rule {
pub head: (ClauseName, Vec<Box<Term>>, QueryTerm),
pub clauses: Vec<QueryTerm>,
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct Predicate(pub Vec<PredicateClause>);
impl Predicate {
@@ -113,7 +113,7 @@ impl Predicate {
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum ListingSource {
File(ClauseName, PathBuf), // filename, path
User,
@@ -282,9 +282,9 @@ impl ClauseConsistency for Rule {
fn name_and_module(&self) -> Option<(ClauseName, ClauseName)> {
Some((self.head.0.owning_module(), self.head.0.clone()))
}
fn arity(&self) -> usize {
self.head.1.len()
self.head.1.len()
}
}
@@ -325,7 +325,7 @@ impl ClauseConsistency for Predicate {
pub type CompiledResult = (Predicate, VecDeque<TopLevel>);
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum PredicateClause {
Fact(Term, usize, usize), // Term, line number, column number.
Rule(Rule, usize, usize), // Term, line number, column number.
@@ -370,21 +370,34 @@ impl PredicateClause {
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum ModuleSource {
Library(ClauseName),
File(ClauseName),
}
impl ModuleSource {
pub fn as_functor_stub(&self) -> MachineStub {
match self {
ModuleSource::Library(ref name) => {
functor!("library", [clause_name(name.clone())])
}
ModuleSource::File(ref name) => {
functor!(clause_name(name.clone()))
}
}
}
}
pub type ScopedPredicateKey = (ClauseName, PredicateKey); // module name, predicate indicator.
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum MultiFileIndicator {
LocalScoped(ClauseName, usize), // name, arity
ModuleScoped(ScopedPredicateKey),
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum Declaration {
Dynamic(ClauseName, usize), // name, arity
EndOfFile,
@@ -394,6 +407,7 @@ pub enum Declaration {
MultiFile(MultiFileIndicator),
NonCountedBacktracking(ClauseName, usize), // name, arity
Op(OpDecl),
SetPrologFlag(DoubleQuotes),
UseModule(ModuleSource),
UseQualifiedModule(ModuleSource, Vec<ModuleExport>),
}
@@ -418,7 +432,7 @@ impl Declaration {
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct OpDecl(pub usize, pub Specifier, pub ClauseName);
impl OpDecl {
@@ -491,11 +505,11 @@ pub fn fetch_atom_op_spec(
spec: Option<SharedOpDesc>,
op_dir: &OpDir,
) -> Option<SharedOpDesc> {
fetch_op_spec(name.clone(), 1, spec.clone(), op_dir)
.or_else(|| fetch_op_spec(name, 2, spec, op_dir))
fetch_op_spec_from_existing(name.clone(), 1, spec.clone(), op_dir)
.or_else(|| fetch_op_spec_from_existing(name, 2, spec, op_dir))
}
pub fn fetch_op_spec(
pub fn fetch_op_spec_from_existing(
name: ClauseName,
arity: usize,
spec: Option<SharedOpDesc>,
@@ -510,7 +524,15 @@ pub fn fetch_op_spec(
}
}
spec.or_else(|| match arity {
spec.or_else(|| fetch_op_spec(name, arity, op_dir))
}
pub fn fetch_op_spec(
name: ClauseName,
arity: usize,
op_dir: &OpDir,
) -> Option<SharedOpDesc> {
match arity {
2 => op_dir
.get(&(name, Fixity::In))
.and_then(|OpDirValue(spec, _)| {
@@ -528,7 +550,7 @@ pub fn fetch_op_spec(
}
op_dir
.get(&(name.clone(), Fixity::Post))
.get(&(name, Fixity::Post))
.and_then(|OpDirValue(spec, _)| {
if spec.prec() > 0 {
Some(spec.clone())
@@ -537,24 +559,27 @@ pub fn fetch_op_spec(
}
})
}
_ => None,
})
_ => {
None
}
}
}
pub type ModuleDir = IndexMap<ClauseName, Module>;
#[derive(Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub enum ModuleExport {
OpDecl(OpDecl),
PredicateKey(PredicateKey),
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct ModuleDecl {
pub name: ClauseName,
pub exports: Vec<ModuleExport>,
}
#[derive(Debug)]
pub struct Module {
pub atom_tbl: TabledData<Atom>,
pub module_decl: ModuleDecl,
@@ -572,11 +597,33 @@ pub struct Module {
pub listing_src: ListingSource,
}
#[derive(Clone, PartialEq, Eq)]
#[derive(Debug, Clone)]
pub enum Number {
Float(OrderedFloat<f64>),
Integer(Integer),
Rational(Rational),
Integer(Rc<Integer>),
Rational(Rc<Rational>),
Fixnum(isize),
}
impl From<Integer> for Number {
#[inline]
fn from(n: Integer) -> Self {
Number::Integer(Rc::new(n))
}
}
impl From<Rational> for Number {
#[inline]
fn from(n: Rational) -> Self {
Number::Rational(Rc::new(n))
}
}
impl From<isize> for Number {
#[inline]
fn from(n: isize) -> Self {
Number::Fixnum(n)
}
}
impl Default for Number {
@@ -585,48 +632,74 @@ impl Default for Number {
}
}
impl Number {
pub fn to_constant(self) -> Constant {
impl Into<Constant> for Number {
#[inline]
fn into(self) -> Constant {
match self {
Number::Fixnum(n) => Constant::Fixnum(n),
Number::Integer(n) => Constant::Integer(n),
Number::Float(f) => Constant::Float(f),
Number::Rational(r) => Constant::Rational(r),
}
}
}
impl Into<HeapCellValue> for Number {
#[inline]
fn into(self) -> HeapCellValue {
match self {
Number::Fixnum(n) => HeapCellValue::Addr(Addr::Fixnum(n)),
Number::Integer(n) => HeapCellValue::Integer(n),
Number::Float(f) => HeapCellValue::Addr(Addr::Float(f)),
Number::Rational(r) => HeapCellValue::Rational(r),
}
}
}
impl Number {
#[inline]
pub fn is_positive(&self) -> bool {
match self {
&Number::Integer(ref n) => n > &0,
&Number::Fixnum(n) => n > 0,
&Number::Integer(ref n) => &**n > &0,
&Number::Float(OrderedFloat(f)) => f.is_sign_positive(),
&Number::Rational(ref r) => r > &0,
&Number::Rational(ref r) => &**r > &0,
}
}
#[inline]
pub fn is_negative(&self) -> bool {
match self {
&Number::Integer(ref n) => n < &0,
&Number::Fixnum(n) => n < 0,
&Number::Integer(ref n) => &**n < &0,
&Number::Float(OrderedFloat(f)) => f.is_sign_negative(),
&Number::Rational(ref r) => r < &0,
&Number::Rational(ref r) => &**r < &0,
}
}
#[inline]
pub fn is_zero(&self) -> bool {
match self {
&Number::Integer(ref n) => n == &0,
&Number::Fixnum(n) => n == 0,
&Number::Integer(ref n) => &**n == &0,
&Number::Float(f) => f == OrderedFloat(0f64),
&Number::Rational(ref r) => r == &0,
&Number::Rational(ref r) => &**r == &0,
}
}
#[inline]
pub fn abs(self) -> Self {
match self {
Number::Integer(n) => Number::Integer(n.abs()),
Number::Fixnum(n) =>
if let Some(n) = n.checked_abs() {
Number::from(n)
} else {
Number::from(Integer::from(n).abs())
}
Number::Integer(n) => Number::from(Integer::from(n.abs_ref())),
Number::Float(f) => Number::Float(OrderedFloat(f.abs())),
Number::Rational(r) => Number::Rational(r.abs()),
Number::Rational(r) => Number::from(Rational::from(r.abs_ref())),
}
}
}

342
src/heap_iter.rs Normal file
View File

@@ -0,0 +1,342 @@
use crate::machine::machine_indices::*;
use crate::machine::machine_state::*;
use crate::indexmap::IndexSet;
use std::cmp::Ordering;
use std::ops::Deref;
use std::vec::Vec;
#[derive(Debug)]
pub struct HCPreOrderIterator<'a> {
pub machine_st: &'a MachineState,
pub state_stack: Vec<Addr>,
}
impl<'a> HCPreOrderIterator<'a> {
pub fn new(machine_st: &'a MachineState, a: Addr) -> Self {
HCPreOrderIterator {
machine_st,
state_stack: vec![a],
}
}
#[inline]
pub fn machine_st(&self) -> &MachineState {
&self.machine_st
}
fn follow_heap(&mut self, h: usize) -> Addr {
match &self.machine_st.heap[h] {
&HeapCellValue::NamedStr(arity, _, _) => {
for idx in (1 .. arity + 1).rev() {
self.state_stack.push(Addr::HeapCell(h + idx));
}
Addr::Str(h)
}
&HeapCellValue::Addr(a) => {
self.follow(a)
}
HeapCellValue::PartialString(..) => {
self.follow(Addr::PStrLocation(h, 0))
}
HeapCellValue::Atom(..) | HeapCellValue::DBRef(_)
| HeapCellValue::Integer(_) | HeapCellValue::Rational(_) => {
Addr::Con(h)
}
HeapCellValue::Stream(_) => {
Addr::Stream(h)
}
&HeapCellValue::TcpListener(_) => {
Addr::TcpListener(h)
}
}
}
// called under the assumption that the location at r is about to
// be visited, and so any follow up states need to be added to
// state_stack. returns the dereferenced Addr from Ref.
fn follow(&mut self, addr: Addr) -> Addr {
let da = self.machine_st.store(self.machine_st.deref(addr));
match da {
Addr::Lis(a) => {
self.state_stack.push(Addr::HeapCell(a + 1));
self.state_stack.push(Addr::HeapCell(a));
da
}
Addr::PStrLocation(h, n) => {
if let &HeapCellValue::PartialString(ref pstr, has_tail) = &self.machine_st.heap[h] {
if let Some(c) = pstr.range_from(n ..).next() {
if !pstr.at_end(n + c.len_utf8()) {
self.state_stack.push(Addr::PStrLocation(h, n + c.len_utf8()));
} else if has_tail {
self.state_stack.push(Addr::HeapCell(h + 1));
} else {
self.state_stack.push(Addr::EmptyList);
}
self.state_stack.push(Addr::Char(c));
} else if has_tail {
return self.follow(Addr::HeapCell(h + 1));
}
} else {
unreachable!()
}
Addr::PStrLocation(h, n)
}
Addr::Str(s) => {
self.follow_heap(s) // record terms of structure.
}
Addr::Con(h) => {
if let &HeapCellValue::PartialString(ref pstr, has_tail) = &self.machine_st.heap[h] {
if let Some(c) = pstr.range_from(0 ..).next() {
self.state_stack.push(Addr::PStrLocation(h, c.len_utf8()));
self.state_stack.push(Addr::Char(c));
Addr::PStrLocation(h, 0)
} else if has_tail {
self.follow(Addr::HeapCell(h + 1))
} else {
Addr::EmptyList
}
} else {
Addr::Con(h)
}
}
da => {
da
}
}
}
}
impl<'a> Iterator for HCPreOrderIterator<'a> {
type Item = Addr;
fn next(&mut self) -> Option<Self::Item> {
self.state_stack.pop().map(|a| self.follow(a))
}
}
pub trait MutStackHCIterator<'b> where Self: Iterator
{
type MutStack;
fn stack(&'b mut self) -> Self::MutStack;
}
#[derive(Debug)]
pub struct HCPostOrderIterator<'a> {
base_iter: HCPreOrderIterator<'a>,
parent_stack: Vec<(usize, Addr)>, // number of children, parent node.
}
impl<'a> Deref for HCPostOrderIterator<'a> {
type Target = HCPreOrderIterator<'a>;
fn deref(&self) -> &Self::Target {
&self.base_iter
}
}
impl<'a> HCPostOrderIterator<'a> {
pub fn new(base_iter: HCPreOrderIterator<'a>) -> Self {
HCPostOrderIterator {
base_iter,
parent_stack: vec![],
}
}
}
impl<'a> Iterator for HCPostOrderIterator<'a> {
type Item = Addr;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some((child_count, node)) = self.parent_stack.pop() {
if child_count == 0 {
return Some(node);
}
self.parent_stack.push((child_count - 1, node));
}
if let Some(item) = self.base_iter.next() {
match self.base_iter.machine_st.heap.index_addr(&item).as_ref() {
&HeapCellValue::NamedStr(arity, ..) => {
self.parent_stack.push((arity, item));
}
&HeapCellValue::Addr(Addr::Lis(a)) => {
self.parent_stack.push((2, Addr::Lis(a)));
}
&HeapCellValue::Addr(Addr::PStrLocation(h, n)) => {
match &self.machine_st.heap[h] {
&HeapCellValue::PartialString(ref pstr, _) => {
let c = pstr.range_from(n ..).next().unwrap();
let next_n = n + c.len_utf8();
if !pstr.at_end(next_n) {
self.parent_stack.push((2, Addr::PStrLocation(h, next_n)));
}
}
_ => {
unreachable!()
}
}
}
_ => {
return Some(item);
}
}
} else {
return None;
}
}
}
}
impl MachineState {
pub fn pre_order_iter<'a>(&'a self, a: Addr) -> HCPreOrderIterator<'a> {
HCPreOrderIterator::new(self, a)
}
pub fn post_order_iter<'a>(&'a self, a: Addr) -> HCPostOrderIterator<'a> {
HCPostOrderIterator::new(HCPreOrderIterator::new(self, a))
}
pub fn acyclic_pre_order_iter<'a>(&'a self, a: Addr,) -> HCAcyclicIterator<'a> {
HCAcyclicIterator::new(HCPreOrderIterator::new(self, a))
}
pub fn zipped_acyclic_pre_order_iter<'a>(
&'a self,
a1: Addr,
a2: Addr,
) -> HCZippedAcyclicIterator<'a> {
HCZippedAcyclicIterator::new(
HCPreOrderIterator::new(self, a1),
HCPreOrderIterator::new(self, a2),
)
}
}
impl<'b, 'a: 'b> MutStackHCIterator<'b> for HCPreOrderIterator<'a> {
type MutStack = &'b mut Vec<Addr>;
fn stack(&'b mut self) -> Self::MutStack {
&mut self.state_stack
}
}
#[derive(Debug)]
pub struct HCAcyclicIterator<'a> {
iter: HCPreOrderIterator<'a>,
seen: IndexSet<Addr>,
}
impl<'a> HCAcyclicIterator<'a> {
pub fn new(iter: HCPreOrderIterator<'a>) -> Self {
HCAcyclicIterator {
iter,
seen: IndexSet::new(),
}
}
}
impl<'a> Deref for HCAcyclicIterator<'a> {
type Target = HCPreOrderIterator<'a>;
fn deref(&self) -> &Self::Target {
&self.iter
}
}
impl<'b, 'a: 'b> MutStackHCIterator<'b> for HCAcyclicIterator<'a> {
type MutStack = &'b mut Vec<Addr>;
fn stack(&'b mut self) -> Self::MutStack {
self.iter.stack()
}
}
impl<'a> Iterator for HCAcyclicIterator<'a>
{
type Item = Addr;
fn next(&mut self) -> Option<Self::Item> {
while let Some(addr) = self.iter.stack().pop() {
if !self.seen.contains(&addr) {
self.iter.stack().push(addr.clone());
self.seen.insert(addr);
break;
}
}
self.iter.next()
}
}
#[derive(Debug)]
pub struct HCZippedAcyclicIterator<'a> {
i1: HCPreOrderIterator<'a>,
i2: HCPreOrderIterator<'a>,
seen: IndexSet<(Addr, Addr)>,
pub first_to_expire: Ordering,
}
impl<'b, 'a: 'b> MutStackHCIterator<'b> for HCZippedAcyclicIterator<'a> {
type MutStack = (&'b mut Vec<Addr>, &'b mut Vec<Addr>);
fn stack(&'b mut self) -> Self::MutStack {
(self.i1.stack(), self.i2.stack())
}
}
impl<'a> HCZippedAcyclicIterator<'a> {
pub fn new(i1: HCPreOrderIterator<'a>, i2: HCPreOrderIterator<'a>) -> Self {
HCZippedAcyclicIterator {
i1,
i2,
seen: IndexSet::new(),
first_to_expire: Ordering::Equal,
}
}
}
impl<'a> Iterator for HCZippedAcyclicIterator<'a>
{
type Item = (Addr, Addr);
fn next(&mut self) -> Option<Self::Item> {
while let (Some(a1), Some(a2)) = (self.i1.stack().pop(), self.i2.stack().pop()) {
if !self.seen.contains(&(a1.clone(), a2.clone())) {
self.i1.stack().push(a1.clone());
self.i2.stack().push(a2.clone());
self.seen.insert((a1, a2));
break;
}
}
match (self.i1.next(), self.i2.next()) {
(Some(v1), Some(v2)) => Some((v1, v2)),
(Some(_), None) => {
self.first_to_expire = Ordering::Greater;
None
}
(None, Some(_)) => {
self.first_to_expire = Ordering::Less;
None
}
_ => {
None
}
}
}
}

1577
src/heap_print.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,26 @@
use prolog_parser::ast::*;
use crate::prolog_parser::ast::*;
use crate::prolog_parser::tabled_rc::*;
use crate::prolog::instructions::*;
use crate::instructions::*;
use crate::rug::Integer;
use indexmap::IndexMap;
use crate::indexmap::IndexMap;
use std::collections::VecDeque;
use std::convert::TryFrom;
use std::hash::Hash;
use std::rc::Rc;
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
enum IntIndex {
External(usize),
Fail,
Internal(usize),
}
#[derive(Debug)]
pub struct CodeOffsets {
atom_tbl: TabledData<Atom>,
pub constants: IndexMap<Constant, ThirdLevelIndex>,
pub lists: ThirdLevelIndex,
pub structures: IndexMap<(ClauseName, usize), ThirdLevelIndex>,
@@ -23,6 +29,7 @@ pub struct CodeOffsets {
impl CodeOffsets {
pub fn new() -> Self {
CodeOffsets {
atom_tbl: TabledData::new(Rc::new("_index".to_string())),
constants: IndexMap::new(),
lists: Vec::new(),
structures: IndexMap::new(),
@@ -48,6 +55,94 @@ impl CodeOffsets {
}
}
fn intercept_overlapping_constant(&mut self, constant: &Constant, index: usize) {
match constant {
&Constant::Atom(ref name, ref op) if name.is_char() => {
let c = name.as_str().chars().next().unwrap();
let code = self.constants
.entry(Constant::Char(c))
.or_insert(vec![]);
code.push(Self::add_index(code.is_empty(), index));
if op.is_some() {
let code = self.constants
.entry(Constant::Atom(name.clone(), None))
.or_insert(vec![]);
code.push(Self::add_index(false, index));
}
}
&Constant::Atom(ref name, Some(_)) => {
let code = self.constants
.entry(Constant::Atom(name.clone(), None))
.or_insert(vec![]);
code.push(Self::add_index(code.is_empty(), index));
}
&Constant::Char(c) => {
let atom = clause_name!(c.to_string(), self.atom_tbl.clone());
let code = self.constants
.entry(Constant::Atom(atom, None))
.or_insert(vec![]);
code.push(Self::add_index(code.is_empty(), index));
}
&Constant::Fixnum(n) => {
let code = self.constants
.entry(Constant::Integer(Rc::new(Integer::from(n))))
.or_insert(vec![]);
code.push(Self::add_index(code.is_empty(), index));
if n >= 0 {
if let Ok(n) = usize::try_from(n) {
let code = self.constants
.entry(Constant::Usize(n))
.or_insert(vec![]);
code.push(Self::add_index(code.is_empty(), index));
}
}
}
&Constant::Integer(ref n) => {
if let Some(n) = n.to_isize() {
let code = self.constants
.entry(Constant::Fixnum(n))
.or_insert(vec![]);
code.push(Self::add_index(code.is_empty(), index));
}
if let Some(n) = n.to_usize() {
let code = self.constants
.entry(Constant::Usize(n))
.or_insert(vec![]);
code.push(Self::add_index(code.is_empty(), index));
}
}
&Constant::Usize(n) => {
let code = self.constants
.entry(Constant::Integer(Rc::new(Integer::from(n))))
.or_insert(vec![]);
code.push(Self::add_index(code.is_empty(), index));
if let Ok(n) = isize::try_from(n) {
let code = self.constants
.entry(Constant::Fixnum(n))
.or_insert(vec![]);
code.push(Self::add_index(code.is_empty(), index));
}
}
_ => {
}
}
}
pub fn index_term(&mut self, first_arg: &Term, index: usize) {
match first_arg {
&Term::Clause(_, ref name, ref terms, _) => {
@@ -59,27 +154,22 @@ impl CodeOffsets {
let is_initial_index = code.is_empty();
code.push(Self::add_index(is_initial_index, index));
}
&Term::Cons(..) => {
&Term::Cons(..) | &Term::Constant(_, Constant::String(_)) => {
let is_initial_index = self.lists.is_empty();
self.lists.push(Self::add_index(is_initial_index, index));
}
&Term::Constant(_, Constant::String(n, ref s)) => {
let is_initial_index = self.lists.is_empty();
self.lists.push(Self::add_index(is_initial_index, index));
let constant = Constant::String(n, s.clone());
let code = self.constants.entry(constant).or_insert(Vec::new());
let is_initial_index = code.is_empty();
code.push(Self::add_index(is_initial_index, index));
}
&Term::Constant(_, ref constant) => {
let code = self.constants.entry(constant.clone()).or_insert(Vec::new());
self.intercept_overlapping_constant(constant, index);
let code = self.constants
.entry(constant.clone())
.or_insert(vec![]);
let is_initial_index = code.is_empty();
code.push(Self::add_index(is_initial_index, index));
}
_ => {}
_ => {
}
};
}
@@ -227,12 +317,11 @@ impl CodeOffsets {
fn switch_on_lst_offset_from(
lst_loc: IntIndex,
prelude_len: usize,
lst_offset: usize,
) -> usize {
match lst_loc {
IntIndex::External(o) => o + prelude_len + 1,
IntIndex::Fail => 0,
IntIndex::Internal(_) => prelude_len - lst_offset + 1,
IntIndex::Internal(_) => 1, // this internal is always 0.
}
}
@@ -245,8 +334,6 @@ impl CodeOffsets {
let mut prelude = VecDeque::new();
let lst_loc = Self::switch_on_list(self.lists, &mut prelude);
let lst_offset = prelude.len();
let str_loc = Self::switch_on_structure(self.structures, &mut prelude);
let con_loc = Self::switch_on_constant(self.constants, &mut prelude);
@@ -254,18 +341,19 @@ impl CodeOffsets {
for (index, line) in prelude.iter_mut().enumerate() {
match line {
&mut Line::IndexedChoice(IndexedChoiceInstruction::Try(ref mut i))
| &mut Line::IndexedChoice(IndexedChoiceInstruction::Retry(ref mut i))
| &mut Line::IndexedChoice(IndexedChoiceInstruction::Trust(ref mut i)) => {
*i += prelude_length - index
&mut Line::IndexedChoice(IndexedChoiceInstruction::Try(ref mut i)) |
&mut Line::IndexedChoice(IndexedChoiceInstruction::Retry(ref mut i)) |
&mut Line::IndexedChoice(IndexedChoiceInstruction::Trust(ref mut i)) => {
*i += prelude_length - index;
}
_ => {
}
_ => {}
}
}
let str_loc = Self::switch_on_str_offset_from(str_loc, prelude.len(), con_loc);
let con_loc = Self::switch_on_con_offset_from(con_loc, prelude.len());
let lst_loc = Self::switch_on_lst_offset_from(lst_loc, prelude.len(), lst_offset);
let lst_loc = Self::switch_on_lst_offset_from(lst_loc, prelude.len());
let switch_instr =
IndexingInstruction::SwitchOnTerm(prelude.len() + 1, con_loc, lst_loc, str_loc);

View File

@@ -1,29 +1,30 @@
use prolog_parser::ast::*;
use crate::prolog_parser::ast::*;
use crate::prolog::clause_types::*;
use crate::prolog::forms::*;
use crate::prolog::machine::machine_errors::MachineStub;
use crate::prolog::machine::machine_indices::*;
use crate::clause_types::*;
use crate::forms::*;
use crate::machine::heap::*;
use crate::machine::machine_errors::MachineStub;
use crate::machine::machine_indices::*;
use crate::rug::Integer;
use crate::prolog::rug::Integer;
use indexmap::IndexMap;
use crate::indexmap::IndexMap;
use std::collections::VecDeque;
use std::rc::Rc;
fn reg_type_into_functor(r: RegType) -> MachineStub {
match r {
RegType::Temp(r) => functor!("x", 1, [heap_integer!(Integer::from(r))]),
RegType::Perm(r) => functor!("y", 1, [heap_integer!(Integer::from(r))]),
RegType::Temp(r) => functor!("x", [integer(r)]),
RegType::Perm(r) => functor!("y", [integer(r)]),
}
}
impl Level {
fn into_functor(self) -> MachineStub {
match self {
Level::Root => functor!("level", 1, [heap_atom!("root")]),
Level::Shallow => functor!("level", 1, [heap_atom!("shallow")]),
Level::Deep => functor!("level", 1, [heap_atom!("deep")]),
Level::Root => functor!("level", [atom("root")]),
Level::Shallow => functor!("level", [atom("shallow")]),
Level::Deep => functor!("level", [atom("deep")]),
}
}
}
@@ -31,15 +32,20 @@ impl Level {
impl ArithmeticTerm {
fn into_functor(&self) -> MachineStub {
match self {
&ArithmeticTerm::Reg(r) => reg_type_into_functor(r),
&ArithmeticTerm::Interm(i) => {
functor!("intermediate", 1, [heap_integer!(Integer::from(i))])
&ArithmeticTerm::Reg(r) => {
reg_type_into_functor(r)
}
&ArithmeticTerm::Interm(i) => {
functor!("intermediate", [integer(i)])
}
&ArithmeticTerm::Number(ref n) => {
vec![n.clone().into()]
}
&ArithmeticTerm::Number(ref n) => vec![heap_con!(n.clone().to_constant())],
}
}
}
#[derive(Debug)]
pub enum ChoiceInstruction {
DefaultRetryMeElse(usize),
DefaultTrustMe,
@@ -52,22 +58,25 @@ impl ChoiceInstruction {
pub fn to_functor(&self) -> MachineStub {
match self {
&ChoiceInstruction::TryMeElse(offset) => {
functor!("try_me_else", 1, [heap_integer!(Integer::from(offset))])
functor!("try_me_else", [integer(offset)])
}
&ChoiceInstruction::RetryMeElse(offset) => {
functor!("retry_me_else", 1, [heap_integer!(Integer::from(offset))])
functor!("retry_me_else", [integer(offset)])
}
&ChoiceInstruction::TrustMe => {
functor!("trust_me")
}
&ChoiceInstruction::DefaultRetryMeElse(offset) => {
functor!("default_retry_me_else", [integer(offset)])
}
&ChoiceInstruction::DefaultTrustMe => {
functor!("default_trust_me")
}
&ChoiceInstruction::TrustMe => vec![heap_atom!("trust_me")],
&ChoiceInstruction::DefaultRetryMeElse(offset) => functor!(
"default_retry_me_else",
1,
[heap_integer!(Integer::from(offset))]
),
&ChoiceInstruction::DefaultTrustMe => vec![heap_atom!("default_trust_me")],
}
}
}
#[derive(Debug)]
pub enum CutInstruction {
Cut(RegType),
GetLevel(RegType),
@@ -79,25 +88,25 @@ impl CutInstruction {
pub fn to_functor(&self, h: usize) -> MachineStub {
match self {
&CutInstruction::Cut(r) => {
let mut stub = functor!("cut", 1, [heap_str!(h + 2)]);
stub.append(&mut reg_type_into_functor(r));
stub
let rt_stub = reg_type_into_functor(r);
functor!("cut", [aux(h, 0)], [rt_stub])
}
&CutInstruction::GetLevel(r) => {
let mut stub = functor!("get_level", 1, [heap_str!(h + 2)]);
stub.append(&mut reg_type_into_functor(r));
stub
let rt_stub = reg_type_into_functor(r);
functor!("get_level", [aux(h, 0)], [rt_stub])
}
&CutInstruction::GetLevelAndUnify(r) => {
let mut stub = functor!("get_level_and_unify", 1, [heap_str!(h + 2)]);
stub.append(&mut reg_type_into_functor(r));
stub
let rt_stub = reg_type_into_functor(r);
functor!("get_level_and_unify", [aux(h, 0)], [rt_stub])
}
&CutInstruction::NeckCut => {
functor!("neck_cut")
}
&CutInstruction::NeckCut => vec![heap_atom!("neck_cut")],
}
}
}
#[derive(Debug)]
pub enum IndexedChoiceInstruction {
Retry(usize),
Trust(usize),
@@ -122,18 +131,19 @@ impl IndexedChoiceInstruction {
pub fn to_functor(&self) -> MachineStub {
match self {
&IndexedChoiceInstruction::Try(offset) => {
functor!("try", 1, [heap_integer!(Integer::from(offset))])
functor!("try", [integer(offset)])
}
&IndexedChoiceInstruction::Trust(offset) => {
functor!("trust", 1, [heap_integer!(Integer::from(offset))])
functor!("trust", [integer(offset)])
}
&IndexedChoiceInstruction::Retry(offset) => {
functor!("retry", 1, [heap_integer!(Integer::from(offset))])
functor!("retry", [integer(offset)])
}
}
}
}
#[derive(Debug)]
pub enum Line {
Arithmetic(ArithmeticInstruction),
Choice(ChoiceInstruction),
@@ -169,7 +179,7 @@ impl Line {
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum ArithmeticInstruction {
Add(ArithmeticTerm, ArithmeticTerm, usize),
Sub(ArithmeticTerm, ArithmeticTerm, usize),
@@ -220,14 +230,11 @@ fn arith_instr_unary_functor(
) -> MachineStub {
let at_stub = at.into_functor();
let mut stub = functor!(
functor!(
name,
2,
[heap_cell!(h + 4), heap_integer!(Integer::from(t))]
);
stub.extend(at_stub.into_iter());
stub
[aux(h, 0), integer(t)],
[at_stub]
)
}
fn arith_instr_bin_functor(
@@ -240,20 +247,11 @@ fn arith_instr_bin_functor(
let at_1_stub = at_1.into_functor();
let at_2_stub = at_2.into_functor();
let mut stub = functor!(
functor!(
name,
3,
[
heap_cell!(h + 4),
heap_cell!(h + 4 + at_1_stub.len()),
heap_integer!(Integer::from(t))
]
);
stub.extend(at_1_stub.into_iter());
stub.extend(at_2_stub.into_iter());
stub
[aux(h, 0), aux(h, 1), integer(t)],
[at_1_stub, at_2_stub]
)
}
impl ArithmeticInstruction {
@@ -319,17 +317,39 @@ impl ArithmeticInstruction {
&ArithmeticInstruction::Gcd(ref at_1, ref at_2, t) => {
arith_instr_bin_functor(h, "gcd", at_1, at_2, t)
}
&ArithmeticInstruction::Sign(ref at, t) => arith_instr_unary_functor(h, "sign", at, t),
&ArithmeticInstruction::Cos(ref at, t) => arith_instr_unary_functor(h, "cos", at, t),
&ArithmeticInstruction::Sin(ref at, t) => arith_instr_unary_functor(h, "sin", at, t),
&ArithmeticInstruction::Tan(ref at, t) => arith_instr_unary_functor(h, "tan", at, t),
&ArithmeticInstruction::Log(ref at, t) => arith_instr_unary_functor(h, "log", at, t),
&ArithmeticInstruction::Exp(ref at, t) => arith_instr_unary_functor(h, "exp", at, t),
&ArithmeticInstruction::ACos(ref at, t) => arith_instr_unary_functor(h, "acos", at, t),
&ArithmeticInstruction::ASin(ref at, t) => arith_instr_unary_functor(h, "asin", at, t),
&ArithmeticInstruction::ATan(ref at, t) => arith_instr_unary_functor(h, "atan", at, t),
&ArithmeticInstruction::Sqrt(ref at, t) => arith_instr_unary_functor(h, "sqrt", at, t),
&ArithmeticInstruction::Abs(ref at, t) => arith_instr_unary_functor(h, "abs", at, t),
&ArithmeticInstruction::Sign(ref at, t) => {
arith_instr_unary_functor(h, "sign", at, t)
}
&ArithmeticInstruction::Cos(ref at, t) => {
arith_instr_unary_functor(h, "cos", at, t)
}
&ArithmeticInstruction::Sin(ref at, t) => {
arith_instr_unary_functor(h, "sin", at, t)
}
&ArithmeticInstruction::Tan(ref at, t) => {
arith_instr_unary_functor(h, "tan", at, t)
}
&ArithmeticInstruction::Log(ref at, t) => {
arith_instr_unary_functor(h, "log", at, t)
}
&ArithmeticInstruction::Exp(ref at, t) => {
arith_instr_unary_functor(h, "exp", at, t)
}
&ArithmeticInstruction::ACos(ref at, t) => {
arith_instr_unary_functor(h, "acos", at, t)
}
&ArithmeticInstruction::ASin(ref at, t) => {
arith_instr_unary_functor(h, "asin", at, t)
}
&ArithmeticInstruction::ATan(ref at, t) => {
arith_instr_unary_functor(h, "atan", at, t)
}
&ArithmeticInstruction::Sqrt(ref at, t) => {
arith_instr_unary_functor(h, "sqrt", at, t)
}
&ArithmeticInstruction::Abs(ref at, t) => {
arith_instr_unary_functor(h, "abs", at, t)
}
&ArithmeticInstruction::Float(ref at, t) => {
arith_instr_unary_functor(h, "float", at, t)
}
@@ -345,8 +365,12 @@ impl ArithmeticInstruction {
&ArithmeticInstruction::Floor(ref at, t) => {
arith_instr_unary_functor(h, "floor", at, t)
}
&ArithmeticInstruction::Neg(ref at, t) => arith_instr_unary_functor(h, "-", at, t),
&ArithmeticInstruction::Plus(ref at, t) => arith_instr_unary_functor(h, "+", at, t),
&ArithmeticInstruction::Neg(ref at, t) => {
arith_instr_unary_functor(h, "-", at, t)
}
&ArithmeticInstruction::Plus(ref at, t) => {
arith_instr_unary_functor(h, "+", at, t)
}
&ArithmeticInstruction::BitwiseComplement(ref at, t) => {
arith_instr_unary_functor(h, "\\", at, t)
}
@@ -354,6 +378,7 @@ impl ArithmeticInstruction {
}
}
#[derive(Debug)]
pub enum ControlInstruction {
Allocate(usize), // num_frames.
// name, arity, perm_vars after threshold, last call, use default call policy.
@@ -378,33 +403,28 @@ impl ControlInstruction {
pub fn to_functor(&self) -> MachineStub {
match self {
&ControlInstruction::Allocate(num_frames) => {
functor!("allocate", 1, [heap_integer!(Integer::from(num_frames))])
functor!("allocate", [integer(num_frames)])
}
&ControlInstruction::CallClause(ref ct, arity, _, false, _) => {
functor!("call", [clause_name(ct.name()), integer(arity)])
}
&ControlInstruction::CallClause(ref ct, arity, _, true, _) => {
functor!("execute", [clause_name(ct.name()), integer(arity)])
}
&ControlInstruction::Deallocate => {
functor!("deallocate")
}
&ControlInstruction::CallClause(ref ct, arity, _, false, _) => functor!(
"call",
2,
[
heap_con!(Constant::Atom(ct.name(), None)),
heap_integer!(Integer::from(arity))
]
),
&ControlInstruction::CallClause(ref ct, arity, _, true, _) => functor!(
"execute",
2,
[
heap_con!(Constant::Atom(ct.name(), None)),
heap_integer!(Integer::from(arity))
]
),
&ControlInstruction::Deallocate => vec![heap_atom!("deallocate")],
&ControlInstruction::JmpBy(_, offset, ..) => {
functor!("jmp_by", 1, [heap_integer!(Integer::from(offset))])
functor!("jmp_by", [integer(offset)])
}
&ControlInstruction::Proceed => {
functor!("proceed")
}
&ControlInstruction::Proceed => vec![heap_atom!("proceed")],
}
}
}
#[derive(Debug)]
pub enum IndexingInstruction {
SwitchOnTerm(usize, usize, usize, usize),
SwitchOnConstant(usize, IndexMap<Constant, usize>),
@@ -420,34 +440,36 @@ impl From<IndexingInstruction> for Line {
impl IndexingInstruction {
pub fn to_functor(&self) -> MachineStub {
match self {
&IndexingInstruction::SwitchOnTerm(vars, constants, lists, structures) => functor!(
"switch_on_term",
4,
[
heap_integer!(Integer::from(vars)),
heap_integer!(Integer::from(constants)),
heap_integer!(Integer::from(lists)),
heap_integer!(Integer::from(structures))
]
),
&IndexingInstruction::SwitchOnConstant(constants, _) => functor!(
"switch_on_constant",
1,
[heap_integer!(Integer::from(constants))]
),
&IndexingInstruction::SwitchOnStructure(structures, _) => functor!(
"switch_on_structure",
1,
[heap_integer!(Integer::from(structures))]
),
&IndexingInstruction::SwitchOnTerm(vars, constants, lists, structures) => {
functor!(
"switch_on_term",
[integer(vars),
integer(constants),
integer(lists),
integer(structures)]
)
}
&IndexingInstruction::SwitchOnConstant(constants, _) => {
functor!(
"switch_on_constant",
[integer(constants)]
)
}
&IndexingInstruction::SwitchOnStructure(structures, _) => {
functor!(
"switch_on_structure",
[integer(structures)]
)
}
}
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum FactInstruction {
GetConstant(Level, Constant, RegType),
GetList(Level, RegType),
GetPartialString(Level, String, RegType, bool),
GetStructure(ClauseType, usize, RegType),
GetValue(RegType, usize),
GetVariable(RegType, usize),
@@ -461,96 +483,106 @@ pub enum FactInstruction {
impl FactInstruction {
pub fn to_functor(&self, h: usize) -> MachineStub {
match self {
&FactInstruction::GetConstant(lvl, ref constant, r) => {
let mut stub = functor!(
&FactInstruction::GetConstant(lvl, ref c, r) => {
let lvl_stub = lvl.into_functor();
let rt_stub = reg_type_into_functor(r);
functor!(
"get_constant",
3,
[
heap_str!(h + 4),
heap_con!(constant.clone()),
heap_str!(h + 6)
]
);
stub.append(&mut lvl.into_functor());
stub.append(&mut reg_type_into_functor(r));
stub
[aux(h, 0), constant(h, c), aux(h, 1)],
[lvl_stub, rt_stub]
)
}
&FactInstruction::GetList(lvl, r) => {
let mut stub = functor!("get_list", 2, [heap_str!(h + 3), heap_str!(h + 5)]);
stub.append(&mut lvl.into_functor());
stub.append(&mut reg_type_into_functor(r));
let lvl_stub = lvl.into_functor();
let rt_stub = reg_type_into_functor(r);
stub
functor!(
"get_list",
[aux(h, 0), aux(h, 1)],
[lvl_stub, rt_stub]
)
}
&FactInstruction::GetPartialString(lvl, ref s, r, has_tail) => {
let lvl_stub = lvl.into_functor();
let rt_stub = reg_type_into_functor(r);
functor!(
"get_partial_string",
[aux(h, 0), string(h, s), aux(h, 1), boolean(has_tail)],
[lvl_stub, rt_stub]
)
}
&FactInstruction::GetStructure(ref ct, arity, r) => {
let mut stub = functor!(
"get_structure",
3,
[
heap_con!(Constant::Atom(ct.name(), None)),
heap_integer!(Integer::from(arity)),
heap_str!(h + 4)
]
);
stub.append(&mut reg_type_into_functor(r));
let rt_stub = reg_type_into_functor(r);
stub
functor!(
"get_structure",
[clause_name(ct.name()), integer(arity), aux(h, 0)],
[rt_stub]
)
}
&FactInstruction::GetValue(r, arg) => {
let mut stub = functor!(
"get_value",
2,
[heap_str!(h + 3), heap_integer!(Integer::from(arg))]
);
stub.append(&mut reg_type_into_functor(r));
let rt_stub = reg_type_into_functor(r);
stub
functor!(
"get_value",
[aux(h, 0), integer(arg)],
[rt_stub]
)
}
&FactInstruction::GetVariable(r, arg) => {
let mut stub = functor!(
"get_variable",
2,
[heap_str!(h + 3), heap_integer!(Integer::from(arg))]
);
stub.append(&mut reg_type_into_functor(r));
let rt_stub = reg_type_into_functor(r);
stub
functor!(
"get_variable",
[aux(h, 0), integer(arg)],
[rt_stub]
)
}
&FactInstruction::UnifyConstant(ref constant) => {
functor!("unify_constant", 1, [heap_con!(constant.clone())])
&FactInstruction::UnifyConstant(ref c) => {
functor!("unify_constant", [constant(h, c)], [])
}
&FactInstruction::UnifyLocalValue(r) => {
let mut stub = functor!("unify_local_value", 1, [heap_str!(h + 2)]);
stub.append(&mut reg_type_into_functor(r));
let rt_stub = reg_type_into_functor(r);
stub
functor!(
"unify_local_value",
[aux(h, 0)],
[rt_stub]
)
}
&FactInstruction::UnifyVariable(r) => {
let mut stub = functor!("unify_variable", 1, [heap_str!(h + 2)]);
stub.append(&mut reg_type_into_functor(r));
let rt_stub = reg_type_into_functor(r);
stub
functor!(
"unify_variable",
[aux(h, 0)],
[rt_stub]
)
}
&FactInstruction::UnifyValue(r) => {
let mut stub = functor!("unify_value", 1, [heap_str!(h + 2)]);
stub.append(&mut reg_type_into_functor(r));
let rt_stub = reg_type_into_functor(r);
stub
functor!(
"unify_value",
[aux(h, 0)],
[rt_stub]
)
}
&FactInstruction::UnifyVoid(vars) => {
functor!("unify_void", 1, [heap_integer!(Integer::from(vars))])
functor!("unify_void", [integer(vars)])
}
}
}
}
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum QueryInstruction {
GetVariable(RegType, usize),
PutConstant(Level, Constant, RegType),
PutList(Level, RegType),
PutPartialString(Level, String, RegType, bool),
PutStructure(ClauseType, usize, RegType),
PutUnsafeValue(usize, usize),
PutValue(RegType, usize),
@@ -567,103 +599,106 @@ impl QueryInstruction {
match self {
&QueryInstruction::PutUnsafeValue(norm, arg) => functor!(
"put_unsafe_value",
2,
[
heap_integer!(Integer::from(norm)),
heap_integer!(Integer::from(arg))
]
[integer(norm), integer(arg)]
),
&QueryInstruction::PutConstant(lvl, ref constant, r) => {
let mut stub = functor!(
&QueryInstruction::PutConstant(lvl, ref c, r) => {
let lvl_stub = lvl.into_functor();
let rt_stub = reg_type_into_functor(r);
functor!(
"put_constant",
3,
[
heap_str!(h + 4),
heap_con!(constant.clone()),
heap_str!(h + 6)
]
);
stub.append(&mut lvl.into_functor());
stub.append(&mut reg_type_into_functor(r));
stub
[aux(h, 0), constant(h, c), aux(h, 1)],
[lvl_stub, rt_stub]
)
}
&QueryInstruction::PutList(lvl, r) => {
let mut stub = functor!("put_list", 2, [heap_str!(h + 3), heap_str!(h + 5)]);
let lvl_stub = lvl.into_functor();
let rt_stub = reg_type_into_functor(r);
stub.append(&mut lvl.into_functor());
stub.append(&mut reg_type_into_functor(r));
functor!(
"put_list",
[aux(h, 0), aux(h, 1)],
[lvl_stub, rt_stub]
)
}
&QueryInstruction::PutPartialString(lvl, ref s, r, has_tail) => {
let lvl_stub = lvl.into_functor();
let rt_stub = reg_type_into_functor(r);
stub
functor!(
"put_partial_string",
[aux(h, 0), string(h, s), aux(h, 1), boolean(has_tail)],
[lvl_stub, rt_stub]
)
}
&QueryInstruction::PutStructure(ref ct, arity, r) => {
let mut stub = functor!(
"put_structure",
3,
[
heap_con!(Constant::Atom(ct.name(), None)),
heap_integer!(Integer::from(arity)),
heap_str!(h + 4)
]
);
let rt_stub = reg_type_into_functor(r);
stub.append(&mut reg_type_into_functor(r));
stub
functor!(
"put_structure",
[clause_name(ct.name()), integer(arity), aux(h, 0)],
[rt_stub]
)
}
&QueryInstruction::PutValue(r, arg) => {
let mut stub = functor!(
"put_value",
2,
[heap_str!(h + 3), heap_integer!(Integer::from(arg))]
);
let rt_stub = reg_type_into_functor(r);
stub.append(&mut reg_type_into_functor(r));
stub
functor!(
"put_value",
[aux(h, 0), integer(arg)],
[rt_stub]
)
}
&QueryInstruction::GetVariable(r, arg) => {
let mut stub = functor!(
"get_variable",
2,
[heap_str!(h + 3), heap_integer!(Integer::from(arg))]
);
let rt_stub = reg_type_into_functor(r);
stub.append(&mut reg_type_into_functor(r));
stub
functor!(
"get_variable",
[aux(h, 0), integer(arg)],
[rt_stub]
)
}
&QueryInstruction::PutVariable(r, arg) => {
let mut stub = functor!(
"put_variable",
2,
[heap_str!(h + 3), heap_integer!(Integer::from(arg))]
);
let rt_stub = reg_type_into_functor(r);
stub.append(&mut reg_type_into_functor(r));
stub
functor!(
"put_variable",
[aux(h, 0), integer(arg)],
[rt_stub]
)
}
&QueryInstruction::SetConstant(ref constant) => {
functor!("set_constant", 1, [heap_con!(constant.clone())])
&QueryInstruction::SetConstant(ref c) => {
functor!("set_constant", [constant(h, c)], [])
}
&QueryInstruction::SetLocalValue(r) => {
let mut stub = functor!("set_local_value", 1, [heap_str!(h + 2)]);
let rt_stub = reg_type_into_functor(r);
stub.append(&mut reg_type_into_functor(r));
stub
functor!(
"set_local_value",
[aux(h, 0)],
[rt_stub]
)
}
&QueryInstruction::SetVariable(r) => {
let mut stub = functor!("set_variable", 1, [heap_str!(h + 2)]);
let rt_stub = reg_type_into_functor(r);
stub.append(&mut reg_type_into_functor(r));
stub
functor!(
"set_variable",
[aux(h, 0)],
[rt_stub]
)
}
&QueryInstruction::SetValue(r) => {
let mut stub = functor!("set_value", 1, [heap_str!(h + 2)]);
let rt_stub = reg_type_into_functor(r);
stub.append(&mut reg_type_into_functor(r));
stub
functor!(
"set_value",
[aux(h, 0)],
[rt_stub]
)
}
&QueryInstruction::SetVoid(vars) => {
functor!("set_void", 1, [heap_integer!(Integer::from(vars))])
functor!("set_void", [integer(vars)])
}
}
}

View File

@@ -1,21 +1,23 @@
use prolog_parser::ast::*;
use crate::prolog_parser::ast::*;
use crate::prolog::clause_types::*;
use crate::prolog::forms::*;
use crate::prolog::machine::machine_indices::*;
use crate::clause_types::*;
use crate::forms::*;
use crate::machine::machine_indices::*;
use std::cell::Cell;
use std::collections::VecDeque;
use std::fmt;
use std::iter::*;
use std::rc::Rc;
use std::vec::Vec;
#[derive(Clone)]
#[derive(Debug, Clone)]
pub enum TermRef<'a> {
AnonVar(Level),
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
Constant(Level, &'a Cell<RegType>, &'a Constant),
Clause(Level, &'a Cell<RegType>, ClauseType, &'a Vec<Box<Term>>),
PartialString(Level, &'a Cell<RegType>, String, Option<&'a Term>),
Var(Level, &'a Cell<VarReg>, Rc<Var>),
}
@@ -27,10 +29,12 @@ impl<'a> TermRef<'a> {
| TermRef::Constant(lvl, ..)
| TermRef::Var(lvl, ..)
| TermRef::Clause(lvl, ..) => lvl,
| TermRef::PartialString(lvl, ..) => lvl,
}
}
}
#[derive(Debug)]
pub enum TermIterState<'a> {
AnonVar(Level),
Constant(Level, &'a Cell<RegType>, &'a Constant),
@@ -43,13 +47,67 @@ pub enum TermIterState<'a> {
),
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
FinalCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
PartialString(Level, &'a Cell<RegType>, String, Option<&'a Term>),
Var(Level, &'a Cell<VarReg>, Rc<Var>),
}
fn is_partial_string<'a>(
head: &'a Term,
mut tail: &'a Term,
) -> Option<(String, Option<&'a Term>)>
{
let mut string =
match head {
&Term::Constant(_, Constant::Atom(ref atom, _)) if atom.is_char() => {
atom.as_str().chars().next().unwrap().to_string()
}
&Term::Constant(_, Constant::Char(c)) => {
c.to_string()
}
_ => {
return None;
}
};
while let Term::Cons(_, ref head, ref succ) = tail {
match head.as_ref() {
&Term::Constant(_, Constant::Atom(ref atom, _)) if atom.is_char() => {
string.push(atom.as_str().chars().next().unwrap());
}
&Term::Constant(_, Constant::Char(c)) => {
string.push(c);
}
_ => {
return None;
}
};
tail = succ.as_ref();
}
match tail {
Term::AnonVar | Term::Var(..) => {
return Some((string, Some(tail)));
}
Term::Constant(_, Constant::EmptyList) => {
return Some((string, None));
}
Term::Constant(_, Constant::String(tail)) => {
string += &tail;
return Some((string, None));
}
_ => {
return None;
}
}
}
impl<'a> TermIterState<'a> {
pub fn subterm_to_state(lvl: Level, term: &'a Term) -> TermIterState<'a> {
match term {
&Term::AnonVar => TermIterState::AnonVar(lvl),
&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())
@@ -62,12 +120,17 @@ impl<'a> TermIterState<'a> {
&Term::Cons(ref cell, ref head, ref 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::Constant(ref cell, ref constant) => {
TermIterState::Constant(lvl, cell, constant)
}
&Term::Var(ref cell, ref var) => {
TermIterState::Var(lvl, cell, var.clone())
}
}
}
}
#[derive(Debug)]
pub struct QueryIterator<'a> {
state_stack: Vec<TermIterState<'a>>,
}
@@ -128,7 +191,7 @@ impl<'a> QueryIterator<'a> {
QueryIterator {
state_stack: vec![state],
}
}
}
&QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
let state = TermIterState::Clause(Level::Root, 0, cell, ct.clone(), terms);
QueryIterator {
@@ -169,7 +232,9 @@ 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 {
@@ -182,7 +247,9 @@ impl<'a> Iterator for QueryIterator<'a> {
lvl => Some(TermRef::Clause(lvl, cell, ct, child_terms)),
}
}
_ => return None,
_ => {
return None;
}
};
} else {
self.state_stack.push(TermIterState::Clause(
@@ -192,23 +259,41 @@ impl<'a> Iterator for QueryIterator<'a> {
ct,
child_terms,
));
self.push_subterm(lvl.child_level(), child_terms[child_num].as_ref());
}
}
TermIterState::InitialCons(lvl, cell, head, tail) => {
self.state_stack
.push(TermIterState::FinalCons(lvl, cell, head, tail));
if let Some((string, tail)) = is_partial_string(head, tail) {
self.state_stack.push(TermIterState::PartialString(
lvl,
cell,
string,
tail,
));
self.push_subterm(lvl.child_level(), tail);
self.push_subterm(lvl.child_level(), head);
if let Some(tail) = tail {
self.push_subterm(lvl.child_level(), tail);
}
} else {
self.state_stack.push(TermIterState::FinalCons(lvl, cell, head, tail));
self.push_subterm(lvl.child_level(), tail);
self.push_subterm(lvl.child_level(), head);
}
}
TermIterState::PartialString(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))
return Some(TermRef::Constant(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,6 +301,7 @@ impl<'a> Iterator for QueryIterator<'a> {
}
}
#[derive(Debug)]
pub struct FactIterator<'a> {
state_queue: VecDeque<TermIterState<'a>>,
iterable_root: bool,
@@ -223,8 +309,7 @@ pub struct FactIterator<'a> {
impl<'a> FactIterator<'a> {
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
self.state_queue
.push_back(TermIterState::subterm_to_state(lvl, term));
self.state_queue.push_back(TermIterState::subterm_to_state(lvl, term));
}
pub fn from_rule_head_clause(terms: &'a Vec<Box<Term>>) -> Self {
@@ -241,7 +326,9 @@ 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::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());
vec![TermIterState::Clause(Level::Root, 0, cell, ct, terms)]
@@ -273,7 +360,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);
@@ -285,16 +374,27 @@ impl<'a> Iterator for FactIterator<'a> {
};
}
TermIterState::InitialCons(lvl, cell, head, tail) => {
self.push_subterm(Level::Deep, head);
self.push_subterm(Level::Deep, tail);
if let Some((string, tail)) = is_partial_string(head, tail) {
if let Some(tail) = tail {
self.push_subterm(Level::Deep, tail);
}
return Some(TermRef::Cons(lvl, cell, head, tail));
return Some(TermRef::PartialString(lvl, cell, string, tail));
} else {
self.push_subterm(Level::Deep, head);
self.push_subterm(Level::Deep, tail);
return Some(TermRef::Cons(lvl, cell, head, tail));
}
}
TermIterState::Constant(lvl, cell, constant) => {
return Some(TermRef::Constant(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));
}
_ => {
}
}
}
@@ -310,6 +410,7 @@ pub fn breadth_first_iter(term: &Term, iterable_root: bool) -> FactIterator {
FactIterator::new(term, iterable_root)
}
#[derive(Debug)]
pub enum ChunkedTerm<'a> {
HeadClause(ClauseName, &'a Vec<Box<Term>>),
BodyTerm(&'a QueryTerm),
@@ -347,6 +448,18 @@ pub struct ChunkedIterator<'a> {
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>);

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

@@ -0,0 +1,123 @@
:- module(arithmetic, [expmod/4, lsb/2, msb/2, number_to_rational/2,
number_to_rational/3,
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
),
!.
number(X) :-
( integer(X)
; float(X)
; rational(X)
).
stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, C/D, Fraction) :-
Fn1 is A + C,
Fd1 is B + D,
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).

View File

@@ -63,12 +63,12 @@ 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, ?, ?).
*/
%! empty_assoc(?Assoc) is semidet.
%
% Is true if Assoc is the empty association list.

View File

@@ -1,9 +1,10 @@
:- module(atts, [op(1199, fx, attribute), call_residue_vars/2,
'$absent_attr'/2, '$copy_attr_list'/2, '$get_attr'/2,
'$put_attr'/2, '$absent_from_list'/2,
'$get_from_list'/3, '$add_to_list'/3, '$del_attr'/3,
'$del_attr_step'/3, '$del_attr_buried'/4,
'$default_attr_list'/4]).
term_attributed_variables/2,
'$absent_attr'/2, '$copy_attr_list'/2, '$get_attr'/2,
'$put_attr'/2, '$absent_from_list'/2,
'$get_from_list'/3, '$add_to_list'/3, '$del_attr'/3,
'$del_attr_step'/3, '$del_attr_buried'/4,
'$default_attr_list'/4]).
:- use_module(library(dcgs)).
:- use_module(library(terms)).
@@ -56,7 +57,8 @@
'$del_attr'(Ls0, V, Attr) :-
Ls0 = [Att | Ls1],
nonvar(Att),
( Att \= Attr -> '$del_attr_buried'(Ls0, Ls1, V, Attr)
( Att \= Attr ->
'$del_attr_buried'(Ls0, Ls1, V, Attr)
; '$enqueue_attr_var'(V),
'$del_attr_head'(V),
'$del_attr'(Ls1, V, Attr)
@@ -132,11 +134,11 @@ put_attr(Name, Arity) -->
'$put_attr'(V, Attr)),
(put_atts(V, Attr) :- !, functor(Attr, Head, Arity),
functor(AttrForm, Head, Arity),
'$get_attr_list'(V, Ls),
'$del_attr'(Ls, V, AttrForm),
'$get_attr_list'(V, Ls),
'$del_attr'(Ls, V, AttrForm),
'$put_attr'(V, Attr)),
(put_atts(V, -Attr) :- !, functor(Attr, _, _),
'$get_attr_list'(V, Ls),
'$get_attr_list'(V, Ls),
'$del_attr'(Ls, V, Attr))].
get_attr(Name, Arity) -->
@@ -158,4 +160,6 @@ 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

@@ -6,14 +6,13 @@
:- use_module(library(error)).
between(Lower, Upper, X) :-
( Upper == inf ->
must_be(integer, Lower),
can_be(integer, X),
enumerate_nats(Lower, X)
; must_be(integer, Lower),
must_be(integer, Upper),
can_be(integer, X),
between_(Lower, Upper, X)
must_be(integer, Lower),
must_be(integer, Upper),
can_be(integer, X),
( nonvar(X) ->
Lower =< X,
X =< Upper
; between_(Lower, Upper, X)
).
between_(Lower, Upper, Lower) :-

View File

@@ -24,6 +24,8 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :-
:- op(400, yfx, [div, //, rdiv, <<, >>, mod, rem]).
:- op(200, fy, [+, -, \]).
:- op(1200, xfx, -->).
% arithmetic comparison operators.
:- op(700, xfx, [>, <, =\=, =:=, >=, =<]).
@@ -41,24 +43,35 @@ user:term_expansion((:- op(Pred, Spec, [Op | OtherOps])), OpResults) :-
:- module(builtins, [(=)/2, (\=)/2, (\+)/1, (',')/2, (->)/2, (;)/2,
(=..)/2, (:)/2, (:)/3, (:)/4, (:)/5, (:)/6,
(:)/7, (:)/8, (:)/9, (:)/10, (:)/11, (:)/12,
abolish/1, asserta/1, assertz/1, atom_chars/2,
atom_codes/2, atom_concat/3, atom_length/2,
bagof/3, catch/3, char_code/2, clause/2,
current_op/3, current_predicate/1,
current_prolog_flag/2, expand_goal/2,
expand_term/2, fail/0, false/0, findall/3,
findall/4, get_char/1, halt/0, max_arity/1,
number_chars/2, number_codes/2, once/1, op/3,
read_term/2, repeat/0, retract/1,
set_prolog_flag/2, setof/3, sub_atom/5,
abolish/1, asserta/1, assertz/1,
at_end_of_stream/0, at_end_of_stream/1,
atom_chars/2, atom_codes/2, atom_concat/3,
atom_length/2, bagof/3, catch/3, char_code/2,
clause/2, close/1, close/2, current_input/1,
current_output/1, current_op/3,
current_predicate/1, current_prolog_flag/2,
expand_goal/2, expand_term/2, fail/0, false/0,
findall/3, findall/4, flush_output/0,
flush_output/1, get_byte/1, get_byte/2,
get_char/1, get_char/2, get_code/1, get_code/2,
halt/0, halt/1, max_arity/1, number_chars/2,
number_codes/2, once/1, op/3, open/3, open/4,
peek_byte/1, peek_byte/2, peek_char/1,
peek_char/2, peek_code/1, peek_code/2,
put_byte/1, put_byte/2, put_code/1, put_code/2,
put_char/1, put_char/2, read_term/2, read_term/3,
repeat/0, retract/1, set_prolog_flag/2,
set_input/1, set_stream_position/2, set_output/1,
setof/3, stream_property/2, sub_atom/5,
subsumes_term/2, term_variables/2, throw/1,
true/0, unify_with_occurs_check/2, write/1,
write_canonical/1, write_term/2, writeq/1]).
write_canonical/1, write_term/2, write_term/3,
writeq/1]).
% the maximum arity flag. needs to be replaced with
% current_prolog_flag(max_arity, MAX_ARITY).
max_arity(255).
max_arity(1023).
% unify.
X = X.
@@ -212,18 +225,41 @@ comma_errors(G1, G2, B) :- '$call_with_default_policy'(','(G1, G2, B)).
;(G1, G2) :- '$get_b_value'(B), ;(G1, G2, B).
:- non_counted_backtracking semicolon_compound_selector/3.
semicolon_compound_selector(->(G2, G3), G4, B) :-
( call(G2) ->
call(G3)
; '$set_cp'(B),
call(G4)
).
semicolon_compound_selector(','(G2, G3), G4, B) :-
( ','(G2, G3, B)
; '$set_cp'(B),
call(G4)
).
semicolon_compound_selector(';'(G2, G3), G4, B) :-
( ';'(G2, G3, B)
; '$set_cp'(B),
call(G4)
).
:- non_counted_backtracking (;)/3.
;(G1, G4, B) :- compound(G1),
'$call_with_default_policy'(G1 = ->(G2, G3)),
!,
( call(G2) -> call(G3)
; '$set_cp'(B),
call(G4)
).
;(G1, G2, B) :- G1 == !, '$set_cp'(B), call(G2).
;(G1, G2, B) :- G2 == !, call(G1), '$set_cp'(B).
;(G, _, _) :- call(G).
;(_, G, _) :- call(G).
;(G1, G4, B) :-
( ( G1 = (_ -> _)
; G1 = (_ , _)
; G1 = (_ ; _)
) ->
!,
semicolon_compound_selector(G1, G4, B)
).
;(G1, G2, B) :-
G1 == !, !, '$set_cp'(B), call(G2).
;(G1, G2, B) :-
G2 == !, !, call(G1), '$set_cp'(B).
;(G, _, _) :-
call(G).
;(_, G, _) :-
call(G).
G1 -> G2 :- '$get_b_value'(B), '$call_with_default_policy'(->(G1, G2, B)).
@@ -261,8 +297,11 @@ univ_errors(Term, List, N) :-
Term =.. List :- '$call_with_default_policy'(univ_errors(Term, List, N)),
'$call_with_default_policy'(univ_worker(Term, List, N)).
:- non_counted_backtracking univ_worker/3.
univ_worker(Term, List, _) :- atomic(Term), !, '$call_with_default_policy'(List = [Term]).
univ_worker(Term, List, _) :-
atomic(Term), !, '$call_with_default_policy'(List = [Term]).
univ_worker(Term, [Name|Args], N) :-
var(Term), !,
'$call_with_default_policy'(Arity is N-1),
@@ -273,7 +312,9 @@ univ_worker(Term, List, _) :-
'$call_with_default_policy'(get_args(Args, Term, 1, Arity)),
'$call_with_default_policy'(List = [Name|Args]).
:- non_counted_backtracking get_args/4.
get_args(Args, _, _, 0) :-
!, '$call_with_default_policy'(Args = []).
get_args([Arg], Func, N, N) :-
@@ -283,34 +324,39 @@ get_args([Arg|Args], Func, I0, N) :-
'$call_with_default_policy'(I1 is I0 + 1),
'$call_with_default_policy'(get_args(Args, Func, I1, N)).
% write, write_canonical, writeq, write_term.
is_write_option(Functor) :-
Functor =.. [Name, Arg],
( Arg == true -> true
; Arg == false -> true
; Name == variable_names -> must_be_var_names_list(Arg)
; var(Arg) -> throw(error(instantiation_error, write_term/2))
; throw(error(domain_error(write_option, Functor), write_term/2))
), % 8.14.2.3 e)
( Name == ignore_ops -> true
; Name == quoted -> true
; Name == numbervars -> true
; Name == variable_names -> true
; throw(error(domain_error(write_option, Functor), write_term/2))
). % 8.14.2.3 e)
parse_write_options(Options, OptionValues, Stub) :-
DefaultOptions = [ignore_ops-false, max_depth-0, numbervars-false,
quoted-false, variable_names-[]],
parse_options_list(Options, parse_write_options_, DefaultOptions, OptionValues, Stub).
inst_member_or([X|Xs], Y, Z) :-
( var(X) -> throw(error(instantiation_error, write_term/2))
; is_write_option(X) -> ( Y = X, ! ; inst_member_or(Xs, Y, Z) )
; throw(error(domain_error(write_option, X), write_term/2))
parse_write_options_(ignore_ops(IgnoreOps), ignore_ops-IgnoreOps) :-
( nonvar(IgnoreOps), lists:member(IgnoreOps, [true, false])
;
throw(error(domain_error(write_option, ignore_ops(IgnoreOps)), _))
).
parse_write_options_(quoted(Quoted), quoted-Quoted) :-
( nonvar(Quoted), lists:member(Quoted, [true, false])
;
throw(error(domain_error(write_option, quoted(Quoted)), _))
).
parse_write_options_(numbervars(NumberVars), numbervars-NumberVars) :-
( nonvar(NumberVars), lists:member(NumberVars, [true, false])
;
throw(error(domain_error(write_option, numbervars(NumberVars)), _))
).
parse_write_options_(variable_names(VNNames), variable_names-VNNames) :-
must_be_var_names_list(VNNames).
parse_write_options_(max_depth(MaxDepth), max_depth-MaxDepth) :-
( integer(MaxDepth), MaxDepth >= 0
;
throw(error(domain_error(write_option, max_depth(MaxDepth)), _))
).
inst_member_or([], Y, Y).
must_be_var_names_list(VarNames) :-
'$skip_max_list'(_, -1, VarNames, Tail),
( Tail == [] -> must_be_var_names_list_(VarNames, VarNames)
; var(Tail) -> throw(error(instantiation_error, write_term/2))
; throw(error(domain_error(write_options, variable_names(VarNames)), write_term/2))
; throw(error(domain_error(write_option, variable_names(VarNames)), write_term/2))
).
must_be_var_names_list_([], List).
@@ -319,44 +365,90 @@ must_be_var_names_list_([VarName | VarNames], List) :-
( VarName = (Atom = _) ->
( atom(Atom) -> must_be_var_names_list_(VarNames, List)
; var(Atom) -> throw(error(instantiation_error, write_term/2))
; throw(error(domain_error(write_options, variable_names(List)), write_term/2))
; throw(error(domain_error(write_option, variable_names(List)), write_term/2))
)
; throw(error(domain_error(write_options, variable_names(List)), write_term/2))
; throw(error(domain_error(write_option, variable_names(List)), write_term/2))
)
; throw(error(instantiation_error, write_term/2)) % throw(error(domain_error(write_options, variable_names(List)), write_term/2))
; throw(error(instantiation_error, write_term/2))
).
write_term(_, Options) :-
var(Options), throw(error(instantiation_error, write_term/2)).
write_term(Term, Options) :-
'$skip_max_list'(_, -1, Options, Options0),
( var(Options0) -> throw(error(instantiation_error, write_term/2))
; Options0 == [] -> true
; throw(error(type_error(list, Options), write_term/2))
), % 8.14.2.3 c)
inst_member_or(Options, ignore_ops(IgnoreOps), ignore_ops(false)),
inst_member_or(Options, numbervars(NumberVars), numbervars(false)),
inst_member_or(Options, quoted(Quoted), quoted(false)),
inst_member_or(Options, variable_names(VarNames), variable_names([])),
'$write_term'(Term, IgnoreOps, NumberVars, Quoted, VarNames).
current_output(Stream),
write_term(Stream, Term, Options).
write(Term) :- write_term(Term, [numbervars(true)]).
write_term(Stream, Term, Options) :-
parse_write_options(Options, [IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames], write_term/3),
'$write_term'(Stream, Term, IgnoreOps, NumberVars, Quoted, VNNames, MaxDepth).
write_canonical(Term) :- write_term(Term, [ignore_ops(true), quoted(true)]).
writeq(Term) :- write_term(Term, [quoted(true), numbervars(true)]).
write(Term) :-
current_output(Stream),
'$write_term'(Stream, Term, false, true, false, [], 0).
%% TODO: complete the predicate! Most read options are missing.
read_term(Term, Options) :-
'$skip_max_list'(_, -1, Options, Options0),
( Options0 == [] -> true
; var(Options0) -> throw(error(instantiation_error, read_term/2)) % 8.14.1.3 b)
; throw(error(type_error(list, Options), read_term/2)) % 8.14.1.3 d)
),
( Options = [variable_names(VarList)] -> '$read_term'(Term, VarList)
; Options = [] -> read(Term)
; false
write_canonical(Term) :-
current_output(Stream),
'$write_term'(Stream, Term, true, false, true, [], 0).
writeq(Term) :-
current_output(Stream),
'$write_term'(Stream, Term, false, true, true, [], 0).
select_rightmost_options([Option-Value | OptionPairs], OptionValues) :-
( pairs:same_key(Option, OptionPairs, OtherValues, _),
OtherValues == [] ->
OptionValues = [Value | OptionValues0],
select_rightmost_options(OptionPairs, OptionValues0)
;
select_rightmost_options(OptionPairs, OptionValues)
).
select_rightmost_options([], []).
parse_options_list(Options, Selector, DefaultPairs, OptionValues, Stub) :-
'$skip_max_list'(_, -1, Options, Tail),
( Tail == [] ->
true
; var(Tail) ->
throw(error(instantiation_error, Stub)) % 8.11.5.3c)
; Tail \== [] ->
throw(error(type_error(list, Options), Stub)) % 8.11.5.3e)
),
( lists:maplist(nonvar, Options),
catch(lists:maplist(Selector, Options, OptionPairs0),
error(E, _),
throw(error(E, Stub))) ->
lists:append(DefaultPairs, OptionPairs0, OptionPairs1),
keysort(OptionPairs1, OptionPairs),
select_rightmost_options(OptionPairs, OptionValues)
;
throw(error(instantiation_error, Stub)) % 8.11.5.3c)
).
parse_read_term_options(Options, OptionValues, Stub) :-
DefaultOptions = [singletons-_, variables-_, variable_names-_],
parse_options_list(Options, parse_read_term_options_, DefaultOptions, OptionValues, Stub).
parse_read_term_options_(singletons(Vars), singletons-Vars).
parse_read_term_options_(variables(Vars), variables-Vars).
parse_read_term_options_(variable_names(Vars), variable_names-Vars).
parse_read_term_options_(E,_) :-
throw(error(domain_error(read_option, E), _)).
read_term(Stream, Term, Options) :-
parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term/3),
'$read_term'(Stream, Term, Singletons, Variables, VariableNames).
read_term(Term, Options) :-
current_input(Stream),
read_term(Stream, Term, Options).
% expand_goal.
@@ -448,7 +540,7 @@ set_difference([], _, []) :- !.
set_difference(Xs, [], Xs).
group_by_variant([V2-S2 | Pairs], V1-S1, [S2 | Solutions], Pairs0) :-
non_iso:variant(V1, V2), !, V1 = V2, group_by_variant(Pairs, V2-S2, Solutions, Pairs0).
iso_ext:variant(V1, V2), !, V1 = V2, group_by_variant(Pairs, V2-S2, Solutions, Pairs0).
group_by_variant(Pairs, _, [], Pairs).
group_by_variants([V-S|Pairs], [V-Solution|Solutions]) :-
@@ -456,7 +548,10 @@ group_by_variants([V-S|Pairs], [V-Solution|Solutions]) :-
group_by_variants(Pairs0, Solutions).
group_by_variants([], []).
iterate_variants([V-Solution|GroupSolutions], V, Solution).
iterate_variants([V-Solution|GroupSolutions], V, Solution) :-
( GroupSolutions == [] -> !
; true
).
iterate_variants([_|GroupSolutions], Ws, Solution) :-
iterate_variants(GroupSolutions, Ws, Solution).
@@ -472,7 +567,9 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses)
( nonvar(Goal), Goal = _ ^ _ ->
rightmost_power(Goal, Goal1, ExistentialVars0),
term_variables(ExistentialVars0, ExistentialVars),
set_difference(Witnesses0, ExistentialVars, Witnesses),
sort(Witnesses0, Witnesses1),
sort(ExistentialVars, ExistentialVars1),
set_difference(Witnesses1, ExistentialVars1, Witnesses),
findall(Witnesses-Template, Goal1, PairedSolutions)
; Witnesses = Witnesses0,
findall(Witnesses-Template, Goal, PairedSolutions)
@@ -491,7 +588,10 @@ bagof(Template, Goal, Solution) :-
iterate_variants(GroupedSolutions, Witnesses, Solution).
iterate_variants_and_sort([V-Solution0|GroupSolutions], V, Solution) :-
sort(Solution0, Solution).
sort(Solution0, Solution),
( GroupSolutions == [] -> !
; true
).
iterate_variants_and_sort([_|GroupSolutions], Ws, Solution) :-
iterate_variants_and_sort(GroupSolutions, Ws, Solution).
@@ -531,18 +631,19 @@ setof(Template, Goal, Solution) :-
clause(H, B) :-
( var(H) -> throw(error(instantiation_error, clause/2))
; functor(H, Name, Arity) -> ( Name == '.' -> throw(error(type_error(callable, H), clause/2))
; Name == (:), Arity =:= 2 ->
arg(1, H, Module),
arg(2, H, F),
'$module_clause'(F, B, Module)
%% '$no_such_predicate' fails if H is not callable.
; '$no_such_predicate'(H) -> '$fail'
; '$head_is_dynamic'(H) -> '$clause_body_is_valid'(B),
'$get_clause'(H, B)
; throw(error(permission_error(access, private_procedure, Name/Arity),
clause/2))
)
; functor(H, Name, Arity) ->
( Name == '.' -> throw(error(type_error(callable, H), clause/2))
; Name == (:), Arity =:= 2 ->
arg(1, H, Module),
arg(2, H, F),
'$module_clause'(F, B, Module)
%% '$no_such_predicate' fails if H is not callable.
; '$no_such_predicate'(H) -> '$fail'
; '$head_is_dynamic'(H) -> '$clause_body_is_valid'(B),
'$get_clause'(H, B)
; throw(error(permission_error(access, private_procedure, Name/Arity),
clause/2))
)
; throw(error(type_error(callable, H), clause/2))
).
@@ -813,7 +914,14 @@ op(Priority, OpSpec, Op) :-
; throw(error(type_error(list, Op), op/3)) % 8.14.3.3 f)
).
halt :- '$halt'.
halt :- halt(0).
halt(N) :-
( -2^31 =< N, N =< 2^31 - 1 ->
'$halt'(N)
; throw(error(domain_error(exit_code, N), halt/1))
).
atom_length(Atom, Length) :-
( var(Atom) -> throw(error(instantiation_error, atom_length/2)) % 8.16.1.3 a)
@@ -833,10 +941,10 @@ atom_chars(Atom, List) :-
),
( var(Atom) ->
( var(Tail) -> throw(error(instantiation_error, atom_chars/2))
; ground(List), Tail == [] -> '$atom_chars'(Atom, List)
; ground(List) -> '$atom_chars'(Atom, List)
; throw(error(instantiation_error, atom_chars/2))
)
; atom(Atom) -> can_be_chars_or_vars(List, atom_chars/2), '$atom_chars'(Atom, List)
; atom(Atom) -> '$atom_chars'(Atom, List)
; throw(error(type_error(atom, Atom), atom_chars/2))
).
@@ -850,7 +958,7 @@ atom_codes(Atom, List) :-
; ground(List), Tail == [] -> '$atom_codes'(Atom, List)
; throw(error(instantiation_error, atom_codes/2))
)
; atom(Atom) -> can_be_codes_or_vars(List, atom_codes/2), '$atom_codes'(Atom, List)
; atom(Atom) -> '$atom_codes'(Atom, List)
; throw(error(type_error(atom, Atom), atom_codes/2))
).
@@ -907,11 +1015,11 @@ char_code(Char, Code) :-
).
get_char(C) :-
( var(C) -> '$get_char'(C)
; C == end_of_file -> '$get_char'(C)
; atom_length(C, 1) -> '$get_char'(C)
; throw(error(type_error(in_character, C), get_char/1))
).
current_input(S),
'$get_char'(S, C).
get_char(S, C) :-
'$get_char'(S, C).
can_be_number(N, PI) :-
( var(N) -> true
@@ -928,45 +1036,6 @@ must_be_number(N, PI) :-
; throw(error(instantiation_error, PI))
).
can_be_chars_or_vars(Cs, _) :- var(Cs), !.
can_be_chars_or_vars(Cs, PI) :-
( string(Cs) ->
current_prolog_flag(double_quotes, chars)
; chars_or_vars(Cs, PI)
).
chars_or_vars([], _).
chars_or_vars([C|Cs], PI) :-
( nonvar(C) ->
( catch(atom_length(C, 1), _, false) ->
( nonvar(Cs) -> chars_or_vars(Cs, PI)
; false
)
; throw(error(type_error(character, C), PI))
)
; chars_or_vars(Cs, PI)
).
can_be_codes_or_vars(Cs, _) :- var(Cs), !.
can_be_codes_or_vars(Cs, PI) :-
( string(Cs) ->
current_prolog_flag(double_quotes, codes)
; codes_or_vars(Cs, PI)
).
codes_or_vars([], _).
codes_or_vars([C|Cs], PI) :-
( nonvar(C) ->
( catch(char_code(_, C), _, false) ->
( nonvar(Cs) -> codes_or_vars(Cs, PI)
; false
)
; integer(C) -> throw(error(representation_error(character_code), PI))
; throw(error(type_error(integer, C), PI))
)
; codes_or_vars(Cs, PI)
).
number_chars(N, Chs) :-
( ground(Chs)
-> can_be_number(N, number_chars/2),
@@ -1006,3 +1075,226 @@ subsumes_term(General, Specific) :-
).
unify_with_occurs_check(X, Y) :- '$unify_with_occurs_check'(X, Y).
current_input(S) :- '$current_input'(S).
current_output(S) :- '$current_output'(S).
set_input(S) :-
( var(S) ->
throw(error(instantiation_error, set_input/1))
; '$set_input'(S)
).
set_output(S) :-
( var(S) ->
throw(error(instantiation_error, set_output/1))
; '$set_output'(S)
).
parse_stream_options(Options, OptionValues, Stub) :-
DefaultOptions = [alias-[], eof_action-eof_code, reposition-false, type-text],
parse_options_list(Options, parse_stream_options_, DefaultOptions, OptionValues, Stub).
parse_stream_options_(type(Type), type-Type) :-
( nonvar(Type), lists:member(Type, [text, binary]), !, true
;
throw(error(domain_error(stream_option, type(Type)), _))
).
parse_stream_options_(reposition(Bool), reposition-Bool) :-
( nonvar(Bool), lists:member(Bool, [true, false]), !, true
;
throw(error(domain_error(stream_option, reposition(Bool)), _))
).
parse_stream_options_(alias(A), alias-A) :-
( atom(A), A \== [], !, true
;
throw(error(domain_error(stream_option, alias(A)), _))
).
parse_stream_options_(eof_action(Action), eof_action-Action) :-
( nonvar(Action), lists:member(Action, [eof_code, error, reset]), !, true
;
throw(error(domain_error(stream_option, eof_action(Action)), _))
).
parse_stream_options_(E, _) :-
throw(error(domain_error(stream_option, E), _)). % 8.11.5.3i)
open(SourceSink, Mode, Stream) :-
open(SourceSink, Mode, Stream, []).
open(SourceSink, Mode, Stream, StreamOptions) :-
( var(SourceSink) ->
throw(error(instantiation_error, open/4)) % 8.11.5.3a)
; var(Mode) ->
throw(error(instantiation_error, open/4)) % 8.11.5.3b)
; \+ atom(Mode) ->
throw(error(type_error(atom, Mode), open/4)) % 8.11.5.3d)
; nonvar(Stream) ->
throw(error(type_error(variable, Stream), open/4)) % 8.11.5.3f)
;
parse_stream_options(StreamOptions, [Alias, EOFAction, Reposition, Type], open/4),
'$open'(SourceSink, Mode, Stream, Alias, EOFAction, Reposition, Type)
).
parse_close_options(Options, OptionValues, Stub) :-
DefaultOptions = [force-false],
parse_options_list(Options, parse_close_options_, DefaultOptions, OptionValues, Stub).
parse_close_options_(force(Force), force-Force) :-
( nonvar(Force), lists:member(Force, [true, false]), !
;
throw(error(domain_error(close_option, force(Force)), _))
).
parse_close_options_(E, _) :-
throw(error(domain_error(close_option, E), _)).
close(Stream, CloseOptions) :-
parse_close_options(CloseOptions, [Force], close/2),
'$close'(Stream, CloseOptions).
close(Stream) :-
'$close'(Stream, []).
flush_output(S) :-
'$flush_output'(S).
flush_output :-
current_output(S),
'$flush_output'(S).
get_byte(S, B) :-
'$get_byte'(S, B).
get_byte(B) :-
current_input(S),
'$get_byte'(S, B).
put_char(C) :-
current_output(S),
'$put_char'(S, C).
put_char(S, C) :-
'$put_char'(S, C).
put_byte(C) :-
current_output(S),
'$put_byte'(S, C).
put_byte(S, C) :-
'$put_byte'(S, C).
put_code(C) :-
current_output(S),
'$put_code'(S, C).
put_code(S, C) :-
'$put_code'(S, C).
get_code(C) :-
current_input(S),
'$get_code'(S, C).
get_code(S, C) :-
'$get_code'(S, C).
peek_byte(S, B) :-
'$peek_byte'(S, B).
peek_byte(B) :-
current_input(S),
'$peek_byte'(S, B).
peek_code(C) :-
current_input(S),
'$peek_code'(S, C).
peek_code(S, C) :-
'$peek_code'(S, C).
peek_char(C) :-
current_input(S),
'$peek_char'(S, C).
peek_char(S, C) :-
'$peek_char'(S, C).
check_stream_property(file_name(F), file_name, F) :-
( var(F) -> true ; atom(F) ).
check_stream_property(mode(M), mode, M) :-
( var(M) -> true ; lists:member(M, [read, write, append]) ).
check_stream_property(D, direction, D) :-
( var(D) -> true ; lists:member(D, [input, output, input_output]), ! ).
check_stream_property(alias(A), alias, A) :-
( var(A) -> true ; atom(A) ).
check_stream_property(position(P), position, P) :-
( var(P) -> true ; integer(P), P >= 0 ).
check_stream_property(end_of_stream(E), end_of_stream, E) :-
( var(E) -> true ; lists:member(E, [not, at, past]) ).
check_stream_property(eof_action(A), eof_action, A) :-
( var(A) -> true ; lists:member(A, [error, eof_code, reset]) ).
check_stream_property(reposition(B), reposition, B) :-
( var(B) -> true ; lists:member(B, [true, false]) ).
check_stream_property(type(T), type, T) :-
( var(T) -> true ; lists:member(T, [text, binary]) ).
stream_iter_(S, S).
stream_iter_(S, S1) :-
'$next_stream'(S, S0),
stream_iter_(S0, S1).
stream_iter(S) :-
( nonvar(S) ->
true
; '$first_stream'(S0),
stream_iter_(S0, S)
).
stream_property(S, P) :-
( nonvar(P), \+ check_stream_property(P, _, _) ->
throw(error(domain_error(stream_property, P), stream_property/2))
; stream_iter(S),
check_stream_property(P, PropertyName, PropertyValue),
'$stream_property'(S, PropertyName, PropertyValue)
).
at_end_of_stream(S_or_a) :-
( atom(S_or_a) ->
stream_property(S, alias(S_or_a))
; S = S_or_a
),
stream_property(S, end_of_stream(E)),
( E = at -> true ; E = past ).
at_end_of_stream :-
current_input(S),
stream_property(S, end_of_stream(E)),
!,
( E = at ; E = past ).
set_stream_position(S_or_a, Position) :-
( var(Position) ->
throw(error(instantiation_error, set_stream_position/2))
; integer(Position), Position >= 0 ->
true
; throw(error(domain_error(stream_position, Position)))
),
'$set_stream_position'(S_or_a, Position).

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

@@ -0,0 +1,242 @@
:- module(charsio, [char_type/2,
chars_utf8bytes/2,
get_single_char/1,
read_line_to_chars/3,
read_term_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)).
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_([], _, VarList, [], _).
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) :-
( var(Char) -> instantiation_error(char_type/2)
; atom_length(Char, 1) ->
( ground(Type) ->
( ctype(Type) ->
'$char_type'(Char, Type)
; domain_error(char_type, Type, char_type/2)
)
; ctype(Type),
'$char_type'(Char, Type)
)
; type_error(in_character, Char, char_type/2)
).
ctype(alnum).
ctype(alpha).
ctype(alphabetic).
ctype(ascii).
ctype(ascii_graphic).
ctype(ascii_punctuation).
ctype(binary_digit).
ctype(control).
ctype(decimal_digit).
ctype(exponent).
ctype(graphic).
ctype(hexadecimal_digit).
ctype(layout).
ctype(lower).
ctype(meta).
ctype(numeric).
ctype(octal_digit).
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_term_from_chars(Chars, Term) :-
( var(Chars) ->
instantiation_error(read_term_from_chars/2)
; nonvar(Term) ->
throw(error(uninstantiation_error(Term), read_term_from_chars/2))
; '$skip_max_list'(_, -1, Chars, Chars0),
Chars0 == [],
partial_string(Chars) ->
true
;
type_error(complete_string, Chars, read_term_from_chars/2)
),
'$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)
)
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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="
; false.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
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(list, Bs),
maplist(must_be(character), Bs),
'$chars_base64'(Cs, Bs, Padding, Charset)
; must_be(list, Cs),
maplist(must_be(character), Cs),
'$chars_base64'(Cs, Bs, Padding, Charset)
).

View File

@@ -30,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(error), []).
:- use_module(library(error), [domain_error/3, type_error/3]).
:- attribute
clpb/1,
@@ -96,75 +98,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) :-
foldl(sum_, Ls, 0, S).
sum_(L, S0, S) :- S is S0 + L.
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 +669,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),
@@ -1157,7 +1094,7 @@ labeling_var(V) :- V == 1, !.
labeling_var(V) :- domain_error(clpb_variable, V).
variables_in_index_order(Vs0, Vs) :-
maplist(var_with_index, Vs0, IVs0),
maplist(var_with_index, Vs0, IVs0),
keysort(IVs0, IVs),
pairs_values(IVs, Vs).
@@ -1184,7 +1121,7 @@ indomain(1).
% Examples:
%
% ==
% ?- sat(A =< B), Vs = [A,B], sat_count(+[1|Vs], Count).
% ?- sat(A =< B), Vs = [A,B], sat_count(+[1|Vs], Count).
% Vs = [A, B],
% Count = 3,
% sat(A=:=A*B).
@@ -1506,7 +1443,7 @@ max_variable_node(Node, V0-N0, V-N) :-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
ands_fusion(Ands0, Ands) :-
maplist(with_variables, Ands0, Pairs0),
maplist(with_variables, Ands0, Pairs0),
keysort(Pairs0, Pairs),
group_pairs_by_key(Pairs, Groups),
pairs_values(Groups, Andss),

7622
src/lib/clpz.pl Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
:- module(cont, [reset/3, shift/1]).
reset(Goal, Ball, Cont) :-
@@ -7,14 +6,14 @@ reset(Goal, Ball, Cont) :-
'$bind_from_register'(Cont, 3),
'$bind_from_register'(Ball, 4).
shift(Term) :-
shift(Ball) :-
'$nextEP'(first, E, P),
get_chunks(E, P, L),
( L == [] ->
Cont = none
Cont = cont(true)
; Cont = cont(call_continuation(L))
),
'$write_cont_and_term'(_, _, Cont, Term),
'$write_cont_and_term'(_, _, Cont, Ball),
'$unwind_environments'.
get_chunks(E, P, L) :-

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

@@ -0,0 +1,826 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written May 2020 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)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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]
; false.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
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(list, Chars),
( member(Char, Chars),
char_code(Char, Code),
\+ between(0, 255, Code) ->
domain_error(byte_char, Char, 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"
; false.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 is 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.
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
),
must_be_bytes(Key, crypto_data_encrypt/6),
must_be_bytes(IV, crypto_data_encrypt/6),
must_be(atom, Algorithm),
( Algorithm = 'chacha20-poly1305' -> true
; domain_error('chacha20-poly1305', Algorithm, crypto_data_encrypt/6)
),
'$crypto_data_encrypt'(PlainText, Encoding, Key, IV, Tag, CipherText).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
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),
must_be(atom, Encoding),
member(Encoding, [utf8,octet]),
must_be(list, CipherText0),
encoding_chars(octet, CipherText0, CipherText1),
maplist(char_code, TagChars, Tag),
append(CipherText1, TagChars, CipherText),
( Algorithm = 'chacha20-poly1305' -> true
; domain_error('chacha20-poly1305', Algorithm, crypto_data_decrypt/6)
),
'$crypto_data_decrypt'(CipherText, octet, 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(list, Cs),
maplist(must_be(character), 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, octet, PublicKey).
ed25519_sign(Key, Data0, Signature, Options) :-
must_be_byte_chars(Key, ed25519_sign),
options_data_chars(Options, Data0, Data, Encoding),
'$ed25519_sign'(Key, octet, 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, octet, 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) ->
Scalar #>= 0,
Scalar #< 2^256,
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, []).

View File

@@ -1,14 +1,11 @@
% :- op(1105, xfy, ('|')).
:- module(dcgs, [op(1200, xfx, -->), phrase/2, phrase/3]).
:- module(dcgs,
[op(1105, xfy, '|'),
phrase/2,
phrase/3]).
:- use_module(library(error)).
:- use_module(library(lists), [append/3]).
user:term_expansion(Term0, Term) :-
nonvar(Term0),
dcg_rule(Term0, (Head :- Body)),
Term = (Head :- Body).
phrase(GRBody, S0) :-
phrase(GRBody, S0, []).
@@ -31,8 +28,8 @@ phrase_((A -> B ; C), S0, S) :-
).
phrase_((A ; B), S0, S) :-
( phrase(A, S0, S) ; phrase(B, S0, S) ).
%% phrase_((A | B), S0, S) :-
%% ( phrase(A, S0, S) ; phrase(B, S0, S) ).
phrase_((A | B), S0, S) :-
( phrase(A, S0, S) ; phrase(B, S0, S) ).
phrase_({G}, S0, S) :-
( call(G), S0 = S ).
phrase_(call(G), S0, S) :-
@@ -95,7 +92,7 @@ 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.6 - alternative
dcg_constr({_}). % 7.14.7
dcg_constr(call(_)). % 7.14.8
dcg_constr(phrase(_)). % 7.14.9
@@ -107,6 +104,7 @@ dcg_constr((_->_)). % 7.14.12 - if-then (existence implementation dep.)
% the construct to be expanded.
dcg_cbody([], S0, S, S0 = S).
dcg_cbody([T|Ts], S0, S, Goal) :-
must_be(list, [T|Ts]),
dcg_terminals([T|Ts], S0, S, Goal).
dcg_cbody(( GRFirst, GRSecond ), S0, S, ( First, Second )) :-
dcg_body(GRFirst, S0, S1, First),
@@ -119,9 +117,9 @@ dcg_cbody(( GRCond ; GRElse ), S0, S, ( Cond ; Else )) :-
subsumes_term(( _GRIf -> _GRThen ), GRCond),
dcg_cbody(GRCond, S0, S, Cond),
dcg_body(GRElse, S0, S, Else).
%% dcg_cbody(( GREither '|' GROr ), S0, S, ( Either ; Or )) :-
%% dcg_body(GREither, S0, S, Either),
%% dcg_body(GROr, S0, S, Or).
dcg_cbody(( GREither '|' GROr ), S0, S, ( Either ; Or )) :-
dcg_body(GREither, S0, S, Either),
dcg_body(GROr, S0, S, Or).
dcg_cbody({Goal}, S0, S, ( Goal, S0 = S )).
dcg_cbody(call(Cont), S0, S, call(Cont, S0, S)).
dcg_cbody(phrase(Body), S0, S, phrase(Body, S0, S)).
@@ -130,3 +128,8 @@ dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )).
dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :-
dcg_body(GRIf, S0, S1, If),
dcg_body(GRThen, S1, S, Then).
user:term_expansion(Term0, Term) :-
nonvar(Term0),
dcg_rule(Term0, (Head :- Body)),
Term = (Head :- Body).

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

@@ -0,0 +1,22 @@
% 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]).
$-(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).
*(_).

View File

@@ -1,5 +1,9 @@
:- module(error, [must_be/2,
can_be/2]).
can_be/2,
instantiation_error/1,
domain_error/3,
type_error/3
]).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written September 2018 by Markus Triska (triska@metalevel.at)
@@ -23,7 +27,8 @@
- integer
- atom
- list.
- list
- boolean
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
must_be(Type, Term) :-
@@ -32,31 +37,41 @@ must_be(Type, Term) :-
must_be_(Type, _) :-
var(Type),
instantiation_error(Type).
instantiation_error(must_be/2).
must_be_(var, Term) :-
( var(Term) -> true
; throw(error(uninstantiation_error, _))
; throw(error(uninstantiation_error, must_be/2))
).
must_be_(integer, Term) :- check_(integer, integer, Term).
must_be_(atom, Term) :- check_(atom, atom, Term).
must_be_(character, T) :- check_(character, character, T).
must_be_(list, Term) :- check_(ilist, list, Term).
must_be_(type, Term) :- check_(type, type, Term).
must_be_(boolean, Term) :- check_(boolean, boolean, Term).
check_(Pred, Type, Term) :-
( var(Term) -> instantiation_error(Term)
( var(Term) -> instantiation_error(must_be/2)
; call(Pred, Term) -> true
; type_error(Type, Term)
; type_error(Type, Term, must_be/2)
).
ilist(V) :- var(V), instantiation_error(V).
boolean(B) :- ( B == true ; B == false ).
character(C) :-
atom(C),
atom_length(C, 1).
ilist(V) :- var(V), instantiation_error(must_be/2).
ilist([]).
ilist([_|Ls]) :- ilist(Ls).
type(type).
type(integer).
type(atom).
type(character).
type(list).
type(var).
type(boolean).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
can_be(Type, Term)
@@ -76,12 +91,14 @@ can_be(Type, Term) :-
must_be(type, Type),
( var(Term) -> true
; can_(Type, Term) -> true
; type_error(Type, Term)
; type_error(Type, Term, can_be/2)
).
can_(integer, Term) :- integer(Term).
can_(atom, Term) :- atom(Term).
can_(character, T) :- character(T).
can_(list, Term) :- list_or_partial_list(Term).
can_(boolean, Term) :- boolean(Term).
list_or_partial_list(Var) :- var(Var).
list_or_partial_list([]).
@@ -92,11 +109,11 @@ list_or_partial_list([_|Ls]) :-
Shorthands for throwing ISO errors.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
instantiation_error(_Term) :-
throw(error(instantiation_error, _)).
instantiation_error(Context) :-
throw(error(instantiation_error, Context)).
domain_error(Type, Term) :-
throw(error(domain_error(Type, Term), _)).
domain_error(Type, Term, Context) :-
throw(error(domain_error(Type, Term), Context)).
type_error(Type, Term) :-
throw(error(type_error(Type, Term), _)).
type_error(Type, Term, Context) :-
throw(error(type_error(Type, Term), Context)).

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

@@ -0,0 +1,200 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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,
make_directory/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) :-
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).
delete_file(File) :-
list_of_chars(File),
'$delete_file'(File).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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_time'(File, Which, T0),
read_term_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"]
; false.
?- path_segments(Path, ["hello","there"]).
Path = "hello/there"
; false.
To obtain the platform-specific directory separator, you can use:
?- path_segments(Separator, ["",""]).
Separator = "/"
; false.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
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]
).

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

@@ -0,0 +1,580 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written March 2020 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)
~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.
~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(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 = "hello\n......there!"
%@ ; false.
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/2,
listing/1
]).
:- use_module(library(dcgs)).
:- use_module(library(lists)).
:- use_module(library(error)).
:- use_module(library(charsio)).
:- use_module(library(between)).
format_(Fs, Args) -->
{ must_be(list, Fs),
must_be(list, Args),
phrase(cells(Fs,Args,0,[]), 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)) --> list(Cs).
format_element(glue(Fill,Num)) -->
{ length(Ls, Num),
maplist(=(Fill), Ls) },
list(Ls).
list([]) --> [].
list([L|Ls]) --> [L], list(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 from_to(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 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) --> !,
cells(Fs, Args, Tab, [chars("~")|Es]).
cells([~,w|Fs], [Arg|Args], Tab, Es) --> !,
{ write_term_to_chars(Arg, [], Chars) },
cells(Fs, Args, Tab, [chars(Chars)|Es]).
cells([~,q|Fs], [Arg|Args], Tab, Es) --> !,
{ write_term_to_chars(Arg, [quoted(true)], Chars) },
cells(Fs, Args, Tab, [chars(Chars)|Es]).
cells([~,a|Fs], [Arg|Args], Tab, Es) --> !,
{ atom_chars(Arg, Chars) },
cells(Fs, Args, Tab, [chars(Chars)|Es]).
cells([~|Fs0], Args0, Tab, Es) -->
{ 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.",list(Zs),list(Cs0)), Cs)
; BeforeComma is L - Num,
length(Bs, BeforeComma),
append(Bs, Ds, Cs0),
phrase((list(Bs),".",list(Ds)), Cs)
) }
),
cells(Fs, Args, Tab, [chars(Cs)|Es]).
cells([~|Fs0], Args0, Tab, Es) -->
{ numeric_argument(Fs0, Num, ['D'|Fs], Args0, [Arg|Args]) },
!,
{ number_chars(Num, NCs),
phrase(("~",list(NCs),"d"), FStr),
phrase(format_(FStr, [Arg]), Cs0),
phrase(upto_what(Bs0, .), Cs0, Ds),
reverse(Bs0, Bs1),
phrase(groups_of_three(Bs1), Bs2),
reverse(Bs2, Bs),
append(Bs, Ds, Cs) },
cells(Fs, Args, Tab, [chars(Cs)|Es]).
cells([~,i|Fs], [_|Args], Tab, Es) --> !,
cells(Fs, Args, Tab, Es).
cells([~,n|Fs], Args, Tab, Es) --> !,
cell(Tab, Tab, Es),
n_newlines(1),
cells(Fs, Args, 0, []).
cells([~|Fs0], Args0, Tab, Es) -->
{ numeric_argument(Fs0, Num, [n|Fs], Args0, Args) },
!,
cell(Tab, Tab, Es),
n_newlines(Num),
cells(Fs, Args, 0, []).
cells([~,s|Fs], [Arg|Args], Tab, Es) --> !,
cells(Fs, Args, Tab, [chars(Arg)|Es]).
cells([~,f|Fs], [Arg|Args], Tab, Es) --> !,
{ format_number_chars(Arg, Chars) },
cells(Fs, Args, Tab, [chars(Chars)|Es]).
cells([~|Fs0], Args0, Tab, Es) -->
{ 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]).
cells([~|Fs0], Args0, Tab, Es) -->
{ numeric_argument(Fs0, Num, [r|Fs], Args0, [Arg|Args]) },
!,
{ integer_to_radix(Arg, Num, lowercase, Cs) },
cells(Fs, Args, Tab, [chars(Cs)|Es]).
cells([~|Fs0], Args0, Tab, Es) -->
{ numeric_argument(Fs0, Num, ['R'|Fs], Args0, [Arg|Args]) },
!,
{ integer_to_radix(Arg, Num, uppercase, Cs) },
cells(Fs, Args, Tab, [chars(Cs)|Es]).
cells([~,'`',Char,t|Fs], Args, Tab, Es) --> !,
cells(Fs, Args, Tab, [glue(Char,_)|Es]).
cells([~,t|Fs], Args, Tab, Es) --> !,
cells(Fs, Args, Tab, [glue(' ',_)|Es]).
cells([~|Fs0], Args0, Tab, Es) -->
{ numeric_argument(Fs0, Num, ['|'|Fs], Args0, Args) },
!,
cell(Tab, Num, Es),
cells(Fs, Args, Num, []).
cells([~|Fs0], Args0, Tab0, Es) -->
{ numeric_argument(Fs0, Num, [+|Fs], Args0, Args) },
!,
{ Tab is Tab0 + Num },
cell(Tab0, Tab, Es),
cells(Fs, Args, Tab, []).
cells([~,C|_], _, _, _) -->
{ atom_chars(A, [~,C]),
domain_error(format_string, A, format_//2) }.
cells(Fs0, Args, Tab, Es) -->
{ phrase(upto_what(Fs1, ~), Fs0, Fs),
Fs1 = [_|_] },
cells(Fs, Args, Tab, [chars(Fs1)|Es]).
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 = [].
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
upto_what([], W), [W] --> [W], !.
upto_what([C|Cs], W) --> [C], !, upto_what(Cs, W).
upto_what([], _) --> [].
groups_of_three([A,B,C,D|Rs]) --> !, [A,B,C], ",", groups_of_three([D|Rs]).
groups_of_three(Ls) --> list(Ls).
cell(From, To, Es0) -->
( { Es0 == [] } -> []
; { reverse(Es0, Es) },
[cell(From,To,Es)]
).
%?- numeric_argument("2f", Num, ['f'|Fs], Args0, Args).
%?- numeric_argument("100b", Num, Rs, Args0, Args).
numeric_argument(Ds, Num, Rest, Args0, Args) :-
( Ds = [*|Rest] ->
Args0 = [Num|Args]
; numeric_argument_(Ds, [], Ns, Rest),
foldl(pow10, Ns, 0-0, Num-_),
Args0 = Args
).
numeric_argument_([D|Ds], Ns0, Ns, Rest) :-
( member(D, "0123456789") ->
number_chars(N, [D]),
numeric_argument_(Ds, [N|Ns0], Ns, Rest)
; Ns = Ns0,
Rest = [D|Ds]
).
pow10(D, N0-Pow0, N-Pow) :-
N is N0 + D*10^Pow0,
Pow is Pow0 + 1.
integer_to_radix(I0, R, Which, Cs) :-
I is I0, % evaluate compound expression
must_be(integer, I),
must_be(integer, R),
( \+ between(2, 36, R) ->
domain_error(radix, R, format_//2)
; 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(format_(Fs, Args), Cs),
% we use a specialised internal predicate that uses only a
% single "write" operation for efficiency. It is equivalent to
% maplist(put_char(Stream), Cs). It also works for binary streams.
'$put_chars'(Stream, Cs),
flush_output(Stream).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- phrase(cells("hello", [], 0, []), Cs).
?- phrase(cells("hello~10|", [], 0, []), Cs).
?- phrase(cells("~ta~t~10|", [], 0, []), Cs).
?- phrase(format_("~`at~50|", []), Ls).
?- phrase(cells("~`at~50|", [], 0, []), Cs),
phrase(format_cells(Cs), Ls).
?- phrase(cells("~ta~t~tb~tc~21|", [], 0, []), Cs).
Cs = [cell(0,21,[glue(' ',_38),chars([a]),glue(' ',_62),glue(' ',_67),chars([b]),glue(' ',_91),chars([c])])].
?- phrase(cells("~ta~t~4|", [], 0, []), Cs).
Cs = [cell(0,4,[glue(' ',_38),chars([a]),glue(' ',_62)])].
?- phrase(format_cell(cell(0,1,[glue(a,_94)])), Ls).
?- phrase(format_cell(cell(0,50,[chars("hello")])), Ls).
?- phrase(format_("~`at~50|~n", []), Ls).
?- phrase(format_("hello~n~tthere~6|", []), Ls).
?- format("~ta~t~4|", []).
a true
; false.
?- format("~ta~tb~tc~10|", []).
a b c true
; false.
?- format("~tabc~3|", []).
?- format("~ta~t~4|", []).
?- format("~ta~t~tb~tc~20|", []).
a b c true
; false.
?- format("~2f~n", [3]).
3.00
true
?- format("~20f", [0.1]).
0.10000000000000000000 true % this should use higher accuracy!
; false.
?- 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(portray_clause_(Term), Ls),
format(Stream, "~s", [Ls]).
portray_clause_(Term) -->
{ term_variables(Term, Vs),
foldl(var_name, Vs, VNs, 0, _) },
portray_(Term, VNs), ".\n".
var_name(V, Name=V, Num0, Num) :-
charsio:fabricate_var_name(numbervars, Name, Num0),
Num is Num0 + 1.
literal(Lit, VNs) -->
{ write_term_to_chars(Lit, [quoted(true),variable_names(VNs)], Ls) },
list(Ls).
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_((A ; Else), C, I, VNs) --> % ( If -> Then ; Else )
{ nonvar(A), A = (If -> Then) },
!,
indent_to(C, I),
"( ",
{ C1 is I + 3 },
body_(If, C1, C1, VNs), " ->\n",
body_(Then, 0, C1, VNs), "\n",
else_branch(Else, C1, I, VNs).
body_((A;B), C, I, VNs) --> !,
indent_to(C, I),
"( ",
{ C1 is I + 3 },
body_(A, C1, C1, VNs), "\n",
else_branch(B, C1, I, VNs).
body_(Goal, C, I, VNs) -->
indent_to(C, I), literal(Goal, VNs).
else_branch(Else, C, I, VNs) -->
indent_to(0, I),
"; ",
body_(Else, C, C, VNs), "\n",
indent_to(0, I),
")".
indent_to(CurrentColumn, Indent) -->
{ Delta is Indent - CurrentColumn },
format_("~t~*|", [Delta]).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- 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,7 +3,7 @@
:- use_module(library(error)).
:- use_module(library(lists)).
:- use_module(library(non_iso)).
:- use_module(library(iso_ext)).
:- use_module(library(si)).
gensym_key(Base, BaseKey) :-

84
src/lib/http/http_open.pl Normal file
View File

@@ -0,0 +1,84 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written June 2020 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
http_open(+Address, -Stream, +Options)
======================================
Yields Stream to read the body of an HTTP reply from Address.
Address is a list of characters, and includes the method. Both HTTP
and HTTPS are supported. Redirects are followed.
Currently, Options must be the empty list. Options may be
added in the future to give more control over the connection.
We use HTTP/1.0 until we can read chunked transfer-encoding.
Example:
?- http_open("https://github.com/mthom/scryer-prolog", S, []).
%@ S = '$stream'(0x7f86f94a6cd0)
%@ ; false.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(http_open, [http_open/3]).
:- use_module(library(sockets)).
:- use_module(library(error)).
:- use_module(library(format)).
:- use_module(library(charsio)).
:- use_module(library(dcgs)).
:- use_module(library(lists), [member/2]).
http_open(Address, Stream, Options) :-
must_be(list, Options),
must_be(list, Address),
once(phrase((list(SchemeCs), "://", list(Rest)), Address)),
atom_chars(Scheme, SchemeCs),
chars_host_url(Rest, Host, URL),
connect(Scheme, Host, Stream0),
format(Stream0, "\
GET ~s HTTP/1.0\r\n\
Host: ~w\r\n\
User-Agent: Scryer Prolog\r\n\
Connection: close\r\n\r\n\
", [URL,Host]),
read_line_to_chars(Stream0, StatusLine, []),
once(phrase(("HTTP/1.",(['0']|['1'])," ",[D1]), StatusLine, _)),
read_header_lines(Stream0, HeaderLines),
handle_response(D1, HeaderLines, Stream0, Stream).
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
handle_response('2', _, Stream, Stream). % ok
handle_response('3', HeaderLines, Stream0, Stream) :- % redirect
close(Stream0),
once((member(Line, HeaderLines),
phrase(("Location: ",list(Location),"\r\n"), Line))),
http_open(Location, Stream, []).
% Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
read_header_lines(Stream, Hs) :-
read_line_to_chars(Stream, Cs, []),
( Cs == "" -> Hs = []
; Cs == "\r\n" -> Hs = []
; Hs = [Cs|Rest],
read_header_lines(Stream, Rest)
).
chars_host_url(Cs, Host, [/|Us]) :-
( phrase((list(Hs),"/",list(Us)), Cs) ->
true
; Hs = Cs,
Us = []
),
atom_chars(Host, Hs).
connect(https, Host, Stream) :-
socket_client_open(Host:443, Stream, [tls(true)]).
connect(http, Host, Stream) :-
socket_client_open(Host:80, Stream, []).

View File

@@ -1,13 +1,13 @@
%% for builtins that are not part of the ISO standard.
%% must be loaded at the REPL with
%% ?- use_module(library(non_iso)).
%% ?- use_module(library(iso_ext)).
:- module(non_iso, [bb_b_put/2, bb_get/2, bb_put/2, call_cleanup/2,
call_with_inference_limit/3, forall/2, maybe/0,
partial_string/1, partial_string/3,
partial_string_tail/2, set_random/1,
setup_call_cleanup/3, variant/2]).
:- module(iso_ext, [bb_b_put/2, bb_get/2, bb_put/2, call_cleanup/2,
call_with_inference_limit/3, forall/2,
partial_string/1, partial_string/3,
partial_string_tail/2, setup_call_cleanup/3,
variant/2]).
forall(Generate, Test) :-
\+ (Generate, \+ Test).
@@ -22,9 +22,9 @@ bb_put(Key, _) :- throw(error(type_error(atom, Key), bb_put/2)).
bb_b_put(Key, NewValue) :-
( '$bb_get_with_offset'(Key, OldValue, OldOffset) ->
call_cleanup((store_global_var_with_offset(Key, NewValue) ; false),
reset_global_var_at_offset(Key, OldValue, OldOffset))
reset_global_var_at_offset(Key, OldValue, OldOffset))
; call_cleanup((store_global_var_with_offset(Key, NewValue) ; false),
reset_global_var_at_key(Key))
reset_global_var_at_key(Key))
).
store_global_var_with_offset(Key, Value) :- '$store_global_var_with_offset'(Key, Value).
@@ -68,7 +68,8 @@ scc_helper(C, G, Bb) :-
; '$reset_block'(NBb),
'$fail').
scc_helper(_, _, Bb) :-
'$reset_block'(Bb), '$get_ball'(Ball),
'$reset_block'(Bb),
'$get_ball'(Ball),
'$call_with_default_policy'(run_cleaners_with_handling),
'$erase_ball',
'$call_with_default_policy'(throw(Ball)).
@@ -143,27 +144,14 @@ call_with_inference_limit(_, _, R, Bb, B) :-
variant(X, Y) :- '$variant'(X, Y).
% succeeds with probability 0.5.
maybe :- '$maybe'.
set_random(Seed) :-
( nonvar(Seed) ->
( Seed = seed(S) ->
( var(S) -> throw(error(instantiation_error, set_random/1))
; integer(S) -> '$set_seed'(S)
; throw(error(type_error(integer(S), set_random/1)))
)
)
; throw(error(instantiation_error, set_random/1))
).
partial_string(String, L, L0) :-
( String == [] -> throw(error(type_error(list, []), partial_string/3))
( String == [] ->
L = L0
; catch(atom_chars(Atom, String),
error(E, _),
throw(error(E, partial_string/3)))
),
'$create_partial_string'(Atom, L, L0).
error(E, _),
throw(error(E, partial_string/3))),
'$create_partial_string'(Atom, L, L0)
).
partial_string(String) :-
'$is_partial_string'(String).

View File

@@ -1,8 +1,8 @@
:- module(lists, [member/2, select/3, append/3, foldl/4, foldl/5,
:- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5,
memberchk/2, reverse/2, length/2, maplist/2,
maplist/3, maplist/4, maplist/5, maplist/6,
maplist/7, maplist/8, maplist/9, same_length/2,
sumlist/2]).
maplist/7, maplist/8, maplist/9, same_length/2, nth0/3,
sum_list/2, transpose/2, list_to_set/2]).
:- use_module(library(error)).
@@ -21,9 +21,9 @@ length(Xs, N) :-
; var(Xs0) -> R is N-M, length_rundown(Xs0, R)).
length(_, N) :-
integer(N), !,
throw(error(domain_error(not_less_than_zero, N), length/2)).
domain_error(not_less_than_zero, N, length/2).
length(_, N) :-
throw(error(type_error(integer, N), length/2)).
type_error(integer, N, length/2).
length_addendum([], N, N).
length_addendum([_|Xs], N, M) :-
@@ -44,6 +44,12 @@ select(X, [X|Xs], Xs).
select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys).
append([], []).
append([L0|Ls0], Ls) :-
append(L0, Rest, Ls),
append(Ls0, Rest).
append([], R, R).
append([X|L], R, [X|S]) :- append(L, R, S).
@@ -57,7 +63,7 @@ reverse(Xs, Ys) :-
).
reverse([], [], YsRev, YsRev).
reverse([X1|Xs], [Y1|Ys], YsPreludeRev, Xss) :-
reverse([_|Xs], [Y1|Ys], YsPreludeRev, Xss) :-
reverse(Xs, Ys, [Y1|YsPreludeRev], Xss).
@@ -98,18 +104,14 @@ maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7
maplist(_, [], [], [], [], [], [], [], []).
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s], [E8|E8s]) :-
call(Cont, E1, E2, E3, E4, E5, E6, E7),
call(Cont, E1, E2, E3, E4, E5, E6, E7, E8),
maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s, E7s, E8s).
sumlist_([], S, S).
sumlist_([N|Ns], S, S0) :-
S1 is S0 + N,
sumlist_(Ns, S, S1).
sum_list(Ls, S) :-
foldl(sum_, Ls, 0, S).
sumlist(Ns, S) :-
must_be(list, Ns),
sumlist_(Ns, S, 0).
sum_(L, S0, S) :- S is S0 + L.
@@ -134,3 +136,67 @@ foldl_([], [], _, A, A).
foldl_([X|Xs], [Y|Ys], G_4, A0, A) :-
call(G_4, X, Y, A0, A1),
foldl_(Xs, Ys, G_4, A1, A).
transpose(Ls, Ts) :-
lists_transpose(Ls, Ts).
lists_transpose([], []).
lists_transpose([L|Ls], Ts) :-
maplist(same_length(L), Ls),
foldl(transpose_, L, Ts, [L|Ls], _).
transpose_(_, Fs, Lists0, Lists) :-
maplist(list_first_rest, Lists0, Fs, Lists).
list_first_rest([L|Ls], L, Ls).
list_to_set(Ls0, Ls) :-
maplist(with_var, Ls0, LVs0),
keysort(LVs0, LVs),
same_elements(LVs),
pick_firsts(LVs0, Ls).
pick_firsts([], []).
pick_firsts([E-V|EVs], Fs0) :-
( V == visited ->
Fs0 = Fs
; V = visited,
Fs0 = [E|Fs]
),
pick_firsts(EVs, Fs).
with_var(E, E-_).
same_elements([]).
same_elements([EV|EVs]) :-
foldl(unify_same, EVs, EV, _).
unify_same(E-V, Prev-Var, E-V) :-
( Prev == E ->
Var = V
; true
).
nth0(N, Es, E) :-
can_be(integer, N),
can_be(list, Es),
( integer(N) ->
nth0_index(N, Es, E)
; nth0_search(N, Es, E)
).
nth0_index(0, [E|_], E) :- !.
nth0_index(N, [_|Es], E) :-
N > 0,
N1 is N - 1,
nth0_index(N1, Es, E).
nth0_search(N, Es, E) :-
nth0_search(0, N, Es, E).
nth0_search(N, N, [E|_], E).
nth0_search(N0, N, [_|Es], E) :-
N1 is N0 + 1,
nth0_search(N1, N, Es, E).

58
src/lib/os.pl Normal file
View File

@@ -0,0 +1,58 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Predicates for reasoning about the operating system (OS) environment.
Written July 2020 by Markus Triska (triska@metalevel.at).
Lists of characters are used throughout to represent keys and values.
Example:
?- getenv("LANG", Ls).
Ls = "en_US.UTF-8"
; false.
Public domain code.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(os, [getenv/2,
setenv/2,
unsetenv/1]).
:- use_module(library(error)).
:- use_module(library(charsio)).
:- use_module(library(lists)).
getenv(Key, Value) :-
must_be_env_var(Key),
'$getenv'(Key, Value).
setenv(Key, Value) :-
must_be_env_var(Key),
must_be_chars(Value),
'$setenv'(Key, Value).
unsetenv(Key) :-
must_be_env_var(Key),
'$unsetenv'(Key).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
For now, we only support a restricted subset of variable names.
The reason is that Rust may panic if a key is empty, contains an
ASCII equals sign '=' or the NUL character '\0', or when the value
contains the NUL character.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
must_be_env_var(Cs) :-
must_be_chars(Cs),
Cs = [_|_],
( maplist(permitted, Cs) -> true
; domain_error(env_var, Cs, os)
).
permitted(C) :- char_type(C, alnum).
permitted(C) :- char_type(C, ascii_punctuation).
permitted('_').
must_be_chars(Cs) :-
must_be(list, Cs),
maplist(must_be(character), Cs).

33
src/lib/pairs.pl Normal file
View File

@@ -0,0 +1,33 @@
:- module(pairs, [pairs_keys_values/3,
pairs_keys/2,
pairs_values/2,
group_pairs_by_key/2,
map_list_to_pairs/3]).
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).
group_pairs_by_key([], []).
group_pairs_by_key([K-V|KVs0], [K-[V|Vs]|KVs]) :-
same_key(K, KVs0, Vs, KVs1),
group_pairs_by_key(KVs1, KVs).
same_key(K0, [K1-V|KVs0], [V|Vs], KVs) :-
K0 == K1, !,
same_key(K0, KVs0, Vs, KVs).
same_key(_, KVs, [], KVs).

42
src/lib/pio.pl Normal file
View File

@@ -0,0 +1,42 @@
:- module(pio, [phrase_from_file/2,
phrase_from_file/3]).
:- use_module(library(dcgs)).
:- use_module(library(error)).
:- use_module(library(freeze)).
:- use_module(library(iso_ext), [setup_call_cleanup/3, partial_string/3]).
:- use_module(library(lists), [member/2]).
phrase_from_file(NT, File) :-
phrase_from_file(NT, File, []).
phrase_from_file(NT, File, Options) :-
( var(File) -> instantiation_error(phrase_from_file/3)
; (\+ atom(File) ; File = []) ->
domain_error(source_sink, File, phrase_from_file/3)
; must_be(list, Options),
( member(Var, Options), var(Var) -> instantiation_error(phrase_from_file/3)
; member(type(Type), Options) ->
must_be(atom, Type),
member(Type, [text,binary])
; Type = text
),
setup_call_cleanup(open(File, read, Stream, [reposition(true)|Options]),
( stream_to_lazy_list(Stream, Xs),
phrase(NT, Xs) ),
close(Stream))
).
stream_to_lazy_list(Stream, Xs) :-
stream_property(Stream, position(Pos)),
freeze(Xs, reader_step(Stream, Pos, Xs)).
reader_step(Stream, Pos, Xs0) :-
set_stream_position(Stream, Pos),
( at_end_of_stream(Stream)
-> Xs0 = []
; '$get_n_chars'(Stream, 4096, Cs),
partial_string(Cs, Xs0, Xs),
stream_to_lazy_list(Stream, Xs)
).

59
src/lib/random.pl Normal file
View File

@@ -0,0 +1,59 @@
:- module(random, [maybe/0, random/1, random_integer/3, set_random/1]).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
To retain desirable declarative properties, predicates that internally
use random numbers should be equipped with an argument that specifies
the random seed. This makes everything completely reproducible.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- use_module(library(error)).
% succeeds with probability 0.5.
maybe :- '$maybe'.
% The higher the precision, the slower it gets.
random_number_precision(64).
random(R) :-
var(R),
random_number_precision(N),
rnd(N, R).
random_integer(Lower, Upper, R) :-
var(R),
( (var(Lower) ; var(Upper)) ->
instantiation_error(random_integer/3)
; \+ integer(Lower) ->
domain_error(integer, Lower, random_integer/3)
; \+ integer(Upper) ->
domain_error(integer, Upper, random_integer/3)
; Upper > Lower,
random(R0),
R is floor((Upper - Lower) * R0 + Lower)
).
rnd(N, R) :-
rnd_(N, 0, R).
rnd_(0, R, R) :- !.
rnd_(N, R0, R) :-
maybe,
!,
N1 is N - 1,
rnd_(N1, R0, R).
rnd_(N, R0, R) :-
N1 is N - 1,
R1 is R0 + 1.0 / 2.0 ^ N,
rnd_(N1, R1, R).
set_random(Seed) :-
( nonvar(Seed) ->
( Seed = seed(S) ->
( var(S) -> instantiation_error(set_random/1)
; integer(S) -> '$set_seed'(S)
; type_error(integer, S, set_random/1)
)
)
; instantiation_error(set_random/1)
).

92
src/lib/sgml.pl Normal file
View File

@@ -0,0 +1,92 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Predicates for parsing HTML and XML documents.
Written June 2020 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
Currently, two predicates are provided:
- load_html(+Source, -Es, +Options)
- load_xml(+Source, -Es, +Options)
These predicates parse HTML and XML documents, respectively.
Source must be a stream, specified as stream(S), or a file,
specified as file(Name), where Name is a list of characters, or a
list of characters with the document contents.
Es is unified with the abstract syntax tree of the parsed document,
represented as a list of elements where each is of the form:
* a list of characters, representing text
* element(Name, Attrs, Children)
- Name is the name of the tag
- Attrs is a list of Key=Value pairs:
Key is an atom, and Value is a list of characters
- Children is a list of elements as specified here.
Currently, Options are ignored. In the future, more options may be
provided to control parsing.
Example:
?- load_html("<html><head><title>Hello!</title></head></html>", Es, []).
Yielding:
Es = [element(html,[],
[element(head,[],
[element(title,[],
["Hello!"])]),
element(body,[],[])])].
library(xpath) provides convenient reasoning about parsed documents.
For example, to fetch the title of the document above, we can use:
?- load_html("<html><head><title>Hello!</title></head></html>", Es, []),
xpath(Es, //title(text), T).
Yielding T = "Hello!".
Use http_open/3 from library(http/http_open) to read answers from
web servers via streams.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(sgml, [load_html/3,
load_xml/3]).
:- use_module(library(iso_ext)).
:- use_module(library(error)).
:- use_module(library(dcgs)).
:- use_module(library(pio)).
load_html(Source, Es, Options) :-
load_structure_(Source, Es, Options, html).
load_xml(Source, Es, Options) :-
load_structure_(Source, Es, Options, xml).
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
load_structure_([], [], _, _).
load_structure_([C|Cs], [E], Options, What) :-
load_(What, [C|Cs], E, Options).
load_structure_(file(Fs), [E], Options, What) :-
must_be(list, Options),
must_be(list, Fs),
atom_chars(File, Fs),
once(phrase_from_file(list(Cs), File)),
load_(What, Cs, E, Options).
load_structure_(stream(Stream), [E], Options, What) :-
must_be(list, Options),
read_to_end(Stream, Cs),
load_(What, Cs, E, Options).
load_(html, Cs, E, Options) :- '$load_html'(Cs, E, Options).
load_(xml, Cs, E, Options) :- '$load_xml'(Cs, E, Options).
read_to_end(Stream, Cs) :-
'$get_n_chars'(Stream, 4096, Cs0),
( Cs0 = [] -> Cs = []
; partial_string(Cs0, Cs, Rest),
read_to_end(Stream, Rest)
).

67
src/lib/sockets.pl Normal file
View File

@@ -0,0 +1,67 @@
:- module(sockets, [socket_client_open/3,
socket_server_open/2,
socket_server_accept/4,
socket_server_close/1,
current_hostname/1]).
:- use_module(library(error)).
:- use_module(library(lists)).
parse_socket_options_(tls(TLS), tls-TLS) :-
must_be(boolean, TLS), !.
parse_socket_options_(Option, OptionPair) :-
builtins:parse_stream_options_(Option, OptionPair).
parse_socket_options(Options, OptionValues, Stub) :-
DefaultOptions = [alias-[], eof_action-eof_code, reposition-false, tls-false, type-text],
builtins:parse_options_list(Options, parse_socket_options_, DefaultOptions, OptionValues, Stub).
socket_client_open(Addr, Stream, Options) :-
( var(Addr) ->
throw(error(instantiation_error, socket_client_open/3))
;
true
),
must_be(var, Stream),
must_be(list, Options),
( Addr = Address:Port,
atom(Address),
( atom(Port) ; integer(Port) ) ->
true
;
throw(error(type_error(socket_address, Addr), socket_client_open/3))
),
parse_socket_options(Options,
[Alias, EOFAction, Reposition, TLS, Type],
socket_client_open/3),
'$socket_client_open'(Address, Port, Stream, Alias, EOFAction, Reposition, Type, TLS).
socket_server_open(Addr, ServerSocket) :-
must_be(var, ServerSocket),
( ( integer(Addr) ; var(Addr) ) ->
'$socket_server_open'([], Addr, ServerSocket)
;
Addr = Address:Port,
must_be(atom, Address),
can_be(integer, Port),
'$socket_server_open'(Address, Port, ServerSocket)
).
socket_server_accept(ServerSocket, Client, Stream, Options) :-
must_be(var, Client),
must_be(var, Stream),
builtins:parse_stream_options(Options,
[Alias, EOFAction, Reposition, Type],
socket_server_accept/4),
'$socket_server_accept'(ServerSocket, Client, Stream, Alias, EOFAction, Reposition, Type).
socket_server_close(ServerSocket) :-
'$socket_server_close'(ServerSocket).
current_hostname(HostName) :-
'$current_hostname'(HostName).

View File

@@ -8,17 +8,17 @@
op(1150, fx, table)
]).
:- use_module('tabling/double_linked_list').
:- use_module('tabling/table_data_structure').
:- use_module('tabling/batched_worklist').
:- use_module('tabling/wrapper').
:- use_module('tabling/global_worklist').
:- use_module('tabling/table_link_manager').
:- use_module(library(tabling/double_linked_list)).
:- use_module(library(tabling/table_data_structure)).
:- use_module(library(tabling/batched_worklist)).
:- use_module(library(tabling/wrapper)).
:- use_module(library(tabling/global_worklist)).
:- use_module(library(tabling/table_link_manager)).
:- use_module(library(cont)).
:- use_module(library(lists)).
%:- use_module(library(debug)).
:- use_module(library(non_iso)).
:- use_module(library(iso_ext)).
%% :- meta_predicate
%% start_tabling(+, 0).
@@ -135,19 +135,15 @@ activate(Wrapper,Worker,T) :-
delim(Wrapper,Worker,Table) :-
% debug(tabling, 'ACT: ~p on ~p', [Wrapper, Table]),
reset(Worker,SourceCall,Continuation),
( Continuation == none, var(SourceCall) ->
reset(Worker,SourceCall,Continuation),
( Continuation = none ->
( add_answer(Table,Wrapper)
-> true %debug(tabling, 'ADD: ~p', [Wrapper])
; %debug(tabling, 'DUP: ~p', [Wrapper]),
fail
)
;
( Continuation = cont(Cont) ->
true
; Continuation = none ->
Cont = true
),
Continuation = cont(Cont),
SourceCall = call_info(_,SourceTable),
TargetCall = call_info(Wrapper,Table),
Dependency = dependency(SourceCall,Cont,TargetCall),
@@ -178,7 +174,7 @@ completion_step(SourceTableID) :-
fail
;
true
).
).
table_get_work(NBWorklistID,Answer,Dependency) :-
% get_worklist(Table, Worklist),
@@ -192,7 +188,7 @@ table_get_work(NBWorklistID,Answer,Dependency) :-
table_get_work_(NBWorklistID,Answer,Dependency) :-
worklist_do_all_work(NBWorklistID,Answer,Dependency0), % This will eventually fail
copy_term(Dependency0,Dependency).
table_get_work_(NBWorklistID,_Answer,_Dependency) :-
bb_get(NBWorklistID, table_nb_worklist(Worklist)),
unset_flag_executing_all_work(Worklist),

View File

@@ -45,8 +45,8 @@
wkl_worklist_work_done/1 % +WorkList
]).
:- use_module(global_worklist).
:- use_module(double_linked_list).
:- use_module(library(tabling/global_worklist)).
:- use_module(library(tabling/double_linked_list)).
:- use_module(library(atts)).
:- use_module(library(lists)).
@@ -291,7 +291,7 @@ wkl_add_to_existing_answer_cluster(Worklist, Answer) :-
wkl_add_to_new_answer_cluster(
wkl_worklist(Dll,_Ria,_FlagExecutingWork,_AlreadyInMetaworklist,_TableIdentifier),
Answer,AnswerClusterPointer
) :-
) :-
dll_append_left(Dll,wkl_answer_cluster(AnswerFlag),AnswerClusterPointer),
put_atts(AnswerFlag, wkl_answer_cluster([Answer])).

View File

@@ -208,4 +208,3 @@ dll_get_reverse_contents_(List, Contents) :-
dll_get_pointer_to_previous(List, Prev),
dll_get_reverse_contents_(Prev, Rest)
).

View File

@@ -1,4 +1,4 @@
/* Ported to Scryer Prolog by Mark Thom (2019/2020).
/* Ported to Scryer Prolog by Mark Thom (2019/2020).
*/
:- module(global_worklist,
@@ -9,7 +9,7 @@
]).
:- use_module(library(atts)).
:- use_module(library(non_iso)).
:- use_module(library(iso_ext)).
:- attribute table_global_worklist/1.
@@ -21,7 +21,7 @@ put_new_global_worklist :-
bb_b_put(table_global_worklist_initialized, [])
).
add_to_global_worklist(TableIdentifier) :-
add_to_global_worklist(TableIdentifier) :-
bb_get(table_global_worklist, TableGlobalWorklistFlag),
get_atts(TableGlobalWorklistFlag, table_global_worklist(L1)),
put_atts(TableGlobalWorklistFlag, table_global_worklist([TableIdentifier|L1])),
@@ -34,7 +34,7 @@ worklist_empty :-
pop_worklist(TableIdentifier) :-
bb_get(table_global_worklist,TableGlobalWorklistFlag),
get_atts(TableGlobalWorklistFlag, table_global_worklist(L1)),
get_atts(TableGlobalWorklistFlag, table_global_worklist(L1)),
L1 = [TableIdentifier|L2],
put_atts(TableGlobalWorklistFlag, table_global_worklist(L2)),
bb_put(table_global_worklist, TableGlobalWorklistFlag).

View File

@@ -15,8 +15,8 @@
get_nb_identifiers/3 % +Table, -NbWorklistID, -NbAnswerTreeID
]).
:- use_module(table_link_manager).
:- use_module(trie).
:- use_module(library(tabling/table_link_manager)).
:- use_module(library(tabling/trie)).
/* Part of SWI-Prolog
@@ -53,11 +53,11 @@
POSSIBILITY OF SUCH DAMAGE.
*/
:- use_module(batched_worklist).
:- use_module(library(tabling/batched_worklist)).
:- use_module(library(atts)).
:- use_module(library(gensym)).
:- use_module(library(non_iso)).
:- use_module(library(iso_ext)).
:- attribute table_status/1, newly_created_table_identifiers/1.

View File

@@ -44,10 +44,10 @@
:- use_module(library(atts)).
:- use_module(library(lists)).
:- use_module(library(non_iso)).
:- use_module(library(iso_ext)).
:- use_module(library(terms)).
:- use_module(trie).
:- use_module(library(tabling/trie)).
:- attribute trie_table_link/1.

View File

@@ -66,7 +66,7 @@
% p_trie_arity_univ(+Term,-FunctorData,-ArgumentsList).
p_trie_arity_univ(Term,functor_data(Name,Arity),Arguments) :-
( var(Term) ->
Name = var,
Name = Term,
Arity = 0,
Arguments = []
; Term =.. [Name|Arguments],
@@ -187,7 +187,7 @@ trie_insert_1_1_1(>,_V,_L,R,Assoc,FunctorData,Trie,First,Rest,Value) :-
% Look in the right part of the assoc tree.
trie_insert_1_1(R,Assoc,FunctorData,Trie,First,Rest,Value).
trie_insert_2(RegularTerm,Rest,Trie,Value) :-
trie_insert_2(RegularTerm,Rest,Trie,Value) :-
p_trie_arity_univ(RegularTerm,FunctorData,KList),
append(KList,Rest,KList2),
trie_insert_1(KList2,FunctorData,Trie,Value).

129
src/lib/time.pl Normal file
View File

@@ -0,0 +1,129 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written 2020 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
This library provides predicates for reasoning about time.
current_time(T) yields the current system time in an opaque form,
called a time stamp. Use format_time//2 to describe strings that
contain attributes of the time stamp.
The nonterminal format_time//2 describes a list of characters that
are formatted according to a format string. Usage:
phrase(format_time(FormatString, TimeStamp), Cs)
TimeStamp represents a moment in time in an opaque form, as for
example obtained by current_time/1.
FormatString is a list of characters that are interpreted literally,
except for the following specifiers (and possibly more in the future):
%Y year of the time stamp. Example: 2020.
%m month number (01-12), zero-padded to 2 digits
%d day number (01-31), zero-padded to 2 digits
%H hour number (00-24), zero-padded to 2 digits
%M minute number (00-59), zero-padded to 2 digits
%S second number (00-60), zero-padded to 2 digits
%b abbreviated month name, always 3 letters
%a abbreviated weekday name, always 3 letters
%A full weekday name
%j day of the year (001-366), zero-padded to 3 digits
%% the literal %
Example:
?- current_time(T), phrase(format_time("%d.%m.%Y (%H:%M:%S)", T), Cs).
T = [...], Cs = "11.06.2020 (00:24:32)"
; false.
sleep(S) sleeps for S seconds (a floating point number).
time(Goal) reports the execution time of Goal.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(time, [max_sleep_time/1, sleep/1, time/1, current_time/1, format_time//2]).
:- use_module(library(format)).
:- use_module(library(iso_ext)).
:- use_module(library(error)).
:- use_module(library(dcgs)).
:- use_module(library(lists)).
:- use_module(library(charsio), [read_term_from_chars/2]).
current_time(T) :-
'$current_time'(T0),
read_term_from_chars(T0, T).
format_time([], _) --> [].
format_time(['%','%'|Fs], T) --> !, "%", format_time(Fs, T).
format_time(['%',Spec|Fs], T) --> !,
( { member(Spec=Value, T) } ->
list(Value)
; { domain_error(time_specifier, Spec, format_time//2) }
),
format_time(Fs, T).
format_time([F|Fs], T) --> [F], format_time(Fs, T).
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
max_sleep_time(0xfffffffffffffbff).
sleep(T) :-
builtins:must_be_number(T, sleep),
( T < 0 ->
domain_error(not_less_than_zero, T, sleep/1)
; max_sleep_time(N), T > N ->
throw(error(representation_error(max_sleep_time), sleep/1))
; '$sleep'(T)
).
% '$cpu_now' can be replaced by statistics/2 once that is implemented.
time(Goal) :-
'$cpu_now'(T0),
setup_call_cleanup(true,
( Goal,
report_time(T0)
),
report_time(T0)).
report_time(T0) :-
'$cpu_now'(T),
Time is T - T0,
( bb_get('$first_answer', true) ->
format(" % CPU time: ~3f seconds~n", [Time])
; format("% CPU time: ~3f seconds~n ", [Time])
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- time((true;false)).
% CPU time: 0.000 seconds
true
; % CPU time: 0.001 seconds
false.
:- time(use_module(library(clpz))).
% CPU time: 2.762 seconds
true
; false.
:- time(use_module(library(lists))).
% CPU time: 0.000 seconds
true
; % CPU time: 0.001 seconds
false.
?- time(member(X, [a,b,c])).
% CPU time: 0.000 seconds
X = a
; % CPU time: 0.002 seconds
X = b
; % CPU time: 0.004 seconds
X = c
; % CPU time: 0.007 seconds
false.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

642
src/lib/xpath.pl Normal file
View File

@@ -0,0 +1,642 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The original copyright and licence information is shown below.
Adapted in June 2020 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
In order to improve portability and reliability of this library, I
have changed various parts of the original code to make it
conforming to the Prolog ISO standard. I have removed all
occurrences of proprietary non-standard ad hoc data types.
In Scryer Prolog, we represent strings as lists of characters.
This makes them amenable to processing with DCGs and other
built-in mechanisms such as existing predicates on lists.
Scryer Prolog represents lists of characters very compactly,
making it an ideal representation for large amounts of text. It
fully conforms to the ISO standard and is very portable.
This library is intended to be used in tandem with load_html/3
from library(sgml), which creates DOM trees from markup documents.
It can be combined with http_open/3 from library(http/http_open)
to read such documents directly from network connections.
For instance, to inspect all links to Prolog files (*.pl) on
Scryer Prolog's project page, we can use:
:- use_module(library(http/http_open)).
:- use_module(library(sgml)).
:- use_module(library(lists)).
:- use_module(library(xpath)).
link_to_pl_file(File) :-
http_open("https://github.com/mthom/scryer-prolog", S, []),
load_html(stream(S), DOM, []),
xpath(DOM, //a(@href), File),
append(_, ".pl", File).
Yielding:
?- link_to_pl_file(File).
%@ File = "/mthom/scryer-prolog/blob/master/src/lib/tabling.pl"
%@ ; File = "/mthom/scryer-prolog/blob/master/src/lib/dif.pl"
%@ ; File = "/mthom/scryer-prolog/blob/master/src/lib/freeze.pl"
%@ ; ...
Parts of the original functionality may not yet work. Please
consider such parts opportunities for improvements, and file
issues in case you need additional features.
The original copyright information follows.
======================================================================
Author: Jan Wielemaker
E-mail: J.Wielemaker@vu.nl
WWW: http://www.swi-prolog.org
Copyright (c) 2009-2019, University of Amsterdam
VU University Amsterdam
CWI, Amsterdam
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(xpath,
[ xpath/3, % +DOM, +Spec, -Value
xpath_chk/3, % +DOM, +Spec, -Value
op(400, fx, //),
op(400, fx, /),
op(200, fy, @)
]).
:- use_module(library(lists),[member/2,memberchk/2]).
:- use_module(library(error)).
:- use_module(library(dcgs)).
:- use_module(library(si)).
/** <module> Select nodes in an XML DOM
The library xpath.pl provides predicates to select nodes from an XML DOM
tree as produced by library(sgml) based on descriptions inspired by the
XPath language.
The predicate xpath/3 selects a sub-structure of the DOM
non-deterministically based on an XPath-like specification. Not all
selectors of XPath are implemented, but the ability to mix xpath/3 calls
with arbitrary Prolog code provides a powerful tool for extracting
information from XML parse-trees.
@see http://www.w3.org/TR/xpath
*/
element_name(element(Name,_,_), Name).
element_attributes(element(_,Attributes,_), Attributes).
element_content(element(_,_,Content), Content).
%! xpath_chk(+DOM, +Spec, ?Content) is semidet.
%
% Semi-deterministic version of xpath/3.
xpath_chk(DOM, Spec, Content) :-
xpath(DOM, Spec, Content),
!.
%! xpath(+DOM, +Spec, ?Content) is nondet.
%
% Match an element in a DOM structure. The syntax is inspired by
% XPath, using () rather than [] to select inside an element.
% First we can construct paths using / and //:
%
% $ =|//|=Term :
% Select any node in the DOM matching term.
% $ =|/|=Term :
% Match the root against Term.
% $ Term :
% Select the immediate children of the root matching Term.
%
% The Terms above are of type _callable_. The functor specifies
% the element name. The element name '*' refers to any element.
% The name =self= refers to the top-element itself and is often
% used for processing matches of an earlier xpath/3 query. A term
% NS:Term refers to an XML name in the namespace NS. Optional
% arguments specify additional constraints and functions. The
% arguments are processed from left to right. Defined conditional
% argument values are:
%
% $ index(?Index) :
% True if the element is the Index-th child of its parent,
% where 1 denotes the first child. Index can be one of:
% $ `Var` :
% `Var` is unified with the index of the matched element.
% $ =last= :
% True for the last element.
% $ =last= - `IntExpr` :
% True for the last-minus-nth element. For example,
% `last-1` is the element directly preceding the last one.
% $ `IntExpr` :
% True for the element whose index equals `IntExpr`.
% $ Integer :
% The N-th element with the given name, with 1 denoting the
% first element. Same as index(Integer).
% $ =last= :
% The last element with the given name. Same as
% index(last).
% $ =last= - IntExpr :
% The IntExpr-th element before the last.
% Same as index(last-IntExpr).
%
% Defined function argument values are:
%
% $ =self= :
% Evaluate to the entire element
% $ =content= :
% Evaluate to the content of the element (a list)
% $ =text= :
% Evaluates to all text from the sub-tree, represented
% as a list of characters.
% $ `text(atom)` :
% Evaluates to all text from the sub-tree as an atom.
% $ =normalize_space= :
% As =text=, but uses normalize_space/2 to normalise
% white-space in the output
% $ =number= :
% Extract an integer or float from the value. Ignores
% leading and trailing white-space
% $ =|@|=Attribute :
% Evaluates to the value of the given attribute. Attribute
% can be a compound term. In this case the functor name
% denotes the element and arguments perform transformations
% on the attribute value. Defined transformations are:
%
% - number
% Translate the value into a number using
% xsd_number_chars/2.
% - integer
% As `number`, but subsequently transform the value
% into an integer using the round/1 function.
% - float
% As `number`, but subsequently transform the value
% into a float using the float/1 function.
% - lower
% Translate the value to lower case, preserving
% the type.
% - upper
% Translate the value to upper case, preserving
% the type.
%
% In addition, the argument-list can be _conditions_:
%
% $ Left = Right :
% Succeeds if the left-hand unifies with the right-hand.
% If the left-hand side is a function, this is evaluated.
% The right-hand side is _never_ evaluated, and thus the
% condition `content = content` defines that the content
% of the element is the atom `content`.
% The functions `lower_case` and `upper_case` can be applied
% to Right (see example below).
% $ contains(Haystack, Needle) :
% Succeeds if Needle is a sub-list of Haystack.
% $ XPath :
% Succeeds if XPath matches in the currently selected
% sub-DOM. For example, the following expression finds
% an =h3= element inside a =div= element, where the =div=
% element itself contains an =h2= child with a =strong=
% child.
%
% ==
% //div(h2/strong)/h3
% ==
%
% This is equivalent to the conjunction of XPath goals below.
%
% ==
% ...,
% xpath(DOM, //(div), Div),
% xpath(Div, h2/strong, _),
% xpath(Div, h3, Result)
% ==
%
% **Examples**:
%
% Match each table-row in DOM:
%
% ==
% xpath(DOM, //tr, TR)
% ==
%
% Match the last cell of each tablerow in DOM. This example
% illustrates that a result can be the input of subsequent xpath/3
% queries. Using multiple queries on the intermediate TR term
% guarantee that all results come from the same table-row:
%
% ==
% xpath(DOM, //tr, TR),
% xpath(TR, /td(last), TD)
% ==
%
% Match each =href= attribute in an <a> element
%
% ==
% xpath(DOM, //a(@href), HREF)
% ==
%
% Suppose we have a table containing rows where each first column
% is the name of a product with a link to details and the second
% is the price (a number). The following predicate matches the
% name, URL and price:
%
% ==
% product(DOM, Name, URL, Price) :-
% xpath(DOM, //tr, TR),
% xpath(TR, td(1), C1),
% xpath(C1, /self(normalize_space), Name),
% xpath(C1, a(@href), URL),
% xpath(TR, td(2, number), Price).
% ==
%
% Suppose we want to select books with genre="thriller" from a
% tree containing elements =|<book genre=...>|=
%
% ==
% thriller(DOM, Book) :-
% xpath(DOM, //book(@genre=thiller), Book).
% ==
%
% Match the elements =|<table align="center">|= _and_ =|<table
% align="CENTER">|=:
%
% ```prolog
% //table(@align(lower) = center)
% ```
%
% Get the `width` and `height` of a `div` element as a number,
% and the `div` node itself:
%
% ==
% xpath(DOM, //div(@width(number)=W, @height(number)=H), Div)
% ==
%
% Note that `div` is an infix operator, so parentheses must be
% used in cases like the following:
%
% ==
% xpath(DOM, //(div), Div)
% ==
xpath(DOM, Spec, Content) :-
in_dom(Spec, DOM, Content).
in_dom(//Spec, DOM, Value) :-
!,
element_spec(Spec, Name, Modifiers),
sub_dom(I, Len, Name, E, DOM),
modifiers(Modifiers, I, Len, E, Value).
in_dom(/Spec, E, Value) :-
!,
element_spec(Spec, Name, Modifiers),
( Name == self
-> true
; element_name(E, Name)
),
modifiers(Modifiers, 1, 1, E, Value).
in_dom(A/B, DOM, Value) :-
!,
in_dom(A, DOM, Value0),
in_dom(B, Value0, Value).
in_dom(A//B, DOM, Value) :-
!,
in_dom(A, DOM, Value0),
in_dom(//B, Value0, Value).
in_dom(Spec, element(_, _, Content), Value) :-
element_spec(Spec, Name, Modifiers),
count_named_elements(Content, Name, CLen),
CLen > 0,
nth_element(N, Name, E, Content),
modifiers(Modifiers, N, CLen, E, Value).
element_spec(Var, _, _) :-
var(Var),
!,
instantiation_error(Var).
element_spec(NS:Term, NS:Name, Modifiers) :-
!,
callable_name_arguments(Term, Name0, Modifiers),
star(Name0, Name).
element_spec(Term, Name, Modifiers) :-
!,
callable_name_arguments(Term, Name0, Modifiers),
star(Name0, Name).
callable_name_arguments(Atom, Name, Arguments) :-
atom(Atom),
!,
Name = Atom, Arguments = [].
callable_name_arguments(Compound, Name, Arguments) :-
Compound =.. [Name|Arguments].
star(*, _) :- !.
star(Name, Name).
%! sub_dom(-Index, -Count, +Name, -Sub, +DOM) is nondet.
%
% Sub is a node in DOM with Name.
%
% @param Count is the total number of nodes in the content
% list Sub appears that have the same name.
% @param Index is the 1-based index of Sub of nodes with
% Name.
sub_dom(1, 1, Name, DOM, DOM) :-
element_name(DOM, Name0),
\+ Name \= Name0.
sub_dom(N, Len, Name, E, element(_,_,Content)) :-
!,
sub_dom_2(N, Len, Name, E, Content).
sub_dom(N, Len, Name, E, Content) :-
list_si(Content),
sub_dom_2(N, Len, Name, E, Content).
sub_dom_2(N, Len, Name, Element, Content) :-
( count_named_elements(Content, Name, Len),
nth_element(N, Name, Element, Content)
; member(element(_,_,C2), Content),
sub_dom_2(N, Len, Name, Element, C2)
).
%! count_named_elements(+Content, +Name, -Count) is det.
%
% Count is the number of nodes with Name in Content.
count_named_elements(Content, Name, Count) :-
count_named_elements(Content, Name, 0, Count).
count_named_elements([], _, Count, Count).
count_named_elements([element(Name,_,_)|T], Name0, C0, C) :-
\+ Name \= Name0,
!,
C1 is C0+1,
count_named_elements(T, Name0, C1, C).
count_named_elements([_|T], Name, C0, C) :-
count_named_elements(T, Name, C0, C).
%! nth_element(?N, +Name, -Element, +Content:list) is nondet.
%
% True if Element is the N-th element with name in Content.
nth_element(N, Name, Element, Content) :-
nth_element_(1, N, Name, Element, Content).
nth_element_(I, N, Name, E, [H|T]) :-
element_name(H, Name0),
\+ Name \= Name0,
!,
( N = I,
E = H
; I2 is I + 1,
( nonvar(N), I2 > N
-> !, fail
; true
),
nth_element_(I2, N, Name, E, T)
).
nth_element_(I, N, Name, E, [_|T]) :-
nth_element_(I, N, Name, E, T).
%! modifiers(+Modifiers, +I, +Clen, +DOM, -Value)
%
%
modifiers([], _, _, Value, Value).
modifiers([H|T], I, L, Value0, Value) :-
modifier(H, I, L, Value0, Value1),
modifiers(T, I, L, Value1, Value).
modifier(M, _, _, _, _) :-
var(M),
!,
instantiation_error(M).
modifier(Index, I, L, Value0, Value) :-
implicit_index_modifier(Index),
!,
Value = Value0,
index_modifier(Index, I, L).
modifier(index(Index), I, L, Value, Value) :-
!,
index_modifier(Index, I, L).
modifier(Function, _, _, In, Out) :-
xpath_function(Function),
!,
xpath_function(Function, In, Out).
modifier(Function, _, _, In, Out) :-
xpath_condition(Function, In),
Out = In.
implicit_index_modifier(I) :-
integer(I),
!.
implicit_index_modifier(last).
implicit_index_modifier(last-_Expr).
index_modifier(Var, I, _L) :-
var(Var),
!,
Var = I.
index_modifier(last, I, L) :-
!,
I =:= L.
index_modifier(last-Expr, I, L) :-
!,
I =:= L-Expr.
index_modifier(N, I, _) :-
N =:= I.
xpath_function(self, DOM, DOM). % self
xpath_function(content, Element, Value) :- % content
element_content(Element, Value).
xpath_function(text, DOM, Text) :- % text
text_of_dom(DOM, chars, Text).
xpath_function(text(As), DOM, Text) :- % text(atom)
text_of_dom(DOM, As, Text).
xpath_function(normalize_space, DOM, Text) :- % normalize_space
text_of_dom(DOM, chars, Text0),
normalize_space(Text0, Text).
xpath_function(number, DOM, Number) :- % number
text_of_dom(DOM, chars, Text0),
normalize_space(Text0, Text),
catch(xsd_number_chars(Number, Text), _, fail).
xpath_function(@Name, element(_, Attrs, _), Value) :- % @Name
( atom(Name)
-> memberchk(Name=Value, Attrs)
; compound(Name)
-> Name =.. [AName|AOps],
memberchk(AName=Value0, Attrs),
translate_attribute(AOps, Value0, Value)
; member(Name=Value, Attrs)
).
xpath_function(quote(Value), _, Value). % quote(Value)
xpath_function(self).
xpath_function(content).
xpath_function(text).
xpath_function(text(_)).
xpath_function(normalize_space).
xpath_function(number).
xpath_function(@_).
xpath_function(quote(_)).
translate_attribute([], Value, Value).
translate_attribute([H|T], Value0, Value) :-
translate_attr(H, Value0, Value1),
translate_attribute(T, Value1, Value).
translate_attr(number, Value0, Value) :-
xsd_number_chars(Value, Value0).
translate_attr(integer, Value0, Value) :-
xsd_number_chars(Value1, Value0),
Value is round(Value1).
translate_attr(float, Value0, Value) :-
xsd_number_chars(Value1, Value0),
Value is float(Value1).
% The implementation of these translations is left for later.
% translate_attr(lower, Value0, Value) :- ...
% translate_attr(upper, Value0, Value) :- ...
xpath_condition(Left = Right, Value) :- % =
!,
var_or_function(Left, Value, LeftValue),
process_equality(LeftValue, Right).
xpath_condition(contains(Haystack, Needle), Value) :- % contains(Haystack, Needle)
!,
val_or_function(Haystack, Value, HaystackValue),
val_or_function(Needle, Value, NeedleValue),
( phrase((list(_),list(NeedleValue),list(_)), HaystackValue)
-> true
).
xpath_condition(Spec, Dom) :-
in_dom(Spec, Dom, _).
%! process_equality(+Left, +Right) is semidet.
%
% Provides (very) partial support for XSLT functions that can be
% applied according to the XPath 2 specification.
%
% For example the XPath expression in [1], and the equivalent
% Prolog expression in [2], would both match the HTML element in
% [3].
%
% ==
% [1] //table[align=lower-case(center)]
% [2] //table(@align=lower_case(center))
% [3] <table align="CENTER">
% ==
process_equality(Left, Right) :-
var(Right),
!,
Left = Right.
process_equality(Left, lower_case(Right)) :-
!,
downcase_atom(Left, Right).
process_equality(Left, upper_case(Right)) :-
!,
upcase_atom(Left, Right).
process_equality(Left, Right) :-
Left = Right.
var_or_function(Arg, _, Arg) :-
var(Arg),
!.
var_or_function(Func, Value0, Value) :-
xpath_function(Func),
!,
xpath_function(Func, Value0, Value).
var_or_function(Value, _, Value).
val_or_function(Arg, _, Arg) :-
var(Arg),
!,
instantiation_error(Arg).
val_or_function(Func, Value0, Value) :- % TBD
xpath_function(Func, Value0, Value),
!.
val_or_function(Value, _, Value).
%! text_of_dom(+DOM, +As, -Text:(chars | atom)) is det.
%
% Text is the joined textual content of DOM.
text_of_dom(DOM, As, Text) :-
phrase(text_of(DOM), Text0),
( As == chars ->
Text = Text0
; As == atom ->
atom_chars(Text, Text0)
; domain_error(As, atom_or_chars, xpath)
).
text_of(element(_,_,Content)) -->
text_of_list(Content).
text_of([]) -->
[].
text_of([H|T]) -->
text_of(H),
text_of(T).
text_of_list([]) -->
[].
text_of_list([H|T]) -->
text_of_1(H),
text_of_list(T).
text_of_1(element(_,_,Content)) -->
text_of_list(Content).
text_of_1([C|Cs]) --> list([C|Cs]).
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
% For now, we use number_chars/2 to parse XML numbers.
% If the need arises, we can extend this to additional
% float constants and syntax that may occur in XML files.
xsd_number_chars(Number, Chars) :-
number_chars(Number, Chars).
normalize_space(Text0, Text) :-
Text0 = Text. % no conversion for the moment.

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
:- module('$atts', []).
driver(Vars, Values) :-
iterate(Vars, Values, ListOfListsOfGoalLists),
'$clear_attr_var_bindings',
!,
call_goals(ListOfListsOfGoalLists),
'$return_from_verify_attr'.
@@ -17,6 +18,14 @@ gather_modules([Attr|Attrs], [Module|Modules]) :-
'$module_of'(Module, Attr), % write the owning module of Attr to Module.
gather_modules(Attrs, Modules).
call_verify_attributes(Attrs, _, _, []) :-
var(Attrs), !.
call_verify_attributes([], _, _, []).
call_verify_attributes([Attr|Attrs], Var, Value, ListOfGoalLists) :-
gather_modules([Attr|Attrs], Modules0),
sort(Modules0, Modules),
verify_attrs(Modules, Var, Value, ListOfGoalLists).
verify_attrs([Module|Modules], Var, Value, [Goals|ListOfGoalLists]) :-
catch(Module:verify_attributes(Var, Value, Goals),
error(evaluation_error((Module:verify_attributes)/3), verify_attributes/3),
@@ -24,14 +33,6 @@ verify_attrs([Module|Modules], Var, Value, [Goals|ListOfGoalLists]) :-
verify_attrs(Modules, Var, Value, ListOfGoalLists).
verify_attrs([], _, _, []).
call_verify_attributes(Attrs, _, _, []) :-
var(Attrs), !.
call_verify_attributes([], _, _, []).
call_verify_attributes([Attr|Attrs], Var, Value, ListOfGoalLists) :-
gather_modules([Attr|Attrs], Modules0),
sort(Modules0, Modules),
verify_attrs(Modules, Var, Value, ListOfGoalLists).
call_goals([ListOfGoalLists | ListsCubed]) :-
call_goals_0(ListOfGoalLists),
call_goals(ListsCubed).

View File

@@ -1,5 +1,9 @@
use crate::prolog::machine::*;
use crate::heap_iter::*;
use crate::machine::*;
use crate::indexmap::IndexSet;
use std::cmp::Ordering;
use std::vec::IntoIter;
pub static VERIFY_ATTRS: &str = include_str!("attributed_variables.pl");
@@ -7,6 +11,7 @@ pub static PROJECT_ATTRS: &str = include_str!("project_attributes.pl");
pub(super) type Bindings = Vec<(usize, Addr)>;
#[derive(Debug)]
pub(super) struct AttrVarInitializer {
pub(super) attribute_goals: Vec<Addr>,
pub(super) attr_var_queue: Vec<usize>,
@@ -18,7 +23,8 @@ pub(super) struct AttrVarInitializer {
}
impl AttrVarInitializer {
pub(super) fn new(verify_attrs_loc: usize, project_attrs_loc: usize) -> Self {
pub(super)
fn new(verify_attrs_loc: usize, project_attrs_loc: usize) -> Self {
AttrVarInitializer {
attribute_goals: vec![],
attr_var_queue: vec![],
@@ -31,21 +37,24 @@ impl AttrVarInitializer {
}
#[inline]
pub(super) fn reset(&mut self) {
self.attribute_goals.clear();
pub(super)
fn reset(&mut self) {
self.attribute_goals.clear();
self.attr_var_queue.clear();
self.bindings.clear();
}
#[inline]
pub(super) fn backtrack(&mut self, queue_b: usize, bindings_b: usize) {
pub(super)
fn backtrack(&mut self, queue_b: usize, bindings_b: usize) {
self.attr_var_queue.truncate(queue_b);
self.bindings.truncate(bindings_b);
}
}
impl MachineState {
pub(super) fn push_attr_var_binding(&mut self, h: usize, addr: Addr) {
pub(super)
fn push_attr_var_binding(&mut self, h: usize, addr: Addr) {
if self.attr_var_init.bindings.is_empty() {
self.attr_var_init.instigating_p = self.p.local();
@@ -66,18 +75,17 @@ impl MachineState {
.attr_var_init
.bindings
.iter()
.map(|(ref h, _)| Addr::AttrVar(*h));
.map(|(ref h, _)| HeapCellValue::Addr(Addr::AttrVar(*h)));
let var_list_addr = Addr::HeapCell(self.heap.to_list(iter));
let iter = self
.attr_var_init
.bindings
.iter()
.map(|(_, ref addr)| addr.clone());
.drain(0 ..)
.map(|(_, addr)| HeapCellValue::Addr(addr));
let value_list_addr = Addr::HeapCell(self.heap.to_list(iter));
(var_list_addr, value_list_addr)
}
@@ -92,7 +100,8 @@ impl MachineState {
self[temp_v!(2)] = value_list_addr;
}
pub(super) fn gather_attr_vars_created_since(&self, b: usize) -> IntoIter<Addr> {
pub(super)
fn gather_attr_vars_created_since(&self, b: usize) -> IntoIter<Addr> {
let mut attr_vars: Vec<_> = self.attr_var_init.attr_var_queue[b..]
.iter()
.filter_map(|h| match self.store(self.deref(Addr::HeapCell(*h))) {
@@ -101,13 +110,16 @@ impl MachineState {
})
.collect();
attr_vars.sort_unstable_by(|a1, a2| self.compare_term_test(a1, a2));
attr_vars.sort_unstable_by(|a1, a2| {
self.compare_term_test(a1, a2).unwrap_or(Ordering::Less)
});
self.term_dedup(&mut attr_vars);
attr_vars.into_iter()
}
pub(super) fn verify_attr_interrupt(&mut self, p: usize) {
pub(super)
fn verify_attr_interrupt(&mut self, p: usize) {
self.allocate(self.num_of_args + 2);
let e = self.e;
@@ -118,9 +130,9 @@ impl MachineState {
}
self.stack.index_and_frame_mut(e)[self.num_of_args + 1] =
Addr::Con(Constant::CutPoint(self.b0));
Addr::CutPoint(self.b0);
self.stack.index_and_frame_mut(e)[self.num_of_args + 2] =
Addr::Con(Constant::Usize(self.num_of_args));
Addr::Usize(self.num_of_args);
self.verify_attributes();
@@ -128,4 +140,37 @@ impl MachineState {
self.b0 = self.b;
self.p = CodePtr::Local(LocalCodePtr::DirEntry(p));
}
pub(super)
fn attr_vars_of_term(&self, addr: Addr) -> Vec<Addr> {
let mut seen_set = IndexSet::new();
let mut seen_vars = vec![];
let mut iter = self.acyclic_pre_order_iter(addr);
while let Some(addr) = iter.next() {
if let HeapCellValue::Addr(Addr::AttrVar(h)) = self.heap.index_addr(&addr).as_ref() {
if seen_set.contains(h) {
continue;
}
seen_vars.push(addr);
seen_set.insert(*h);
let mut l = h + 1;
let mut list_elements = vec![];
while let Addr::Lis(elem) = self.store(self.deref(Addr::HeapCell(l))) {
list_elements.push(self.heap[elem].as_addr(elem));
l = elem + 1;
}
for element in list_elements.into_iter().rev() {
iter.stack().push(element);
}
}
}
seen_vars
}
}

View File

@@ -1,17 +1,18 @@
use crate::prolog::clause_types::*;
use crate::prolog::codegen::*;
use crate::prolog::debray_allocator::*;
use crate::prolog::forms::*;
use crate::prolog::instructions::*;
use crate::prolog::machine::compile::*;
use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::machine_indices::*;
use crate::clause_types::*;
use crate::codegen::*;
use crate::debray_allocator::*;
use crate::forms::*;
use crate::instructions::*;
use crate::machine::compile::*;
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use indexmap::IndexSet;
use crate::indexmap::IndexSet;
use std::collections::VecDeque;
use std::mem;
#[derive(Debug)]
pub struct CodeRepo {
pub(super) cached_query: Code,
pub(super) goal_expanders: Code,
@@ -52,15 +53,27 @@ impl CodeRepo {
self.term_dir
.get_mut(&key)
.map(|entry| {
(
Predicate((entry.0).0.drain(len..).collect()),
entry.1.drain(queue_len..).collect(),
)
let terms =
if len < (entry.0).0.len() {
(entry.0).0.drain(len ..).collect()
} else {
vec![]
};
let queue =
if queue_len < entry.1.len() {
entry.1.drain(queue_len ..).collect()
} else {
VecDeque::new()
};
(Predicate(terms), queue)
})
.unwrap_or((Predicate::new(), VecDeque::from(vec![])))
.unwrap_or((Predicate::new(), VecDeque::new()))
}
pub(crate) fn add_in_situ_result(
pub(crate)
fn add_in_situ_result(
&mut self,
result: &CompiledResult,
in_situ_code_dir: &mut InSituCodeDir,
@@ -89,7 +102,7 @@ impl CodeRepo {
_ => {
in_situ_code_dir.insert((name, arity), p);
}
}
}
let mut cg = CodeGenerator::<DebrayAllocator>::new(non_counted_bt);
let mut decl_code = cg.compile_predicate(&decl.0)?;
@@ -100,16 +113,19 @@ impl CodeRepo {
}
#[inline]
pub(super) fn size_of_cached_query(&self) -> usize {
pub(super)
fn size_of_cached_query(&self) -> usize {
self.cached_query.len()
}
#[inline]
pub(super) fn take_in_situ_code(&mut self) -> Code {
pub(super)
fn take_in_situ_code(&mut self) -> Code {
mem::replace(&mut self.in_situ_code, Code::new())
}
pub(super) fn lookup_instr<'a>(
pub(super)
fn lookup_instr<'a>(
&'a self,
last_call: bool,
p: &CodePtr,

View File

@@ -1,4 +1,4 @@
use crate::prolog::instructions::*;
use crate::instructions::*;
use std::collections::VecDeque;

View File

@@ -1,27 +1,26 @@
use prolog_parser::ast::*;
use prolog_parser::parser::get_desc;
use prolog_parser::tabled_rc::TabledData;
use crate::prolog_parser::ast::*;
use crate::prolog_parser::parser::get_desc;
use crate::prolog_parser::tabled_rc::TabledData;
use crate::prolog::codegen::*;
use crate::prolog::debray_allocator::*;
use crate::prolog::forms::*;
use crate::prolog::instructions::*;
use crate::prolog::iterators::*;
use crate::prolog::machine::code_walker::*;
use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::machine::term_expansion::ExpansionAdditionResult;
use crate::prolog::machine::toplevel::*;
use crate::prolog::machine::*;
use crate::codegen::*;
use crate::debray_allocator::*;
use crate::forms::*;
use crate::instructions::*;
use crate::iterators::*;
use crate::machine::code_walker::*;
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use crate::machine::term_expansion::ExpansionAdditionResult;
use crate::machine::toplevel::*;
use crate::machine::*;
use indexmap::{IndexMap, IndexSet};
use crate::indexmap::{IndexMap, IndexSet};
use ref_thread_local::RefThreadLocal;
use crate::ref_thread_local::RefThreadLocal;
use std::cell::Cell;
use std::collections::VecDeque;
use std::fs::File;
use std::io::Read;
use std::mem;
use std::path::PathBuf;
@@ -60,9 +59,9 @@ fn fix_filename(
Ok(path)
}
fn load_module<R: Read>(
fn load_module(
wam: &mut Machine,
stream: ParsingStream<R>,
stream: Stream,
suppress_warnings: bool,
listing_src: &ListingSource,
) -> Result<ClauseName, SessionError> {
@@ -78,7 +77,13 @@ fn load_module<R: Read>(
listing_src.clone(),
);
let results = compiler.gather_items(wam, stream, &mut indices);
let mut stream = parsing_stream(stream)?;
let results = compiler.gather_items(
wam,
&mut stream,
&mut indices,
);
let module_name = if let Some(ref module) = &compiler.module {
module.module_decl.name.clone()
@@ -114,14 +119,14 @@ fn load_module_from_file(
let mut path_buf = fix_filename(wam.indices.atom_tbl.clone(), path_buf)?;
let filename = clause_name!(path_buf.to_string_lossy().to_string(), wam.indices.atom_tbl);
let file_handle = File::open(&path_buf).or_else(|_| {
let file_handle = Stream::from_file_as_input(filename.clone(), File::open(&path_buf).or_else(|_| {
Err(SessionError::InvalidFileName(filename.clone()))
})?;
})?);
path_buf.pop();
let listing_src = ListingSource::from_file_and_path(filename, path_buf);
load_module(wam, parsing_stream(file_handle), suppress_warnings, &listing_src)
load_module(wam, file_handle, suppress_warnings, &listing_src)
}
pub type PredicateCompileQueue = (Predicate, VecDeque<TopLevel>);
@@ -185,6 +190,8 @@ fn set_first_index(code: &mut Code) {
if *offset == 0 =>
{
*offset = code_len - idx;
debug_assert!(*offset > 0);
break;
}
_ => {}
@@ -320,10 +327,11 @@ fn setup_module_expansions(wam: &mut Machine, module: &Module) {
);
}
pub(super) fn compile_into_module<R: Read>(
pub(super)
fn compile_into_module(
wam: &mut Machine,
module_name: ClauseName,
src: ParsingStream<R>,
src: Stream,
name: ClauseName,
) -> EvalSession {
let mut indices = default_index_store!(wam.atom_tbl_of(&name));
@@ -333,37 +341,45 @@ pub(super) fn compile_into_module<R: Read>(
indices.op_dir = module.op_dir.clone();
indices.atom_tbl = module.atom_tbl.clone();
let mut compiler = ListingCompiler::new(&wam.code_repo, true, module.listing_src.clone());
let mut compiler = ListingCompiler::new(
&wam.code_repo,
true,
module.listing_src.clone(),
);
match compile_into_module_impl(wam, &mut compiler, module, src, indices) {
Ok(()) => EvalSession::EntrySuccess,
Ok(()) => {
EvalSession::EntrySuccess
}
Err(e) => {
if let Some(module) = compiler.module.take() {
wam.indices.insert_module(module);
}
compiler.drop_expansions(&mut wam.code_repo);
EvalSession::from(e)
}
}
}
fn compile_into_module_impl<R: Read>(
fn compile_into_module_impl(
wam: &mut Machine,
compiler: &mut ListingCompiler,
module: Module,
src: ParsingStream<R>,
src: Stream,
mut indices: IndexStore,
) -> Result<(), SessionError> {
setup_module_expansions(wam, &module);
let module_name = module.module_decl.name.clone();
compiler.module = Some(module);
// compiler.module = Some(module); This trips the goal expansion up. Should be possible to 'merge' modules.
// A much better strategy!
wam.indices.insert_module(module);
wam.code_repo.compile_hook(CompileTimeHook::TermExpansion)?;
wam.code_repo.compile_hook(CompileTimeHook::GoalExpansion)?;
let mut results = compiler.gather_items(wam, src, &mut indices)?;
let mut results = compiler.gather_items(
wam,
&mut parsing_stream(src)?,
&mut indices,
)?;
compiler.adapt_in_situ_code(
results.worker_results,
@@ -383,21 +399,22 @@ fn compile_into_module_impl<R: Read>(
clause_code_generator.generate_clause_code(&results.dynamic_clause_map, wam)?;
let top_level_term_dir = results.top_level_term_dirs.consolidate();
let module = wam.indices.take_module(module_name).unwrap();
add_module(
wam,
compiler.module.take().unwrap(),
module,
indices,
top_level_term_dir,
);
wam.code_repo.code.extend(results.in_situ_code.into_iter());
clause_code_generator.add_clause_code(wam, results.dynamic_clause_map);
Ok(compiler.drop_expansions(&mut wam.code_repo))
}
#[derive(Debug)]
pub struct GatherResult {
dynamic_clause_map: DynamicClauseMap,
pub(crate) worker_results: Vec<PredicateCompileQueue>,
@@ -412,6 +429,7 @@ pub struct GatherResult {
in_situ_module_dir: ModuleStubDir,
}
#[derive(Debug)]
pub struct ClauseCodeGenerator {
len_offset: usize,
code: Code,
@@ -451,6 +469,7 @@ impl ClauseCodeGenerator {
vec![Box::new(head.clone()), Box::new(tail.clone())],
None,
);
PredicateClause::Fact(clause, 0, 0)
})
.collect(),
@@ -518,6 +537,7 @@ fn insert_or_refresh_term_dir_quantum(
}
}
#[derive(Debug)]
pub struct ListingCompiler {
module: Option<Module>,
user_term_dir: TermDir,
@@ -548,8 +568,8 @@ fn add_module(
term_dir: TermDir,
) {
module.code_dir.extend(indices.code_dir);
module.op_dir.extend(indices.op_dir.into_iter());
module.term_dir.extend(term_dir.into_iter());
module.op_dir.extend(indices.op_dir);
module.term_dir.extend(term_dir);
wam.add_in_situ_module_dir(indices.module_dir);
wam.add_module(module);
@@ -568,7 +588,7 @@ fn add_non_module_code(
clause_code_generator.generate_clause_code(&dynamic_clause_map, wam)?;
add_toplevel(wam, indices, term_dir);
wam.code_repo.code.extend(code.into_iter());
wam.code_repo.code.extend(code);
clause_code_generator.add_clause_code(wam, dynamic_clause_map);
Ok(())
@@ -582,21 +602,22 @@ fn load_library(
) -> Result<ClauseName, SessionError> {
match LIBRARIES.borrow().get(name.as_str()) {
Some(code) => {
let mut lib_path = current_dir();
lib_path.pop();
lib_path.push("lib");
let listing_src = ListingSource::from_file_and_path(name, lib_path);
let listing_src = ListingSource::User;
load_module(
wam,
parsing_stream(code.as_bytes()),
Stream::from(*code),
suppress_warnings,
&listing_src,
)
}
None => Err(SessionError::ModuleNotFound)
None => {
let err = ExistenceError::ModuleSource(ModuleSource::Library(
name.clone()
));
Err(SessionError::ExistenceError(err))
}
}
}
@@ -614,7 +635,7 @@ impl ListingCompiler {
.term_dir_entry_len((clause_name!("term_expansion"), 2)),
orig_goal_expansion_lens: code_repo
.term_dir_entry_len((clause_name!("goal_expansion"), 2)),
initialization_goals: (vec![], VecDeque::from(vec![])),
initialization_goals: (vec![], VecDeque::from(vec![])),
suppress_warnings,
listing_src
}
@@ -682,7 +703,11 @@ impl ListingCompiler {
Ok(wam_indices.insert_module(submodule))
} else {
Err(SessionError::ModuleNotFound)
let err = ExistenceError::ModuleSource(ModuleSource::File(
module_name,
));
Err(SessionError::ExistenceError(err))
}
}
@@ -716,7 +741,11 @@ impl ListingCompiler {
Ok(wam_indices.insert_module(submodule))
} else {
Err(SessionError::ModuleNotFound)
let err = ExistenceError::ModuleSource(ModuleSource::File(
module_name
));
Err(SessionError::ExistenceError(err))
}
}
@@ -729,14 +758,14 @@ impl ListingCompiler {
}
fn generate_init_goal_code(
&mut self,
&mut self,
) -> Result<Code, SessionError> {
let query_terms = mem::replace(&mut self.initialization_goals.0, vec![]);
let queue = mem::replace(&mut self.initialization_goals.1, VecDeque::new());
let query_terms = mem::replace(&mut self.initialization_goals.0, vec![]);
let queue = mem::replace(&mut self.initialization_goals.1, VecDeque::new());
compile_query(query_terms, queue)
.map(|(code, _)| code)
.map_err(SessionError::from)
compile_query(query_terms, queue)
.map(|(code, _)| code)
.map_err(SessionError::from)
}
fn set_code_index(
@@ -875,7 +904,7 @@ impl ListingCompiler {
.or_insert((Predicate::new(), VecDeque::from(vec![])));
len += 1;
queue_len += queue_len;
queue_len += queue.len();
(preds.0).0.push(clause);
preds.1.extend(queue.into_iter());
@@ -952,12 +981,12 @@ impl ListingCompiler {
Err(SessionError::from(ParserError::InvalidModuleDecl))
}
}
Declaration::ModuleInitialization(query_terms, queue) => {
self.initialization_goals.0.extend(query_terms.into_iter());
self.initialization_goals.1.extend(queue.into_iter());
Declaration::ModuleInitialization(query_terms, queue) => {
self.initialization_goals.0.extend(query_terms.into_iter());
self.initialization_goals.1.extend(queue.into_iter());
Ok(())
}
Ok(())
}
Declaration::MultiFile(..) => {
Ok(())
}
@@ -968,6 +997,10 @@ impl ListingCompiler {
Declaration::Op(op_decl) => {
self.submit_op(wam, indices, &op_decl)
}
Declaration::SetPrologFlag(dbl_quotes) => {
wam.machine_st.flags.double_quotes = dbl_quotes;
Ok(())
}
Declaration::UseModule(ModuleSource::Library(name)) => {
let name = if !wam.indices.modules.contains_key(&name) {
load_library(wam, name, true)?
@@ -1018,10 +1051,10 @@ impl ListingCompiler {
}
}
fn setup_multifile_decl<R: Read>(
fn setup_multifile_decl(
&self,
indicator: MultiFileIndicator,
worker: &mut TopLevelBatchWorker<R>,
worker: &mut TopLevelBatchWorker,
) -> Result<(), SessionError> {
match indicator {
MultiFileIndicator::LocalScoped(name, arity) => {
@@ -1042,7 +1075,11 @@ impl ListingCompiler {
insert_or_refresh_term_dir_quantum(term_dir, key, term_dirs);
}
None => {
return Err(SessionError::ModuleNotFound);
let err = ExistenceError::ModuleSource(ModuleSource::File(
module_name,
));
return Err(SessionError::ExistenceError(err));
}
}
}
@@ -1051,10 +1088,10 @@ impl ListingCompiler {
Ok(())
}
fn process_and_commit_decl<R: Read>(
fn process_and_commit_decl(
&mut self,
decl: Declaration,
worker: &mut TopLevelBatchWorker<R>,
worker: &mut TopLevelBatchWorker,
indices: &mut IndexStore,
flags: MachineFlags,
) -> Result<(), SessionError> {
@@ -1067,9 +1104,9 @@ impl ListingCompiler {
.entry((name.clone(), arity))
.or_insert(vec![]);
indices.code_dir
.entry((name.clone(), arity))
.or_insert(CodeIndex::dynamic_undefined(self.get_module_name()));
indices.code_dir
.entry((name.clone(), arity))
.or_insert(CodeIndex::dynamic_undefined(self.get_module_name()));
}
&Declaration::Hook(hook, _, ref queue) if self.module.is_none() => worker
.term_stream
@@ -1101,15 +1138,16 @@ impl ListingCompiler {
result
}
pub(crate) fn gather_items<R: Read>(
pub(crate)
fn gather_items(
&mut self,
wam: &mut Machine,
mut src: ParsingStream<R>,
src: &mut ParsingStream<Stream>,
indices: &mut IndexStore,
) -> Result<GatherResult, SessionError> {
let flags = wam.machine_flags();
let atom_tbl = indices.atom_tbl.clone();
let mut worker = TopLevelBatchWorker::new(&mut src, atom_tbl.clone(), flags, wam);
let mut worker = TopLevelBatchWorker::new(src, atom_tbl.clone(), flags, wam);
let mut toplevel_results = vec![];
let mut toplevel_indices = default_index_store!(atom_tbl.clone());
@@ -1246,6 +1284,17 @@ fn compile_work_impl(
.filter(|(name, _)| name.owning_module().as_str() != "builtins")
.map(ModuleExport::PredicateKey)
.collect();
module.module_decl.exports.extend(
indices.op_dir.iter()
.map(|((name, _), OpDirValue (shared_op_desc, _))|
ModuleExport::OpDecl(OpDecl(
shared_op_desc.prec(),
shared_op_desc.assoc(),
name.clone(),
))
)
);
}
let mut clause_code_generator =
@@ -1286,7 +1335,7 @@ fn compile_work_impl(
let init_goal_code = compiler.generate_init_goal_code()?;
if init_goal_code.len() > 0 {
if !wam.run_init_code(init_goal_code) {
if !wam.run_init_code(init_goal_code) {
println!("Warning: initialization goal for {} failed",
compiler.listing_src.name());
}
@@ -1302,30 +1351,35 @@ fn compile_work_impl(
Ok(())
}
fn compile_work<R: Read>(
fn compile_work(
compiler: &mut ListingCompiler,
wam: &mut Machine,
src: ParsingStream<R>,
src: Stream,
mut indices: IndexStore,
) -> EvalSession {
let mut stream = try_eval_session!(parsing_stream(src));
let src = &mut stream;
let results = try_eval_session!(compiler.gather_items(wam, src, &mut indices));
try_eval_session!(compile_work_impl(compiler, wam, indices, results));
EvalSession::EntrySuccess
}
/* This is a truncated version of compile_user_module, used for
compiling code composing special forms, ie. the code that calls
M:verify_attributes on attributed variables. */
pub fn compile_special_form<R: Read>(
pub fn compile_special_form(
wam: &mut Machine,
src: ParsingStream<R>,
src: Stream,
listing_src: ListingSource,
) -> Result<usize, SessionError> {
let mut indices = default_index_store!(wam.indices.atom_tbl.clone());
setup_indices(wam, clause_name!("builtins"), &mut indices)?;
let mut src = parsing_stream(src)?;
let mut compiler = ListingCompiler::new(&wam.code_repo, true, listing_src);
let mut results = compiler.gather_items(wam, src, &mut indices)?;
let mut results = compiler.gather_items(wam, &mut src, &mut indices)?;
compiler.adapt_in_situ_code(
results.worker_results,
@@ -1348,9 +1402,9 @@ pub fn compile_special_form<R: Read>(
}
#[inline]
pub fn compile_listing<R: Read>(
pub fn compile_listing(
wam: &mut Machine,
src: ParsingStream<R>,
src: Stream,
indices: IndexStore,
suppress_warnings: bool,
listing_src: ListingSource,
@@ -1373,20 +1427,24 @@ pub(super) fn setup_indices(
module: ClauseName,
indices: &mut IndexStore,
) -> Result<(), SessionError> {
if let Some(module) = wam.indices.take_module(module) {
if let Some(module) = wam.indices.take_module(module.clone()) {
let flags = wam.machine_flags();
let result = indices.use_module(&mut wam.code_repo, flags, &module);
wam.indices.insert_module(module);
result
} else {
Err(SessionError::ModuleNotFound)
let err = ExistenceError::ModuleSource(ModuleSource::Library(
module
));
Err(SessionError::ExistenceError(err))
}
}
pub fn compile_user_module<R: Read>(
pub fn compile_user_module(
wam: &mut Machine,
src: ParsingStream<R>,
src: Stream,
suppress_warnings: bool,
listing_src: ListingSource,
) -> EvalSession {

314
src/machine/copier.rs Normal file
View File

@@ -0,0 +1,314 @@
use crate::machine::machine_indices::*;
use crate::machine::stack::*;
use std::mem;
use std::ops::IndexMut;
type Trail = Vec<(Ref, HeapCellValue)>;
#[derive(Debug, Clone, Copy)]
pub enum AttrVarPolicy {
DeepCopy,
StripAttributes
}
pub(crate)
trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
fn deref(&self, val: Addr) -> Addr;
fn push(&mut self, val: HeapCellValue);
fn stack(&mut self) -> &mut Stack;
fn store(&self, val: Addr) -> Addr;
fn threshold(&self) -> usize;
}
pub(crate)
fn copy_term<T: CopierTarget>(target: T, addr: Addr, attr_var_policy: AttrVarPolicy) {
let mut copy_term_state = CopyTermState::new(target, attr_var_policy);
copy_term_state.copy_term_impl(addr);
}
#[derive(Debug)]
struct CopyTermState<T: CopierTarget> {
trail: Trail,
scan: usize,
old_h: usize,
target: T,
attr_var_policy: AttrVarPolicy,
}
impl<T: CopierTarget> CopyTermState<T> {
fn new(target: T, attr_var_policy: AttrVarPolicy) -> Self {
CopyTermState {
trail: Trail::new(),
scan: 0,
old_h: target.threshold(),
target,
attr_var_policy
}
}
#[inline]
fn value_at_scan(&mut self) -> &mut HeapCellValue {
let scan = self.scan;
&mut self.target[scan]
}
fn trail_list_cell(&mut self, addr: usize, threshold: usize) {
let trail_item = mem::replace(
&mut self.target[addr],
HeapCellValue::Addr(Addr::Lis(threshold)),
);
self.trail.push((
Ref::HeapCell(addr),
trail_item,
));
}
fn copy_list(&mut self, addr: usize) {
for offset in 0 .. 2 {
if let Addr::Lis(h) = self.target[addr + offset].as_addr(addr + offset) {
if h >= self.old_h {
*self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(h));
self.scan += 1;
return;
}
}
}
let threshold = self.target.threshold();
*self.value_at_scan() = HeapCellValue::Addr(Addr::Lis(threshold));
for i in 0 .. 2 {
let hcv = self.target[addr + i].context_free_clone();
self.target.push(hcv);
}
let cdr = self.target.store(self.target.deref(Addr::HeapCell(addr + 1)));
if !cdr.is_ref() {
self.trail_list_cell(addr + 1, threshold);
} else {
let car = self.target.store(self.target.deref(Addr::HeapCell(addr)));
if !car.is_ref() {
self.trail_list_cell(addr, threshold);
}
}
self.scan += 1;
}
fn copy_partial_string(&mut self, addr: usize, n: usize) {
if let &HeapCellValue::Addr(Addr::PStrLocation(h, _)) = &self.target[addr] {
if h >= self.old_h {
*self.value_at_scan() = HeapCellValue::Addr(Addr::PStrLocation(h, n));
self.scan += 1;
return;
}
}
let threshold = self.target.threshold();
*self.value_at_scan() =
HeapCellValue::Addr(Addr::PStrLocation(threshold, n));
self.scan += 1;
let (pstr, has_tail) =
match &self.target[addr] {
&HeapCellValue::PartialString(ref pstr, has_tail) => {
(pstr.clone_from_offset(0), has_tail)
}
_ => {
unreachable!()
}
};
self.target.push(HeapCellValue::PartialString(pstr, has_tail));
let replacement = HeapCellValue::Addr(Addr::PStrLocation(threshold, n));
let trail_item = mem::replace(
&mut self.target[addr],
replacement,
);
self.trail.push((
Ref::HeapCell(addr),
trail_item,
));
if has_tail {
let tail_addr = self.target[addr + 1].as_addr(addr + 1);
self.target.push(HeapCellValue::Addr(tail_addr));
}
}
fn reinstantiate_var(&mut self, addr: Addr, frontier: usize) {
match addr {
Addr::HeapCell(h) => {
self.target[frontier] = HeapCellValue::Addr(Addr::HeapCell(frontier));
self.target[h] = HeapCellValue::Addr(Addr::HeapCell(frontier));
self.trail.push((
Ref::HeapCell(h),
HeapCellValue::Addr(Addr::HeapCell(h)),
));
}
Addr::StackCell(fr, sc) => {
self.target[frontier] = HeapCellValue::Addr(Addr::HeapCell(frontier));
self.target.stack().index_and_frame_mut(fr)[sc] = Addr::HeapCell(frontier);
self.trail.push((
Ref::StackCell(fr, sc),
HeapCellValue::Addr(Addr::StackCell(fr, sc)),
));
}
Addr::AttrVar(h) => {
let threshold = if let AttrVarPolicy::DeepCopy = self.attr_var_policy {
self.target.threshold()
} else {
frontier
};
self.target[frontier] = HeapCellValue::Addr(Addr::HeapCell(threshold));
self.target[h] = HeapCellValue::Addr(Addr::HeapCell(threshold));
self.trail.push((
Ref::AttrVar(h),
HeapCellValue::Addr(Addr::AttrVar(h)),
));
if let AttrVarPolicy::DeepCopy = self.attr_var_policy {
self.target.push(HeapCellValue::Addr(Addr::AttrVar(threshold)));
let list_val = self.target[h + 1].context_free_clone();
self.target.push(list_val);
}
}
_ => {
unreachable!()
}
}
}
fn copy_var(&mut self, addr: Addr) {
let rd = self.target.store(self.target.deref(addr));
match rd {
Addr::AttrVar(h) | Addr::HeapCell(h) if h >= self.old_h => {
*self.value_at_scan() = HeapCellValue::Addr(rd);
self.scan += 1;
}
_ if addr == rd => {
self.reinstantiate_var(addr, self.scan);
self.scan += 1;
}
_ => {
*self.value_at_scan() = HeapCellValue::Addr(rd);
}
}
}
fn copy_structure(&mut self, addr: usize) {
match self.target[addr].context_free_clone() {
HeapCellValue::NamedStr(arity, name, fixity) => {
let threshold = self.target.threshold();
*self.value_at_scan() = HeapCellValue::Addr(Addr::Str(threshold));
let trail_item = mem::replace(
&mut self.target[addr],
HeapCellValue::Addr(Addr::Str(threshold)),
);
self.trail.push((
Ref::HeapCell(addr),
trail_item,
));
self.target.push(HeapCellValue::NamedStr(arity, name, fixity));
for i in 0..arity {
let hcv = self.target[addr + 1 + i].context_free_clone();
self.target.push(hcv);
}
}
HeapCellValue::Addr(Addr::Str(addr)) => {
*self.value_at_scan() = HeapCellValue::Addr(Addr::Str(addr))
}
_ => {
unreachable!()
}
}
self.scan += 1;
}
fn copy_term_impl(&mut self, addr: Addr) {
self.scan = self.target.threshold();
self.target.push(HeapCellValue::Addr(addr));
while self.scan < self.target.threshold() {
match self.value_at_scan() {
&mut HeapCellValue::Addr(addr) => {
match addr {
Addr::Con(h) => {
let addr = self.target[h].as_addr(h);
if addr == Addr::Con(h) {
*self.value_at_scan() = self.target[h].context_free_clone();
} else {
*self.value_at_scan() = HeapCellValue::Addr(addr);
}
}
Addr::Lis(h) => {
if h >= self.old_h {
self.scan += 1;
} else {
self.copy_list(h);
}
}
addr @ Addr::AttrVar(_) |
addr @ Addr::HeapCell(_) |
addr @ Addr::StackCell(..) => {
self.copy_var(addr);
}
Addr::Str(addr) => {
self.copy_structure(addr);
}
Addr::PStrLocation(addr, n) => {
self.copy_partial_string(addr, n);
}
Addr::Stream(h) => {
*self.value_at_scan() = self.target[h].context_free_clone();
}
_ => {
self.scan += 1;
}
}
}
_ => {
self.scan += 1;
}
}
}
self.unwind_trail();
}
fn unwind_trail(&mut self) {
for (r, value) in self.trail.drain(0..) {
match r {
Ref::AttrVar(h) | Ref::HeapCell(h) =>
self.target[h] = value,
Ref::StackCell(fr, sc) =>
self.target.stack().index_and_frame_mut(fr)[sc] = value.as_addr(0),
}
}
}
}

View File

@@ -1,11 +1,12 @@
use prolog_parser::ast::*;
use crate::prolog_parser::ast::*;
use crate::prolog::heap_print::*;
use crate::prolog::machine::compile::*;
use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::*;
use crate::heap_print::*;
use crate::machine::*;
use crate::machine::compile::*;
use crate::machine::machine_errors::*;
use crate::machine::streams::*;
use std::io::Read;
use std::convert::TryFrom;
impl Machine {
pub(super) fn atom_tbl_of(&self, name: &ClauseName) -> TabledData<Atom> {
@@ -15,9 +16,9 @@ impl Machine {
}
}
fn compile_into_machine<R: Read>(
fn compile_into_machine(
&mut self,
src: ParsingStream<R>,
src: Stream,
name: ClauseName,
arity: usize,
) -> EvalSession {
@@ -42,13 +43,38 @@ impl Machine {
let arity = self.machine_st[arity].clone();
let name = match self.machine_st.store(self.machine_st.deref(name)) {
Addr::Con(Constant::Atom(name, _)) => name,
Addr::Con(h) =>
if let HeapCellValue::Atom(ref name, _) = &self.machine_st.heap[h] {
name.clone()
} else {
unreachable!()
},
_ => unreachable!(),
};
let arity = match self.machine_st.store(self.machine_st.deref(arity)) {
Addr::Con(Constant::Integer(arity)) => arity.to_usize().unwrap(),
_ => unreachable!(),
Addr::Con(h) => {
match &self.machine_st.heap[h] {
HeapCellValue::Integer(ref arity) => {
arity.to_usize().unwrap()
}
HeapCellValue::Addr(Addr::Fixnum(arity)) => {
usize::try_from(*arity).unwrap()
}
_ => {
unreachable!()
}
}
}
Addr::Fixnum(arity) => {
usize::try_from(arity).unwrap()
}
Addr::Usize(n) => {
n
}
_ => {
unreachable!()
}
};
(name, arity)
@@ -75,6 +101,18 @@ impl Machine {
}
fn make_undefined(&mut self, name: ClauseName, arity: usize) {
let module_name = name.owning_module();
match self.indices.modules.get(&module_name) {
Some(ref module) => {
if let Some(idx) = module.code_dir.get(&(name.clone(), arity)) {
set_code_index!(idx, IndexPtr::DynamicUndefined, module_name);
}
}
None => {
}
}
if let Some(idx) = self.indices.code_dir.get(&(name, arity)) {
set_code_index!(idx, IndexPtr::DynamicUndefined, clause_name!("user"));
}
@@ -87,7 +125,7 @@ impl Machine {
}
}
}
fn abolish_dynamic_clause(&mut self, name: RegType, arity: RegType) {
let (name, arity) = self.get_predicate_key(name, arity);
@@ -102,16 +140,21 @@ impl Machine {
let module_addr = self.machine_st[module].clone();
let module_name = match self.machine_st.store(self.machine_st.deref(module_addr)) {
Addr::Con(Constant::Atom(module, _)) => match self.indices.modules.get_mut(&module) {
Some(ref mut module) => {
module.code_dir.remove(&(name.clone(), arity));
module.module_decl.name.clone()
}
_ => {
self.machine_st.fail = true;
return;
}
},
Addr::Con(h) =>
if let HeapCellValue::Atom(ref module, _) = &self.machine_st.heap[h] {
match self.indices.modules.get_mut(module) {
Some(ref mut module) => {
module.code_dir.remove(&(name.clone(), arity));
module.module_decl.name.clone()
}
_ => {
self.machine_st.fail = true;
return;
}
}
} else {
unreachable!()
},
_ => unreachable!(),
};
@@ -130,7 +173,12 @@ impl Machine {
) {
let machine_st = mem::replace(&mut self.machine_st, MachineState::new());
let result = self.compile_into_machine(parsing_stream(pred_str.as_bytes()), name, arity);
let result = self.compile_into_machine(
Stream::from(pred_str),
name,
arity,
);
self.machine_st = machine_st;
if let EvalSession::Error(err) = result {
@@ -158,21 +206,34 @@ impl Machine {
place.push_to_queue(&mut addrs, added_clause);
self.print_new_dynamic_clause(addrs, name.clone(), arity)
}
Err(err) => return self.machine_st.throw_exception(err),
Err(err) => {
return self.machine_st.throw_exception(err);
}
};
self.handle_eval_result_from_dynamic_compile(pred_str, name, arity, place.predicate_name());
self.handle_eval_result_from_dynamic_compile(
pred_str,
name,
arity,
place.predicate_name(),
);
}
fn set_module_atom_tbl(&mut self, module_addr: Addr, name: &mut ClauseName) -> bool {
let atom_tbl = match self.machine_st.store(self.machine_st.deref(module_addr)) {
Addr::Con(Constant::Atom(module, _)) => match self.indices.modules.get(&module) {
Some(ref module) => module.atom_tbl.clone(),
None => {
Addr::Con(h) =>
if let HeapCellValue::Atom(ref module, _) = &self.machine_st.heap[h] {
match self.indices.modules.get(module) {
Some(ref module) => module.atom_tbl.clone(),
None => {
self.machine_st.fail = true;
return false;
}
}
} else {
self.machine_st.fail = true;
return false;
}
},
},
_ => unreachable!(),
};
@@ -200,8 +261,24 @@ impl Machine {
fn retract_from_dynamic_predicate_in_module(&mut self) {
let index = self.machine_st[temp_v!(3)].clone();
let index = match self.machine_st.store(self.machine_st.deref(index)) {
Addr::Con(Constant::Integer(n)) => n.to_usize().unwrap(),
_ => unreachable!(),
Addr::Con(h) =>
match &self.machine_st.heap[h] {
HeapCellValue::Integer(ref arity) => {
arity.to_usize().unwrap()
}
HeapCellValue::Addr(Addr::Fixnum(arity)) => {
usize::try_from(*arity).unwrap()
}
_ => {
unreachable!()
}
}
Addr::Fixnum(arity) => {
usize::try_from(arity).unwrap()
}
_ => {
unreachable!()
}
};
let (mut name, arity) = self.get_predicate_key(temp_v!(1), temp_v!(2));
@@ -217,10 +294,12 @@ impl Machine {
if addrs.is_empty() {
self.make_undefined(name.clone(), arity);
}
self.print_new_dynamic_clause(addrs, name.clone(), arity)
}
Err(err) => return self.machine_st.throw_exception(err),
Err(err) => {
return self.machine_st.throw_exception(err);
}
};
self.handle_eval_result_from_dynamic_compile(
@@ -235,8 +314,28 @@ impl Machine {
fn retract_from_dynamic_predicate(&mut self) {
let index = self.machine_st[temp_v!(3)].clone();
let index = match self.machine_st.store(self.machine_st.deref(index)) {
Addr::Con(Constant::Integer(n)) => n.to_usize().unwrap(),
_ => unreachable!(),
Addr::Con(h) => {
match &self.machine_st.heap[h] {
HeapCellValue::Integer(ref arity) => {
arity.to_usize().unwrap()
}
HeapCellValue::Addr(Addr::Fixnum(arity)) => {
usize::try_from(*arity).unwrap()
}
_ => {
unreachable!()
}
}
}
Addr::Usize(n) => {
n
}
Addr::Fixnum(n) => {
usize::try_from(n).unwrap()
}
_ => {
unreachable!()
}
};
let (name, arity) = self.get_predicate_key(temp_v!(1), temp_v!(2));
@@ -250,10 +349,12 @@ impl Machine {
if addrs.is_empty() {
self.make_undefined(name.clone(), arity);
}
self.print_new_dynamic_clause(addrs, name.clone(), arity)
}
Err(err) => return self.machine_st.throw_exception(err),
Err(err) => {
return self.machine_st.throw_exception(err);
}
};
self.handle_eval_result_from_dynamic_compile(

View File

@@ -1,15 +1,17 @@
use core::marker::PhantomData;
use crate::prolog_parser::ast::*;
use crate::prolog_parser::ast::Constant;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::machine::partial_string::*;
use crate::prolog::machine::raw_block::*;
use crate::machine::machine_indices::*;
use crate::machine::partial_string::*;
use crate::machine::raw_block::*;
use std::convert::TryFrom;
use std::mem;
use std::ops::{Index, IndexMut};
use std::ptr;
#[derive(Debug)]
pub(crate) struct StandardHeapTraits {}
impl RawBlockTraits for StandardHeapTraits {
@@ -24,6 +26,7 @@ impl RawBlockTraits for StandardHeapTraits {
}
}
#[derive(Debug)]
pub(crate) struct HeapTemplate<T: RawBlockTraits> {
buf: RawBlock<T>,
_marker: PhantomData<HeapCellValue>,
@@ -38,13 +41,14 @@ impl<T: RawBlockTraits> Drop for HeapTemplate<T> {
}
}
#[derive(Debug)]
pub(crate)
struct HeapIntoIterator<T: RawBlockTraits> {
struct HeapIntoIter<T: RawBlockTraits> {
offset: usize,
buf: RawBlock<T>,
}
impl<T: RawBlockTraits> Drop for HeapIntoIterator<T> {
impl<T: RawBlockTraits> Drop for HeapIntoIter<T> {
fn drop(&mut self) {
let mut heap =
HeapTemplate { buf: self.buf.take(), _marker: PhantomData };
@@ -54,7 +58,7 @@ impl<T: RawBlockTraits> Drop for HeapIntoIterator<T> {
}
}
impl<T: RawBlockTraits> Iterator for HeapIntoIterator<T> {
impl<T: RawBlockTraits> Iterator for HeapIntoIter<T> {
type Item = HeapCellValue;
fn next(&mut self) -> Option<Self::Item> {
@@ -71,20 +75,21 @@ impl<T: RawBlockTraits> Iterator for HeapIntoIterator<T> {
}
}
#[derive(Debug)]
pub(crate)
struct HeapIterator<'a, T: RawBlockTraits> {
struct HeapIter<'a, T: RawBlockTraits> {
offset: usize,
buf: &'a RawBlock<T>,
}
impl<'a, T: RawBlockTraits> HeapIterator<'a, T> {
impl<'a, T: RawBlockTraits> HeapIter<'a, T> {
pub(crate)
fn new(buf: &'a RawBlock<T>, offset: usize) -> Self {
HeapIterator { buf, offset }
HeapIter { buf, offset }
}
}
impl<'a, T: RawBlockTraits> Iterator for HeapIterator<'a, T> {
impl<'a, T: RawBlockTraits> Iterator for HeapIter<'a, T> {
type Item = &'a HeapCellValue;
fn next(&mut self) -> Option<Self::Item> {
@@ -101,20 +106,29 @@ impl<'a, T: RawBlockTraits> Iterator for HeapIterator<'a, T> {
}
}
#[allow(dead_code)]
pub(crate)
struct HeapIteratorMut<'a, T: RawBlockTraits> {
fn print_heap_terms<'a, I: Iterator<Item = &'a HeapCellValue>>(heap: I, h: usize) {
for (index, term) in heap.enumerate() {
println!("{} : {}", h + index, term);
}
}
#[derive(Debug)]
pub(crate)
struct HeapIterMut<'a, T: RawBlockTraits> {
offset: usize,
buf: &'a mut RawBlock<T>,
}
impl<'a, T: RawBlockTraits> HeapIteratorMut<'a, T> {
impl<'a, T: RawBlockTraits> HeapIterMut<'a, T> {
pub(crate)
fn new(buf: &'a mut RawBlock<T>, offset: usize) -> Self {
HeapIteratorMut { buf, offset }
HeapIterMut { buf, offset }
}
}
impl<'a, T: RawBlockTraits> Iterator for HeapIteratorMut<'a, T> {
impl<'a, T: RawBlockTraits> Iterator for HeapIterMut<'a, T> {
type Item = &'a mut HeapCellValue;
fn next(&mut self) -> Option<Self::Item> {
@@ -140,44 +154,195 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
#[inline]
pub(crate)
fn push(&mut self, val: HeapCellValue) {
unsafe {
let new_top = self.buf.new_block(mem::size_of::<HeapCellValue>());
ptr::write(self.buf.top as *mut _, val);
self.buf.top = new_top;
fn clone(&self, h: usize) -> HeapCellValue {
match &self[h] {
&HeapCellValue::Addr(addr) => {
HeapCellValue::Addr(addr)
}
&HeapCellValue::Atom(ref name, ref op) => {
HeapCellValue::Atom(name.clone(), op.clone())
}
&HeapCellValue::DBRef(ref db_ref) => {
HeapCellValue::DBRef(db_ref.clone())
}
&HeapCellValue::Integer(ref n) => {
HeapCellValue::Integer(n.clone())
}
&HeapCellValue::NamedStr(arity, ref name, ref op) => {
HeapCellValue::NamedStr(arity, name.clone(), op.clone())
}
&HeapCellValue::PartialString(..) => {
HeapCellValue::Addr(Addr::PStrLocation(h, 0))
}
&HeapCellValue::Rational(ref r) => {
HeapCellValue::Rational(r.clone())
}
&HeapCellValue::Stream(_) => {
HeapCellValue::Addr(Addr::Stream(h))
}
&HeapCellValue::TcpListener(_) => {
HeapCellValue::Addr(Addr::TcpListener(h))
}
}
}
#[inline]
pub(crate)
fn allocate_pstr(&mut self, mut src: &str) -> Option<Addr> {
fn put_complete_string(&mut self, s: &str) -> Addr {
if s.is_empty() {
return Addr::EmptyList;
}
let addr = self.allocate_pstr(s);
self.pop();
let h = self.h();
match &mut self[h - 1] {
&mut HeapCellValue::PartialString(_, ref mut has_tail) => {
*has_tail = false;
}
_ => {
unreachable!()
}
}
addr
}
#[inline]
pub(crate)
fn put_constant(&mut self, c: Constant) -> Addr {
match c {
Constant::Atom(name, op) => {
Addr::Con(self.push(HeapCellValue::Atom(name, op)))
}
Constant::Char(c) => {
Addr::Char(c)
}
Constant::EmptyList => {
Addr::EmptyList
}
Constant::Fixnum(n) => {
Addr::Fixnum(n)
}
Constant::Integer(n) => {
Addr::Con(self.push(HeapCellValue::Integer(n)))
}
Constant::Rational(r) => {
Addr::Con(self.push(HeapCellValue::Rational(r)))
}
Constant::Float(f) => {
Addr::Float(f)
}
Constant::String(s) => {
if s.is_empty() {
Addr::EmptyList
} else {
self.put_complete_string(&s)
}
}
Constant::Usize(n) => {
Addr::Usize(n)
}
}
}
#[inline]
pub(crate)
fn pop(&mut self) {
let h = self.h();
if h > 0 {
self.truncate(h - 1);
}
}
#[inline]
pub(crate)
fn push(&mut self, val: HeapCellValue) -> usize {
let h = self.h();
unsafe {
let new_top = self.buf.new_block(mem::size_of::<HeapCellValue>());
ptr::write(self.buf.top as *mut _, val);
self.buf.top = new_top;
}
h
}
#[inline]
pub(crate)
fn atom_at(&self, h: usize) -> bool {
if let HeapCellValue::Atom(..) = &self[h] {
true
} else {
false
}
}
#[inline]
pub(crate)
fn to_unifiable(&mut self, non_heap_value: HeapCellValue) -> Addr {
match non_heap_value {
HeapCellValue::Addr(addr) => {
addr
}
val @ HeapCellValue::Atom(..) |
val @ HeapCellValue::Integer(_) |
val @ HeapCellValue::DBRef(_) |
val @ HeapCellValue::Rational(_) => {
Addr::Con(self.push(val))
}
val @ HeapCellValue::NamedStr(..) => {
Addr::Str(self.push(val))
}
HeapCellValue::PartialString(pstr, has_tail) => {
let h = self.push(HeapCellValue::PartialString(pstr, has_tail));
if has_tail {
self.push(HeapCellValue::Addr(Addr::EmptyList));
}
Addr::Con(h)
}
val @ HeapCellValue::Stream(..) => {
Addr::Stream(self.push(val))
}
val @ HeapCellValue::TcpListener(..) => {
Addr::TcpListener(self.push(val))
}
}
}
#[inline]
pub(crate)
fn allocate_pstr(&mut self, src: &str) -> Addr {
self.write_pstr(src)
.unwrap_or_else(|| Addr::EmptyList)
}
#[inline]
fn write_pstr(&mut self, mut src: &str) -> Option<Addr> {
let orig_h = self.h();
loop {
if src == "" {
return if orig_h == self.h() {
None
} else {
let prev_h = self.h() - 1;
match &mut self[prev_h] {
HeapCellValue::PartialString(ref mut pstr) => {
let s = pstr.block_as_str();
pstr.tail = Addr::PStrTail(prev_h, s.len());
}
_ => {
unreachable!()
}
}
let tail_h = self.h() - 1;
self[tail_h] = HeapCellValue::Addr(Addr::HeapCell(tail_h));
Some(Addr::PStrLocation(orig_h, 0))
};
}
let h = self.h();
let (mut pstr, rest_src) =
match PartialString::new(src, h) {
let (pstr, rest_src) =
match PartialString::new(src) {
Some(tuple) => {
tuple
}
@@ -188,46 +353,21 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
} else if orig_h == h {
return None;
} else {
let prev_h = h - 1;
match &mut self[prev_h] {
HeapCellValue::PartialString(ref mut pstr) => {
let s = pstr.block_as_str();
pstr.tail = Addr::PStrTail(prev_h, s.len());
}
_ => {
unreachable!()
}
}
self[h - 1] = HeapCellValue::Addr(Addr::HeapCell(h - 1));
return Some(Addr::PStrLocation(orig_h, 0));
}
}
};
let new_top = unsafe {
self.buf.new_block(mem::size_of::<HeapCellValue>())
};
self.push(HeapCellValue::PartialString(pstr, true));
if rest_src != "" {
pstr.tail = Addr::PStrLocation(h+1, 0);
self.push(HeapCellValue::Addr(Addr::PStrLocation(h + 2, 0)));
src = rest_src;
} else {
pstr.tail = Addr::PStrTail(h, src.len());
}
unsafe{
ptr::write(
self.buf.top as *mut _,
HeapCellValue::PartialString(pstr),
);
}
self.buf.top = new_top;
if rest_src == "" {
self.push(HeapCellValue::Addr(Addr::HeapCell(h + 1)));
return Some(Addr::PStrLocation(orig_h, 0));
}
}
}
}
@@ -279,35 +419,39 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
}
pub(crate)
fn to_list<Iter: Iterator<Item = Addr>>(&mut self, values: Iter) -> usize {
fn to_list<Iter, SrcT>(&mut self, values: Iter) -> usize
where Iter: Iterator<Item = SrcT>,
SrcT: Into<HeapCellValue>
{
let head_addr = self.h();
let mut h = head_addr;
for value in values {
let h = self.h();
for value in values.map(|v| v.into()) {
self.push(HeapCellValue::Addr(Addr::Lis(h + 1)));
self.push(HeapCellValue::Addr(value));
self.push(value);
h += 2;
}
self.push(HeapCellValue::Addr(Addr::Con(Constant::EmptyList)));
self.push(HeapCellValue::Addr(Addr::EmptyList));
head_addr
}
/* Create an iterator starting from the passed offset. */
pub(crate)
fn iter_from<'a>(&'a self, offset: usize) -> HeapIterator<'a, T> {
HeapIterator::new(&self.buf, offset * mem::size_of::<HeapCellValue>())
fn iter_from<'a>(&'a self, offset: usize) -> HeapIter<'a, T> {
HeapIter::new(&self.buf, offset * mem::size_of::<HeapCellValue>())
}
pub(crate)
fn iter_mut_from<'a>(&'a mut self, offset: usize) -> HeapIteratorMut<'a, T> {
HeapIteratorMut::new(&mut self.buf, offset * mem::size_of::<HeapCellValue>())
fn iter_mut_from<'a>(&'a mut self, offset: usize) -> HeapIterMut<'a, T> {
HeapIterMut::new(&mut self.buf, offset * mem::size_of::<HeapCellValue>())
}
pub(crate)
fn into_iter(mut self) -> HeapIntoIterator<T> {
HeapIntoIterator { buf: self.buf.take(), offset: 0 }
fn into_iter(mut self) -> HeapIntoIter<T> {
HeapIntoIter { buf: self.buf.take(), offset: 0 }
}
pub(crate)
@@ -320,8 +464,9 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
pub(crate)
fn to_local_code_ptr(&self, addr: &Addr) -> Option<LocalCodePtr> {
let extract_integer = |s: usize| -> Option<usize> {
match self[s].as_addr(s) {
Addr::Con(Constant::Integer(n)) => n.to_usize(),
match &self[s] {
&HeapCellValue::Addr(Addr::Fixnum(n)) => usize::try_from(n).ok(),
&HeapCellValue::Integer(ref n) => n.to_usize(),
_ => None
}
};
@@ -361,6 +506,19 @@ impl<T: RawBlockTraits> HeapTemplate<T> {
_ => None
}
}
#[inline]
pub
fn index_addr<'a>(&'a self, addr: &Addr) -> RefOrOwned<'a, HeapCellValue> {
match addr {
&Addr::Con(h) | &Addr::Str(h) | &Addr::Stream(h) | &Addr::TcpListener(h) => {
RefOrOwned::Borrowed(&self[h])
}
addr => {
RefOrOwned::Owned(HeapCellValue::Addr(*addr))
}
}
}
}
impl<T: RawBlockTraits> Index<usize> for HeapTemplate<T> {

View File

@@ -0,0 +1,843 @@
use crate::prolog_parser::ast::*;
use crate::forms::{ModuleSource, Number, PredicateKey};
use crate::machine::heap::*;
use crate::machine::machine_indices::*;
use crate::machine::machine_state::*;
use crate::rug::Integer;
use std::rc::Rc;
pub(crate) type MachineStub = Vec<HeapCellValue>;
#[derive(Debug, Clone, Copy)]
enum ErrorProvenance {
Constructed, // if constructed, offset the addresses.
Received, // otherwise, preserve the addresses.
}
#[derive(Debug)]
pub(crate) struct MachineError {
stub: MachineStub,
location: Option<(usize, usize)>, // line_num, col_num
from: ErrorProvenance,
}
pub(crate)
trait TypeError {
fn type_error(self, h: usize, valid_type: ValidType) -> MachineError;
}
impl TypeError for Addr {
fn type_error(self, _: usize, valid_type: ValidType) -> MachineError {
let stub = functor!(
"type_error",
[atom(valid_type.as_str()), addr(self)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received
}
}
}
impl TypeError for HeapCellValue {
fn type_error(self, _: usize, valid_type: ValidType) -> MachineError {
let stub = functor!(
"type_error",
[atom(valid_type.as_str()), value(self)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received
}
}
}
impl TypeError for MachineStub {
fn type_error(self, h: usize, valid_type: ValidType) -> MachineError {
let stub = functor!(
"type_error",
[atom(valid_type.as_str()), aux(h, 0)],
[self]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Constructed
}
}
}
impl TypeError for Number {
fn type_error(self, _h: usize, valid_type: ValidType) -> MachineError {
let stub = functor!(
"type_error",
[atom(valid_type.as_str()), number(self)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received
}
}
}
pub(crate)
trait PermissionError {
fn permission_error(self, h: usize, index_str: &'static str, perm: Permission) -> MachineError;
}
impl PermissionError for Addr {
fn permission_error(self, _: usize, index_str: &'static str, perm: Permission) -> MachineError {
let stub = functor!(
"permission_error",
[atom(perm.as_str()), atom(index_str), addr(self)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received
}
}
}
impl PermissionError for MachineStub {
fn permission_error(self, h: usize, index_str: &'static str, perm: Permission) -> MachineError {
let stub = functor!(
"permission_error",
[atom(perm.as_str()), atom(index_str), aux(h, 0)],
[self]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Constructed
}
}
}
pub(super)
trait DomainError {
fn domain_error(self, error: DomainErrorType) -> MachineError;
}
impl DomainError for Addr {
fn domain_error(self, error: DomainErrorType) -> MachineError {
let stub = functor!(
"domain_error",
[atom(error.as_str()), addr(self)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
}
impl DomainError for Number {
fn domain_error(self, error: DomainErrorType) -> MachineError {
let stub = functor!(
"domain_error",
[atom(error.as_str()), number(self)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
}
impl MachineError {
pub(super)
fn functor_stub(name: ClauseName, arity: usize) -> MachineStub {
functor!(
"/",
SharedOpDesc::new(400, YFX),
[clause_name(name), integer(arity)]
)
}
#[inline]
pub(super)
fn interrupt_error() -> Self {
let stub = functor!("$interrupt_thrown");
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
pub(super)
fn evaluation_error(eval_error: EvalError) -> Self {
let stub = functor!("evaluation_error", [atom(eval_error.as_str())]);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
pub(super)
fn type_error<T: TypeError>(h: usize, valid_type: ValidType, culprit: T) -> Self {
culprit.type_error(h, valid_type)
}
pub(super)
fn module_resolution_error(
h: usize,
mod_name: ClauseName,
name: ClauseName,
arity: usize,
) -> Self {
let res_stub = functor!(
":",
SharedOpDesc::new(600, XFY),
[clause_name(mod_name), clause_name(name)]
);
let ind_stub = functor!(
"/",
SharedOpDesc::new(400, YFX),
[aux(h + 2, 0), integer(arity)],
[res_stub]
);
let stub = functor!(
"evaluation_error",
[aux(h, 0)],
[ind_stub]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Constructed,
}
}
pub(super)
fn existence_error(h: usize, err: ExistenceError) -> Self {
match err {
ExistenceError::Module(name) => {
let stub = functor!(
"existence_error",
[atom("source_sink"), clause_name(name)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
ExistenceError::Procedure(name, arity) => {
let culprit = functor!(
"/",
SharedOpDesc::new(400, YFX),
[clause_name(name), integer(arity)]
);
let stub = functor!(
"existence_error",
[atom("procedure"), aux(h, 0)],
[culprit]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Constructed,
}
}
ExistenceError::ModuleSource(source) => {
let source_stub = source.as_functor_stub();
let stub = functor!(
"existence_error",
[atom("source_sink"), aux(h, 0)],
[source_stub]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Constructed,
}
}
ExistenceError::SourceSink(culprit) => {
let stub = functor!(
"existence_error",
[atom("source_sink"), addr(culprit)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
ExistenceError::Stream(culprit) => {
let stub = functor!(
"existence_error",
[atom("stream"), addr(culprit)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
}
}
pub(super)
fn permission_error<T: PermissionError>(
h: usize,
err: Permission,
index_str: &'static str,
culprit: T,
) -> Self {
culprit.permission_error(
h,
index_str,
err,
)
}
fn arithmetic_error(h: usize, err: ArithmeticError) -> Self {
match err {
ArithmeticError::UninstantiatedVar => {
Self::instantiation_error()
}
ArithmeticError::NonEvaluableFunctor(name, arity) => {
let culprit = functor!(
"/",
SharedOpDesc::new(400, YFX),
[constant(h, &name), integer(arity)]
);
Self::type_error(h, ValidType::Evaluable, culprit)
}
}
}
#[inline]
pub(super)
fn domain_error<T: DomainError>(error: DomainErrorType, culprit: T) -> Self {
culprit.domain_error(error)
}
pub(super)
fn instantiation_error() -> Self {
let stub = functor!("instantiation_error");
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
pub(super)
fn uninstantiation_error(culprit: Addr) -> Self {
let stub = functor!(
"uninstantiation_error",
[addr(culprit)]
);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
pub(super)
fn session_error(h: usize, err: SessionError) -> Self {
match err {
SessionError::CannotOverwriteBuiltIn(pred_str) |
SessionError::CannotOverwriteImport(pred_str) => {
Self::permission_error(
h,
Permission::Modify,
"private_procedure",
functor!(clause_name(pred_str)),
)
}
SessionError::ExistenceError(err) => {
Self::existence_error(h, err)
}
SessionError::InvalidFileName(filename) => {
Self::existence_error(h, ExistenceError::Module(filename))
}
SessionError::ModuleDoesNotContainExport(..) => {
Self::permission_error(
h,
Permission::Access,
"private_procedure",
functor!("module_does_not_contain_claimed_export"),
)
}
SessionError::NamelessEntry => {
Self::permission_error(
h,
Permission::Create,
"static_procedure",
functor!("nameless_procedure")
)
}
SessionError::OpIsInfixAndPostFix(op) => {
Self::permission_error(
h,
Permission::Create,
"operator",
functor!(clause_name(op)),
)
}
SessionError::ParserError(err) => {
Self::syntax_error(h, err)
}
SessionError::QueryCannotBeDefinedAsFact => {
Self::permission_error(
h,
Permission::Create,
"static_procedure",
functor!("query_cannot_be_defined_as_fact")
)
}
}
}
pub(super)
fn syntax_error(h: usize, err: ParserError) -> Self {
if let ParserError::Arithmetic(err) = err {
return Self::arithmetic_error(h, err);
}
let location = err.line_and_col_num();
let stub = functor!(err.as_str());
let stub = functor!(
"syntax_error",
[aux(h, 0)],
[stub]
);
MachineError {
stub,
location,
from: ErrorProvenance::Constructed,
}
}
pub(super)
fn representation_error(flag: RepFlag) -> Self {
let stub = functor!("representation_error", [atom(flag.as_str())]);
MachineError {
stub,
location: None,
from: ErrorProvenance::Received,
}
}
fn into_iter(self, offset: usize) -> Box<dyn Iterator<Item = HeapCellValue>> {
match self.from {
ErrorProvenance::Constructed => {
Box::new(self.stub.into_iter().map(move |hcv| match hcv {
HeapCellValue::Addr(addr) => HeapCellValue::Addr(addr + offset),
hcv => hcv,
}))
}
ErrorProvenance::Received => Box::new(self.stub.into_iter()),
}
}
fn len(&self) -> usize {
self.stub.len()
}
}
#[derive(Debug, Clone, Copy)]
pub enum Permission {
Access,
Create,
InputStream,
Modify,
Open,
OutputStream,
Reposition,
}
impl Permission {
#[inline]
pub fn as_str(self) -> &'static str {
match self {
Permission::Access => "access",
Permission::Create => "create",
Permission::InputStream => "input",
Permission::Modify => "modify",
Permission::Open => "open",
Permission::OutputStream => "output",
Permission::Reposition => "reposition",
}
}
}
// from 7.12.2 b) of 13211-1:1995
#[derive(Debug, Clone, Copy)]
pub enum ValidType {
Atom,
Atomic,
// Boolean,
Byte,
Callable,
Character,
Compound,
Evaluable,
Float,
InByte,
InCharacter,
Integer,
List,
Number,
Pair,
// PredicateIndicator,
// Variable
TcpListener,
}
impl ValidType {
pub fn as_str(self) -> &'static str {
match self {
ValidType::Atom => "atom",
ValidType::Atomic => "atomic",
// ValidType::Boolean => "boolean",
ValidType::Byte => "byte",
ValidType::Callable => "callable",
ValidType::Character => "character",
ValidType::Compound => "compound",
ValidType::Evaluable => "evaluable",
ValidType::Float => "float",
ValidType::InByte => "in_byte",
ValidType::InCharacter => "in_character",
ValidType::Integer => "integer",
ValidType::List => "list",
ValidType::Number => "number",
ValidType::Pair => "pair",
// ValidType::PredicateIndicator => "predicate_indicator",
// ValidType::Variable => "variable"
ValidType::TcpListener => "tcp_listener",
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum DomainErrorType {
IOMode,
NotLessThanZero,
Order,
SourceSink,
Stream,
StreamOrAlias,
}
impl DomainErrorType {
pub fn as_str(self) -> &'static str {
match self {
DomainErrorType::IOMode => "io_mode",
DomainErrorType::NotLessThanZero => "not_less_than_zero",
DomainErrorType::Order => "order",
DomainErrorType::SourceSink => "source_sink",
DomainErrorType::Stream => "stream",
DomainErrorType::StreamOrAlias => "stream_or_alias",
}
}
}
// from 7.12.2 f) of 13211-1:1995
#[derive(Debug, Clone, Copy)]
pub enum RepFlag {
Character,
CharacterCode,
InCharacterCode,
MaxArity,
// MaxInteger,
// MinInteger
}
impl RepFlag {
pub fn as_str(self) -> &'static str {
match self {
RepFlag::Character => "character",
RepFlag::CharacterCode => "character_code",
RepFlag::InCharacterCode => "in_character_code",
RepFlag::MaxArity => "max_arity",
// RepFlag::MaxInteger => "max_integer",
// RepFlag::MinInteger => "min_integer"
}
}
}
// from 7.12.2 g) of 13211-1:1995
#[derive(Debug, Clone, Copy)]
pub enum EvalError {
FloatOverflow,
Undefined,
// Underflow,
ZeroDivisor,
}
impl EvalError {
pub fn as_str(self) -> &'static str {
match self {
EvalError::FloatOverflow => "float_overflow",
EvalError::Undefined => "undefined",
// EvalError::FloatUnderflow => "underflow",
EvalError::ZeroDivisor => "zero_divisor",
}
}
}
// used by '$skip_max_list'.
#[derive(Debug, Clone, Copy)]
pub(super) enum CycleSearchResult {
EmptyList,
NotList,
PartialList(usize, Ref), // the list length (up to max), and an offset into the heap.
ProperList(usize), // the list length.
PStrLocation(usize, usize, usize), // the list length (up to max), the heap offset, byte offset into the string.
UntouchedList(usize), // the address of an uniterated Addr::Lis(address).
}
impl MachineState {
// see 8.4.3 of Draft Technical Corrigendum 2.
pub(super)
fn check_sort_errors(&self) -> CallResult {
let stub = MachineError::functor_stub(clause_name!("sort"), 2);
let list = self.store(self.deref(self[temp_v!(1)].clone()));
let sorted = self.store(self.deref(self[temp_v!(2)].clone()));
match self.detect_cycles(list.clone()) {
CycleSearchResult::PartialList(..) => {
return Err(self.error_form(MachineError::instantiation_error(), stub))
}
CycleSearchResult::NotList => {
return Err(self.error_form(MachineError::type_error(0, ValidType::List, list), stub))
}
_ => {}
};
match self.detect_cycles(sorted.clone()) {
CycleSearchResult::NotList if !sorted.is_ref() => {
Err(self.error_form(MachineError::type_error(0, ValidType::List, sorted), stub))
}
_ => Ok(()),
}
}
fn check_for_list_pairs(&self, list: Addr) -> CallResult {
let stub = MachineError::functor_stub(clause_name!("keysort"), 2);
match self.detect_cycles(list.clone()) {
CycleSearchResult::NotList if !list.is_ref() => {
Err(self.error_form(MachineError::type_error(0, ValidType::List, list), stub))
}
_ => {
let mut addr = list;
while let Addr::Lis(l) = self.store(self.deref(addr)) {
let mut new_l = l;
loop {
match self.heap.clone(new_l) {
HeapCellValue::Addr(Addr::Str(l)) => {
new_l = l;
}
HeapCellValue::NamedStr(2, ref name, Some(_))
if name.as_str() == "-" => {
break;
}
HeapCellValue::Addr(Addr::HeapCell(_)) => {
break;
}
HeapCellValue::Addr(Addr::StackCell(..)) => {
break;
}
_ => {
return Err(self.error_form(
MachineError::type_error(0, ValidType::Pair, Addr::HeapCell(l)),
stub,
))
}
};
}
addr = Addr::HeapCell(l + 1);
}
Ok(())
}
}
}
// see 8.4.4 of Draft Technical Corrigendum 2.
pub(super)
fn check_keysort_errors(&self) -> CallResult {
let stub = MachineError::functor_stub(clause_name!("keysort"), 2);
let pairs = self.store(self.deref(self[temp_v!(1)].clone()));
let sorted = self.store(self.deref(self[temp_v!(2)].clone()));
match self.detect_cycles(pairs.clone()) {
CycleSearchResult::PartialList(..) => {
Err(self.error_form(MachineError::instantiation_error(), stub))
}
CycleSearchResult::NotList => {
Err(self.error_form(MachineError::type_error(0, ValidType::List, pairs), stub))
}
_ => Ok(()),
}?;
self.check_for_list_pairs(sorted)
}
#[inline]
pub(crate)
fn type_error<T: TypeError>(
&self,
valid_type: ValidType,
culprit: T,
caller: ClauseName,
arity: usize,
) -> MachineStub {
let stub = MachineError::functor_stub(caller, arity);
let err = MachineError::type_error(
self.heap.h(),
valid_type,
culprit,
);
return self.error_form(err, stub);
}
#[inline]
pub(crate)
fn representation_error(
&self,
rep_flag: RepFlag,
caller: ClauseName,
arity: usize,
) -> MachineStub {
let stub = MachineError::functor_stub(caller, arity);
let err = MachineError::representation_error(
rep_flag,
);
return self.error_form(err, stub);
}
pub(super)
fn error_form(&self, err: MachineError, src: MachineStub) -> MachineStub {
let location = err.location;
let err_len = err.len();
let h = self.heap.h();
let mut stub = vec![
HeapCellValue::NamedStr(2, clause_name!("error"), None),
HeapCellValue::Addr(Addr::HeapCell(h + 3)),
HeapCellValue::Addr(Addr::HeapCell(h + 3 + err_len)),
];
stub.extend(err.into_iter(3));
if let Some((line_num, _)) = location {
let colon_op_desc = Some(SharedOpDesc::new(600, XFY));
stub.push(HeapCellValue::NamedStr(2, clause_name!(":"), colon_op_desc));
stub.push(HeapCellValue::Addr(Addr::HeapCell(h + 6 + err_len)));
stub.push(HeapCellValue::Integer(Rc::new(Integer::from(line_num))));
}
stub.extend(src.into_iter());
stub
}
pub(super)
fn throw_exception(&mut self, err: MachineStub) {
let h = self.heap.h();
self.ball.boundary = 0;
self.ball.stub.truncate(0);
self.heap.append(err);
self.registers[1] = Addr::HeapCell(h);
self.set_ball();
self.unwind_stack();
}
}
#[derive(Debug)]
pub enum ExistenceError {
Module(ClauseName),
ModuleSource(ModuleSource),
Procedure(ClauseName, usize),
SourceSink(Addr),
Stream(Addr),
}
#[derive(Debug)]
pub enum SessionError {
CannotOverwriteBuiltIn(ClauseName),
CannotOverwriteImport(ClauseName),
ExistenceError(ExistenceError),
InvalidFileName(ClauseName),
ModuleDoesNotContainExport(ClauseName, PredicateKey),
NamelessEntry,
OpIsInfixAndPostFix(ClauseName),
QueryCannotBeDefinedAsFact,
ParserError(ParserError),
}
#[derive(Debug)]
pub enum EvalSession {
EntrySuccess,
Error(SessionError),
}
impl From<SessionError> for EvalSession {
fn from(err: SessionError) -> Self {
EvalSession::Error(err)
}
}
impl From<ParserError> for SessionError {
fn from(err: ParserError) -> Self {
SessionError::ParserError(err)
}
}
impl From<ParserError> for EvalSession {
fn from(err: ParserError) -> Self {
EvalSession::from(SessionError::ParserError(err))
}
}

View File

@@ -1,32 +1,38 @@
use prolog_parser::ast::*;
use prolog_parser::tabled_rc::*;
use crate::prolog_parser::ast::*;
use crate::prolog_parser::tabled_rc::*;
use crate::prolog::clause_types::*;
use crate::prolog::fixtures::*;
use crate::prolog::forms::*;
use crate::prolog::machine::code_repo::CodeRepo;
use crate::prolog::machine::Ball;
use crate::prolog::machine::heap::*;
use crate::prolog::machine::partial_string::*;
use crate::prolog::machine::raw_block::RawBlockTraits;
use crate::prolog::instructions::*;
use crate::prolog::rug::Integer;
use crate::clause_types::*;
use crate::fixtures::*;
use crate::forms::*;
use crate::machine::code_repo::CodeRepo;
use crate::machine::Ball;
use crate::machine::heap::*;
use crate::machine::machine_state::*;
use crate::machine::partial_string::*;
use crate::machine::raw_block::RawBlockTraits;
use crate::machine::streams::Stream;
use crate::instructions::*;
use crate::ordered_float::OrderedFloat;
use crate::rug::{Integer, Rational};
use indexmap::IndexMap;
use crate::indexmap::IndexMap;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::{BTreeMap, VecDeque};
use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::convert::TryFrom;
use std::fmt;
use std::mem;
use std::net::TcpListener;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::rc::Rc;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OrderedOpDirKey(pub ClauseName, pub Fixity);
pub type OssifiedOpDir = BTreeMap<OrderedOpDirKey, (usize, Specifier)>;
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum DBRef {
NamedPred(ClauseName, usize, Option<SharedOpDesc>),
Op(
@@ -38,25 +44,40 @@ pub enum DBRef {
),
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Addr {
AttrVar(usize),
Con(Constant),
DBRef(DBRef),
Lis(usize),
HeapCell(usize),
StackCell(usize, usize),
Str(usize),
PStrLocation(usize, usize), // location of pstr in heap, offset into string in bytes.
PStrTail(usize, usize), // location of pstr in heap, offset into string in bytes.
// 7.2
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum TermOrderCategory {
Variable,
FloatingPoint,
Integer,
Atom,
Compound,
}
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Addr {
AttrVar(usize),
Char(char),
Con(usize),
CutPoint(usize),
EmptyList,
Fixnum(isize),
Float(OrderedFloat<f64>),
Lis(usize),
HeapCell(usize),
PStrLocation(usize, usize), // location of pstr in heap, offset into string in bytes.
StackCell(usize, usize),
Str(usize),
Stream(usize),
TcpListener(usize),
Usize(usize),
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, PartialOrd)]
pub enum Ref {
AttrVar(usize),
HeapCell(usize),
StackCell(usize, usize),
PStrTail(usize, usize),
}
impl Ref {
@@ -65,7 +86,28 @@ impl Ref {
Ref::AttrVar(h) => Addr::AttrVar(h),
Ref::HeapCell(h) => Addr::HeapCell(h),
Ref::StackCell(fr, sc) => Addr::StackCell(fr, sc),
Ref::PStrTail(h, n) => Addr::PStrTail(h, n),
}
}
}
impl Ord for Ref {
fn cmp(&self, other: &Ref) -> Ordering {
match (self, other) {
(Ref::AttrVar(h1), Ref::AttrVar(h2))
| (Ref::HeapCell(h1), Ref::HeapCell(h2))
| (Ref::HeapCell(h1), Ref::AttrVar(h2))
| (Ref::AttrVar(h1), Ref::HeapCell(h2)) => {
h1.cmp(&h2)
}
(Ref::StackCell(fr1, sc1), Ref::StackCell(fr2, sc2)) => {
fr1.cmp(&fr2).then_with(|| sc1.cmp(&sc2))
}
(Ref::StackCell(..), _) => {
Ordering::Greater
}
(_, Ref::StackCell(..)) => {
Ordering::Less
}
}
}
}
@@ -76,13 +118,13 @@ impl PartialEq<Ref> for Addr {
}
}
// for use in MachineState::bind.
// for use crate::in MachineState::bind.
impl PartialOrd<Ref> for Addr {
fn partial_cmp(&self, r: &Ref) -> Option<Ordering> {
match self {
&Addr::StackCell(fr, sc) => {
match *r {
Ref::AttrVar(_) | Ref::HeapCell(_) | Ref::PStrTail(..) => {
Ref::AttrVar(_) | Ref::HeapCell(_) => {
Some(Ordering::Greater)
}
Ref::StackCell(fr1, sc1) => {
@@ -104,22 +146,6 @@ impl PartialOrd<Ref> for Addr {
Ref::AttrVar(h1) | Ref::HeapCell(h1) => {
h.partial_cmp(h1)
}
Ref::PStrTail(h1, _) => {
h.partial_cmp(h1)
}
}
}
&Addr::PStrTail(h, n) => {
match r {
Ref::StackCell(..) => {
Some(Ordering::Less)
}
Ref::AttrVar(h1) | Ref::HeapCell(h1) => {
h.partial_cmp(h1)
}
Ref::PStrTail(h1, n1) => {
Some(h.cmp(h1).then_with(|| n.cmp(n1)))
}
}
}
_ => {
@@ -130,9 +156,24 @@ impl PartialOrd<Ref> for Addr {
}
impl Addr {
#[inline]
pub fn is_heap_bound(&self) -> bool {
match self {
Addr::Char(_) | Addr::EmptyList |
Addr::CutPoint(_) | Addr::Usize(_) | Addr::Fixnum(_) |
Addr::Float(_) => {
false
}
_ => {
true
}
}
}
#[inline]
pub fn is_ref(&self) -> bool {
match self {
Addr::HeapCell(_) | Addr::StackCell(_, _) | Addr::AttrVar(_) | Addr::PStrTail(..) => {
Addr::HeapCell(_) | Addr::StackCell(_, _) | Addr::AttrVar(_) => {
true
}
_ => {
@@ -141,16 +182,105 @@ impl Addr {
}
}
#[inline]
pub fn as_var(&self) -> Option<Ref> {
match self {
&Addr::AttrVar(h) => Some(Ref::AttrVar(h)),
&Addr::HeapCell(h) => Some(Ref::HeapCell(h)),
&Addr::StackCell(fr, sc) => Some(Ref::StackCell(fr, sc)),
&Addr::PStrTail(h, n) => Some(Ref::PStrTail(h, n)),
_ => None,
}
}
pub(super)
fn order_category(&self, heap: &Heap) -> Option<TermOrderCategory> {
match Number::try_from((*self, heap)) {
Ok(Number::Integer(_)) | Ok(Number::Fixnum(_)) | Ok(Number::Rational(_)) => {
Some(TermOrderCategory::Integer)
}
Ok(Number::Float(_)) => {
Some(TermOrderCategory::FloatingPoint)
}
_ => {
match self {
Addr::HeapCell(_) | Addr::AttrVar(_) | Addr::StackCell(..) => {
Some(TermOrderCategory::Variable)
}
Addr::Float(_) => {
Some(TermOrderCategory::FloatingPoint)
}
&Addr::Con(h) => {
match &heap[h] {
HeapCellValue::Atom(..) => {
Some(TermOrderCategory::Atom)
}
HeapCellValue::DBRef(_) => {
None
}
_ => {
unreachable!()
}
}
}
Addr::Char(_) | Addr::EmptyList => {
Some(TermOrderCategory::Atom)
}
Addr::Fixnum(_) | Addr::Usize(_) => {
Some(TermOrderCategory::Integer)
}
Addr::Lis(_) | Addr::PStrLocation(..) | Addr::Str(_) => {
Some(TermOrderCategory::Compound)
}
Addr::CutPoint(_) | Addr::Stream(_) | Addr::TcpListener(_) => {
None
}
}
}
}
}
pub fn as_constant_index(&self, machine_st: &MachineState) -> Option<Constant> {
match self {
&Addr::Char(c) => {
Some(Constant::Char(c))
}
&Addr::Con(h) => {
match &machine_st.heap[h] {
&HeapCellValue::Atom(ref name, _) if name.is_char() => {
Some(Constant::Char(name.as_str().chars().next().unwrap()))
}
&HeapCellValue::Atom(ref name, _) => {
Some(Constant::Atom(name.clone(), None))
}
&HeapCellValue::Integer(ref n) => {
Some(Constant::Integer(n.clone()))
}
&HeapCellValue::Rational(ref n) => {
Some(Constant::Rational(n.clone()))
}
_ => {
None
}
}
}
&Addr::EmptyList => {
Some(Constant::EmptyList)
}
&Addr::Fixnum(n) => {
Some(Constant::Fixnum(n))
}
&Addr::Float(f) => {
Some(Constant::Float(f))
}
&Addr::Usize(n) => {
Some(Constant::Usize(n))
}
_ => {
None
}
}
}
pub fn is_protected(&self, e: usize) -> bool {
match self {
&Addr::StackCell(addr, _) if addr >= e => false,
@@ -164,12 +294,13 @@ impl Add<usize> for Addr {
fn add(self, rhs: usize) -> Self::Output {
match self {
Addr::Stream(h) => Addr::Stream(h + rhs),
Addr::Con(h) => Addr::Con(h + rhs),
Addr::Lis(a) => Addr::Lis(a + rhs),
Addr::AttrVar(h) => Addr::AttrVar(h + rhs),
Addr::HeapCell(h) => Addr::HeapCell(h + rhs),
Addr::Str(s) => Addr::Str(s + rhs),
Addr::PStrLocation(h, n) => Addr::PStrLocation(h + rhs, n),
Addr::PStrTail(h, n) => Addr::PStrTail(h + rhs, n),
_ => self,
}
}
@@ -181,11 +312,12 @@ impl Sub<i64> for Addr {
fn sub(self, rhs: i64) -> Self::Output {
if rhs < 0 {
match self {
Addr::Stream(h) => Addr::Stream(h + rhs.abs() as usize),
Addr::Con(h) => Addr::Con(h + rhs.abs() as usize),
Addr::Lis(a) => Addr::Lis(a + rhs.abs() as usize),
Addr::AttrVar(h) => Addr::AttrVar(h + rhs.abs() as usize),
Addr::HeapCell(h) => Addr::HeapCell(h + rhs.abs() as usize),
Addr::Str(s) => Addr::Str(s + rhs.abs() as usize),
Addr::PStrTail(h, n) => Addr::PStrTail(h + rhs.abs() as usize, n),
Addr::PStrLocation(h, n) => Addr::PStrLocation(h + rhs.abs() as usize, n),
_ => self,
}
@@ -200,11 +332,12 @@ impl Sub<usize> for Addr {
fn sub(self, rhs: usize) -> Self::Output {
match self {
Addr::Stream(h) => Addr::Stream(h - rhs),
Addr::Con(h) => Addr::Con(h - rhs),
Addr::Lis(a) => Addr::Lis(a - rhs),
Addr::AttrVar(h) => Addr::AttrVar(h - rhs),
Addr::HeapCell(h) => Addr::HeapCell(h - rhs),
Addr::Str(s) => Addr::Str(s - rhs),
Addr::PStrTail(h, n) => Addr::PStrTail(h - rhs, n),
Addr::PStrLocation(h, n) => Addr::PStrLocation(h - rhs, n),
_ => self,
}
@@ -217,7 +350,7 @@ impl SubAssign<usize> for Addr {
}
}
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub enum TrailRef {
Ref(Ref),
AttrVarHeapLink(usize),
@@ -230,30 +363,87 @@ impl From<Ref> for TrailRef {
}
}
#[derive(Clone, PartialEq)]
#[derive(Debug)]
pub enum HeapCellValue {
Addr(Addr),
Atom(ClauseName, Option<SharedOpDesc>),
DBRef(DBRef),
Integer(Rc<Integer>),
NamedStr(usize, ClauseName, Option<SharedOpDesc>), // arity, name, precedence/Specifier if it has one.
PartialString(PartialString),
Rational(Rc<Rational>),
PartialString(PartialString, bool), // the partial string, a bool indicating whether it came from a Constant.
Stream(Stream),
TcpListener(TcpListener),
}
impl HeapCellValue {
#[inline]
pub fn as_addr(&self, focus: usize) -> Addr {
match self {
HeapCellValue::Addr(ref a) => {
a.clone()
*a
}
HeapCellValue::Atom(..) | HeapCellValue::DBRef(..) | HeapCellValue::Integer(..) |
HeapCellValue::Rational(..) => {
Addr::Con(focus)
}
HeapCellValue::NamedStr(_, _, _) => {
Addr::Str(focus)
}
HeapCellValue::PartialString(_) => {
HeapCellValue::PartialString(..) => {
Addr::PStrLocation(focus, 0)
}
HeapCellValue::Stream(_) => {
Addr::Stream(focus)
}
HeapCellValue::TcpListener(_) => {
Addr::TcpListener(focus)
}
}
}
#[inline]
pub fn context_free_clone(&self) -> HeapCellValue {
match self {
&HeapCellValue::Addr(addr) => {
HeapCellValue::Addr(addr)
}
&HeapCellValue::Atom(ref name, ref op) => {
HeapCellValue::Atom(name.clone(), op.clone())
}
&HeapCellValue::DBRef(ref db_ref) => {
HeapCellValue::DBRef(db_ref.clone())
}
&HeapCellValue::Integer(ref n) => {
HeapCellValue::Integer(n.clone())
}
&HeapCellValue::NamedStr(arity, ref name, ref op) => {
HeapCellValue::NamedStr(arity, name.clone(), op.clone())
}
&HeapCellValue::Rational(ref r) => {
HeapCellValue::Rational(r.clone())
}
&HeapCellValue::PartialString(ref pstr, has_tail) => {
HeapCellValue::PartialString(pstr.clone(), has_tail)
}
&HeapCellValue::Stream(ref stream) => {
HeapCellValue::Stream(stream.clone())
}
&HeapCellValue::TcpListener(_) => {
HeapCellValue::Atom(clause_name!("$tcp_listener"), None)
}
}
}
}
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
impl From<Addr> for HeapCellValue {
#[inline]
fn from(value: Addr) -> HeapCellValue {
HeapCellValue::Addr(value)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub enum IndexPtr {
DynamicUndefined, // a predicate, declared as dynamic, whose location in code is as yet undefined.
Undefined,
@@ -263,7 +453,7 @@ pub enum IndexPtr {
UserTermExpansion
}
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct CodeIndex(pub Rc<RefCell<(IndexPtr, ClauseName)>>);
impl CodeIndex {
@@ -318,7 +508,7 @@ impl From<(usize, ClauseName)> for CodeIndex {
}
}
#[derive(Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DynamicAssertPlace {
Back,
Front,
@@ -342,7 +532,7 @@ impl DynamicAssertPlace {
}
}
#[derive(Clone, Copy, PartialEq)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DynamicTransactionType {
Abolish,
Assert(DynamicAssertPlace),
@@ -352,7 +542,7 @@ pub enum DynamicTransactionType {
Retract, // dynamic index of the clause to remove.
}
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
pub enum REPLCodePtr {
CompileBatch,
UseModule,
@@ -361,7 +551,7 @@ pub enum REPLCodePtr {
UseQualifiedModuleFromFile
}
#[derive(Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub enum CodePtr {
BuiltInClause(BuiltInClauseType, LocalCodePtr), // local is the successor call.
CallN(usize, LocalCodePtr, bool), // arity, local, last call.
@@ -428,37 +618,31 @@ impl LocalCodePtr {
LocalCodePtr::DirEntry(p) => {
heap.append(functor!(
"dir_entry",
1,
[heap_integer!(Integer::from(*p))]
[integer(*p)]
));
}
LocalCodePtr::InSituDirEntry(p) => {
heap.append(functor!(
"in_situ_dir_entry",
1,
[heap_integer!(Integer::from(*p))]
[integer(*p)]
));
}
LocalCodePtr::TopLevel(chunk_num, offset) => {
heap.append(functor!(
"top_level",
2,
[heap_integer!(Integer::from(*chunk_num)),
heap_integer!(Integer::from(*offset))]
[integer(*chunk_num), integer(*offset)]
));
}
LocalCodePtr::UserGoalExpansion(p) => {
heap.append(functor!(
"user_goal_expansion",
1,
[heap_integer!(Integer::from(*p))]
[integer(*p)]
));
}
LocalCodePtr::UserTermExpansion(p) => {
heap.append(functor!(
"user_term_expansion",
1,
[heap_integer!(Integer::from(*p))]
[integer(*p)]
));
}
}
@@ -470,8 +654,12 @@ impl LocalCodePtr {
impl PartialOrd<CodePtr> for CodePtr {
fn partial_cmp(&self, other: &CodePtr) -> Option<Ordering> {
match (self, other) {
(&CodePtr::Local(ref l1), &CodePtr::Local(ref l2)) => l1.partial_cmp(l2),
_ => Some(Ordering::Greater),
(&CodePtr::Local(ref l1), &CodePtr::Local(ref l2)) => {
l1.partial_cmp(l2)
}
_ => {
Some(Ordering::Greater)
}
}
}
}
@@ -486,8 +674,12 @@ impl PartialOrd<LocalCodePtr> for LocalCodePtr {
| (&LocalCodePtr::TopLevel(_, p1), &LocalCodePtr::TopLevel(_, ref p2)) => {
p1.partial_cmp(p2)
}
(_, &LocalCodePtr::TopLevel(_, _)) => Some(Ordering::Less),
_ => Some(Ordering::Greater),
(_, &LocalCodePtr::TopLevel(_, _)) => {
Some(Ordering::Less)
}
_ => {
Some(Ordering::Greater)
}
}
}
}
@@ -578,7 +770,7 @@ impl AddAssign<usize> for CodePtr {
pub type HeapVarDict = IndexMap<Rc<Var>, Addr>;
pub type AllocVarDict = IndexMap<Rc<Var>, VarData>;
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct DynamicPredicateInfo {
pub(super) clauses_subsection_p: usize, // a LocalCodePtr::DirEntry value.
}
@@ -598,6 +790,7 @@ pub type DynamicCodeDir = IndexMap<(ClauseName, ClauseName, usize), DynamicPredi
pub type GlobalVarDir = IndexMap<ClauseName, (Ball, Option<usize>)>;
#[derive(Debug)]
pub(crate) struct ModuleStub {
pub(crate) atom_tbl: TabledData<Atom>,
pub(crate) in_situ_code_dir: InSituCodeDir,
@@ -613,17 +806,22 @@ impl ModuleStub {
}
pub(crate) type ModuleStubDir = IndexMap<ClauseName, ModuleStub>;
pub(crate) type StreamAliasDir = IndexMap<ClauseName, Stream>;
pub(crate) type StreamDir = BTreeSet<Stream>;
#[derive(Debug)]
pub struct IndexStore {
pub(super) atom_tbl: TabledData<Atom>,
pub(super) code_dir: CodeDir,
pub(super) module_dir: ModuleDir,
pub(super) dynamic_code_dir: DynamicCodeDir,
pub(super) global_variables: GlobalVarDir,
pub(super) in_situ_code_dir: InSituCodeDir,
pub(super) in_situ_module_dir: ModuleStubDir,
pub(super) module_dir: ModuleDir,
pub(super) modules: ModuleDir,
pub(super) op_dir: OpDir,
pub(super) streams: StreamDir,
pub(super) stream_aliases: StreamAliasDir,
}
impl IndexStore {
@@ -710,6 +908,8 @@ impl IndexStore {
in_situ_module_dir: ModuleStubDir::new(),
op_dir: default_op_dir(),
modules: ModuleDir::new(),
stream_aliases: StreamAliasDir::new(),
streams: StreamDir::new(),
}
}
@@ -740,13 +940,13 @@ impl IndexStore {
let r_w_h = clause_name!("run_cleaners_with_handling");
let r_wo_h = clause_name!("run_cleaners_without_handling");
let non_iso = clause_name!("non_iso");
let iso_ext = clause_name!("iso_ext");
let r_w_h = self
.get_internal(r_w_h, 0, non_iso.clone())
.get_internal(r_w_h, 0, iso_ext.clone())
.and_then(|item| item.local());
let r_wo_h = self
.get_internal(r_wo_h, 1, non_iso)
.get_internal(r_wo_h, 1, iso_ext)
.and_then(|item| item.local());
if let Some(r_w_h) = r_w_h {
@@ -762,6 +962,7 @@ impl IndexStore {
pub type CodeDir = BTreeMap<PredicateKey, CodeIndex>;
pub type TermDir = IndexMap<PredicateKey, (Predicate, VecDeque<TopLevel>)>;
#[derive(Debug)]
pub struct TermDirQuantumEntry {
pub old_terms: (Predicate, VecDeque<TopLevel>),
pub new_terms: (Predicate, VecDeque<TopLevel>),
@@ -790,6 +991,7 @@ impl TermDirQuantumEntry {
}
}
#[derive(Debug)]
pub struct TermDirQuantum(IndexMap<PredicateKey, TermDirQuantumEntry>);
impl TermDirQuantum {
@@ -833,7 +1035,7 @@ impl TermDirQuantum {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
pub enum CompileTimeHook {
GoalExpansion,
TermExpansion,
@@ -887,6 +1089,16 @@ pub enum RefOrOwned<'a, T: 'a> {
Owned(T),
}
impl<'a, T: 'a + fmt::Debug> fmt::Debug for RefOrOwned<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&RefOrOwned::Borrowed(ref borrowed) =>
write!(f, "Borrowed({:?})", borrowed),
&RefOrOwned::Owned(ref owned) => write!(f, "Owned({:?})", owned),
}
}
}
impl<'a, T> RefOrOwned<'a, T> {
pub fn as_ref(&'a self) -> &'a T {
match self {

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
use prolog_parser::ast::*;
use prolog_parser::tabled_rc::*;
use crate::prolog_parser::ast::*;
use crate::prolog_parser::tabled_rc::*;
use crate::prolog::clause_types::*;
use crate::prolog::forms::*;
use crate::prolog::heap_print::*;
use crate::prolog::instructions::*;
use crate::prolog::machine::heap::*;
use crate::prolog::read::*;
use crate::clause_types::*;
use crate::forms::*;
use crate::heap_print::*;
use crate::instructions::*;
use crate::machine::heap::*;
use crate::read::*;
mod attributed_variables;
pub(super) mod code_repo;
@@ -19,37 +19,41 @@ pub mod machine_errors;
pub mod machine_indices;
pub(super) mod machine_state;
pub mod modules;
mod partial_string;
pub mod partial_string;
mod raw_block;
mod stack;
pub(crate) mod streams;
pub(super) mod term_expansion;
pub mod toplevel;
#[macro_use]
mod arithmetic_ops;
#[macro_use]
mod machine_state_impl;
mod system_calls;
use crate::prolog::machine::attributed_variables::*;
use crate::prolog::machine::code_repo::*;
use crate::prolog::machine::compile::*;
use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::machine::machine_state::*;
use crate::prolog::machine::modules::*;
use crate::prolog::machine::toplevel::*;
use crate::prolog::read::PrologStream;
use crate::machine::attributed_variables::*;
use crate::machine::code_repo::*;
use crate::machine::compile::*;
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use crate::machine::machine_state::*;
use crate::machine::modules::*;
use crate::machine::streams::*;
use crate::machine::toplevel::*;
use indexmap::IndexMap;
use crate::indexmap::IndexMap;
use std::collections::VecDeque;
use std::convert::TryFrom;
use std::fs::File;
use std::io::Read;
use std::mem;
use std::ops::Index;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::atomic::AtomicBool;
#[derive(Debug)]
pub struct MachinePolicies {
call_policy: Box<dyn CallPolicy>,
cut_policy: Box<dyn CutPolicy>,
@@ -69,6 +73,14 @@ impl MachinePolicies {
}
}
impl Default for MachinePolicies {
#[inline]
fn default() -> Self {
MachinePolicies::new()
}
}
#[derive(Debug)]
pub struct Machine {
pub(super) machine_st: MachineState,
pub(super) inner_heap: Heap,
@@ -76,7 +88,8 @@ pub struct Machine {
pub(super) indices: IndexStore,
pub(super) code_repo: CodeRepo,
pub(super) toplevel_idx: usize,
pub(super) prolog_stream: ParsingStream<Box<dyn Read>>,
pub(super) current_input_stream: Stream,
pub(super) current_output_stream: Stream,
}
impl Index<LocalCodePtr> for CodeRepo {
@@ -149,7 +162,15 @@ impl SubModuleUser for IndexStore {
fn insert_dir_entry(&mut self, name: ClauseName, arity: usize, idx: CodeIndex) {
if let Some(ref code_idx) = self.code_dir.get(&(name.clone(), arity)) {
if !code_idx.is_undefined() {
println!("Warning: overwriting {}/{}", &name, arity);
match (name.as_str(), arity) {
("term_expansion", 2) => {
}
("goal_expansion", 2) => {
}
_ => {
println!("Warning: overwriting {}/{}", &name, arity);
}
}
}
let (p, module_name) = idx.0.borrow().clone();
@@ -191,13 +212,6 @@ impl SubModuleUser for IndexStore {
}
}
#[inline]
fn current_dir() -> std::path::PathBuf {
let mut path_buf = std::path::PathBuf::from(file!());
path_buf.pop();
path_buf
}
include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
static TOPLEVEL: &str = include_str!("../toplevel.pl");
@@ -205,14 +219,13 @@ static TOPLEVEL: &str = include_str!("../toplevel.pl");
impl Machine {
fn compile_special_forms(&mut self)
{
let current_dir = current_dir();
let verify_attrs_src = ListingSource::User;
let verify_attrs_src = ListingSource::from_file_and_path(
clause_name!("attributed_variables.pl"),
current_dir.clone(),
);
match compile_special_form(self, parsing_stream(VERIFY_ATTRS.as_bytes()), verify_attrs_src)
match compile_special_form(
self,
Stream::from(VERIFY_ATTRS),
verify_attrs_src,
)
{
Ok(p) => {
self.machine_st.attr_var_init.verify_attrs_loc = p;
@@ -221,12 +234,13 @@ impl Machine {
panic!("Machine::compile_special_forms() failed at VERIFY_ATTRS"),
}
let project_attrs_src = ListingSource::from_file_and_path(
clause_name!("project_attributes.pl"),
current_dir,
);
let project_attrs_src = ListingSource::User;
match compile_special_form(self, parsing_stream(PROJECT_ATTRS.as_bytes()), project_attrs_src)
match compile_special_form(
self,
Stream::from(PROJECT_ATTRS),
project_attrs_src,
)
{
Ok(p) => {
self.machine_st.attr_var_init.project_attrs_loc = p;
@@ -240,17 +254,11 @@ impl Machine {
{
self.toplevel_idx = self.code_repo.code.len();
let mut current_dir = current_dir();
current_dir.pop();
let top_lvl_src = ListingSource::from_file_and_path(
clause_name!("toplevel.pl"),
current_dir,
);
let top_lvl_src = ListingSource::User;
compile_user_module(
self,
parsing_stream(TOPLEVEL.as_bytes()),
Stream::from(TOPLEVEL),
true,
top_lvl_src,
);
@@ -264,7 +272,11 @@ impl Machine {
Ok(self.indices.insert_module(module))
} else {
Err(SessionError::ModuleNotFound)
let err = ExistenceError::ModuleSource(ModuleSource::File(
clause_name!("$toplevel"),
));
Err(SessionError::ExistenceError(err))
}
}
@@ -278,7 +290,10 @@ impl Machine {
if path.is_file() {
let file_src = match File::open(&path) {
Ok(file_handle) => parsing_stream(file_handle),
Ok(file_handle) => Stream::from_file_as_input(
clause_name!(".scryerrc"),
file_handle,
),
Err(_) => return,
};
@@ -293,47 +308,46 @@ impl Machine {
#[cfg(test)]
pub fn reset(&mut self) {
self.prolog_stream = readline::input_stream();
self.current_input_stream = readline::input_stream();
self.policies.cut_policy = Box::new(DefaultCutPolicy {});
self.machine_st.reset();
}
pub fn run_init_code(&mut self, code: Code) -> bool {
let old_machine_st = self.sink_to_snapshot();
self.machine_st.reset();
let old_machine_st = self.sink_to_snapshot();
self.machine_st.reset();
self.code_repo.cached_query = code;
self.run_query();
self.code_repo.cached_query = code;
self.run_query();
let result = self.machine_st.fail;
self.absorb_snapshot(old_machine_st);
self.absorb_snapshot(old_machine_st);
!result
}
pub fn run_top_level(&mut self) {
use std::env;
use std::env;
let mut filename_atoms = vec![];
let mut arg_pstrs = vec![];
// the first of these is the path to the scryer-prolog executable, so skip
// it.
for filename in env::args().skip(1) {
let atom = atom!(filename, self.indices.atom_tbl);
filename_atoms.push(Addr::Con(atom));
}
for arg in env::args() {
arg_pstrs.push(self.machine_st.heap.put_complete_string(&arg));
}
let list_addr =
Addr::HeapCell(self.machine_st.heap.to_list(filename_atoms.into_iter()));
let list_addr = Addr::HeapCell(self.machine_st.heap.to_list(arg_pstrs.into_iter()));
self.machine_st[temp_v!(1)] = list_addr;
self.machine_st[temp_v!(1)] = list_addr;
self.machine_st.p = CodePtr::Local(LocalCodePtr::DirEntry(self.toplevel_idx));
self.run_query();
loop {
self.machine_st.p = CodePtr::Local(LocalCodePtr::DirEntry(self.toplevel_idx));
self.run_query();
}
}
pub fn new(prolog_stream: PrologStream) -> Self
pub fn new(current_input_stream: Stream, current_output_stream: Stream) -> Self
{
use crate::ref_thread_local::RefThreadLocal;
let mut wam = Machine {
machine_st: MachineState::new(),
inner_heap: Heap::new(),
@@ -341,53 +355,64 @@ impl Machine {
indices: IndexStore::new(),
code_repo: CodeRepo::new(),
toplevel_idx: 0,
prolog_stream,
current_input_stream,
current_output_stream,
};
let atom_tbl = wam.indices.atom_tbl.clone();
let mut lib_path = current_dir();
lib_path.pop();
lib_path.push("lib");
wam.indices.add_term_and_goal_expansion_indices();
compile_listing(
&mut wam,
parsing_stream(BUILTINS.as_bytes()),
Stream::from(LIBRARIES.borrow()["builtins"]),
default_index_store!(atom_tbl.clone()),
true,
ListingSource::from_file_and_path(
clause_name!("builtins.pl"),
lib_path.clone(),
),
ListingSource::User,
);
wam.compile_special_forms();
compile_user_module(&mut wam, parsing_stream(ERROR.as_bytes()), true,
ListingSource::from_file_and_path(
clause_name!("error"),
lib_path.clone(),
)
compile_user_module(
&mut wam,
Stream::from(LIBRARIES.borrow()["error"]),
true,
ListingSource::User,
);
compile_user_module(&mut wam, parsing_stream(LISTS.as_bytes()), true,
ListingSource::from_file_and_path(
clause_name!("lists"),
lib_path.clone(),
),
compile_user_module(
&mut wam,
Stream::from(LIBRARIES.borrow()["pairs"]),
true,
ListingSource::User,
);
compile_user_module(&mut wam, parsing_stream(NON_ISO.as_bytes()), true,
ListingSource::from_file_and_path(
clause_name!("non_iso"),
lib_path.clone(),
)
compile_user_module(
&mut wam,
Stream::from(LIBRARIES.borrow()["lists"]),
true,
ListingSource::User,
);
compile_user_module(&mut wam, parsing_stream(SI.as_bytes()), true,
ListingSource::from_file_and_path(
clause_name!("si"),
lib_path.clone(),
)
compile_user_module(
&mut wam,
Stream::from(LIBRARIES.borrow()["iso_ext"]),
true,
ListingSource::User,
);
compile_user_module(
&mut wam,
Stream::from(LIBRARIES.borrow()["si"]),
true,
ListingSource::User,
);
compile_user_module(
&mut wam,
Stream::from(LIBRARIES.borrow()["charsio"]),
true,
ListingSource::User,
);
if wam.compile_top_level().is_err() {
@@ -395,10 +420,35 @@ impl Machine {
}
wam.compile_scryerrc();
wam.configure_streams();
wam
}
pub fn configure_streams(&mut self) {
self.current_input_stream.options.alias = Some(clause_name!("user_input"));
self.indices.stream_aliases.insert(
clause_name!("user_input"),
self.current_input_stream.clone(),
);
self.indices.streams.insert(
self.current_input_stream.clone()
);
self.current_output_stream.options.alias = Some(clause_name!("user_output"));
self.indices.stream_aliases.insert(
clause_name!("user_output"),
self.current_output_stream.clone(),
);
self.indices.streams.insert(
self.current_output_stream.clone()
);
}
#[inline]
pub fn machine_flags(&self) -> MachineFlags {
self.machine_st.flags
@@ -504,53 +554,57 @@ impl Machine {
fn extract_module_export_list(&mut self) -> Result<Vec<ModuleExport>, ParserError>
{
let mut export_list = self.machine_st[temp_v!(2)].clone();
let mut exports = vec![];
let mut export_list = self.machine_st[temp_v!(2)].clone();
let mut exports = vec![];
while let Addr::Lis(l) = self.machine_st.store(self.machine_st.deref(export_list)) {
match &self.machine_st.heap[l] {
&HeapCellValue::Addr(Addr::Str(s)) => {
while let Addr::Lis(l) = self.machine_st.store(self.machine_st.deref(export_list)) {
match &self.machine_st.heap[l] {
&HeapCellValue::Addr(Addr::Str(s)) => {
match &self.machine_st.heap[s] {
HeapCellValue::NamedStr(arity, ref name, _)
if *arity == 2 && name.as_str() == "/" => {
let name = match &self.machine_st.heap[s+1] {
&HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
name.clone(),
_ =>
unreachable!()
};
let name = match &self.machine_st.heap[s+1] {
&HeapCellValue::Atom(ref name, _) =>
name.clone(),
_ =>
unreachable!()
};
let arity = match &self.machine_st.heap[s+2] {
&HeapCellValue::Addr(Addr::Con(Constant::Integer(ref arity))) =>
arity.to_usize().unwrap(),
_ =>
unreachable!()
};
let arity = match &self.machine_st.heap[s+2] {
&HeapCellValue::Integer(ref arity) =>
arity.to_usize().unwrap(),
&HeapCellValue::Addr(Addr::Fixnum(n)) =>
usize::try_from(n).unwrap(),
_ =>
unreachable!()
};
exports.push(ModuleExport::PredicateKey((name, arity)));
exports.push(ModuleExport::PredicateKey((name, arity)));
}
HeapCellValue::NamedStr(arity, ref name, _)
if *arity == 3 && name.as_str() == "op" => {
let name = match &self.machine_st.heap[s+3] {
&HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
name.clone(),
_ =>
unreachable!()
};
&HeapCellValue::Atom(ref name, _) =>
name.clone(),
_ =>
unreachable!()
};
let spec = match &self.machine_st.heap[s+2] {
&HeapCellValue::Addr(Addr::Con(Constant::Atom(ref name, _))) =>
name.clone(),
_ =>
unreachable!()
};
&HeapCellValue::Atom(ref name, _) =>
name.clone(),
_ =>
unreachable!()
};
let prec = match &self.machine_st.heap[s+1] {
&HeapCellValue::Addr(Addr::Con(Constant::Integer(ref arity))) =>
arity.to_usize().unwrap(),
_ =>
unreachable!()
};
let prec = match &self.machine_st.heap[s+1] {
&HeapCellValue::Integer(ref arity) =>
arity.to_usize().unwrap(),
&HeapCellValue::Addr(Addr::Fixnum(n)) =>
usize::try_from(n).unwrap(),
_ =>
unreachable!()
};
exports.push(ModuleExport::OpDecl(to_op_decl(
prec,
@@ -560,43 +614,58 @@ impl Machine {
}
_ => unreachable!()
}
}
_ => unreachable!()
}
_ => unreachable!()
}
export_list = self.machine_st.heap[l+1].as_addr(l+1);
}
export_list = self.machine_st.heap[l+1].as_addr(l+1);
}
Ok(exports)
Ok(exports)
}
fn use_module<ToSource>(&mut self, to_src: ToSource)
where ToSource: Fn(ClauseName) -> ModuleSource
{
// the term expander will overwrite the cached query, so save it here.
let cached_query = mem::replace(&mut self.code_repo.cached_query, vec![]);
// the term expander will overwrite the cached query, so save it here.
let cached_query = mem::replace(&mut self.code_repo.cached_query, vec![]);
let module_spec = self.machine_st[temp_v!(1)].clone();
let name = match self.machine_st.store(self.machine_st.deref(module_spec)) {
Addr::Con(Constant::Atom(name, _)) => name,
_ => unreachable!()
};
let module_spec = self.machine_st[temp_v!(1)].clone();
let name = {
let addr = self.machine_st.store(self.machine_st.deref(module_spec));
let load_result = match to_src(name) {
ModuleSource::Library(name) =>
match self.machine_st.heap.index_addr(&addr).as_ref() {
HeapCellValue::Atom(name, _) =>
name.clone(),
HeapCellValue::Addr(Addr::Char(c)) =>
clause_name!(c.to_string(), self.indices.atom_tbl),
HeapCellValue::Addr(addr @ Addr::PStrLocation(..)) => {
let mut heap_pstr_iter =
self.machine_st.heap_pstr_iter(*addr);
clause_name!(
heap_pstr_iter.to_string(),
self.indices.atom_tbl
)
}
_ => unreachable!(),
}
};
let load_result = match to_src(name) {
ModuleSource::Library(name) =>
if let Some(module) = self.indices.take_module(name.clone()) {
self.indices.remove_module(clause_name!("user"), &module);
self.indices.modules.insert(name.clone(), module);
Ok(name)
} else {
load_library(self, name, false)
},
ModuleSource::File(name) =>
Ok(name)
} else {
load_library(self, name, false)
},
ModuleSource::File(name) =>
load_module_from_file(self, PathBuf::from(name.as_str()), false)
};
};
let result = load_result.and_then(|name| {
let result = load_result.and_then(|name| {
let module = self.indices.take_module(name.clone()).unwrap();
if !module.is_impromptu_module {
@@ -606,26 +675,34 @@ impl Machine {
Ok(self.indices.insert_module(module))
});
self.code_repo.cached_query = cached_query;
self.code_repo.cached_query = cached_query;
if let Err(e) = result {
self.throw_session_error(e, (clause_name!("use_module"), 1));
}
if let Err(e) = result {
self.throw_session_error(e, (clause_name!("use_module"), 1));
}
}
fn use_qualified_module<ToSource>(&mut self, to_src: ToSource)
where ToSource: Fn(ClauseName) -> ModuleSource
{
// the term expander will overwrite the cached query, so save it here.
let cached_query = mem::replace(&mut self.code_repo.cached_query, vec![]);
// the term expander will overwrite the cached query, so save it here.
let cached_query = mem::replace(&mut self.code_repo.cached_query, vec![]);
let module_spec = self.machine_st[temp_v!(1)].clone();
let name = match self.machine_st.store(self.machine_st.deref(module_spec)) {
Addr::Con(Constant::Atom(name, _)) => name,
_ => unreachable!()
};
let module_spec = self.machine_st[temp_v!(1)].clone();
let name = {
let addr = self.machine_st.store(self.machine_st.deref(module_spec));
let exports = match self.extract_module_export_list() {
match self.machine_st.heap.index_addr(&addr).as_ref() {
HeapCellValue::Atom(name, _) =>
name.clone(),
HeapCellValue::Addr(Addr::Char(c)) =>
clause_name!(c.to_string(), self.indices.atom_tbl),
_ =>
unreachable!(),
}
};
let exports = match self.extract_module_export_list() {
Ok(exports) => exports,
Err(e) => {
self.throw_session_error(SessionError::from(e), (clause_name!("use_module"), 2));
@@ -633,38 +710,38 @@ impl Machine {
}
};
let load_result = match to_src(name) {
ModuleSource::Library(name) =>
let load_result = match to_src(name) {
ModuleSource::Library(name) =>
if let Some(module) = self.indices.take_module(name.clone()) {
self.indices.remove_module(clause_name!("user"), &module);
self.indices.modules.insert(name.clone(), module);
Ok(name)
} else {
load_library(self, name, false)
},
ModuleSource::File(name) =>
Ok(name)
} else {
load_library(self, name, false)
},
ModuleSource::File(name) =>
load_module_from_file(self, PathBuf::from(name.as_str()), false)
};
};
let result = load_result.and_then(|name| {
let module = self.indices.take_module(name.clone()).unwrap();
let result = load_result.and_then(|name| {
let module = self.indices.take_module(name.clone()).unwrap();
if !module.is_impromptu_module {
self.indices.use_qualified_module(&mut self.code_repo,
self.machine_st.flags,
&module,
&exports)?;
self.indices.use_qualified_module(&mut self.code_repo,
self.machine_st.flags,
&module,
&exports)?;
}
Ok(self.indices.insert_module(module))
Ok(self.indices.insert_module(module))
});
self.code_repo.cached_query = cached_query;
self.code_repo.cached_query = cached_query;
if let Err(e) = result {
self.throw_session_error(e, (clause_name!("use_module"), 2));
}
if let Err(e) = result {
self.throw_session_error(e, (clause_name!("use_module"), 2));
}
}
fn handle_toplevel_command(&mut self, code_ptr: REPLCodePtr, p: LocalCodePtr) {
@@ -679,14 +756,14 @@ impl Machine {
self.throw_session_error(e, (clause_name!("repl"), 0));
}
}
REPLCodePtr::UseModule =>
self.use_module(ModuleSource::Library),
REPLCodePtr::UseModuleFromFile =>
self.use_module(ModuleSource::File),
REPLCodePtr::UseQualifiedModule =>
self.use_qualified_module(ModuleSource::Library),
REPLCodePtr::UseQualifiedModuleFromFile =>
self.use_qualified_module(ModuleSource::File)
REPLCodePtr::UseModule =>
self.use_module(ModuleSource::Library),
REPLCodePtr::UseModuleFromFile =>
self.use_module(ModuleSource::File),
REPLCodePtr::UseQualifiedModule =>
self.use_qualified_module(ModuleSource::Library),
REPLCodePtr::UseQualifiedModuleFromFile =>
self.use_qualified_module(ModuleSource::File)
}
self.machine_st.p = CodePtr::Local(p);
@@ -699,7 +776,7 @@ impl Machine {
snapshot.e = self.machine_st.e;
snapshot.b = self.machine_st.b;
snapshot.b0 = self.machine_st.b0;
snapshot.s = self.machine_st.s;
snapshot.s = self.machine_st.s.clone();
snapshot.tr = self.machine_st.tr;
snapshot.num_of_args = self.machine_st.num_of_args;
@@ -743,7 +820,7 @@ impl Machine {
}
pub(super) fn run_query(&mut self) {
self.machine_st.cp = LocalCodePtr::TopLevel(0, self.code_repo.size_of_cached_query());
self.machine_st.cp = LocalCodePtr::TopLevel(0, self.code_repo.size_of_cached_query());
let end_ptr = CodePtr::Local(self.machine_st.cp);
while self.machine_st.p < end_ptr {
@@ -751,12 +828,16 @@ impl Machine {
&mut self.indices,
&mut self.policies,
&mut self.code_repo,
&mut self.prolog_stream,
&mut self.current_input_stream,
&mut self.current_output_stream,
);
match self.machine_st.p {
CodePtr::Local(LocalCodePtr::TopLevel(_, p)) if p > 0 => {}
CodePtr::REPL(code_ptr, p) => self.handle_toplevel_command(code_ptr, p),
CodePtr::Local(LocalCodePtr::TopLevel(_, p)) if p > 0 => {
}
CodePtr::REPL(code_ptr, p) => {
self.handle_toplevel_command(code_ptr, p)
}
CodePtr::DynamicTransaction(trans_type, p) => {
// self.code_repo.cached_query is about to be overwritten by the term expander,
// so hold onto it locally and restore it after the compiler has finished.
@@ -784,29 +865,37 @@ impl MachineState {
indices: &mut IndexStore,
policies: &mut MachinePolicies,
code_repo: &CodeRepo,
prolog_stream: &mut PrologStream,
current_input_stream: &mut Stream,
current_output_stream: &mut Stream,
) {
match instr {
&Line::Arithmetic(ref arith_instr) => self.execute_arith_instr(arith_instr),
&Line::Arithmetic(ref arith_instr) => {
self.execute_arith_instr(arith_instr)
}
&Line::Choice(ref choice_instr) => {
self.execute_choice_instr(choice_instr, &mut policies.call_policy)
}
&Line::Cut(ref cut_instr) => {
self.execute_cut_instr(cut_instr, &mut policies.cut_policy)
}
&Line::Control(ref control_instr) => self.execute_ctrl_instr(
indices,
code_repo,
&mut policies.call_policy,
&mut policies.cut_policy,
prolog_stream,
control_instr,
),
&Line::Control(ref control_instr) => {
self.execute_ctrl_instr(
indices,
code_repo,
&mut policies.call_policy,
&mut policies.cut_policy,
current_input_stream,
current_output_stream,
control_instr,
)
}
&Line::Fact(ref fact_instr) => {
self.execute_fact_instr(&fact_instr);
self.p += 1;
}
&Line::Indexing(ref indexing_instr) => self.execute_indexing_instr(&indexing_instr),
&Line::Indexing(ref indexing_instr) => {
self.execute_indexing_instr(&indexing_instr)
}
&Line::IndexedChoice(ref choice_instr) => {
self.execute_indexed_choice_instr(choice_instr, &mut policies.call_policy)
}
@@ -822,14 +911,22 @@ impl MachineState {
indices: &mut IndexStore,
policies: &mut MachinePolicies,
code_repo: &CodeRepo,
prolog_stream: &mut PrologStream,
current_input_stream: &mut Stream,
current_output_stream: &mut Stream,
) {
let instr = match code_repo.lookup_instr(self.last_call, &self.p) {
Some(instr) => instr,
None => return,
};
self.dispatch_instr(instr.as_ref(), indices, policies, code_repo, prolog_stream);
self.dispatch_instr(
instr.as_ref(),
indices,
policies,
code_repo,
current_input_stream,
current_output_stream,
);
}
fn backtrack(&mut self) {
@@ -880,7 +977,8 @@ impl MachineState {
indices: &mut IndexStore,
policies: &mut MachinePolicies,
code_repo: &mut CodeRepo,
prolog_stream: &mut PrologStream,
current_input_stream: &mut Stream,
current_output_stream: &mut Stream,
) -> bool {
loop {
let instr = match code_repo.lookup_instr(self.last_call, &self.p) {
@@ -896,7 +994,14 @@ impl MachineState {
None => return false,
};
self.dispatch_instr(instr.as_ref(), indices, policies, code_repo, prolog_stream);
self.dispatch_instr(
instr.as_ref(),
indices,
policies,
code_repo,
current_input_stream,
current_output_stream,
);
if self.fail {
self.backtrack();
@@ -920,10 +1025,17 @@ impl MachineState {
indices: &mut IndexStore,
policies: &mut MachinePolicies,
code_repo: &mut CodeRepo,
prolog_stream: &mut PrologStream,
current_input_stream: &mut Stream,
current_output_stream: &mut Stream,
) {
loop {
self.execute_instr(indices, policies, code_repo, prolog_stream);
self.execute_instr(
indices,
policies,
code_repo,
current_input_stream,
current_output_stream,
);
if self.fail {
self.backtrack();
@@ -939,7 +1051,13 @@ impl MachineState {
if !instigating_instr.as_ref().is_head_instr() {
let cp = self.p.local();
self.run_verify_attr_interrupt(cp);
} else if !self.verify_attr_stepper(indices, policies, code_repo, prolog_stream) {
} else if !self.verify_attr_stepper(
indices,
policies,
code_repo,
current_input_stream,
current_output_stream,
) {
if self.fail {
break;
}

View File

@@ -1,10 +1,10 @@
use prolog_parser::ast::*;
use prolog_parser::tabled_rc::*;
use crate::prolog_parser::ast::*;
use crate::prolog_parser::tabled_rc::*;
use crate::prolog::forms::*;
use crate::prolog::machine::code_repo::*;
use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::machine_indices::*;
use crate::forms::*;
use crate::machine::code_repo::*;
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use std::collections::VecDeque;
use std::mem;
@@ -48,6 +48,7 @@ impl Module {
(te.0)
.0
.extend((self.user_term_expansions.0).0.iter().cloned());
te.1.extend(self.user_term_expansions.1.iter().cloned());
}
@@ -60,6 +61,7 @@ impl Module {
(ge.0)
.0
.extend((self.user_goal_expansions.0).0.iter().cloned());
ge.1.extend(self.user_goal_expansions.1.iter().cloned());
}
@@ -166,7 +168,7 @@ pub trait SubModuleUser {
}
}
}
}
}
_ => {}
};
},
@@ -202,7 +204,7 @@ pub trait SubModuleUser {
_: &Module,
_: &Vec<ModuleExport>,
) -> Result<(), SessionError>;
fn use_module(
&mut self,
_: &mut CodeRepo,
@@ -232,7 +234,7 @@ where
if !user.import_decl(name.clone(), arity, submodule) {
let submodule_name = submodule.module_decl.name.clone();
return Err(SessionError::ModuleDoesNotContainExport(
submodule_name,
(name, arity)
@@ -250,12 +252,12 @@ where
let op_dir = user.op_dir();
let prec = op_decl.0;
op_decl.insert_into_op_dir(
submodule.module_decl.name.clone(),
op_dir,
prec,
);
);
}
}
}
@@ -282,12 +284,12 @@ pub fn use_module<User: SubModuleUser>(
ModuleExport::OpDecl(op_decl) => {
let op_dir = user.op_dir();
let prec = op_decl.0;
op_decl.insert_into_op_dir(
submodule.module_decl.name.clone(),
op_dir,
prec,
);
);
}
}
}

View File

@@ -0,0 +1,202 @@
use core::marker::PhantomData;
use std::alloc;
use std::mem;
use std::ptr;
use std::ops::RangeFrom;
use std::slice;
use std::str;
#[derive(Debug)]
pub struct PartialString {
buf: *const u8,
len: usize,
_marker: PhantomData<[u8]>,
}
impl Drop for PartialString {
fn drop(&mut self) {
unsafe {
let layout = alloc::Layout::from_size_align_unchecked(self.len, mem::align_of::<u8>());
alloc::dealloc(self.buf as *mut u8, layout);
self.buf = ptr::null();
self.len = 0;
}
}
}
impl Clone for PartialString {
#[inline]
fn clone(&self) -> Self {
self.clone_from_offset(0)
}
}
fn scan_for_terminator<Iter: Iterator<Item = char>>(iter: Iter) -> usize {
let mut terminator_idx = 0;
for c in iter {
if c == '\u{0}' && terminator_idx != 0 {
return terminator_idx;
}
terminator_idx += c.len_utf8();
}
terminator_idx
}
#[derive(Debug)]
pub struct PStrIter {
buf: *const u8,
len: usize,
}
impl PStrIter {
#[inline]
fn from(buf: *const u8, len: usize, idx: usize) -> Self {
PStrIter {
buf: (buf as usize + idx) as *const _,
len: len - idx,
}
}
}
impl Iterator for PStrIter {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let slice = slice::from_raw_parts(self.buf, self.len);
let s = str::from_utf8(slice).unwrap();
if let Some(c) = s.chars().next() {
self.buf = self.buf.offset(c.len_utf8() as isize);
self.len -= c.len_utf8();
Some(c)
} else {
None
}
}
}
}
impl PartialString {
#[inline]
pub(super)
fn new(src: &str) -> Option<(Self, &str)> {
let pstr = PartialString {
buf: ptr::null_mut(),
len: 0,
_marker: PhantomData,
};
unsafe {
pstr.append_chars(src)
}
}
unsafe fn append_chars(mut self, src: &str) -> Option<(Self, &str)> {
let terminator_idx = scan_for_terminator(src.chars());
let layout = alloc::Layout::from_size_align_unchecked(
terminator_idx + '\u{0}'.len_utf8(),
mem::align_of::<u8>(),
);
self.buf = alloc::alloc(layout) as *const _;
self.len = terminator_idx + '\u{0}'.len_utf8();
ptr::copy(
src.as_ptr(),
self.buf as *mut _,
terminator_idx,
);
self.write_terminator_at(terminator_idx);
Some(if terminator_idx != src.as_bytes().len() {
(self, &src[terminator_idx ..])
} else {
(self, "")
})
}
pub(super)
fn clone_from_offset(&self, n: usize) -> Self {
let len =
if self.len - '\u{0}'.len_utf8() > n {
self.len - n - '\u{0}'.len_utf8()
} else {
0
};
let mut pstr = PartialString {
buf: ptr::null_mut(),
len: len + '\u{0}'.len_utf8(),
_marker: PhantomData,
};
unsafe {
let layout = alloc::Layout::from_size_align_unchecked(
len + '\u{0}'.len_utf8(),
mem::align_of::<u8>(),
);
pstr.buf = alloc::alloc(layout);
if len > 0 {
ptr::copy(
(self.buf as usize + n) as *const u8,
pstr.buf as *mut _,
len,
);
}
pstr.write_terminator_at(len);
}
pstr
}
#[inline]
pub(super)
fn write_terminator_at(&mut self, index: usize) {
unsafe {
ptr::write(
(self.buf as usize + index) as *mut u8,
0u8,
);
}
}
#[inline]
pub fn range_from(&self, index: RangeFrom<usize>) -> PStrIter {
if self.len >= '\u{0}'.len_utf8() {
PStrIter::from(self.buf, self.len - '\u{0}'.len_utf8(), index.start)
} else {
PStrIter::from(self.buf, 0, 0)
}
}
#[inline]
pub fn at_end(&self, end_n: usize) -> bool {
end_n + 1 == self.len
}
#[inline]
pub fn as_str_from(&self, n: usize) -> &str {
unsafe {
let slice = slice::from_raw_parts(
self.buf,
self.len - '\u{0}'.len_utf8(),
);
let s = str::from_utf8(slice).unwrap();
&s[n ..]
}
}
}

View File

@@ -1,3 +1,5 @@
:- module('$project_atts', [copy_term/3]).
'$attribute_goals_driver'(QueryVars, AttrVars) :-
gather_modules(AttrVars, Modules0, _),
sort(Modules0, Modules),
@@ -99,7 +101,7 @@ call_attribute_goals_with_module_prefix([Module | Modules], GoalCaller, AttrVars
call_attribute_goals_with_module_prefix(Modules, GoalCaller, AttrVars, Gs).
copy_term(Source, Dest, Goals) :-
term_variables(Source, Vars),
'$term_attributed_variables'(Source, Vars),
gather_modules(Vars, Modules0, _),
sort(Modules0, Modules),
call_attribute_goals_with_module_prefix(Modules, call_query_var_goals, Vars, Goals0),

View File

@@ -5,7 +5,7 @@ use std::mem;
use std::ptr;
pub(crate) trait RawBlockTraits {
fn init_size() -> usize;
fn init_size() -> usize;
fn align() -> usize;
#[inline]
@@ -14,6 +14,7 @@ pub(crate) trait RawBlockTraits {
}
}
#[derive(Debug)]
pub(crate) struct RawBlock<T: RawBlockTraits> {
pub(crate) size: usize,
pub(crate) base: *const u8,
@@ -36,20 +37,6 @@ impl<T: RawBlockTraits> RawBlock<T> {
block
}
pub(crate)
fn with_capacity(cap: usize) -> Self {
let mut block = RawBlock { size: 0,
base: ptr::null(),
top: ptr::null(),
_marker: PhantomData };
unsafe {
block.init_at_size(cap);
}
block
}
unsafe fn init_at_size(&mut self, cap: usize) {
let layout = alloc::Layout::from_size_align_unchecked(cap, T::align());
@@ -86,7 +73,6 @@ impl<T: RawBlockTraits> RawBlock<T> {
mem::replace(self, Self::empty_block())
}
#[inline]
fn free_space(&self) -> usize {
debug_assert!(self.top >= self.base,

View File

@@ -1,12 +1,13 @@
use crate::prolog::machine::machine_indices::*;
use crate::prolog::machine::raw_block::*;
use core::marker::PhantomData;
use crate::machine::machine_indices::*;
use crate::machine::raw_block::*;
use std::mem;
use std::ops::{Index, IndexMut};
use std::ptr;
#[derive(Debug)]
struct StackTraits {}
impl RawBlockTraits for StackTraits {
@@ -35,6 +36,7 @@ const fn prelude_size<Prelude>() -> usize {
(size & !(align - 1)) + align
}
#[derive(Debug)]
pub struct Stack {
buf: RawBlock<StackTraits>,
_marker: PhantomData<Addr>,
@@ -47,12 +49,12 @@ impl Drop for Stack {
}
}
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub struct FramePrelude {
is_or_frame: u8,
pub num_cells: usize,
}
#[derive(Debug)]
pub struct AndFramePrelude {
pub univ_prelude: FramePrelude,
pub e: usize,
@@ -60,9 +62,9 @@ pub struct AndFramePrelude {
pub interrupt_cp: LocalCodePtr,
}
#[derive(Debug)]
pub struct AndFrame {
pub prelude: AndFramePrelude,
_marker: PhantomData<Addr>,
}
impl AndFrame {
@@ -101,24 +103,7 @@ impl IndexMut<usize> for AndFrame {
}
}
impl Drop for AndFrame {
fn drop(&mut self) {
let prelude_offset = prelude_size::<AndFramePrelude>();
unsafe {
let ptr = mem::transmute::<&mut AndFrame, *const u8>(self);
let ptr = ptr as usize + prelude_offset;
for idx in 0 .. self.prelude.univ_prelude.num_cells {
let index_offset = idx * mem::size_of::<Addr>();
let ptr = (ptr + index_offset) as *mut Addr;
ptr::drop_in_place(ptr);
}
}
}
}
#[derive(Debug)]
pub struct OrFramePrelude {
pub univ_prelude: FramePrelude,
pub e: usize,
@@ -133,9 +118,9 @@ pub struct OrFramePrelude {
pub attr_var_init_bindings_b: usize,
}
#[derive(Debug)]
pub struct OrFrame {
pub prelude: OrFramePrelude,
_marker: PhantomData<Addr>
}
impl Index<usize> for OrFrame {
@@ -170,24 +155,6 @@ impl IndexMut<usize> for OrFrame {
}
}
impl Drop for OrFrame {
fn drop(&mut self) {
let prelude_offset = prelude_size::<OrFramePrelude>();
unsafe {
let ptr = mem::transmute::<&mut OrFrame, *const u8>(self);
let ptr = ptr as usize + prelude_offset;
for idx in 0 .. self.prelude.univ_prelude.num_cells {
let index_offset = idx * mem::size_of::<Addr>();
let ptr = (ptr + index_offset) as *mut Addr;
ptr::drop_in_place(ptr);
}
}
}
}
impl OrFrame {
pub fn size_of(num_cells: usize) -> usize {
prelude_size::<OrFramePrelude>() + num_cells * mem::size_of::<Addr>()
@@ -204,18 +171,19 @@ impl Stack {
unsafe {
let new_top = self.buf.new_block(frame_size);
let e = self.buf.top as usize - self.buf.base as usize;
for idx in 0 .. num_cells {
let offset = prelude_size::<AndFramePrelude>() + idx * mem::size_of::<Addr>();
ptr::write((self.buf.top as usize + offset) as *mut Addr, Addr::StackCell(0,0));
ptr::write(
(self.buf.top as usize + offset) as *mut Addr,
Addr::StackCell(e, idx + 1),
);
}
let and_frame = &mut *(self.buf.top as *mut AndFrame);
and_frame.prelude.univ_prelude.is_or_frame = 0;
and_frame.prelude.univ_prelude.num_cells = num_cells;
let e = self.buf.top as usize - self.buf.base as usize;
self.buf.top = new_top;
e
@@ -227,18 +195,19 @@ impl Stack {
unsafe {
let new_top = self.buf.new_block(frame_size);
let b = self.buf.top as usize - self.buf.base as usize;
for idx in 0 .. num_cells {
let offset = prelude_size::<OrFramePrelude>() + idx * mem::size_of::<Addr>();
ptr::write((self.buf.top as usize + offset) as *mut Addr, Addr::StackCell(0,0));
ptr::write(
(self.buf.top as usize + offset) as *mut Addr,
Addr::StackCell(b, idx),
);
}
let or_frame = &mut *(self.buf.top as *mut OrFrame);
or_frame.prelude.univ_prelude.is_or_frame = 1;
or_frame.prelude.univ_prelude.num_cells = num_cells;
let b = self.buf.top as usize - self.buf.base as usize;
self.buf.top = new_top;
b
@@ -281,6 +250,7 @@ impl Stack {
Stack { buf: self.buf.take(), _marker: PhantomData }
}
#[inline]
pub fn truncate(&mut self, b: usize) {
if b == 0 {
self.inner_truncate(mem::align_of::<Addr>());
@@ -289,40 +259,12 @@ impl Stack {
}
}
#[inline]
fn inner_truncate(&mut self, b: usize) {
let mut b = b + self.buf.base as usize;
let base = b;
let base = b + self.buf.base as usize;
unsafe {
while b as *const _ < self.buf.top {
let univ_prelude = ptr::read(b as *const FramePrelude);
let offset = if univ_prelude.is_or_frame == 0 {
let frame_ptr = b as *mut AndFrame;
let frame = &mut *frame_ptr;
let size_of_frame = AndFrame::size_of(frame.prelude.univ_prelude.num_cells);
ptr::drop_in_place(frame_ptr);
b + size_of_frame
} else {
debug_assert!(univ_prelude.is_or_frame == 1);
let frame_ptr = b as *mut OrFrame;
let frame = &mut *frame_ptr;
let size_of_frame = OrFrame::size_of(frame.prelude.univ_prelude.num_cells);
ptr::drop_in_place(frame_ptr);
b + size_of_frame
};
b = offset;
}
if base < self.buf.top as usize {
self.buf.top = base as *const _;
}
if base < self.buf.top as usize {
self.buf.top = base as *const _;
}
}

1236
src/machine/streams.rs Normal file

File diff suppressed because it is too large Load Diff

6013
src/machine/system_calls.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,42 +1,16 @@
use prolog_parser::ast::*;
use prolog_parser::parser::*;
use crate::prolog_parser::ast::*;
use crate::prolog_parser::parser::*;
use crate::prolog::machine::machine_indices::HeapCellValue;
use crate::prolog::machine::*;
use crate::prolog::rug::ops::Pow;
use crate::prolog::rug::Integer;
use crate::machine::machine_indices::HeapCellValue;
use crate::machine::*;
use crate::rug::ops::Pow;
use crate::rug::Integer;
use std::cell::Cell;
use std::collections::VecDeque;
use std::io::Read;
use std::iter::Rev;
use std::vec::IntoIter;
fn unfold_by_str_once(term: &mut Term, s: &str) -> Option<(Term, Term)> {
if let &mut Term::Clause(_, ref name, ref mut subterms, _) = term {
if name.as_str() == s && subterms.len() == 2 {
let snd = *subterms.pop().unwrap();
let fst = *subterms.pop().unwrap();
return Some((fst, snd));
}
}
None
}
pub fn unfold_by_str(mut term: Term, s: &str) -> Vec<Term> {
let mut terms = vec![];
while let Some((fst, snd)) = unfold_by_str_once(&mut term, s) {
terms.push(fst);
term = snd;
}
terms.push(term);
terms
}
pub fn fold_by_str<I>(terms: I, mut term: Term, sym: ClauseName) -> Term
where
I: DoubleEndedIterator<Item = Term>,
@@ -53,7 +27,11 @@ where
term
}
fn extract_from_list(head: Box<Term>, tail: Box<Term>) -> Result<Rev<IntoIter<Term>>, ParserError> {
fn extract_from_list(
head: Box<Term>,
tail: Box<Term>,
) -> Result<Rev<IntoIter<Term>>, ParserError>
{
let mut terms = vec![*head];
let mut tail = *tail;
@@ -69,16 +47,18 @@ fn extract_from_list(head: Box<Term>, tail: Box<Term>) -> Result<Rev<IntoIter<Te
}
}
pub struct TermStream<'a, R: Read> {
#[derive(Debug)]
pub struct TermStream<'a> {
stack: Vec<Term>,
pub(crate) wam: &'a mut Machine,
parser: Parser<'a, R>,
parser: Parser<'a, Stream>,
pub(crate) flags: MachineFlags,
term_expansion_lens: (usize, usize),
goal_expansion_lens: (usize, usize),
top_level_terms: Vec<(Term, usize, usize)>, // term, line_num, col_num.
}
#[derive(Debug)]
pub struct ExpansionAdditionResult {
term_expansion_additions: (Predicate, VecDeque<TopLevel>),
goal_expansion_additions: (Predicate, VecDeque<TopLevel>),
@@ -100,7 +80,7 @@ impl ExpansionAdditionResult {
}
}
impl<'a, R: Read> Drop for TermStream<'a, R> {
impl<'a> Drop for TermStream<'a> {
fn drop(&mut self) {
self.wam.indices.in_situ_code_dir.clear();
self.wam.indices.in_situ_module_dir.clear();
@@ -110,9 +90,9 @@ impl<'a, R: Read> Drop for TermStream<'a, R> {
}
}
impl<'a, R: Read> TermStream<'a, R> {
impl<'a> TermStream<'a> {
pub fn new(
src: &'a mut ParsingStream<R>,
src: &'a mut ParsingStream<Stream>,
atom_tbl: TabledData<Atom>,
flags: MachineFlags,
wam: &'a mut Machine,
@@ -185,7 +165,7 @@ impl<'a, R: Read> TermStream<'a, R> {
#[inline]
pub fn eof(&mut self) -> Result<bool, ParserError> {
self.parser.devour_whitespace()?; // eliminate dangling comments before checking for EOF.
self.parser.devour_whitespace()?; // eliminate dangling comments before checking for EOF.
Ok(self.stack.is_empty() && self.parser.eof()?)
}
@@ -201,6 +181,7 @@ impl<'a, R: Read> TermStream<'a, R> {
te_len,
te_queue_len,
);
let goal_expansion_additions = self.wam.code_repo.truncate_terms(
(clause_name!("goal_expansion"), 2),
ge_len,
@@ -240,7 +221,7 @@ impl<'a, R: Read> TermStream<'a, R> {
term_string: &str,
op_dir: &OpDir,
) -> Result<Term, ParserError> {
let mut stream = parsing_stream(term_string.trim().as_bytes());
let mut stream = parsing_stream(term_string.trim().as_bytes())?;
let mut parser = Parser::new(&mut stream, self.parser.get_atom_tbl(), self.flags);
parser.read_term(composite_op!(
@@ -257,7 +238,7 @@ impl<'a, R: Read> TermStream<'a, R> {
while let Some(term) = self.stack.pop() {
match machine_st.try_expand_term(self.wam, &term, CompileTimeHook::TermExpansion) {
Some(term_string) => {
Some(term_string) => {
let term = self.parse_expansion_output(term_string.as_str(), op_dir)?;
self.enqueue_term(term)?;
}
@@ -269,7 +250,7 @@ impl<'a, R: Read> TermStream<'a, R> {
unreachable!()
}
pub fn read_term(&mut self, op_dir: &OpDir) -> Result<Term, ParserError> {
loop {
if let Some(term) = self.stack.pop() {
@@ -326,7 +307,8 @@ impl<'a, R: Read> TermStream<'a, R> {
}
impl MachineState {
pub(super) fn print_with_locs(&self, addr: Addr, op_dir: &OpDir) -> PrinterOutputter {
pub(super)
fn print_with_locs(&self, addr: Addr, op_dir: &OpDir) -> PrinterOutputter {
let output = PrinterOutputter::new();
let mut printer = HCPrinter::from_heap_locs(&self, op_dir, output);
let mut max_var_length = 0;
@@ -337,12 +319,16 @@ impl MachineState {
printer.quoted = true;
printer.numbervars = true;
// the purpose of the offset is to avoid clashes with variable names that might
// occur after the addresses in the expanded term are substituted with the variable
// names in the pre-expansion term. This formula ensures that all generated "numbervars"-
// style variable names will be longer than the keys of the var_dict, and therefore
// not equal to any of them.
// the purpose of the offset is to avoid clashes with variable
// names that might occur after the addresses in the expanded
// term are substituted with the variable names in the
// pre-expansion term. This formula ensures that all generated
// "numbervars"- style variable names will be longer than the
// keys of the var_dict, and therefore not equal to any of
// them.
printer.numbervars_offset = Integer::from(10).pow(max_var_length as u32) * 26;
printer.print_strings_as_strs = true;
printer.drop_toplevel_spec();
printer.see_all_locs();
@@ -354,8 +340,8 @@ impl MachineState {
}
// reset the machine, but keep the heap contents as they were.
// this prevents clashes between underscored variable names
// in the same query.
// this prevents clashes between underscored variable names in the
// same query.
fn reset_with_heap_preservation(&mut self) {
let heap = self.heap.take();
self.reset();
@@ -379,13 +365,16 @@ impl MachineState {
wam.code_repo.cached_query = code;
self.cp = LocalCodePtr::TopLevel(0, 0);
self.at_end_of_expansion = false;
self.flags.double_quotes = DoubleQuotes::Chars;
self.query_stepper(
&mut wam.indices,
&mut wam.policies,
&mut MachinePolicies::default(),
&mut wam.code_repo,
&mut readline::input_stream(),
&mut Stream::stdout(),
);
if self.fail || self.at_end_of_expansion {
@@ -398,6 +387,7 @@ impl MachineState {
let output = self.print_with_locs(Addr::HeapCell(h), &wam.indices.op_dir);
self.reset_with_heap_preservation();
Some(output.result())
}
}

View File

@@ -1,19 +1,20 @@
use prolog_parser::ast::*;
use prolog_parser::tabled_rc::*;
use crate::prolog_parser::ast::*;
use crate::prolog_parser::tabled_rc::*;
use crate::prolog::forms::*;
use crate::prolog::iterators::*;
use crate::prolog::machine::machine_errors::*;
use crate::prolog::machine::machine_indices::*;
use crate::prolog::machine::term_expansion::*;
use crate::prolog::machine::*;
use crate::forms::*;
use crate::iterators::*;
use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use crate::machine::term_expansion::*;
use crate::machine::*;
use indexmap::{IndexMap, IndexSet};
use crate::indexmap::{IndexMap, IndexSet};
use std::borrow::BorrowMut;
use std::cell::Cell;
use std::collections::VecDeque;
use std::io::Read;
use std::convert::TryFrom;
use std::fmt;
use std::mem;
use std::ops::DerefMut;
use std::rc::Rc;
@@ -23,6 +24,15 @@ enum IndexSource<'a, T> {
Local(&'a mut T)
}
impl<'a, T: fmt::Debug> fmt::Debug for IndexSource<'a, T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
IndexSource::TermStream => write!(fmt, "TermStream"),
IndexSource::Local(ref local) => write!(fmt, "Local({:?})", local),
}
}
}
fn op_dir<'a, 'b: 'a>(from: &'b IndexSource<'a, IndexStore>) -> RefOrOwned<'a, OpDir> {
match from {
IndexSource::TermStream => RefOrOwned::Owned(OpDir::new()),
@@ -30,15 +40,16 @@ fn op_dir<'a, 'b: 'a>(from: &'b IndexSource<'a, IndexStore>) -> RefOrOwned<'a, O
}
}
struct CompositeIndices<'a, 'b, 'c, R: Read> {
term_stream: &'b mut TermStream<'a, R>,
#[derive(Debug)]
struct CompositeIndices<'a, 'b, 'c> {
term_stream: &'b mut TermStream<'a>,
index_src: IndexSource<'c, IndexStore>,
static_code_dir: Option<IndexSource<'c, CodeDir>>
}
impl<'a, 'b, 'c, R: Read> CompositeIndices<'a, 'b, 'c, R> {
impl<'a, 'b, 'c> CompositeIndices<'a, 'b, 'c> {
fn new(
term_stream: &'b mut TermStream<'a, R>,
term_stream: &'b mut TermStream<'a>,
index_src: IndexSource<'c, IndexStore>,
static_code_dir: Option<IndexSource<'c, CodeDir>>,
) -> Self {
@@ -212,8 +223,8 @@ fn setup_op_decl(
};
let prec = match *terms.pop().unwrap() {
Term::Constant(_, Constant::Integer(bi)) => match bi.to_usize() {
Some(n) if n <= 1200 => n,
Term::Constant(_, Constant::Fixnum(bi)) => match usize::try_from(bi) {
Ok(n) if n <= 1200 => n,
_ => return Err(ParserError::InconsistentEntry),
},
_ => return Err(ParserError::InconsistentEntry),
@@ -225,16 +236,21 @@ fn setup_op_decl(
fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, ParserError>
{
match term {
Term::Clause(_, ref name, ref mut terms, Some(_))
if name.as_str() == "/" && terms.len() == 2 =>
Term::Clause(_, ref slash, ref mut terms, Some(_))
if (slash.as_str() == "/" || slash.as_str() == "//") && terms.len() == 2 =>
{
let arity = *terms.pop().unwrap();
let name = *terms.pop().unwrap();
let arity = arity
.to_constant()
.and_then(|c| c.to_integer())
.and_then(|n| n.to_usize())
.and_then(|c| {
match c {
Constant::Integer(n) => n.to_usize(),
Constant::Fixnum(n) => usize::try_from(n).ok(),
_ => None
}
})
.ok_or(ParserError::InvalidModuleExport)?;
let name = name
@@ -242,7 +258,11 @@ fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, ParserErro
.and_then(|c| c.to_atom())
.ok_or(ParserError::InvalidModuleExport)?;
Ok((name, arity))
if slash.as_str() == "/" {
Ok((name, arity))
} else {
Ok((name, arity + 2))
}
}
_ => Err(ParserError::InvalidModuleExport),
}
@@ -320,16 +340,37 @@ fn setup_module_decl(
}
}
fn setup_use_module_decl(mut terms: Vec<Box<Term>>) -> Result<ModuleSource, ParserError> {
fn read_library_path(
term: Term,
atom_tbl: TabledData<Atom>,
) -> Option<ClauseName> {
match term {
Term::Constant(_, Constant::Atom(atom, _)) => {
Some(atom.defrock_brackets())
}
_ => {
let mut atoms = vec![];
for term in unfold_by_str(term, "/") {
match term {
Term::Constant(_, Constant::Atom(atom, _)) => {
atoms.push(atom.as_str().to_owned());
}
_ => return None,
}
}
Some(clause_name!(atoms.join("/"), atom_tbl))
}
}
}
fn setup_use_module_decl(mut terms: Vec<Box<Term>>, atom_tbl: TabledData<Atom>) -> Result<ModuleSource, ParserError> {
match *terms.pop().unwrap() {
Term::Clause(_, ref name, ref mut terms, None)
if name.as_str() == "library" && terms.len() == 1 =>
{
terms
.pop()
.unwrap()
.to_constant()
.and_then(|c| c.to_atom())
read_library_path(*terms.pop().unwrap(), atom_tbl)
.map(|c| ModuleSource::Library(c))
.ok_or(ParserError::InvalidUseModuleDecl)
}
@@ -339,6 +380,32 @@ fn setup_use_module_decl(mut terms: Vec<Box<Term>>) -> Result<ModuleSource, Pars
}
}
fn setup_double_quotes(mut terms: Vec<Box<Term>>) -> Result<DoubleQuotes, ParserError> {
let dbl_quotes = *terms.pop().unwrap();
match terms[0].as_ref() {
Term::Constant(_, Constant::Atom(ref name, _))
if name.as_str() == "double_quotes" => {
match dbl_quotes {
Term::Constant(_, Constant::Atom(name, _)) => {
match name.as_str() {
"atom" => Ok(DoubleQuotes::Atom),
"chars" => Ok(DoubleQuotes::Chars),
"codes" => Ok(DoubleQuotes::Codes),
_ => Err(ParserError::InvalidDoubleQuotesDecl),
}
}
_ => {
Err(ParserError::InvalidDoubleQuotesDecl)
}
}
},
_ => {
Err(ParserError::InvalidDoubleQuotesDecl)
}
}
}
type UseModuleExport = (ModuleSource, Vec<ModuleExport>);
fn setup_qualified_import(
@@ -350,11 +417,7 @@ fn setup_qualified_import(
Term::Clause(_, ref name, ref mut terms, None)
if name.as_str() == "library" && terms.len() == 1 =>
{
terms
.pop()
.unwrap()
.to_constant()
.and_then(|c| c.to_atom())
read_library_path(*terms.pop().unwrap(), atom_tbl.clone())
.map(|c| ModuleSource::Library(c))
.ok_or(ParserError::InvalidUseModuleDecl)
}
@@ -551,8 +614,8 @@ fn draw_from_term_dir_impl(
}
}
fn draw_from_term_dir<R: Read>(
indices: &CompositeIndices<R>,
fn draw_from_term_dir(
indices: &CompositeIndices,
intra_module_term_dirs: &mut IndexMap<ClauseName, TermDirQuantum>,
top_level_term_dirs: &mut TermDirQuantum,
key: &PredicateKey,
@@ -589,8 +652,8 @@ fn draw_from_term_dir<R: Read>(
);
}
fn setup_declaration<'a, 'b, 'c, R: Read>(
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
fn setup_declaration<'a, 'b, 'c>(
indices: &mut CompositeIndices<'a, 'b, 'c>,
flags: MachineFlags,
mut terms: Vec<Box<Term>>,
line_num: usize,
@@ -600,54 +663,62 @@ fn setup_declaration<'a, 'b, 'c, R: Read>(
match term {
Term::Clause(_, name, mut terms, _) =>
match (name.as_str(), terms.len()) {
("dynamic", 1) => {
let (name, arity) = setup_predicate_indicator(&mut *terms.pop().unwrap())?;
Ok(Declaration::Dynamic(name, arity))
}
("initialization", 1) => {
let mut rel_worker = RelationWorker::new(flags, line_num, col_num);
let query_terms = rel_worker.setup_query(indices, terms, false)?;
let queue = rel_worker.parse_queue(indices)?;
match (name.as_str(), terms.len()) {
("dynamic", 1) => {
let (name, arity) = setup_predicate_indicator(&mut *terms.pop().unwrap())?;
Ok(Declaration::Dynamic(name, arity))
}
("initialization", 1) => {
let mut rel_worker = RelationWorker::new(flags, line_num, col_num);
let (query_terms, _) = rel_worker.setup_query(indices, terms, false, false)?;
let queue = rel_worker.parse_queue(indices)?;
Ok(Declaration::ModuleInitialization(query_terms, queue))
}
("module", 2) =>
Ok(Declaration::Module(setup_module_decl(terms, indices.atom_tbl())?)),
("op", 3) =>
Ok(Declaration::Op(setup_op_decl(terms, indices.atom_tbl())?)),
("non_counted_backtracking", 1) => {
let (name, arity) = setup_predicate_indicator(&mut *terms.pop().unwrap())?;
Ok(Declaration::NonCountedBacktracking(name, arity))
}
Ok(Declaration::ModuleInitialization(query_terms, queue))
}
("module", 2) =>
Ok(Declaration::Module(setup_module_decl(terms, indices.atom_tbl())?)),
("op", 3) =>
Ok(Declaration::Op(setup_op_decl(terms, indices.atom_tbl())?)),
("non_counted_backtracking", 1) => {
let (name, arity) = setup_predicate_indicator(&mut *terms.pop().unwrap())?;
Ok(Declaration::NonCountedBacktracking(name, arity))
}
("set_prolog_flag", 2) => {
Ok(Declaration::SetPrologFlag(setup_double_quotes(terms)?))
}
("multifile", 1) => {
let mut term = *terms.pop().unwrap();
match setup_predicate_indicator(&mut term) {
Ok((name, arity)) =>
Ok(Declaration::MultiFile(MultiFileIndicator::LocalScoped(name, arity))),
_ =>
Ok((name, arity)) => {
Ok(Declaration::MultiFile(MultiFileIndicator::LocalScoped(name, arity)))
}
_ => {
setup_scoped_predicate_indicator(&mut term)
.map(|key| {
Declaration::MultiFile(MultiFileIndicator::ModuleScoped(key))
})
}
}
}
("use_module", 1) => {
Ok(Declaration::UseModule(setup_use_module_decl(terms)?))
("use_module", 1) => {
Ok(Declaration::UseModule(setup_use_module_decl(terms, indices.atom_tbl())?))
}
("use_module", 2) => {
let (name, exports) = setup_qualified_import(terms, indices.atom_tbl())?;
Ok(Declaration::UseQualifiedModule(name, exports))
}
_ => {
Err(ParserError::InconsistentEntry)
("use_module", 2) => {
let (name, exports) = setup_qualified_import(terms, indices.atom_tbl())?;
Ok(Declaration::UseQualifiedModule(name, exports))
}
_ => {
Err(ParserError::InconsistentEntry)
}
},
_ => Err(ParserError::InconsistentEntry),
},
_ => {
Err(ParserError::InconsistentEntry)
}
}
}
#[derive(Debug)]
struct RelationWorker {
flags: MachineFlags,
dynamic_clauses: Vec<(Term, Term)>, // Head, Body.
@@ -767,9 +838,9 @@ impl RelationWorker {
self.fabricate_rule(fold_by_str(prec_seq.into_iter(), body_term, comma_sym))
}
fn to_query_term<'a, 'b, 'c, R: Read>(
fn to_query_term<'a, 'b, 'c>(
&mut self,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
term: Term,
) -> Result<QueryTerm, ParserError> {
match term {
@@ -818,6 +889,7 @@ impl RelationWorker {
let term = Term::Clause(Cell::default(), clause_name!(";"), terms, None);
let (stub, clauses) = self.fabricate_disjunct(term);
debug_assert!(clauses.len() > 0);
self.queue.push_back(clauses);
Ok(QueryTerm::Jump(stub))
}
@@ -843,9 +915,9 @@ impl RelationWorker {
}
}
fn pre_query_term<'a, 'b, 'c, R: Read>(
fn pre_query_term<'a, 'b, 'c>(
&mut self,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
term: Term,
) -> Result<QueryTerm, ParserError> {
match term {
@@ -864,16 +936,19 @@ impl RelationWorker {
}
}
fn setup_query<'a, 'b, 'c, R: Read>(
fn setup_query<'a, 'b, 'c>(
&mut self,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
terms: Vec<Box<Term>>,
blocks_cuts: bool,
) -> Result<Vec<QueryTerm>, ParserError> {
assume_dyn: bool,
) -> Result<(Vec<QueryTerm>, Term), ParserError> {
let mut query_terms = vec![];
let mut work_queue = VecDeque::from(terms);
let mut machine_st = MachineState::new();
let mut dynamic_clause_terms = vec![];
while let Some(term) = work_queue.pop_front() {
let term = *term;
let op_dir = op_dir(&indices.index_src);
@@ -892,7 +967,7 @@ impl RelationWorker {
let mut term = *term;
if let Term::Clause(cell, name, terms, op_spec) = term {
if name.as_str() == "," {
if name.as_str() == "," && terms.len() == 2 {
let term = Term::Clause(cell, name, terms, op_spec);
let mut subterms = unfold_by_str(term, ",");
@@ -910,17 +985,28 @@ impl RelationWorker {
mark_cut_variable(&mut term);
}
if assume_dyn {
dynamic_clause_terms.push(term.clone());
}
query_terms.push(self.pre_query_term(indices, term)?);
}
}
Ok(query_terms)
let dynamic_clause_body =
if let Some(term) = dynamic_clause_terms.pop() {
fold_by_str(dynamic_clause_terms.into_iter(), term, clause_name!(","))
} else {
Term::Constant(Cell::default(), Constant::Atom(clause_name!("true"), None))
};
Ok((query_terms, dynamic_clause_body))
}
fn setup_hook<'a, 'b, 'c, R: Read>(
fn setup_hook<'a, 'b, 'c>(
&mut self,
hook: CompileTimeHook,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
term: Term,
) -> Result<CompileTimeHookCompileInfo, ParserError> {
match flatten_hook(term) {
@@ -941,24 +1027,24 @@ impl RelationWorker {
}
}
fn setup_rule<'a, 'b, 'c, R: Read>(
fn setup_rule<'a, 'b, 'c>(
&mut self,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
mut terms: Vec<Box<Term>>,
blocks_cuts: bool,
assume_dyn: bool,
) -> Result<Rule, ParserError> {
let head = *terms.first().cloned().unwrap();
let post_head_terms: Vec<_> = terms.drain(1..).collect();
let dynamic_term_head = *terms.first().cloned().unwrap();
let post_head_terms: Vec<_> = terms.drain(1 ..).collect();
let tail = *post_head_terms.first().cloned().unwrap();
let (mut query_terms, dynamic_term_body) =
self.setup_query(indices, post_head_terms, blocks_cuts, assume_dyn)?;
if assume_dyn {
self.dynamic_clauses.push((head, tail));
self.dynamic_clauses.push((dynamic_term_head, dynamic_term_body));
}
let mut query_terms = self.setup_query(indices, post_head_terms, blocks_cuts)?;
let clauses = query_terms.drain(1..).collect();
let clauses = query_terms.drain(1 ..).collect();
let qt = query_terms.pop().unwrap();
match *terms.pop().unwrap() {
@@ -974,23 +1060,26 @@ impl RelationWorker {
}
}
fn try_term_to_query<'a, 'b, 'c, R: Read>(
fn try_term_to_query<'a, 'b, 'c>(
&mut self,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
terms: Vec<Box<Term>>,
blocks_cuts: bool,
) -> Result<TopLevel, ParserError> {
Ok(TopLevel::Query(self.setup_query(
let (result, _) = self.setup_query(
indices,
terms,
blocks_cuts,
)?))
false,
)?;
Ok(TopLevel::Query(result))
}
fn compact_module_scoped_head<'a, 'b, 'c, R: Read>(
fn compact_module_scoped_head<'a, 'b, 'c>(
&self,
term: &mut Term,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
) {
let inner_term = match term {
Term::Clause(_, ref name, ref mut inner_terms, _)
@@ -1015,9 +1104,9 @@ impl RelationWorker {
*term = inner_term;
}
fn try_term_to_tl<'a, 'b, 'c, R: Read>(
fn try_term_to_tl<'a, 'b, 'c>(
&mut self,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
term: Term,
blocks_cuts: bool,
) -> Result<TopLevel, ParserError> {
@@ -1051,19 +1140,20 @@ impl RelationWorker {
Ok(TopLevel::Fact(self.setup_fact(term, true)?, self.line_num, self.col_num))
}
}
term =>
Ok(TopLevel::Fact(self.setup_fact(term, true)?, self.line_num, self.col_num)),
term => {
Ok(TopLevel::Fact(self.setup_fact(term, true)?, self.line_num, self.col_num))
}
}
}
fn try_terms_to_tls<'a, 'b, 'c, I, R>(
fn try_terms_to_tls<'a, 'b, 'c, I>(
&mut self,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
terms: I,
blocks_cuts: bool,
) -> Result<VecDeque<TopLevel>, ParserError>
where
I: IntoIterator<Item = Term>, R: Read
I: IntoIterator<Item = Term>
{
let mut results = VecDeque::new();
@@ -1074,9 +1164,9 @@ impl RelationWorker {
Ok(results)
}
fn parse_queue<'a, 'b, 'c, R: Read>(
fn parse_queue<'a, 'b, 'c>(
&mut self,
indices: &mut CompositeIndices<'a, 'b, 'c, R>,
indices: &mut CompositeIndices<'a, 'b, 'c>,
) -> Result<VecDeque<TopLevel>, ParserError> {
let mut queue = VecDeque::new();
@@ -1090,8 +1180,7 @@ impl RelationWorker {
fn absorb(&mut self, other: RelationWorker) {
self.queue.extend(other.queue.into_iter());
self.dynamic_clauses
.extend(other.dynamic_clauses.into_iter());
self.dynamic_clauses.extend(other.dynamic_clauses.into_iter());
}
}
@@ -1099,8 +1188,9 @@ pub type DynamicClause = Vec<(Term, Term)>;
pub type DynamicClauseMap = IndexMap<(ClauseName, usize), DynamicClause>;
pub struct TopLevelBatchWorker<'a, R: Read> {
pub(crate) term_stream: TermStream<'a, R>,
#[derive(Debug)]
pub struct TopLevelBatchWorker<'a> {
pub(crate) term_stream: TermStream<'a>,
rel_worker: RelationWorker,
pub(crate) results: Vec<(Predicate, VecDeque<TopLevel>)>,
pub(crate) dynamic_clause_map: DynamicClauseMap,
@@ -1110,14 +1200,14 @@ pub struct TopLevelBatchWorker<'a, R: Read> {
pub(crate) non_counted_bt_preds: IndexSet<PredicateKey>,
}
impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
impl<'a> TopLevelBatchWorker<'a> {
pub fn new(
inner: &'a mut ParsingStream<R>,
stream: &'a mut ParsingStream<Stream>,
atom_tbl: TabledData<Atom>,
flags: MachineFlags,
wam: &'a mut Machine,
) -> Self {
let term_stream = TermStream::new(inner, atom_tbl, flags, wam);
let term_stream = TermStream::new(stream, atom_tbl, flags, wam);
let line_num = term_stream.line_num();
let col_num = term_stream.col_num();
@@ -1201,7 +1291,7 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
match self.dynamic_clause_map.get_mut(&(name.clone(), arity)) {
Some(ref mut entry) => {
entry.clear(); // don't treat dynamic predicates as if they're discontiguous.
entry.extend(self.rel_worker.dynamic_clauses.drain(0..));
entry.extend(self.rel_worker.dynamic_clauses.drain(0 ..));
}
_ => {
self.rel_worker.dynamic_clauses.clear();
@@ -1217,7 +1307,7 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
while !self.term_stream.eof()? {
let term = self.term_stream.read_term(&indices.op_dir)?;
// if is_consistent is false, preds is non-empty.
let term = if !term.is_consistent(&preds) {
self.process_result(indices, &mut preds)?;
@@ -1248,7 +1338,7 @@ impl<'a, R: Read> TopLevelBatchWorker<'a, R> {
TopLevel::Declaration(decl) =>
return Ok(Some(decl)),
TopLevel::Query(_) =>
return Err(SessionError::NamelessEntry),
return Err(SessionError::QueryCannotBeDefinedAsFact),
}
}

431
src/macros.rs Normal file
View File

@@ -0,0 +1,431 @@
macro_rules! interm {
($n: expr) => {
ArithmeticTerm::Interm($n)
};
}
/* A simple macro to count the arguments in a variadic list
* of token trees.
*/
macro_rules! count_tt {
() => { 0 };
($odd:tt $($a:tt $b:tt)*) => { (count_tt!($($a)*) << 1) | 1 };
($($a:tt $even:tt)*) => { count_tt!($($a)*) << 1 };
}
macro_rules! functor {
($name:expr, $fixity:expr, [$($dt:ident($($value:expr),*)),+], [$($aux:ident),*]) => ({
{
#[allow(unused_variables, unused_mut)]
let mut addendum = Heap::new();
let arity = count_tt!($($dt) +);
let aux_lens = [$($aux.len()),*];
let mut result =
vec![ HeapCellValue::NamedStr(arity, clause_name!($name), Some($fixity)),
$(functor_term!( $dt($($value),*), arity, aux_lens, addendum ),)+ ];
$(
result.extend($aux.into_iter());
)*
result.extend(addendum.into_iter());
result
}
});
($name:expr, $fixity:expr, [$($dt:ident($($value:expr),*)),+]) => ({
{
#[allow(unused_variables, unused_mut)]
let mut addendum = Heap::new();
let arity = count_tt!($($dt) +);
let mut result =
vec![ HeapCellValue::NamedStr(arity, clause_name!($name), Some($fixity)),
$(functor_term!( $dt($($value),*), arity, [], addendum ),)+ ];
result.extend(addendum.into_iter());
result
}
});
($name:expr, [$($dt:ident($($value:expr),*)),+], [$($aux:ident),*]) => ({
{
#[allow(unused_variables, unused_mut)]
let mut addendum = Heap::new();
let arity = count_tt!($($dt) +);
let aux_lens = [$($aux.len()),*];
let mut result =
vec![ HeapCellValue::NamedStr(arity, clause_name!($name), None),
$(functor_term!( $dt($($value),*), arity, aux_lens, addendum ),)+ ];
$(
result.extend($aux.into_iter());
)*
result.extend(addendum.into_iter());
result
}
});
($name:expr, [$($dt:ident($($value:expr),*)),+]) => ({
{
let arity = count_tt!($($dt) +);
vec![ HeapCellValue::NamedStr(arity, clause_name!($name), None),
$(functor_term!( $dt($($value),*), arity, [], addendum ),)+ ]
}
});
($name:expr, $fixity:expr) => (
vec![ HeapCellValue::Atom(clause_name!($name), Some($fixity)) ]
);
(clause_name($name:expr)) => (
vec![ HeapCellValue::Atom($name, None) ]
);
($name:expr) => (
vec![ HeapCellValue::Atom(clause_name!($name), None) ]
);
}
macro_rules! functor_term {
(aux(0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
HeapCellValue::Addr(Addr::HeapCell($arity + 1))
});
(aux($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
let len: usize = $aux_lens[0 .. $e].iter().sum();
HeapCellValue::Addr(Addr::HeapCell($arity + 1 + len))
});
(aux($h:expr, 0), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
HeapCellValue::Addr(Addr::HeapCell($arity + $h + 1))
});
(aux($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => ({
let len: usize = $aux_lens[0 .. $e].iter().sum();
HeapCellValue::Addr(Addr::HeapCell($arity + $h + 1 + len))
});
(addr($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
HeapCellValue::Addr($e)
);
(constant($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
from_constant!($e, $h, $arity, $aux_lens, $addendum)
);
(constant($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
from_constant!($e, 0, $arity, $aux_lens, $addendum)
);
(number($e:expr), $arity:expr, $aux_lens:expr, $addendum:ident) => (
$e.into()
);
(integer($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => (
HeapCellValue::Integer(Rc::new(Integer::from($e)))
);
(clause_name($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => (
HeapCellValue::Atom($e, None)
);
(atom($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => (
HeapCellValue::Atom(clause_name!($e), None)
);
(value($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => (
$e
);
(string($h:expr, $e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => ({
let len: usize = $aux_lens.iter().sum();
let h = len + $arity + 1 + $addendum.h() + $h;
$addendum.put_complete_string(&$e);
HeapCellValue::Addr(Addr::PStrLocation(h, 0))
});
(boolean($e:expr), $arity:expr, $aux_lens:expr, $addendum: ident) => ({
if $e {
functor_term!(atom("true"), $arity, $aux_lens, $addendum)
} else {
functor_term!(atom("false"), $arity, $aux_lens, $addendum)
}
});
($e:expr, $arity:expr, $aux_lens:expr, $addendum:ident) => (
$e
);
}
macro_rules! from_constant {
($e:expr, $over_h:expr, $arity:expr, $aux_lens:expr, $addendum:ident) => ({
match $e {
&Constant::Atom(ref name, ref op) => {
HeapCellValue::Atom(name.clone(), op.clone())
}
&Constant::Char(c) => {
HeapCellValue::Addr(Addr::Char(c))
}
&Constant::Fixnum(n) => {
HeapCellValue::Addr(Addr::Fixnum(n))
}
&Constant::Integer(ref n) => {
HeapCellValue::Integer(n.clone())
}
&Constant::Rational(ref r) => {
HeapCellValue::Rational(r.clone())
}
&Constant::Float(f) => {
HeapCellValue::Addr(Addr::Float(f))
}
&Constant::String(ref s) => {
let len: usize = $aux_lens.iter().sum();
let h = len + $arity + 1 + $addendum.h() + $over_h;
$addendum.put_complete_string(&s);
HeapCellValue::Addr(Addr::PStrLocation(h, 0))
}
&Constant::Usize(u) => {
HeapCellValue::Addr(Addr::Usize(u))
}
&Constant::EmptyList => {
HeapCellValue::Addr(Addr::EmptyList)
}
}
})
}
macro_rules! is_atom {
($r:expr) => {
call_clause!(ClauseType::Inlined(InlinedClauseType::IsAtom($r)), 1, 0)
};
}
macro_rules! is_atomic {
($r:expr) => {
call_clause!(ClauseType::Inlined(InlinedClauseType::IsAtomic($r)), 1, 0)
};
}
macro_rules! is_integer {
($r:expr) => {
call_clause!(ClauseType::Inlined(InlinedClauseType::IsInteger($r)), 1, 0)
};
}
macro_rules! is_compound {
($r:expr) => {
call_clause!(ClauseType::Inlined(InlinedClauseType::IsCompound($r)), 1, 0)
};
}
macro_rules! is_float {
($r:expr) => {
call_clause!(ClauseType::Inlined(InlinedClauseType::IsFloat($r)), 1, 0)
};
}
macro_rules! is_rational {
($r:expr) => {
call_clause!(ClauseType::Inlined(InlinedClauseType::IsRational($r)), 1, 0)
};
}
macro_rules! is_nonvar {
($r:expr) => {
call_clause!(ClauseType::Inlined(InlinedClauseType::IsNonVar($r)), 1, 0)
};
}
macro_rules! is_var {
($r:expr) => {
call_clause!(ClauseType::Inlined(InlinedClauseType::IsVar($r)), 1, 0)
};
}
macro_rules! call_clause {
($ct:expr, $arity:expr, $pvs:expr) => {
Line::Control(ControlInstruction::CallClause(
$ct, $arity, $pvs, false, false,
))
};
($ct:expr, $arity:expr, $pvs:expr, $lco:expr) => {
Line::Control(ControlInstruction::CallClause(
$ct, $arity, $pvs, $lco, false,
))
};
}
macro_rules! call_clause_by_default {
($ct:expr, $arity:expr, $pvs:expr) => {
Line::Control(ControlInstruction::CallClause(
$ct, $arity, $pvs, false, true,
))
};
($ct:expr, $arity:expr, $pvs:expr, $lco:expr) => {
Line::Control(ControlInstruction::CallClause(
$ct, $arity, $pvs, $lco, true,
))
};
}
macro_rules! proceed {
() => {
Line::Control(ControlInstruction::Proceed)
};
}
macro_rules! is_call {
($r:expr, $at:expr) => {
call_clause!(ClauseType::BuiltIn(BuiltInClauseType::Is($r, $at)), 2, 0)
};
}
macro_rules! is_call_by_default {
($r:expr, $at:expr) => {
call_clause_by_default!(ClauseType::BuiltIn(BuiltInClauseType::Is($r, $at)), 2, 0)
};
}
macro_rules! set_cp {
($r:expr) => {
call_clause!(ClauseType::System(SystemClauseType::SetCutPoint($r)), 1, 0)
};
}
macro_rules! succeed {
() => {
call_clause!(ClauseType::System(SystemClauseType::Succeed), 0, 0)
};
}
macro_rules! fail {
() => {
call_clause!(ClauseType::System(SystemClauseType::Fail), 0, 0)
};
}
macro_rules! compare_number_instr {
($cmp: expr, $at_1: expr, $at_2: expr) => {{
let ct = ClauseType::Inlined(InlinedClauseType::CompareNumber($cmp, $at_1, $at_2));
call_clause!(ct, 2, 0)
}};
}
macro_rules! jmp_call {
($arity:expr, $offset:expr, $pvs:expr) => {
Line::Control(ControlInstruction::JmpBy($arity, $offset, $pvs, false))
};
}
macro_rules! try_eval_session {
($e:expr) => {
match $e {
Ok(result) => result,
Err(e) => return EvalSession::from(e),
}
};
}
macro_rules! return_from_clause {
($lco:expr, $machine_st:expr) => {{
if let CodePtr::VerifyAttrInterrupt(_) = $machine_st.p {
return Ok(());
}
if $lco {
$machine_st.p = CodePtr::Local($machine_st.cp);
} else {
$machine_st.p += 1;
}
Ok(())
}};
}
macro_rules! dir_entry {
($idx:expr) => {
LocalCodePtr::DirEntry($idx)
};
}
macro_rules! set_code_index {
($idx:expr, $ip:expr, $mod_name:expr) => {{
let mut idx = $idx.0.borrow_mut();
idx.0 = $ip;
idx.1 = $mod_name.clone();
}};
}
macro_rules! index_store {
($atom_tbl:expr, $code_dir:expr, $op_dir:expr, $modules:expr) => {
IndexStore {
atom_tbl: $atom_tbl,
code_dir: $code_dir,
module_dir: ModuleDir::new(),
dynamic_code_dir: DynamicCodeDir::new(),
global_variables: GlobalVarDir::new(),
in_situ_code_dir: InSituCodeDir::new(),
in_situ_module_dir: ModuleStubDir::new(),
op_dir: $op_dir,
modules: $modules,
stream_aliases: StreamAliasDir::new(),
streams: StreamDir::new(),
}
};
}
macro_rules! default_index_store {
($atom_tbl:expr) => {
index_store!($atom_tbl, CodeDir::new(), default_op_dir(), IndexMap::new())
};
}
macro_rules! put_constant {
($lvl:expr, $cons:expr, $r:expr) => {
QueryInstruction::PutConstant($lvl, $cons, $r)
};
}
macro_rules! get_level_and_unify {
($r: expr) => {
Line::Cut(CutInstruction::GetLevelAndUnify($r))
};
}
macro_rules! unwind_protect {
($e: expr, $protected: expr) => {
match $e {
Err(e) => {
$protected;
return Err(e);
}
_ => {}
}
};
}
macro_rules! discard_result {
($f: expr) => {
match $f {
_ => (),
}
};
}
macro_rules! ar_reg {
($r: expr) => {
ArithmeticTerm::Reg($r)
};
}
macro_rules! atom_from {
($self:expr, $indices:expr, $e:expr) => {
match $e {
Addr::Con(h) if $self.heap.atom_at(h) => {
match &$self.heap[h] {
HeapCellValue::Atom(ref atom, _) => {
atom.clone()
}
_ => {
unreachable!()
}
}
}
Addr::Char(c) => {
clause_name!(c.to_string(), $indices.atom_tbl.clone())
}
_ => {
unreachable!()
}
}
}
}

View File

@@ -1,29 +1,62 @@
extern crate blake2;
extern crate chrono;
extern crate cpu_time;
extern crate crossterm;
extern crate divrem;
#[macro_use]
extern crate downcast;
extern crate git_version;
extern crate hostname;
extern crate indexmap;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate native_tls;
extern crate nix;
extern crate openssl;
extern crate ordered_float;
#[macro_use]
extern crate prolog_parser;
#[macro_use]
extern crate ref_thread_local;
extern crate termion;
extern crate ring;
extern crate ripemd160;
extern crate rug;
extern crate rustyline;
extern crate sha3;
extern crate unicode_reader;
use nix::sys::signal;
use crate::nix::sys::signal;
mod prolog;
#[macro_use]
mod macros;
mod allocator;
mod arithmetic;
mod codegen;
mod clause_types;
mod debray_allocator;
mod fixtures;
mod forms;
mod heap_iter;
mod heap_print;
mod indexing;
mod instructions;
mod iterators;
mod machine;
mod read;
mod targets;
mod write;
use crate::prolog::machine::*;
use crate::prolog::read::*;
use machine::*;
use machine::streams::*;
use read::*;
use std::sync::atomic::Ordering;
extern fn handle_sigint(signal: libc::c_int) {
let signal = signal::Signal::from_c_int(signal).unwrap();
if signal == signal::Signal::SIGINT {
INTERRUPT.store(true, Ordering::Relaxed);
INTERRUPT.store(true, Ordering::Relaxed);
}
}
@@ -31,6 +64,6 @@ fn main() {
let handler = signal::SigHandler::Handler(handle_sigint);
unsafe { signal::signal(signal::Signal::SIGINT, handler) }.unwrap();
let mut wam = Machine::new(readline::input_stream());
let mut wam = Machine::new(readline::input_stream(), Stream::stdout());
wam.run_top_level();
}

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