1676 Commits

Author SHA1 Message Date
Mark
78b35dc57d update Cargo.toml version numbers and bump version number
Some checks failed
CI / build-test (macos-11, stable, bash, x86_64-apple-darwin) (push) Has been cancelled
CI / build-test (true, ubuntu-20.04, stable, bash, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-20.04, 1.65, bash, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-20.04, beta, bash, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-20.04, nightly, bash, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-20.04, stable, bash, i686-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (windows-latest, stable, msys2 {0}, x86_64-pc-windows-gnu) (push) Has been cancelled
CI / logtalk-test (push) Has been cancelled
CI / release (push) Has been cancelled
Docker Publish / build (push) Has been cancelled
2023-08-28 13:23:56 -06:00
Mark Thom
c65d1204af Merge pull request #1978 from aarroyoc/ci-32-bits
32 bit CI for Linux
2023-08-27 12:49:20 -06:00
Mark Thom
3bfed50d81 Merge pull request #1979 from triska/normalize_space
ENHANCED: rudimentary implementation of normalize_space
2023-08-27 12:48:48 -06:00
Adrián Arroyo Calle
4523bb5b81 32 bit CI for Linux 2023-08-27 19:25:54 +02:00
Markus Triska
75aec69de1 replace adjacent whitespace characters with a single SPACE character 2023-08-27 13:01:13 +02:00
Markus Triska
9b96735615 ENHANCED: rudimentary implementation of normalize_space
This removes leading and trailing whitespace from the text. Whitespace
within the string is not yet considered.
2023-08-27 09:56:30 +02:00
Mark Thom
f74d74fe6e Merge pull request #1972 from rujialiu/32-bit-support
32-bit system support #615 #1509 (WIP)
2023-08-25 10:51:15 -06:00
Rujia Liu
4fd059be5f 32-bit system support, addressing all (at most) 4GB addresses of RAM. 2023-08-24 20:05:40 +08:00
Mark Thom
81712f4c82 Merge pull request #1973 from triska/clpz
CLP(ℤ) improvements, notably related to queue handling
2023-08-22 15:04:33 -06:00
Markus Triska
98086de77e attach more propagators before starting propagation
This prevents repeated triggering of already attached propagators
while new propagators are being attached to involved variables.
2023-08-22 21:52:55 +02:00
Markus Triska
24456e9703 remove optional (+)/1 prefix in get_atts/2 and put_atts/2 calls
The (+)/1 prefix in get_atts/2 at line 4219 by itself already causes a
greater than 15% slowdown for the benchmark shown in #1730:

    ?- N #= 2^14,
       time(((between(1, N, _),
              X #\= Y,
              false)
            ; true)).

The performance impact is not a good reason to remove the optional
(+)/1 prefix! Performance issues should be addressed at the root, in
this case get_atts/2 (#1962). We should never manually work around
performance issues in built-in predicates.

In contrast, readability is a good argument, and I find the calls
slightly easier to read without the optional (+)/1 prefix.

The prefix is now consistently omitted when possible.
2023-08-22 21:52:55 +02:00
Markus Triska
7f024f3b8d use pneq/2 2023-08-22 21:52:55 +02:00
Markus Triska
67c1b171c7 enforce same queue in more constraints 2023-08-22 21:52:55 +02:00
Markus Triska
04fbb0c1ce append the entire queues instead of inserting each element individually 2023-08-22 21:52:55 +02:00
Markus Triska
327423ab84 adapt query to Scryer Prolog 2023-08-21 21:19:29 +02:00
Markus Triska
20cfffdff5 update answers 2023-08-21 21:19:29 +02:00
Markus Triska
d8e126044f use can_be/2 2023-08-21 21:19:29 +02:00
Mark Thom
61f975ea18 Merge pull request #1967 from rujialiu/optional-features
Allow users to disable optional features
2023-08-21 11:42:03 -06:00
Rujia Liu
25d4950216 Allow users to disable optional features. Needed for wasm32 support (see #615). 2023-08-21 10:01:24 +08:00
Mark Thom
5aa1521819 Merge pull request #1968 from rujialiu/atom-64bit
Make Atom 64-bit regardless of architecture
2023-08-20 11:50:58 -06:00
Mark Thom
dd030fa18b Merge pull request #1970 from triska/crrl
switch to using crrl for scalar multiplication on Curve25519
2023-08-20 11:24:37 -06:00
Markus Triska
013df58fea ENHANCED: more extensive type checking in curve25519_scalar_mult/3 2023-08-20 14:43:14 +02:00
Markus Triska
745ddc2c87 switch to using crrl for scalar multiplication on Curve25519
This is to facilitate WASM compilation as currently worked on
by @rujialiu in #615. Many thanks, and many thanks to @pornin
for crrl which makes this possible!
2023-08-20 14:43:14 +02:00
Rujia Liu
f6d3b2f896 Make Atom 64-bit regardless of architecture 2023-08-20 19:43:13 +08:00
Mark Thom
7da321ab4f Merge pull request #1964 from triska/master
use newly available double_quotes/1 write option in portray_clause_//1
2023-08-17 15:18:25 -06:00
Markus Triska
92fdc7e380 use newly available double_quotes/1 write option in portray_clause_//1 2023-08-17 23:05:21 +02:00
Mark Thom
2e811de0f5 Merge pull request #1963 from triska/master
ENHANCED: domain error in zcompare/3 (#1723)
2023-08-14 12:18:45 -06:00
Markus Triska
f02cd0ad3c ENHANCED: domain error in zcompare/3 (#1723) 2023-08-14 17:54:18 +02:00
Mark Thom
a36df33a67 Merge pull request #1961 from triska/succ
ADDED: succ/2, part of the Prologue for Prolog
2023-08-12 14:35:21 -06:00
Markus Triska
4a6bf5fd5f reorder tests, per https://github.com/mthom/scryer-prolog/pull/1961#pullrequestreview-1574380588 2023-08-11 23:41:54 +02:00
Markus Triska
26c0b4fc75 ADDED: succ/2, part of the Prologue for Prolog
Specification:

   https://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue#succ
2023-08-11 22:46:46 +02:00
Mark
fad363e64a shed CodeIndex for control predicates in disjuncts.rs (#1791) 2023-08-06 01:26:01 -06:00
Mark
094cf2ac5d retract discontiguous non-multifile predicates between consultations (#1202, #1058, #1585) 2023-08-05 17:42:33 -06:00
Mark Thom
cc82727d20 Merge pull request #1950 from triska/chars_si
ENHANCED: more efficient chars_si/1, using specialized predicates of Scryer
2023-08-04 15:25:42 -06:00
Markus Triska
924750f826 ENHANCED: more efficient chars_si/1, using specialized predicates of Scryer
Source:

    https://github.com/mthom/scryer-prolog/issues/1947#issuecomment-1665113488
2023-08-04 21:18:37 +02:00
Mark Thom
ffbf630b5c Merge pull request #1948 from triska/chars_si
add character_si/1 and use it to correct chars_si/1
2023-08-03 18:34:19 -06:00
Markus Triska
8073a4ba87 add character_si/1 and use it to correct chars_si/1
This addresses #1947.
2023-08-03 22:27:13 +02:00
Mark
ec450fc567 allocate negator results in arena (#1898) 2023-08-02 19:50:36 -06:00
Mark Thom
2c05ebbded Merge pull request #1943 from triska/dead_skeleton
FIXED: do not attach constraint if the propagator is already entailed and killed
2023-08-02 16:32:27 -06:00
Markus Triska
0ddda0a864 FIXED: do not attach constraint if the propagator is already entailed and killed
Example:

    ?- A#=A//A#==>B,A-B=1-1.
       A = 1, B = 1.

This addresses #1941.
2023-08-03 00:26:40 +02:00
Mark Thom
02328d818c Merge pull request #1938 from triska/remove_auxiliary_constraints
ENHANCED: forget auxiliary constraints set up by the propagator for multiplication
2023-07-31 14:52:34 -06:00
Mark Thom
0f55ba7218 Merge pull request #1939 from triska/oncology_trial_design
better wording, applying the feedback of @dcnorris. Thank you a lot!
2023-07-31 14:52:21 -06:00
Markus Triska
1c089a2bbb better wording, applying the feedback from @dcnorris. Thank you a lot! 2023-07-31 22:07:13 +02:00
Markus Triska
54a887cdc3 ENHANCED: forget auxiliary constraints set up by the propagator for multiplication
This addresses the issue raised in https://github.com/mthom/scryer-prolog/discussions/1937.
2023-07-31 21:57:25 +02:00
Mark Thom
ca28c76e52 Merge pull request #1929 from Skgland/master
detect and prevent concurrent AtomTable use
2023-07-29 10:52:36 -06:00
Bennet Bleßmann
a70157003b fix spelling 2023-07-29 13:11:00 +02:00
Bennet Bleßmann
6aa3c7d5d6 handle atom table resize
* bumping serial_test dev-dependency due to broken should_panic handling in old version
2023-07-29 11:22:25 +02:00
Bennet Bleßmann
4ef8c5c47d detect and prevent² concurrent AtomTable use
²in the case of `#[cfg(not(test))]` there is still a toctou race as I am not sufficently familiar with Atomics
2023-07-27 23:37:03 +02:00
Mark Thom
31d17f135a Merge pull request #1927 from triska/ascii_punctuation
FIXED: correct handling of ascii_punctuation in char_type/2 (#1926)
2023-07-27 14:51:01 -06:00
Markus Triska
03f7b01109 FIXED: correct handling of ascii_punctuation in char_type/2 (#1926) 2023-07-27 20:22:05 +02:00
Mark
bff48e7c7f simplify and correct prefix-bracket spacing in heap_print.rs (#1914, #1918) 2023-07-26 09:32:54 -06:00
Mark
3b67ffa814 overwrite code indices of dynamic_undefined predicates (dynamic, multifile, discontiguous) on export 2023-07-25 13:48:34 -06:00
Mark Thom
49b4e4cbcb Merge pull request #1907 from coasys/dashu
Replace rug with dashu
2023-07-25 11:35:10 -06:00
Mark
287c308bc3 track the parent operator of the current operator in heap_print to emit space if necessary (#1906) 2023-07-24 20:05:28 -06:00
Mark
af44d91568 Merge branch 'dashu' of https://github.com/coasys/scryer-prolog into coasys-dashu 2023-07-24 11:47:57 -06:00
Fayeed Pawaskar
40b6890c54 updated cargo to use git 2023-07-24 19:11:41 +05:30
Fayeed Pawaskar
7248425a76 Fixed warnings 2023-07-24 12:41:56 +05:30
Fayeed Pawaskar
1dcc1ca524 Fixed stackoverflow error 2023-07-24 12:19:41 +05:30
Fayeed Pawaskar
0e17d6acd7 remove rug completely 2023-07-24 10:12:22 +05:30
Fayeed Pawaskar
c89217903a fix all the move errors 2023-07-24 10:12:14 +05:30
Mark
762e6d3ba4 pop both pending redirections in format_bar_separator_op when max depth exceeded (#1903) 2023-07-23 15:02:27 -06:00
Mark
e9ae80e250 fix list abbreviation (#1901) 2023-07-23 14:43:13 -06:00
Mark
fd7f24e265 remove EMIT_NEWLINE (#1900) 2023-07-22 21:42:40 -06:00
Mark
24450a8827 use ExitCode when halting so Drop is called, close terminal stream in rustyline Drop 2023-07-22 19:06:13 -06:00
Mark Thom
57e8ed65b0 Merge pull request #1050 from pmoura/add_float_integer_part_and_float_fractional_part_standard_functions
WIP: Add float_integer_part/1 and float_fractional_part/1 standard arithmetic functions
2023-07-22 13:48:16 -06:00
Mark
3f819e2dfd additional write fixes, use rustyline 12.0.0 (#1876, #1901) 2023-07-22 11:53:09 -06:00
Mark
9a7862c322 correct max_depth option (#1876) 2023-07-22 11:11:23 -06:00
Mark Thom
92c77cdff5 Merge pull request #1899 from triska/master
remove another !/0 which is now no longer necessary due to improved indexing
2023-07-21 23:26:20 -06:00
Markus Triska
554e956ef5 remove another !/0 which is now no longer necessary due to improved indexing 2023-07-22 06:52:29 +02:00
Mark
60d9d01a55 Merge branch 'add_float_integer_part_and_float_fractional_part_standard_functions' of https://github.com/pmoura/scryer-prolog into pmoura-add_float_integer_part_and_float_fractional_part_standard_functions 2023-07-21 15:35:28 -06:00
Mark
cb79552dd0 correct max_depth option (#1876) 2023-07-21 15:05:27 -06:00
Mark
f310ff24a5 remove now unnecessary cut in lists.pl 2023-07-21 11:35:57 -06:00
Nicolas Luck
b1963864d2 Explicitly dereference pointer to avoid calling neg() on reference 2023-07-21 17:07:40 +02:00
Fayeed Pawaskar
b4e7000eb2 Fixed missing functionality in dashu with their methods still has some issue with move 2023-07-21 14:21:32 +05:30
Mark
e529e7ba21 improve goal expansion and (',') interpretation error handling 2023-07-20 14:27:10 -06:00
Mark
1697cd5c7f add log10, hyperbolic tan and inverse hyperbolic tan functions (#1898) 2023-07-20 12:33:51 -06:00
Mark
dcd7360b17 add EMIT_NEWLINE to add newlines to readline input only after query terms begin to be read (#1074, #1897) 2023-07-20 09:39:06 -06:00
Mark
4fd247f881 check for unexpected EOF in get_to_eof (#1897) 2023-07-19 17:13:47 -06:00
Mark
85bc544fb9 dereference TermWriteResult variables sooner in read_term_body (#1894) 2023-07-18 15:39:51 -06:00
Mark
14646074be remove failing append choicepoint in atom_concat/3 special case (#1893) 2023-07-18 14:44:02 -06:00
Mark Thom
1ba040c24d Merge pull request #1885 from mthom/setof_bagof_fixes
Fix group_by_variants/4 and keysort in setof/3
2023-07-18 12:35:10 -06:00
Mark
56f6772422 call write_read_term_options if read_term_from_chars/3 succeeds by unifying Term to end_of_file (#1892) 2023-07-18 12:15:20 -06:00
Mark
db43d461b9 catch errors thrown from tabling Worker (#1526, #1888) 2023-07-18 12:10:27 -06:00
Mark
42a50474da remove read/{1,2} as a builtin, write read options upon EOF, throw better domain errors in parse_read_term_options/2 2023-07-17 16:45:03 -06:00
Mark
cf367024fd add specialized EOF handling for user input (#1892) 2023-07-17 14:38:26 -06:00
Mark
67d198ac77 Merge branch 'read_term_eof_changes' 2023-07-17 13:22:51 -06:00
Mark
a154a34f87 omit anonymous variables from read_term variable_names and singletons lists 2023-07-17 13:20:10 -06:00
Mark
86c90d77dd do a better job handling EOF in read_term (#1887) 2023-07-17 13:19:30 -06:00
Mark
4e1a4dae6c print strings in tails of lists (#1890) 2023-07-17 11:38:50 -06:00
Fayeed Pawaskar
cf345d8174 wip dashu move 2023-07-17 20:40:41 +05:30
Mark Thom
a18da368a1 Merge pull request #1891 from triska/initialization_goals
ENHANCED: use newly available read_term_from_chars/3 for better errors
2023-07-16 10:26:13 -06:00
Markus Triska
5d3295c40c ENHANCED: use newly available read_term_from_chars/3 for better errors
Examples, previously:

    $ scryer-prolog -g "member(X,Ls"
    ?-
    $ scryer-prolog -g "member(X,Ls)"
    member(_542,_543) causes: error(existence_error(procedure,member/2),member/2)
    ?-

Now:

    $ scryer-prolog -g "member(X,Ls"
    "member(X,Ls" cannot be read: error(syntax_error(incomplete_reduction),read_term_from_chars/3:0)
    $ scryer-prolog -g "member(X,Ls)"
    member(X,Ls) causes: error(existence_error(procedure,member/2),member/2)
    ?-

This also addresses #1185.
2023-07-16 14:16:07 +02:00
Mark
de10ccfdee re-factor options handling of read_term into read_term_body (#1887) 2023-07-15 21:52:54 -06:00
Mark
65a8ce8e22 generalize simple goal detection to fix call/N test failures in logtalk test suite 2023-07-15 19:29:06 -06:00
Mark
5a7da721cd add read_term_from_chars/3 (#637) 2023-07-15 11:20:20 -06:00
Mark
b234ef7ea3 use double_quotes in write_error (#1886) 2023-07-15 10:58:25 -06:00
Mark
617c961f88 add is_inbuilt check to err_on_builtin_overwrite (#1872) 2023-07-15 10:44:00 -06:00
Mark
e95355e56e eliminate call_with_inference_limit/3 leaks (#1300) 2023-07-15 10:22:23 -06:00
Mark
ff5e9a793b add unknown flag to set_prolog_flag and current_prolog_flag 2023-07-14 19:10:10 -06:00
Mark
101d0548db fix group_by_variants/4 and keysort in setof/3 (#1440, #1856) 2023-07-14 18:52:29 -06:00
Mark
b6a81c51ab add (:-)/1 and (:-)/2 to ClauseType::is_inbuilt (#1872) 2023-07-14 12:44:28 -06:00
Mark
ba2cd43144 fix assert(a|z)/1 errors 2023-07-13 17:12:17 -06:00
Mark Thom
bd720b49f3 Merge pull request #1884 from triska/countall
ADDED: countall/2, for compatibility with GNU Prolog.
2023-07-13 16:09:29 -06:00
Markus Triska
9590d5200c ADDED: countall/2, for compatibility with GNU Prolog.
Example:

    ?- countall(member(X, "abc"), N).
       N = 3.
2023-07-13 23:08:13 +02:00
Mark
12f890e4a2 throw permission_error in compile_assert when attempting to assert a built-in (#1872) 2023-07-13 15:03:26 -06:00
Mark
a9cb826bf3 arith_eval_by_metacall may receive a stack variable 2023-07-13 13:58:51 -06:00
Mark
4163cb038d correct peek_code/2, don't set stream position in peek functions 2023-07-13 13:37:34 -06:00
Mark
b051f39145 correct peek_byte/2 bugs (#1882) 2023-07-13 12:23:20 -06:00
Mark
cfc49243c8 improve ground/1 performance (#1389) 2023-07-12 18:11:56 -06:00
Mark
29430ec88b fix peek_byte/2 crash (#1882) 2023-07-12 16:34:11 -06:00
Mark Thom
d520046a4f Merge pull request #1878 from mthom/lookahead_indexing
use lookahead to skip inapplicable clauses (#1028, #1502)
2023-07-12 16:21:06 -06:00
Markus Triska
814ce2d672 ENHANCED: improved determinism of member/2
Example:

    ?- member(X, "abc").
    %@    X = a
    %@ ;  X = b
    %@ ;  X = c.

This addresses #750.
2023-07-12 14:39:08 -06:00
Mark
4fd37335f5 use lookahead to skip inapplicable clauses (#1028, #1502) 2023-07-12 14:39:02 -06:00
Mark
1791bd8626 remove unsafe unwrap in put_char (#1881) 2023-07-12 14:24:20 -06:00
Mark
3f5dbc1680 emit stream aliases as permission error culprits whenever possible 2023-07-12 12:17:06 -06:00
Mark
44052cb373 Revert "Allow comparisons with stream terms"
This reverts commit 076a75d138.
2023-07-12 11:44:45 -06:00
Mark
8c33da11ce Revert "add stream alias check to atom/1 (#1855)"
This reverts commit b746a8f9ab.
2023-07-12 11:44:44 -06:00
Mark
ab80c84714 Revert "add stream alias processing to atom_chars/2, atom_codes/2"
This reverts commit 811ff65209.
2023-07-12 11:44:41 -06:00
Mark
bb420e9347 use indexing functions to set num_cells in allocate_and_frame/or_frame (#1877) 2023-07-11 13:43:34 -06:00
Mark
55a1f8d3da clean commented code from disjuncts.rs 2023-07-10 10:32:15 -06:00
Mark
fba7790637 remove errant debug_hook from read_and_match 2023-07-09 10:30:30 -06:00
Markus Triska
918dfca409 DOC: new section on applications of Scryer Prolog
This addresses an important aspect of #1777.
2023-07-09 10:29:53 -06:00
Markus Triska
d18f128a3c correct \\ to \, addressing #1865 2023-07-09 10:29:53 -06:00
Mark
c8b9059289 refine EOF handling more (#1873) 2023-07-09 10:29:47 -06:00
Mark
b8a6882a27 refine EOF handling 2023-07-08 22:25:30 -06:00
Mark
fb8e3071f2 follow EOF action after open_parsing_stream in get_char if stream at EOF 2023-07-08 19:27:32 -06:00
Mark
067b5998ee clarify EOF error across stream types and predicates (#1867, #1870) 2023-07-08 13:38:52 -06:00
Mark Thom
2e26f37f5e Merge pull request #1869 from triska/master
print version more readably, addressing #1868
2023-07-08 01:24:04 -06:00
Markus Triska
6525c1f543 print version more readably, addressing #1868 2023-07-08 08:19:05 +02:00
Mark
4db0b385f3 treat unexpected EOF as incomplete reduction in bracketed_comment 2023-07-07 13:04:27 -06:00
Mark
b7f77d1747 interpret '\u{0}' as end_of_file in get_char/1 2023-07-07 11:05:44 -06:00
Mark
811ff65209 add stream alias processing to atom_chars/2, atom_codes/2 2023-07-07 10:38:05 -06:00
Mark Thom
fd70d8975b Merge pull request #1832 from aarroyoc/http-fixes
Multiple fixes for http libraries
2023-07-06 16:30:56 -06:00
Mark
b746a8f9ab add stream alias check to atom/1 (#1855) 2023-07-06 11:38:12 -06:00
Mark
483e4568a2 add scc_block to MachineState to avoid SCC cleanup terms being deallocated too early (#1427) 2023-07-06 11:20:49 -06:00
Mark
9ff1b660f1 correct heap_print.rs tests 2023-07-05 18:11:09 -06:00
Mark
5ffdd2d91a shrink scope of control_entry_point catch, add CutPoint tag to printer 2023-07-05 14:50:49 -06:00
Mark Thom
5b81bee941 Merge pull request #1864 from triska/double_quotes
make double_quotes write option not dependent on double_quotes flag
2023-07-05 13:49:29 -06:00
Markus Triska
3b9b9e75c4 make double_quotes write option not dependent on double_quotes flag
This gives consistent results without depending on another flag.
2023-07-05 21:37:11 +02:00
Mark Thom
faef45fd68 Merge pull request #1862 from mthom/iso_conformity_tests
Revise iso_conformity_tests.pl to remove unnecessary spaces
2023-07-05 10:49:51 -06:00
Mark
b5b45dde9d add missing self.pos to peek_char slices (#1726) 2023-07-04 17:44:27 -06:00
Mark
5ab087bc1e revise iso_conformity_tests.pl in response to new ambiguity check of #1860 2023-07-04 16:12:03 -06:00
Mark
7683367c0e throw lexer errors from devour_whitespace (#1778) 2023-07-04 16:09:54 -06:00
Mark Thom
d2db66b9f7 Merge pull request #1861 from aarroyoc/fix-1855
Allow comparisons with stream terms
2023-07-04 15:11:59 -06:00
Mark
c58d8804a1 widen CharReader buffer (#1859) 2023-07-04 15:08:46 -06:00
Mark
5d09449c95 widen CharReader buffer (#1859) 2023-07-04 11:22:22 -06:00
Adrián Arroyo Calle
076a75d138 Allow comparisons with stream terms 2023-07-04 17:33:48 +02:00
Mark Thom
7f69ac3f4a Merge pull request #1857 from triska/master
advertise newly achieved strong syntactic conformance: all current tests pass
2023-07-03 14:05:16 -06:00
Markus Triska
7f159a7ed2 advertise newly achieved strong syntactic conformance: all current tests pass
This addresses an important aspect of #1777.
2023-07-03 22:04:26 +02:00
Mark Thom
42274ef3ae Merge pull request #1850 from mthom/iso-conformity-tests
Automate ISO syntax conformity tests
2023-07-03 13:53:41 -06:00
Mark
ab893be418 update tests 2023-07-03 13:18:53 -06:00
Mark
f5e7573bd6 correct tests #171 and #300 2023-07-03 12:09:26 -06:00
Mark
9cdad087ef add double_quotes write option for printing to strings, enable it at toplevel 2023-07-03 12:09:05 -06:00
Mark
e36f96fd47 correct ISO conformity test #185 2023-07-03 11:34:41 -06:00
Mark
cb25a90250 add iso-conformity-tests.pl to test suite 2023-07-02 11:10:33 -06:00
Mark
38a9d23174 correct initialization_goals misnomer in iso-conformity-tests.pl 2023-07-02 11:10:33 -06:00
Mark
521118265a make setup of test_166_167 pass 2023-07-02 11:10:33 -06:00
Mark
a09306c585 check ambiguity of "'" against tail if atom token is about to be quoted 2023-07-02 11:10:33 -06:00
Mark
8140ff9154 always print a space between prefix operator and its operand 2023-07-02 11:10:33 -06:00
Mark
3cbe78cb9b correct tests 259 and 304 of tests-pl/iso-conformity-tests.pl 2023-07-02 11:10:33 -06:00
Mark
699afb2c00 introduce tests-pl/iso-conformity-tests.pl 2023-07-02 11:10:33 -06:00
Mark
d079a18459 removing residual debugging comments from format.pl 2023-07-02 11:10:25 -06:00
Mark
b0566e4150 use lexer to detect remaining layout in parse_number_from_string (#1773) 2023-06-30 17:18:10 -06:00
Mark Thom
c38a26e6cd Merge pull request #1854 from triska/needs_bracketing
remove now unneeded check
2023-06-30 15:43:45 -06:00
Markus Triska
31030738a4 remove now unneeded check 2023-06-30 22:14:00 +02:00
Mark
caf84a259e check that F in needs_bracketing/2 is an atom 2023-06-30 13:45:45 -06:00
Mark
1620824d3a do not enclose '(' as atom in brackets (#1487) 2023-06-30 12:06:07 -06:00
Mark Thom
1d2f5053d6 Merge pull request #1852 from triska/needs_bracketing
Improvements to needs_bracketing/2
2023-06-30 11:13:24 -06:00
Markus Triska
42282c6e6e remove unneeded case: only operator definitions should count, not quoting 2023-06-30 18:03:21 +02:00
Markus Triska
a3f8ddd24a remove subsumed case: F == Value means Arity =:= 0, now considered above 2023-06-30 18:02:43 +02:00
Markus Triska
bfe808a779 shorten needs_bracketing/2 2023-06-30 18:01:54 +02:00
Markus Triska
28065b0565 constrain bracketing to operators with pertaining arity
Example:

    ?- X = -->(a,b,c).
       X = -->(a,b,c).
2023-06-30 18:01:17 +02:00
Mark Thom
4953cfd10e Merge pull request #1851 from triska/operator_bracketing
bracket all operators that are direct operands of (=)/2
2023-06-30 00:26:18 -06:00
Markus Triska
db972de40c bracket all operators that are direct operands of (=)/2
This addresses #804.
2023-06-30 08:03:38 +02:00
Adrián Arroyo Calle
330e9ba4ef Multiple fixes for http libraries
* use reqwest for http_open (still uses Hyper underneath)
* use Hyper 1.0.0-rc3 for server
* Modify all internal handling of server
2023-06-29 23:43:37 +02:00
Mark
bb09de1805 fix ReadlineStream peek_char using CharReader 2023-06-29 14:58:15 -06:00
Mark
a6a0cef9fc read the cell written to by mark_var when needed in compile_is (#1846) 2023-06-29 12:58:15 -06:00
Mark
c36bd4dc07 introduce CutPoint heap tag so that they can be offset by call_continuation/1 2023-06-29 12:14:15 -06:00
Mark Thom
83f352b95e Merge pull request #1849 from triska/showterm
remove unneeded single quotes
2023-06-29 12:11:12 -06:00
Markus Triska
c84a5c3282 remove unneeded single quotes 2023-06-29 19:25:23 +02:00
Mark
58af615dd4 correct and generalize current_predicate/1 2023-06-28 17:31:43 -06:00
Mark
c4b13a2176 unify stack variables to streams in unify_constant (#1845) 2023-06-27 17:35:10 -06:00
Mark
039fffb339 better detect syntax errors in lexer.rs (#1771) 2023-06-27 16:39:20 -06:00
Mark Thom
8613513b9c Merge pull request #1844 from triska/copy_term_nat
use copy_term_nat/2
2023-06-27 15:26:33 -06:00
Markus Triska
ceb276b249 use copy_term_nat/2 2023-06-27 22:37:23 +02:00
Mark
16f281e3d1 enable unification of streams to alias atoms (#1823) 2023-06-27 12:23:40 -06:00
Mark
b593fffc7d support module resolution in current_predicate/1 (#1817) 2023-06-27 11:08:29 -06:00
Mark
ce890799bc fix builtin_predicate (#1819) 2023-06-27 10:41:15 -06:00
Mark
aa65287c3b remove tabling attributes in each module's attribute_goals//1 (#1825) 2023-06-27 10:31:10 -06:00
Mark
a6522d6317 properly account for partial string offsets in '$skip_max_list' (#1827) 2023-06-26 16:36:04 -06:00
Mark
0b45d42912 mark chunk boundary at beginning of disjunct in disjuncts.rs (#1843) 2023-06-24 17:18:00 -06:00
Mark
d9829a3606 fix string incompleteness (#1828) 2023-06-24 14:12:08 -06:00
Mark Thom
59766e2db4 Merge pull request #1842 from infogulch/release-on-tag
Fix tags trigger; bump MSRV to 1.65
2023-06-24 13:13:07 -06:00
infogulch
e52a4fbfc0 Bump msrv to 1.65 due to bumping rug to 1.19
https://gitlab.com/tspiteri/rug#version-1190-news-2023-01-06
2023-06-24 13:21:42 -05:00
infogulch
c05afb4705 Fix tags trigger 2023-06-24 13:05:57 -05:00
Mark
9f209dadd9 fix branch subsumption bug (#1840, #1841) 2023-06-24 10:59:29 -06:00
Mark Thom
2ec45b7413 Merge pull request #1838 from triska/get_line_to_chars
MODIFIED: read_line_to_chars/3 is now called get_line_to_chars/3
2023-06-24 09:19:22 -06:00
Mark
bf581879e6 update README 2023-06-24 09:11:30 -06:00
Markus Triska
9bc3757a9e another case of "read" --> "get", for an only internally used predicate 2023-06-24 11:48:28 +02:00
Markus Triska
18d0a74f23 MODIFIED: read_line_to_chars/3 is now called get_line_to_chars/3
This is for consistency with other I/O predicates, where "read" always
indicates Prolog terms. Please adjust your programs accordingly.
2023-06-24 11:47:32 +02:00
Mark
7a188744da correct code_walker.rs in light of compilation improvements 2023-06-23 16:53:18 -06:00
Mark
7f45ac3f7a Merge branch 'compiling_disj' 2023-06-23 15:45:04 -06:00
Mark
612861e010 correct reversions after rebase 2023-06-23 14:32:37 -06:00
Mark
fcae0d9fcf polish perm free list management 2023-06-23 14:11:31 -06:00
infogulch
f446939770 Add steps to publish binaries when releases are tagged 2023-06-23 14:11:31 -06:00
Mark
92853a6a12 free local cut variables after cut 2023-06-23 14:11:31 -06:00
Mark
4ad113a6f8 mark is/2 allocated permanent variables as safe, add CompareNumber terms to ClauseType::is_inlined 2023-06-23 14:11:31 -06:00
dependabot[bot]
89ed1aa8de Bump openssl from 0.10.48 to 0.10.55
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.48 to 0.10.55.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.48...openssl-v0.10.55)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-23 14:11:31 -06:00
Mark
d7f5675727 improve call/N implementation (#1829) 2023-06-23 14:11:31 -06:00
Mark
4d982d22c1 set_local_value does not make values safe (#1812) 2023-06-23 14:11:31 -06:00
Mark
5ed1802f0f read set_value args from temp regs of put_unsafe_value (#1812) 2023-06-23 14:11:31 -06:00
Markus Triska
2716381e7b FIXED: correct dereferencing in atom_codes/2 and number_codes/2.
This addresses #1818.

Test case:

    run :-
	    length(Ls, L),
	    portray_clause(L),
	    maplist(=(X), Ls),
	    X = Y,
	    Y = 12,
	    atom_codes(_, Ls),
	    false.
2023-06-23 14:11:31 -06:00
Adrián Arroyo Calle
c5c83d724a Rename INDEX.md to INDEX.dj and add banner about Scryer Prolog Meetup 2023-06-23 14:11:31 -06:00
Mark
749dedf477 read from machine stack in stackful pre-order iterator (#1812) 2023-06-23 14:11:31 -06:00
notoria
911c49c43f Compute correctly the domain of the remainder 2023-06-23 14:11:31 -06:00
notoria
770a682d8b Don't add variable
?- Z #= 0, Z #= X mod Y.
   Z = 0, clpz:(_A*Y#=X), clpz:(Y in inf.. -1\/1..sup) % Unexpected.

The expected result:
   Z = 0, clpz:(X mod Y#=0), clpz:(Y in inf.. -1\/1..sup).
2023-06-23 14:11:31 -06:00
notoria
73ca37ecca Remove and move comments 2023-06-23 14:11:31 -06:00
Mark
e0f49e8f43 optionally read from machine stack in stackful pre-order iterator (#1812) 2023-06-23 14:11:31 -06:00
Mark
dae34b6009 affirm integers as rational/1 (#1810) 2023-06-23 14:11:31 -06:00
Adrián Arroyo Calle
5850125d97 Update select crate to 0.6.0 and remove warning 2023-06-23 14:11:31 -06:00
Markus Triska
97bd778745 FIXED: correctly reset counter in reset_gensym/2 (#1807)
Many thanks to @infradig for detecting this issue and suggesting this
correction!
2023-06-23 14:11:31 -06:00
Markus Triska
43df2e2649 shorten gensym/2 2023-06-23 14:11:31 -06:00
Markus Triska
c2f2623471 extend logic to all control and whitespace characters
This addresses #1802.
2023-06-23 14:11:31 -06:00
Mark
8e4465315f use same logic to print Chars and Atoms (#1804) 2023-06-23 14:11:31 -06:00
Markus Triska
86beb222ae rely on first instantiated argument indexing in the definitions of foldl/N
This allows shorter and more natural definitions.
2023-06-23 14:11:31 -06:00
Markus Triska
5e124ccf44 ENHANCED: allow Roman numerals in strings
Example:

    ?- X = "ↁ".
       X = "ↁ".

This addresses #1790.
2023-06-23 14:11:31 -06:00
Markus Triska
47d4e6d2f9 FIXED: consistent read/write of further control characters, and non-breaking space
Example:

    ?- X = '\xa0\'.
       X = '\xa0\'.

This addresses #1768.
2023-06-23 14:11:31 -06:00
Mark
33f65210ee make tests compatible 2023-06-23 14:11:31 -06:00
Mark
9ea6cb4cab backtrack on emission of unsafe register instructions on internal branches 2023-06-23 14:11:31 -06:00
Mark
0e583d620a implement new disjunction compilation 2023-06-23 14:11:31 -06:00
Mark Thom
b205abe949 remove BuildIf, BuildNot, BuildThen TermIterState variants 2023-06-23 14:11:31 -06:00
Mark Thom
cb59c3003a correct chunk type labeling 2023-06-23 14:11:30 -06:00
Mark Thom
942095baa7 remove GetLevelAndUnify and replace it with GetCutPoint 2023-06-23 14:11:30 -06:00
Mark Thom
097849385e add QueryTerm::ChunkTypeBoundary 2023-06-23 14:11:30 -06:00
Mark Thom
c4783062ff delete ChunkedTerm, chunked iteration 2023-06-23 14:11:30 -06:00
Mark Thom
063cf0c608 new TermIterState variants 2023-06-23 14:11:30 -06:00
Mark Thom
a66d666bed variable classification al a carte 2023-06-23 14:11:22 -06:00
Mark Thom
170818759d add more variable probing, chunk type labeling 2023-06-23 14:11:07 -06:00
Mark Thom
e41d1b319b adapt code generation 2023-06-23 14:11:04 -06:00
Mark Thom
b9c9de5222 add classifications and occurrence counting 2023-06-23 13:54:46 -06:00
Mark Thom
d565f5901b milestone marker for surgery 2023-06-23 13:54:04 -06:00
Mark Thom
46317c3a39 begin adapting the techniques of "Compiling Large Disjunctions" 2023-06-23 13:52:24 -06:00
Mark Thom
bcc5bff376 Merge pull request #1834 from infogulch/ci-publish-releases
Add steps to publish binaries when releases are tagged
2023-06-23 12:01:32 -06:00
infogulch
5244d71570 Add steps to publish binaries when releases are tagged 2023-06-22 23:36:04 -05:00
Mark Thom
9fac289d9a Merge pull request #1833 from mthom/dependabot/cargo/openssl-0.10.55
Bump openssl from 0.10.48 to 0.10.55
2023-06-22 11:30:56 -06:00
dependabot[bot]
de665c05d8 Bump openssl from 0.10.48 to 0.10.55
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.48 to 0.10.55.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.48...openssl-v0.10.55)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-21 22:54:36 +00:00
Mark
98b0ab3409 improve call/N implementation (#1829) 2023-06-10 17:32:18 -06:00
Mark
3c344b176b set_local_value does not make values safe (#1812) 2023-06-06 00:58:44 -06:00
Mark
6093c2858d read set_value args from temp regs of put_unsafe_value (#1812) 2023-05-29 20:49:54 -06:00
Mark Thom
e06ab1ca1c Merge pull request #1822 from triska/master
FIXED: correct dereferencing in atom_codes/2 and number_codes/2.
2023-05-29 10:15:43 -06:00
Markus Triska
5154314786 FIXED: correct dereferencing in atom_codes/2 and number_codes/2.
This addresses #1818.

Test case:

    run :-
	    length(Ls, L),
	    portray_clause(L),
	    maplist(=(X), Ls),
	    X = Y,
	    Y = 12,
	    atom_codes(_, Ls),
	    false.
2023-05-29 11:43:56 +02:00
Mark Thom
fa07306f09 Merge pull request #1820 from aarroyoc/banner-meetup
Rename INDEX.md to INDEX.dj and add banner about Scryer Prolog Meetup
2023-05-28 16:39:05 -06:00
Adrián Arroyo Calle
07115ce4f5 Rename INDEX.md to INDEX.dj and add banner about Scryer Prolog Meetup 2023-05-29 00:29:53 +02:00
Mark
b656700294 read from machine stack in stackful pre-order iterator (#1812) 2023-05-28 12:59:01 -06:00
Mark Thom
7bc7f0ad06 Merge pull request #1816 from notoria/clpz
Small fixes for `CLP(ℤ)`
2023-05-27 11:07:50 -06:00
notoria
495df8846a Compute correctly the domain of the remainder 2023-05-27 13:47:14 +02:00
notoria
05d48cdcc3 Don't add variable
?- Z #= 0, Z #= X mod Y.
   Z = 0, clpz:(_A*Y#=X), clpz:(Y in inf.. -1\/1..sup) % Unexpected.

The expected result:
   Z = 0, clpz:(X mod Y#=0), clpz:(Y in inf.. -1\/1..sup).
2023-05-27 13:41:04 +02:00
notoria
dc02be4944 Remove and move comments 2023-05-27 13:08:25 +02:00
Mark
462097d956 optionally read from machine stack in stackful pre-order iterator (#1812) 2023-05-26 15:19:07 -06:00
Mark
0e374c2e96 affirm integers as rational/1 (#1810) 2023-05-24 13:58:43 -06:00
Mark Thom
94a1313916 Merge pull request #1809 from aarroyoc/update-select
Update select crate to 0.6.0 and remove warning
2023-05-18 12:31:44 -06:00
Adrián Arroyo Calle
b54a4afad6 Update select crate to 0.6.0 and remove warning 2023-05-17 18:19:19 +02:00
Mark Thom
c71b8e05f0 Merge pull request #1808 from triska/gensym
gensym-related improvement and correction
2023-05-16 15:55:39 -06:00
Markus Triska
021c01dfd0 FIXED: correctly reset counter in reset_gensym/2 (#1807)
Many thanks to @infradig for detecting this issue and suggesting this
correction!
2023-05-16 22:42:57 +02:00
Markus Triska
30f222b837 shorten gensym/2 2023-05-16 22:41:20 +02:00
Mark Thom
f7d9237bb9 Merge pull request #1805 from triska/master
extend logic to all control and whitespace characters
2023-05-14 23:27:49 -06:00
Markus Triska
49addc7b04 extend logic to all control and whitespace characters
This addresses #1802.
2023-05-14 09:25:08 +02:00
Mark
2f9996f9ac use same logic to print Chars and Atoms (#1804) 2023-05-10 00:04:35 -06:00
Mark Thom
c1218fc986 Merge pull request #1801 from triska/foldl
rely on first instantiated argument indexing in the definitions of foldl/N
2023-05-04 21:25:16 -06:00
Mark Thom
57ef706eb0 Merge pull request #1799 from triska/roman_numerals
ENHANCED: allow Roman numerals in strings
2023-05-04 21:25:03 -06:00
Mark Thom
e4b19dc1dd Merge pull request #1800 from triska/nbsp
FIXED: consistent read/write of further control characters, and non-breaking space
2023-05-04 21:24:45 -06:00
Markus Triska
4e60cc46a2 rely on first instantiated argument indexing in the definitions of foldl/N
This allows shorter and more natural definitions.
2023-05-04 00:50:27 +02:00
Markus Triska
d8edf7bfff FIXED: consistent read/write of further control characters, and non-breaking space
Example:

    ?- X = '\xa0\'.
       X = '\xa0\'.

This addresses #1768.
2023-05-03 23:29:22 +02:00
Markus Triska
e951db662d ENHANCED: allow Roman numerals in strings
Example:

    ?- X = "ↁ".
       X = "ↁ".

This addresses #1790.
2023-05-03 22:02:47 +02:00
Mark
c5a3ec3ba8 fix current_predicate/1 (#1761) 2023-04-24 23:23:27 -06:00
Mark Thom
f12a90351a Merge pull request #1794 from aarroyoc/uppercase-2
Fix to_upper/to_lower when string is instantiated
2023-04-23 17:08:21 -06:00
Adrián Arroyo Calle
b162c40007 Fix to_upper/to_lower when string is instantiated 2023-04-23 23:01:29 +02:00
Mark Thom
402100fd52 Merge pull request #1797 from triska/clpb_residuals
remove residual goal for ground BDD
2023-04-23 10:31:40 -06:00
Markus Triska
2a1b8f37ec remove residual goal for ground BDD
Example:

    ?- sat(X).
       X = 1.
2023-04-23 09:23:00 +02:00
Mark
7d2e59ab64 discard CodeIndex literals from unfolded control operators in preprocessor (#1791) 2023-04-22 18:38:50 -06:00
Mark
f35298a227 add and document inlined_instructions/2 to/in diag.pl (#1791) 2023-04-22 18:38:49 -06:00
Mark Thom
198e925430 Merge pull request #1796 from triska/dif_si
ADDED: dif_si/2
2023-04-22 17:21:03 -06:00
Markus Triska
8b7281fad0 ADDED: dif_si/2
Source: https://stackoverflow.com/questions/20223390/prolog-a-person-is-a-sibling-of-himself

In Scryer Prolog, this is actually not needed, since Scryer Prolog provides
dif/2 in library(dif). However, it is still useful to provide dif_si/2 for two reasons:

  1) to more easily port code from systems where only dif_si/2 is available
  2) to provide correct disequality in other systems that adopt this library
2023-04-23 00:37:14 +02:00
Mark Thom
502574dfc3 Merge pull request #1795 from infogulch/fix-ci-install
Use --force to install cargo2junit
2023-04-22 16:03:39 -06:00
infogulch
7a0f4e5787 Use --force to install cargo2junit
This probably appeared now because it's the first time this tool was cached
from a previous run.
2023-04-22 15:42:10 -05:00
Mark Thom
f277f660f3 Merge pull request #1793 from infogulch/fix-action-ref
Change ref for `publish-unit-test-result-action`
2023-04-22 11:26:45 -06:00
infogulch
9c037d6028 Change ref for publish-unit-test-result-action 2023-04-22 10:18:10 -05:00
Mark Thom
396528189d Merge pull request #1789 from aarroyoc/uppercase
library(charsio): add to_upper and to_lower
2023-04-20 07:17:09 +02:00
Adrián Arroyo Calle
4b882c465c library(charsio): add to_upper and to_lower 2023-04-18 18:58:09 +02:00
Mark Thom
8937cac47d Merge pull request #1783 from infogulch/test
Refactor CI Workflow
2023-04-18 08:35:45 +02:00
infogulch
5a3e2899dd Refactor CI Workflow
* Rename the workflow from Test to CI, since it does more than tests
* Run logtalk tests in a separate job to improve isolation
* Publish all xunit/junit test files as build artifacts to be consumed
  by a separate publishing workflow.
* Add "job summary" feature to show a formatted summary of the test
  results on the job summary status page
* Run the CI job once every Wed to ensure that there are always some
  recent builds on master that haven't expired.
2023-04-14 14:55:49 -05:00
Mark Thom
3a5ed4723b Merge pull request #1785 from mthom/dependabot/cargo/h2-0.3.17
Bump h2 from 0.3.15 to 0.3.17
2023-04-14 06:22:32 +02:00
Mark Thom
dcf4c44173 Merge pull request #1786 from triska/tuples_in
FIXED: propagation for ground tuples
2023-04-14 06:22:18 +02:00
Markus Triska
5763a4b9df FIXED: propagation for ground tuples
Example:

    ?- tuples_in([[A,A]],[[0,1],[2,0]]).
       false.

See https://github.com/triska/clpz/issues/22.
2023-04-13 23:39:45 +02:00
dependabot[bot]
82200a21eb Bump h2 from 0.3.15 to 0.3.17
Bumps [h2](https://github.com/hyperium/h2) from 0.3.15 to 0.3.17.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.15...v0.3.17)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-13 17:03:50 +00:00
Mark Thom
fc7d98d748 Merge pull request #1780 from triska/tuples_in
Various improvements to tuples_in/2
2023-04-12 19:10:50 +02:00
Markus Triska
5dce7d9075 FIXED: enforce equality also for ground elements in tuples
Example:

    ?- tuples_in([[A,B]], [[1,2],[3,4]]),
       tuples_in([[A,B]], [[3,2]]).
       false.

See https://github.com/SWI-Prolog/swipl-devel/issues/1160.
2023-04-11 22:33:19 +02:00
Markus Triska
f08f539768 do not create attributed variables for ground tuples 2023-04-11 21:26:18 +02:00
Markus Triska
be45672e22 actually disable and reenable the queue 2023-04-11 21:26:18 +02:00
Markus Triska
94efb9ffe3 remove no longer needed clpz_relation attributes 2023-04-11 21:26:18 +02:00
Markus Triska
fd1e902492 do not leave an attribute when (re-)enabling a queue 2023-04-11 21:14:18 +02:00
Mark Thom
8d6e3d7d56 Merge pull request #1776 from infogulch/master
Add iso prolog / logtalk tests to test runner
2023-04-07 06:09:08 +02:00
infogulch
a197bb6815 Allow the test suite to fail without failing the build 2023-04-06 09:40:03 -05:00
infogulch
9de8456c7f Add logtalk test suite 2023-04-02 17:31:20 -05:00
infogulch
ebf8091dba Test workflow cleanup; switch to macos-11 2023-04-02 14:47:52 -05:00
Mark Thom
47ce84b273 Merge pull request #1772 from triska/number_chars
FIXED: number_chars(N, "0' "), addressing #1580.
2023-04-02 19:24:59 +02:00
Mark Thom
449e097f4a Merge pull request #1774 from infogulch/master
Optimize Test GH action
2023-04-02 19:24:36 +02:00
Markus Triska
fe27605497 FIXED: number_chars(N, "0' "), addressing #1580.
There may be a more elegant way to solve this.
2023-04-02 10:04:52 +02:00
infogulch
8792ee438c Add cache step to test workflow 2023-04-02 02:09:41 -05:00
Mark Thom
7279062d3c Merge pull request #1770 from triska/doc
Smaller documentation improvements to explain compilation and target directory
2023-04-01 23:28:19 +02:00
Markus Triska
b87fe1e21f DOC: link to "Indexing dif/2" 2023-04-01 11:04:37 +02:00
Markus Triska
d6ac125425 DOC: explain location of scryer-prolog after compilation
This question was recently raised on the #scryer IRC channel.
2023-04-01 11:04:37 +02:00
Markus Triska
b79d8732ea use the release flag so that the instructions can be used verbatim
Also, the location of the executable depends on this flag.
2023-04-01 11:04:33 +02:00
Markus Triska
3df0806017 change "run" to "build", since "run" leads to a Scryer prompt which can be unexpected 2023-04-01 11:03:31 +02:00
Mark Thom
c7759aa737 Merge pull request #1769 from triska/show_pending_constraints
ENHANCED: use call_residue_vars/2 to show all pending constraints
2023-03-31 04:51:34 +02:00
Markus Triska
8ab1155fc5 ENHANCED: use call_residue_vars/2 to show all pending constraints
Example:

    ?- freeze(_, false).
       freeze:freeze(_A,false).

This was originally added in 04ba9bc11a,
then reverted, and is now restored.
2023-03-30 23:49:33 +02:00
Mark Thom
adc77985d7 broaden the definition of alpha_char! (#1749, #1515, #1591) 2023-03-26 11:10:46 -06:00
Mark Thom
d85fc7c9f8 Merge pull request #1765 from mthom/dependabot/cargo/openssl-0.10.48
Bump openssl from 0.10.42 to 0.10.48
2023-03-25 06:20:00 +01:00
dependabot[bot]
41b083c962 Bump openssl from 0.10.42 to 0.10.48
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.42 to 0.10.48.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.42...openssl-v0.10.48)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-25 01:27:40 +00:00
Mark Thom
4ee6a7bfb8 add '$unattributed_var' builtin (#1758) 2023-03-18 23:08:38 -06:00
Mark Thom
6e53d08d40 Merge pull request #1759 from triska/better_queue
Better queue
2023-03-16 04:34:33 +01:00
Markus Triska
01285f12c3 remove no longer needed queue attributes after propagation 2023-03-15 21:17:06 +01:00
Markus Triska
cd586aab8c show remaining queue/2 attributes as residual goals
This lets us verify that all attributes are correctly removed earlier.
2023-03-15 19:56:02 +01:00
Mark Thom
4da646252b Revert "correct call_residue_vars/3 using new copy_term_3 (#1239)"
This reverts commit 9b35a316c9.
2023-03-14 21:34:49 -06:00
Mark Thom
cc8bb38abc Revert "use new call_residue_vars/2 in toplevel.pl (#847)"
This reverts commit 04ba9bc11a.
2023-03-14 21:34:48 -06:00
Mark Thom
04ba9bc11a use new call_residue_vars/2 in toplevel.pl (#847) 2023-03-12 17:56:54 -06:00
Mark Thom
9b35a316c9 correct call_residue_vars/3 using new copy_term_3 (#1239) 2023-03-12 17:56:54 -06:00
Mark Thom
3a522f3c98 Merge pull request #1757 from aarroyoc/fix-docs-1756
DOCS: Fixes #1756
2023-03-12 18:26:54 +01:00
Adrián Arroyo Calle
4c44859132 Fixes #1756 2023-03-12 16:47:20 +01:00
Mark Thom
ecd77f7512 Merge pull request #1755 from triska/doc
various smaller documentation improvements
2023-03-10 02:40:29 +01:00
Markus Triska
56bd596af3 use actual toplevel answers 2023-03-08 23:35:11 +01:00
Markus Triska
21acb9361a use string notation as discussed on #scryer IRC 2023-03-08 23:32:05 +01:00
Markus Triska
c3477d8476 items --> elements
This addresses #1740.
2023-03-08 23:31:02 +01:00
Mark Thom
cba09d4ea1 Merge pull request #1754 from triska/dif
ENHANCED: more compact definition of dif/2
2023-03-08 21:48:34 +01:00
Markus Triska
884b0ca10e FIXED: Take all variables into account during goal projection.
This addresses #1751.
2023-03-08 21:16:54 +01:00
Markus Triska
c9ecfb11d9 ENHANCED: more compact definition of dif/2
As outlined in #1753.
2023-03-08 20:50:33 +01:00
Mark Thom
9288dcabe9 Merge pull request #1744 from aarroyoc/ffi
Foreign Function Interface - library(ffi)
2023-03-02 21:57:56 +01:00
Adrián Arroyo Calle
73df96244d Fill more cases 2023-03-01 22:13:04 +01:00
Adrián Arroyo Calle
7396630627 Merge branch 'master' into ffi 2023-02-28 22:10:40 +01:00
Adrián Arroyo Calle
0ac93751d0 FFI: Documentation 2023-02-28 22:09:35 +01:00
Mark Thom
400ca21213 invoke '$default_attr_list' in project_attributes.pl (#1748) 2023-02-26 22:42:02 -07:00
Mark Thom
3286e78cd2 third argument of copy_term should be instantiated as a list (#1747) 2023-02-26 22:42:02 -07:00
Mark Thom
24eb9ce483 Merge pull request #1746 from triska/type_over_instantiation_error
ENHANCED: must_be/2: prefer type error over instantiation error
2023-02-26 23:46:23 +01:00
Markus Triska
3dc6ed79d2 ENHANCED: must_be/2: prefer type error over instantiation error
This addresses #1594.
2023-02-26 22:28:18 +01:00
Adrián Arroyo Calle
f94294dbd9 FFI: Nested structs 2023-02-26 20:48:00 +01:00
Mark Thom
04ba58067a add, implement and use the Unifier trait 2023-02-26 12:21:22 -07:00
Adrián Arroyo Calle
92b262d599 Macroization of the code 2023-02-25 22:27:28 +01:00
Mark Thom
7ffb40e0ad Merge pull request #1743 from triska/rely_on_indexing
rely on first instantiated argument indexing
2023-02-25 19:23:59 +01:00
Markus Triska
997161c740 rely on first instantiated argument indexing
This great improvement to indexing allows much more natural
definitions of virtually all meta-predicates. Many thanks to @notoria!
2023-02-25 10:17:55 +01:00
Mark Thom
84c95c59e9 Merge pull request #1741 from triska/clpz_residuals
in projection of residual goals, mark considered propagators as processed
2023-02-23 01:51:22 +01:00
Markus Triska
669242a8ce DOC: update residual goals 2023-02-23 00:10:36 +01:00
Markus Triska
2a04d5e799 in projection of residual goals, mark considered propagators as processed
This is to avoid duplicated goals with the new projection mechanism.
2023-02-23 00:05:27 +01:00
Adrián Arroyo Calle
9d52d2a653 MVP of Foreign Function Interface 2023-02-22 23:10:03 +01:00
Mark Thom
95f6ebc000 assign responsibility for emitting dif goal to the first variable of the left-hand term (#1739) 2023-02-22 10:28:29 -07:00
Mark Thom
6e9cd072c5 catch attribute_goals errors in copy_term/3, don't discard variable module qualifiers in dcg_body/3 (#1738) 2023-02-21 00:50:46 -07:00
Mark Thom
729f2b1cb9 Merge pull request #1737 from triska/link_to_homepage
add link to newly available homepage
2023-02-20 21:10:01 +01:00
Markus Triska
1a01438064 add link to newly available homepage
Many thanks to @aarroyoc for the documentation system, and for hosting the page!
2023-02-20 20:09:47 +01:00
Robert Jacobson
34ec6d3167 Changed the links for the delimited continuations papers and the precise garbage collection paper. 2023-02-20 01:11:52 -07:00
Robert Jacobson
c9295323f6 Added links to referenced research papers in the Phase 2 and Nice to Have Features sections. 2023-02-20 01:11:52 -07:00
Mark Thom
92d543b8a8 change '$delete_all_attributes' to '$delete_all_attributes_from_var' 2023-02-20 01:11:52 -07:00
Mark Thom
3f445c76be add '$delete_all_attributes', use copy_term/3 as defined in #1272 2023-02-20 01:11:51 -07:00
Mark Thom
a6e416f13d compile '$atts' and '$project_atts' modules using loader.pl 2023-02-20 01:11:35 -07:00
Mark Thom
601ff567e3 keep phrase goal qualified even if qualifier is a variable 2023-02-17 00:20:15 -07:00
Mark Thom
56783b8e4b correct incremental compilation bugs 2023-02-12 23:41:25 -07:00
Mark Thom
326f18ea75 copy attributed variable attribute lists specially via copy_attr_var_list 2023-02-12 17:30:04 -07:00
Mark Thom
491472a8c5 retire TrailedAttrVarHeapLink TrailEntry tag 2023-02-10 22:52:27 -07:00
Mark Thom
359619e035 simplify and optimize attributed variables (#1590, #1634, #1730) 2023-02-10 21:56:42 -07:00
Mark Thom
9454d670c7 port '$get_from_list' to '$get_from_attr_list' in Rust 2023-02-06 01:23:29 -07:00
Mark Thom
2fcec4fff9 include wambook errata 2023-02-04 18:14:16 -07:00
Mark Thom
2c2a9fe01e correct shl stub_gen 2023-02-03 22:27:15 -07:00
Mark Thom
165a55dac6 Merge pull request #1734 from triska/clpz_corrections
DOC: teletype font for reification
2023-02-04 05:49:11 +01:00
Markus Triska
196e9c1e47 DOC: teletype font for reification 2023-02-03 20:47:08 +01:00
Mark Thom
17450520ba shift by usize instead of u32 in shl and shr (#1718, #1719) 2023-02-02 20:49:39 -07:00
Mark Thom
97702f3071 Merge pull request #1732 from aarroyoc/docs-minor-fixes
Minor fixes to docs
2023-02-03 01:58:52 +01:00
Adrián Arroyo Calle
e8408ca93f Minor fixes to docs 2023-02-02 21:35:35 +01:00
Mark Thom
e0dcf88b68 Merge pull request #1731 from triska/clpz_corrections
correct DocLog ~/.scryerrc rendering
2023-02-02 06:26:08 +01:00
Mark Thom
cc38cf15a4 Merge pull request #1729 from aarroyoc/negative-shifts
Negative shifts (fixes #1719 and #1718)
2023-02-02 06:25:52 +01:00
Markus Triska
da4c0a359b correct DocLog ~/.scryerrc rendering 2023-02-01 23:26:52 +01:00
Mark Thom
ce56a7303e avoid arena allocation of stream in read_term_from_chars (#1266) 2023-01-31 00:15:57 -07:00
Mark Thom
5667ec8699 update tokio version 2023-01-30 23:27:07 -07:00
Adrián Arroyo Calle
36b3150225 Negative shifts (fixes #1719 and #1718) 2023-01-30 18:56:37 +01:00
Mark Thom
28c338416f Merge pull request #1728 from aarroyoc/docs-csv
Compatible Doclog docs for library(csv)
2023-01-30 04:24:04 +01:00
Mark Thom
26a8fc1a37 Merge pull request #1727 from triska/clpz_corrections
DOC: use valid Prolog terms as predicate indicators
2023-01-30 04:23:52 +01:00
Adrián Arroyo Calle
f347baafa3 Compatible Doclog docs for library(csv) 2023-01-29 22:36:16 +01:00
Markus Triska
95278c221b DOC: use valid Prolog terms as predicate indicators 2023-01-29 21:48:22 +01:00
Mark Thom
5a28366158 Merge pull request #1725 from aarroyoc/fix-docs-http-server
Fix library(http/http_server) docs. Other minor fixes
2023-01-28 20:12:09 +01:00
Mark Thom
ac0cea8a73 Merge pull request #1724 from triska/clpz_corrections
CLP(ℤ) corrections
2023-01-28 20:11:48 +01:00
Markus Triska
814b631543 use DocLog syntax for section anchors and links within the document 2023-01-28 17:37:43 +01:00
Markus Triska
0d8c7f8785 small documentation adjustments 2023-01-28 17:37:43 +01:00
Markus Triska
5bae8fcaf8 FIXED: use lsb/2 and msb/2 from library(arithmetic)
This addresses #1720.
2023-01-28 17:37:43 +01:00
Adrián Arroyo Calle
4110cfcfcb Fix library(http/http_server) docs. Other minor fixes 2023-01-28 16:00:27 +01:00
Mark Thom
2bdba261ea Merge pull request #1717 from triska/format_doc
correct table layout for entries that themselves contain |
2023-01-27 07:47:53 +01:00
Markus Triska
58fb851717 correct table layout for entries that themselves contain | 2023-01-27 00:22:48 +01:00
Mark Thom
1118b37c92 Merge pull request #1674 from aarroyoc/docs-builtins
Compatible Doclog docs for builtins
2023-01-26 05:10:20 +01:00
Mark Thom
39606a2277 Merge pull request #1705 from triska/format_doc
DOC: convert library(format) documentation to DocLog format
2023-01-26 02:05:23 +01:00
Mark Thom
275d306b69 Merge pull request #1706 from triska/si_doc
DOC: convert library(si) documentation to DocLog format
2023-01-26 02:03:24 +01:00
Mark Thom
e948413c09 Merge pull request #1707 from triska/reif_doc
DOC: add link to "Indexing dif/2" in DocLog format
2023-01-26 02:03:14 +01:00
Mark Thom
f8bb3148d0 Merge pull request #1708 from triska/freeze_doc
DOC: add documentation for library(freeze) in DocLog format
2023-01-26 02:02:58 +01:00
Mark Thom
e7cf70936a Merge pull request #1709 from triska/pio_doc
DOC: convert library(pio) documentation to DocLog format
2023-01-26 02:02:47 +01:00
Mark Thom
409287e8f8 Merge pull request #1710 from triska/tabling_doc
DOC: initial documentation for library(tabling) in DocLog format
2023-01-26 02:02:35 +01:00
Mark Thom
87d6ef16f7 Merge pull request #1711 from triska/pairs_doc
DOC: initial documentation for library(pairs) in DocLog format
2023-01-26 02:02:22 +01:00
Mark Thom
3c08eba559 Merge pull request #1712 from triska/lambda_doc
DOC: convert code samples in library(lambda) to DocLog format
2023-01-26 02:02:12 +01:00
Mark Thom
d399b68d01 Merge pull request #1713 from triska/simplex_doc
DOC: convert library(simplex) documentation to DocLog format
2023-01-26 02:02:01 +01:00
Markus Triska
2e9ec653a8 DOC: convert library(simplex) documentation to DocLog format 2023-01-26 00:46:06 +01:00
Markus Triska
7ca782b92d DOC: convert code samples in library(lambda) to DocLog format 2023-01-26 00:40:08 +01:00
Markus Triska
996496c3f5 DOC: initial documentation for library(pairs) in DocLog format 2023-01-26 00:36:05 +01:00
Markus Triska
90cf713186 add link to Desouter et al., "Tabling as a Library with Delimited Control" 2023-01-26 00:25:26 +01:00
Markus Triska
ca4aaf44de DOC: initial documentation for library(tabling) in DocLog format 2023-01-26 00:20:53 +01:00
Markus Triska
b04d845ec0 DOC: convert library(pio) documentation to DocLog format 2023-01-26 00:09:41 +01:00
Markus Triska
a8ea2b0f97 DOC: add documentation for library(freeze) in DocLog format 2023-01-25 23:54:30 +01:00
Markus Triska
a29227d0d4 DOC: add link to "Indexing dif/2" in DocLog format 2023-01-25 23:40:55 +01:00
Markus Triska
7f8f137aa0 DOC: convert library(si) documentation to DocLog format 2023-01-25 23:27:45 +01:00
Markus Triska
cc7e721611 DOC: convert library(format) documentation to DocLog format 2023-01-25 23:18:39 +01:00
Mark Thom
058cbcf19a Merge pull request #1704 from triska/time_doc
DOC: correctly format the table using DocLog syntax
2023-01-25 22:51:05 +01:00
Markus Triska
22b815dc5c DOC: correctly format the table using DocLog syntax 2023-01-25 22:29:14 +01:00
Mark Thom
c709853aa7 Merge pull request #1703 from aarroyoc/docs-arithmetic
Compatible Doclog docs for library(arithmetic) and small fixes on INDEX.md
2023-01-25 21:40:15 +01:00
Adrián Arroyo Calle
d755bb7e12 Apply feedback on builtins 2023-01-25 21:04:10 +01:00
Adrián Arroyo Calle
6d99912b7b Compatible Doclog docs for library(arithmetic) and small fixes on INDEX.md 2023-01-25 19:48:01 +01:00
Mark Thom
82dd9e0596 Merge pull request #1701 from triska/diag_doc
DOC: add DocLog documentation for library(diag)
2023-01-25 00:16:53 +01:00
Mark Thom
6ba3d49034 Merge pull request #1702 from triska/debug_doc
DOC: add DocLog documentation for library(debug)
2023-01-25 00:16:34 +01:00
Markus Triska
64be8e0fba DOC: add DocLog documentation for library(debug) 2023-01-25 00:19:14 +01:00
Markus Triska
43a297b691 DOC: add DocLog documentation for library(diag) 2023-01-25 00:09:44 +01:00
Mark Thom
df266378c4 Merge pull request #1689 from triska/clpb_doc
DOC: preliminary CLP(B) documentation in DocLog format
2023-01-24 22:51:35 +01:00
Mark Thom
ec55765cb2 Merge pull request #1697 from triska/crypto_doc
DOC: convert library(crypto) documentation to DocLog format
2023-01-24 22:46:14 +01:00
Mark Thom
de2ff8118f Merge pull request #1696 from triska/clpz_doc
DOC: improve CLP(ℤ) DocLog documentation
2023-01-24 22:45:51 +01:00
Mark Thom
c30c9dc98f Merge pull request #1698 from triska/time_doc
DOC: convert library(time) documentation to DocLog format
2023-01-24 22:43:53 +01:00
Mark Thom
1f872c3f0c Merge pull request #1694 from aarroyoc/docs-dcgs
Compatible Doclog docs for library(dcgs)
2023-01-24 22:43:43 +01:00
Mark Thom
b6204eb6f1 Merge pull request #1699 from triska/sgml_doc
DOC: convert library(sgml) documentation to DocLog format
2023-01-24 22:43:22 +01:00
Markus Triska
af9f0f81d8 DOC: convert library(sgml) documentation to DocLog format 2023-01-24 22:42:58 +01:00
Markus Triska
1c08b56e05 strengthen reified division for divisor == 1 2023-01-24 22:15:05 +01:00
Markus Triska
6cb8020f62 DOC: add CLP(B) documentation in DocLog format 2023-01-24 22:15:05 +01:00
Markus Triska
d742d4cde9 DOC: convert library(time) documentation to DocLog format 2023-01-24 22:14:15 +01:00
Markus Triska
b7d06540e6 DOC: convert library(crypto) documentation to DocLog format 2023-01-24 22:13:38 +01:00
Markus Triska
909f2e1058 DOC: improve CLP(ℤ) DocLog documentation 2023-01-24 20:39:47 +01:00
Mark Thom
26d39c3617 Merge pull request #1695 from aarroyoc/website
Add website frontpage
2023-01-24 20:29:48 +01:00
Mark Thom
371bc3b231 Merge pull request #1676 from aarroyoc/docs-http-server
Compatible Doclog docs for library(http/http_server)
2023-01-24 04:09:39 +01:00
Adrián Arroyo Calle
bdeabcdd89 Website page 2023-01-23 23:51:27 +01:00
Adrián Arroyo Calle
7f177c3d03 Compatible Doclog docs for library(dcgs) 2023-01-23 21:03:11 +01:00
Mark Thom
4ed36a12e9 Merge branch 'retract_improvements' 2023-01-22 21:04:55 -07:00
Mark Thom
a7e93db363 improve retract/1 and related predicates (#1598) 2023-01-22 21:04:41 -07:00
Mark Thom
789715f31a Merge pull request #1693 from aarroyoc/docs-xpath
Compatible Doclog docs for library(xpath).
2023-01-23 04:04:00 +01:00
Mark Thom
7cc900d437 Merge pull request #1691 from aarroyoc/docs-os-3
Compatible Doclog docs for library(os)
2023-01-23 04:02:57 +01:00
Mark Thom
ff0f6f4fc2 Merge pull request #1690 from aarroyoc/docs-charsio
Compatible Doclog docs for library(charsio)
2023-01-23 04:02:02 +01:00
Mark Thom
5698e8f21a Merge pull request #1692 from aarroyoc/docs-between
Compatible Doclog docs for library(between).
2023-01-23 04:00:11 +01:00
Adrián Arroyo Calle
ddcae2c906 Compatible Doclog docs for library(xpath). 2023-01-22 21:22:54 +01:00
Adrián Arroyo Calle
468d096ccb Compatible Doclog docs for library(between). 2023-01-22 20:26:30 +01:00
Adrián Arroyo Calle
bca79d12c0 Compatible Doclog docs for library(os) 2023-01-22 17:45:50 +01:00
Adrián Arroyo Calle
03eba9594b Compatible Doclog docs for library(charsio) 2023-01-21 21:05:13 +01:00
Mark Thom
5e0e3e2754 Merge pull request #1687 from gruhn/master
Fix failing Docker build
2023-01-21 05:02:33 +01:00
Mark Thom
04fd835267 Merge pull request #1688 from aarroyoc/djot-migration
Migrate from Markdown to Djot
2023-01-21 04:48:26 +01:00
Adrián Arroyo Calle
f072761150 Fix assoc.pl file 2023-01-19 21:18:40 +01:00
Adrián Arroyo Calle
46d1e3bee3 Migrate from Markdown to Djot 2023-01-19 21:15:25 +01:00
Niklas Gruhn
f8e6e0252d Use lastest 1.xx Rust version in Docker build
With the previous Rust version 1.61, the build fails with

> error[E0658]: use of unstable library feature 'scoped_threads'

This has been "stabilized" in Rust 1.63. To avoid the hassle
of manually updating the version, we can just default to the
latest minor release.
2023-01-18 19:08:19 +01:00
Mark Thom
f9e3bdb6b0 restore free list usage on structures in facts without crashing lgtunit loader 2023-01-13 18:35:13 -07:00
Mark Thom
a80aab48cd Merge pull request #1677 from aarroyoc/docs-dif
Compatible Doclog docs for library(dif)
2023-01-13 07:50:17 +01:00
Mark Thom
3a4aa2a541 tighten deallocate truncation of stack (#1686) 2023-01-12 23:47:07 -07:00
Mark Thom
84583da5b8 Merge pull request #1685 from triska/reify_sign
ADDED: sign/1 is now reifiable.
2023-01-11 17:41:14 +01:00
Markus Triska
f213956ceb use (#)/1 2023-01-11 17:23:53 +01:00
Markus Triska
c5caa9d311 ADDED: sign/1 is now reifiable.
This addresses #1500.
2023-01-11 17:22:38 +01:00
Mark Thom
0c4d93f01f remove add_term_to_free_list from AddToList (#1684) 2023-01-10 18:24:48 -07:00
Mark Thom
a4e8bfc1ba Merge pull request #1682 from triska/reify_slash
FIXED: correctly reify (/)/2.
2023-01-10 23:44:48 +01:00
Markus Triska
542b9e1976 rely on newly available operator notation for (#)/1 2023-01-10 22:59:11 +01:00
Markus Triska
73a1ee59fa replace several more instances of ?/1 by (#)/1 2023-01-10 22:57:14 +01:00
Markus Triska
2771109427 use (#)/1 already internally for describing constraint projections 2023-01-10 22:50:38 +01:00
Markus Triska
cc420bd31a FIXED: reification of (xor)/2.
Example:

    ?- A #= 1 xor 0 #<==> R.
    %@    clpz:(A#=1#<==>R), clpz:(R in 0..1).
2023-01-10 22:09:41 +01:00
Markus Triska
2fe1d2ef53 FIXED: correctly reify (/)/2.
Example:

    ?- 0 #==> X #= 1/2.
    %@    clpz:(X in inf..sup)
    %@ ;  false.

This addresses #1501.
2023-01-10 17:35:43 +01:00
Mark Thom
bb624cc971 use free lists to allow register re-use (#1612) 2023-01-08 12:11:04 -07:00
Mark Thom
aa7b8e52f3 use '$enqueue_attr_var' when adding attributes only 2022-12-24 00:31:47 -07:00
Adrián Arroyo Calle
07f358c91b Compatible Doclog docs for library(dif) 2022-12-22 23:23:07 +01:00
Adrián Arroyo Calle
8ae0a1a4af Compatible Doclog docs for library(http/http_server) 2022-12-22 22:36:33 +01:00
Adrián Arroyo Calle
7647ad14b8 More feedback applied 2022-12-21 23:19:38 +01:00
Adrián Arroyo Calle
21e4347b3e Apply some feedback 2022-12-21 22:32:36 +01:00
Adrián Arroyo Calle
f2d041ea23 Merge branch 'master' into docs-builtins 2022-12-21 21:29:22 +01:00
Adrián Arroyo Calle
56c1c4e43c Compatible Doclog docs for builtins 2022-12-21 21:28:16 +01:00
Mark Thom
820011412b Merge pull request #1658 from aarroyoc/copy_file
Add predicate file_copy/2 in library(files)
2022-12-18 21:22:25 +01:00
Mark Thom
81afa6942f Merge pull request #1672 from triska/deref_register
dereference more registers, analogous to d660e4244f
2022-12-18 21:21:58 +01:00
Markus Triska
6cb8d7596a dereference more registers, analogous to d660e4244f
See also #1654 for a nice test case by @notoria which this corrects.
2022-12-18 17:43:33 +01:00
Mark Thom
a2d46af5ac Merge pull request #1671 from aarroyoc/docs-iso-ext
Compatible Doclog docs for library(iso_ext)
2022-12-18 00:38:22 +01:00
Mark Thom
6d94b8ab75 Merge pull request #1669 from aarroyoc/docs-http-open
Compatible Doclog docs for library(http/http_open).
2022-12-18 00:38:13 +01:00
Adrián Arroyo Calle
0166c3bf0f Compatible Doclog docs for library(iso_ext) 2022-12-17 22:46:44 +01:00
Adrián Arroyo Calle
6a995fb62b Compatible Doclog docs for library(http/http_open). 2022-12-17 20:43:43 +01:00
Mark Thom
d804d8a92e use proper dynamic arities in JmpByCall and JmpByExecute (#1605, #1606) 2022-12-17 11:54:20 -07:00
Mark Thom
cf63e8375d track attr_var_init lengths on the stack (#1667) 2022-12-16 20:10:17 -07:00
Mark Thom
1eff758751 update README to point to local wambook (#1668) 2022-12-16 00:45:00 -07:00
Mark Thom
0a8fc70ba9 detect cyclic bindings in attr_vars_of_term (#1666) 2022-12-16 00:42:38 -07:00
Mark Thom
b4b72a3166 Merge pull request #1662 from mthom/interpreting_disj
Fix mishandled if-then-else interpretation (#1659)
2022-12-16 04:36:02 +01:00
Mark Thom
a5054c0064 use append/3 rather than set_difference/3 to gather witnesses in bagof/3 and setof/3 (#1663, #1664) 2022-12-12 23:55:55 -07:00
Mark Thom
705f421d53 Merge pull request #1661 from aarroyoc/lcm2
Add predicate lcm/2 to library(arithmetic)
2022-12-12 00:38:40 +01:00
Mark Thom
4dc0114c52 fix mishandled if-then-else interpretation (#1659) 2022-12-11 16:35:32 -07:00
Adrián Arroyo Calle
5f2c77fa74 Add predicate lcm/2 to library(arithmetic) 2022-12-11 21:49:41 +01:00
Adrián Arroyo Calle
5f703afed1 Rename copy_file/2 to file_copy/2 2022-12-11 00:08:41 +01:00
Adrián Arroyo Calle
c6aa2068e2 Add predicate copy_file/2 in library(files) 2022-12-11 00:06:56 +01:00
Mark Thom
209f7a239a Merge pull request #1657 from aarroyoc/docs-files
Compatible Doclog docs for library(files)
2022-12-10 18:09:01 +01:00
Mark Thom
0d404ad374 Merge pull request #1656 from aarroyoc/docs-ordsets
Compatible Doclog docs for library(ordsets)
2022-12-10 18:08:45 +01:00
Mark Thom
d5db0c641c Merge pull request #1655 from triska/deref_register
more uses of newly available deref_register(n)
2022-12-10 18:08:26 +01:00
Adrián Arroyo Calle
21d6220f3f Compatible Doclog docs for library(files) 2022-12-08 23:41:28 +01:00
Adrián Arroyo Calle
e0464d5447 Compatible Doclog docs for library(ordsets) 2022-12-08 22:30:48 +01:00
Markus Triska
309e5b320e more uses of newly available deref_register(n) 2022-12-07 23:19:31 +01:00
Mark Thom
afa9703eb5 Merge pull request #1653 from triska/deref_register
introduce and use deref_register(n)
2022-12-07 05:52:07 +01:00
Markus Triska
56e5da6680 introduce and use deref_register(n) 2022-12-06 21:07:50 +01:00
Mark Thom
a8810d73e4 Merge pull request #1652 from aarroyoc/docs-lists
Compatible Doclog docs for library(lists)
2022-12-06 17:57:46 +01:00
Mark Thom
4b31c30cab Merge pull request #1651 from aarroyoc/docs-sockets
Compatible Doclog docs for library(sockets)
2022-12-06 17:57:36 +01:00
Adrián Arroyo Calle
df4b56148f Compatible Doclog docs for library(lists) 2022-12-06 13:19:10 +01:00
Adrián Arroyo Calle
f22a48f576 Compatible Doclog docs for library(sockets) 2022-12-06 11:58:26 +01:00
Mark Thom
d660e4244f dereference encoding register in crypto_data_decrypt (#1650) 2022-12-05 23:28:47 -07:00
Mark Thom
d383e5eb8b avoid pushing stack variables to the heap in get_continuation_chunk (#1644) 2022-12-05 23:17:15 -07:00
Mark Thom
e1dc114517 Merge pull request #1649 from aarroyoc/docs-ugraphs
Compatible Doclog docs for library(ugraphs)
2022-12-05 06:05:13 +01:00
Mark Thom
73780ebf63 Merge pull request #1648 from aarroyoc/docs-random
Compatible Doclog docs for library(random)
2022-12-05 06:05:02 +01:00
Mark Thom
a19f7a0b9f Merge pull request #1647 from aarroyoc/docs-uuid
Compatible Doclog docs for library(uuid)
2022-12-05 06:04:48 +01:00
Mark Thom
e07473d155 Merge pull request #1646 from aarroyoc/docs-assoc
Compatible Doclog docs for library(assoc)
2022-12-05 06:04:30 +01:00
Adrián Arroyo Calle
d429b263eb Compatible Doclog docs for library(ugraphs) 2022-12-05 00:09:32 +01:00
Adrián Arroyo Calle
b2a0d0c1c7 Compatible Doclog docs for library(random) 2022-12-02 23:43:24 +01:00
Adrián Arroyo Calle
8bafd7adb1 Compatible Doclog docs for library(uuid) 2022-12-02 23:23:16 +01:00
Adrián Arroyo Calle
76d24fe4e3 Compatible Doclog docs for library(assoc) 2022-12-02 23:05:03 +01:00
Mark Thom
3bdcc3aba9 return -1 from get_code to indicate end of file (#1622) 2022-11-18 18:19:55 -07:00
Mark Thom
cb25b27963 Merge pull request #1640 from gruhn/master
Adjust Github Action: Docker Publish
2022-11-16 20:57:35 +01:00
Niklas Gruhn
b3df81c143 Adjust Github Action: Docker Publish
1. Not only publish Docker images when new release tags are created but
   on every push to master, since release frequency is so low.

2. Use newer versions of the various actions (setup-buildx-action,
   login-action, metadata-action, ...) to suppress some deprecation
   warnings see:
   https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-11-15 18:55:15 +01:00
Mark Thom
68b3c480c9 update Dockerfile to use rustc 1.61 2022-11-14 21:18:03 -07:00
Mark Thom
9fd1bf5574 mention #scryer in README 2022-11-14 21:16:46 -07:00
Mark Thom
b491a06c6d README rustc version bump 2022-11-14 21:13:52 -07:00
Mark Thom
d16312a314 use existing bindings in compile_is (#1545) 2022-11-14 20:58:07 -07:00
Mark Thom
d19a8d6b98 begin to mark registers as safe from built-in predicates like is/2 (#1545) 2022-11-14 20:58:07 -07:00
Mark Thom
c90dd80ece revise UnsafeVarMarker (#1545) 2022-11-14 20:57:51 -07:00
Mark Thom
eb9d865635 Merge pull request #1635 from mthom/rebis-dev
Some checks failed
Test / windows (push) Has been cancelled
Docker Publish / build (push) Has been cancelled
Test / build (macos-10.15, beta) (push) Has been cancelled
Test / build (macos-10.15, stable) (push) Has been cancelled
Test / build (ubuntu-20.04, beta) (push) Has been cancelled
Test / build (ubuntu-20.04, stable) (push) Has been cancelled
Test / msrv (macos-10.15) (push) Has been cancelled
Test / msrv (ubuntu-20.04) (push) Has been cancelled
Merge rebis-dev into master
2022-11-10 07:18:10 +01:00
Mark Thom
694c87cb61 bump version to 0.9.1 2022-11-10 06:59:03 +01:00
Mark Thom
c41aba6b90 remove num-rug-adapter 2022-11-10 06:56:57 +01:00
Mark Thom
28ea672e36 move asserta/2, assertz/2 to iso_ext 2022-11-10 06:47:32 +01:00
Mark Thom
9366a48d6d introduce InlineTermStream to avoid arena allocations during call_inline (#1576) 2022-11-09 00:01:01 +01:00
Mark Thom
c7e1f5d568 Merge pull request #1630 from epilys/fix-1625
fix type_error with instantiated EOF -1 byte literal in get_byte/2
2022-10-26 23:37:36 -06:00
Markus Triska
d91ee5b77c FIXED: CLP(ℤ): Correctly remove all attributes during propagation of all_distinct/1 and other global constraints.
The constraints from library(clpz) were already correctly removed, but
others such as pending freeze/2 goals were not, potentially leading to
an accumulation of redundant constraints during propagation.

Test case:

   ?- freeze(A,(X=1;X=2)), all_distinct([A]), A = 1.
      A = 1, X = 1
   ;  A = 1, X = 2.

The combination of freeze/2 and CLP(ℤ) is useful for example when
creating animations of search processes.

This addresses #1624.
2022-10-26 23:36:07 -06:00
Mark Thom
fd97b84916 build partial strings using PStrLoc in write_term_to_heap (#1627) 2022-10-26 23:36:07 -06:00
Mark Thom
23f59970cb export asserta/2 (#1623) 2022-10-26 23:36:07 -06:00
Mark Thom
8781e03863 cache ball terms before unifying in handle_ball/3 (#1608) 2022-10-26 23:36:07 -06:00
Adrián Arroyo Calle
f2940ddfcf Some minor fixes. Thanks to @triska for them! 2022-10-26 23:36:07 -06:00
Mark Thom
140149f051 fix crash in read_term (#1616) 2022-10-26 23:36:07 -06:00
Skgland
9055370326 adjust rust-version declaration 2022-10-26 23:36:07 -06:00
Adrián Arroyo Calle
1af62c8d11 Set default Shell 2022-10-26 23:36:07 -06:00
Adrián Arroyo Calle
ec5430826e Add Windows MSYS2 2022-10-26 23:36:07 -06:00
Markus Triska
b2cccab768 ENHANCED: library(crypto): Faster conversion to bytes when the integer is known.
These entailed constraints only affect performance, and can be removed
without affecting the correctness of the code. They speed up scalar
multiplication of a curve point, used for example in Bitcoinolog.
2022-10-26 23:36:07 -06:00
Markus Triska
579aa8acd4 use newly available u8s_to_string 2022-10-26 23:36:07 -06:00
Markus Triska
9c2cb144b5 remove redundant error handling, since this case is checked in Prolog
This restores f668640e3d, where this
change was already made. It seems it was accidentally overwritten in
0404c3bd94.
2022-10-26 23:36:07 -06:00
Markus Triska
3d04689660 introduce and use u8s_to_string 2022-10-26 23:36:07 -06:00
Markus Triska
a891cc4edf use (slightly) better variable names 2022-10-26 23:36:07 -06:00
Markus Triska
706ab2ae5b use newly available must_be(chars, ...) for better and shorter type test 2022-10-26 23:36:07 -06:00
Markus Triska
9c1de8e00b ENHANCED: Eliminate the OpenSSL dependency of library(crypto).
This is achieved by using the newly available crrl crate by @pornin
to implement crypto_curve_scalar_mult/4 for secp256k1. Many thanks!
2022-10-26 23:36:07 -06:00
Mark Thom
1109e05e06 properly match strings concluding with characters in lists (#1597) 2022-10-26 23:36:07 -06:00
Markus Triska
ce313b8a6b correct nth0/4 and nth1/4
As per 56b430ecaf (commitcomment-82155379).
2022-10-26 23:36:07 -06:00
Markus Triska
7c96b91663 remove nth1/3, which is now provided by library(lists) 2022-10-26 23:36:07 -06:00
Markus Triska
87e966e185 remove nth1/3, which is now provided by library(lists) 2022-10-26 23:36:07 -06:00
Markus Triska
1e018681de ENHANCED: Much improved nth0/3, taken from #1529.
This also adds nth0/4 and bootstraps nth1/[3,4] from it.
2022-10-26 23:36:07 -06:00
Markus Triska
5b324680ba ADDED: must_be/can_be(not_less_than_zero, ...)
See #1309.
2022-10-26 23:36:07 -06:00
Markus Triska
abca5fc405 better implementation of list_si/1
Per discussion in https://github.com/mthom/scryer-prolog/pull/1589.
2022-10-26 23:36:07 -06:00
Markus Triska
6a455d2866 ADDED: chars_si/1, testing for a string
Examples:

    ?- chars_si("hello").
       true.

    ?- chars_si(5).
       false.

    ?- chars_si([a,b,c|Rs]).
       error(instantiation_error,sort/2).
2022-10-26 23:36:07 -06:00
Markus Triska
70da818101 use new domain names from library(error): octet_character, octet_chars 2022-10-26 23:36:07 -06:00
Markus Triska
5ec837acb4 ADDED: octet_character and octet_chars, testing for strings that represent bytes. 2022-10-26 23:36:07 -06:00
Markus Triska
76e33d051c rely on the built-in DCG expansion, per discussion in #1577 2022-10-26 23:36:07 -06:00
Markus Triska
7c5318b784 ENHANCED: Use '$skip_max_list'/4 for greater efficiency of nth0/3.
This is now possible due to 4c7e2eb8614da9c04f2286f6116b468017fb442c.

See #1529 for the suggestion by @UWN.
2022-10-26 23:36:07 -06:00
Markus Triska
47892bf24a ENHANCED: Improve determinism of ... //0 and seq//1.
Example:

    ?- phrase(..., "hello").
       true.

This addresses #1577.
2022-10-26 23:36:07 -06:00
Mark Thom
8a9cd7779c offset into complete strings with '$skip_max_list'/4 (#1529) 2022-10-26 23:36:07 -06:00
Mark Thom
d4c0277065 don't return ops with priority 0 when building set for current_op (#1571) 2022-10-26 23:36:07 -06:00
Mark Thom
069e132c0e treat functors with PI '.'/2 as lists (#1570) 2022-10-26 23:36:07 -06:00
Mark Thom
4e6c138099 introduce expand_call_goal/3 to expand goals in control meta-predicates (#1568) 2022-10-26 23:36:07 -06:00
Markus Triska
0ab355eada use a DCG to describe the path 2022-10-26 23:36:07 -06:00
Markus Triska
44825826df remove entailed must_be/2 goals 2022-10-26 23:36:07 -06:00
Markus Triska
612f09893c use newly available ... //0 from library(dcgs) 2022-10-26 23:36:07 -06:00
Markus Triska
e9f507b868 add meta_predicate/1 declaration for phrase_to_file/3 2022-10-26 23:36:07 -06:00
Mark Thom
78278c804f strip modules from GRBody in phrase/3 (#1565) 2022-10-26 23:36:07 -06:00
Mark Thom
b51460a59a fail on non-list solutions in setof/3 (#1553) 2022-10-26 23:36:07 -06:00
Adrián Arroyo Calle
181be5be3f HTTP Server 2.0 2022-10-26 23:36:07 -06:00
Mark Thom
91d4e91f53 prefix inlined goals with module names in expand_subgoal/5 (#1551) 2022-10-26 23:36:07 -06:00
Mark Thom
ad3ae7991b try to invoke '$call_inline' a second time in call/N (#1543) 2022-10-26 23:36:07 -06:00
Markus Triska
c7caf6b7a9 ENHANCED: CLP(ℤ): Reduce redundant propagator invocations during all_distinct/1 filtering
First, the current propagator is now logged and not re-triggered
during filtering. Second, and more significantly, all neq_num/2
constraints are scheduled and processed before more global constraints
are invoked. In this way, all the distilled information can be taken
into account by subsequently invoked global constraints.

These changes yield a 3-fold improvement in several Sudoku instances,
and a significant runtime reduction in social golfer instance 8-4-9.
2022-10-26 23:36:07 -06:00
Markus Triska
4422ffe39f add meta_predicate/1 declarations 2022-10-26 23:36:07 -06:00
Mark Thom
1ff52f70aa resolve phrase modules internally (#1541) 2022-10-26 23:36:07 -06:00
Mark Thom
fce45167a5 fail when exception caught from subgoal expansion (#1535) 2022-10-26 23:36:07 -06:00
Mark Thom
b8f384045c fix module resolution in dcgs, call/N (#1539) 2022-10-26 23:36:07 -06:00
Mark Thom
ea95a7900c deduplicate index ptr inlining for 0-arity atoms (#1538) 2022-10-26 23:36:07 -06:00
Mark Thom
edea1273c8 trim get_structure and put_structure arities when last arg is an index ptr (#1536) 2022-10-26 23:36:07 -06:00
Mark Thom
ec9c763211 print index_ptr offset (#1534) 2022-10-26 23:36:07 -06:00
Mark Thom
5a08117e75 use separate predicate for findall cleanup 2022-10-26 23:36:07 -06:00
Mark Thom
0aec980aa8 inference count call_inline, fail on undefined index 2022-10-26 23:36:07 -06:00
Mark Thom
6b05ee5130 streamline assertz/1, asserta/1 2022-10-26 23:36:07 -06:00
Mark Thom
1ffbf63d20 inline metacalls 2022-10-26 23:36:07 -06:00
Manos Pitsidianakis
b905d2758d fix type_error with instantiated EOF -1 byte literal in get_byte/2
According to ISO Prolog, get_byte/2 predicate can receive an
instantiated input byte:

http://www.gprolog.org/manual/html_node/gprolog037.html#sec156

    get_byte(+stream_or_alias, ?in_byte)

Since in_byte can be -1 if EOF is reached, instantiating it with -1
should work but does not because the implementation is trying to convert
it to a u8 which is unsigned:

?- open("/dev/null", read, S, [type(binary)]), get_byte(S, -1).
   error(type_error(in_byte,-1),get_byte/2).

This commit adds an extra check for -1 before checking for a valid u8
instantiated value if in_byte is an input:

?- open("/dev/null", read, S, [type(binary)]), get_byte(S, -1).
   S = '$stream'(0x55601e65c998).

Closes #1625
2022-10-26 13:49:38 +03:00
Mark Thom
311d985145 Merge pull request #1628 from epilys/fix-1626
Fix xor/2 type error reporting wrong argument
2022-10-25 20:46:49 -06:00
Manos Pitsidianakis
a9a06b5297 Fix xor/2 type error reporting wrong argument
If first argument n1 in xor/2 is of wrong numerical type, the match
patterns fall through to a catch all case that reports the second
argument in the type error.

Fixes #1626

`xor/2 function reports the wrong argument in type error #1626`

https://github.com/mthom/scryer-prolog/issues/1626
2022-10-24 19:26:43 +03:00
Mark Thom
6b8e620495 Merge pull request #1530 from RossSmyth/FixImports
Fixed imports for num-rug-adapter
2022-07-18 21:36:46 -06:00
Ross Smyth
577f85099d Fixed imports num-rug-adapter 2022-07-17 02:06:37 -04:00
Mark Thom
607c84a79b capture and propagate (=..)/2 errors in phrase/{2,3} (#1460) 2022-06-21 22:47:55 -06:00
Mark Thom
2eec6499ff propagate inference_limit_exceeded errors through the loader (#1298) 2022-06-18 11:26:31 -06:00
Mark Thom
cd1150c11d use setup_cal_n_init_goal_info in dynamic_module_resolution 2022-06-15 22:33:13 -06:00
Mark Thom
987bbdecf5 improve efficiency of call/N, replace '$call_with_default_policy' with
'$call_with_inference_counting'
2022-06-12 22:34:07 -06:00
Mark Thom
a68394b6f2 simplify detecting undefined goal_ and term_expansion 2022-06-12 22:34:07 -06:00
Mark Thom
3d6fbabc86 Merge pull request #1510 from Skgland/add-gitattributes
add a .gitattributes file
2022-06-12 15:44:14 -06:00
Skgland
1479b5d38c add a .gitattributes file
- this should stop problems such as mthom/scryer-prolog#1491
  by specifying the line ending
  as this should take precedence over the global config
2022-06-12 15:39:55 +02:00
Mark Thom
336311ecc8 replace call with $call in more meta-predicates 2022-06-06 09:28:03 -06:00
Mark Thom
e9bb35c895 emit indexing instructions in single clauses of dynamic predicates 2022-06-04 23:43:48 -06:00
Mark Thom
ab4f93dcee do not emit indexing instructions for single clause subsequences (#1503) 2022-06-04 15:29:00 -06:00
Mark Thom
140a199805 Merge pull request #1505 from Skgland/optional_rustfmt
make rustfmt optional
2022-06-04 09:56:51 -06:00
Skgland
232b66cfc3 adjust error message on failed formatting 2022-06-03 23:50:17 +02:00
Skgland
11ba7e47fd make rustfmt optional
- when rustfmt is not detected (by attempting to run ``rustfmt --version)
  no formatting will be attempted
- this should resolve issue mthom/scryer-prolog#1504
2022-06-03 23:35:55 +02:00
Mark Thom
c36d4a9c9a unmark cells in arith_eval_by_metacall (#1497) 2022-06-01 00:02:11 -06:00
Mark Thom
4b5c22864e don't print superfluous round brackets in curly braces (#1416) 2022-05-31 23:34:55 -06:00
Mark Thom
5e1faeb5d2 fix broken quoting in writeq/1 (#1498) 2022-05-31 18:29:42 -06:00
Mark Thom
4b7c2ba6c8 fix off-by-one error reporting in write_term/3 (#1490) 2022-05-30 23:25:44 -06:00
Mark Thom
b9285f8de1 don't quote most characters in strings (#1495, #301) 2022-05-30 23:09:20 -06:00
Mark Thom
084fc84590 use existence_error for dropped streams (#1472) 2022-05-23 11:15:58 -06:00
Mark Thom
7a9b71cc03 Merge pull request #1485 from triska/in_character
Implement and use can_be(in_character, ...)
2022-05-22 19:52:19 -06:00
Markus Triska
6b49653754 use in_character
This addresses one aspect of #1472.
2022-05-22 23:04:27 +02:00
Markus Triska
d5fa8cc211 ADDED: must_be(in_character, ...) and can_be(in_character, ...) 2022-05-22 23:04:27 +02:00
Mark Thom
10a11c293e fix stream position and term comparison bugs (#1472) 2022-05-22 14:16:14 -06:00
Mark Thom
e486862db8 Merge pull request #1484 from aarroyoc/fix-windows-build-2
Fix build on Windows 10
2022-05-22 10:15:16 -06:00
Adrián Arroyo Calle
1c8aae0839 MSI installer 2022-05-22 14:18:51 +02:00
Adrián Arroyo Calle
6feb1f8b82 Fix build on Windows 10 2022-05-22 13:18:11 +02:00
Mark Thom
bef8eb538c throw a representation error if max arity is exceeded (#1483) 2022-05-22 00:03:55 -06:00
Mark Thom
4e3b066555 Merge pull request #1482 from triska/multiple_of_five_answers
ENHANCED: "f" on toplevel to report answers up to the next multiple of 5.
2022-05-21 12:35:35 -06:00
Mark Thom
4d23542ef3 add max arity checks at various stages (#1459) 2022-05-21 12:32:48 -06:00
Markus Triska
47a6b498e3 ENHANCED: "f" on toplevel to report answers up to the next multiple of 5.
This makes counting solutions easy.
2022-05-21 18:20:08 +02:00
Mark Thom
b6f77f4e6f check asserta/2, assertz/2, abolish/2 for uninstantiated Module variables (#1475) 2022-05-16 20:45:45 -06:00
Mark Thom
b96cd06129 detect character type errors in atom_{chars|codes}/2 (#1472) 2022-05-16 16:32:38 -06:00
Mark Thom
e8eb6765bd Merge pull request #1478 from triska/sgml_improvements
Various improvements to library(sgml)
2022-05-15 09:44:44 -06:00
Mark Thom
8df552e952 Merge pull request #1477 from triska/document_tab_completion
document atom and predicate completion with TAB
2022-05-15 09:44:26 -06:00
Markus Triska
18c52e076f indent enumerations 2022-05-15 10:55:56 +02:00
Markus Triska
1810dabc14 clarify the representation 2022-05-15 10:55:27 +02:00
Markus Triska
609a3a229f strengthen and improve type checks 2022-05-15 10:51:00 +02:00
Markus Triska
dff56643f0 clarify comment 2022-05-15 10:23:00 +02:00
Markus Triska
77f8d52271 use phrase_from_file/2 directly on the file name 2022-05-15 10:19:00 +02:00
Markus Triska
1e9821ec0c document atom and predicate completion with TAB
This feature, added via
https://github.com/mthom/scryer-prolog/pull/1467, makes the toplevel
interaction a lot more convenient in many cases.

Many thanks to @euanlacy!
2022-05-15 10:05:30 +02:00
Mark Thom
66e047083a Merge pull request #1474 from triska/correct_can_be_chars
FIXED: can_be(chars, [a,X]), i.e., if variables occur as elements
2022-05-14 23:28:04 -06:00
Mark Thom
e6438e79d8 consider deallocate a head instruction (#1473) 2022-05-14 11:01:26 -06:00
Markus Triska
a5e72679bc prioritize type errors over instantiation errors
Example:

    ?- must_be(chars, [a,X,cc]).
       error(type_error(character,cc),can_be/2).

See https://github.com/mthom/scryer-prolog/pull/1474#issuecomment-1126664368
2022-05-14 09:53:21 +02:00
Markus Triska
15cc916333 FIXED: can_be(chars, [a,X]), i.e., if variables occur as elements
This should simplify 2f3de51e55 and
other cases like it.
2022-05-14 08:39:20 +02:00
Mark Thom
2f3de51e55 remove non-determinism in number_chars/2 and numbercodes/2 (#1473) 2022-05-13 18:38:58 -06:00
Mark Thom
cea1353fbb use must_be(chars) in number_chars/2 (#1470) 2022-05-12 01:09:06 -06:00
Mark Thom
bfbafd4168 Merge pull request #1467 from euanlacy/master
Completion of predicates in repl
2022-05-11 22:32:09 -06:00
Euan Lacy
27a15a2464 wip: add more atoms to completion list 2022-05-11 21:37:03 +01:00
Euan Lacy
c2a87f898e fix: remove unnecessary string & vec allocations 2022-05-10 18:10:18 +01:00
Euan Lacy
aaa75c3ffc wip: basic completion of predicates 2022-05-10 18:10:18 +01:00
Mark Thom
2dfa108034 unify partial string tail later in create_partial_string (#1462, #1465, #1427) 2022-05-08 14:52:22 -06:00
Mark Thom
4895cb7b22 fix culprit in stream_position permission_error (#1463) 2022-05-06 17:38:25 -06:00
Mark Thom
2d3eb8483f correct cycle detection in unify_* (#1455) 2022-05-05 18:58:56 -06:00
Mark Thom
36ef988514 Merge pull request #1461 from triska/clpz_sign
ADDED: sign/1
2022-05-05 17:24:54 -06:00
Markus Triska
d58dd7cc91 ADDED: sign/1 2022-05-05 22:51:49 +02:00
Markus Triska
7e06abebd5 remove comment that seems no longer valid 2022-05-05 22:51:49 +02:00
Mark Thom
8dd7f51ae3 fix rnd_i fixnum casting bug (#1456, #1458) 2022-05-04 22:44:12 -06:00
Mark Thom
6030fae685 track f64 offsets in Literal (#1190) 2022-05-04 21:54:46 -06:00
Mark Thom
0f502fff84 compact lists of characters to partial strings in read_term_from_heap (#1452, #1453) 2022-05-03 19:12:16 -06:00
Mark Thom
789f662ec8 fix match_partial_string, compare_pstr_to_string (#1451) 2022-04-30 19:51:19 -06:00
Mark Thom
72536037ca remove redundant register assignments in numeric comparisons (#1438, #1448) 2022-04-29 09:16:22 -06:00
Mark Thom
e4eefc92f4 remove redundant skip_stub_try_me_else condition (#1444) 2022-04-29 09:16:22 -06:00
Mark Thom
23c4e935b4 Merge pull request #1436 from triska/must_be_term
ADDED: can_be(term, ...) and must_be(term, ...)
2022-04-26 13:31:43 -06:00
Mark Thom
68b4951bc9 replace SliceDeque with VecDeque 2022-04-25 22:55:28 -06:00
Mark Thom
ed89c43e48 catch existence_error exceptions from top-level calls to attribute_goals/3 (#1432) 2022-04-24 20:59:00 -06:00
Mark Thom
16137ace0e unify structures from left to right (#1396) 2022-04-22 19:00:42 -06:00
Mark Thom
b4ef7556da update to crossterm 0.20.0 (#1439) 2022-04-21 18:41:07 -06:00
Markus Triska
eea83d9474 ADDED: can_be(term, ...) and must_be(term, ...)
This terminology is a bit harder to understand than that of the other
type checks.

Scryer Prolog comprises two individually consistent but incompatible
notions of terms, namely terms and rational trees (which are also
called "cyclic terms"). This leads to a confusion in terminology,
because cyclic terms are not terms in the former sense even though
they also contain the word "term" in their name.

This terminological confusion is not restricted to terms: We also find
it for example in the notion of lists. Lists are defined inductively:

    - The empty list (written as "" or [], called 'nil') is a list.
    - A term with principal functor '.'/2 is a list if its second argument
      is a list.

Note that a list may contain cyclic terms as elements, and still be
called a list. However, if we post:

    ?- Ls = [_|Ls].

then Ls is not a list, because it does not fit the inductive
definition. Still, it is sometimes referred to as a "cyclic list" or
"circular list", even though it is not a list. Even Prolog
implementors that aim for strict conformance to the Prolog ISO
standard and are deeply familiar with the standard use this
terminology when talking about such terms, see for example:

    https://github.com/ichiban/prolog/pull/198

Consequently, must_be(list, Ls) raises a *type error* in this case.

Terms are distinguished from cyclic terms completely analogously. This
leads to the counterintuitive consequence that *cyclic lists are not
terms*. The reason for this apparent contradiction in terminology is
the mutual incompatibility of different notions of terms. The notion
of "term" precludes rational trees, similar to the notion of "list"
which precludes cyclic lists.

We therefore introduce:

    * must_be(term, Term)
      - raises an instantiation error if Term contains variables (since
        it could still become a rational tree)
      - raises a type error if Term is a rational tree

    * can_be(term, Term)
      - raises a type error if Term is a rational tree

Note that using the notion of "cyclic term" and "acyclic term" in this
terminology would not be a good idea, because this confusion is
precisely what we want to eliminate: must_be(term, Term) should be
usable as a test that reliably determines whether Term can be treated
as a term with the logical properties we expect from terms.

This addresses #1428.
2022-04-20 22:15:32 +02:00
Mark Thom
9d5264c5a3 very minor optimizations 2022-04-17 20:10:13 -06:00
Mark Thom
66075bf45b mark unsafe variables and substitute temporary variables in is/2 when appropriate (#1430) 2022-04-17 17:33:09 -06:00
Mark Thom
35b8f69f92 fix panic caused by jmp offset off by one error (#1429) 2022-04-17 12:22:34 -06:00
Mark Thom
2d5fe21b05 avoid writing chars to the atom table in several places 2022-04-17 12:20:00 -06:00
Mark Thom
b448681872 Merge pull request #1421 from gruhn/master
push Docker image on new release
2022-04-16 11:11:33 -06:00
Mark Thom
05c14d5780 fix comparisons to partial string tails (#1420) 2022-04-14 22:14:23 -06:00
Niklas Gruhn
bb9de52a53 push Docker image on new release
Setup GitHub action which automatically builds and pushes Docker
images, when new release tags are created.
2022-04-14 21:42:28 +02:00
Mark Thom
7595ec16e5 instantiate Module in definition of retract/1 (#1392) 2022-04-13 20:31:16 -06:00
Mark Thom
d8cf0f320d mark cells that are about to be iterated in the stackful iterator (#1418) 2022-04-12 22:51:20 -06:00
Mark Thom
d644a3996e use '$call' all throughout setup_call_cleanup/3 2022-04-12 17:27:48 -06:00
Mark Thom
525050c407 dereference head of list in is_continuable (#1417) 2022-04-11 17:01:47 -06:00
Mark Thom
468088fd6b stop detecting cycles in compare_pstr_to_string 2022-04-10 19:46:34 -06:00
Mark Thom
95c192a988 mark variable referents in stackful iterator, fix tests (#1408) 2022-04-10 14:17:47 -06:00
Mark Thom
d6287fae06 Merge pull request #1415 from wkral/set_output-error
Fix reported source for set_output/1 error (#1407)
2022-04-10 14:11:43 -06:00
William Kral
9e98f75495 Fix reported source for set_output/1 error (#1407) 2022-04-10 12:00:59 -07:00
Mark Thom
56681af1b6 print dot after errors, fix tests (#1408) 2022-04-10 10:15:00 -06:00
Mark Thom
8bf3d4ea71 fix tests, use '$first_answer' to print indentation before errors (#1408) 2022-04-09 20:32:52 -06:00
Mark Thom
00eab4d415 print errors as goals (#1408) 2022-04-09 14:01:48 -06:00
Mark Thom
0876d46880 unmark dereferenced cells in term_variables/2 (#1412) 2022-04-09 12:15:46 -06:00
Mark Thom
ed803d30cd dereference bindings returned by stackful iterator (#1410, #1411) 2022-04-09 11:47:54 -06:00
Mark Thom
b6504bf377 deduplicate variable list in term_variables/2 (#1409) 2022-04-08 21:18:59 -06:00
Mark Thom
85b4f235ef throw errors on cyclic terms when evaluating is/2 expressions 2022-04-07 23:06:58 -06:00
Mark Thom
064bfa5e0d fix loop in stackful iterator (#1405, #1384) 2022-04-07 20:48:40 -06:00
Mark Thom
4a6ffb6b9e detect immediately cyclic lists (#1384) 2022-04-06 22:53:48 -06:00
Mark Thom
d952b7ace8 restore correct order to term_variables/2 (#1400, #1401) 2022-04-06 08:39:16 -06:00
Mark Thom
3b6138eaaa limit use of stackless iterator to test suite 2022-04-05 21:02:39 -06:00
Mark Thom
f3167f6b5f add forwarding bit to all heap cells (#1384) 2022-04-05 20:34:27 -06:00
Mark Thom
8c99d748e2 allocate floats in dedicated buffer 2022-04-05 20:34:27 -06:00
Mark Thom
0ab928063e make marker into an iterators, recover post_order_iterator 2022-04-05 20:34:27 -06:00
Mark Thom
c9a34bde3f removal of old stackless iterator, implementation of new more faithful one 2022-04-05 20:34:27 -06:00
Mark Thom
7f85c682b8 Merge pull request #1395 from wkral/fix-test-indentation
Fix indentation before false in tests (#676)
2022-04-05 10:58:12 -06:00
William Kral
181185bad6 Fix indentation before false in tests (#676) 2022-04-04 23:58:20 -07:00
Mark Thom
01a9fd9e25 indent plain false (#676) 2022-04-03 20:35:11 -06:00
Mark Thom
3be00045cf Merge pull request #1386 from triska/better_call_nth
ENHANCED: more memory efficient call_nth/2 (#1264)
2022-04-03 01:38:37 -06:00
Mark Thom
86176dce8d compare complete strings to one another in write_literal_to_var (#1388) 2022-04-03 01:37:19 -06:00
Markus Triska
1fa8a0a969 ENHANCED: more memory efficient call_nth/2 (#1264)
I hope that this will allow more extensive tests of Scryer Prolog. Enjoy!
2022-04-02 17:42:45 +02:00
Mark Thom
d5fe2e88d1 don't mark forwarded refs in stackless iterator/marker (#1384) 2022-04-02 01:29:51 -06:00
Mark Thom
9177dbce7f be aware of temporary variables of stackless iterator in term_variables (#1383) 2022-03-31 18:20:41 -06:00
Mark Thom
55a896367d Merge pull request #1381 from wkral/build-fmt-error
Improve rustfmt missing error during build (#1379)
2022-03-31 17:59:02 -06:00
William Kral
f2e49c84cc Improve rustfmt missing error during build (#1379) 2022-03-31 11:57:17 -07:00
Mark Thom
afa0bea1b1 Merge pull request #1380 from triska/as_is
as --> is
2022-03-30 13:56:25 -06:00
Markus Triska
f7e31fceac as --> is 2022-03-30 21:33:46 +02:00
Mark Thom
c490d818fb fix dif/2 crash (#1378) 2022-03-29 18:39:45 -06:00
Mark Thom
32bdedde6d Merge pull request #1373 from aarroyoc/http-open
Add Hyper based `http_open/3`
2022-03-28 23:10:08 -06:00
Mark Thom
48960f1beb print closed stream handle as '$dropped_value' (#1376) 2022-03-28 19:03:33 -06:00
Mark Thom
cf65cbe2f1 dereference numbervars terms (#1377) 2022-03-28 18:58:38 -06:00
Adrián Arroyo Calle
45c35eca3e Add close handling of http stream 2022-03-28 21:04:46 +02:00
Adrián Arroyo Calle
bb2a8c9090 http_open/3 try_or_throw 2022-03-28 20:38:55 +02:00
Adrián Arroyo Calle
6798b22b49 http_open/3 with headers and data 2022-03-28 20:38:55 +02:00
Adrián Arroyo Calle
3acbe2a418 http_open/3 with method option 2022-03-28 20:38:52 +02:00
Mark Thom
c45cdd6ea0 extend small_letter_char! to include all non-uppercase letters (#1364) 2022-03-27 13:34:29 -06:00
Mark Thom
f520c38b60 Stream::close() should close file handles (#1374) 2022-03-27 13:08:43 -06:00
Mark Thom
046a2175ab use ryu to print floats with a modification for compatible syntax (#1368, #1372) 2022-03-27 11:28:10 -06:00
Mark Thom
83def95383 fix char comparison misordering (#1371) 2022-03-26 21:44:37 -06:00
Mark Thom
012fefa37e retrieve F64 behind Cons tags properly (#1369) 2022-03-26 21:18:07 -06:00
Mark Thom
4878e42e07 Merge pull request #1370 from triska/files_must_be_chars
ENHANCED: use newly available must_be(chars, ...), which is very fast
2022-03-26 12:30:58 -06:00
Markus Triska
1a7992e524 ENHANCED: use newly available must_be(chars, ...), which is very fast 2022-03-26 19:09:54 +01:00
Mark Thom
94d7c6dc5f fix functor/3 arity check (#1366) 2022-03-25 18:07:22 -06:00
Mark Thom
23bfb4da50 advance arg in arithmetic comparison code (#1367) 2022-03-25 17:30:47 -06:00
Mark Thom
1f21a5fe62 detect float rounding overflows (#1365) 2022-03-24 10:25:42 -06:00
Mark Thom
2173a8e3cb recognize missing syntax errors (#1354, #1361) 2022-03-22 22:48:51 -06:00
Mark Thom
067e2633af compare TypedArenaPtr<T> by value not pointer (#1362) 2022-03-22 00:55:44 -06:00
Mark Thom
d916da9ba8 use Rust's native UTF-8 functions (#1355) 2022-03-19 16:52:27 -06:00
Mark Thom
c2fc33afec correct float version of sign/1 (#1360) 2022-03-19 15:55:23 -06:00
Mark Thom
3d4e400ed2 accept remaining UTF-8 codepoints as alphabetic characters (#1355) 2022-03-18 17:49:54 -06:00
Mark Thom
75d719d204 print most characters directly ((#1355) 2022-03-17 18:43:30 -06:00
Mark Thom
172e3e9f34 check self.ignore_ops and max_depth in print_rational (#1359) 2022-03-17 18:08:30 -06:00
Mark Thom
8e32bdd8db remove pstr offsets unconditionally from printer iteration stack, deal with fallout after the fact (#1357) 2022-03-16 19:11:39 -06:00
Mark Thom
895b02b641 improve length/2 (#1325) 2022-03-15 18:43:57 -06:00
Mark Thom
d06b5f7ab5 prepare for publishing to crates.io (#1335) 2022-03-15 00:07:42 -06:00
Mark Thom
e88ec6736c add (:) meta-predicate argument indicator (#1302) 2022-03-13 13:54:58 -06:00
Mark Thom
922abf21e7 derefence target variable in check_sort_errors (#1350) 2022-03-13 13:54:58 -06:00
Mark Thom
c55bfa2c6e throw errors on cycles in sort/2 and keysort/2 (#1350 and #1351) 2022-03-13 11:03:52 -06:00
Mark Thom
5463c1d47f point code index to switch_on_term after appending indexed clause to single-clause predicate (#1346) 2022-03-12 20:44:09 -07:00
Mark Thom
3513a9c7ba Merge pull request #1349 from Skgland/min-rust-version
Set minimum rust version in Cargo.toml (Attempt 2)
2022-03-12 13:12:03 -07:00
Skgland
45db236ec7 add a CI job to verify msrv is still correct 2022-03-12 19:23:34 +01:00
Skgland
5848ff5735 add min rust version
was proposed in https://github.com/mthom/scryer-prolog/issues/1335#issuecomment-1062039516 and got positive feedback

(cherry picked from commit fcfa3beaa3)
2022-03-12 19:23:33 +01:00
Mark Thom
64c9ac4071 Merge pull request #1348 from mthom/revert-1347-min-rust-version
Revert "Set minimum rust version in Cargo.toml"
2022-03-12 11:10:25 -07:00
Mark Thom
75fc922898 Revert "Set minimum rust version in Cargo.toml" 2022-03-12 11:10:17 -07:00
Mark Thom
59e2c3fd5e Merge pull request #1347 from Skgland/min-rust-version
Set minimum rust version in Cargo.toml
2022-03-12 11:07:03 -07:00
Skgland
5bd73741cf add a CI job to verify msrv is still correct 2022-03-12 19:02:34 +01:00
Skgland
fcfa3beaa3 add min rust version
was proposed in https://github.com/mthom/scryer-prolog/issues/1335#issuecomment-1062039516 and got positive feedback
2022-03-12 18:57:33 +01:00
Mark Thom
dfbdaa9dab Merge pull request #1342 from wkral/no-add-history
Add --no-add-history flag
2022-03-11 18:25:47 -07:00
William Kral
5f1f07e5a1 Add --no-add-history flag
Flag prevents the input stream from saving terms to ~/.scryer_history
when set. Use the flag when running tests to increase test isolation.
2022-03-10 21:48:41 -08:00
Mark Thom
eace0d9b37 print rationals that are integers as integers (#663) 2022-03-09 21:24:16 -07:00
Mark Thom
d83b32a200 print variables instead of offsets in setup_call_cleanup tests 2022-03-08 22:05:54 -07:00
Mark Thom
9cefb33388 correct loading of variable name strings in write_term/3 2022-03-08 22:00:12 -07:00
Mark Thom
bd5d6d0686 fix parser bounds check bug (#1333, #1301) 2022-03-08 21:25:19 -07:00
Mark Thom
ecebaf8216 unify empty list to '' in atom_chars/2 (#1334, #1336) 2022-03-08 18:25:40 -07:00
Mark Thom
8762647b55 wrap meta-predicate specifications in functors (#1330) 2022-03-07 22:36:52 -07:00
Mark Thom
664c0ee77c fix float formatting (#1331) 2022-03-07 19:44:39 -07:00
Mark Thom
7a9c3d351d Merge pull request #1332 from triska/call_nth_0
ENHANCED: call_nth(Goal, 0) now fails, correct error for N < 0.
2022-03-07 15:16:47 -07:00
Markus Triska
69e52d1ed8 ENHANCED: call_nth(Goal, 0) now fails, correct error for N < 0.
This addresses #1307.
2022-03-07 23:38:30 +01:00
Mark Thom
bd594e0c05 fix number/1 (#1329) 2022-03-06 19:17:52 -07:00
Mark Thom
3bf42860e2 Merge pull request #1328 from pmoura/fix_bounded_and_max_arity_flags
Fix querying the values of the bounded and max_arity flags
2022-03-06 18:51:14 -07:00
Paulo Moura
0532f51cc2 Fix querying the values of the bounded and max_arity flags 2022-03-07 00:30:35 +00:00
Mark Thom
1808597258 compare functor arity first (#1327) 2022-03-06 16:37:21 -07:00
Mark Thom
2744989cf1 fix compare/3 (#1327) 2022-03-06 15:49:01 -07:00
Mark Thom
0d04abb5c7 Merge pull request #1326 from pmoura/fix_halt_1_predicate_compliance
Fix halt/1 built-in predicate for ISO Prolog Core standard compliance
2022-03-06 14:46:46 -07:00
Paulo Moura
495168e7bb Fix halt/1 built-in predicate for ISO Prolog Core standard compliance 2022-03-06 21:33:49 +00:00
Mark Thom
475fbf9561 Merge pull request #1323 from triska/simplex
ADDED: library(simplex), for solving linear programming problems.
2022-03-06 13:46:32 -07:00
Markus Triska
c6264cf098 ADDED: library(simplex), for solving linear programming problems. 2022-03-06 21:49:52 +01:00
Mark Thom
bc9123affb only recommend git cloning to get the latest scryer version in README.md 2022-03-06 10:27:25 -07:00
Mark Thom
7fd12972ad Merge pull request #1322 from triska/explain_strings_better
DOC: Better explanation of strings and partial strings.
2022-03-06 10:07:37 -07:00
Mark Thom
2d19243b3b Merge branch 'rebis-dev' into 0.9.0 release
Some checks failed
Test / build (macos-10.15, beta) (push) Has been cancelled
Test / build (macos-10.15, stable) (push) Has been cancelled
Test / build (ubuntu-20.04, beta) (push) Has been cancelled
Test / build (ubuntu-20.04, stable) (push) Has been cancelled
2022-03-06 09:45:05 -07:00
Markus Triska
8ad4f188f2 DOC: Better explanation of strings and partial strings. 2022-03-06 11:05:31 +01:00
Mark Thom
0c19c56909 mark variables in ArithmeticEvaluator (#690) 2022-03-05 17:57:10 -07:00
Mark Thom
de35baadf3 use must_be(chars) in read_from_chars/2 (#1305) 2022-03-05 17:39:13 -07:00
Mark Thom
66c209f9e4 reset ReadlineStream from read_query_line (#1317, #1285) 2022-03-05 17:39:13 -07:00
Mark Thom
d7a3ed2d4a change domain_error(integer, ..) to type_error(integer, ..) in random.pl (#1310) 2022-03-05 17:39:13 -07:00
Mark Thom
0c25ffc26e dereference list link in delete_attribute (#1148) 2022-03-05 17:39:03 -07:00
Markus Triska
9f864574de ADDED: "f" to enumerate the next 5 solutions, similar to GUPU. 2022-03-02 21:05:49 +01:00
Mark Thom
a38f7c8524 save arguments to delayed goal before calling verify_attrs (#1304) 2022-03-01 23:59:41 -07:00
Mark Thom
bf85cd404c Merge pull request #1313 from triska/all_solutions
ADDED: "a" to enumerate all solutions
2022-03-01 15:34:21 -07:00
Markus Triska
102adb3544 ADDED: "a" to enumerate all solutions
Example:

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

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

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

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

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

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

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

This addresses #994.

Examples:

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

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

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

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

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

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

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

Example:

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

Example:

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

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

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

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

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

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

Example:

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

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

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

More information:

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

Example:

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

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

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

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

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

Example:

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

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

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

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

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

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

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

Example:

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

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

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

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

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

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

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

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

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

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

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

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

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

Yielding:

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

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

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

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

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

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

I call this raven: *Cryer*. Cryer is an archaic spelling for "crier",
an officer who makes public announcements in a court of justice.
The Prolog top-level likewise makes announcements about what is true.
2020-05-26 22:13:51 +02:00
Mark Thom
fe0bf6f2ef Merge pull request #524 from matt2xu/decode_utf8
Add decoding of UTF-8 sequences
2020-05-26 13:42:42 -03:00
Mark Thom
828c19d478 Merge pull request #563 from triska/master
ADDED: halt/1, halting with specified exit code
2020-05-23 16:02:05 -03:00
Mark Thom
15db77558c throw evaluable type error for unrecognized atoms in arith_eval_by_metacall 2020-05-23 13:33:05 -06:00
Mark Thom
18cf4a3bd3 Add type errors where appropriate in arith_eval_by_metacall (#392) 2020-05-23 13:07:40 -06:00
Mark Thom
e0378acfc2 throw type_error(number, E) in arith_eval_by_metacall (#392) 2020-05-23 12:39:29 -06:00
Mark Thom
53cb2af202 (**)/2 should always evaluate to floating point (#558) 2020-05-23 12:24:00 -06:00
Mark Thom
f74005a166 update (^)/2 in light of Draft Technical Corrigendum 3 (#559) 2020-05-23 12:13:48 -06:00
Markus Triska
b0df981b01 ADDED: halt/1, halting with specified exit code 2020-05-23 19:28:17 +02:00
Matthieu Wipliez
cc9d6587a3 Implementation of UTF-8 bytes to chars 2020-05-22 23:33:12 +02:00
239 changed files with 81443 additions and 38152 deletions

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
*.png binary
*.pl text eol=lf
*.rs text eol=lf diff=rust
*.md text eol=lf diff=markdown

171
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,171 @@
name: CI
on:
push:
branches: [master]
tags:
- "v**"
pull_request:
schedule:
- cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday
workflow_dispatch:
jobs:
build-test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- { os: windows-latest, rust-version: stable, shell: 'msys2 {0}', target: 'x86_64-pc-windows-gnu'}
- { os: macos-11, rust-version: stable, shell: bash, target: 'x86_64-apple-darwin' }
- { os: ubuntu-20.04, rust-version: stable, shell: bash, extra: true, target: 'x86_64-unknown-linux-gnu' }
- { os: ubuntu-20.04, rust-version: stable, shell: bash, target: 'i686-unknown-linux-gnu' }
- { os: ubuntu-20.04, rust-version: 1.65, shell: bash, target: 'x86_64-unknown-linux-gnu'}
- { os: ubuntu-20.04, rust-version: beta, shell: bash, target: 'x86_64-unknown-linux-gnu'}
- { os: ubuntu-20.04, rust-version: nightly, shell: bash, target: 'x86_64-unknown-linux-gnu'}
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
if: "!contains(matrix.os,'windows')"
id: toolchain
with:
toolchain: ${{ matrix.rust-version }}
targets: ${{ matrix.target }}
components: clippy, rustfmt
- name: Install i686 dependencies
if: "contains(matrix.target,'i686')"
run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install libssl-dev:i386 gcc-multilib clang -y && echo "CC=clang" >> $GITHUB_ENV && echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
- uses: msys2/setup-msys2@v2
if: contains(matrix.os,'windows')
with:
update: true
install: >-
base-devel
mingw-w64-x86_64-rust
- uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ matrix.os }}_${{ matrix.target }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
# Build and test.
- name: Build library
run: cargo rustc --target ${{ matrix.target }} --verbose --lib -- -D warnings
- name: Test
if: "!matrix.extra"
run: cargo test --target ${{ matrix.target }} --all --verbose
# Extra steps only run once to avoid duplication, when matrix.extra is true
- name: Test and report
if: matrix.extra
run: |
cargo install cargo2junit --force
RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml
- name: Publish cargo test results artifact
if: matrix.extra
uses: actions/upload-artifact@v3
with:
name: cargo-test-results
path: cargo_test_results.xml
- name: Publish cargo test summary
if: matrix.extra
uses: EnricoMi/publish-unit-test-result-action/composite@master
with:
check_name: Cargo test summary
files: cargo_test_results.xml
fail_on: nothing
comment_mode: off
- name: Check formatting
if: matrix.extra
run: cargo fmt --check || echo "::warning ::cargo fmt found some formatting changes that may improve readability"
- name: Check clippy
if: matrix.extra
run: cargo clippy --no-deps || echo "::warning ::cargo clippy found some code style changes that may be more idiomatic"
# On stable rust builds, build a binary and publish as a github actions
# artifact. These binaries could be useful for testing the pipeline but
# are only retained by github for 90 days.
- name: Build release binary
if: contains(matrix.rust-version,'stable')
run: |
cargo rustc --target ${{ matrix.target }} --verbose --bin scryer-prolog --release -- -D warnings
echo "$PWD/target/release" >> $GITHUB_PATH
- name: Publish release binary artifact
if: contains(matrix.rust-version,'stable')
uses: actions/upload-artifact@v3
with:
path: target/${{ matrix.target }}/release/scryer-prolog*
name: scryer-prolog_${{ matrix.os }}_${{ matrix.target }}
logtalk-test:
runs-on: ubuntu-20.04
needs: [build-test]
steps:
# Download prebuilt ubuntu binary from build-test job, setup logtalk
- uses: actions/download-artifact@v3
with:
name: scryer-prolog_ubuntu-20.04_x86_64-unknown-linux-gnu
- run: |
chmod +x scryer-prolog
echo "$PWD" >> "$GITHUB_PATH"
- name: Install Logtalk
uses: logtalk-actions/setup-logtalk@master
with:
logtalk-version: git
logtalk-tool-dependencies: false
# Run logtalk tests.
- name: Run Logtalk's prolog compliance test suite
working-directory: ${{ env.LOGTALKUSER }}/tests/prolog/
run: |
pwd
scryerlgt -g '{ack(tester)},halt.'
logtalk_tester -p scryer -g "set_logtalk_flag(clean,off)" -w -t 360 \
-f xunit \
-s "$LOGTALKUSER/tests/prolog" \
|| echo "::warning ::logtalk compliance suite failed"
# -u "https://github.com/LogtalkDotOrg/logtalk3/tree/$LOGTALK_GIT_HASH/tests/prolog/" \
- name: Publish Logtalk test logs
uses: actions/upload-artifact@v3
with:
name: logtalk-test-logs
path: '${{ env.LOGTALKUSER }}/tests/prolog/logtalk_tester_logs'
- name: Publish Logtalk test results artifact
uses: actions/upload-artifact@v3
with:
name: logtalk-test-results
path: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml'
- name: Publish Logtalk test summary
uses: EnricoMi/publish-unit-test-result-action/composite@master
with:
check_name: Logtalk test summary
files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml'
fail_on: nothing
comment_mode: off
# Publish binaries when building for a tag
release:
runs-on: ubuntu-20.04
needs: [build-test]
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/download-artifact@v3
- name: Zip binaries for release
run: |
zip scryer-prolog_macos-11.zip ./scryer-prolog_macos-11_x86_64-apple-darwin/scryer-prolog
zip scryer-prolog_ubuntu-20.04.zip ./scryer-prolog_ubuntu-20.04_x86_64-unknown-linux-gnu/scryer-prolog
zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest_x86_64-pc-windows-gnu/scryer-prolog.exe
- name: Release
uses: softprops/action-gh-release@v1
with:
files: |
scryer-prolog_macos-11.zip
scryer-prolog_ubuntu-20.04.zip
scryer-prolog_windows-latest.zip

55
.github/workflows/docker-publish.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: Docker Publish
on:
push:
branches:
- 'master'
tags:
- 'v*.*.*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx
# https://github.com/docker/setup-buildx-action
uses: docker/setup-buildx-action@v2.2.1
# Login against Docker registry
- name: Log into registry
# https://github.com/docker/login-action
uses: docker/login-action@v2.1.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Extract Docker image tag from git tag. E.g. if git tag is "v0.19.1" then use
# Docker image tag "0.19.1". The "latest" tag reflects the most recent build on
# master.
- name: Extract Docker metadata
id: meta
# https://github.com/docker/metadata-action
uses: docker/metadata-action@v4.1.1
with:
images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/scryer-prolog
tags: |
type=semver,pattern={{version}}
type=raw,value=latest,enable={{is_default_branch}}
# type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
# Build and push Docker image with Buildx
- name: Build and push Docker image
id: build-and-push
# https://github.com/docker/build-push-action
uses: docker/build-push-action@v3.2.0
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
src/static_atoms.rs
target/ target/

View File

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

2604
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +1,99 @@
[package] [package]
name = "scryer-prolog" name = "scryer-prolog"
version = "0.8.123" version = "0.9.2"
authors = ["Mark Thom <markjordanthom@gmail.com>"] authors = ["Mark Thom <markjordanthom@gmail.com>"]
edition = "2018" edition = "2021"
description = "A modern Prolog implementation written mostly in Rust." description = "A modern Prolog implementation written mostly in Rust."
readme = "README.md" readme = "README.md"
repository = "https://github.com/mthom/scryer-prolog" repository = "https://github.com/mthom/scryer-prolog"
license = "BSD-3-Clause" license = "BSD-3-Clause"
keywords = ["prolog", "prolog-interpreter", "prolog-implementation"] keywords = ["prolog", "prolog-interpreter", "prolog-system"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
build = "build.rs" build = "build/main.rs"
rust-version = "1.63"
[features]
default = ["ffi", "repl", "hostname", "tls", "http"]
ffi = ["dep:libffi"]
repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"]
hostname = ["dep:hostname"]
tls = ["dep:native-tls"]
http = ["dep:hyper", "dep:reqwest"]
[build-dependencies] [build-dependencies]
indexmap = "1.0.2" indexmap = "1.0.2"
proc-macro2 = "1.0.36"
[features] quote = "1.0.15"
default = ["rug", "prolog_parser/rug"] strum = "0.23"
num = ["num-rug-adapter", "prolog_parser/num"] strum_macros = "0.23"
syn = { version = "1.0.88", features = ['full', 'visit', 'extra-traits'] }
to-syn-value = "0.1.0"
to-syn-value_derive = "0.1.0"
walkdir = "2"
[dependencies] [dependencies]
bit-set = "0.5.3"
bitvec = "1"
cpu-time = "1.0.0" cpu-time = "1.0.0"
crossterm = "0.16.0" dirs-next = "2.0.0"
dirs = "2.0.2"
divrem = "0.1.0" divrem = "0.1.0"
downcast = "0.10.0" fxhash = "0.2.1"
git-version = "0.3.4" git-version = "0.3.4"
hostname = "0.3.1"
indexmap = "1.0.2" indexmap = "1.0.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
lexical = "5.2.2"
libc = "0.2.62" libc = "0.2.62"
nix = "0.15.0" modular-bitfield = "0.11.2"
num-rug-adapter = { optional = true, version = "0.1.3" } ordered-float = "2.6.0"
ordered-float = "0.5.0" phf = { version = "0.9", features = ["macros"] }
prolog_parser = { version = "0.8.59", default-features = false }
ref_thread_local = "0.0.0" ref_thread_local = "0.0.0"
rug = { version = "1.4.0", optional = true }
rustyline = "6.0.0"
unicode_reader = "1.0.0"
ring = "0.16.13"
ripemd160 = "0.8.0" ripemd160 = "0.8.0"
sha3 = "0.8.2" sha3 = "0.8.2"
blake2 = "0.8.1" blake2 = "0.8.1"
crrl = "0.6.0"
chrono = "0.4.11"
select = "0.6.0"
roxmltree = "0.11.0"
base64 = "0.12.3"
smallvec = "1.8.0"
static_assertions = "1.1.0"
ryu = "1.0.9"
futures = "0.3"
libloading = "0.7"
derive_deref = "1.1.1"
http-body-util = "0.1.0-rc.2"
bytes = "1"
dashu = { git = "https://github.com/coasys/dashu.git", version = "0.3.1" }
rand = "0.8.5"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
libffi = { git = "https://github.com/coasys/libffi-rs.git", branch = "windows-space", optional = true, version = "3.2.0" }
hostname = { version = "0.3.1", optional = true }
crossterm = { version = "0.20.0", optional = true }
ctrlc = { version = "3.2.2", optional = true }
rustyline = { version = "12.0.0", optional = true }
native-tls = { version = "0.2.4", optional = true }
hyper = { version = "=1.0.0-rc.3", features = ["full"], optional = true }
reqwest = { version = "0.11.18", features = ["blocking"], optional = true }
tokio = { version = "1.28.2", features = ["full"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2.10", features = ["js"] }
tokio = { version = "1.28.2", features = ["sync", "macros", "io-util", "rt", "time"] }
[target.'cfg(target_os = "wasi")'.dependencies]
ring-wasi = { version = "0.16.25" }
[target.'cfg(not(target_os = "wasi"))'.dependencies]
ring = { version = "0.16.13" }
[dev-dependencies]
assert_cmd = "1.0.3"
predicates-core = "1.0.2"
serial_test = "2.0.0"
[patch.crates-io]
modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" }
[profile.release]
debug = true

View File

@@ -1,30 +1,26 @@
# Based on https://hub.docker.com/_/rust?tab=description and https://blog.sedrik.se/posts/my-docker-setup-for-rust/ # See https://github.com/LukeMathWalker/cargo-chef
ARG RUST_VERSION=1-buster
# The first container is for build purposes only. FROM rust:${RUST_VERSION} as planner
FROM rust as builder WORKDIR /scryer-prolog
RUN cargo install cargo-chef
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 . . COPY . .
RUN touch src/main.rs RUN cargo chef prepare --recipe-path recipe.json
RUN touch build.rs
RUN cargo build --release FROM rust:${RUST_VERSION} as cacher
WORKDIR /scryer-prolog
RUN cargo install cargo-chef
COPY --from=planner /scryer-prolog/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json
RUN ls ./target/release FROM rust:${RUST_VERSION} as builder
WORKDIR /scryer-prolog
COPY . .
# Copy over the cached dependencies
COPY --from=cacher /scryer-prolog/target target
COPY --from=cacher $CARGO_HOME $CARGO_HOME
RUN cargo build --release --bin scryer-prolog
# Finally, copy the scryer-prolog executable to a slimmer container. FROM debian:stable-slim
FROM debian:buster-slim COPY --from=builder /scryer-prolog/target/release/scryer-prolog /usr/local/bin
COPY --from=builder /usr/src/scryer-prolog/target/release/scryer-prolog /usr/local/bin/scryer-prolog ENV RUST_BACKTRACE=1
CMD ["scryer-prolog"] ENTRYPOINT ["/usr/local/bin/scryer-prolog"]

75
INDEX.dj Normal file
View File

@@ -0,0 +1,75 @@
# Scryer Prolog
```
?- append("Hello, ", X, "Hello, Scryer Prolog!").
X = "Scryer Prolog!".
```
``` =html
<div style="border: solid #00007f 3px;padding-left: 15px;padding-right: 15px;font-style: italic;background-color: #00007f30;">
<h4>Scryer Prolog Meetup 2023</h4>
<p>The first annual Scryer Prolog meetup is going to happen in Düsseldorf (Germany) on the 9th and 10th of November 2023. Join us to discover the present and future of Scryer Prolog! Participation is free, registration not required. <a href="https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2023/">More details here.</a></p>
</div>
```
![scryer](scryer.png){width=128 style=float:right;} [Scryer Prolog](https://github.com/mthom/scryer-prolog) is a free software ISO Prolog system intended to be an industrial
strength production environment *and* a testbed for bleeding edge research in
logic and constraint programming.
Some of the Scryer Prolog features are:
* ISO standard compliant
* Integrated constraint programming libraries: [clp(B)](/clpb.html), [clp(Z)](/clpz.html).
* [Definite Clause Grammars](/dcgs.html)
* Coroutining support ([`dif/2`](/dif.html), [`freeze/2`](/freeze.html), ...)
* [Tabling and SLG resolution](/tabling.html)
* Compact string representation
* Network libraries ([TCP sockets](/sockets.html), [HTTP server](/http/http_server.html), [HTTP client](/http/http_open.html), ...)
* [Cryptographical predicates](/crypto.html)
* WAM based engine, cross-platform made in Rust
* _and more..._
## What is Prolog?
Prolog is a logic programming language created by [Alain Colmerauer](https://en.wikipedia.org/wiki/Alain_Colmerauer) and [Robert Kowalski](https://en.wikipedia.org/wiki/Robert_Kowalski) in 1972.
The idea behind Prolog is try to express a task in language similar to First Order Logic.
Prolog systems include _unification_ and _non-determinism_ as key concepts upon which we build programs.
A Prolog program is made up of predicates which define a relation between its arguments. A predicate
is made from clauses. A clause can be either a fact or a rule. There's also a toplevel, which we
can use to ask and reason about our task.
It's still to this day one of the best examples and one of the most popular languages in the field
of logic programming. That's because Prolog allows us to elegantly solve many tasks with short and
general programs.
If you want a more detailed description of Prolog, check [A Tour of Prolog](https://www.youtube.com/watch?v=8XUutFBbUrg).
If you want to learn more about Prolog history, check the videos [l'Aventure Prolog](https://www.youtube.com/watch?v=74Ig_QKndvE) and [50 years of Prolog and beyond](https://prologyear.logicprogramming.org/videos/PrologDay_Session_1_talk.mp4).
## Where can I learn Prolog?
There are a lot of classical Prolog books. Those books can teach you the basics of Prolog. Some
examples are: _The Art of Prolog (Shapiro)_, _Programming in Prolog (Clocksin, Mellish)_ and _The Craft
of Prolog (O'Keefe)_. However, most of them are not updated to _modern_ Prolog.
We recommend _[The Power of Prolog (Markus Triska)](https://www.metalevel.at/prolog)_ for modern Prolog. For reference about
the builtin Prolog modules and libraries in Scryer, check the documentation site. It's this!
## Downloads
The latest version of Scryer Prolog is *0.9.1*. And it's already useful for lots of tasks.
Scryer Prolog can be compiled from source, instructions are on the [GitHub README](https://github.com/mthom/scryer-prolog). It runs on Linux, macOS and Windows. Other operating systems may work but they're not regularly tested.
If you're in Linux, maybe your distribution already has an Scryer Prolog package.
There's also a [Docker image](https://github.com/mthom/scryer-prolog#docker-install) available.
## Support and discussions
If Scryer Prolog crashes or yields unexpected errors, consider filing
an [issue](https://github.com/mthom/scryer-prolog/issues).
To get in touch with the Scryer Prolog community, participate in
[discussions](https://github.com/mthom/scryer-prolog/discussions)
or visit our #scryer IRC channel on [Libera](https://libera.chat)!

423
README.md
View File

@@ -1,3 +1,4 @@
# Scryer Prolog # Scryer Prolog
Scryer Prolog aims to become to ISO Prolog what GHC is to Haskell: an open Scryer Prolog aims to become to ISO Prolog what GHC is to Haskell: an open
@@ -5,12 +6,17 @@ source industrial strength production environment that is also a
testbed for bleeding edge research in logic and constraint testbed for bleeding edge research in logic and constraint
programming, which is itself written in a high-level language. programming, which is itself written in a high-level language.
As of July 2023, **Scryer Prolog passes all [syntactic conformity&nbsp;tests](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing)**.
The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl)
![Scryer Logo: Cryer](logo/scryer.png)
## Phase 1 ## Phase 1
Produce an implementation of the Warren Abstract Machine in Rust, done Produce an implementation of the Warren Abstract Machine in Rust, done
according to the progression of languages in [Warren's Abstract according to the progression of languages in [Warren's Abstract
Machine: A Tutorial Machine: A Tutorial Reconstruction](https://github.com/mthom/scryer-prolog/blob/master/wambook/wambook.pdf).
Reconstruction](http://wambook.sourceforge.net/wambook.pdf).
Phase 1 has been completed in that Scryer Prolog implements in some form Phase 1 has been completed in that Scryer Prolog implements in some form
all of the WAM book, including lists, cuts, Debray allocation, first all of the WAM book, including lists, cuts, Debray allocation, first
@@ -41,7 +47,7 @@ Extend Scryer Prolog to include the following, among other features:
- [x] Support for `attribute_goals/2` and `project_attributes/2` - [x] Support for `attribute_goals/2` and `project_attributes/2`
- [x] `call_residue_vars/2` - [x] `call_residue_vars/2`
- [x] `if_/3` and related predicates, following the developments of the - [x] `if_/3` and related predicates, following the developments of the
paper "Indexing `dif/2`". paper "[Indexing `dif/2`](https://arxiv.org/abs/1607.01590)".
- [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`). - [x] All-solutions predicates (`findall/{3,4}`, `bagof/3`, `setof/3`, `forall/2`).
- [x] Clause creation and destruction (`asserta/1`, `assertz/1`, - [x] Clause creation and destruction (`asserta/1`, `assertz/1`,
`retract/1`, `abolish/1`) with logical update semantics. `retract/1`, `abolish/1`) with logical update semantics.
@@ -49,18 +55,24 @@ Extend Scryer Prolog to include the following, among other features:
`bb_put/2` (non-backtrackable) and `bb_b_put/2` `bb_put/2` (non-backtrackable) and `bb_b_put/2`
(backtrackable). (backtrackable).
- [x] Delimited continuations based on reset/3, shift/1 (documented in - [x] Delimited continuations based on reset/3, shift/1 (documented in
"Delimited Continuations for Prolog"). "[Delimited Continuations for Prolog](https://biblio.ugent.be/publication/5646080/file/5646081)").
- [x] Tabling library based on delimited continuations - [x] Tabling library based on delimited continuations
(documented in "Tabling as a Library with Delimited Control"). (documented in "[Tabling as a Library with Delimited Control](https://biblio.ugent.be/publication/6880648/file/6885145.pdf)").
- [x] A _redone_ representation of strings as difference lists of - [x] A _redone_ representation of strings as difference lists of
characters, using a packed internal representation. characters, using a packed internal representation.
- [x] clp(B) and clp() as builtin libraries. - [x] clp(B) and clp() as builtin libraries.
- [x] Streams and predicates for stream control. - [x] Streams and predicates for stream control.
- [x] A simple sockets library representing TCP connections as streams. - [x] A simple sockets library representing TCP connections as streams.
- [ ] Incremental compilation and loading process, newly written, - [x] Incremental compilation and loading process, newly written,
primarily in Prolog. (_in progress_) primarily in Prolog.
- [ ] A compacting garbage collector satisfying the five - [ ] Improvements to the WAM compiler and heap representation:
properties of "Precise Garbage Collection in Prolog." - [ ] Replacing choice points pivoting on inlined semi-deterministic predicates
(`atom`, `var`, etc) with if/else ladders. (_in progress_)
- [ ] Inlining all built-ins and system call instructions.
- [x] Greatly reducing the number of instructions used to compile disjunctives.
- [ ] Storing short atoms to heap cells without writing them to the atom table.
- [ ] A compacting garbage collector satisfying the five properties of
"[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_)
- [ ] Mode declarations. - [ ] Mode declarations.
## Phase 3 ## Phase 3
@@ -79,12 +91,12 @@ nice to have in the future. They'd make a good project for anyone wanting
to contribute code to Scryer Prolog. to contribute code to Scryer Prolog.
1. Implement the global analysis techniques described in Peter van 1. Implement the global analysis techniques described in Peter van
Roy's thesis, "Can Logic Programming Execute as Fast as Imperative Roy's thesis, "[Can Logic Programming Execute as Fast as Imperative
Programming?" Programming?](https://www.info.ucl.ac.be/~pvr/Peter.thesis/Peter.thesis.html)"
2. Add unum representation and arithmetic, using either an existing 2. Add unum representation and arithmetic, using either an existing
unum implementation or an ad hoc one. Unums are described in unum implementation or an ad hoc one. Unums are described in
Gustafson's book "The End of Error." Gustafson's book "[The End of Error](http://www.johngustafson.net/unums.html)."
3. Add concurrent tables to manage shared references to atoms and 3. Add concurrent tables to manage shared references to atoms and
strings. strings.
@@ -93,7 +105,7 @@ strings.
## Installing Scryer Prolog ## Installing Scryer Prolog
### Native Install (Unix Only) ### Native Install
First, install the latest stable version of First, install the latest stable version of
[Rust](https://www.rust-lang.org/en-US/install.html) using your [Rust](https://www.rust-lang.org/en-US/install.html) using your
@@ -104,31 +116,36 @@ Rust updated to the latest stable release; any existing Rust
distribution should be uninstalled from your system before rustup is distribution should be uninstalled from your system before rustup is
used. used.
Scryer Prolog can be installed with cargo, like so: Currently the only way to install the latest version of Scryer is to
clone directly from this git repository, and compile the system. This
``` can be done as follows:
$> cargo install scryer-prolog
```
cargo will download and install the libraries Scryer Prolog uses
automatically from crates.io. You can find the `scryer-prolog`
executable in `~/.cargo/bin`.
Publishing Rust crates to crates.io and pushing to git are entirely
distinct, independent processes, so to be sure you have the latest
commit, it is recommended to clone directly from this git repository,
which can be done as follows:
``` ```
$> git clone https://github.com/mthom/scryer-prolog $> git clone https://github.com/mthom/scryer-prolog
$> cd scryer-prolog $> cd scryer-prolog
$> cargo run [--release] $> cargo build --release
``` ```
The optional `--release` flag will perform various optimizations, The `--release` flag performs various optimizations, producing a
producing a faster executable. faster executable.
### Docker Install (All Platforms) After compilation, the executable `scryer-prolog` is available in the
directory&nbsp;`target/release` and can be invoked to run the system.
On Windows, Scryer Prolog is easier to build inside a [MSYS2](https://www.msys2.org/)
environment as some crates may require native C compilation. However,
the resulting binary does not need MSYS2 to run. When executing Scryer in a shell, it is recommended to use a more advanced shell than mintty (the default MSYS2 shell). The [Windows Terminal](https://github.com/microsoft/terminal) works correctly.
To build a Windows Installer, you'll need first Scryer Prolog compiled in release mode, then, with WiX Toolset installed, execute:
```
candle.exe scryer-prolog.wxs
light.exe scryer-prolog.wixobj
```
It will generate a very basic MSI file which installs the main executable and a shortcut in the Start Menu. It can be installed with a double-click. To uninstall, go to the Control Panel and uninstall as usual.
Scryer Prolog must be built with **Rust 1.63 and up**.
### Docker Install
First, install [Docker](https://docs.docker.com/get-docker/) on Linux, First, install [Docker](https://docs.docker.com/get-docker/) on Linux,
Windows, or Mac. Windows, or Mac.
@@ -201,9 +218,16 @@ predicates it defines. For example, with the program shown above:
; What = pure_world. ; What = pure_world.
``` ```
Press `SPACE` to show further answers, if any exist. Press `RETURN` or Press `SPACE` to show further answers, if any exist. Press `RETURN`
&nbsp;`.` to abort the search and return to the toplevel&nbsp;prompt. or&nbsp;`.` to abort the search and return to the
Press&nbsp;`h` to show a help message. toplevel&nbsp;prompt. Press&nbsp;`f` to see up to the next multiple of
5 answers, and `a` to see all answers. Press&nbsp;`h` to show a help
message.
Use `TAB` to complete atoms and predicate names in queries. For
instance, after consulting the program above, typing `decl` followed
by&nbsp;`TAB` yields `declarative_world`. Press&nbsp;`TAB` repeatedly
to cycle through alternative completions.
To quit Scryer Prolog, use the standard predicate `halt/0`: To quit Scryer Prolog, use the standard predicate `halt/0`:
@@ -224,11 +248,62 @@ arithmetic operators with the usual precedences,
New operators can be defined using the `op` declaration. New operators can be defined using the `op` declaration.
### First instantiated argument indexing
Scryer Prolog indexes on the leftmost argument that is not a variable
in all clauses of a predicate's&nbsp;definition. We call this strategy
first *instantiated* argument indexing.
A key motivation for first instantiated argument indexing is to enable
indexing for meta-predicates such as `maplist/N` and `foldl/N`, whose
first argument is a partial goal that is a variable in the definition
of these predicates and therefore cannot be used for indexing.
For example, a natural definition of `maplist/2` reads:
```
maplist(_, []).
maplist(Goal_1, [L|Ls]) :-
call(Goal_1, L),
maplist(Goal_1, Ls).
```
In this case, first instantiated argument indexing automatically uses
the *second* argument for indexing, and thus prevents choicepoints for
calls with lists of fixed lengths (and deterministic goals).
Conveniently, no auxiliary predicates with reordered arguments are
needed to benefit from indexing in such cases.
Conventional first argument&nbsp;indexing naturally arises as a
special case of this strategy, if the first argument is instantiated
in any clause of a predicate's definition.
### Strings and partial strings ### Strings and partial strings
A very compact internal representation of *strings* is one of the key
innovations of Scryer Prolog. This means that terms which appear as
lists of characters to Prolog programs are stored in packed
UTF-8&nbsp;encoding by the engine.
Without this innovation, storing a list of characters in memory
would use one memory&nbsp;cell per character, one memory&nbsp;cell per
list constructor, and one memory&nbsp;cell for each tail that occurs
in the list. Since one memory&nbsp;cell takes 8&nbsp;bytes on 64-bit
machines, the packed representation used by Scryer&nbsp;Prolog yields
an up&nbsp;to **24-fold&nbsp;reduction** of memory usage, and
corresponding reduction of memory&nbsp;accesses when creating and
processing strings.
Scryer Prolog's compact internal string representation makes it
ideally suited for the use case Prolog was originally developed for:
efficient and convenient text processing, especially with definite
clause grammars (DCGs) as provided by
[`library(dcgs)`](src/lib/dcgs.pl) and
[`library(pio)`](src/lib/pio.pl) to transparently apply DCGs to files.
In Scryer Prolog, the default value of the Prolog flag `double_quotes` In Scryer Prolog, the default value of the Prolog flag `double_quotes`
is `chars`, which is also the recommended setting. This means that is `chars`, which is also the recommended setting. This means that
double-quoted strings are interpreted as lists of *characters*, in the lists of characters can be written as double-quoted strings, in the
tradition of Marseille&nbsp;Prolog. tradition of Marseille&nbsp;Prolog.
For example, the following query succeeds: For example, the following query succeeds:
@@ -238,15 +313,9 @@ For example, the following query succeeds:
true. true.
``` ```
Internally, strings are represented very compactly in packed This shows that the string `"abc"`, which is represented as a sequence
UTF-8&nbsp;encoding. A naive representation of strings as lists of of 3&nbsp;bytes internally, appears to Prolog programs as a list of
characters would use one memory&nbsp;cell per character, one characters.
memory&nbsp;cell per list constructor, and one memory&nbsp;cell for
each tail that occurs in the list. Since one memory&nbsp;cell takes
8&nbsp;bytes on 64-bit machines, the packed representation used by
Scryer&nbsp;Prolog yields an up&nbsp;to **24-fold&nbsp;reduction** of
memory usage, and corresponding reduction of memory&nbsp;accesses when
creating and processing strings.
Scryer Prolog uses the same efficient encoding for *partial* strings, Scryer Prolog uses the same efficient encoding for *partial* strings,
which appear to Prolog code as partial lists of characters. The which appear to Prolog code as partial lists of characters. The
@@ -269,8 +338,58 @@ the above example, posting <tt>Ls0&nbsp;=&nbsp;[a,b,c|Ls]</tt> yields
the exact same internal representation, and has the advantage that the exact same internal representation, and has the advantage that
only the standard predicate&nbsp;`(=)/2` is used. only the standard predicate&nbsp;`(=)/2` is used.
Definite clause grammars as provided by `library(dcgs)` are ideally The efficient internal representation of strings and partial strings
suited for reasoning about strings. was first proposed and explained by Ulrich Neumerkel in
issues&nbsp;[#24](https://github.com/mthom/scryer-prolog/issues/24)
and&nbsp;[#95](https://github.com/mthom/scryer-prolog/issues/95), and
Scryer&nbsp;Prolog is the first Prolog&nbsp;system that implements it.
### Occurs check and cyclic terms
The *occurs&nbsp;check* is an element of algorithms that perform
syntactic unification, causing the unification to fail if a variable
is unified with a term that contains that variable as a proper
subterm. For efficiency, the *occurs&nbsp;check* is omitted by default
in Scryer&nbsp;Prolog and many other Prolog systems.
In Scryer Prolog, performing unifications which succeed only if the
*occurs&nbsp;check* is omitted yield *cyclic&nbsp;terms*, also called
*rational&nbsp;trees*. For example:
```
?- X = f(X), Y = g(X,Y).
X = f(X), Y = g(f(X),Y).
```
The creation of cyclic terms often indicates a programming mistake in
the formulation of Prolog predicates, and to obtain logically sound
results it is desirable to either perform all unifications with
*occurs&nbsp;check* enabled, or let Prolog throw an error if enabling
the *occurs&nbsp;check* is necessary to prevent a unification.
Scryer Prolog supports this via the Prolog flag `occurs_check`. It can
be set to one of the following values to obtain the desired behaviour:
- `false`
Do not perform the *occurs&nbsp;check*. This is the default.
- `true`
Perform all unifications with the *occurs&nbsp;check* enabled.
- `error`
Yield an error if a unification is performed that the
*occurs&nbsp;check* would have prevented.
Especially when starting with Prolog, we recommend to add the
following directive to the `~/.scryerrc` configuration file so that
programming mistakes in predicates that lead to the creation of cyclic
terms are indicated by errors:
```
:- set_prolog_flag(occurs_check, error).
```
Scryer Prolog implements specialized reasoning to make unifications
fast in many frequently occurring situations also if the
*occurs&nbsp;check* is enabled.
### Tabling (SLG resolution) ### Tabling (SLG resolution)
@@ -287,7 +406,7 @@ even if one exists.
Scryer Prolog supports an alternative execution strategy which is Scryer Prolog supports an alternative execution strategy which is
called *tabling* and also known as tabled&nbsp;execution and called *tabling* and also known as tabled&nbsp;execution and
SLG&nbsp;resolution. To enable tabled execution for a predicate, use SLG&nbsp;resolution. To enable tabled execution for a predicate, use
[`library(tabling)`](src/prolog/lib/tabling.pl) and add a `(table)/1` [`library(tabling)`](src/lib/tabling.pl) and add a `(table)/1`
directive for the desired predicate indicator. For example, if we directive for the desired predicate indicator. For example, if we
write: write:
@@ -312,14 +431,14 @@ Scryer Prolog provides excellent support for Constraint Logic
Programming&nbsp;(CLP), which is the amalgamation of Programming&nbsp;(CLP), which is the amalgamation of
Logic&nbsp;Programming&nbsp;(LP) and Constraints. Logic&nbsp;Programming&nbsp;(LP) and Constraints.
In addition to built-in support for [`dif/2`](src/prolog/lib/dif.pl), In addition to built-in support for [`dif/2`](src/lib/dif.pl),
[`freeze/2`](src/prolog/lib/freeze.pl), [`freeze/2`](src/lib/freeze.pl),
[CLP(B)](src/prolog/lib/clpb.pl) and [CLP()](src/prolog/lib/clpz.pl), [CLP(B)](src/lib/clpb.pl) and [CLP()](src/lib/clpz.pl),
Scryer provides a convenient way to implement new user-defined Scryer provides a convenient way to implement new user-defined
constraints: *Attributed variables* are available via constraints: *Attributed variables* are available via
[`library(atts)`](src/prolog/lib/atts.pl) as in SICStus&nbsp;Prolog, [`library(atts)`](src/lib/atts.pl) as in SICStus&nbsp;Prolog,
which is one of the most sophisticated and fastest constraint systems which is one of the most sophisticated and fastest constraint systems
in existence. In [`library(iso_ext)`](src/prolog/lib/iso_ext.pl), in existence. In [`library(iso_ext)`](src/lib/iso_ext.pl),
Scryer provides predicates for backtrackable (`bb_b_put/2`) and Scryer provides predicates for backtrackable (`bb_b_put/2`) and
non-backtrackable (`bb_put/2`) global variables, which are needed to non-backtrackable (`bb_put/2`) global variables, which are needed to
implement certain types of constraint&nbsp;solvers. implement certain types of constraint&nbsp;solvers.
@@ -332,7 +451,7 @@ learning and developing portable CLP&nbsp;applications.
Scryer has a simple predicate-based module system. It provides a Scryer has a simple predicate-based module system. It provides a
way to separate units of code into distinct namespaces, for both way to separate units of code into distinct namespaces, for both
predicates and operators. See the files predicates and operators. See the files
[`src/prolog/lib/*.pl`](src/prolog/lib) for [`src/lib/*.pl`](src/lib) for
examples. examples.
At the time of this writing, many predicates reside in their own At the time of this writing, many predicates reside in their own
@@ -340,31 +459,31 @@ modules that need to be imported before they can be used.
The modules that ship with Scryer&nbsp;Prolog are also called The modules that ship with Scryer&nbsp;Prolog are also called
*library*&nbsp;modules or *libraries*, and include: *library*&nbsp;modules or *libraries*, and include:
* [`lists`](src/prolog/lib/lists.pl) * [`lists`](src/lib/lists.pl)
providing `length/2`, `member/2`, `select/3`, `append/[2,3]`, providing `length/2`, `member/2`, `select/3`, `append/[2,3]`,
`foldl/[4,5]`, `maplist/[2-9]`, `same_length/2`, `transpose/2` etc. `foldl/[4,5]`, `maplist/[2-9]`, `same_length/2`, `transpose/2` etc.
* [`dcgs`](src/prolog/lib/dcgs.pl) * [`dcgs`](src/lib/dcgs.pl)
Definite Clause Grammars (DCGs), a built-in grammar mechanism Definite Clause Grammars (DCGs), a built-in grammar mechanism
that uses the operator `(-->)/2` to define grammar rules, that uses the operator `(-->)/2` to define grammar rules,
and the predicates `phrase/[2,3]` to invoke them. and the predicates `phrase/[2,3]` to invoke them.
* [`dif`](src/prolog/lib/dif.pl) * [`dif`](src/lib/dif.pl)
The predicate `dif/2` provides declarative disequality: The predicate `dif/2` provides declarative disequality:
It is true if and only if its arguments are different, and It is true if and only if its arguments are different, and
delays the test until a sound decision can be made. delays the test until a sound decision can be made.
* [`reif`](src/prolog/lib/reif.pl) * [`reif`](src/lib/reif.pl)
providing `if_/3`, `tfilter/3` and related predicates providing `if_/3`, `tfilter/3` and related predicates
as described in *Indexing&nbsp;dif/2*. as described in *Indexing&nbsp;dif/2*.
* [`clpz`](src/prolog/lib/clpz.pl) * [`clpz`](src/lib/clpz.pl)
CLP(): Constraint Logic Programming over Integers, CLP(): Constraint Logic Programming over Integers,
providing declarative integer arithmetic via `(#=)/2`, `(#\=)/2`, providing declarative integer arithmetic via `(#=)/2`, `(#\=)/2`,
`(#>=)/2` etc., and various global constraints and `(#>=)/2` etc., and various global constraints and
enumeration predicates for solving combinatorial tasks. enumeration predicates for solving combinatorial tasks.
* [`pairs`](src/prolog/lib/pairs.pl) * [`pairs`](src/lib/pairs.pl)
By convention, *pairs* are Prolog terms with By convention, *pairs* are Prolog terms with
principal&nbsp;functor `(-)/2`, written as `Key-Value`. principal&nbsp;functor `(-)/2`, written as `Key-Value`.
This library provides `pairs_keys_values/3`, This library provides `pairs_keys_values/3`,
`pairs_keys/2`, and other predicates to reason about pairs. `pairs_keys/2`, and other predicates to reason about pairs.
* [`si`](src/prolog/lib/si.pl) * [`si`](src/lib/si.pl)
The predicates `atom_si/1`, `integer_si/1`, `atomic_si/1` The predicates `atom_si/1`, `integer_si/1`, `atomic_si/1`
and `list_si/1` implement sound type checks. They raise and `list_si/1` implement sound type checks. They raise
instantiation errors if no decision can be made. instantiation errors if no decision can be made.
@@ -373,51 +492,105 @@ The modules that ship with Scryer&nbsp;Prolog are also called
write `integer_si(X)` to ensure soundness of your programs. write `integer_si(X)` to ensure soundness of your programs.
"si" stands for *sufficiently instantiated*, and also for "si" stands for *sufficiently instantiated*, and also for
*sound&nbsp;inference*. *sound&nbsp;inference*.
* [`error`](src/prolog/lib/error.pl) * [`debug`](src/lib/debug.pl)
`must_be/2` and `can_be/2` complement the type checks provided Various predicates that allow for declarative debugging.
by `library(si)`, and are especially useful for Prolog library * [`pio`](src/lib/pio.pl)
authors. `phrase_from_file/2` applies a DCG nonterminal to the contents of a
* [`tabling`](src/prolog/lib/tabling.pl) file, reading lazily only as much as is needed. Due to the compact
internal string representation, also extremely large files can be
efficiently processed with Scryer&nbsp;Prolog in this way.
`phrase_to_file/2` and `phrase_to_stream/2` write lists of
characters described by DCGs to files and streams, respectively.
* [`lambda`](src/lib/lambda.pl)
Lambda expressions to simplify higher order programming.
* [`charsio`](src/lib/charsio.pl) Various predicates that are useful
for parsing and reasoning about characters, notably `char_type/2` to
classify characters according to their type, and conversion
predicates for different encodings of strings.
* [`error`](src/lib/error.pl)
`must_be/2` and `can_be/2` complement the type checks provided by
[`library(si)`](src/lib/si.pl), and are especially useful for
Prolog library authors.
* [`tabling`](src/lib/tabling.pl)
The operator `(table)/1` is used in directives that prepare The operator `(table)/1` is used in directives that prepare
predicates for tabled execution (SLG&nbsp;resolution). predicates for tabled execution (SLG&nbsp;resolution).
* [`format`](src/prolog/lib/format.pl) * [`format`](src/lib/format.pl)
The nonterminal `format_//2` is used to describe formatted output, The nonterminal `format_//2` is used to describe formatted output,
arranging arguments according to a given format&nbsp;string. arranging arguments according to a given format&nbsp;string.
The predicates `format/[2,3]`, `portray_clause/1` and `listing/1` The predicates `format/[2,3]`, `portray_clause/[1,2]` and `listing/1`
provide formatted *impure* output. provide formatted *impure* output.
* [`assoc`](src/prolog/lib/assoc.pl) * [`assoc`](src/lib/assoc.pl)
providing `empty_assoc/1`, `get_assoc/3`, `put_assoc/4` etc. providing `empty_assoc/1`, `get_assoc/3`, `put_assoc/4` etc.
to manage elements in AVL&nbsp;trees which ensure to manage elements in AVL&nbsp;trees which ensure
*O*(log(*N*))&nbsp;access. *O*(log(*N*))&nbsp;access.
* [`ordsets`](src/prolog/lib/ordsets.pl) * [`ordsets`](src/lib/ordsets.pl)
represents ordered sets as lists. represents ordered sets as lists.
* [`clpb`](src/prolog/lib/clpb.pl) * [`clpb`](src/lib/clpb.pl)
CLP(B): Constraint Logic Programming over Boolean variables, CLP(B): Constraint Logic Programming over Boolean variables,
a BDD-based SAT&nbsp;solver provided via the predicates a BDD-based SAT&nbsp;solver provided via the predicates
`sat/1`, `taut/2`, `labeling/1` etc. `sat/1`, `taut/2`, `labeling/1` etc.
* [`arithmetic`](src/prolog/lib/arithmetic.pl) * [`arithmetic`](src/lib/arithmetic.pl)
Arithmetic predicates such as `lsb/2`, `msb/2` and Arithmetic predicates such as `lsb/2`, `msb/2` and
`number_to_rational/2`. `number_to_rational/2`.
* [`time`](src/prolog/lib/time.pl) * [`time`](src/lib/time.pl) Predicates for reasoning about
`time/1` reports the CPU&nbsp;time of a goal. It is useful time, including `time/1` to measure the CPU&nbsp;time of a goal,
for measuring the performance of your code. `current_time/1` to obtain the current system time, the nonterminal
* [`cont`](src/prolog/lib/cont.pl) `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`. Provides *delimited continuations* via `reset/3` and `shift/1`.
* [`random`](src/prolog/lib/random.pl) * [`random`](src/lib/random.pl)
Probabilistic predicates and random number generators. Probabilistic predicates and random number generators.
* [`sockets`](src/prolog/lib/sockets.pl) * [`http/http_open`](src/lib/http/http_open.pl) Open a stream to
read answers from web&nbsp;servers. HTTPS is also supported.
* [`http/http_server`](src/lib/http/http_server.pl) Runs a HTTP/1.1 and HTTP/2.0 web server. Uses [Hyper](https://hyper.rs) as a backend. Supports some query and form handling.
* [`sgml`](src/lib/sgml.pl)
`load_html/3` and `load_xml/3` represent HTML and XML&nbsp;documents
as Prolog&nbsp;terms for convenient and efficient reasoning. Use
[`library(xpath)`](src/lib/iso_ext.pl) to extract information from
parsed documents.
* [`csv`](src/lib/csv.pl)
`parse_csv//1` and `parse_csv//2` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
or [`phrase/2`](src/lib/dcgs.pl) to parse csv
* [`serialization/abnf`](src/lib/serialization/abnf.pl)
DCGs describing the
[ABNF grammar core (RFC 5234)](https://tools.ietf.org/html/rfc5234#appendix-B.1),
which is used to describe many [IETF](https://www.ietf.org/standards/rfcs/)
syntaxes, such as [HTTP v1.1](https://www.rfc-editor.org/rfc/rfc7230.html#page-82),
[SMTP](https://www.rfc-editor.org/rfc/rfc5321.html),
[iCalendar](https://www.rfc-editor.org/rfc/rfc5545.html), and more.
* [`serialization/json`](src/lib/serialization/json.pl)
`json_chars//1` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
or [`phrase/2`](src/lib/dcgs.pl) to parse and generate
[JSON](https://www.json.org/json-en.html).
* [`xpath`](src/lib/xpath.pl)
The predicate `xpath/3` is used for convenient reasoning about HTML
and XML&nbsp;documents, inspired by the XPath language. This library
is often used together with [`library(sgml)`](src/lib/sgml.pl).
* [`sockets`](src/lib/sockets.pl)
Predicates for opening and accepting TCP connections as streams. Predicates for opening and accepting TCP connections as streams.
* [`crypto`](src/prolog/lib/crypto.pl) * [`os`](src/lib/os.pl)
Cryptographically secure random numbers and hashes, HMAC-based Predicates for reasoning about environment&nbsp;variables.
key derivation (HKDF), password-based key derivation (PBKDF2), * [`iso_ext`](src/lib/iso_ext.pl)
public key signatures and signature verification with Ed25519, Conforming extensions to and candidates for inclusion in the Prolog
authenticated encryption, and reasoning about elliptic curves. ISO&nbsp;standard, such as `setup_call_cleanup/3`, `call_nth/2` and
`call_with_inference_limit/3`.
To read contents of external files, use `phrase_from_file/2` from * [`crypto`](src/lib/crypto.pl)
[`library(pio)`](src/prolog/lib/pio.pl) to apply a&nbsp;DCG to Cryptographically secure random numbers and hashes, HMAC-based key
file&nbsp;contents. The predicates in derivation&nbsp;(HKDF), password-based key derivation&nbsp;(PBKDF2),
[`library(charsio)`](src/prolog/lib/charsio.pl) are also useful for public key signatures and signature verification with&nbsp;Ed25519,
parsing. ECDH key&nbsp;exchange over Curve25519 (X25519), authenticated symmetric
encryption with ChaCha20-Poly1305, and reasoning about elliptic curves.
* [`uuid`](src/lib/uuid.pl) UUIDv4 generation and hex representation
* [`tls`](src/lib/tls.pl)
Predicates for negotiating TLS connections explicitly.
* [`ugraphs`](src/lib/ugraphs.pl) Graph manipulation library
* [`simplex`](src/lib/simplex.pl) Providing `assignment/2`,
`transportation/4` and other predicates for solving linear
programming problems.
To use predicates provided by the `lists` library, write: To use predicates provided by the `lists` library, write:
@@ -462,3 +635,77 @@ local_member(X, Xs) :- member(X, Xs).
The user listing can also be terminated by placing `end_of_file.` at The user listing can also be terminated by placing `end_of_file.` at
the end of the stream. the end of the stream.
### Configuration file
At startup, Scryer Prolog consults the file `~/.scryerrc`, if the file
exists. This file is useful to automatically load libraries and define
predicates that you need often.
For example, a sensible starting point for `~/.scryerrc` is:
```
:- use_module(library(lists)).
:- use_module(library(dcgs)).
:- use_module(library(reif)).
```
### Development environment
To write and edit Prolog programs, we recommend
[GNU&nbsp;Emacs](https://www.gnu.org/software/emacs/) with the
[Prolog&nbsp;mode](https://bruda.ca/emacs/prolog_mode_for_emacs)
maintained by Stefan Bruda.
Use [ediprolog](https://www.metalevel.at/ediprolog/) to consult
Prolog&nbsp;code and evaluate Prolog queries in arbitrary
Emacs&nbsp;buffers.
Emacs definitions that show Prolog terms as trees are available
in&nbsp;[tools](tools).
To *debug* Prolog code, we recommend the predicates from
[**`library(debug)`**](src/lib/debug.pl), most notably:
- `(*)/1` to *"generalize&nbsp;away"* a Prolog goal. Use it to debug
unexpected failures by generalizing your definitions until they
succeed. Simply place&nbsp;`*` in front of a goal to generalize it away.
- `($)/1` to emit a *trace* of the execution, showing when a goal
is invoked, and when it has succeeded. Place&nbsp;`$` in front of a goal
to emit this information for that goal.
This way of debugging Prolog code has several major benefits, such as:
It stays close to the actual Prolog code under consideration, it does
not need additional tools and formalisms for its application, and
further, it encourages declarative reasoning that can in principle
also be performed automatically.
## Applications
Scryer Prolog's strong commitment to the Prolog ISO standard makes it
ideally suited for use in corporations and government&nbsp;agencies
that are subject to strict regulations pertaining to interoperability,
standards&nbsp;compliance and warranty.
Successful existing applications of Scryer Prolog include the
[DocLog](https://github.com/aarroyoc/doclog)&nbsp;system which
generates Scryer's own documentation and homepage, [Symbolic
Analysis of Grants](https://www.brz.gv.at/en/BRZ-Tech-Blog/Tech-Blog-7-Symbolic-Analysis-of-Grants.html)
by the Austrian Federal Computing Center, and parts of the
[precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog)
package for the analysis of dose-escalation trials in the
safety-critical and highly regulated domain of oncology
trial&nbsp;design.
Scryer Prolog is also very well suited for teaching and learning
Prolog, and for testing syntactic conformance and hence portability of
existing Prolog&nbsp;programs.
## Support and discussions
If Scryer Prolog crashes or yields unexpected errors, consider filing
an&nbsp;[issue](https://github.com/mthom/scryer-prolog/issues).
To get in touch with the Scryer Prolog community, participate in
[discussions](https://github.com/mthom/scryer-prolog/discussions)
or visit our #scryer IRC channel on [Libera](https://libera.chat)!

View File

@@ -1,60 +0,0 @@
extern crate indexmap;
use indexmap::IndexSet;
use std::env;
use std::fs::{File, copy, read_dir};
use std::io::Write;
use std::path::Path;
fn main()
{
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("libraries.rs");
let mut libraries = File::create(&dest_path).unwrap();
let mut library_index = IndexSet::new();
let paths = read_dir("./src/prolog/lib").unwrap();
for item in paths {
let item = item.unwrap().path();
if let Some(file_name) = item.file_name() {
if let Some(ext) = item.extension() {
if ext == "pl" {
let file_stem = item.file_stem().unwrap();
let file_str = file_stem.to_string_lossy().to_uppercase();
let dest = Path::new(&out_dir).join(file_name);
match copy(&item, dest) {
Ok(_) => {},
Err(e) => panic!("die: {:?}", e)
};
let include_line = format!("static {}: &str = include_str!(\"{}.pl\");\n",
file_str, file_stem.to_string_lossy());
libraries.write_all(include_line.as_bytes()).unwrap();
library_index.insert(file_stem.to_string_lossy().to_string());
}
}
}
}
libraries.write_all(b"\nref_thread_local! {
pub static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
let mut m = IndexMap::new();\n").unwrap();
for item in library_index {
let line = format!("\n m.insert(\"{}\", {});", item, item.to_uppercase());
libraries.write_all(line.as_bytes()).unwrap();
}
libraries.write_all(b"\n\n m\n };
}\n").unwrap();
libraries.write_all(b"\npub static PROJECT_DIR: &'static str = \"").unwrap();
libraries.write_all(env::var("CARGO_MANIFEST_DIR").unwrap().as_bytes()).unwrap();
libraries.write_all(b"\";\n").unwrap();
}

File diff suppressed because it is too large Load Diff

114
build/main.rs Normal file
View File

@@ -0,0 +1,114 @@
mod instructions_template;
mod static_string_indexing;
use instructions_template::generate_instructions_rs;
use static_string_indexing::index_static_strings;
use std::env;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::{Command, Stdio};
fn find_prolog_files(libraries: &mut File, prefix: &str, current_dir: &Path) {
let entries = match current_dir.read_dir() {
Ok(entries) => entries,
Err(_) => return,
};
for entry in entries.filter_map(Result::ok).map(|e| e.path()) {
if entry.is_dir() {
if let Some(file_name) = entry.file_name() {
let new_prefix = prefix.to_owned() + file_name.to_str().unwrap() + "/";
find_prolog_files(libraries, &new_prefix, &entry);
}
} else if entry.is_file() {
let ext = std::ffi::OsStr::new("pl");
if entry.extension() == Some(ext) {
let contain = String::from_utf8(fs::read(&entry).unwrap()).unwrap();
let name = entry.file_stem().unwrap().to_str().unwrap();
let line = format!(
" m.insert(\"{}\",\n{:?});\n",
prefix.to_owned() + name,
contain
);
libraries.write_all(line.as_bytes()).unwrap();
}
}
}
}
fn main() {
let has_rustfmt = Command::new("rustfmt")
.arg("--version")
.stdin(Stdio::inherit())
.status()
.is_ok();
if !has_rustfmt {
println!("Failed to run rustfmt, will skip formatting generated files.")
}
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("libraries.rs");
let mut libraries = File::create(&dest_path).unwrap();
let lib_path = Path::new("src/lib");
libraries
.write_all(
b"ref_thread_local::ref_thread_local! {
pub(crate) static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
let mut m = IndexMap::new();\n",
)
.unwrap();
find_prolog_files(&mut libraries, "", &lib_path);
libraries.write_all(b"\n m\n };\n}\n").unwrap();
let instructions_path = Path::new(&out_dir).join("instructions.rs");
let mut instructions_file = File::create(&instructions_path).unwrap();
let quoted_output = generate_instructions_rs();
instructions_file
.write_all(quoted_output.to_string().as_bytes())
.unwrap();
if has_rustfmt {
format_generated_file(instructions_path.as_path());
}
let static_atoms_path = Path::new(&out_dir).join("static_atoms.rs");
let mut static_atoms_file = File::create(&static_atoms_path).unwrap();
let quoted_output = index_static_strings(&instructions_path);
static_atoms_file
.write_all(quoted_output.to_string().as_bytes())
.unwrap();
if has_rustfmt {
format_generated_file(static_atoms_path.as_path());
}
println!("cargo:rerun-if-changed=src/");
}
fn format_generated_file(path: &Path) {
Command::new("rustfmt")
.arg(path.as_os_str())
.spawn()
.unwrap_or_else(|err| {
panic!(
"{}: rustfmt was detected as available, but failed to format generated file '{}'",
err,
path.display()
);
})
.wait()
.unwrap();
}

View File

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

BIN
logo/scryer.png Normal file

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

28
scryer-prolog.wxs Normal file
View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Name="Scryer Prolog" Manufacturer="Scryer Prolog contributors" Id="*" UpgradeCode="cfb2dee4-5dd5-4d7d-b426-cd7340810559" Language="1033" Codepage="1252" Version="0.9.0">
<Package Description="An open source industrial strength production environment for ISO Prolog that is also a testbed for bleeding edge research in logic and constraint programming, which is itself written in a high-level language." Platform="x64" Keywords="prolog" Id="*" Compressed="yes" InstallScope="perMachine" InstallerVersion="300" Languages="1033" SummaryCodepage="1252" Manufacturer="Scryer Prolog contributors"/>
<Property Id="APPHELPLINK" Value="https://github.com/mthom/scryer-prolog"/>
<Media Id="1" Cabinet="scryer.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="INSTALLDIR" Name="Scryer Prolog">
<Component Id="MainExecutable" Guid="1b41ceda-ba18-47f9-911b-ee41b4f20921">
<File Id="ScryerPrologEXE" Name="scryer-prolog.exe" DiskId="1" Source="target/release/scryer-prolog.exe" KeyPath="yes" Checksum="yes"/>
</Component>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Component Id="ApplicationShortcut" Guid="8c9b14a3-e7b1-4d30-a892-61d7371dcae2">
<Shortcut Id="ApplicationStarMenuShortcut" Name="Scryer Prolog" Description="Launch Scryer Prolog" Target="[#ScryerPrologEXE]" WorkingDirectory="INSTALLDIR"/>
<RemoveFolder Id="ApplicationShortcut" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\Microsoft\ScryerProlog" Name="installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</Directory>
</Directory>
<Feature Id="Complete" Level="1" Display="expand" ConfigurableDirectory="INSTALLDIR">
<ComponentRef Id="MainExecutable"/>
<ComponentRef Id="ApplicationShortcut"/>
</Feature>
</Product>
</Wix>

91
src/allocator.rs Normal file
View File

@@ -0,0 +1,91 @@
use crate::parser::ast::*;
use crate::forms::*;
use crate::instructions::*;
use crate::targets::*;
use std::cell::Cell;
pub(crate) trait Allocator {
fn new() -> Self;
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
&mut self,
lvl: Level,
context: GenContext,
code: &mut CodeDeque,
);
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
&mut self,
lvl: Level,
context: GenContext,
cell: &'a Cell<RegType>,
code: &mut CodeDeque,
);
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
&mut self,
var_num: usize,
lvl: Level,
cell: &'a Cell<VarReg>,
term_loc: GenContext,
code: &mut CodeDeque,
r: RegType,
is_new_var: bool,
);
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType;
fn mark_var<'a, Target: CompilationTarget<'a>>(
&mut self,
var_num: usize,
lvl: Level,
cell: &'a Cell<VarReg>,
context: GenContext,
code: &mut CodeDeque,
);
fn reset(&mut self);
fn reset_arg(&mut self, arg_num: usize);
fn reset_at_head(&mut self, args: &Vec<Term>);
fn reset_contents(&mut self);
fn advance_arg(&mut self);
/*
fn bindings(&self) -> &AllocVarDict;
fn bindings_mut(&mut self) -> &mut AllocVarDict;
fn take_bindings(self) -> AllocVarDict;
*/
fn max_reg_allocated(&self) -> usize;
// TODO: wha.. why?? grrr. it drains the VarStatus data from vs (which it owns!)
// into self.bindings and perm_vs after all is computed (i.e. vs.populate_restricting_sets()
// and vs.set_perm_vals(has_deep_cut) have both been called).
/*
fn drain_var_data<'a>(
&mut self,
vs: VariableFixtures,
num_of_chunks: usize,
) -> VariableFixtures {
let mut perm_vs = VariableFixtures::new();
for (var, var_status) in vs.into_iter() {
match var_status {
VarStatus::Temp(chunk_num, tvd) => {
self.bindings_mut()
.insert(var.clone(), VarAlloc::Temp(chunk_num, 0, tvd));
}
VarStatus::Perm(_) => {
self.bindings_mut().insert(var.clone(), VarAlloc::Perm(0));
perm_vs.insert(var, var_status);
}
};
}
perm_vs
}
*/
}

1123
src/arena.rs Normal file

File diff suppressed because it is too large Load Diff

725
src/arithmetic.rs Normal file
View File

@@ -0,0 +1,725 @@
use crate::allocator::*;
use crate::arena::*;
use crate::atom_table::*;
use crate::debray_allocator::*;
use crate::forms::*;
use crate::instructions::*;
use crate::iterators::*;
use crate::targets::QueryInstruction;
use crate::types::*;
use crate::parser::ast::*;
use crate::parser::dashu::{Integer, Rational};
use crate::machine::machine_errors::*;
use dashu::base::Abs;
use ordered_float::*;
use std::cell::Cell;
use std::cmp::{max, min, Ordering};
use std::convert::TryFrom;
use std::f64;
use std::num::FpCategory;
use std::ops::Div;
use std::vec::Vec;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ArithmeticTerm {
Reg(RegType),
Interm(usize),
Number(Number),
}
impl ArithmeticTerm {
pub(crate) fn interm_or(&self, interm: usize) -> usize {
if let &ArithmeticTerm::Interm(interm) = self {
interm
} else {
interm
}
}
}
impl Default for ArithmeticTerm {
fn default() -> Self {
ArithmeticTerm::Number(Number::default())
}
}
#[derive(Debug)]
pub(crate) struct ArithInstructionIterator<'a> {
state_stack: Vec<TermIterState<'a>>,
}
pub(crate) type ArithCont = (CodeDeque, Option<ArithmeticTerm>);
impl<'a> ArithInstructionIterator<'a> {
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
self.state_stack
.push(TermIterState::subterm_to_state(lvl, term));
}
fn from(term: &'a Term) -> Result<Self, ArithmeticError> {
let state = match term {
Term::AnonVar => return Err(ArithmeticError::UninstantiatedVar),
Term::Clause(cell, name, terms) => {
TermIterState::Clause(Level::Shallow, 0, cell, *name, terms)
}
Term::Literal(cell, cons) => TermIterState::Literal(Level::Shallow, cell, cons),
Term::Cons(..) | Term::PartialString(..) | Term::CompleteString(..) => {
return Err(ArithmeticError::NonEvaluableFunctor(
Literal::Atom(atom!(".")),
2,
))
}
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Shallow, cell, var_ptr.clone()),
};
Ok(ArithInstructionIterator {
state_stack: vec![state],
})
}
}
#[derive(Debug)]
pub(crate) enum ArithTermRef<'a> {
Literal(&'a Literal),
Op(Atom, usize), // name, arity.
Var(Level, &'a Cell<VarReg>, VarPtr),
}
impl<'a> Iterator for ArithInstructionIterator<'a> {
type Item = Result<ArithTermRef<'a>, ArithmeticError>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(iter_state) = self.state_stack.pop() {
match iter_state {
TermIterState::AnonVar(_) => return Some(Err(ArithmeticError::UninstantiatedVar)),
TermIterState::Clause(lvl, child_num, cell, name, subterms) => {
let arity = subterms.len();
if child_num == arity {
return Some(Ok(ArithTermRef::Op(name, arity)));
} else {
self.state_stack.push(TermIterState::Clause(
lvl,
child_num + 1,
cell,
name,
subterms,
));
self.push_subterm(lvl.child_level(), &subterms[child_num]);
}
}
TermIterState::Literal(_, _, c) => return Some(Ok(ArithTermRef::Literal(c))),
TermIterState::Var(lvl, cell, var_ptr) => {
return Some(Ok(ArithTermRef::Var(lvl, cell, var_ptr)));
}
_ => {
return Some(Err(ArithmeticError::NonEvaluableFunctor(
Literal::Atom(atom!(".")),
2,
)));
}
};
}
None
}
}
#[derive(Debug)]
pub(crate) struct ArithmeticEvaluator<'a> {
marker: &'a mut DebrayAllocator,
interm: Vec<ArithmeticTerm>,
interm_c: usize,
}
pub(crate) trait ArithmeticTermIter<'a> {
type Iter: Iterator<Item = Result<ArithTermRef<'a>, ArithmeticError>>;
fn iter(self) -> Result<Self::Iter, ArithmeticError>;
}
impl<'a> ArithmeticTermIter<'a> for &'a Term {
type Iter = ArithInstructionIterator<'a>;
fn iter(self) -> Result<Self::Iter, ArithmeticError> {
ArithInstructionIterator::from(self)
}
}
fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), ArithmeticError> {
match c {
Literal::Fixnum(n) => interm.push(ArithmeticTerm::Number(Number::Fixnum(*n))),
Literal::Integer(n) => interm.push(ArithmeticTerm::Number(Number::Integer(*n))),
Literal::Float(n) => interm.push(ArithmeticTerm::Number(Number::Float(*n.as_ptr()))),
Literal::Rational(n) => interm.push(ArithmeticTerm::Number(Number::Rational(*n))),
Literal::Atom(name) if name == &atom!("e") => interm.push(ArithmeticTerm::Number(
Number::Float(OrderedFloat(std::f64::consts::E))
)),
Literal::Atom(name) if name == &atom!("pi") => interm.push(ArithmeticTerm::Number(
Number::Float(OrderedFloat(std::f64::consts::PI))
)),
Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
Number::Float(OrderedFloat(std::f64::EPSILON))
)),
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
}
Ok(())
}
impl<'a> ArithmeticEvaluator<'a> {
pub(crate) fn new(marker: &'a mut DebrayAllocator, target_int: usize) -> Self {
ArithmeticEvaluator {
marker,
interm: Vec::new(),
interm_c: target_int,
}
}
fn get_unary_instr(
&self,
name: Atom,
a1: ArithmeticTerm,
t: usize,
) -> Result<Instruction, ArithmeticError> {
match name {
atom!("abs") => Ok(Instruction::Abs(a1, t)),
atom!("-") => Ok(Instruction::Neg(a1, t)),
atom!("+") => Ok(Instruction::Plus(a1, t)),
atom!("cos") => Ok(Instruction::Cos(a1, t)),
atom!("sin") => Ok(Instruction::Sin(a1, t)),
atom!("tan") => Ok(Instruction::Tan(a1, t)),
atom!("log") => Ok(Instruction::Log(a1, t)),
atom!("asinh") => Ok(Instruction::ASinh(a1, t)),
atom!("acosh") => Ok(Instruction::ACosh(a1, t)),
atom!("atanh") => Ok(Instruction::ATanh(a1, t)),
atom!("sinh") => Ok(Instruction::Sinh(a1, t)),
atom!("cosh") => Ok(Instruction::Cosh(a1, t)),
atom!("tanh") => Ok(Instruction::Tanh(a1, t)),
atom!("log10") => Ok(Instruction::Log10(a1, t)),
atom!("exp") => Ok(Instruction::Exp(a1, t)),
atom!("sqrt") => Ok(Instruction::Sqrt(a1, t)),
atom!("acos") => Ok(Instruction::ACos(a1, t)),
atom!("asin") => Ok(Instruction::ASin(a1, t)),
atom!("atan") => Ok(Instruction::ATan(a1, t)),
atom!("float") => Ok(Instruction::Float(a1, t)),
atom!("truncate") => Ok(Instruction::Truncate(a1, t)),
atom!("round") => Ok(Instruction::Round(a1, t)),
atom!("ceiling") => Ok(Instruction::Ceiling(a1, t)),
atom!("floor") => Ok(Instruction::Floor(a1, t)),
atom!("float_integer_part") => Ok(Instruction::FloatIntegerPart(a1, t)),
atom!("float_fractional_part") => Ok(Instruction::FloatFractionalPart(a1, t)),
atom!("sign") => Ok(Instruction::Sign(a1, t)),
atom!("\\") => Ok(Instruction::BitwiseComplement(a1, t)),
_ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 1)),
}
}
fn get_binary_instr(
&self,
name: Atom,
a1: ArithmeticTerm,
a2: ArithmeticTerm,
t: usize,
) -> Result<Instruction, ArithmeticError> {
match name {
atom!("+") => Ok(Instruction::Add(a1, a2, t)),
atom!("-") => Ok(Instruction::Sub(a1, a2, t)),
atom!("/") => Ok(Instruction::Div(a1, a2, t)),
atom!("//") => Ok(Instruction::IDiv(a1, a2, t)),
atom!("max") => Ok(Instruction::Max(a1, a2, t)),
atom!("min") => Ok(Instruction::Min(a1, a2, t)),
atom!("div") => Ok(Instruction::IntFloorDiv(a1, a2, t)),
atom!("rdiv") => Ok(Instruction::RDiv(a1, a2, t)),
atom!("*") => Ok(Instruction::Mul(a1, a2, t)),
atom!("**") => Ok(Instruction::Pow(a1, a2, t)),
atom!("^") => Ok(Instruction::IntPow(a1, a2, t)),
atom!(">>") => Ok(Instruction::Shr(a1, a2, t)),
atom!("<<") => Ok(Instruction::Shl(a1, a2, t)),
atom!("/\\") => Ok(Instruction::And(a1, a2, t)),
atom!("\\/") => Ok(Instruction::Or(a1, a2, t)),
atom!("xor") => Ok(Instruction::Xor(a1, a2, t)),
atom!("mod") => Ok(Instruction::Mod(a1, a2, t)),
atom!("rem") => Ok(Instruction::Rem(a1, a2, t)),
atom!("gcd") => Ok(Instruction::Gcd(a1, a2, t)),
atom!("atan2") => Ok(Instruction::ATan2(a1, a2, t)),
_ => Err(ArithmeticError::NonEvaluableFunctor(Literal::Atom(name), 2)),
}
}
fn incr_interm(&mut self) -> usize {
let temp = self.interm_c;
self.interm.push(ArithmeticTerm::Interm(temp));
self.interm_c += 1;
temp
}
fn instr_from_clause(
&mut self,
name: Atom,
arity: usize,
) -> Result<Instruction, ArithmeticError> {
match arity {
1 => {
let a1 = self.interm.pop().unwrap();
let ninterm = if a1.interm_or(0) == 0 {
self.incr_interm()
} else {
self.interm.push(a1.clone());
a1.interm_or(0)
};
self.get_unary_instr(name, a1, ninterm)
}
2 => {
let a2 = self.interm.pop().unwrap();
let a1 = self.interm.pop().unwrap();
let min_interm = min(a1.interm_or(0), a2.interm_or(0));
let ninterm = if min_interm == 0 {
let max_interm = max(a1.interm_or(0), a2.interm_or(0));
if max_interm == 0 {
self.incr_interm()
} else {
self.interm.push(ArithmeticTerm::Interm(max_interm));
self.interm_c = max_interm + 1;
max_interm
}
} else {
self.interm.push(ArithmeticTerm::Interm(min_interm));
self.interm_c = min_interm + 1;
min_interm
};
self.get_binary_instr(name, a1, a2, ninterm)
}
_ => Err(ArithmeticError::NonEvaluableFunctor(
Literal::Atom(name),
arity,
)),
}
}
pub(crate) fn compile_is(
&mut self,
src: &'a Term,
term_loc: GenContext,
arg: usize,
) -> Result<ArithCont, ArithmeticError> {
let mut code = CodeDeque::new();
let mut iter = src.iter()?;
while let Some(term_ref) = iter.next() {
match term_ref? {
ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?,
ArithTermRef::Var(lvl, cell, name) => {
let var_num = name.to_var_num().unwrap();
let r = if lvl == Level::Shallow {
self.marker.mark_non_callable(
var_num,
arg,
term_loc,
cell,
&mut code,
)
} else if term_loc.is_last() || cell.get().norm().reg_num() == 0 {
let r = self.marker.get_binding(var_num);
if r.reg_num() == 0 {
self.marker.mark_var::<QueryInstruction>(
var_num,
lvl,
cell,
term_loc,
&mut code,
);
cell.get().norm()
} else {
self.marker.increment_running_count(var_num);
r
}
} else {
self.marker.increment_running_count(var_num);
cell.get().norm()
};
self.interm.push(ArithmeticTerm::Reg(r));
}
ArithTermRef::Op(name, arity) => {
code.push_back(self.instr_from_clause(name, arity)?);
}
}
}
Ok((code, self.interm.pop()))
}
}
// integer division rounding function -- 9.1.3.1.
pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number {
match n {
&Number::Integer(i) => {
if let Some(n) = i.to_i64() {
fixnum!(Number, n, arena)
} else {
*n
}
}
&Number::Fixnum(_) => *n,
&Number::Float(f) => {
let f = f.floor();
const I64_MIN_TO_F: OrderedFloat<f64> = OrderedFloat(i64::MIN as f64);
const I64_MAX_TO_F: OrderedFloat<f64> = OrderedFloat(i64::MAX as f64);
if I64_MIN_TO_F <= f && f <= I64_MAX_TO_F {
fixnum!(Number, f.into_inner() as i64, arena)
} else {
Number::Integer(arena_alloc!(Integer::from(f.0 as i64), arena))
}
}
&Number::Rational(ref r) => {
let (_, floor) = (r.fract(), r.floor());
if let Some(floor) = floor.to_i64() {
fixnum!(Number, floor, arena)
} else {
Number::Integer(arena_alloc!(floor, arena))
}
}
}
}
impl From<Fixnum> for Integer {
#[inline]
fn from(n: Fixnum) -> Integer {
Integer::from(n.get_num())
}
}
// floating point rounding function -- 9.1.4.1.
pub(crate) fn rnd_f(n: &Number) -> f64 {
match n {
&Number::Fixnum(n) => n.get_num() as f64,
&Number::Integer(ref n) => n.to_f64().value(),
&Number::Float(OrderedFloat(f)) => f,
&Number::Rational(ref r) => r.to_f64().value(),
}
}
// floating point result function -- 9.1.4.2.
pub(crate) fn result_f(n: &Number) -> Result<f64, EvalError> {
classify_float(rnd_f(n))
}
fn classify_float(f: f64) -> Result<f64, EvalError> {
match f.classify() {
FpCategory::Normal | FpCategory::Zero => Ok(f),
FpCategory::Infinite => {
if OrderedFloat(f) == OrderedFloat(f64::MAX) {
Ok(f)
} else {
Err(EvalError::FloatOverflow)
}
}
FpCategory::Nan => Err(EvalError::Undefined),
_ => Ok(f)
}
}
#[inline]
pub(crate) fn float_fn_to_f(n: i64) -> Result<f64, EvalError> {
classify_float(n as f64)
}
#[inline]
pub(crate) fn float_i_to_f(n: &Integer) -> Result<f64, EvalError> {
classify_float(n.to_f64().value())
}
#[inline]
pub(crate) fn float_r_to_f(r: &Rational) -> Result<f64, EvalError> {
classify_float(r.to_f64().value())
}
#[inline]
pub(crate) fn add_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
Ok(OrderedFloat(classify_float(f1 + f2)?))
}
#[inline]
pub(crate) fn mul_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
Ok(OrderedFloat(classify_float(f1 * f2)?))
}
#[inline]
fn div_f(f1: f64, f2: f64) -> Result<OrderedFloat<f64>, EvalError> {
if FpCategory::Zero == f2.classify() {
Err(EvalError::ZeroDivisor)
} else {
Ok(OrderedFloat(classify_float(f1 / f2)?))
}
}
impl Div<Number> for Number {
type Output = Result<Number, EvalError>;
fn div(self, rhs: Number) -> Self::Output {
match (self, rhs) {
(Number::Fixnum(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
float_fn_to_f(n1.get_num())?,
float_fn_to_f(n2.get_num())?,
)?)),
(Number::Fixnum(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
float_fn_to_f(n1.get_num())?,
float_i_to_f(&n2)?,
)?)),
(Number::Integer(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
float_i_to_f(&n1)?,
float_fn_to_f(n2.get_num())?,
)?)),
(Number::Fixnum(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
float_fn_to_f(n1.get_num())?,
float_r_to_f(&n2)?,
)?)),
(Number::Rational(n1), Number::Fixnum(n2)) => Ok(Number::Float(div_f(
float_r_to_f(&n1)?,
float_fn_to_f(n2.get_num())?,
)?)),
(Number::Fixnum(n1), Number::Float(OrderedFloat(n2))) => {
Ok(Number::Float(div_f(float_fn_to_f(n1.get_num())?, n2)?))
}
(Number::Float(OrderedFloat(n1)), Number::Fixnum(n2)) => {
Ok(Number::Float(div_f(n1, float_fn_to_f(n2.get_num())?)?))
}
(Number::Integer(n1), Number::Integer(n2)) => Ok(Number::Float(div_f(
float_i_to_f(&n1)?,
float_i_to_f(&n2)?,
)?)),
(Number::Integer(n1), Number::Float(OrderedFloat(n2))) => {
Ok(Number::Float(div_f(float_i_to_f(&n1)?, n2)?))
}
(Number::Float(OrderedFloat(n2)), Number::Integer(n1)) => {
Ok(Number::Float(div_f(n2, float_i_to_f(&n1)?)?))
}
(Number::Integer(n1), Number::Rational(n2)) => Ok(Number::Float(div_f(
float_i_to_f(&n1)?,
float_r_to_f(&n2)?,
)?)),
(Number::Rational(n2), Number::Integer(n1)) => Ok(Number::Float(div_f(
float_r_to_f(&n2)?,
float_i_to_f(&n1)?,
)?)),
(Number::Rational(n1), Number::Float(OrderedFloat(n2))) => {
Ok(Number::Float(div_f(float_r_to_f(&n1)?, n2)?))
}
(Number::Float(OrderedFloat(n2)), Number::Rational(n1)) => {
Ok(Number::Float(div_f(n2, float_r_to_f(&n1)?)?))
}
(Number::Float(OrderedFloat(f1)), Number::Float(OrderedFloat(f2))) => {
Ok(Number::Float(div_f(f1, f2)?))
}
(Number::Rational(r1), Number::Rational(r2)) => Ok(Number::Float(div_f(
float_r_to_f(&r1)?,
float_r_to_f(&r2)?,
)?)),
}
}
}
impl PartialEq for Number {
fn eq(&self, rhs: &Self) -> bool {
match (self, rhs) {
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.eq(&n2),
(&Number::Fixnum(n1), &Number::Integer(ref n2)) => n1.get_num().eq(&**n2),
(&Number::Integer(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2.get_num()),
(&Number::Fixnum(n1), &Number::Rational(ref n2)) => n1.get_num().eq(&**n2),
(&Number::Rational(ref n1), &Number::Fixnum(n2)) => (&**n1).eq(&n2.get_num()),
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).eq(&n2),
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.eq(&OrderedFloat(n2.get_num() as f64)),
(&Number::Integer(ref n1), &Number::Integer(ref n2)) => n1.eq(n2),
(&Number::Integer(ref n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(n2),
(&Number::Float(n1), &Number::Integer(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())),
(&Number::Integer(ref n1), &Number::Rational(ref n2)) => {
#[cfg(feature = "num")]
{
&Rational::from(&**n1) == &**n2
}
#[cfg(not(feature = "num"))]
{
&**n1 == &**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().value()).eq(&n2),
(&Number::Float(n1), &Number::Rational(ref n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())),
(&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2),
(&Number::Rational(ref r1), &Number::Rational(ref r2)) => r1.eq(&r2),
}
}
}
impl Eq for Number {}
impl PartialOrd<usize> for Number {
#[inline]
fn partial_cmp(&self, rhs: &usize) -> Option<Ordering> {
match self {
Number::Fixnum(n) => {
let n = n.get_num();
if n < 0i64 {
Some(Ordering::Less)
} else {
(n as usize).partial_cmp(rhs)
}
}
Number::Integer(n) => (&**n).partial_cmp(rhs),
Number::Rational(r) => (&**r).partial_cmp(rhs),
Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)),
}
}
}
impl PartialEq<usize> for Number {
#[inline]
fn eq(&self, rhs: &usize) -> bool {
match self {
Number::Fixnum(n) => {
let n = n.get_num();
if n < 0i64 {
false
} else {
(n as usize).eq(rhs)
}
}
Number::Integer(n) => (&**n).eq(rhs),
Number::Rational(r) => (&**r).eq(rhs),
Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)),
}
}
}
impl PartialOrd for Number {
fn partial_cmp(&self, rhs: &Number) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
impl Ord for Number {
fn cmp(&self, rhs: &Number) -> Ordering {
match (self, rhs) {
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.get_num().cmp(&n2.get_num()),
(&Number::Fixnum(n1), Number::Integer(n2)) => Integer::from(n1.get_num()).cmp(&*n2),
(Number::Integer(n1), &Number::Fixnum(n2)) => (&**n1).cmp(&Integer::from(n2.get_num())),
(&Number::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1.get_num()).cmp(&*n2),
(Number::Rational(n1), &Number::Fixnum(n2)) => {
(&**n1).cmp(&Rational::from(n2.get_num()))
}
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).cmp(&n2),
(&Number::Float(n1), &Number::Fixnum(n2)) => n1.cmp(&OrderedFloat(n2.get_num() as f64)),
(&Number::Integer(n1), &Number::Integer(n2)) => (*n1).cmp(&*n2),
(&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(n2),
(&Number::Float(n1), &Number::Integer(ref n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())),
(&Number::Integer(n1), &Number::Rational(n2)) => {
#[cfg(feature = "num")]
{
Rational::from(&**n1).cmp(n2)
}
#[cfg(not(feature = "num"))]
{
(&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less)
}
}
(&Number::Rational(n1), &Number::Integer(n2)) => {
#[cfg(feature = "num")]
{
(&**n1).cmp(&Rational::from(&**n2))
}
#[cfg(not(feature = "num"))]
{
(&*n1).partial_cmp(&*n2).unwrap_or(Ordering::Less)
}
}
(&Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(&n2),
(&Number::Float(n1), &Number::Rational(n2)) => n1.cmp(&OrderedFloat(n2.to_f64().value())),
(&Number::Float(f1), &Number::Float(f2)) => f1.cmp(&f2),
(&Number::Rational(r1), &Number::Rational(r2)) => (*r1).cmp(&*r2),
}
}
}
impl TryFrom<HeapCellValue> for Number {
type Error = ();
#[inline]
fn try_from(value: HeapCellValue) -> Result<Number, Self::Error> {
read_heap_cell!(value,
(HeapCellValueTag::Cons, c) => {
match_untyped_arena_ptr!(c,
(ArenaHeaderTag::Integer, n) => {
Ok(Number::Integer(n))
}
(ArenaHeaderTag::Rational, n) => {
Ok(Number::Rational(n))
}
_ => {
Err(())
}
)
}
(HeapCellValueTag::F64, n) => {
Ok(Number::Float(*n))
}
(HeapCellValueTag::Fixnum | HeapCellValueTag::CutPoint, n) => {
Ok(Number::Fixnum(n))
}
_ => {
Err(())
}
)
}
}
// Computes n ^ power. Ignores the sign of power.
pub(crate) fn binary_pow(mut n: Integer, power: &Integer) -> Integer {
let mut power = Integer::from(power.abs());
if power == 0 {
return Integer::from(1);
}
let mut oddand = Integer::from(1);
while power > 1 {
if power.is_odd() {
oddand *= &n;
}
n = n.pow(2);
power >>= 1;
}
n * oddand
}

401
src/atom_table.rs Normal file
View File

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

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

@@ -0,0 +1,12 @@
fn main() -> std::process::ExitCode {
use std::sync::atomic::Ordering;
use scryer_prolog::*;
#[cfg(feature = "repl")]
ctrlc::set_handler(move || {
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed);
}).unwrap();
let mut wam = machine::Machine::new();
wam.run_top_level()
}

1239
src/codegen.rs Normal file

File diff suppressed because it is too large Load Diff

847
src/debray_allocator.rs Normal file
View File

@@ -0,0 +1,847 @@
use crate::allocator::*;
use crate::codegen::SubsumedBranchHits;
use crate::forms::Level;
use crate::instructions::*;
use crate::machine::disjuncts::VarData;
use crate::parser::ast::*;
use crate::targets::*;
use crate::variable_records::*;
use bit_set::*;
use bitvec::prelude::*;
use fxhash::FxBuildHasher;
use indexmap::IndexMap;
use std::cell::Cell;
use std::collections::VecDeque;
use std::ops::{Deref, DerefMut};
pub type BranchHits = IndexMap<usize, BitVec, FxBuildHasher>; // key: var_num, value: branch arm occurrences.
#[derive(Debug, Default)]
pub struct BranchOccurrences {
pub hits: BranchHits,
pub shallow_safety: BitSet<usize>, // unset means safe, set means unsafe (after the branch merge)
pub deep_safety: BitSet<usize>,
pub num_branches: usize,
pub current_branch: usize,
pub subsumed_hits: SubsumedBranchHits,
}
impl BranchOccurrences {
fn new(num_branches: usize) -> Self {
Self {
hits: BranchHits::with_hasher(FxBuildHasher::default()),
shallow_safety: BitSet::default(),
deep_safety: BitSet::default(),
num_branches,
current_branch: 0,
subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
}
}
}
#[derive(Debug)]
pub(crate) struct BranchStack {
stack: Vec<BranchOccurrences>,
}
impl Deref for BranchStack {
type Target = Vec<BranchOccurrences>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.stack
}
}
impl DerefMut for BranchStack {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.stack
}
}
impl BranchStack {
fn branch_subsumes(&self, branch: &BranchDesignator, sub_branch: &BranchDesignator) -> bool {
if branch.branch_stack_num < sub_branch.branch_stack_num {
if branch.branch_stack_num == 0 {
true
} else {
let idx = branch.branch_stack_num - 1;
self[idx].current_branch == branch.branch_num
}
} else {
branch == sub_branch
}
}
fn safety_unneeded_in_branch(&self, safety: &VarSafetyStatus, branch: &BranchDesignator) -> bool {
match safety {
VarSafetyStatus::Needed => false,
VarSafetyStatus::LocallyUnneeded(planter_branch) =>
self.branch_subsumes(planter_branch, branch),
VarSafetyStatus::GloballyUnneeded => true,
}
}
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
if let Some(occurrences) = self.last_mut() {
debug_assert!(occurrences.current_branch < occurrences.num_branches);
let num_branches = occurrences.num_branches;
let entry = occurrences.hits.entry(var_num)
.or_insert_with(|| BitVec::repeat(false, num_branches));
entry.set(occurrences.current_branch, true);
occurrences.subsumed_hits.insert(var_num);
}
}
pub(crate) fn add_branch_stack(&mut self, num_branches: usize) {
self.push(BranchOccurrences::new(num_branches));
}
pub(crate) fn current_branch_designator(&self) -> BranchDesignator {
let branch_stack_num = self.len();
let branch_num = self.last()
.map(|occurrences| occurrences.current_branch)
.unwrap_or(0);
BranchDesignator { branch_stack_num, branch_num }
}
#[inline]
pub(crate) fn incr_current_branch(&mut self) {
let branch_occurrences = self.last_mut().unwrap();
branch_occurrences.current_branch += 1;
}
#[inline]
pub(crate) fn drain_branches(&mut self, depth: usize) -> std::vec::Drain<BranchOccurrences> {
let start_idx = self.len() - depth;
self.drain(start_idx ..)
}
}
#[derive(Debug)]
pub(crate) struct DebrayAllocator {
pub(crate) var_data: VarData, // var_data replaces bindings.
pub(crate) branch_stack: BranchStack,
pub(crate) in_tail_position: bool,
arg_c: usize,
temp_lb: usize,
perm_lb: usize,
arity: usize, // 0 if not at head.
shallow_temp_mappings: IndexMap<usize, usize, FxBuildHasher>,
in_use: BitSet<usize>, // deep and non-var allocations
temp_free_list: Vec<usize>,
perm_free_list: VecDeque<(usize, usize)>, // chunk_num, var_num
}
impl DebrayAllocator {
pub(crate) fn add_branch(&mut self) {
let branch_designator = self.branch_stack.current_branch_designator();
let subsumed_hits = {
let branch_occurrences = self.branch_stack.last_mut().unwrap();
std::mem::replace(
&mut branch_occurrences.subsumed_hits,
SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
)
};
for var_num in subsumed_hits {
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, ref mut allocation) => {
match allocation {
PermVarAllocation::Done { shallow_safety, deep_safety, .. } => {
if !self.branch_stack.safety_unneeded_in_branch(shallow_safety, &branch_designator) {
let branch_occurrences = self.branch_stack.last_mut().unwrap();
branch_occurrences.shallow_safety.insert(var_num);
}
if !self.branch_stack.safety_unneeded_in_branch(deep_safety, &branch_designator) {
let branch_occurrences = self.branch_stack.last_mut().unwrap();
branch_occurrences.deep_safety.insert(var_num);
}
}
_ => {
unreachable!();
}
}
*allocation = PermVarAllocation::Pending;
}
_ => unreachable!(),
}
}
}
pub(crate) fn pop_branch(&mut self, depth: usize, subsumed_hits: SubsumedBranchHits) {
let removed_branches = self.branch_stack.drain_branches(depth);
let (deep_safety, shallow_safety) = removed_branches
.into_iter()
.fold((BitSet::default(), BitSet::default()),
|(mut deep_safety, mut shallow_safety), branch_occurrences| {
deep_safety.union_with(&branch_occurrences.deep_safety);
shallow_safety.union_with(&branch_occurrences.shallow_safety);
(deep_safety, shallow_safety)
});
let branch_designator = self.branch_stack.current_branch_designator();
let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() {
Some(latest_branch) => {
latest_branch.deep_safety.union_with(&deep_safety);
latest_branch.shallow_safety.union_with(&shallow_safety);
(&latest_branch.deep_safety, &latest_branch.shallow_safety)
}
None => (&deep_safety, &shallow_safety)
};
for var_num in subsumed_hits.iter().cloned() {
let running_count = self.var_data.records[var_num].running_count;
let num_occurrences = self.var_data.records[var_num].num_occurrences;
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, allocation) => {
let shallow_safety = VarSafetyStatus::needed_if(
shallow_safety.contains(var_num),
branch_designator,
);
let deep_safety = VarSafetyStatus::needed_if(
deep_safety.contains(var_num),
branch_designator,
);
if running_count < num_occurrences {
*allocation = PermVarAllocation::Done { shallow_safety, deep_safety };
}
}
_ => unreachable!()
}
}
if self.branch_stack.len() > 0 {
for var_num in subsumed_hits {
self.branch_stack.add_branch_occurrence(var_num);
}
}
}
fn is_curr_arg_distinct_from(&self, var_num: usize) -> bool {
match self.shallow_temp_mappings.get(&self.arg_c).cloned() {
Some(t_var) => t_var != var_num,
_ => false,
}
}
fn occurs_shallowly_in_head(&self, var_num: usize, r: usize) -> bool {
match &self.var_data.records[var_num].allocation {
VarAlloc::Temp { temp_var_data, term_loc: GenContext::Head, .. } => {
temp_var_data.use_set.contains(&(GenContext::Head, r))
}
_ => false,
}
}
#[inline]
fn is_in_use(&self, r: usize) -> bool {
let in_use_range = r <= self.arity && r >= self.arg_c;
in_use_range || self.in_use.contains(r)
}
fn alloc_with_cr(&self, var_num: usize) -> usize {
match &self.var_data.records[var_num].allocation {
VarAlloc::Temp { temp_var_data, .. } => {
for &(_, reg) in temp_var_data.use_set.iter() {
if !self.is_in_use(reg) {
return reg;
}
}
let mut result = 0;
for reg in self.temp_lb.. {
if !self.is_in_use(reg) {
if !temp_var_data.no_use_set.contains(reg) {
result = reg;
break;
}
}
}
result
}
_ => 0,
}
}
fn alloc_with_ca(&self, var_num: usize) -> usize {
match &self.var_data.records[var_num].allocation {
VarAlloc::Temp { temp_var_data, .. } => {
for &(_, reg) in temp_var_data.use_set.iter() {
if !self.is_in_use(reg) {
return reg;
}
}
let mut result = 0;
for reg in self.temp_lb.. {
if !self.is_in_use(reg) {
if !temp_var_data.no_use_set.contains(reg) {
if !temp_var_data.conflict_set.contains(reg) {
result = reg;
break;
}
}
}
}
result
}
_ => 0,
}
}
fn alloc_in_last_goal_hint(&self, chunk_num: usize) -> Option<(usize, usize)> {
// we want to allocate a register to the k^{th} parameter, par_k.
// par_k may not be a temporary variable.
let k = self.arg_c;
match self.shallow_temp_mappings.get(&k).cloned() {
Some(t_var) => {
// suppose this branch fires. then t_var is a
// temp. var. belonging to the current chunk.
// consider its use set. T == par_k iff
// (GenContext::Last(_), k) is in t_var.use_set.
match &self.var_data.records[t_var].allocation {
VarAlloc::Temp { temp_var_data, .. } => {
if !temp_var_data.use_set.contains(&(GenContext::Last(chunk_num), k)) {
return Some((t_var, self.alloc_with_ca(t_var)));
}
}
_ => {
}
}
None
}
_ => None,
}
}
fn evacuate_arg<'a, Target: CompilationTarget<'a>>(
&mut self,
chunk_num: usize,
code: &mut CodeDeque,
) {
match self.alloc_in_last_goal_hint(chunk_num) {
Some((var_num, r)) => {
let k = self.arg_c;
if r != k {
let r = RegType::Temp(r);
code.push_back(Target::move_to_register(r, k));
self.shallow_temp_mappings.swap_remove(&k);
self.shallow_temp_mappings.insert(r.reg_num(), var_num);
self.var_data.records[var_num].allocation.set_register(r.reg_num());
self.in_use.insert(r.reg_num());
}
}
_ => {}
};
}
fn alloc_reg_to_var<'a, Target: CompilationTarget<'a>>(
&mut self,
var_num: usize,
lvl: Level,
term_loc: GenContext,
target: &mut CodeDeque,
) -> usize {
match term_loc {
GenContext::Head => {
if let Level::Shallow = lvl {
self.evacuate_arg::<Target>(0, target);
self.alloc_with_cr(var_num)
} else {
self.alloc_with_ca(var_num)
}
}
GenContext::Mid(_) => self.alloc_with_ca(var_num),
GenContext::Last(chunk_num) => {
if let Level::Shallow = lvl {
self.evacuate_arg::<Target>(chunk_num, target);
self.alloc_with_cr(var_num)
} else {
self.alloc_with_ca(var_num)
}
}
}
}
fn alloc_reg_to_non_var(&mut self) -> usize {
let mut final_index = 0;
while let Some(r) = self.temp_free_list.pop() {
if !self.is_in_use(r) {
self.in_use.insert(r);
return r;
}
}
for index in self.temp_lb.. {
if !self.in_use.contains(index) {
final_index = index;
self.in_use.insert(final_index);
break;
}
}
self.temp_lb = final_index + 1;
final_index
}
fn in_place(&self, var_num: usize, term_loc: GenContext, r: RegType, k: usize) -> bool {
match term_loc {
GenContext::Head if !r.is_perm() => r.reg_num() == k,
_ => {
match &self.var_data.records[var_num].allocation {
&VarAlloc::Temp { temp_reg, .. } if r.reg_num() == k =>
temp_reg == k,
_ => false,
}
},
}
}
fn alloc_perm_var(&mut self, var_num: usize, chunk_num: usize) -> usize {
let p = if let Some(p) = self.pop_free_perm(chunk_num) {
p
} else {
let p = self.perm_lb;
self.perm_lb += 1;
p
};
self.var_data.records[var_num].allocation = VarAlloc::Perm(p, PermVarAllocation::done());
p
}
pub(crate) fn add_reg_to_free_list(&mut self, r: RegType) {
if let RegType::Temp(r) = r {
self.in_use.remove(r);
self.temp_free_list.push(r);
}
}
pub fn reset_free_list(&mut self) {
self.temp_free_list.clear();
}
#[inline(always)]
pub fn get_binding(&self, var_num: usize) -> RegType {
self.var_data.records[var_num].allocation.as_reg_type()
}
pub fn num_perm_vars(&self) -> usize {
self.perm_lb - 1
}
pub fn increment_running_count(&mut self, var_num: usize) {
self.var_data.records[var_num].running_count += 1;
}
fn add_perm_to_free_list(&mut self, chunk_num: usize, var_num: usize) {
match &self.var_data.records[var_num].allocation {
VarAlloc::Perm(..) => {
self.perm_free_list.push_back((chunk_num, var_num));
}
_ => {}
}
}
fn pop_free_perm(&mut self, chunk_num: usize) -> Option<usize> {
while let Some((perm_chunk_num, var_num)) = self.perm_free_list.front().cloned() {
if chunk_num > perm_chunk_num {
self.perm_free_list.pop_front();
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(p, PermVarAllocation::Pending) if *p > 0 => {
return Some(std::mem::replace(p, 0));
}
_ => {
}
}
} else {
return None;
}
}
None
}
pub(crate) fn free_var(&mut self, chunk_num: usize, var_num: usize) {
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, allocation) => {
*allocation = PermVarAllocation::Pending;
self.add_perm_to_free_list(chunk_num, var_num);
}
_ => {
}
}
}
pub(crate) fn mark_safe_var_unconditionally(&mut self, var_num: usize) {
let branch_designator = self.branch_stack.current_branch_designator();
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => {
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
}
VarAlloc::Temp { safety, .. } => {
*safety = VarSafetyStatus::unneeded(branch_designator);
}
_ => unreachable!(),
}
}
fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) {
let branch_designator = self.branch_stack.current_branch_designator();
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, shallow_safety, .. }) => {
// GetVariable in head chunk is considered safe.
if lvl == Level::Deep {
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
} else if term_loc == GenContext::Head {
*shallow_safety = VarSafetyStatus::GloballyUnneeded;
} else {
if let Some(temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c).cloned() {
match &mut self.var_data.records[temp_var_num].allocation {
VarAlloc::Temp { ref mut to_perm_var_num, .. } => {
*to_perm_var_num = Some(var_num);
}
_ => unreachable!()
}
}
}
}
VarAlloc::Temp { ref mut safety, .. } => {
*safety = VarSafetyStatus::GloballyUnneeded;
}
_ => {
unreachable!()
}
}
}
fn argument_to_value<'a, Target: CompilationTarget<'a>>(
&mut self,
var_num: usize,
r: RegType,
arg_c: usize,
) -> Instruction {
let branch_designator = self.branch_stack.current_branch_designator();
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, PermVarAllocation::Done { ref mut shallow_safety, .. }) => {
if !self.in_tail_position || self.branch_stack.safety_unneeded_in_branch(shallow_safety, &branch_designator) {
Target::argument_to_value(r, arg_c)
} else {
*shallow_safety = VarSafetyStatus::unneeded(branch_designator);
Target::unsafe_argument_to_value(r, arg_c)
}
}
VarAlloc::Temp { ref mut safety, .. } => {
if self.branch_stack.safety_unneeded_in_branch(safety, &branch_designator) {
Target::argument_to_value(r, arg_c)
} else {
*safety = VarSafetyStatus::GloballyUnneeded;
Target::unsafe_argument_to_value(r, arg_c)
}
}
_ => {
unreachable!()
}
}
}
fn subterm_to_value<'a, Target: CompilationTarget<'a>>(
&mut self,
var_num: usize,
r: RegType,
) -> Instruction {
let branch_designator = self.branch_stack.current_branch_designator();
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, PermVarAllocation::Done { ref mut deep_safety, .. }) => {
if self.branch_stack.safety_unneeded_in_branch(deep_safety, &branch_designator) {
Target::subterm_to_value(r)
} else {
*deep_safety = VarSafetyStatus::unneeded(branch_designator);
Target::unsafe_subterm_to_value(r)
}
}
VarAlloc::Temp { ref mut safety, .. } => {
if self.branch_stack.safety_unneeded_in_branch(safety, &branch_designator) {
Target::subterm_to_value(r)
} else {
*safety = VarSafetyStatus::unneeded(branch_designator);
Target::unsafe_subterm_to_value(r)
}
}
_ => {
unreachable!()
}
}
}
}
impl Allocator for DebrayAllocator {
fn new() -> DebrayAllocator {
Self {
var_data: VarData::default(),
in_tail_position: false,
arity: 0,
arg_c: 1,
temp_lb: 1,
perm_lb: 1,
shallow_temp_mappings: IndexMap::with_hasher(FxBuildHasher::default()),
in_use: BitSet::default(),
temp_free_list: vec![],
perm_free_list: VecDeque::new(),
branch_stack: BranchStack { stack: vec![] }
}
}
fn mark_anon_var<'a, Target: CompilationTarget<'a>>(
&mut self,
lvl: Level,
term_loc: GenContext,
code: &mut CodeDeque,
) {
let r = RegType::Temp(self.alloc_reg_to_non_var());
match lvl {
Level::Deep => code.push_back(Target::subterm_to_variable(r)),
Level::Root | Level::Shallow => {
let k = self.arg_c;
if let GenContext::Last(chunk_num) = term_loc {
self.evacuate_arg::<Target>(chunk_num, code);
}
self.arg_c += 1;
code.push_back(Target::argument_to_variable(r, k));
}
};
}
fn mark_non_var<'a, Target: CompilationTarget<'a>>(
&mut self,
lvl: Level,
term_loc: GenContext,
cell: &'a Cell<RegType>,
code: &mut CodeDeque,
) {
let r = cell.get();
let r = match lvl {
Level::Shallow => {
let k = self.arg_c;
if let GenContext::Last(chunk_num) = term_loc {
self.evacuate_arg::<Target>(chunk_num, code);
}
self.arg_c += 1;
RegType::Temp(k)
}
_ if r.reg_num() == 0 => RegType::Temp(self.alloc_reg_to_non_var()),
_ => {
self.in_use.insert(r.reg_num());
r
}
};
cell.set(r);
}
fn mark_var<'a, Target: CompilationTarget<'a>>(
&mut self,
var_num: usize,
lvl: Level,
cell: &'a Cell<VarReg>,
term_loc: GenContext,
code: &mut CodeDeque,
) {
let (r, is_new_var) = match self.get_binding(var_num) {
RegType::Temp(0) => {
let o = self.alloc_reg_to_var::<Target>(var_num, lvl, term_loc, code);
cell.set(VarReg::Norm(RegType::Temp(o)));
(RegType::Temp(o), true)
}
RegType::Perm(0) => {
let p = self.alloc_perm_var(var_num, term_loc.chunk_num());
(RegType::Perm(p), true)
}
r @ RegType::Perm(_) => {
let is_new_var = match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, allocation) => if allocation.pending() {
*allocation = PermVarAllocation::done();
true
} else {
false
},
_ => unreachable!(),
};
(r, is_new_var)
}
r => (r, false),
};
self.mark_reserved_var::<Target>(var_num, lvl, cell, term_loc, code, r, is_new_var);
}
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
&mut self,
var_num: usize,
lvl: Level,
cell: &'a Cell<VarReg>,
term_loc: GenContext,
code: &mut CodeDeque,
r: RegType,
is_new_var: bool,
) {
match lvl {
Level::Root | Level::Shallow => {
let k = self.arg_c;
if self.is_curr_arg_distinct_from(var_num) {
self.evacuate_arg::<Target>(term_loc.chunk_num(), code);
}
cell.set(VarReg::ArgAndNorm(r, k));
if !self.in_place(var_num, term_loc, r, k) {
if is_new_var {
self.mark_safe_var(var_num, lvl, term_loc);
code.push_back(Target::argument_to_variable(r, k));
} else {
code.push_back(self.argument_to_value::<Target>(var_num, r, k));
}
}
self.arg_c += 1;
}
Level::Deep if is_new_var => {
if let GenContext::Head = term_loc {
if self.occurs_shallowly_in_head(var_num, r.reg_num()) {
code.push_back(self.subterm_to_value::<Target>(var_num, r));
} else {
self.mark_safe_var(var_num, lvl, term_loc);
code.push_back(Target::subterm_to_variable(r));
}
} else {
self.mark_safe_var(var_num, lvl, term_loc);
code.push_back(Target::subterm_to_variable(r));
}
}
Level::Deep => code.push_back(self.subterm_to_value::<Target>(var_num, r)),
}
let o = r.reg_num();
if !r.is_perm() {
self.shallow_temp_mappings.insert(o, var_num);
} else if r.is_perm() && is_new_var {
self.branch_stack.add_branch_occurrence(var_num);
}
let record = &mut self.var_data.records[var_num];
record.allocation.set_register(o);
if record.running_count < record.num_occurrences {
record.running_count += 1;
} else {
self.free_var(term_loc.chunk_num(), var_num);
}
self.in_use.insert(o);
}
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType {
match self.get_binding(var_num) {
RegType::Perm(0) | RegType::Temp(0) => {
RegType::Perm(self.alloc_perm_var(var_num, chunk_num))
}
r => r,
}
}
fn reset(&mut self) {
self.perm_lb = 1;
self.shallow_temp_mappings.clear();
self.in_use.clear();
self.temp_free_list.clear();
}
fn reset_contents(&mut self) {
self.in_use.clear();
self.shallow_temp_mappings.clear();
self.temp_free_list.clear();
}
fn advance_arg(&mut self) {
self.arg_c += 1;
}
fn reset_at_head(&mut self, args: &Vec<Term>) {
self.reset_arg(args.len());
self.arity = args.len();
for (idx, arg) in args.iter().enumerate() {
if let &Term::Var(_, ref var) = arg {
let var_num = var.to_var_num().unwrap();
let r = self.get_binding(var_num);
if !r.is_perm() && r.reg_num() == 0 {
self.in_use.insert(idx + 1);
self.shallow_temp_mappings.insert(idx + 1, var_num);
self.var_data.records[var_num].allocation.set_register(idx + 1);
}
}
}
}
fn reset_arg(&mut self, arity: usize) {
self.arity = 0;
self.arg_c = 1;
self.temp_lb = arity + 1;
}
#[inline(always)]
fn max_reg_allocated(&self) -> usize {
std::cmp::max(self.temp_lb, self.arg_c)
}
}

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

View File

@@ -1,4 +1,4 @@
:- module(bimetatrans_ruleml, [parse_ruleml/3]). :- module(bimetatrans, [parse_ruleml/3]).
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
:- use_module(library(iso_ext)). :- use_module(library(iso_ext)).
@@ -19,28 +19,34 @@
* all indentation stripped out. "Indented XML" contains indentation * all indentation stripped out. "Indented XML" contains indentation
* and newlines, and is usually formatted for human readers. * and newlines, and is usually formatted for human readers.
* *
* parse_ruleml/3 is the sole public predicate of BiMetaTrans(RuleML, * parse_ruleml/3 is the sole public predicate of BiMetaTrans(Prolog,
* Prolog), a bidirectional translator capable of parsing well-formed * RuleML), a bidirectional translator capable of parsing well-formed
* RuleML/XML (valid w.r.t. a proposed anchor schema nafhornlogeq * RuleML/XML (valid w.r.t. a proposed anchor schema nafhornlogeq
* defining a sublanguage of the existing anchor schema naffologeq) to * defining a sublanguage of the existing anchor schema naffologeq) to
* an equivalent Prolog/'$V' term and back to RuleML/xmL. It has the * an equivalent Prolog/'$V' term and back to RuleML/xmL. It has the
* modes * modes
* *
* +AssertItem, +QueryItems, ?XML (Prolog->RuleML) * +AssertItems, +QueryItems, ?XML (Prolog->RuleML)
* ?AssertItem, ?QueryItems, +XML (RuleML->Prolog) * ?AssertItems, ?QueryItems, +XML (RuleML->Prolog)
* *
* The modes constrain the inputs to fit one of two patterns, the * The modes constrain the inputs to fit one of two patterns, the
* first where AssertItem and QueryItem are instantiated and XML is * first where AssertItems and QueryItems are instantiated and XML is
* possibly a variable, and conversely for the second. Instantiated * possibly a variable, and conversely for the second. Instantiated
* inputs are expected to be ground, meaning they should not contain * inputs are expected to be ground, meaning they should not contain
* free variables. * free variables.
* *
* A RuleML/XML document is assumed to contain an optional Assert
* element (with zero or more children) and zero or more Query
* elements. The children of the Assert element are the Items of
* AssertItems, while the Items of QueryItems each correspond to a
* single Query element.
*
* parse_ruleml/3 is meant to subsume an invertible function in the * parse_ruleml/3 is meant to subsume an invertible function in the
* sense that the top-level query * sense that the top-level query
* *
* ?- parse_ruleml(AssertItem, QueryItems, XML0), % Prolog->RuleML * ?- parse_ruleml(AssertItems, QueryItems, XML0), % Prolog->RuleML
* parse_ruleml(AssertItem0, QueryItems0, XML), % RuleML->Prolog * parse_ruleml(AssertItems0, QueryItems0, XML), % RuleML->Prolog
* AssertItem0 == AssertItem, % Test for expected round-trip results. * AssertItems0 == AssertItems, % Test for expected round-trip results.
* QueryItems0 == QueryItems, * QueryItems0 == QueryItems,
* XML0 == XML. * XML0 == XML.
* *
@@ -48,10 +54,11 @@
* not 0-appended are instantiated and therefore ground. * not 0-appended are instantiated and therefore ground.
*/ */
parse_ruleml(AssertItem, QueryItem, XML) :- parse_ruleml(AssertItems, QueryItems, XML) :-
( ( var(AssertItem) ; var(QueryItem) ), var(XML) -> ( ( var(AssertItems) ; var(QueryItems) ), var(XML) ->
throw(error(instantiation_error, parse_ruleml/2)) throw(error(instantiation_error, parse_ruleml/2))
; phrase(ruleml_top_level_items(AssertItem, QueryItem), XML) ; phrase(ruleml_top_level_items(AssertItems, QueryItems), XML),
!
). ).
@@ -82,24 +89,15 @@ parse_header -->
* *
* The two arguments are (un)instantiated depending on the direction * The two arguments are (un)instantiated depending on the direction
* in which parse_ruleml/2 operates. In either direction, once * in which parse_ruleml/2 operates. In either direction, once
* ruleml_query_item(QueryItems) or ruleml_assert(AssertItem) * ruleml_query_item(QueryItems) or ruleml_assert(AssertItems)
* succeeds: * succeeds, AssertItems and QueryItems are ground lists of terms,
* * corresponding to Assert/Query performative(s) of the RuleML/XML KB.
* 1) AssertItem/QueryItems is a fully ground list of terms,
* corresponding to an Assert/Query performative of the RuleML/XML KB.
*
* 2) It would not be meaningful to backtrack after the last success,
* thus undoing its effects; that is because we have already refuted
* the possibility of there being further items for the grammar to
* consume. Hence we see that the cut (!) does not restrict the
* generality of the grammar.
*/ */
ruleml_top_level_items(AssertItem, QueryItems) --> ruleml_top_level_items(AssertItems, QueryItems) -->
( ruleml_assert(AssertItem), ( ruleml_assert(AssertItems),
ruleml_query_items(QueryItems), ruleml_query_items(QueryItems)
! ; { AssertItems = [] },
; { AssertItem = [] },
ruleml_query_items(QueryItems) ruleml_query_items(QueryItems)
). ).
@@ -169,22 +167,16 @@ ruleml_top_level_items(AssertItem, QueryItems) -->
* outset of ruleml_assert//1 will cause it to fail, meaning that * outset of ruleml_assert//1 will cause it to fail, meaning that
* an empty <Assert></Assert> element is never emitted to the output * an empty <Assert></Assert> element is never emitted to the output
* RuleML/xmL. * RuleML/xmL.
*
* The two cuts are green cuts, meaning they do not change the meaning
* or output of the program, they simply prevent needless
* backtracking.
*/ */
ruleml_assert(Items) --> ruleml_assert(Items) -->
( { var(Items) } -> ( { var(Items) } ->
list_ws("<Assert mapClosure=\"universal\">"), list_ws("<Assert mapClosure=\"universal\">"),
ruleml_assert_items(Items), ruleml_assert_items(Items),
!,
list_ws("</Assert>") list_ws("</Assert>")
; "<Assert mapClosure=\"universal\">", ; "<Assert mapClosure=\"universal\">",
{ Items \== [] }, { Items \== [] },
ruleml_assert_items(Items), ruleml_assert_items(Items),
!,
"</Assert>" "</Assert>"
). ).
@@ -206,7 +198,6 @@ ruleml_assert(Items) -->
ruleml_assert_items([Item | Items]) --> ruleml_assert_items([Item | Items]) -->
ruleml_assert_item(Item), ruleml_assert_item(Item),
!,
ruleml_assert_items(Items). ruleml_assert_items(Items).
ruleml_assert_items([]) --> []. ruleml_assert_items([]) --> [].
@@ -214,7 +205,6 @@ ruleml_assert_items([]) --> [].
ruleml_query_items(Items) --> ruleml_query_items(Items) -->
( { var(Items) } -> ( { var(Items) } ->
ruleml_query_item(Item), ruleml_query_item(Item),
!,
( { var(Item) } -> ( { var(Item) } ->
ruleml_query_items(Items) ruleml_query_items(Items)
; { Items = [Item | Items0] }, ; { Items = [Item | Items0] },
@@ -222,7 +212,6 @@ ruleml_query_items(Items) -->
) )
; { Items = [Item | Items0] }, ; { Items = [Item | Items0] },
ruleml_query_item(Item), ruleml_query_item(Item),
!,
ruleml_query_items(Items0) ruleml_query_items(Items0)
). ).
ruleml_query_items([]) --> []. ruleml_query_items([]) --> [].
@@ -279,7 +268,7 @@ space(' ') --> " ".
decimal_point('.') --> ".". decimal_point('.') --> ".".
sign('-') --> "-", !. sign('-') --> "-".
sign('+') --> "+". sign('+') --> "+".
double_quote('"') --> "\"". double_quote('"') --> "\"".
@@ -332,7 +321,6 @@ ruleml_condition(Item) -->
ruleml_item_conjunction([Item | Items]) --> ruleml_item_conjunction([Item | Items]) -->
ruleml_condition(Item), ruleml_condition(Item),
!,
ruleml_item_conjunction(Items). ruleml_item_conjunction(Items).
ruleml_item_conjunction([]) --> []. ruleml_item_conjunction([]) --> [].
@@ -404,7 +392,6 @@ ruleml_conjunction_of_items(Items) -->
ruleml_item_disjunction([Item | Items]) --> ruleml_item_disjunction([Item | Items]) -->
ruleml_condition(Item), ruleml_condition(Item),
!,
ruleml_item_disjunction(Items). ruleml_item_disjunction(Items).
ruleml_item_disjunction([]) --> []. ruleml_item_disjunction([]) --> [].
@@ -549,7 +536,6 @@ ruleml_naf(Item) -->
ruleml_atoms([Item|Items]) --> ruleml_atoms([Item|Items]) -->
ruleml_atom(Item), ruleml_atom(Item),
!,
ruleml_atoms(Items). ruleml_atoms(Items).
ruleml_atoms([]) --> []. ruleml_atoms([]) --> [].
@@ -652,7 +638,6 @@ ruleml_expr(Item) -->
( list_ws("<repo>"), ( list_ws("<repo>"),
ruleml_item(RepoItem), ruleml_item(RepoItem),
list_ws("</repo>"), list_ws("</repo>"),
!,
{ fold_commas(Args, ArgsCommas) }, { fold_commas(Args, ArgsCommas) },
{ Item =.. [Name, (ArgsCommas | RepoItem)] } { Item =.. [Name, (ArgsCommas | RepoItem)] }
; { Item =.. [Name | Args] } ; { Item =.. [Name | Args] }
@@ -823,15 +808,12 @@ ruleml_data(Name) -->
* converting from lists of characters (here denoted as Cs) to the * converting from lists of characters (here denoted as Cs) to the
* named data type in ISO Prolog. The type information is given * named data type in ISO Prolog. The type information is given
* in the first argument. * in the first argument.
*
* Grammatically, ruleml_data_contents//3 is a disjoint union of its
* subgrammars, justifying the cut at the end of the first two rules.
*/ */
ruleml_data_contents(number, Cs) --> ruleml_data_contents(number, Cs) -->
ruleml_number(Cs), !. ruleml_number(Cs).
ruleml_data_contents(symbol, Cs) --> ruleml_data_contents(symbol, Cs) -->
ruleml_symbol(Cs), !. ruleml_symbol(Cs).
ruleml_data_contents(string, Cs) --> ruleml_data_contents(string, Cs) -->
ruleml_string(Cs). ruleml_string(Cs).
@@ -1195,8 +1177,7 @@ fold_semicolons(List, Output) :-
* complicated by Scryer's current lack of multi-argument indexing. * complicated by Scryer's current lack of multi-argument indexing.
*/ */
fold_list([Item], _, Item) :- fold_list([Item], _, Item).
!.
fold_list([Item|Items], F, Form) :- fold_list([Item|Items], F, Form) :-
Form =.. [F, Item, Fs], Form =.. [F, Item, Fs],
fold_list(Items, F, Fs). fold_list(Items, F, Fs).

View File

@@ -1,6 +1,6 @@
:- module(bimetatrans_ruleml_tests, [test_bimetatrans_ruleml/0]). :- module(bimetatrans_tests, [test_bimetatrans/0]).
:- use_module('bimetatrans_ruleml'). :- use_module(bimetatrans).
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
:- use_module(library(iso_ext)). :- use_module(library(iso_ext)).
@@ -21,28 +21,6 @@
* order of ascending N. * order of ascending N.
*/ */
term_expansion(Term0, Term) :-
nonvar(Term0),
Term0 = test(N, Assert, Query, XML),
integer(N),
list_si(Assert),
list_si(Query),
partial_string(XML),
number_chars(N, NChars),
atom_chars(NAtom, NChars),
atom_concat(prolog2ruleml_, NAtom, Prolog2RuleML),
atom_concat(ruleml2prolog_, NAtom, RuleML2Prolog),
atom_concat(test_, NAtom, TestN),
strip_indentation(XML, XML1),
Term = [(Prolog2RuleML :- parse_ruleml(Assert, Query, XML0),
XML0 = XML1),
(RuleML2Prolog :- parse_ruleml(Assert0, Query0, XML),
Assert0 = Assert,
Query0 = Query),
(TestN :- write(test(N)), nl, Prolog2RuleML, RuleML2Prolog, !),
(TestN :- throw(error(test_failure, TestN)))].
until_non_space_or_end([C|Cs], Cs1) :- until_non_space_or_end([C|Cs], Cs1) :-
( C == (' ') -> ( C == (' ') ->
until_non_space_or_end(Cs, Cs1) until_non_space_or_end(Cs, Cs1)
@@ -66,6 +44,28 @@ strip_indentation_([C|Cs], Cs0) :-
strip_indentation_([], []). strip_indentation_([], []).
user:term_expansion(Term0, Term) :-
nonvar(Term0),
Term0 = test(N, Assert, Query, XML),
integer(N),
list_si(Assert),
list_si(Query),
partial_string(XML),
number_chars(N, NChars),
atom_chars(NAtom, NChars),
atom_concat(prolog2ruleml_, NAtom, Prolog2RuleML),
atom_concat(ruleml2prolog_, NAtom, RuleML2Prolog),
atom_concat(test_, NAtom, TestN),
strip_indentation(XML, XML1),
Term = [(Prolog2RuleML :- parse_ruleml(Assert, Query, XML0),
XML0 = XML1),
(RuleML2Prolog :- parse_ruleml(Assert0, Query0, XML),
Assert0 = Assert,
Query0 = Query),
(TestN :- write(test(N)), nl, Prolog2RuleML, RuleML2Prolog, !),
(TestN :- throw(error(test_failure, TestN)))].
test(1, test(1,
[people('Alex',male),people('Alex',female),people('Siri',female)], [people('Alex',male),people('Alex',female),people('Siri',female)],
[], [],
@@ -1385,11 +1385,7 @@ test(46,
%% Try a more substantial test case. %% Try a more substantial test case.
test(47, test(47,
[(mtow('$V'(kg)) :- aircraftType('$V'(type)), aircraftChar('$V'(type), '$V'(kg), '$V'(wingspan), '$V'(appSpeed))), [(runwayOneOrientation('$V'(runOneOr)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code))),
(mtowPreceding('$V'(kg)) :- precedingAircraftType('$V'(type)), aircraftChar('$V'(type), '$V'(kg), '$V'(wingspan), '$V'(appSpeed))),
(wingspan('$V'(feet)) :-aircraftType('$V'(type)), aircraftChar('$V'(type), '$V'(kg), '$V'(feet), '$V'(appSpeed))),
(wingspanPreceding('$V'(feet)) :- precedingAircraftType('$V'(type)), aircraftChar('$V'(type), '$V'(kg), '$V'(feet), '$V'(appSpeed))),
(runwayOneOrientation('$V'(runOneOr)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code))),
(runwayTwoOrientation('$V'(runTwoOr)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code))), (runwayTwoOrientation('$V'(runTwoOr)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code))),
(runwayThreeOrientation('$V'(runThreeOr)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code))), (runwayThreeOrientation('$V'(runThreeOr)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code))),
(runwayFourOrientation('$V'(runFourOr)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code))), (runwayFourOrientation('$V'(runFourOr)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code))),
@@ -1400,98 +1396,6 @@ test(47,
(distanceBetweenRunways('$V'(distanceBetweenRunways)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code)))], (distanceBetweenRunways('$V'(distanceBetweenRunways)) :- airportChar('$V'(code), '$V'(runOneOr), '$V'(runTwoOr), '$V'(runThreeOr), '$V'(runFourOr), '$V'(runOneName), '$V'(runTwoName), '$V'(runThreeName), '$V'(runFourName), '$V'(distanceBetweenRunways), '$V'(rules)), airportName('$V'(code)))],
[], [],
"<Assert mapClosure=\"universal\">\ "<Assert mapClosure=\"universal\">\
<Implies>\
<then>\
<Atom>\
<Rel>mtow</Rel>\
<Var>kg</Var>\
</Atom>\
</then>\
<if>\
<And>\
<Atom>\
<Rel>aircraftType</Rel>\
<Var>type</Var>\
</Atom>\
<Atom>\
<Rel>aircraftChar</Rel>\
<Var>type</Var>\
<Var>kg</Var>\
<Var>wingspan</Var>\
<Var>appSpeed</Var>\
</Atom>\
</And>\
</if>\
</Implies>\
<Implies>\
<then>\
<Atom>\
<Rel>mtowPreceding</Rel>\
<Var>kg</Var>\
</Atom>\
</then>\
<if>\
<And>\
<Atom>\
<Rel>precedingAircraftType</Rel>\
<Var>type</Var>\
</Atom>\
<Atom>\
<Rel>aircraftChar</Rel>\
<Var>type</Var>\
<Var>kg</Var>\
<Var>wingspan</Var>\
<Var>appSpeed</Var>\
</Atom>\
</And>\
</if>\
</Implies>\
<Implies>\
<then>\
<Atom>\
<Rel>wingspan</Rel>\
<Var>feet</Var>\
</Atom>\
</then>\
<if>\
<And>\
<Atom>\
<Rel>aircraftType</Rel>\
<Var>type</Var>\
</Atom>\
<Atom>\
<Rel>aircraftChar</Rel>\
<Var>type</Var>\
<Var>kg</Var>\
<Var>feet</Var>\
<Var>appSpeed</Var>\
</Atom>\
</And>\
</if>\
</Implies>\
<Implies>\
<then>\
<Atom>\
<Rel>wingspanPreceding</Rel>\
<Var>feet</Var>\
</Atom>\
</then>\
<if>\
<And>\
<Atom>\
<Rel>precedingAircraftType</Rel>\
<Var>type</Var>\
</Atom>\
<Atom>\
<Rel>aircraftChar</Rel>\
<Var>type</Var>\
<Var>kg</Var>\
<Var>feet</Var>\
<Var>appSpeed</Var>\
</Atom>\
</And>\
</if>\
</Implies>\
<Implies>\ <Implies>\
<then>\ <then>\
<Atom>\ <Atom>\
@@ -1765,14 +1669,9 @@ test(47,
</Assert>"). </Assert>").
test(48, test(48,
[(icaoCategory(light) :- mtow('$V'(kg)), lessThanOrEqual('$V'(kg), 7000.0)), [(icaoCategory('$V'(aircraft), light) :- aircraftChar(['$V'(aircraft), '$V'(kg) | '$V'(rest)]), lessThanOrEqual('$V'(kg), 7000.0)),
(icaoCategory(medium):- mtow('$V'(kg)), lessThan('$V'(kg), 136000.0), greaterThan('$V'(kg), 7000.0)), (icaoCategory('$V'(aircraft), medium):- aircraftChar(['$V'(aircraft), '$V'(kg) | '$V'(rest)]), lessThan('$V'(kg), 136000.0), greaterThan('$V'(kg), 7000.0)),
(icaoCategory(heavy) :- aircraftType('$V'(model)), notEqual('$V'(model),'A388'), notEqual('$V'(model),'A38F'), mtow('$V'(kg)), greaterThanOrEqual('$V'(kg), 136000.0)), (icaoCategory('$V'(aircraft), heavy) :- aircraftChar(['$V'(aircraft), '$V'(kg) | '$V'(rest)]), greaterThanOrEqual('$V'(kg), 136000.0), \+ icaoCategory('$V'(aircraft), super)),
(icaoCategory(super) :- aircraftType('A388')),
(icaoCategory(super) :- aircraftType('A38F')),
%(icaoCategory(super) :- aircraftType('A225')),
(icaoCategoryPreceding(light) :- mtowPreceding('$V'(kgPreceding)), lessThanOrEqual('$V'(kgPreceding), 7000.0)), (icaoCategoryPreceding(light) :- mtowPreceding('$V'(kgPreceding)), lessThanOrEqual('$V'(kgPreceding), 7000.0)),
(icaoCategoryPreceding(medium) :- mtowPreceding('$V'(kgPreceding)), lessThan('$V'(kgPreceding), 136000.0), greaterThan('$V'(kgPreceding), 7000.0)), (icaoCategoryPreceding(medium) :- mtowPreceding('$V'(kgPreceding)), lessThan('$V'(kgPreceding), 136000.0), greaterThan('$V'(kgPreceding), 7000.0)),
(icaoCategoryPreceding(heavy) :- precedingAircraftType('$V'(modelPreceding)), notEqual('$V'(modelPreceding),'A388'), notEqual('$V'(modelPreceding),'A38F'), notEqual('$V'(modelPreceding),'A225'), mtowPreceding('$V'(kgPreceding)), greaterThanOrEqual('$V'(kgPreceding), 136000.0)), (icaoCategoryPreceding(heavy) :- precedingAircraftType('$V'(modelPreceding)), notEqual('$V'(modelPreceding),'A388'), notEqual('$V'(modelPreceding),'A38F'), notEqual('$V'(modelPreceding),'A225'), mtowPreceding('$V'(kgPreceding)), greaterThanOrEqual('$V'(kgPreceding), 136000.0)),
@@ -1797,14 +1696,21 @@ test(48,
<then>\ <then>\
<Atom>\ <Atom>\
<Rel>icaoCategory</Rel>\ <Rel>icaoCategory</Rel>\
<Var>aircraft</Var>\
<Data iso:type=\"symbol\">light</Data>\ <Data iso:type=\"symbol\">light</Data>\
</Atom>\ </Atom>\
</then>\ </then>\
<if>\ <if>\
<And>\ <And>\
<Atom>\ <Atom>\
<Rel>mtow</Rel>\ <Rel>aircraftChar</Rel>\
<Plex>\
<Var>aircraft</Var>\
<Var>kg</Var>\ <Var>kg</Var>\
<repo>\
<Var>rest</Var>\
</repo>\
</Plex>\
</Atom>\ </Atom>\
<Atom>\ <Atom>\
<Rel>lessThanOrEqual</Rel>\ <Rel>lessThanOrEqual</Rel>\
@@ -1818,14 +1724,21 @@ test(48,
<then>\ <then>\
<Atom>\ <Atom>\
<Rel>icaoCategory</Rel>\ <Rel>icaoCategory</Rel>\
<Var>aircraft</Var>\
<Data iso:type=\"symbol\">medium</Data>\ <Data iso:type=\"symbol\">medium</Data>\
</Atom>\ </Atom>\
</then>\ </then>\
<if>\ <if>\
<And>\ <And>\
<Atom>\ <Atom>\
<Rel>mtow</Rel>\ <Rel>aircraftChar</Rel>\
<Plex>\
<Var>aircraft</Var>\
<Var>kg</Var>\ <Var>kg</Var>\
<repo>\
<Var>rest</Var>\
</repo>\
</Plex>\
</Atom>\ </Atom>\
<Atom>\ <Atom>\
<Rel>lessThan</Rel>\ <Rel>lessThan</Rel>\
@@ -1844,65 +1757,37 @@ test(48,
<then>\ <then>\
<Atom>\ <Atom>\
<Rel>icaoCategory</Rel>\ <Rel>icaoCategory</Rel>\
<Var>aircraft</Var>\
<Data iso:type=\"symbol\">heavy</Data>\ <Data iso:type=\"symbol\">heavy</Data>\
</Atom>\ </Atom>\
</then>\ </then>\
<if>\ <if>\
<And>\ <And>\
<Atom>\ <Atom>\
<Rel>aircraftType</Rel>\ <Rel>aircraftChar</Rel>\
<Var>model</Var>\ <Plex>\
</Atom>\ <Var>aircraft</Var>\
<Atom>\
<Rel>notEqual</Rel>\
<Var>model</Var>\
<Ind>A388</Ind>\
</Atom>\
<Atom>\
<Rel>notEqual</Rel>\
<Var>model</Var>\
<Ind>A38F</Ind>\
</Atom>\
<Atom>\
<Rel>mtow</Rel>\
<Var>kg</Var>\ <Var>kg</Var>\
<repo>\
<Var>rest</Var>\
</repo>\
</Plex>\
</Atom>\ </Atom>\
<Atom>\ <Atom>\
<Rel>greaterThanOrEqual</Rel>\ <Rel>greaterThanOrEqual</Rel>\
<Var>kg</Var>\ <Var>kg</Var>\
<Data iso:type=\"number\">136000.0</Data>\ <Data iso:type=\"number\">136000.0</Data>\
</Atom>\ </Atom>\
<Naf>\
<Atom>\
<Rel>icaoCategory</Rel>\
<Var>aircraft</Var>\
<Data iso:type=\"symbol\">super</Data>\
</Atom>\
</Naf>\
</And>\ </And>\
</if>\ </if>\
</Implies>\ </Implies>\
<Implies>\
<then>\
<Atom>\
<Rel>icaoCategory</Rel>\
<Data iso:type=\"symbol\">super</Data>\
</Atom>\
</then>\
<if>\
<Atom>\
<Rel>aircraftType</Rel>\
<Ind>A388</Ind>\
</Atom>\
</if>\
</Implies>\
<Implies>\
<then>\
<Atom>\
<Rel>icaoCategory</Rel>\
<Data iso:type=\"symbol\">super</Data>\
</Atom>\
</then>\
<if>\
<Atom>\
<Rel>aircraftType</Rel>\
<Ind>A38F</Ind>\
</Atom>\
</if>\
</Implies>\
<Implies>\ <Implies>\
<then>\ <then>\
<Atom>\ <Atom>\
@@ -4114,7 +3999,7 @@ test(53,
</Assert>"). </Assert>").
test_bimetatrans_ruleml :- test_bimetatrans :-
catch((test_1, catch((test_1,
test_2, test_2,
test_3, test_3,
@@ -4172,3 +4057,5 @@ test_bimetatrans_ruleml :-
(writeq(E), (writeq(E),
nl, nl,
false)). false)).
:- initialization(test_bimetatrans).

View File

@@ -16,15 +16,10 @@
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
:- use_module(library(format)). :- use_module(library(format)).
:- use_module(library(lists)).
:- use_module(library(reif)). :- use_module(library(reif)).
permutation([], []).
permutation([X|Xs], Ys) :-
permutation(Xs, Yss),
select(X, Ys, Yss).
valid_time([H1,H2,M1,M2], T) :- valid_time([H1,H2,M1,M2], T) :-
memberd_t(H1, [0,1,2], TH1), memberd_t(H1, [0,1,2], TH1),
memberd_t(H2, [0,1,2,3,4,5,6,7,8,9], TH2), memberd_t(H2, [0,1,2,3,4,5,6,7,8,9], TH2),
@@ -49,15 +44,7 @@ permuted_times(Time, PermutedTimes) :-
find_min_time(Time, Min) :- find_min_time(Time, Min) :-
valid_time(Time, true), valid_time(Time, true),
permuted_times(Time, PermutedTimes), permuted_times(Time, [Min|_]).
find_min_time_(PermutedTimes, Time, Min).
find_min_time_([], Min, Min).
find_min_time_([Time|Times], MinSoFar, Min) :-
( Time @< MinSoFar ->
find_min_time_(Times, Time, Min)
; find_min_time_(Times, MinSoFar, Min)
).
write_time_nl(Time) :- write_time_nl(Time) :-

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

444
src/ffi.rs Normal file
View File

@@ -0,0 +1,444 @@
/* How does FFI work?
Each WAM machine has a ForeignFunctionTable instance that contains a table of functions and structs.
Structs are defined via foreign_struct/2. Basic types are defined by libffi, but struct types need to
be manually defined to get an ffi_type. Additionally, to recover structs from return arguments, we store
fields and atom_fields, as a way to lookup the content of the struct (fields) and the nested structs (atom_fields).
Functions are defined via use_foreign_module/2. It opens a library and leaks the memory of the library,
to prevent Rust freeing the memory. There's no way to recover that memory at the moment. We get a pointer for
each function and we build a CIF for each one, with the input arguments and the return argument.
Exec happens via '$foreign_call', we find the function, we try to cast the values that we have to the definition
of the function, we reserve memory for them and we build an array of pointers. To get the return argument, we
reserve enough memory for the return and we build the Scryer values from them.
Structs are a bit tricky as they need to be aligned. For that, we reserve enough memory (libffi calculates that)
and for each field: we add to the pointer until we're aligned to the next data type we're going to write, we write it,
and finally we add the pointer the size of what we've written.
*/
use crate::atom_table::Atom;
use std::alloc::{alloc, Layout};
use std::any::Any;
use std::collections::HashMap;
use std::error::Error;
use std::ffi::{CString, c_void};
use std::convert::TryFrom;
use libffi::low::{ffi_cif, types, CodePtr, ffi_abi_FFI_DEFAULT_ABI, prep_cif, ffi_type, type_tag};
use libloading::{Symbol, Library};
pub struct FunctionDefinition {
pub name: String,
pub return_value: Atom,
pub args: Vec<Atom>,
}
#[derive(Debug)]
pub struct FunctionImpl {
cif: ffi_cif,
args: Vec<*mut ffi_type>,
code_ptr: CodePtr,
return_struct_name: Option<String>,
}
#[derive(Debug, Default)]
pub struct ForeignFunctionTable {
table: HashMap<String, FunctionImpl>,
structs: HashMap<String, StructImpl>,
}
#[derive(Debug, Clone)]
struct StructImpl {
ffi_type: ffi_type,
fields: Vec<*mut ffi_type>,
atom_fields: Vec<Atom>,
}
struct PointerArgs {
pointers: Vec<*mut c_void>,
_memory: Vec<Box<dyn Any>>,
}
impl ForeignFunctionTable {
pub fn merge(&mut self, other: ForeignFunctionTable) {
self.table.extend(other.table);
}
pub fn define_struct(&mut self, name: &str, atom_fields: Vec<Atom>) {
let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(&x)).collect();
fields.push(std::ptr::null_mut::<ffi_type>());
let mut struct_type: ffi_type = Default::default();
struct_type.type_ = type_tag::STRUCT;
struct_type.elements = fields.as_mut_ptr();
self.structs.insert(name.to_string(), StructImpl { ffi_type: struct_type, fields, atom_fields});
}
fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type {
unsafe {
match source {
atom!("sint64") => &mut types::sint64,
atom!("sint32") => &mut types::sint32,
atom!("sint16") => &mut types::sint16,
atom!("sint8") => &mut types::sint8,
atom!("uint64") => &mut types::uint64,
atom!("uint32") => &mut types::uint32,
atom!("uint16") => &mut types::uint16,
atom!("uint8") => &mut types::uint8,
atom!("bool") => &mut types::sint8,
atom!("void") => &mut types::void,
atom!("cstr") => &mut types::pointer,
atom!("ptr") => &mut types::pointer,
atom!("f32") => &mut types::float,
atom!("f64") => &mut types::double,
struct_name => {
match self.structs.get_mut(struct_name.as_str()) {
Some(ref mut struct_type) => {
&mut struct_type.ffi_type
},
None => unreachable!()
}
}
}
}
}
pub(crate) fn load_library(&mut self, library_name: &str, functions: &Vec<FunctionDefinition>) -> Result<(), Box<dyn Error>> {
let mut ff_table: ForeignFunctionTable = Default::default();
unsafe {
let library = Library::new(library_name)?;
for function in functions {
let symbol_name: CString = CString::new(function.name.clone())?;
let code_ptr: Symbol<*mut c_void> = library.get(&symbol_name.into_bytes_with_nul())?;
let mut args: Vec<_> = function.args.iter().map(|x| self.map_type_ffi(&x)).collect();
let mut cif: ffi_cif = Default::default();
prep_cif(
&mut cif,
ffi_abi_FFI_DEFAULT_ABI,
args.len(),
self.map_type_ffi(&function.return_value),
args.as_mut_ptr()
).unwrap();
let return_struct_name = if (*self.map_type_ffi(&function.return_value)).type_ as u32 == libffi::raw::FFI_TYPE_STRUCT {
Some(function.return_value.as_str().to_string())
} else {
None
};
ff_table.table.insert(function.name.clone(), FunctionImpl {
cif,
args,
code_ptr: CodePtr(code_ptr.into_raw().into_raw() as *mut _),
return_struct_name,
});
}
std::mem::forget(library);
}
self.merge(ff_table);
Ok(())
}
fn build_pointer_args(args: &mut Vec<Value>, type_args: &Vec<*mut ffi_type>, structs_table: &mut HashMap<String, StructImpl>) -> Result<PointerArgs, FFIError> {
let mut pointers = Vec::with_capacity(args.len());
let mut _memory = Vec::new();
for i in 0..args.len() {
let field_type = type_args[i];
unsafe {
macro_rules! push_int {
($type:ty) => {
{
let n: $type = <$type>::try_from(args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?;
let mut box_value = Box::new(n) as Box<dyn Any>;
pointers.push(&mut *box_value as *mut _ as *mut c_void);
_memory.push(box_value);
}
}
}
match (*field_type).type_ as u32 {
libffi::raw::FFI_TYPE_UINT8 => push_int!(u8),
libffi::raw::FFI_TYPE_SINT8 => push_int!(i8),
libffi::raw::FFI_TYPE_UINT16 => push_int!(u16),
libffi::raw::FFI_TYPE_SINT16 => push_int!(i16),
libffi::raw::FFI_TYPE_UINT32 => push_int!(u32),
libffi::raw::FFI_TYPE_SINT32 => push_int!(i32),
libffi::raw::FFI_TYPE_UINT64 => push_int!(u64),
libffi::raw::FFI_TYPE_SINT64 => push_int!(i64),
libffi::raw::FFI_TYPE_FLOAT => {
let n: f32 = args[i].as_float()? as f32;
let mut box_value = Box::new(n) as Box<dyn Any>;
pointers.push(&mut *box_value as *mut _ as *mut c_void);
_memory.push(box_value);
},
libffi::raw::FFI_TYPE_DOUBLE => {
let n: f64 = args[i].as_float()?;
let mut box_value = Box::new(n) as Box<dyn Any>;
pointers.push(&mut *box_value as *mut _ as *mut c_void);
_memory.push(box_value);
},
libffi::raw::FFI_TYPE_POINTER => {
let ptr: *mut c_void = args[i].as_ptr()?;
pointers.push(ptr);
},
libffi::raw::FFI_TYPE_STRUCT => {
let (mut ptr, _size, _align) = Self::build_struct(&mut args[i], structs_table)?;
pointers.push(&mut *ptr as *mut _ as *mut c_void);
_memory.push(ptr);
},
_ => return Err(FFIError::InvalidFFIType)
}
}
}
Ok(PointerArgs {
pointers,
_memory
})
}
fn build_struct(arg: &mut Value, structs_table: &mut HashMap<String, StructImpl>) -> Result<(Box<dyn Any>, usize, usize), FFIError> {
unsafe {
match arg {
Value::Struct(ref name, ref mut struct_args) => {
if let Some(ref mut struct_type) = structs_table.clone().get_mut(name) {
let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap();
let align = struct_type.ffi_type.alignment as usize;
let size = struct_type.ffi_type.size;
let ptr = alloc(layout) as *mut c_void;
let mut field_ptr = ptr;
for i in 0..(struct_type.fields.len()-1) {
macro_rules! try_write_int {
($type:ty) => {
{
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
let n: $type = <$type>::try_from(struct_args[i].as_int()?).map_err(|_| FFIError::ValueDontFit)?;
std::ptr::write(field_ptr as *mut $type, n);
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
}
}
}
macro_rules! write {
($type:ty, $value:expr) => {
{
let data: $type = $value;
std::ptr::write(field_ptr as *mut $type, data);
field_ptr = field_ptr.add(align);
}
}
}
let field = struct_type.fields[i];
match (*field).type_ as u32 {
libffi::raw::FFI_TYPE_UINT8 => try_write_int!(u8),
libffi::raw::FFI_TYPE_SINT8 => try_write_int!(i8),
libffi::raw::FFI_TYPE_UINT16 => try_write_int!(u16),
libffi::raw::FFI_TYPE_SINT16 => try_write_int!(i16),
libffi::raw::FFI_TYPE_UINT32 => try_write_int!(u32),
libffi::raw::FFI_TYPE_SINT32 => try_write_int!(i32),
libffi::raw::FFI_TYPE_UINT64 => try_write_int!(u64),
libffi::raw::FFI_TYPE_SINT64 => try_write_int!(i64),
libffi::raw::FFI_TYPE_POINTER => write!(*mut c_void, struct_args[i].as_ptr()?),
libffi::raw::FFI_TYPE_FLOAT => write!(f32, struct_args[i].as_float()? as f32),
libffi::raw::FFI_TYPE_DOUBLE => write!(f64, struct_args[i].as_float()?),
libffi::raw::FFI_TYPE_STRUCT => {
let (struct_ptr, struct_size, struct_align) = Self::build_struct(&mut struct_args[i], structs_table)?;
field_ptr = field_ptr.add(field_ptr.align_offset(struct_align));
std::ptr::copy(& *struct_ptr as *const _ as *const c_void, field_ptr as *mut c_void, struct_size);
field_ptr = field_ptr.add(struct_size);
},
_ => {
unreachable!()
}
}
}
return Ok((Box::from_raw(ptr), size, align));
} else {
return Err(FFIError::InvalidStructName);
}
}
_ => return Err(FFIError::ValueCast)
}
}
}
pub fn exec(&mut self, name: &str, mut args: Vec<Value>) -> Result<Value, FFIError> {
let function_impl = self.table.get_mut(name).ok_or(FFIError::FunctionNotFound)?;
let mut pointer_args = Self::build_pointer_args(&mut args, &function_impl.args, &mut self.structs)?;
return unsafe {
macro_rules! call_and_return {
($type:ty) => {
{
let mut n: Box<u8> = Box::new(0);
libffi::raw::ffi_call(
&mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()),
&mut *n as *mut _ as *mut c_void,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
);
Ok(Value::Int(i64::from(*n)))
}
}
}
match (*function_impl.cif.rtype).type_ as u32 {
libffi::raw::FFI_TYPE_VOID => call_and_return!(i32),
libffi::raw::FFI_TYPE_UINT8 => call_and_return!(u8),
libffi::raw::FFI_TYPE_SINT8 => call_and_return!(i8),
libffi::raw::FFI_TYPE_UINT16 => call_and_return!(u16),
libffi::raw::FFI_TYPE_SINT16 => call_and_return!(i16),
libffi::raw::FFI_TYPE_UINT32 => call_and_return!(u32),
libffi::raw::FFI_TYPE_SINT32 => call_and_return!(i32),
libffi::raw::FFI_TYPE_UINT64 => {
let mut n: Box<u64> = Box::new(0);
libffi::raw::ffi_call(
&mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()),
&mut *n as *mut _ as *mut c_void,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
);
Ok(Value::Int(i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?))
},
libffi::raw::FFI_TYPE_SINT64 => call_and_return!(i64),
libffi::raw::FFI_TYPE_POINTER => call_and_return!(*mut c_void),
libffi::raw::FFI_TYPE_FLOAT => {
let mut n: Box<f32> = Box::new(0.0);
libffi::raw::ffi_call(
&mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()),
&mut *n as *mut _ as *mut c_void,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
);
Ok(Value::Float((*n).into()))
},
libffi::raw::FFI_TYPE_DOUBLE => {
let mut n: Box<f64> = Box::new(0.0);
libffi::raw::ffi_call(
&mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()),
&mut *n as *mut _ as *mut c_void,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
);
Ok(Value::Float(*n))
},
libffi::raw::FFI_TYPE_STRUCT => {
let name = &function_impl.return_struct_name.clone().ok_or(FFIError::StructNotFound)?;
let struct_type = self.structs.get(name).ok_or(FFIError::StructNotFound)?;
let layout = Layout::from_size_align(struct_type.ffi_type.size, struct_type.ffi_type.alignment.into()).unwrap();
let ptr = alloc(layout) as *mut c_void;
libffi::raw::ffi_call(
&mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()),
&mut *ptr as *mut _ as *mut c_void,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void
);
let struct_val = self.read_struct(ptr, name, struct_type);
drop(Box::from_raw(ptr));
struct_val
}
_ => unreachable!()
}
};
}
fn read_struct(&self, ptr: *mut c_void, name: &str, struct_type: &StructImpl) -> Result<Value, FFIError> {
unsafe {
let mut returns = Vec::new();
let mut field_ptr = ptr;
for i in 0..(struct_type.fields.len()-1) {
let field = struct_type.fields[i];
macro_rules! read_and_push_int {
($type:ty) => {
{
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<$type>()));
let n = std::ptr::read(field_ptr as *mut $type);
returns.push(Value::Int(i64::from(n)));
field_ptr = field_ptr.add(std::mem::size_of::<$type>());
}
}
}
match (*field).type_ as u32 {
libffi::raw::FFI_TYPE_UINT8 => read_and_push_int!(u8),
libffi::raw::FFI_TYPE_SINT8 => read_and_push_int!(i8),
libffi::raw::FFI_TYPE_UINT16 => read_and_push_int!(u16),
libffi::raw::FFI_TYPE_SINT16 => read_and_push_int!(i16),
libffi::raw::FFI_TYPE_UINT32 => read_and_push_int!(u32),
libffi::raw::FFI_TYPE_SINT32 => read_and_push_int!(i32),
libffi::raw::FFI_TYPE_UINT64 => {
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<u64>()));
let n = std::ptr::read(field_ptr as *mut u64);
returns.push(Value::Int(i64::try_from(n).map_err(|_| FFIError::ValueDontFit)?));
field_ptr = field_ptr.add(std::mem::size_of::<u64>());
},
libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64),
libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64),
libffi::raw::FFI_TYPE_STRUCT => {
let substruct = struct_type.atom_fields[i].as_str();
let struct_type = self.structs.get(substruct).ok_or(FFIError::StructNotFound)?;
field_ptr = field_ptr.add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize));
let struct_val = self.read_struct(field_ptr, substruct, struct_type);
returns.push(struct_val?);
field_ptr = field_ptr.add(struct_type.ffi_type.size);
},
_ => {
unreachable!()
}
}
}
Ok(Value::Struct(name.into(), returns))
}
}
}
#[derive(Clone, Debug)]
pub enum Value {
Int(i64),
Float(f64),
CString(CString),
Struct(String, Vec<Value>),
}
impl Value {
fn as_int(&self) -> Result<i64, FFIError> {
match self {
Value::Int(n) => Ok(*n),
_ => Err(FFIError::ValueCast),
}
}
fn as_float(&self) -> Result<f64, FFIError> {
match self {
Value::Float(n) => Ok(*n),
Value::Int(n) => Ok(*n as f64),
_ => Err(FFIError::ValueCast),
}
}
fn as_ptr(&mut self) -> Result<*mut c_void, FFIError> {
match self {
Value::CString(ref mut cstr) => Ok(&mut *cstr as *mut _ as *mut c_void),
Value::Int(n) => Ok(*n as *mut c_void),
_ => Err(FFIError::ValueCast)
}
}
}
#[derive(Debug)]
pub enum FFIError {
ValueCast,
ValueDontFit,
InvalidFFIType,
InvalidStructName,
FunctionNotFound,
StructNotFound,
}

1031
src/forms.rs Normal file

File diff suppressed because it is too large Load Diff

2919
src/heap_iter.rs Normal file

File diff suppressed because it is too large Load Diff

2034
src/heap_print.rs Normal file

File diff suppressed because it is too large Load Diff

54
src/http.rs Normal file
View File

@@ -0,0 +1,54 @@
use std::sync::{Arc, Mutex, Condvar};
use std::future::Future;
use std::pin::Pin;
use http_body_util::Full;
use bytes::Bytes;
use hyper::service::Service;
use hyper::{body::Incoming as IncomingBody, Request, Response};
pub struct HttpListener {
pub incoming: std::sync::mpsc::Receiver<HttpRequest>
}
#[derive(Debug)]
pub struct HttpRequest {
pub request: Request<IncomingBody>,
pub response: HttpResponse,
}
pub type HttpResponse = Arc<(Mutex<bool>, Mutex<Option<Response<Full<Bytes>>>>, Condvar)>;
pub struct HttpService {
pub tx: std::sync::mpsc::SyncSender<HttpRequest>,
}
impl Service<Request<IncomingBody>> for HttpService {
type Response = Response<Full<Bytes>>;
type Error = hyper::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn call(&mut self, req: Request<IncomingBody>) -> Self::Future {
// new connection!
// we send the Request info to Prolog
let response = Arc::new((Mutex::new(false), Mutex::new(None), Condvar::new()));
let http_request = HttpRequest { request: req, response: Arc::clone(&response) };
self.tx.send(http_request).unwrap();
// we wait for the Response info from Prolog
{
let (ready, _response, cvar) = &*response;
let mut ready = ready.lock().unwrap();
while !*ready {
ready = cvar.wait(ready).unwrap();
}
}
{
let (_, response, _) = &*response;
let response = response.lock().unwrap().take();
let res = response.expect("Data race error in HTTP Server");
Box::pin(async move {
Ok(res)
})
}
}
}

1558
src/indexing.rs Normal file

File diff suppressed because it is too large Load Diff

448
src/iterators.rs Normal file
View File

@@ -0,0 +1,448 @@
use crate::atom_table::*;
use crate::forms::*;
use crate::instructions::*;
use crate::parser::ast::*;
use std::cell::Cell;
use std::collections::VecDeque;
use std::iter::*;
use std::vec::Vec;
#[derive(Debug, Clone)]
pub(crate) enum TermRef<'a> {
AnonVar(Level),
Cons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
Literal(Level, &'a Cell<RegType>, &'a Literal),
Clause(Level, &'a Cell<RegType>, Atom, &'a Vec<Term>),
PartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
CompleteString(Level, &'a Cell<RegType>, Atom),
Var(Level, &'a Cell<VarReg>, VarPtr),
}
/*
impl<'a> TermRef<'a> {
pub(crate) fn level(&self) -> Level {
match self {
TermRef::AnonVar(lvl) |
TermRef::Cons(lvl, ..) |
TermRef::Literal(lvl, ..) |
TermRef::Var(lvl, ..) |
TermRef::Clause(lvl, ..) |
TermRef::CompleteString(lvl, ..) |
TermRef::PartialString(lvl, ..) => *lvl,
}
}
}
*/
#[derive(Debug)]
pub(crate) enum TermIterState<'a> {
AnonVar(Level),
Clause(Level, usize, &'a Cell<RegType>, Atom, &'a Vec<Term>),
Literal(Level, &'a Cell<RegType>, &'a Literal),
InitialCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
FinalCons(Level, &'a Cell<RegType>, &'a Term, &'a Term),
InitialPartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
FinalPartialString(Level, &'a Cell<RegType>, &'a String, &'a Box<Term>),
CompleteString(Level, &'a Cell<RegType>, Atom),
Var(Level, &'a Cell<VarReg>, VarPtr),
}
impl<'a> TermIterState<'a> {
pub(crate) fn subterm_to_state(lvl: Level, term: &'a Term) -> TermIterState<'a> {
match term {
Term::AnonVar => TermIterState::AnonVar(lvl),
Term::Clause(cell, name, subterms) => {
TermIterState::Clause(lvl, 0, cell, *name, subterms)
}
Term::Cons(cell, head, tail) => {
TermIterState::InitialCons(lvl, cell, head.as_ref(), tail.as_ref())
}
Term::Literal(cell, constant) => TermIterState::Literal(lvl, cell, constant),
Term::PartialString(cell, string_buf, tail) => {
TermIterState::InitialPartialString(lvl, cell, string_buf, tail)
}
Term::CompleteString(cell, atom) => {
TermIterState::CompleteString(lvl, cell, *atom)
}
Term::Var(cell, var_ptr) => TermIterState::Var(lvl, cell, var_ptr.clone()),
}
}
}
#[derive(Debug)]
pub(crate) struct QueryIterator<'a> {
state_stack: Vec<TermIterState<'a>>,
}
impl<'a> QueryIterator<'a> {
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
self.state_stack.push(TermIterState::subterm_to_state(lvl, term));
}
/*
fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
let state_stack = terms
.iter()
.rev()
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
.collect();
QueryIterator { state_stack }
}
*/
fn from_term(term: &'a Term) -> Self {
let state = match term {
Term::AnonVar | Term::Cons(..) | Term::Literal(..) |
Term::PartialString(..) | Term::CompleteString(..) => {
return QueryIterator {
state_stack: vec![],
}
}
Term::Clause(r, name, terms) => TermIterState::Clause(
Level::Root,
0,
r,
*name,
terms,
),
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Root, cell, var_ptr.clone()),
};
QueryIterator {
state_stack: vec![state],
}
}
fn extend_state(&mut self, lvl: Level, term: &'a QueryTerm) {
match term {
&QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => {
self.state_stack.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms));
}
&QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
self.state_stack.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms));
}
_ => {
}
}
}
pub fn new(term: &'a QueryTerm) -> Self {
let mut iter = QueryIterator { state_stack: vec![] };
iter.extend_state(Level::Root, term);
iter
}
}
impl<'a> Iterator for QueryIterator<'a> {
type Item = TermRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(iter_state) = self.state_stack.pop() {
match iter_state {
TermIterState::AnonVar(lvl) => {
return Some(TermRef::AnonVar(lvl));
}
TermIterState::Clause(lvl, child_num, cell, name, child_terms) => {
if child_num == child_terms.len() {
match name {
atom!("$call") if lvl == Level::Root => {
self.push_subterm(Level::Shallow, &child_terms[0]);
}
_ => {
return match lvl {
Level::Root => None,
lvl => Some(TermRef::Clause(lvl, cell, name, child_terms)),
}
}
};
} else {
self.state_stack.push(TermIterState::Clause(
lvl,
child_num + 1,
cell,
name,
child_terms,
));
self.push_subterm(lvl.child_level(), &child_terms[child_num]);
}
}
TermIterState::InitialCons(lvl, cell, head, tail) => {
self.state_stack.push(TermIterState::FinalCons(lvl, cell, head, tail));
self.push_subterm(lvl.child_level(), tail);
self.push_subterm(lvl.child_level(), head);
}
TermIterState::InitialPartialString(lvl, cell, string, tail) => {
self.state_stack.push(TermIterState::FinalPartialString(lvl, cell, string, tail));
self.push_subterm(lvl.child_level(), tail);
}
TermIterState::FinalPartialString(lvl, cell, atom, tail) => {
return Some(TermRef::PartialString(lvl, cell, atom, tail));
}
TermIterState::CompleteString(lvl, cell, atom) => {
return Some(TermRef::CompleteString(lvl, cell, atom));
}
TermIterState::FinalCons(lvl, cell, head, tail) => {
return Some(TermRef::Cons(lvl, cell, head, tail));
}
TermIterState::Literal(lvl, cell, constant) => {
return Some(TermRef::Literal(lvl, cell, constant));
}
TermIterState::Var(lvl, cell, var_ptr) => {
return Some(TermRef::Var(lvl, cell, var_ptr));
}
};
}
None
}
}
#[derive(Debug)]
pub(crate) struct FactIterator<'a> {
state_queue: VecDeque<TermIterState<'a>>,
iterable_root: RootIterationPolicy,
}
impl<'a> FactIterator<'a> {
fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
self.state_queue
.push_back(TermIterState::subterm_to_state(lvl, term));
}
pub(crate) fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self {
let state_queue = terms
.iter()
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
.collect();
FactIterator {
state_queue,
iterable_root: RootIterationPolicy::NotIterated,
}
}
fn new(term: &'a Term, iterable_root: RootIterationPolicy) -> Self {
let states = match term {
Term::AnonVar => {
vec![TermIterState::AnonVar(Level::Root)]
}
Term::Clause(cell, name, terms) => {
vec![TermIterState::Clause(Level::Root, 0, cell, *name, terms)]
}
Term::Cons(cell, head, tail) => vec![TermIterState::InitialCons(
Level::Root,
cell,
head.as_ref(),
tail.as_ref(),
)],
Term::PartialString(cell, string_buf, tail) => {
vec![TermIterState::InitialPartialString(
Level::Root,
cell,
string_buf,
tail,
)]
}
Term::CompleteString(cell, atom) => {
vec![TermIterState::CompleteString(
Level::Root,
cell,
*atom,
)]
}
Term::Literal(cell, constant) => {
vec![TermIterState::Literal(Level::Root, cell, constant)]
}
Term::Var(cell, var_ptr) => {
vec![TermIterState::Var(Level::Root, cell, var_ptr.clone())]
}
};
FactIterator {
state_queue: VecDeque::from(states),
iterable_root,
}
}
}
impl<'a> Iterator for FactIterator<'a> {
type Item = TermRef<'a>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(state) = self.state_queue.pop_front() {
match state {
TermIterState::AnonVar(lvl) => {
return Some(TermRef::AnonVar(lvl));
}
TermIterState::Clause(lvl, _, cell, name, child_terms) => {
for child_term in child_terms {
self.push_subterm(lvl.child_level(), child_term);
}
match lvl {
Level::Root if !self.iterable_root.iterable() => continue,
_ => return Some(TermRef::Clause(lvl, cell, name, child_terms)),
};
}
TermIterState::InitialCons(lvl, cell, head, tail) => {
self.push_subterm(Level::Deep, head);
self.push_subterm(Level::Deep, tail);
return Some(TermRef::Cons(lvl, cell, head, tail));
}
TermIterState::InitialPartialString(lvl, cell, string_buf, tail) => {
self.push_subterm(Level::Deep, tail);
return Some(TermRef::PartialString(lvl, cell, string_buf, tail));
}
TermIterState::CompleteString(lvl, cell, atom) => {
return Some(TermRef::CompleteString(lvl, cell, atom));
}
TermIterState::Literal(lvl, cell, constant) => {
return Some(TermRef::Literal(lvl, cell, constant))
}
TermIterState::Var(lvl, cell, var_ptr) => {
return Some(TermRef::Var(lvl, cell, var_ptr));
}
_ => {}
}
}
None
}
}
pub(crate) fn post_order_iter<'a>(term: &'a Term) -> QueryIterator<'a> {
QueryIterator::from_term(term)
}
pub(crate) fn breadth_first_iter<'a>(term: &'a Term, iterable_root: RootIterationPolicy) -> FactIterator<'a> {
FactIterator::new(term, iterable_root)
}
#[derive(Debug, Copy, Clone)]
enum ClauseIteratorState<'a> {
RemainingChunks(&'a VecDeque<ChunkedTerms>, usize),
RemainingBranches(&'a Vec<VecDeque<ChunkedTerms>>, usize),
}
#[derive(Debug, Clone)]
pub(crate) enum ClauseItem<'a> {
FirstBranch(usize),
NextBranch,
BranchEnd(usize),
Chunk(&'a VecDeque<QueryTerm>),
}
#[derive(Debug)]
pub(crate) struct ClauseIterator<'a> {
state_stack: Vec<ClauseIteratorState<'a>>,
remaining_chunks_on_stack: usize,
}
fn state_from_chunked_terms<'a>(chunk_vec: &'a VecDeque<ChunkedTerms>) -> ClauseIteratorState<'a> {
if chunk_vec.len() == 1 {
if let Some(ChunkedTerms::Branch(ref branches)) = chunk_vec.front() {
return ClauseIteratorState::RemainingBranches(branches, 0);
}
}
ClauseIteratorState::RemainingChunks(chunk_vec, 0)
}
impl<'a> ClauseIterator<'a> {
pub fn new(clauses: &'a ChunkedTermVec) -> Self {
match state_from_chunked_terms(&clauses.chunk_vec) {
state @ ClauseIteratorState::RemainingBranches(..) => {
Self {
state_stack: vec![state],
remaining_chunks_on_stack: 0,
}
}
state @ ClauseIteratorState::RemainingChunks(..) => {
Self {
state_stack: vec![state],
remaining_chunks_on_stack: 1,
}
}
}
}
#[inline(always)]
pub fn in_tail_position(&self) -> bool {
self.remaining_chunks_on_stack == 0
}
fn branch_end_depth(&mut self) -> usize {
let mut depth = 1;
while let Some(state) = self.state_stack.pop() {
match state {
ClauseIteratorState::RemainingBranches(terms, focus) if terms.len() == focus => {
depth += 1;
}
_ => {
self.state_stack.push(state);
break;
}
}
}
depth
}
}
impl<'a> Iterator for ClauseIterator<'a> {
type Item = ClauseItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(state) = self.state_stack.pop() {
match state {
ClauseIteratorState::RemainingChunks(chunks, focus) if focus < chunks.len() => {
if focus + 1 < chunks.len() {
self.state_stack.push(ClauseIteratorState::RemainingChunks(chunks, focus + 1));
} else {
self.remaining_chunks_on_stack -= 1;
}
match &chunks[focus] {
ChunkedTerms::Branch(branches) => {
self.state_stack.push(ClauseIteratorState::RemainingBranches(branches, 0));
}
ChunkedTerms::Chunk(chunk) => {
return Some(ClauseItem::Chunk(chunk));
}
}
}
ClauseIteratorState::RemainingChunks(chunks, focus) => {
debug_assert_eq!(chunks.len(), focus);
}
ClauseIteratorState::RemainingBranches(branches, focus) if focus < branches.len() => {
self.state_stack.push(ClauseIteratorState::RemainingBranches(&branches, focus + 1));
let state = state_from_chunked_terms(&branches[focus]);
if let ClauseIteratorState::RemainingChunks(..) = &state {
self.remaining_chunks_on_stack += 1;
}
self.state_stack.push(state);
return if focus == 0 {
Some(ClauseItem::FirstBranch(branches.len()))
} else {
Some(ClauseItem::NextBranch)
};
}
ClauseIteratorState::RemainingBranches(branches, focus) => {
debug_assert_eq!(branches.len(), focus);
return Some(ClauseItem::BranchEnd(self.branch_end_depth()));
}
}
}
None
}
}

40
src/lib.rs Normal file
View File

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

View File

@@ -1,11 +1,20 @@
:- module(arithmetic, [expmod/4, lsb/2, msb/2, number_to_rational/2, /** Arithmetic predicates
number_to_rational/3,
These predicates are additions to standard the arithmetic functions provided by `is/2`.
*/
:- module(arithmetic, [expmod/4, lcm/3, lsb/2, msb/2, number_to_rational/2,
number_to_rational/3, popcount/2,
rational_numerator_denominator/3]). rational_numerator_denominator/3]).
:- use_module(library(charsio), [write_term_to_chars/3]). :- use_module(library(charsio), [write_term_to_chars/3]).
:- use_module(library(error)). :- use_module(library(error)).
:- use_module(library(lists), [append/3, member/2]). :- use_module(library(lists), [append/3, member/2]).
%% expmod(+Base, +Expo, +Mod, -R).
%
% Modular exponentiation. Base, Expo and Mod must be integers.
expmod(Base, Expo, Mod, R) :- expmod(Base, Expo, Mod, R) :-
( member(N, [Base, Expo, Mod]), var(N) -> instantiation_error(expmod/4) ( member(N, [Base, Expo, Mod]), var(N) -> instantiation_error(expmod/4)
; member(N, [Base, Expo, Mod]), \+ integer(N) -> ; member(N, [Base, Expo, Mod]), \+ integer(N) ->
@@ -28,6 +37,25 @@ expmod_(Base0, Expo0, Mod, C, R) :-
Base is (Base0 * Base0) mod Mod, Base is (Base0 * Base0) mod Mod,
expmod_(Base, Expo, Mod, C, R). expmod_(Base, Expo, Mod, C, R).
%% lcm(+A, +B, -Lcm) is det.
%
% Calculates the Least common multiple for A and B: the smallest positive integer
% that is divisible by both A and B.
%
% A and B need to be integers.
lcm(A, B, X) :-
builtins:must_be_number(A, lcm/2),
builtins:must_be_number(B, lcm/2),
( \+ integer(A) -> type_error(integer, A, lcm/2)
; \+ integer(B) -> type_error(integer, B, lcm/2)
; (A = 0, B = 0) -> X = 0
; builtins:can_be_number(X, lcm/2),
X is abs(B) // gcd(A,B) * abs(A)
).
%% lsb(+X, -N).
%
% True iff N is the least significat bit of integer X
lsb(X, N) :- lsb(X, N) :-
builtins:must_be_number(X, lsb/2), builtins:must_be_number(X, lsb/2),
( \+ integer(X) -> type_error(integer, X, lsb/2) ( \+ integer(X) -> type_error(integer, X, lsb/2)
@@ -37,6 +65,9 @@ lsb(X, N) :-
msb_(X1, -1, N) msb_(X1, -1, N)
). ).
%% msb(+X, -N).
%
% True iff N is the most significant bit of integer X
msb(X, N) :- msb(X, N) :-
builtins:must_be_number(X, msb/2), builtins:must_be_number(X, msb/2),
( \+ integer(X) -> type_error(integer, X, msb/2) ( \+ integer(X) -> type_error(integer, X, msb/2)
@@ -52,6 +83,9 @@ msb_(X, M, N) :-
M1 is M + 1, M1 is M + 1,
msb_(X1, M1, N). msb_(X1, M1, N).
%% number_to_rational(+Real, -Fraction).
%
% True iff given a number Real, Fraction is the same number represented as a fraction.
number_to_rational(Real, Fraction) :- number_to_rational(Real, Fraction) :-
( var(Real) -> instantiation_error(number_to_rational/2) ( var(Real) -> instantiation_error(number_to_rational/2)
; integer(Real) -> Fraction is Real rdiv 1 ; integer(Real) -> Fraction is Real rdiv 1
@@ -94,12 +128,6 @@ number_to_rational(Eps0, Real0, Fraction) :-
), ),
!. !.
number(X) :-
( integer(X)
; float(X)
; rational(X)
).
stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, C/D, Fraction) :- stern_brocot_(Qnn/Qnd, Qpn/Qpd, A/B, C/D, Fraction) :-
Fn1 is A + C, Fn1 is A + C,
Fd1 is B + D, Fd1 is B + D,
@@ -116,8 +144,20 @@ simplify_fraction(A0/B0, A/B) :-
A is A0 // G, A is A0 // G,
B is B0 // G. B is B0 // G.
%% rational_numerator_denominator(+Fraction, -Numerator, -Denominator).
%
% True iff given a fraction Fraction, Numerator is the numerator of that fraction
% and Denominator the denominator.
rational_numerator_denominator(R, N, D) :- rational_numerator_denominator(R, N, D) :-
write_term_to_chars(R, [], Cs), write_term_to_chars(R, [], Cs),
append(Ns, [' ', r, d, i, v, ' '|Ds], Cs), append(Ns, [' ', r, d, i, v, ' '|Ds], Cs),
number_chars(N, Ns), number_chars(N, Ns),
number_chars(D, Ds). number_chars(D, Ds).
%% popcount(+Number, -Bits1).
%
% True iff given an integer Number, Bits1 is the amount of 1 bits the binary representation
% of that number has.
popcount(X, N) :-
must_be(integer, X),
'$popcount'(X, N).

View File

@@ -54,28 +54,24 @@
:- use_module(library(lists)). :- use_module(library(lists)).
/** <module> Binary associations /** Binary associations
Assocs are Key-Value associations implemented as a balanced binary tree Assocs are Key-Value associations implemented as a balanced binary tree
(AVL tree). (AVL tree).
@see library(pairs), library(rbtrees) Authors: R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
@author R.A.O'Keefe, L.Damas, V.S.Costa and Jan Wielemaker
*/ */
/* :- meta_predicate map_assoc(1, ?).
:- meta_predicate :- meta_predicate map_assoc(2, ?, ?).
map_assoc(1, ?),
map_assoc(2, ?, ?).
*/
%! empty_assoc(?Assoc) is semidet. %% empty_assoc(?Assoc) is semidet.
% %
% Is true if Assoc is the empty association list. % Is true if Assoc is the empty association list.
empty_assoc(t). empty_assoc(t).
%! assoc_to_list(+Assoc, -Pairs) is det. %% assoc_to_list(+Assoc, -Pairs) is det.
% %
% Translate Assoc to a list Pairs of Key-Value pairs. The keys % Translate Assoc to a list Pairs of Key-Value pairs. The keys
% in Pairs are sorted in ascending order. % in Pairs are sorted in ascending order.
@@ -89,7 +85,7 @@ assoc_to_list(t(Key,Val,_,L,R), List, Rest) :-
assoc_to_list(t, List, List). assoc_to_list(t, List, List).
%! assoc_to_keys(+Assoc, -Keys) is det. %% assoc_to_keys(+Assoc, -Keys) is det.
% %
% True if Keys is the list of keys in Assoc. The keys are sorted % True if Keys is the list of keys in Assoc. The keys are sorted
% in ascending order. % in ascending order.
@@ -103,7 +99,7 @@ assoc_to_keys(t(Key,_,_,L,R), List, Rest) :-
assoc_to_keys(t, List, List). assoc_to_keys(t, List, List).
%! assoc_to_values(+Assoc, -Values) is det. %% assoc_to_values(+Assoc, -Values) is det.
% %
% True if Values is the list of values in Assoc. Values are % True if Values is the list of values in Assoc. Values are
% ordered in ascending order of the key to which they were % ordered in ascending order of the key to which they were
@@ -117,7 +113,7 @@ assoc_to_values(t(_,Value,_,L,R), List, Rest) :-
assoc_to_values(R, More, Rest). assoc_to_values(R, More, Rest).
assoc_to_values(t, List, List). assoc_to_values(t, List, List).
%! is_assoc(+Assoc) is semidet. %% is_assoc(+Assoc) is semidet.
% %
% True if Assoc is an association list. This predicate checks % True if Assoc is an association list. This predicate checks
% that the structure is valid, elements are in order, and tree % that the structure is valid, elements are in order, and tree
@@ -154,12 +150,10 @@ balance(=,-).
balance(<,<). balance(<,<).
balance(>,>). balance(>,>).
%! gen_assoc(?Key, +Assoc, ?Value) is nondet. %% gen_assoc(?Key, +Assoc, ?Value) is nondet.
% %
% True if Key-Value is an association in Assoc. Enumerates keys in % True if Key-Value is an association in Assoc. Enumerates keys in
% ascending order on backtracking. % ascending order on backtracking.
%
% @see get_assoc/3.
gen_assoc(Key, Assoc, Value) :- gen_assoc(Key, Assoc, Value) :-
( ground(Key) ( ground(Key)
@@ -174,11 +168,11 @@ gen_assoc_(Key, t(_,_,_,_,R), Val) :-
gen_assoc_(Key, R, Val). gen_assoc_(Key, R, Val).
%! get_assoc(+Key, +Assoc, -Value) is semidet. %% get_assoc(+Key, +Assoc, -Value) is semidet.
% %
% True if Key-Value is an association in Assoc. % True if Key-Value is an association in Assoc.
% %
% @error type_error(assoc, Assoc) if Assoc is not an association list. % Throws error: `type_error(assoc, Assoc)` if Assoc is not an association list.
get_assoc(Key, Assoc, Val) :- get_assoc(Key, Assoc, Val) :-
must_be(assoc, Assoc), must_be(assoc, Assoc),
@@ -204,7 +198,7 @@ get_assoc(>, Key, _, _, Tree, Val) :-
% :- endif. % :- endif.
%! get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet. %% get_assoc(+Key, +Assoc0, ?Val0, ?Assoc, ?Val) is semidet.
% %
% True if Key-Val0 is in Assoc0 and Key-Val is in Assoc. % True if Key-Val0 is in Assoc0 and Key-Val is in Assoc.
@@ -219,12 +213,12 @@ get_assoc(>, Key, V, L, R, Val, V, L, NR, NVal) :-
get_assoc(Key, R, Val, NR, NVal). get_assoc(Key, R, Val, NR, NVal).
%! list_to_assoc(+Pairs, -Assoc) is det. %% list_to_assoc(+Pairs, -Assoc) is det.
% %
% Create an association from a list Pairs of Key-Value pairs. List % Create an association from a list Pairs of Key-Value pairs. List
% must not contain duplicate keys. % must not contain duplicate keys.
% %
% @error domain_error(unique_key_pairs, List) if List contains duplicate keys % Throws error: `domain_error(unique_key_pairs, List)` if List contains duplicate keys
list_to_assoc(List, Assoc) :- list_to_assoc(List, Assoc) :-
( List = [] -> Assoc = t ( List = [] -> Assoc = t
@@ -249,13 +243,13 @@ list_to_assoc(N, List, More, Depth, t(K,V,Balance,L,R)) :-
compare(B, RDepth, LDepth), compare(B, RDepth, LDepth),
balance(B, Balance). balance(B, Balance).
%! ord_list_to_assoc(+Pairs, -Assoc) is det. %% ord_list_to_assoc(+Pairs, -Assoc) is det.
% %
% Assoc is created from an ordered list Pairs of Key-Value % Assoc is created from an ordered list Pairs of Key-Value
% pairs. The pairs must occur in strictly ascending order of % pairs. The pairs must occur in strictly ascending order of
% their keys. % their keys.
% %
% @error domain_error(key_ordered_pairs, List) if pairs are not ordered. % Throws error: `domain_error(key_ordered_pairs, List)` if pairs are not ordered.
ord_list_to_assoc(Sorted, Assoc) :- ord_list_to_assoc(Sorted, Assoc) :-
( Sorted = [] -> Assoc = t ( Sorted = [] -> Assoc = t
@@ -266,7 +260,7 @@ ord_list_to_assoc(Sorted, Assoc) :-
) )
). ).
%! ord_pairs(+Pairs) is semidet %% ord_pairs(+Pairs) is semidet
% %
% True if Pairs is a list of Key-Val pairs strictly ordered by key. % True if Pairs is a list of Key-Val pairs strictly ordered by key.
@@ -277,7 +271,7 @@ ord_pairs([K-_V|Rest], K0) :-
K0 @< K, K0 @< K,
ord_pairs(Rest, K). ord_pairs(Rest, K).
%! map_assoc(:Pred, +Assoc) is semidet. %% map_assoc(:Pred, +Assoc) is semidet.
% %
% True if Pred(Value) is true for all values in Assoc. % True if Pred(Value) is true for all values in Assoc.
@@ -290,7 +284,7 @@ map_assoc_(t(_,Val,_,L,R), Pred) :-
call(Pred, Val), call(Pred, Val),
map_assoc_(R, Pred). map_assoc_(R, Pred).
%! map_assoc(:Pred, +Assoc0, ?Assoc) is semidet. %% map_assoc(:Pred, +Assoc0, ?Assoc) is semidet.
% %
% Map corresponding values. True if Assoc is Assoc0 with Pred % Map corresponding values. True if Assoc is Assoc0 with Pred
% applied to all corresponding pairs of of values. % applied to all corresponding pairs of of values.
@@ -305,7 +299,7 @@ map_assoc_(t(Key,Val,B,L0,R0), Pred, t(Key,Ans,B,L1,R1)) :-
map_assoc_(R0, Pred, R1). map_assoc_(R0, Pred, R1).
%! max_assoc(+Assoc, -Key, -Value) is semidet. %% max_assoc(+Assoc, -Key, -Value) is semidet.
% %
% True if Key-Value is in Assoc and Key is the largest key. % True if Key-Value is in Assoc and Key is the largest key.
@@ -317,7 +311,7 @@ max_assoc(t(K,V,_,_,R), _, _, Key, Val) :-
max_assoc(R, K, V, Key, Val). max_assoc(R, K, V, Key, Val).
%! min_assoc(+Assoc, -Key, -Value) is semidet. %% min_assoc(+Assoc, -Key, -Value) is semidet.
% %
% True if Key-Value is in assoc and Key is the smallest key. % True if Key-Value is in assoc and Key is the smallest key.
@@ -329,7 +323,7 @@ min_assoc(t(K,V,_,L,_), _, _, Key, Val) :-
min_assoc(L, K, V, Key, Val). min_assoc(L, K, V, Key, Val).
%! put_assoc(+Key, +Assoc0, +Value, -Assoc) is det. %% put_assoc(+Key, +Assoc0, +Value, -Assoc) is det.
% %
% Assoc is Assoc0, except that Key is associated with % Assoc is Assoc0, except that Key is associated with
% Value. This can be used to insert and change associations. % Value. This can be used to insert and change associations.
@@ -364,7 +358,7 @@ table(< , right , - , no , no ) :- !.
table(> , left , - , no , no ) :- !. table(> , left , - , no , no ) :- !.
table(> , right , - , no , yes ) :- !. table(> , right , - , no , yes ) :- !.
%! del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. %% del_min_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
% %
% True if Key-Value is in Assoc0 and Key is the smallest key. % True if Key-Value is in Assoc0 and Key is the smallest key.
% Assoc is Assoc0 with Key-Value removed. Warning: This will % Assoc is Assoc0 with Key-Value removed. Warning: This will
@@ -378,7 +372,7 @@ del_min_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :-
del_min_assoc(L, Key, Val, NewL, LeftChanged), del_min_assoc(L, Key, Val, NewL, LeftChanged),
deladjust(LeftChanged, t(K,V,B,NewL,R), left, NewTree, Changed). deladjust(LeftChanged, t(K,V,B,NewL,R), left, NewTree, Changed).
%! del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet. %% del_max_assoc(+Assoc0, ?Key, ?Val, -Assoc) is semidet.
% %
% True if Key-Value is in Assoc0 and Key is the greatest key. % True if Key-Value is in Assoc0 and Key is the greatest key.
% Assoc is Assoc0 with Key-Value removed. Warning: This will % Assoc is Assoc0 with Key-Value removed. Warning: This will
@@ -392,7 +386,7 @@ del_max_assoc(t(K,V,B,L,R), Key, Val, NewTree, Changed) :-
del_max_assoc(R, Key, Val, NewR, RightChanged), del_max_assoc(R, Key, Val, NewR, RightChanged),
deladjust(RightChanged, t(K,V,B,L,NewR), right, NewTree, Changed). deladjust(RightChanged, t(K,V,B,L,NewR), right, NewTree, Changed).
%! del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet. %% del_assoc(+Key, +Assoc0, ?Value, -Assoc) is semidet.
% %
% True if Key-Value is in Assoc0. Assoc is Assoc0 with % True if Key-Value is in Assoc0. Assoc is Assoc0 with
% Key-Value removed. % Key-Value removed.

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

@@ -0,0 +1,121 @@
:- module(atts, [op(1199, fx, attribute),
call_residue_vars/2,
term_attributed_variables/2]).
:- use_module(library(dcgs)).
:- use_module(library(terms)).
/* represent the list of attributes belonging to a variable,
of a particular module, as a list of terms of the form
Module:put_atts(V, ListOfAtts). */
'$default_attr_list'(Module, V) -->
( { Module:get_atts(V, Attributes) } ->
'$default_attr_list'(Attributes, Module, V)
; []
).
'$default_attr_list'([PG | PGs], Module, AttrVar) -->
[Module:put_atts(AttrVar, PG)],
'$default_attr_list'(PGs, Module, AttrVar).
'$default_attr_list'([], _, _) --> [].
'$absent_attr'(V, Module, Attr) :-
( '$get_from_attr_list'(V, Module, Attr) ->
false
; true
).
'$copy_attr_list'(L, _Module, []) :- var(L), !.
'$copy_attr_list'([Module0:Att|Atts], Module, CopiedAtts) :-
( Module0 == Module ->
CopiedAtts = [Att|CopiedAtts0],
'$copy_attr_list'(Atts, Module, CopiedAtts0)
; '$copy_attr_list'(Atts, Module, CopiedAtts)
).
user:term_expansion(Term0, Terms) :-
nonvar(Term0),
Term0 = (:- attribute Atts),
nonvar(Atts),
prolog_load_context(module, Module),
phrase(expand_terms(Atts, Module), Terms).
expand_terms(Atts, Module) -->
put_attrs_var_check,
put_attrs(Atts, Module),
get_attrs_var_check(Module),
get_attrs(Atts, Module).
put_attrs_var_check -->
[(put_atts(Var, Attr) :- nonvar(Var),
throw(error(uninstantiation_error(Var), put_atts/2))),
(put_atts(Var, Attr) :- var(Attr),
throw(error(instantiation_error, put_atts/2)))].
get_attrs_var_check(Module) -->
[(get_atts(Var, Attr) :- nonvar(Var),
throw(error(uninstantiation_error(Var), get_atts/2))),
(get_atts(Var, Attr) :- var(Attr),
!,
'$get_attr_list'(Var, Ls),
nonvar(Ls),
atts:'$copy_attr_list'(Ls, Module, Attr))].
put_attrs(Name/Arity, Module) -->
put_attr(Name, Arity, Module),
[(put_atts(Var, Attr) :- lists:maplist(Module:put_atts(Var), Attr), !)].
put_attrs((Name/Arity, Atts), Module) -->
{ nonvar(Atts) },
put_attr(Name, Arity, Module),
put_attrs(Atts, Module).
get_attrs(Name/Arity, Module) -->
get_attr(Name, Arity, Module).
get_attrs((Name/Arity, Atts), Module) -->
{ nonvar(Atts) },
get_attr(Name, Arity, Module),
get_attrs(Atts, Module).
put_attr(Name, Arity, Module) -->
{ functor(Attr, Name, Arity) },
[(put_atts(V, +Attr) :-
!,
'$put_to_attr_list'(V, Module, Attr)),
(put_atts(V, Attr) :-
!,
'$put_to_attr_list'(V, Module, Attr)),
(put_atts(V, -Attr) :-
!,
'$del_from_attr_list'(V, Module, Attr))].
get_attr(Name, Arity, Module) -->
{ functor(Attr, Name, Arity) },
[(get_atts(V, +Attr) :-
!,
functor(Attr, _, _),
atts:'$get_from_attr_list'(V, Module, Attr)),
(get_atts(V, Attr) :-
!,
functor(Attr, _, _),
atts:'$get_from_attr_list'(V, Module, Attr)),
(get_atts(V, -Attr) :-
!,
functor(Attr, _, _),
atts:'$absent_attr'(V, Module, Attr))].
user:goal_expansion(Term, M:put_atts(Var, Attr)) :-
nonvar(Term),
Term = put_atts(Var, M, Attr).
user:goal_expansion(Term, M:get_atts(Var, Attr)) :-
nonvar(Term),
Term = get_atts(Var, M, Attr).
:- meta_predicate call_residue_vars(0, ?).
call_residue_vars(Goal, Vars) :-
'$get_attr_var_queue_delim'(B),
call(Goal),
'$get_attr_var_queue_beyond'(B, Vars).
term_attributed_variables(Term, Vars) :-
'$term_attributed_variables'(Term, Vars).

View File

@@ -1,3 +1,10 @@
/** Predicates that generate integers
These predicates can be used to reason about integers in a reduced domain that
follow some property. `library(clpz)` provides another way of reasoning about
integers that may also be interesting.
*/
:- module(between, [between/3, gen_int/1, gen_nat/1, numlist/2, numlist/3, repeat/1]). :- module(between, [between/3, gen_int/1, gen_nat/1, numlist/2, numlist/3, repeat/1]).
%% TODO: numlist/5. %% TODO: numlist/5.
@@ -5,6 +12,24 @@
:- use_module(library(lists), [length/2]). :- use_module(library(lists), [length/2]).
:- use_module(library(error)). :- use_module(library(error)).
%% between(+Lower, +Upper, -X).
%
% Given Lower and Upper are both integer numbers, true iff X is an integer so that _Lower =< X =< Upper_.
% Can be used both to check if X is between Lower and Upper or to generate an integer between
% Lower and Upper.
%
% Examples:
%
% ```
% ?- between(10, 20, 15).
% true.
% ?- between(10, 20, 25).
% false.
% ?- between(3, 5, X).
% X = 3
% ; X = 4
% ; X = 5.
% ```
between(Lower, Upper, X) :- between(Lower, Upper, X) :-
must_be(integer, Lower), must_be(integer, Lower),
must_be(integer, Upper), must_be(integer, Upper),
@@ -12,21 +37,27 @@ between(Lower, Upper, X) :-
( nonvar(X) -> ( nonvar(X) ->
Lower =< X, Lower =< X,
X =< Upper X =< Upper
; between_(Lower, Upper, X) ; Lower =< Upper,
between_(Lower, Upper, X)
). ).
between_(Lower, Upper, Lower) :- between_(Lower, Upper, Lower1) :-
Lower =< Upper. Lower < Upper,
between_(Lower1, Upper, X) :- !,
Lower1 < Upper, ( Lower1 = Lower
Lower2 is Lower1 + 1, ; Lower0 is Lower + 1,
between_(Lower2, Upper, X). between_(Lower0, Upper, Lower1)
).
between_(Lower, Lower, Lower).
enumerate_nats(I, I). enumerate_nats(I, I).
enumerate_nats(I0, N) :- enumerate_nats(I0, N) :-
I1 is I0 + 1, I1 is I0 + 1,
enumerate_nats(I1, N). enumerate_nats(I1, N).
%% gen_nat(?N)
%
% True iff N is a natural number.
gen_nat(N) :- gen_nat(N) :-
can_be(integer, N), can_be(integer, N),
( var(N) -> enumerate_nats(0, N) ( var(N) -> enumerate_nats(0, N)
@@ -41,6 +72,9 @@ enumerate_ints(I0, N) :-
I1 is I0 + 1, I1 is I0 + 1,
enumerate_ints(I1, N). enumerate_ints(I1, N).
%% gen_int(?N)
%
% True iff N is an integer.
gen_int(N) :- gen_int(N) :-
can_be(integer, N), can_be(integer, N),
( var(N) -> enumerate_ints(0, N) ( var(N) -> enumerate_ints(0, N)
@@ -52,9 +86,24 @@ repeat_integer(N) :-
repeat_integer(N0) :- repeat_integer(N0) :-
N0 > 0, N1 is N0 - 1, repeat_integer(N1). N0 > 0, N1 is N0 - 1, repeat_integer(N1).
%% repeat(+N)
%
% Succeeds N times. This predicate is only included for compatibility and *should not be used*
% because it lacks a declarative interpretation.
repeat(N) :- repeat(N) :-
must_be(integer, N), repeat_integer(N). must_be(integer, N), repeat_integer(N).
%% numlist(?Upper, ?List)
%
% True iff List is the list of integers _[1, ..., Upper]_. Example:
%
% ```
% ?- numlist(X, Y).
% X = 1, Y = [1],
% ; X = 2, Y = [1,2]
% ; X = 3, Y = [1,2,3]
% ; ... .
% ```
numlist(Upper, List) :- numlist(Upper, List) :-
( integer(Upper) -> findall(X, between(1, Upper, X), List) ( integer(Upper) -> findall(X, between(1, Upper, X), List)
; List = [_|_], length(List, Upper), findall(X, between(1, Upper, X), List) ; List = [_|_], length(List, Upper), findall(X, between(1, Upper, X), List)
@@ -103,5 +152,14 @@ gen_ints(L, U) :-
), ),
L =< U. L =< U.
%% numlist(?Lower, ?Upper, ?List).
%
% True iff List is a list of the form _[Lower, ..., Upper]_.
% Example:
%
% ```
% ?- numlist(5, 10, X).
% X = [5,6,7,8,9,10].
% ```
numlist(Lower, Upper, List) :- numlist(Lower, Upper, List) :-
gen_ints(Lower, Upper), findall(X, between(Lower, Upper, X), List). gen_ints(Lower, Upper), findall(X, between(Lower, Upper, X), List).

2212
src/lib/builtins.pl Normal file

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,383 @@
/** High-level predicates to work with chars and strings
This module contains predicates that relates strings of chars
to other representations, as well as high-level predicates to
read and write chars.
*/
:- module(charsio, [char_type/2,
chars_utf8bytes/2,
get_single_char/1,
get_n_chars/3,
get_line_to_chars/3,
read_from_chars/2,
read_term_from_chars/3,
write_term_to_chars/3,
chars_base64/3]).
:- use_module(library(dcgs)).
:- use_module(library(iso_ext)).
:- use_module(library(error)).
:- use_module(library(lists)).
:- use_module(library(iso_ext), [partial_string/1,partial_string/3]).
fabricate_var_name(VarType, VarName, N) :-
char_code('A', AC),
LN is N mod 26 + AC,
char_code(LC, LN),
NN is N // 26,
( NN =:= 0 ->
( VarType == fabricated ->
atom_chars(VarName, ['_', LC])
; VarType == numbervars ->
atom_chars(VarName, [LC])
)
; number_chars(NN, NNChars),
( VarType == fabricated ->
atom_chars(VarName, ['_', LC | NNChars])
; VarType == numbervars ->
atom_chars(VarName, [LC | NNChars])
)
).
var_list_contains_name([VarName = _ | VarList], VarName0) :-
( VarName == VarName0 -> true
; var_list_contains_name(VarList, VarName0)
).
var_list_contains_variable([_ = Var | VarList], Var0) :-
( Var == Var0 -> true
; var_list_contains_variable(VarList, Var0)
).
make_new_var_name(VarType, V, VarName, N, N1, VarList) :-
fabricate_var_name(VarType, VarName0, N),
( var_list_contains_name(VarList, VarName0) ->
N0 is N + 1,
make_new_var_name(VarType, V, VarName, N0, N1, VarList)
; VarName = VarName0,
N1 is N + 1
).
extend_var_list(Vars, VarList, NewVarList, VarType) :-
extend_var_list_(Vars, 0, VarList, NewVarList0, VarType),
append(VarList, NewVarList0, NewVarList).
extend_var_list_([], _, _, [], _).
extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
( var_list_contains_variable(VarList, V) ->
extend_var_list_(Vs, N, VarList, NewVarList, VarType)
; make_new_var_name(VarType, V, VarName, N, N1, VarList),
NewVarList = [VarName = V | NewVarList0],
extend_var_list_(Vs, N1, VarList, NewVarList0, VarType)
).
%% char_type(+Char, -Type).
%
% Given a Char, Type is one of the categories that char fits in.
% Possible categories are:
%
% - `alnum`
% - `alpha`
% - `alphabetic`
% - `alphanumeric`
% - `ascii`
% - `ascii_graphic`
% - `ascii_punctuation`
% - `binary_digit`
% - `control`
% - `decimal_digit`
% - `exponent`
% - `graphic`
% - `graphic_token`
% - `hexadecimal_digit`
% - `layout`
% - `lower`
% - `meta`
% - `numeric`
% - `octal_digit`
% - `octet`
% - `prolog`
% - `sign`
% - `solo`
% - `symbolic_control`
% - `symbolic_hexadecimal`
% - `upper`
% - `to_lower(Lower)`
% - `to_upper(Upper)`
% - `whitespace`
%
% An example:
%
% ```
% ?- char_type(a, Type).
% Type = alnum
% ; Type = alpha
% ; Type = alphabetic
% ; Type = alphanumeric
% ; Type = ascii
% ; Type = ascii_graphic
% ; Type = hexadecimal_digit
% ; Type = lower
% ; Type = octet
% ; Type = prolog
% ; Type = symbolic_control
% ; Type = to_lower("a")
% ; Type = to_upper("A")
% ; false.
% ```
%
% Note that uppercase and lowercase transformations use a string. This is because
% some characters do not map 1:1 between lowercase and uppercase.
char_type(Char, Type) :-
must_be(character, Char),
( ground(Type) ->
( ctype(Type) ->
'$char_type'(Char, Type)
; domain_error(char_type, Type, char_type/2)
)
; ctype(Type),
'$char_type'(Char, Type)
).
ctype(alnum).
ctype(alpha).
ctype(alphabetic).
ctype(alphanumeric).
ctype(ascii).
ctype(ascii_graphic).
ctype(ascii_punctuation).
ctype(binary_digit).
ctype(control).
ctype(decimal_digit).
ctype(exponent).
ctype(graphic).
ctype(graphic_token).
ctype(hexadecimal_digit).
ctype(layout).
ctype(lower).
ctype(meta).
ctype(numeric).
ctype(octal_digit).
ctype(octet).
ctype(prolog).
ctype(sign).
ctype(solo).
ctype(symbolic_control).
ctype(symbolic_hexadecimal).
ctype(to_lower(_)).
ctype(to_upper(_)).
ctype(upper).
ctype(whitespace).
%% get_single_char(-Char).
%
% Gets a single char from the current input stream.
get_single_char(C) :-
( var(C) -> '$get_single_char'(C)
; atom_length(C, 1) -> '$get_single_char'(C)
; type_error(in_character, C, get_single_char/1)
).
%% read_from_chars(+Chars, -Term).
%
% Given a string made of chars which contains a representation of
% a Prolog term, Term is the Prolog term represented. Example:
%
% ```
% ?- read_from_chars("f(x,y).", X).
% X = f(x,y).
% ```
read_from_chars(Chars, Term) :-
must_be(chars, Chars),
must_be(var, Term),
'$read_from_chars'(Chars, Term).
%% read_term_from_chars(+Chars, -Term, +Options).
%
% Like `read_from_chars`, except the reader is configured according to
% `Options` which are those of `read_term`.
%
% ```
% ?- read_term_from_chars("f(X,y).", T, [variable_names(['X'=X])]).
% T = f(X,y).
% ```
read_term_from_chars(Chars, Term, Options) :-
must_be(chars, Chars),
must_be(var, Term),
builtins:parse_read_term_options(Options, [Singletons, VariableNames, Variables], read_term_from_chars/3),
'$read_term_from_chars'(Chars, Term, Singletons, Variables, VariableNames).
%% write_term_to_chars(+Term, +Options, -Chars).
%
% Given a Term which is a Prolog term and a set of options, Chars is
% string representation of that term. Options available are:
%
% * `ignore_ops(+Boolean)` if `true`, the generic term representation is used everywhere. In `false`
% (default), operators do not use that generic term representation.
% * `max_depth(+N)` if the term is nested deeper than N, print the reminder as ellipses.
% If N = 0 (default), there's no limit.
% * `numbervars(+Boolean)` if true, replaces `$VAR(N)` variables with letters, in order. Default is false.
% * `quoted(+Boolean)` if true, strings and atoms that need quotes to be valid Prolog syntax, are quoted. Default is false.
% * `variable_names(+List)` assign names to variables in term. List should be a list of terms of format `Name=Var`.
% * `double_quotes(+Boolean)` if true, strings are printed in double quotes rather than with list notation. Default is false.
write_term_to_chars(_, Options, _) :-
var(Options), instantiation_error(write_term_to_chars/3).
write_term_to_chars(Term, Options, Chars) :-
builtins:parse_write_options(Options,
[DoubleQuotes, IgnoreOps, MaxDepth, NumberVars, Quoted, VNNames],
write_term_to_chars/3),
( nonvar(Chars) ->
throw(error(uninstantiation_error(Chars), write_term_to_chars/3))
;
true
),
term_variables(Term, Vars),
extend_var_list(Vars, VNNames, NewVarNames, numbervars),
'$write_term_to_chars'(Chars, Term, IgnoreOps, NumberVars, Quoted, NewVarNames, MaxDepth, DoubleQuotes).
% Encodes Ch character to list of Bytes.
char_utf8bytes(Ch, Bytes) :-
char_code(Ch, Code),
phrase(code_to_utf8(Code), Bytes).
code_to_utf8(Code) --> {Code @< 0x80}, [Code], !.
code_to_utf8(Code) --> {Code @< 0x800}, encode(Code, 0xC0, 2), !.
code_to_utf8(Code) --> {Code @< 0x10000}, encode(Code, 0xE0, 3), !.
code_to_utf8(Code) --> {Code @< 0x110000}, encode(Code, 0xF0, 4), !.
encode(_, _, 0) --> !.
encode(Code, Prefix, Nb) -->
{ Nb1 is Nb - 1, Byte is Prefix \/ ((Code >> (6 * Nb1)) /\ 0x3F) },
[Byte], encode(Code, 0x80, Nb1).
% Maps characters and UTF-8 bytes.
% If Cs is a variable, parses Bs as a list of UTF-8 bytes.
% Otherwise, transform the list of characters Cs to UTF-8 bytes.
%% chars_utf8bytes(?Chars, ?Bytes).
%
% Maps a string made of chars with a list of UTF-8 bytes. Some examples:
%
% ```
% ?- chars_utf8bytes("Prolog", X).
% X = [80,114,111,108,111,103].
% ?- chars_utf8bytes(X, [226, 136, 145]).
% X = "∑".
% ```
chars_utf8bytes(Cs, Bs) :-
var(Cs), must_be(list, Bs) ->
once(phrase(decode_utf8(Cs), Bs))
; (must_be(list, Cs),
maplist(must_be(atom), Cs),
maplist(char_utf8bytes, Cs, Bss),
append(Bss, Bs)).
decode_utf8([]) --> [].
decode_utf8(Chars) --> leading(Nb, Code), continuation(Code, Chars, Nb).
leading(1, Byte) --> [Byte], {Byte /\ 0x80 =:= 0}.
leading(2, Code) --> [Byte], {Byte /\ 0xE0 =:= 0xC0, Code is Byte - 0xC0}.
leading(3, Code) --> [Byte], {Byte /\ 0xF0 =:= 0xE0, Code is Byte - 0xE0}.
leading(4, Code) --> [Byte], {Byte /\ 0xF8 =:= 0xF0, Code is Byte - 0xF0}.
leading(1, 0xFFFD) --> [_]. % invalid first byte
continuation(Code, [H|T], 1) --> {char_code(H, Code)}, decode_utf8(T).
continuation(Code, Chars, Nb) --> [Byte],
{Nb1 is Nb - 1, Byte /\ 0xC0 =:= 0x80, NextCode is (Code << 6) \/ (Byte - 0x80)},
continuation(NextCode, Chars, Nb1).
% invalid continuation byte
% each remaining continuation byte (if any) will raise 0xFFFD too
continuation(_, ['\xFFFD\'|T], _) --> [_], decode_utf8(T).
%% get_line_to_chars(+Stream, -Chars, +InitialChars).
%
% Reads chars from stream Stream until it finds a `\n` character.
% InitialChars will be appended at the end of Chars
get_line_to_chars(Stream, Cs0, Cs) :-
'$get_n_chars'(Stream, 1, Char), % this also works for binary streams
( Char == [] -> Cs0 = Cs
; Char = [C],
Cs0 = [C|Rest],
( C == '\n' -> Rest = Cs
; get_line_to_chars(Stream, Rest, Cs)
)
).
%% get_n_chars(+Stream, ?N, -Chars).
%
% Read N chars from stream Stream. N can be an integer, in that case
% only N chars are read, or a variable, unifying N with the number of chars
% read until it found EOF.
get_n_chars(Stream, N, Cs) :-
can_be(integer, N),
( var(N) ->
get_to_eof(Stream, Cs),
length(Cs, N)
; N >= 0,
'$get_n_chars'(Stream, N, Cs)
).
get_n_chars_wrapper(Stream, N, Cs) :-
'$get_n_chars'(Stream, N, Cs).
get_to_eof(Stream, Cs) :-
catch(get_n_chars_wrapper(Stream, 512, Cs0),
error(syntax_error(unexpected_end_of_file), _),
Cs0 = []),
( Cs0 == [] -> Cs = []
; partial_string(Cs0, Cs, Rest),
get_to_eof(Stream, Rest)
).
%% chars_base64(?Chars, ?Base64, +Options).
%
% Relation between a list of characters Cs and its Base64 encoding Bs,
% also a list of characters.
%
% At least one of the arguments must be instantiated.
%
% Options are:
%
% - `padding(Boolean)`
% Whether to use padding: true (the default) or false.
% - `charset(C)`
% Either 'standard' (RFC 4648 §4, the default) or 'url' (RFC 4648 §5).
%
% Example:
%
% ```
% ?- chars_base64("hello", Bs, []).
% Bs = "aGVsbG8=".
% ```
chars_base64(Cs, Bs, Options) :-
must_be(list, Options),
( member(O, Options), var(O) ->
instantiation_error(chars_base64/3)
; ( member(padding(Padding), Options) -> true
; Padding = true
),
( member(charset(Charset), Options) -> true
; Charset = standard
)
),
must_be(boolean, Padding),
must_be(atom, Charset),
( member(Charset, [standard,url]) -> true
; domain_error(charset, Charset, chars_base64/3)
),
( var(Cs) ->
must_be(chars, Bs),
'$chars_base64'(Cs, Bs, Padding, Charset)
; must_be(chars, Cs),
( '$first_non_octet'(Cs, N) ->
domain_error(octet_character, N, chars_base64/3)
; '$chars_base64'(Cs, Bs, Padding, Charset)
)
).

View File

@@ -1,6 +1,6 @@
/* CLP(B): Constraint Logic Programming over Boolean Variables /* CLP(B): Constraint Logic Programming over Boolean Variables
Copyright (C): 2019 Markus Triska Copyright (C): 2019-2023 Markus Triska
All rights reserved. All rights reserved.
E-mail: triska@metalevel.at E-mail: triska@metalevel.at
@@ -49,17 +49,6 @@
Compatibility predicates. Compatibility predicates.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
group_pairs_by_key([], []).
group_pairs_by_key([M-N|T0], [M-[N|TN]|T]) :-
same_key(M, T0, TN, T1),
group_pairs_by_key(T1, T).
same_key(M0, [M-N|T0], [N|TN], T) :-
M0 == M,
!,
same_key(M, T0, TN, T).
same_key(_, L, [], L).
must_be(What, Term) :- must_be(What, unknown(Term)-1, Term). must_be(What, Term) :- must_be(What, unknown(Term)-1, Term).
must_be(acyclic, Where, Term) :- !, must_be(acyclic, Where, Term) :- !,
@@ -116,6 +105,262 @@ goal_expansion(del_attr(Var, Module), (var(Var) -> put_atts(Var, -Access);true))
Access =.. [Module,_]. Access =.. [Module,_].
/** Constraint Logic Programming over Boolean variables
## Introduction
This library provides CLP(B), Constraint Logic Programming over
Boolean variables. It can be used to model and solve combinatorial
problems such as verification, allocation and covering tasks.
CLP(B) is an instance of the general CLP(_X_) scheme,
extending logic programming with reasoning over specialised domains.
The implementation is based on reduced and ordered Binary Decision
Diagrams (BDDs).
Benchmarks and usage examples of this library are available from:
[*https://www.metalevel.at/clpb/*](https://www.metalevel.at/clpb/)
## Boolean expressions
A _Boolean expression_ is one of:
| `0` | false |
| `1` | true |
| _variable_ | unknown truth value |
| _atom_ | universally quantified variable |
| ~ _Expr_ | logical NOT |
| _Expr_ + _Expr_ | logical OR |
| _Expr_ * _Expr_ | logical AND |
| _Expr_ # _Expr_ | exclusive OR |
| _Var_ ^ _Expr_ | existential quantification |
| _Expr_ =:= _Expr_ | equality |
| _Expr_ =\= _Expr_ | disequality (same as #) |
| _Expr_ =< _Expr_ | less or equal (implication) |
| _Expr_ >= _Expr_ | greater or equal |
| _Expr_ < _Expr_ | less than |
| _Expr_ > _Expr_ | greater than |
| card(Is,Exprs) | cardinality constraint (_see below_) |
| `+(Exprs)` | n-fold disjunction (_see below_) |
| `*(Exprs)` | n-fold conjunction (_see below_) |
where _Expr_ again denotes a Boolean expression.
The Boolean expression `card(Is,Exprs)` is true iff the number of true
expressions in the list `Exprs` is a member of the list `Is` of
integers and integer ranges of the form `From-To`. For example, to
state that precisely two of the three variables `X`, `Y` and `Z` are
`true`, you can use `sat(card([2],[X,Y,Z]))`.
`+(Exprs)` and `*(Exprs)` denote, respectively, the disjunction and
conjunction of all elements in the list `Exprs` of Boolean
expressions.
Atoms denote parametric values that are universally quantified. All
universal quantifiers appear implicitly in front of the entire
expression. In residual goals, universally quantified variables always
appear on the right-hand side of equations. Therefore, they can be
used to express functional dependencies on input variables.
## Interface predicates
The most frequently used CLP(B) predicates are:
* `sat(+Expr)`
True iff the Boolean expression Expr is satisfiable.
* `taut(+Expr, -T)`
If Expr is a tautology with respect to the posted constraints, succeeds
with *T = 1*. If Expr cannot be satisfied, succeeds with *T = 0*.
Otherwise, it fails.
* `labeling(+Vs)`
Assigns truth values to the variables Vs such that all constraints
are satisfied.
The unification of a CLP(B) variable _X_ with a term _T_ is equivalent
to posting the constraint sat(X=:=T).
## Examples
Here is an example session with a few queries and their answers:
```
?- use_module(library(clpb)).
true.
?- sat(X*Y).
X = 1, Y = 1.
?- sat(X * ~X).
false.
?- taut(X * ~X, T).
T = 0, clpb:sat(X=:=X).
?- sat(X^Y^(X+Y)).
clpb:sat(X=:=X), clpb:sat(Y=:=Y).
?- sat(X*Y + X*Z), labeling([X,Y,Z]).
X = 1, Y = 0, Z = 1
; X = 1, Y = 1, Z = 0
; X = 1, Y = 1, Z = 1.
?- sat(X =< Y), sat(Y =< Z), taut(X =< Z, T).
T = 1, clpb:sat(X=:=X*Y), clpb:sat(Y=:=Y*Z).
?- sat(1#X#a#b).
clpb:sat(X=:=a#b).
```
The pending residual goals constrain remaining variables to Boolean
expressions and are declaratively equivalent to the original query.
The last example illustrates that when applicable, remaining variables
are expressed as functions of universally quantified variables.
## Obtaining BDDs
By default, CLP(B) residual goals appear in (approximately) algebraic
normal form (ANF). This projection is often computationally expensive.
We can assert `clpb:clpb_residuals(bdd)` to see the BDD representation
of all constraints. This results in faster projection to residual
goals, and is also useful for learning more about BDDs. For example:
```
?- asserta(clpb:clpb_residuals(bdd)).
true.
?- sat(X#Y).
node(3)- (v(X, 0)->node(2);node(1)),
node(1)- (v(Y, 1)->true;false),
node(2)- (v(Y, 1)->false;true).
```
Note that this representation cannot be pasted back on the toplevel,
and its details are subject to change. Use copy_term/3 to obtain
such answers as Prolog terms.
The variable order of the BDD is determined by the order in which the
variables first appear in constraints. To obtain different orders,
we can for example use:
```
?- sat(+[1,Y,X]), sat(X#Y).
node(3)- (v(Y, 0)->node(2);node(1)),
node(1)- (v(X, 1)->true;false),
node(2)- (v(X, 1)->false;true).
```
## Enabling monotonic CLP(B)
In the default execution mode, CLP(B) constraints are _not_ monotonic.
This means that _adding_ constraints can yield new solutions. For
example:
```
?- sat(X=:=1), X = 1+0.
false.
?- X = 1+0, sat(X=:=1), X = 1+0.
X = 1+0.
```
This behaviour is highly problematic from a logical point of view, and
it may render [*declarative
debugging*](https://www.metalevel.at/prolog/debugging)
techniques inapplicable.
Assert `clpb:monotonic` to make CLP(B) *monotonic*. If this mode is
enabled, then you must wrap CLP(B) variables with the functor
`v/1`. For example:
```
?- asserta(clpb:monotonic).
true.
?- sat(v(X)=:=1#1).
X = 0.
```
## Example: Pigeons
In this example, we are attempting to place _I_ pigeons into _J_ holes
in such a way that each hole contains at most one pigeon. One
interesting property of this task is that it can be formulated using
only _cardinality constraints_ (`card/2`). Another interesting aspect
is that this task has no short resolution refutations in general.
In the following, we use [*Prolog DCG
notation*](https://www.metalevel.at/prolog/dcg) to describe a
list `Cs` of CLP(B) constraints that must all be satisfied.
```
:- use_module(library(clpb)).
:- use_module(library(clpz)).
:- use_module(library(lists)).
:- use_module(library(dcgs)).
pigeon(I, J, Rows, Cs) :-
length(Rows, I), length(Row, J),
maplist(same_length(Row), Rows),
transpose(Rows, TRows),
phrase((all_cards(Rows,[1]),all_cards(TRows,[0,1])), Cs).
all_cards([], _) --> [].
all_cards([Ls|Lss], Cs) --> [card(Cs,Ls)], all_cards(Lss, Cs).
```
Example queries:
```
?- pigeon(9, 8, Rows, Cs), sat(*(Cs)).
false.
?- pigeon(2, 3, Rows, Cs), sat(*(Cs)),
append(Rows, Vs), labeling(Vs),
maplist(portray_clause, Rows).
[0,0,1].
[0,1,0].
etc.
```
## Example: Boolean circuit
Consider a Boolean circuit that express the Boolean function =|XOR|=
with 4 =|NAND|= gates. We can model such a circuit with CLP(B)
constraints as follows:
```
:- use_module(library(clpb)).
nand_gate(X, Y, Z) :- sat(Z =:= ~(X*Y)).
xor(X, Y, Z) :-
nand_gate(X, Y, T1),
nand_gate(X, T1, T2),
nand_gate(Y, T1, T3),
nand_gate(T2, T3, Z).
```
Using universally quantified variables, we can show that the circuit
does compute =|XOR|= as intended:
```
?- xor(x, y, Z).
clpb:sat(Z=:=x#y).
```
## Acknowledgments
The interface predicates of this library follow the example of
[*SICStus Prolog*](https://sicstus.sics.se).
Use SICStus Prolog for higher performance in many cases.
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Each CLP(B) variable belongs to exactly one BDD. Each CLP(B) Each CLP(B) variable belongs to exactly one BDD. Each CLP(B)
variable gets an attribute (in module "clpb") of the form: variable gets an attribute (in module "clpb") of the form:
@@ -842,9 +1087,8 @@ verify_attributes(Var, Other, Gs) :-
( integer(Other) -> ( integer(Other) ->
( between(0, 1, Other) -> ( between(0, 1, Other) ->
root_get_formula_bdd(Root, Sat, BDD0), root_get_formula_bdd(Root, Sat, BDD0),
bdd_restriction(BDD0, I, Other, BDD),
root_put_formula_bdd(Root, Sat, BDD), root_put_formula_bdd(Root, Sat, BDD),
Gs = [satisfiable_bdd(BDD)] Gs = [bdd_restriction(BDD0,I,Other,BDD),satisfiable_bdd(BDD)]
; no_truth_value(Other) ; no_truth_value(Other)
) )
; atom(Other) -> ; atom(Other) ->
@@ -1120,11 +1364,9 @@ indomain(1).
% %
% Examples: % 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], % Vs = [A,B], Count = 3, clpb:sat(A=:=A*B).
% Count = 3,
% sat(A=:=A*B).
% %
% ?- length(Vs, 120), % ?- length(Vs, 120),
% sat_count(+Vs, CountOr), % sat_count(+Vs, CountOr),
@@ -1132,7 +1374,9 @@ indomain(1).
% Vs = [...], % Vs = [...],
% CountOr = 1329227995784915872903807060280344575, % CountOr = 1329227995784915872903807060280344575,
% CountAnd = 1. % CountAnd = 1.
% == % ```
sat_count(Sat0, N) :- sat_count(Sat0, N) :-
catch((parse_sat(Sat0, Sat), catch((parse_sat(Sat0, Sat),
@@ -1258,7 +1502,7 @@ random_bindings(VNum, Node) -->
% linear objective function over Boolean variables Vs with integer % linear objective function over Boolean variables Vs with integer
% coefficients Weights. This predicate assigns 0 and 1 to the % coefficients Weights. This predicate assigns 0 and 1 to the
% variables in Vs such that all stated constraints are satisfied, and % variables in Vs such that all stated constraints are satisfied, and
% Maximum is the maximum of sum(Weight_i*V_i) over all admissible % Maximum is the maximum of `sum(Weight_i*V_i)` over all admissible
% assignments. On backtracking, all admissible assignments that % assignments. On backtracking, all admissible assignments that
% attain the optimum are generated. % attain the optimum are generated.
% %
@@ -1267,10 +1511,11 @@ random_bindings(VNum, Node) -->
% %
% Example: % Example:
% %
% == % ```
% ?- sat(A#B), weighted_maximum([1,2,1], [A,B,C], Maximum). % ?- sat(A#B), weighted_maximum([1,2,1], [A,B,C], Maximum).
% A = 0, B = 1, C = 1, Maximum = 3. % A = 0, B = 1, C = 1, Maximum = 3
% == % ; false.
% ```
weighted_maximum(Ws, Vars, Max) :- weighted_maximum(Ws, Vars, Max) :-
must_be(list(integer), Ws), must_be(list(integer), Ws),
@@ -1288,13 +1533,18 @@ weighted_maximum(Ws, Vars, Max) :-
maplist(var_with_index, Vars, IVs), maplist(var_with_index, Vars, IVs),
pairs_keys_values(Pairs0, IVs, Ws), pairs_keys_values(Pairs0, IVs, Ws),
keysort(Pairs0, Pairs1), keysort(Pairs0, Pairs1),
pairs_keys_values(Pairs1, IVs1, WeightsIndexOrder), % sum linear combinations of repeated variables
group_pairs_by_key(Pairs1, Groups),
maplist(group_sumweights_pair, Groups, Pairs2),
pairs_keys_values(Pairs2, IVs1, WeightsIndexOrder),
pairs_values(IVs1, VarsIndexOrder), pairs_values(IVs1, VarsIndexOrder),
% Pairs is a list of Var-Weight terms, in index order of Vars % Pairs is a list of Var-Weight terms, in index order of Vars
pairs_keys_values(Pairs, VarsIndexOrder, WeightsIndexOrder), pairs_keys_values(Pairs, VarsIndexOrder, WeightsIndexOrder),
bdd_maximum(BDD, Pairs, Max), bdd_maximum(BDD, Pairs, Max),
max_labeling(BDD, Pairs). max_labeling(BDD, Pairs).
group_sumweights_pair((I-V)-Ws, (I-V)-W) :- sum_list(Ws, W).
max_labeling(1, Pairs) :- max_upto(Pairs, _, _). max_labeling(1, Pairs) :- max_upto(Pairs, _, _).
max_labeling(node(_,Var,Low,High,Aux), Pairs0) :- max_labeling(node(_,Var,Low,High,Aux), Pairs0) :-
max_upto(Pairs0, Var, Pairs), max_upto(Pairs0, Var, Pairs),
@@ -1378,6 +1628,7 @@ skip_to_var_(Var, Weight, [Var0-Weight0|VWs0], VWs) -->
attribute_goals(Var) --> attribute_goals(Var) -->
{ var_index_root(Var, _, Root) }, { var_index_root(Var, _, Root) },
!,
( { root_get_formula_bdd(Root, Formula, BDD) } -> ( { root_get_formula_bdd(Root, Formula, BDD) } ->
{ del_bdd(Root) }, { del_bdd(Root) },
( { clpb_residuals(bdd) } -> ( { clpb_residuals(bdd) } ->
@@ -1405,6 +1656,10 @@ attribute_goals(Var) -->
booleans(RestVs) booleans(RestVs)
; boolean(Var) % the variable may have occurred only in taut/2 ; boolean(Var) % the variable may have occurred only in taut/2
). ).
attribute_goals(Var) -->
{ get_atts(Var, clpb_bdd(BDD)),
ground(BDD),
put_atts(Var, -clpb_bdd(_)) }.
del_clpb(Var) :- del_clpb(Var) :-
del_attr(Var, clpb), del_attr(Var, clpb),
@@ -1529,8 +1784,8 @@ pairs_([], _) --> [].
pairs_([B|Bs], A) --> [A-B], pairs_(Bs, A). pairs_([B|Bs], A) --> [A-B], pairs_(Bs, A).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Set the Prolog flag clpb_residuals to bdd to obtain the BDD nodes Assert clpb:clpb_residuals(bdd) to obtain the BDD nodes as
as residuals. Note that they cannot be used as regular goals. residuals. Note that they cannot be used as regular goals.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
nodes([]) --> []. nodes([]) --> [].
@@ -1558,9 +1813,10 @@ sats([]) --> [].
sats([A|As]) --> [clpb:sat(A)], sats(As). sats([A|As]) --> [clpb:sat(A)], sats(As).
booleans([]) --> []. booleans([]) --> [].
booleans([B|Bs]) --> boolean(B), { del_clpb(B) }, booleans(Bs). booleans([B|Bs]) --> boolean(B), booleans(Bs).
boolean(Var) --> boolean(Var) -->
{ del_clpb(Var) },
( { get_attr(Var, clpb_omit_boolean, true) } -> [] ( { get_attr(Var, clpb_omit_boolean, true) } -> []
; [clpb:sat(Var =:= Var)] ; [clpb:sat(Var =:= Var)]
). ).

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,7 @@
:- module(cont, [reset/3, shift/1]). :- module(cont, [reset/3, shift/1]).
:- meta_predicate reset(0, ?, ?).
reset(Goal, Ball, Cont) :- reset(Goal, Ball, Cont) :-
call(Goal), call(Goal),
'$reset_cont_marker', '$reset_cont_marker',
@@ -11,7 +13,7 @@ shift(Ball) :-
get_chunks(E, P, L), get_chunks(E, P, L),
( L == [] -> ( L == [] ->
Cont = cont(true) Cont = cont(true)
; Cont = cont(call_continuation(L)) ; Cont = cont(cont:call_continuation(L))
), ),
'$write_cont_and_term'(_, _, Cont, Ball), '$write_cont_and_term'(_, _, Cont, Ball),
'$unwind_environments'. '$unwind_environments'.

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

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

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

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

View File

@@ -1,46 +1,80 @@
:- module(dcgs, [op(1200, xfx, -->), /** Support for Definite Clause Grammars.
op(1105, xfy, '|'),
A Prolog definite clause grammar (DCG) describes a sequence. Operationally, DCGs
can be used to parse, generate, complete and check sequences manifested as lists.
Check [The Power of Prolog chapter on DCGs](https://www.metalevel.at/prolog/dcg)
to learn more about them.
*/
:- module(dcgs,
[op(1105, xfy, '|'),
phrase/2, phrase/2,
phrase/3]). phrase/3,
seq//1,
seqq//1,
... //0
]).
:- use_module(library(error)). :- use_module(library(error)).
:- use_module(library(lists), [append/3]). :- use_module(library(iso_ext)).
:- use_module(library(lists), [append/3, member/2]).
:- use_module(library(loader), [strip_module/3]).
:- meta_predicate phrase(2, ?).
:- meta_predicate phrase(2, ?, ?).
%% phrase(+Body, ?Ls).
%
% True iff Body describes the list Ls. Body must be a DCG body.
% It is equivalent to `phrase(Body, Ls, [])`.
%
% Examples:
%
% ```
% as --> [].
% as --> [a], as.
%
% ?- phrase(as, Ls).
% Ls = []
% ; Ls = "a"
% ; Ls = "aa"
% ; Ls = "aaa"
% ; ... .
%
% ?- phrase(as, "aaa").
% true.
% ```
phrase(GRBody, S0) :- phrase(GRBody, S0) :-
phrase(GRBody, S0, []). phrase(GRBody, S0, []).
%% phrase(+Body, ?Ls, ?Ls0).
%
% True iff Body describes part of the list Ls and the rest of Ls is Ls0.
%
% Example:
%
% ```
% ?- phrase(seq(X), "aaa", Y).
% X = [], Y = "aaa"
% ; X = "a", Y = "aa"
% ; X = "aa", Y = "a"
% ; X = "aaa", Y = [].
% ```
phrase(GRBody, S0, S) :- phrase(GRBody, S0, S) :-
( var(GRBody) -> throw(error(instantiation_error, phrase/3)) strip_module(GRBody, M, GRBody1),
; dcg_constr(GRBody) -> phrase_(GRBody, S0, S) ( var(GRBody) ->
; functor(GRBody, _, _) -> call(GRBody, S0, S) instantiation_error(phrase/3)
; throw(error(type_error(callable, GRBody), phrase/3)) ; nonvar(GRBody1),
dcg_constr(GRBody1),
dcg_body(GRBody1, S0, S, GRBody2) ->
call(M:GRBody2)
; call(M:GRBody1, S0, S)
). ).
phrase_([], S, S).
phrase_(!, S, S).
phrase_((A, B), S0, S) :-
phrase(A, S0, S1), phrase(B, S1, S).
phrase_((A -> B ; C), S0, S) :-
!,
( phrase(A, S0, S1) ->
phrase(B, S1, S)
; phrase(C, S0, S)
).
phrase_((A ; B), S0, S) :-
( phrase(A, S0, S) ; phrase(B, S0, S) ).
phrase_((A | B), S0, S) :-
( phrase(A, S0, S) ; phrase(B, S0, S) ).
phrase_({G}, S0, S) :-
( call(G), S0 = S ).
phrase_(call(G), S0, S) :-
call(G, S0, S).
phrase_((A -> B), S0, S) :-
phrase((A -> B ; fail), S0, S).
phrase_(phrase(NonTerminal), S0, S) :-
phrase(NonTerminal, S0, S).
phrase_([T|Ts], S0, S) :-
append([T|Ts], S, S0).
% The same version of the below two dcg_rule clauses, but with module scoping. % The same version of the below two dcg_rule clauses, but with module scoping.
dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :- dcg_rule(( M:NonTerminal, Terminals --> GRBody ), ( M:Head :- Body )) :-
dcg_non_terminal(NonTerminal, S0, S, Head), dcg_non_terminal(NonTerminal, S0, S, Head),
@@ -79,12 +113,17 @@ dcg_body(GRBody, S0, S, Body) :-
nonvar(GRBody), nonvar(GRBody),
dcg_constr(GRBody), dcg_constr(GRBody),
dcg_cbody(GRBody, S0, S, Body). dcg_cbody(GRBody, S0, S, Body).
dcg_body(NonTerminal, S0, S, Goal) :- dcg_body(NonTerminal, S0, S, Goal1) :-
nonvar(NonTerminal), nonvar(NonTerminal),
\+ dcg_constr(NonTerminal), \+ dcg_constr(NonTerminal),
NonTerminal \= ( _ -> _ ), NonTerminal \= ( _ -> _ ),
NonTerminal \= ( \+ _ ), NonTerminal \= ( \+ _ ),
dcg_non_terminal(NonTerminal, S0, S, Goal). loader:strip_module(NonTerminal, M, NonTerminal0),
dcg_non_terminal(NonTerminal0, S0, S, Goal0),
( functor(NonTerminal, (:), 2) ->
Goal1 = M:Goal0
; Goal1 = Goal0
).
% The following constructs in a grammar rule body % The following constructs in a grammar rule body
% are defined in the corresponding subclauses. % are defined in the corresponding subclauses.
@@ -131,5 +170,50 @@ dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :-
user:term_expansion(Term0, Term) :- user:term_expansion(Term0, Term) :-
nonvar(Term0), nonvar(Term0),
dcg_rule(Term0, (Head :- Body)), dcg_rule(Term0, Term).
Term = (Head :- Body).
%% seq(Seq)//
%
% Describes a sequence
seq(Xs, Cs0,Cs) :-
var(Xs),
Cs0 == [],
!,
Xs = [],
Cs0 = Cs.
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
%% seqq(SeqOfSeqs)//
%
% Describes a sequence of sequences
seqq([]) --> [].
seqq([Es|Ess]) --> seq(Es), seqq(Ess).
%% ...//
%
% Describes an arbitrary number of elements
...(Cs0,Cs) :-
Cs0 == [],
!,
Cs0 = Cs.
... --> [] | [_], ... .
error_goal(error(E, must_be/2), error(E, must_be/2)).
error_goal(error(E, (=..)/2), error(E, (=..)/2)).
error_goal(E, _) :- throw(E).
user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
loader:strip_module(GRBody, M, GRBody0),
nonvar(GRBody0),
catch(dcgs:dcg_body(GRBody0, S, S0, GRBody1),
E,
dcgs:error_goal(E, GRBody1)
),
( GRBody = (_:_) ->
GRBody2 = M:GRBody1
; GRBody2 = GRBody1
).
user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])).

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

@@ -0,0 +1,57 @@
/** Declarative debugging.
This library provides three predicates with associated operators.
The operators can be placed in front of goals to debug Prolog
programs.
Of these predicates, the most frequently used is `(*)/1`, with
associated prefix operator `*` (star). Placing `*` in front of a
goal means to _generalize away_ the goal. `* Goal` acts as if `Goal`
did not appear at all in the source code. It is declaratively
equivalent to _commenting out_ the goal, and easier to write,
because `*` can also be placed in front of the last goal in a clause
without any additional changes.
Source: [https://stackoverflow.com/a/30791637](https://stackoverflow.com/a/30791637)
*/
:- module(debug, [
op(900, fx, $),
op(900, fx, $-),
op(950, fy, *),
(*)/1,
($)/1,
($-)/1
]).
:- use_module(library(format), [portray_clause/1]).
:- meta_predicate *(0).
:- meta_predicate $(0).
:- meta_predicate $-(0).
%% $-(Goal)
%
% Portray exceptions thrown by Goal.
$-(G_0) :-
catch(G_0, Ex, ( portray_clause(exception:Ex:G_0), throw(Ex) ) ).
%% $(Goal)
%
% Provide a _trace_ for calls of Goal.
$(G_0) :-
portray_clause(call:G_0),
$-G_0,
portray_clause(exit:G_0).
%% *(Goal)
%
% Generalize away Goal.
*(_).

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

@@ -0,0 +1,187 @@
:- module(diag, [wam_instructions/2, inlined_instructions/2]).
/** Diagnostics library
The predicate `wam_instructions/2` _decompiles_ a predicate so that
we can inspect its Warren Abstract Machine (WAM) instructions.
In this way, we can verify and reason about compiled programs,
and detect opportunities for optimization.
For example, we have:
```
?- use_module(library(lists)).
true.
?- use_module(library(diag)).
true.
?- use_module(library(format)).
true.
?- wam_instructions(append/3, Is),
maplist(portray_clause, Is).
switch_on_term(1,external(1),external(2),external(6),fail).
try_me_else(4).
get_constant(level(shallow),[],x(1)).
get_value(x(2),3).
proceed.
trust_me(0).
get_list(level(shallow),x(1)).
unify_variable(x(4)).
unify_variable(x(1)).
get_list(level(shallow),x(3)).
unify_value(x(4)).
unify_variable(x(3)).
execute(append,3).
Is = [switch_on_term(1,external(1),external(2),external(6),fail)|...].
```
`inlined_instructions/2` decompiles predicates at the code offset in
its first argument.
For example, given the program
```
?- [user].
:- use_module(library(clpz)).
all_eq(Vs, E) :- maplist(#=(E), Vs).
```
we inspect the code of `all_eqs/2` using `wam_instructions/2`,
revealing:
```
?- wam_instructions(all_eq/2, Is),
maplist(portray_clause, Is).
put_structure('$aux',2,x(3)).
set_local_value(x(2)).
set_void(1).
set_constant('$index_ptr'(115334)).
get_variable(x(4),1).
put_structure(:,2,x(1)).
set_constant(user).
set_local_value(x(3)).
get_variable(x(5),2).
put_value(x(4),2).
execute(maplist,2).
Is = [put_structure('$aux',2,x(3)),set_local_value(x(2)),set_void(1),set_constant('$index_ptr'(115334)),get_variable(x(4),1),put_structure(:,2,x(1)),set_constant(user),set_local_value(x(3)),get_variable(x(5),2),put_value(x(4),2),execute(maplist,2)].
```
The `'$index_ptr(115334)` functor gives a code offset to an inlined
predicate compiled for the use of maplist/2. `inlined_instructions/2`
can be used to decompile its source code:
```
?- inlined_instructions(115334, Is),
maplist(portray_clause, Is).
allocate(1).
get_level(y(1)).
get_variable(x(5),2).
put_value(x(3),2).
get_variable(x(6),3).
put_value(x(5),3).
put_unsafe_value(1,4).
deallocate.
jmp_by_execute(1).
try_me_else(8).
call(integer,1).
neck_cut.
get_variable(x(5),1).
put_value(x(2),1).
get_variable(x(6),2).
put_value(x(5),2).
jmp_by_execute(7).
try_me_else(12).
allocate(3).
get_level(y(1)).
get_variable(y(3),1).
get_variable(y(2),2).
call_default(true,0).
call(var,1).
cut(y(1)).
put_unsafe_value(3,1).
put_unsafe_value(2,2).
deallocate.
execute_default(is,2).
default_retry_me_else(4).
call(integer,1).
neck_cut.
execute(=:=,2).
default_trust_me(0).
allocate(2).
get_variable(y(1),1).
get_variable(y(2),3).
put_value(y(2),1).
call_default(is,2).
put_unsafe_value(2,1).
put_unsafe_value(1,2).
deallocate.
execute_default(clpz_equal,2).
default_retry_me_else(4).
call(integer,1).
neck_cut.
jmp_by_execute(29).
try_me_else(12).
allocate(3).
get_level(y(1)).
get_variable(y(3),1).
get_variable(y(2),2).
call_default(true,0).
call(var,1).
cut(y(1)).
put_unsafe_value(3,1).
put_unsafe_value(2,2).
deallocate.
execute_default(is,2).
default_trust_me(0).
allocate(2).
get_variable(y(2),1).
get_variable(y(1),3).
put_value(y(1),1).
call_default(is,2).
put_unsafe_value(2,1).
put_unsafe_value(1,2).
deallocate.
execute_default(clpz_equal,2).
default_trust_me(0).
execute_default(clpz_equal,2).
Is = [allocate(1),get_level(y(1)),get_variable(x(5),2),put_value(x(3),2),get_variable(x(6),3),put_value(x(5),3),put_unsafe_value(1,4),deallocate,jmp_by_execute(1),try_me_else(8),call(integer,1),neck_cut,get_variable(x(5),1),put_value(x(2),1),get_variable(x(6),2),put_value(x(5),2),jmp_by_execute(7),try_me_else(12),allocate(3),get_level(...),...].
```
*/
:- use_module(library(error)).
%% wam_instructions(+PI, -Instrs)
%
% _Instrs_ are the WAM instructions corresponding to predicate indicator _PI_.
wam_instructions(Clause, Listing) :-
( nonvar(Clause) ->
( Clause = Name / Arity ->
fetch_instructions(user, Name, Arity, Listing)
; Clause = Module : (Name / Arity) ->
fetch_instructions(Module, Name, Arity, Listing)
)
; throw(error(instantiation_error, wam_instructions/2))
).
%% inlined_instructions(+IndexPtr, -Instrs)
%
% _Instrs_ are the WAM instructions corresponding to code offset _IndexPtr_.
inlined_instructions(IndexPtr, Listing) :-
must_be(integer, IndexPtr),
( IndexPtr >= 0 ->
'$inlined_instructions'(IndexPtr, Listing)
; throw(error(domain_error(not_less_than_zero, IndexPtr), inlined_instructions/2))
).
fetch_instructions(Module, Name, Arity, Listing) :-
must_be(atom, Module),
must_be(atom, Name),
must_be(integer, Arity),
( Arity >= 0 ->
'$wam_instructions'(Module, Name, Arity, Listing)
; throw(error(domain_error(not_less_than_zero, Arity), wam_instructions/2))
).

78
src/lib/dif.pl Normal file
View File

@@ -0,0 +1,78 @@
/**
Provides predicate `dif/2`. `dif/2` is a constraint that is true only if both of its
arguments are different terms.
*/
:- module(dif, [dif/2]).
:- use_module(library(atts)).
:- use_module(library(dcgs)).
:- use_module(library(lists), [append/3]).
:- attribute dif/1.
put_dif_att(Var, X, Y) :-
( get_atts(Var, +dif(Z)) ->
sort([X \== Y | Z], NewZ),
put_atts(Var, +dif(NewZ))
; put_atts(Var, +dif([X \== Y]))
).
dif_set_variables([], _, _).
dif_set_variables([Var|Vars], X, Y) :-
put_dif_att(Var, X, Y),
dif_set_variables(Vars, X, Y).
append_goals([], _).
append_goals([Var|Vars], Goals) :-
( get_atts(Var, +dif(VarGoals)) ->
append(Goals, VarGoals, NewGoals0),
sort(NewGoals0, NewGoals)
; NewGoals = Goals
),
put_atts(Var, +dif(NewGoals)),
append_goals(Vars, Goals).
verify_attributes(Var, Value, Goals) :-
( get_atts(Var, +dif(Goals)) ->
term_variables(Value, ValueVars),
append_goals(ValueVars, Goals)
; Goals = []
).
%% dif(?X, ?Y).
%
% True iff X and Y are different terms. Unlike `\=/2`, `dif/2` is more declarative because if X and Y can
% unify but they're not yet equal, the decision is delayed, and prevents X and Y to become equal later.
% Examples:
%
% ```
% ?- dif(a, a).
% false.
% ?- dif(a, b).
% true.
% ?- dif(X, b).
% dif:dif(X,b).
% ?- dif(X, b), X = b.
% false.
% ```
dif(X, Y) :-
X \== Y,
( X \= Y -> true
; term_variables(dif(X,Y), Vars),
dif_set_variables(Vars, X, Y)
).
gather_dif_goals(_, []) --> [].
gather_dif_goals(V, [(X \== Y) | Goals]) -->
( { term_variables(X-Y, [V0 | _]),
V == V0 } ->
[dif:dif(X, Y)]
; []
),
gather_dif_goals(V, Goals).
attribute_goals(X) -->
{ get_atts(X, +dif(Goals)) },
gather_dif_goals(X, Goals),
{ put_atts(X, -dif(_)) }.

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

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

104
src/lib/ffi.pl Normal file
View File

@@ -0,0 +1,104 @@
:- module(ffi, [use_foreign_module/2, foreign_struct/2]).
/** Foreign Function Interface
This module contains predicates used to call native code (exposed by the C ABI).
It uses [libffi](https://sourceware.org/libffi/) under the hood. The bridge is very simple
and is very unsafe and should be used with care. FFI isn't the only way to communicate with
the outside world in Prolog: sockets, pipes and HTTP may be good enough for your use case.
The main predicate is `use_foreign_module/2`. It takes a library name (which depending on the
operating system could be a `.so`, `.dylib` or `.dll` file). and a list of functions. Each
function is defined by its name, a list of the type of the arguments, and the return argument.
Types available are: `sint8`, `uint8`, `sint16`, `uint16`, `sint32`, `uint32`, `sint64`,
`uint64`, `f32`, `f64`, `cstr`, `void`, `bool`, `ptr` and custom structs, which can be defined
with `foreign_struct/2`.
After that, each function on the lists maps to a predicate created in the ffi module which
are used to call the native code.
The predicate takes the functor name after the function name. Then, the arguments are the input
arguments followed by a return argument. However, functions with return type `void` or `bool`
don't have that return argument. Predicates with `void` always succeed and `bool` predicates depend
on the return value on the native side.
```
ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN, -ReturnArg). % for all return types except void and bool
ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN). % for void and bool
```
## Example
For example, let's see how to define a function from the [raylib](https://www.raylib.com/) library.
```
?- use_foreign_module("./libraylib.so", ['InitWindow'([sint32, sint32, cstr], void)]).
```
This creates a `'InitWindow'` predicate under the ffi module. Now, we can call it:
```
?- ffi:'InitWindow'(800, 600, "Scryer Prolog + Raylib").
```
And a new window should pop up!
*/
:- use_module(library(lists)).
:- use_module(library(error)).
%% foreign_struct(+Name, +Elements).
%
% Defines a new struct type with name Name, composed of the elements Elements, which is a list
% of other types.
%
% The name of the types doesn't matter, but the order of Elements must match the ones in the
% native code.
%
% Example:
%
% ```
% ?- foreign_struct(color, [uint8, uint8, uint8, uint8]).
% ```
foreign_struct(Name, Elements) :-
'$define_foreign_struct'(Name, Elements).
use_foreign_module(LibName, Predicates) :-
'$load_foreign_lib'(LibName, Predicates),
maplist(assert_predicate, Predicates).
assert_predicate(PredicateDefinition) :-
PredicateDefinition =.. [Name, Inputs, void],
length(Inputs, NumInputs),
functor(Head, Name, NumInputs),
term_variables(Head, TermList),
Body = (
'$foreign_call'(Name, TermList, _),!
),
Predicate = (Head:-Body),
assertz(ffi:Predicate).
assert_predicate(PredicateDefinition) :-
PredicateDefinition =.. [Name, Inputs, bool],
length(Inputs, NumInputs),
functor(Head, Name, NumInputs),
term_variables(Head, TermList),
Body = (
'$foreign_call'(Name, TermList, 1),!
),
Predicate = (Head:-Body),
assertz(ffi:Predicate).
assert_predicate(PredicateDefinition) :-
PredicateDefinition =.. [Name, Inputs, Return],
\+ member(Return, [void, bool]),
length(Inputs, NumInputs),
NumArgs is NumInputs + 1,
functor(Head, Name, NumArgs),
term_variables(Head, TermList),
Body = (
lists:append(TermListInputs, [TermListReturn], TermList),
'$foreign_call'(Name, TermListInputs, TermListReturn),!
),
Predicate = (Head:-Body),
assertz(ffi:Predicate).

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

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

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

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

View File

@@ -1,8 +1,13 @@
:- module(freeze, [freeze/2]). :- module(freeze, [freeze/2]).
/** Provides the constraint `freeze/2`.
*/
:- use_module(library(atts)). :- use_module(library(atts)).
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
:- meta_predicate freeze(-, 0).
:- attribute frozen/1. :- attribute frozen/1.
verify_attributes(Var, Other, Goals) :- verify_attributes(Var, Other, Goals) :-
@@ -17,6 +22,15 @@ verify_attributes(Var, Other, Goals) :-
). ).
verify_attributes(_, _, []). verify_attributes(_, _, []).
%% freeze(Var, Goal)
%
% Schedules Goal to be executed when Var is instantiated. This can
% be useful to observe the exact moment a variable becomes bound to a
% more concrete term, for example when creating animations of search
% processes. Higher-level constructs such as `phrase_from_file/2` can
% also be implemented with `freeze/2`, by scheduling a goal that
% reads additional data from a file as soon as it is needed.
freeze(X, Goal) :- freeze(X, Goal) :-
put_atts(Fresh, frozen(Goal)), put_atts(Fresh, frozen(Goal)),
Fresh = X. Fresh = X.
@@ -24,5 +38,5 @@ freeze(X, Goal) :-
attribute_goals(Var) --> attribute_goals(Var) -->
{ get_atts(Var, frozen(Goals)), { get_atts(Var, frozen(Goals)),
put_atts(Var, -frozen(_)) }, put_atts(Var, -frozen(_)) },
[freeze(Var, Goals)]. [freeze:freeze(Var, Goals)].

View File

@@ -19,14 +19,14 @@ gensym(Base, Unique) :-
must_be(var, Unique), must_be(var, Unique),
atom_si(Base), atom_si(Base),
gensym_key(Base, BaseKey), gensym_key(Base, BaseKey),
( bb_get(BaseKey, UniqueID0) -> ( bb_get(BaseKey, UniqueID0) -> true
; UniqueID0 = 0
),
UniqueID is UniqueID0 + 1, UniqueID is UniqueID0 + 1,
bb_put(BaseKey, UniqueID), append_id(Base, UniqueID, Unique),
append_id(Base, UniqueID, Unique) bb_put(BaseKey, UniqueID).
; bb_put(BaseKey, 1),
append_id(Base, 1, Unique)
).
reset_gensym(Base) :- reset_gensym(Base) :-
atom_si(Base), atom_si(Base),
bb_put(Base, 0). gensym_key(Base, BaseKey),
bb_put(BaseKey, 0).

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

@@ -0,0 +1,73 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written 2022 by Adrián Arroyo Calle (adrian.arroyocalle@gmail.com)
Part of Scryer Prolog.
*/
/** Make HTTP requests.
This library contains the predicate `http_open/3` which allows you to perform HTTP(S) calls.
Useful for making API calls, or parsing websites. It uses Hyper underneath.
*/
:- module(http_open, [http_open/3]).
:- use_module(library(lists)).
%% http_open(+Address, -Stream, +Options).
%
% Yields Stream to read the body of an HTTP reply from Address.
% Address is a list of characters, and includes the method. Both HTTP
% and HTTPS are supported.
%
% Options supported:
%
% * `method(+Method)`: Sets the HTTP method of the call. Method can be `get` (default), `head`, `delete`, `post`, `put` or `patch`.
% * `data(+Data)`: Data to be sent in the request. Useful for POST, PUT and PATCH operations.
% * `size(-Size)`: Unifies with the value of the Content-Length header
% * `request_headers(+RequestHeaders)`: Headers to be used in the request
% * `headers(-ListHeaders)`: Unifies with a list with all headers returned in the response
% * `status_code(-Code)`: Unifies with the status code of the request (200, 201, 404, ...)
%
% Example:
%
% ```
% ?- http_open("https://www.example.com", S, []), get_n_chars(S, N, HTML).
% S = '$stream'(0x7fb548001be8), N = 1256, HTML = "<!doctype html>\n<ht ...".
% ```
http_open(Address, Response, Options) :-
parse_http_options(Options, OptionValues),
( member(method(Method), OptionValues) -> true; Method = get),
( member(data(Data), OptionValues) -> true; Data = []),
( member(request_headers(RequestHeaders), OptionValues) -> true; RequestHeaders = ['user-agent'("Scryer Prolog")]),
( member(status_code(Code), OptionValues) -> true; true),
( member(headers(Headers), OptionValues) -> true; true),
( member(size(Size), OptionValues) -> member('content-length'(Size), Headers); true),
'$http_open'(Address, Response, Method, Code, Data, Headers, RequestHeaders).
parse_http_options(Options, OptionValues) :-
maplist(parse_http_options_, Options, OptionValues).
parse_http_options_(method(Method), method(Method)) :-
( var(Method) ->
throw(error(instantiation_error, http_open/3))
;
member(Method, [get, post, put, delete, patch, head]) -> true
;
throw(error(domain_error(http_option, method(Method)), _))
).
parse_http_options_(data(Data), data(Data)) :-
( var(Data) ->
throw(error(instantiation_error, http_open/3))
; true
).
parse_http_options_(request_headers(Headers), request_headers(Headers)) :-
( var(Headers) ->
throw(error(instantiation_error, http_open/3))
; true
).
parse_http_options_(size(Size), size(Size)).
parse_http_options_(status_code(Code), status_code(Code)).
parse_http_options_(headers(Headers), headers(Headers)).

354
src/lib/http/http_server.pl Normal file
View File

@@ -0,0 +1,354 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written in December 2020 by Adrián Arroyo (adrian.arroyocalle@gmail.com)
Updated in March 2022 by Adrián Arroyo to use the Hyper backend
Part of Scryer Prolog.
I place this code in the public domain. Use it in any way you want.
*/
/** This library provides an starting point to build HTTP server based applications.
It is based on [Hyper](https://hyper.rs/), which allows for HTTP/1.0, HTTP/1.1 and HTTP/2. However,
some advanced features that Hyper provides are still not accesible.
## Usage
The main predicate of the library is `http_listen/2`, which needs a port number
(usually 80) and a list of handlers. A handler is a compound term with the functor
as one HTTP method (in lowercase) and followed by a Route Match and a predicate
which will handle the call.
```
text_handler(Request, Response) :-
http_status_code(Response, 200),
http_body(Response, text("Welcome to Scryer Prolog!")).
parameter_handler(User, Request, Response) :-
http_body(Response, text(User)).
http_listen(7890, [
get(echo, text_handler), % GET /echo
post(user/User, parameter_handler(User)) % POST /user/<User>
]).
```
Every handler predicate will have at least 2-arity, with Request and Response.
Although you can work directly with `http_request` and `http_response` terms, it is
recommeded to use the helper predicates, which are easier to understand and cleaner:
- `http_headers(Response/Request, Headers)`
- `http_status_code(Responde, StatusCode)`
- `http_body(Response/Request, text(Body))`
- `http_body(Response/Request, binary(Body))`
- `http_body(Request, form(Form))`
- `http_body(Response, file(Filename))`
- `http_redirect(Response, Url)`
- `http_query(Request, QueryName, QueryValue)`
Some things that are still missing:
- Read forms in multipart format
- HTTP Basic Auth
- Session handling via cookies
- HTML Templating (but you can use [Teruel](https://github.com/aarroyoc/teruel/), [Marquete](https://github.com/aarroyoc/marquete/) or [Djota](https://github.com/aarroyoc/djota) for that)
*/
:- module(http_server, [
http_listen/2,
http_headers/2,
http_status_code/2,
http_body/2,
http_redirect/2,
http_query/3
]).
:- meta_predicate http_listen(?, :).
:- use_module(library(charsio)).
:- use_module(library(crypto)).
:- use_module(library(error)).
:- use_module(library(format)).
:- use_module(library(iso_ext)).
:- use_module(library(lists)).
:- use_module(library(pio)).
:- use_module(library(time)).
%% http_listen(+Port, +Handlers).
%
% Listens for HTTP connections on port Port. Each handler on the list Handlers should be of the form: `HttpVerb(PathUnification, Predicate)`.
% For example: `get(user/User, get_info(User))` will match an HTTP request that is a GET, the path unifies with /user/User (where User is a variable)
% and it will call `get_info` with three arguments: an `http_request` term, an `http_response` term and User.
http_listen(Port, Module:Handlers0) :-
must_be(integer, Port),
must_be(list, Handlers0),
maplist(module_qualification(Module), Handlers0, Handlers),
http_listen_(Port, Handlers).
module_qualification(M, H0, H) :-
H0 =.. [Method, Path, Goal],
H =.. [Method, Path, M:Goal].
http_listen_(Port, Handlers) :-
phrase(format_("0.0.0.0:~d", [Port]), Addr),
'$http_listen'(Addr, HttpListener),!,
format("Listening at ~s\n", [Addr]),
http_loop(HttpListener, Handlers).
http_loop(HttpListener, Handlers) :-
'$http_accept'(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle),
current_time(Time),
phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString),
format("~s ~w ~s\n", [TimeString, RequestMethod, RequestPath]),
maplist(map_header_kv, RequestHeaders, RequestHeadersKV),
phrase(parse_queries(RequestQueries), RequestQuery),
(
match_handler(Handlers, RequestMethod, RequestPath, Handler) ->
(
HttpRequest = http_request(RequestHeadersKV, stream(RequestStream), RequestQueries),
HttpResponse = http_response(_, _, _),
(call(Handler, HttpRequest, HttpResponse) ->
send_response(ResponseHandle, HttpResponse)
; (
'$http_answer'(ResponseHandle, 500, [], ResponseStream),
call_cleanup(format(ResponseStream, "Internal Server Error", []), close(ResponseStream)))
)
)
; (
'$http_answer'(ResponseHandle, 404, [], ResponseStream),
call_cleanup(format(ResponseStream, "Not Found"), close(ResponseStream)))
),
http_loop(HttpListener, Handlers).
send_response(ResponseHandle, http_response(StatusCode0, text(ResponseText), ResponseHeaders0)) :-
default(StatusCode0, 200, StatusCode),
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
'$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream0),
open(stream(ResponseStream0), write, ResponseStream, [type(text)]),
catch(
call_cleanup(format(ResponseStream, "~s", [ResponseText]),close(ResponseStream)),
error(existence_error(stream, _), _),
true
).
send_response(ResponseHandle, http_response(StatusCode0, bytes(ResponseBytes), ResponseHeaders0)) :-
default(StatusCode0, 200, StatusCode),
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
'$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
catch(
call_cleanup(format(ResponseStream, "~s", [ResponseBytes]),close(ResponseStream)),
error(existence_error(stream, _), _),
true
).
send_response(ResponseHandle, http_response(StatusCode0, file(Filename), ResponseHeaders0)) :-
default(StatusCode0, 200, StatusCode),
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
'$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
catch(
call_cleanup(
setup_call_cleanup(
open(Filename, read, FileStream, [type(binary)]),
(
get_n_chars(FileStream, _, FileCs),
format(ResponseStream, "~s", [FileCs])
),
close(FileStream)
),
close(ResponseStream)
),
error(existence_error(stream, _), _),
true
).
default(Var, Default, Out) :-
(var(Var) -> Out = Default
; Var = Out
).
map_header_kv(T, K-V) :-
T =.. [K0, V],
atom_chars(K0, K).
map_header_kv_2(T, K-V) :-
atom_chars(K0, K),
T =.. [K0, V].
match_handler(Handlers, Method, "/", Handler) :-
member(H, Handlers),
H =.. [Method, /, Handler].
match_handler(Handlers, Method, Path, Handler) :-
member(H, Handlers),
copy_term(H, H1),
H1 =.. [Method, Pattern, Handler],
\+ var(Pattern),
phrase(path(Pattern), Path).
match_handler(Handlers, Method, Path, Handler) :-
member(H, Handlers),
copy_term(H, H1),
H1 =.. [Method, Var, Handler],
var(Var),
Var = Path.
path(Pattern) -->
{
Pattern =.. Parts,
length(Parts, 3),
nth0(1, Parts, Pattern0),
nth0(2, Parts, PartAtom),
(var(PartAtom) -> Part = PartAtom; atom_chars(PartAtom, Part))
},
path(Pattern0),
"/",
string_without("/", Part).
path(Pattern) -->
{
Pattern =.. Parts,
Parts = [PartAtom],
(var(PartAtom) -> Part = PartAtom; atom_chars(PartAtom, Part))
},
"/",
string_without("/", Part).
path([]) --> [].
string_without(Not, [Char|String]) -->
[Char],
{
\+ member(Char, Not)
},
string_without(Not, String).
string_without(_, []) -->
[].
%% http_headers(?Request_Response, ?Headers).
%
% True iff `Request_Response` is a request or response with headers Headers. Can be used both to get headers (usually in from a request)
% and to add headers (usually in a response).
http_headers(http_request(Headers, _, _), Headers).
http_headers(http_response(_, _, Headers), Headers).
%% http_body(?Request_Response, ?Body).
%
% True iff Body is the body of the request or response. A body can be of the following types:
%
% * `bytes(Bytes)` for both requests and responses, interprets the body as bytes
% * `text(Bytes)` for both requests and responses, interprets the body as text
% * `form(Form)` only for requests, interprets the body as an `application/x-www-form-urlencoded` form.
% * `file(File)` only for responses, interprets the body as the content of a file (useful to send static files).
http_body(http_request(_, stream(StreamBody), _), bytes(BytesBody)) :- get_n_chars(StreamBody, _, BytesBody).
http_body(http_request(_, stream(StreamBody), _), text(TextBody)) :- get_n_chars(StreamBody, _, TextBody).
http_body(http_request(Headers, stream(StreamBody), _), form(FormBody)) :-
member("content-type"-"application/x-www-form-urlencoded", Headers),
get_n_chars(StreamBody, _, TextBody),
phrase(parse_queries(FormBody), TextBody).
http_body(http_request(_, Body, _), Body).
http_body(http_response(_, Body, _), Body).
%% http_status_code(?Response, ?StatusCode).
%
% True iff the status code of the response Response unifies with StatusCode.
http_status_code(http_response(StatusCode, _, _), StatusCode).
%% http_redirect(-Response, +Uri).
%
% True iff Response is a response that redirects the user to the uri Uri.
http_redirect(http_response(307, text("Moved Temporarily"), ["Location"-Uri]), Uri).
%% http_query(+Request, ?Key, ?Value).
%
% True iff there's a query in request Request with key Key and value Value.
http_query(http_request(_, _, Queries), Key, Value) :- member(Key-Value, Queries).
parse_queries([Key-Value|Queries]) -->
string_without("=", Key0),
{
phrase(url_decode(Key), Key0)
},
"=",
string_without("&", Value0),
{
phrase(url_decode(Value), Value0)
},
"&",
parse_queries(Queries).
parse_queries([Key-Value]) -->
string_without("=", Key0),
{
phrase(url_decode(Key), Key0)
},
"=",
string_without(" ", Value0),
{
phrase(url_decode(Value), Value0)
}.
parse_queries([]) -->
[].
% Decodes a UTF-8 URL Encoded string: RFC-1738
url_decode([Char|Chars]) -->
[Char],
{
Char \= '%'
},
url_decode(Chars).
url_decode([Char|Chars]) -->
"%",
[A],
[B],
{
hex_bytes([A,B], Bytes),
Bytes = [FirstByte|_],
FirstByte < 128,
chars_utf8bytes(Chars0, Bytes),
Chars0 = [Char]
},
url_decode(Chars).
url_decode([Char|Chars]) -->
"%",
[A, B],
"%",
[C, D],
{
hex_bytes([A,B,C,D], Bytes),
Bytes = [FirstByte|_],
FirstByte < 224,
chars_utf8bytes(Chars0, Bytes),
Chars0 = [Char]
},
url_decode(Chars).
url_decode([Char|Chars]) -->
"%",
[A, B],
"%",
[C, D],
"%",
[E, F],
{
hex_bytes([A,B,C,D,E,F], Bytes),
Bytes = [FirstByte|_],
FirstByte < 240,
chars_utf8bytes(Chars0, Bytes),
Chars0 = [Char]
},
url_decode(Chars).
url_decode([Char|Chars]) -->
"%",
[A, B],
"%",
[C, D],
"%",
[E, F],
"%",
[H, I],
{
hex_bytes([A,B,C,D,E,F,H,I], Bytes),
chars_utf8bytes(Chars0, Bytes),
Chars0 = [Char]
},
url_decode(Chars).
url_decode([]) --> [].

415
src/lib/iso_ext.pl Normal file
View File

@@ -0,0 +1,415 @@
/** Useful general predicates that are not ISO standard yet
Predicates available here are similar to the ones defined in builtin.pl,
but they're not part of the ISO Prolog standard at the moment.
*/
:- 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,
succ/2,
call_nth/2,
countall/2,
copy_term_nat/2,
asserta/2,
assertz/2]).
:- use_module(library(error), [can_be/2,
domain_error/3,
instantiation_error/1,
type_error/3]).
:- use_module(library(lists), [maplist/3]).
:- meta_predicate(forall(0, 0)).
%% forall(Generate, Test).
%
% For all bindings possible by Generate, Test must be true.
%
% In this example, it checks that all numbers are even:
%
% ```
% ?- Ns = [2,4,6], forall(member(N, Ns), 0 is N mod 2).
% Ns = [2,4,6].
% ```
forall(Generate, Test) :-
\+ (Generate, \+ Test).
% (non-)backtrackable global variables.
%% bb_put(+Key, +Value).
%
% Sets a global variable named Key (must be an atom) with value Value.
% The global variable isn't backtrackable. Check `bb_b_put/2` for the
% backtrackable version.
%
% ```
% ?- bb_put(city, "Valladolid").
% true.
% ?- bb_get(city, X).
% X = "Valladolid".
% ```
%
% In this example one can understand the difference between `bb_put/2` and
% `bb_b_put/2`:
%
% ```
% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)).
% X = "Salamanca".
% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)).
% X = "Valladolid".
% ```
bb_put(Key, Value) :-
( atom(Key) ->
'$store_global_var'(Key, Value)
; type_error(atom, Key, bb_put/2)
).
% backtrackable global variables.
%% bb_b_put(+Key, +Value).
%
% Sets a global variable named Key (must be an atom) with value Value.
% The global variable is backtrackable. Check `bb_put/2` for the
% non-backtrackable version.
%
% ```
% ?- bb_b_put(city, "Valladolid").
% true.
% ?- bb_get(city, X).
% X = "Valladolid".
% ```
%
% In this example one can understand the difference between `bb_put/2` and
% `bb_b_put/2`:
%
% ```
% ?- bb_put(city, "Valladolid"), (bb_put(city, "Salamanca"), false);(bb_get(city, X)).
% X = "Salamanca".
% ?- bb_put(city, "Valladolid"), (bb_b_put(city, "Salamanca"), false);(bb_get(city, X)).
% X = "Valladolid".
% ```
bb_b_put(Key, Value) :-
( atom(Key) ->
'$store_backtrackable_global_var'(Key, Value)
; type_error(atom, Key, bb_b_put/2)
).
%% bb_get(+Key, -Value).
%
% Gets the value Value of a global variable named Key (must be an atom)
bb_get(Key, Value) :-
( atom(Key) ->
'$fetch_global_var'(Key, Value)
; type_error(atom, Key, bb_get/2)
).
%% succ(?I, ?S).
%
% True iff S is the successor of the non-negative integer I.
% At least one of the arguments must be instantiated.
succ(I, S) :-
can_be(not_less_than_zero, I),
can_be(not_less_than_zero, S),
( integer(S) ->
S > 0,
I is S-1
; integer(I) ->
S is I+1
; instantiation_error(succ/2)
).
% setup_call_cleanup.
:- meta_predicate(call_cleanup(0, 0)).
%% call_cleanup(Goal, Cleanup).
%
% Executes Goal and then, either on success or failure, executes Cleanup.
% The success or failure of Cleanup is ignored and choice points created inside are destroyed.
call_cleanup(G, C) :- setup_call_cleanup(true, G, C).
:- meta_predicate(setup_call_cleanup(0, 0, 0)).
:- non_counted_backtracking setup_call_cleanup/3.
%% setup_call_cleanup(Setup, Goal, Cleanup).
%
% If Setup succeeds, Cleanup will be called after the execution of Goal. Goal itself can succeed or not.
%
% In this example, we use the predicate to always close an open file:
%
% ```
% ?- setup_call_cleanup(open(File, read, Stream), do_something_with_stream(Stream), close(Stream)).
% ```
setup_call_cleanup(S, G, C) :-
'$get_b_value'(B),
'$call_with_inference_counting'(call(S)),
'$set_cp_by_default'(B),
'$get_current_scc_block'(Bb),
( C = _:CC,
var(CC) ->
instantiation_error(setup_call_cleanup/3)
; scc_helper(C, G, Bb)
).
:- meta_predicate(scc_helper(?,0,?)).
:- non_counted_backtracking scc_helper/3.
scc_helper(C, G, Bb) :-
'$get_cp'(Cp),
'$install_scc_cleaner'(C),
'$call_with_inference_counting'(call(G)),
( '$check_cp'(Cp) ->
'$reset_scc_block'(Bb),
run_cleaners_without_handling(Cp)
; true
; '$fail'
).
scc_helper(_, _, Bb) :-
'$reset_scc_block'(Bb),
'$push_ball_stack',
run_cleaners_with_handling,
'$pop_from_ball_stack',
'$unwind_stack'.
scc_helper(_, _, _) :-
'$get_cp'(Cp),
run_cleaners_without_handling(Cp),
'$fail'.
:- non_counted_backtracking run_cleaners_with_handling/0.
run_cleaners_with_handling :-
'$get_scc_cleaner'(C),
'$get_cp'(B),
catch(C, _, true),
'$set_cp_by_default'(B),
run_cleaners_with_handling.
run_cleaners_with_handling :-
'$restore_cut_policy'.
:- non_counted_backtracking run_cleaners_without_handling/1.
run_cleaners_without_handling(Cp) :-
'$get_scc_cleaner'(C),
'$get_cp'(B),
call(C),
'$set_cp_by_default'(B),
run_cleaners_without_handling(Cp).
run_cleaners_without_handling(Cp) :-
'$set_cp_by_default'(Cp),
'$restore_cut_policy'.
% call_with_inference_limit
:- non_counted_backtracking end_block/4.
end_block(_, Bb, NBb, _L) :-
'$clean_up_block'(NBb),
'$reset_block'(Bb).
end_block(B, _Bb, NBb, L) :-
'$install_inference_counter'(B, L, _),
'$reset_block'(NBb),
'$fail'.
:- non_counted_backtracking handle_ile/3.
handle_ile(B, inference_limit_exceeded(B), inference_limit_exceeded) :-
!,
'$pop_ball_stack'.
handle_ile(B, _, _) :-
'$remove_call_policy_check'(B),
'$pop_from_ball_stack',
'$unwind_stack'.
:- meta_predicate(call_with_inference_limit(0, ?, ?)).
:- non_counted_backtracking call_with_inference_limit/3.
%% call_with_inference_limit(Goal, Limit, Result).
%
% Similar to `call(Goal)` but it limits the number of inferences for each solution of Goal.
call_with_inference_limit(G, L, R) :-
( integer(L) ->
( L < 0 ->
domain_error(not_less_than_zero, L, call_with_inference_limit/3)
; true
)
; var(L) ->
instantiation_error(call_with_inference_limit/3)
; type_error(integer, L, call_with_inference_limit/3)
),
'$get_current_block'(Bb),
'$get_b_value'(B),
call_with_inference_limit(G, L, R, Bb, B),
'$remove_call_policy_check'(B).
install_inference_counter(B, L, Count0) :-
'$install_inference_counter'(B, L, Count0).
:- meta_predicate(call_with_inference_limit(0,?,?,?,?)).
:- non_counted_backtracking call_with_inference_limit/5.
call_with_inference_limit(G, L, R, Bb, B) :-
'$install_new_block'(NBb),
'$install_inference_counter'(B, L, Count0),
'$call_with_inference_counting'(call(G)),
'$inference_level'(R, B),
'$remove_inference_counter'(B, Count1),
Diff is L - (Count1 - Count0),
end_block(B, Bb, NBb, Diff).
call_with_inference_limit(_, _, R, Bb, B) :-
'$reset_block'(Bb),
'$remove_inference_counter'(B, _),
( '$get_ball'(Ball),
'$push_ball_stack',
'$get_cp'(Cp),
'$set_cp_by_default'(Cp)
; '$remove_call_policy_check'(B),
'$fail'
),
handle_ile(B, Ball, R).
%% partial_string(String, L, L0)
%
% Explicitly construct a partial string "manually". It can be used as an optimized append/3.
% It's not recommended to use this predicate in application code.
partial_string(String, L, L0) :-
( String == [] ->
L = L0
; catch(atom_chars(Atom, String),
error(E, _),
throw(error(E, partial_string/3))),
'$create_partial_string'(Atom, L, L0)
).
%% partial_string(+String)
%
% Succeeds if String is a _partial string_. A partial string is a string composed of several smaller
% strings, even just one. That means all strings in Scryer are partial strings.
partial_string(String) :-
'$is_partial_string'(String).
%% partial_string_tail(+String, -Tail).
%
% Unifies Tail with the last section of the partial string.
% It's not recommended to use this predicate in application code.
partial_string_tail(String, Tail) :-
( partial_string(String) ->
'$partial_string_tail'(String, Tail)
; throw(error(type_error(partial_string, String), partial_string_tail/2))
).
:- dynamic(i_call_nth_nesting/2).
:- dynamic(i_call_nth_counter/1).
:- meta_predicate(call_nth(0, ?)).
%% call_nth(Goal, N).
%
% Succeeds when Goal succeeded for the Nth time (there are at least N solutions)
call_nth(Goal, N) :-
can_be(integer, N),
( integer(N) ->
( N < 0 ->
domain_error(not_less_than_zero, N, call_nth/2)
; N > 0
)
; true
),
setup_call_cleanup(call_nth_nesting(C, ID),
( Goal,
bb_get(ID, N0),
N1 is N0 + 1,
bb_put(ID, N1),
( integer(N) ->
N = N1,
!
; N = N1
)
),
( bb_get(i_call_nth_counter, C) ->
C1 is C - 1,
bb_put(i_call_nth_counter, C1)
; true
)).
call_nth_nesting(C, ID) :-
( bb_get(i_call_nth_counter, C0) ->
C is C0 + 1
; C = 0
),
number_chars(C, Cs),
atom_chars(Atom, Cs),
atom_concat(i_call_nth_nesting_, Atom, ID),
bb_put(ID, 0),
bb_put(i_call_nth_counter, C).
%% countall(Goal, N).
%
% countall(Goal, N) counts all solutions of Goal and unifies N with
% this number of solutions. This predicate always succeeds once.
:- meta_predicate(countall(0, ?)).
countall(Goal, N) :-
can_be(integer, N),
( integer(N) ->
( N < 0 ->
domain_error(not_less_than_zero, N, countall/2)
; N > 0
)
; true
),
setup_call_cleanup(call_nth_nesting(C, ID),
( ( Goal,
bb_get(ID, N0),
N1 is N0 + 1,
bb_put(ID, N1),
false
; bb_get(ID, N)
)
),
( bb_get(i_call_nth_counter, C) ->
C1 is C - 1,
bb_put(i_call_nth_counter, C1)
; true
)).
%% copy_term_nat(Source, Dest)
%
% Similar to `copy_term/2` but without attribute variables
copy_term_nat(Source, Dest) :-
'$copy_term_without_attr_vars'(Source, Dest).
%% asserta(Module, Rule_Fact).
%
% Similar to `asserta/1` but allows specifying a Module
asserta(Module, (Head :- Body)) :-
!,
'$asserta'(Module, Head, Body).
asserta(Module, Fact) :-
'$asserta'(Module, Fact, true).
%% assertz(Module, Rule_Fact).
%
% Similar to `assertz/1` but allows specifying a Module
assertz(Module, (Head :- Body)) :-
!,
'$assertz'(Module, Head, Body).
assertz(Module, Fact) :-
'$assertz'(Module, Fact, true).

233
src/lib/lambda.pl Normal file
View File

@@ -0,0 +1,233 @@
/*
Author: Ulrich Neumerkel
E-mail: ulrich@complang.tuwien.ac.at
Copyright (C): 2009 Ulrich Neumerkel. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY Ulrich Neumerkel ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Ulrich Neumerkel OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation
are those of the authors and should not be interpreted as representing
official policies, either expressed or implied, of Ulrich Neumerkel.
*/
:- module(lambda, [
(^)/3, (^)/4, (^)/5, (^)/6, (^)/7, (^)/8, (^)/9, (^)/10,
(\)/1, (\)/2, (\)/3, (\)/4, (\)/5, (\)/6, (\)/7, (\)/8,
(+\)/2, (+\)/3, (+\)/4, (+\)/5, (+\)/6, (+\)/7, (+\)/8,
(+\)/9, op(201,xfx,+\)]).
:- use_module(library(iso_ext)).
/** <module> Lambda expressions
This library provides lambda expressions to simplify higher order
programming based on call/N.
Lambda expressions are represented by ordinary Prolog terms.
There are two kinds of lambda expressions:
```
Free+\X1^X2^ ..^XN^Goal
\X1^X2^ ..^XN^Goal
```
The second is a shorthand for `t+\X1^X2^..^XN^Goal`.
Xi are the parameters.
Goal is a goal or continuation. Syntax note: Operators within Goal
require parentheses due to the low precedence of the ^ operator.
Free contains variables that are valid outside the scope of the lambda
expression. They are thus free variables within.
All other variables of Goal are considered local variables. They must
not appear outside the lambda expression. This restriction is
currently not checked. Violations may lead to unexpected bindings.
In the following example the parentheses around X>3 are necessary.
```
?- use_module(library(lambda)).
?- use_module(library(lists)).
?- maplist(\X^(X>3),[4,5,9]).
true.
```
In the following X is a variable that is shared by both instances of
the lambda expression. The second query illustrates the cooperation of
continuations and lambdas. The lambda expression is in this case a
continuation expecting a further argument.
```
?- use_module(library(dif)).
true.
?- Xs = [A,B], maplist(X+\Y^dif(X,Y), Xs).
Xs = [A,B], dif:dif(X,A), dif:dif(X,B).
?- Xs = [A,B], maplist(X+\dif(X), Xs).
Xs = [A,B], dif:dif(X,A), dif:dif(X,B).
```
The following queries are all equivalent. To see this, use
the fact `f(x,y)`.
```
?- call(f,A1,A2).
?- call(\X^f(X),A1,A2).
?- call(\X^Y^f(X,Y), A1,A2).
?- call(\X^(X+\Y^f(X,Y)), A1,A2).
?- call(call(f, A1),A2).
?- call(f(A1),A2).
?- f(A1,A2).
A1 = x, A2 = y.
```
Further discussions
[http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord](http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/ISO-Hiord)
@tbd Static expansion similar to apply_macros.
@author Ulrich Neumerkel
*/
:- meta_predicate ^(?,0,?).
:- meta_predicate ^(?,1,?,?).
:- meta_predicate ^(?,2,?,?,?).
:- meta_predicate ^(?,3,?,?,?,?).
:- meta_predicate ^(?,4,?,?,?,?,?).
:- meta_predicate ^(?,5,?,?,?,?,?,?).
:- meta_predicate ^(?,6,?,?,?,?,?,?,?).
:- meta_predicate ^(?,7,?,?,?,?,?,?,?,?).
:- meta_predicate \(0).
:- meta_predicate \(1,?).
:- meta_predicate \(2,?,?).
:- meta_predicate \(3,?,?,?).
:- meta_predicate \(4,?,?,?,?).
:- meta_predicate \(5,?,?,?,?,?).
:- meta_predicate \(6,?,?,?,?,?,?).
:- meta_predicate \(7,?,?,?,?,?,?,?).
:- meta_predicate +\(?,0).
:- meta_predicate +\(?,1,?).
:- meta_predicate +\(?,2,?,?).
:- meta_predicate +\(?,3,?,?,?).
:- meta_predicate +\(?,4,?,?,?,?).
:- meta_predicate +\(?,5,?,?,?,?,?).
:- meta_predicate +\(?,6,?,?,?,?,?,?).
:- meta_predicate +\(?,7,?,?,?,?,?,?,?).
:- meta_predicate no_hat_call(0).
^(V1,C_0,V1) :-
no_hat_call(C_0).
^(V1,C_1,V1,V2) :-
call(C_1,V2).
^(V1,C_2,V1,V2,V3) :-
call(C_2,V2,V3).
^(V1,C_3,V1,V2,V3,V4) :-
call(C_3,V2,V3,V4).
^(V1,C_4,V1,V2,V3,V4,V5) :-
call(C_4,V2,V3,V4,V5).
^(V1,C_5,V1,V2,V3,V4,V5,V6) :-
call(C_5,V2,V3,V4,V5,V6).
^(V1,C_6,V1,V2,V3,V4,V5,V6,V7) :-
call(C_6,V2,V3,V4,V5,V6,V7).
^(V1,C_7,V1,V2,V3,V4,V5,V6,V7,V8) :-
call(C_7,V2,V3,V4,V5,V6,V7,V8).
\(FC_0) :-
copy_term_nat(FC_0,C_0),
no_hat_call(C_0).
\(FC_1,V1) :-
copy_term_nat(FC_1,C_1),
call(C_1,V1).
\(FC_2,V1,V2) :-
copy_term_nat(FC_2,C_2),
call(C_2,V1,V2).
\(FC_3,V1,V2,V3) :-
copy_term_nat(FC_3,C_3),
call(C_3,V1,V2,V3).
\(FC_4,V1,V2,V3,V4) :-
copy_term_nat(FC_4,C_4),
call(C_4,V1,V2,V3,V4).
\(FC_5,V1,V2,V3,V4,V5) :-
copy_term_nat(FC_5,C_5),
call(C_5,V1,V2,V3,V4,V5).
\(FC_6,V1,V2,V3,V4,V5,V6) :-
copy_term_nat(FC_6,C_6),
call(C_6,V1,V2,V3,V4,V5,V6).
\(FC_7,V1,V2,V3,V4,V5,V6,V7) :-
copy_term_nat(FC_7,C_7),
call(C_7,V1,V2,V3,V4,V5,V6,V7).
+\(GV,FC_0) :-
copy_term_nat(GV+FC_0,GV+C_0),
no_hat_call(C_0).
+\(GV,FC_1,V1) :-
copy_term_nat(GV+FC_1,GV+C_1),
call(C_1,V1).
+\(GV,FC_2,V1,V2) :-
copy_term_nat(GV+FC_2,GV+C_2),
call(C_2,V1,V2).
+\(GV,FC_3,V1,V2,V3) :-
copy_term_nat(GV+FC_3,GV+C_3),
call(C_3,V1,V2,V3).
+\(GV,FC_4,V1,V2,V3,V4) :-
copy_term_nat(GV+FC_4,GV+C_4),
call(C_4,V1,V2,V3,V4).
+\(GV,FC_5,V1,V2,V3,V4,V5) :-
copy_term_nat(GV+FC_5,GV+C_5),
call(C_5,V1,V2,V3,V4,V5).
+\(GV,FC_6,V1,V2,V3,V4,V5,V6) :-
copy_term_nat(GV+FC_6,GV+C_6),
call(C_6,V1,V2,V3,V4,V5,V6).
+\(GV,FC_7,V1,V2,V3,V4,V5,V6,V7) :-
copy_term_nat(GV+FC_7,GV+C_7),
call(C_7,V1,V2,V3,V4,V5,V6,V7).
%% no_hat_call(:Goal_0)
%
% Like call, but issues an error for a goal (^)/2. Such goals are
% likely the result of an insufficient number of arguments.
no_hat_call(MGoal_0) :-
strip_module(MGoal_0, _, Goal_0),
( nonvar(Goal_0),
Goal_0 = (_^_)
-> throw(
error(
existence_error(lambda_parameter,MGoal_0),
_))
; call(MGoal_0)
).
% I would like to replace this by:
% V1^Goal :- throw(error(existence_error(lambda_parameter,V1^Goal),_)).

540
src/lib/lists.pl Normal file
View File

@@ -0,0 +1,540 @@
/**
List manipulation predicates
*/
:- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5,
memberchk/2, reverse/2, length/2, maplist/2,
maplist/3, maplist/4, maplist/5, maplist/6,
maplist/7, maplist/8, maplist/9, same_length/2, nth0/3, nth0/4, nth1/3, nth1/4,
sum_list/2, transpose/2, list_to_set/2, list_max/2,
list_min/2, permutation/2]).
/* Author: Mark Thom, Jan Wielemaker, and Richard O'Keefe
Copyright (c) 2018-2021, Mark Thom
Copyright (c) 2002-2020, University of Amsterdam
VU University Amsterdam
SWI-Prolog Solutions b.v.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
:- use_module(library(error)).
:- meta_predicate maplist(1, ?).
:- meta_predicate maplist(2, ?, ?).
:- meta_predicate maplist(3, ?, ?, ?).
:- meta_predicate maplist(4, ?, ?, ?, ?).
:- meta_predicate maplist(5, ?, ?, ?, ?, ?).
:- meta_predicate maplist(6, ?, ?, ?, ?, ?, ?).
:- meta_predicate maplist(7, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate maplist(8, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate foldl(3, ?, ?, ?).
:- meta_predicate foldl(4, ?, ?, ?, ?).
:- use_module(library(error)).
:- meta_predicate(resource_error(+,:)).
resource_error(Resource, Context) :-
throw(error(resource_error(Resource), Context)).
%% length(?Xs, ?N).
%
% Relates a list to its length (number of elements). It can be used to count the elements of a current list or
% to create a list full of free variables with N length.
%
% ```
% ?- length("abc", 3).
% true.
% ?- length("abc", N).
% N = 3.
% ?- length(Xs, 3).
% Xs = [_A,_B,_C].
% ```
length(Xs0, N) :-
'$skip_max_list'(M, N, Xs0,Xs),
!,
( Xs == [] -> N = M
; nonvar(Xs) -> var(N), Xs = [_|_], resource_error(finite_memory,length/2)
; nonvar(N) -> R is N-M, length_rundown(Xs, R)
; N == Xs -> failingvarskip(Xs), resource_error(finite_memory,length/2)
; length_addendum(Xs, N, M)
).
length(_, N) :-
integer(N), !,
domain_error(not_less_than_zero, N, length/2).
length(_, N) :-
type_error(integer, N, length/2).
length_rundown(Xs, 0) :- !, Xs = [].
length_rundown(Vs, N) :-
'$unattributed_var'(Vs), % unconstrained
!,
'$det_length_rundown'(Vs, N).
length_rundown([_|Xs], N) :- % force unification
N1 is N-1,
length(Xs, N1). % maybe some new info on Xs
failingvarskip(Xs) :-
'$unattributed_var'(Xs), % unconstrained
!.
failingvarskip([_|Xs0]) :- % force unification
'$skip_max_list'(_, _, Xs0,Xs),
( nonvar(Xs) -> Xs = [_|_]
; failingvarskip(Xs)
).
length_addendum([], N, N).
length_addendum([_|Xs], N, M) :-
M1 is M + 1,
length_addendum(Xs, N, M1).
%% member(?X, ?Xs).
%
% Succeeds when X unifies with an item of the list Xs, which can be at any position.
%
% ```
% ?- member(X, "hello world").
% X = h
% ; ... .
% ```
member(X, [L|Ls]) :-
member_(Ls, L, X).
member_(_, X, X).
member_([L|Ls], _, X) :-
member_(Ls, L, X).
%% select(X, Xs0, Xs1).
%
% Succeeds when the list Xs1 is the list Xs0 without the item X
%
% ```
% ?- select(c, "abcd", X).
% X = "abd"
% ; false.
% ```
select(X, [X|Xs], Xs).
select(X, [Y|Xs], [Y|Ys]) :- select(X, Xs, Ys).
%% append(+XsXs, ?Xs).
%
% Concatenates a list of lists
%
% ```
% ?- append([[1, 2], [3]], Xs).
% Xs = [1,2,3].
% ```
append([], []).
append([L0|Ls0], Ls) :-
append(L0, Rest, Ls),
append(Ls0, Rest).
%% append(Xs0, Xs1, Xs).
%
% List Xs is the concatenation of Xs0 and Xs1
%
% ```
% ?- append([1,2,3], [4,5,6], Xs).
% Xs = [1,2,3,4,5,6].
% ```
append([], R, R).
append([X|L], R, [X|S]) :- append(L, R, S).
%% memberchk(?X, +Xs).
%
% This predicate is similar to `member/2`, but it only provides a single answer
memberchk(X, Xs) :- member(X, Xs), !.
%% reverse(?Xs, ?Ys).
%
% Xs is the Ys list in reverse order
%
% ?- reverse([1,2,3], [3,2,1]).
% true.
%
reverse(Xs, Ys) :-
( nonvar(Xs) -> reverse(Xs, Ys, [], Xs)
; reverse(Ys, Xs, [], Ys)
).
reverse([], [], YsRev, YsRev).
reverse([_|Xs], [Y1|Ys], YsPreludeRev, Xss) :-
reverse(Xs, Ys, [Y1|YsPreludeRev], Xss).
%% maplist(+Predicate, ?Xs0).
%
% This is a metapredicate that applies predicate to each element of the list Xs0
%
% ```
% ?- maplist(write, [1,2,3]).
% 123 true.
% ```
maplist(_, []).
maplist(Cont1, [E1|E1s]) :-
call(Cont1, E1),
maplist(Cont1, E1s).
%% maplist(+Predicate, ?Xs0, ?Xs1).
%
% This is a metapredicate that applies predicate to each element of the lists Xs0 and Xs1.
%
% ```
% ?- maplist(length, ["hello", "prolog", "marseille"], Xs1).
% Xs1 = [5,6,9].
% ```
maplist(_, [], []).
maplist(Cont2, [E1|E1s], [E2|E2s]) :-
call(Cont2, E1, E2),
maplist(Cont2, E1s, E2s).
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2).
%
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1 and Xs2.
maplist(_, [], [], []).
maplist(Cont3, [E1|E1s], [E2|E2s], [E3|E3s]) :-
call(Cont3, E1, E2, E3),
maplist(Cont3, E1s, E2s, E3s).
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3).
%
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2 and Xs3.
maplist(_, [], [], [], []).
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s]) :-
call(Cont, E1, E2, E3, E4),
maplist(Cont, E1s, E2s, E3s, E4s).
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4).
%
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3 and Xs4.
maplist(_, [], [], [], [], []).
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s]) :-
call(Cont, E1, E2, E3, E4, E5),
maplist(Cont, E1s, E2s, E3s, E4s, E5s).
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5).
%
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4 and Xs5.
maplist(_, [], [], [], [], [], []).
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s]) :-
call(Cont, E1, E2, E3, E4, E5, E6),
maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s).
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5, ?Xs6).
%
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4, Xs5 and Xs6.
maplist(_, [], [], [], [], [], [], []).
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s]) :-
call(Cont, E1, E2, E3, E4, E5, E6, E7),
maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s, E7s).
%% maplist(+Predicate, ?Xs0, ?Xs1, ?Xs2, ?Xs3, ?Xs4, ?Xs5, ?Xs6, ?Xs7).
%
% This is a metapredicate that applies predicate to each element of the lists Xs0, Xs1, Xs2, Xs3, Xs4, Xs5, Xs6 and Xs7.
maplist(_, [], [], [], [], [], [], [], []).
maplist(Cont, [E1|E1s], [E2|E2s], [E3|E3s], [E4|E4s], [E5|E5s], [E6|E6s], [E7|E7s], [E8|E8s]) :-
call(Cont, E1, E2, E3, E4, E5, E6, E7, E8),
maplist(Cont, E1s, E2s, E3s, E4s, E5s, E6s, E7s, E8s).
%% sum_list(+Xs, -Sum).
%
% Takes a lists of numbers and unifies Sum with the result of summing all the elements of the list.
%
% ```
% ?- sum_list([2,2,2], 6).
% true.
% ```
sum_list(Ls, S) :-
foldl(lists:sum_, Ls, 0, S).
sum_(L, S0, S) :- S is S0 + L.
%% same_length(?Xs, ?Ys).
%
% Succeeds if Xs and Ys are lists of the same length
same_length([], []).
same_length([_|As], [_|Bs]) :-
same_length(As, Bs).
%% foldl(+Predicate, ?Ls, +A0, ?A).
%
% foldl, sometimes called reduce, is a metapredicate that takes a predicate, a list of items
% and a starting value, and outputs a single value. The predicate _Predicate_ must be able to take the current
% element of the list, the previous value of the computation and the next value of the computation.
%
% For example, if we define sum_ as:
%
% ```
% sum_(L, S0, S) :- S is S0 + L.
% ```
%
% Then we can define `sum_list/2` as the following:
%
% ```
% sum_list(Ls, S) :- foldl(sum_, Ls, 0, S).
% ```
foldl(_, [], A, A).
foldl(G_3, [L|Ls], A0, A) :-
call(G_3, L, A0, A1),
foldl(G_3, Ls, A1, A).
%% foldl(+Predicate, ?Ls0, ?Ls1, +A0, ?A).
%
% Same as `foldl/4` but with an extra list
foldl(_, [], [], A, A).
foldl(G_4, [X|Xs], [Y|Ys], A0, A) :-
call(G_4, X, Y, A0, A1),
foldl(G_4, Xs, Ys, A1, A).
%% transpose(?Ls, ?Ts).
%
% If Ls is a list of lists, Ts contains the transposition
%
% ```
% ?- transpose([[1,1],[2,2]], Ts).
% Ts = [[1,2],[1,2]].
% ```
transpose(Ls, Ts) :-
lists_transpose(Ls, Ts).
lists_transpose([], []).
lists_transpose([L|Ls], Ts) :-
maplist(lists:same_length(L), Ls),
foldl(lists:transpose_, L, Ts, [L|Ls], _).
transpose_(_, Fs, Lists0, Lists) :-
maplist(lists:list_first_rest, Lists0, Fs, Lists).
list_first_rest([L|Ls], L, Ls).
%% list_to_set(+Ls0, -Set).
%
% Takes a list Ls0 and returns a list Set that doesn't contain any repeated element
%
% ```
% ?- list_to_set([2,3,4,4,1,2], Set).
% Set = [2,3,4,1].
% ```
list_to_set(Ls0, Ls) :-
maplist(lists:with_var, Ls0, LVs0),
keysort(LVs0, LVs),
same_elements(LVs),
pick_firsts(LVs0, Ls).
pick_firsts([], []).
pick_firsts([E-V|EVs], Fs0) :-
( V == visited ->
Fs0 = Fs
; V = visited,
Fs0 = [E|Fs]
),
pick_firsts(EVs, Fs).
with_var(E, E-_).
same_elements([]).
same_elements([EV|EVs]) :-
foldl(lists:unify_same, EVs, EV, _).
unify_same(E-V, Prev-Var, E-V) :-
( Prev == E ->
Var = V
; true
).
%% nth0(?N, ?Ls, ?E).
%
% Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from zero.
%
% ```
% ?- nth0(2, [1,2,3,4], 3).
% true.
% ```
nth0(N, Es0, E) :-
nonvar(N),
'$skip_max_list'(Skip, N, Es0,Es1),
!,
( Skip == N
-> Es1 = [E|_]
; ( var(Es1) ; Es1 = [_|_] ) % a partial or infinite list
-> R is N-Skip,
skipn(R,Es1,Es2),
Es2 = [E|_]
).
nth0(N, Es0, E) :-
can_be(not_less_than_zero, N),
Es0 = [E0|Es1],
nth0_el(0,N, E0,E, Es1).
skipn(N0, Es0,Es) :-
N0>0,
N1 is N0-1,
Es0 = [_|Es1],
skipn(N1, Es1,Es).
skipn(0, Es,Es).
nth0_el(N0,N, E0,E, Es0) :-
Es0 == [],
!, % indexing
N0 = N,
E0 = E.
nth0_el(N,N, E,E, _).
nth0_el(N0,N, _,E, [E0|Es0]) :-
N1 is N0+1,
nth0_el(N1,N, E0,E, Es0).
%% nth1(?N, ?Ls, ?E).
%
% Succeeds if in the N position of the list Ls, we found the element E. The elements start counting from one.
%
% ```
% ?- nth1(2, [1,2,3,4], 2).
% true.
% ```
nth1(N, Es0, E) :-
N \== 0,
nth0(N, [_|Es0], E),
N \== 0.
skipn(N0, Es0,Es, Xs0,Xs) :-
N0>0,
N1 is N0-1,
Es0 = [E|Es1],
Xs0 = [E|Xs1],
skipn(N1, Es1,Es, Xs1,Xs).
skipn(0, Es,Es, Xs,Xs).
%% nth0(?N, ?Ls, ?E, ?Rs).
%
% Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from zero.
%
% ```
% ?- nth0(2, [1,2,3,4], 3, [1,2,4]).
% true.
% ```
nth0(N, Es0, E, Es) :-
integer(N),
N >= 0,
!,
skipn(N, Es0,Es1, Es,Es2),
Es1 = [E|Es2].
nth0(N, Es0, E, Es) :-
can_be(not_less_than_zero, N),
Es0 = [E0|Es1],
nth0_elx(0,N, E0,E, Es1, Es).
nth0_elx(N0,N, E0,E, Es0, Es) :-
Es0 == [],
!,
N0 = N,
E0 = E,
Es0 = Es.
nth0_elx(N,N, E,E, Es, Es).
nth0_elx(N0,N, E0,E, [E1|Es0], [E0|Es]) :-
N1 is N0+1,
nth0_elx(N1,N, E1,E, Es0, Es).
% p.p.8.5
%% nth1(?N, ?Ls, ?E, ?Rs).
%
% Succeeds if in the N position of the list Ls, we found the element E and the rest of the list is Rs. The elements start counting from one.
%
% ```
% ?- nth1(2, [1,2,3,4], 2, [1,3,4]).
% true.
% ```
nth1(N, Es0, E, Es) :-
N \== 0,
nth0(N, [_|Es0], E, [_|Es]),
N \== 0.
%% list_max(+Xs, -Max).
%
% Takes a list Xs and unifies with the maximum value of the list
list_max([N|Ns], Max) :-
foldl(lists:list_max_, Ns, N, Max).
list_max_(N, Max0, Max) :-
Max is max(N, Max0).
%% list_min(+Xs, -Min).
%
% Takes a list Xs and unifies with the minimum value of the list
list_min([N|Ns], Min) :-
foldl(lists:list_min_, Ns, N, Min).
list_min_(N, Min0, Min) :-
Min is min(N, Min0).
%% permutation(?Xs, ?Ys) is nondet.
%
% True when Xs is a permutation of Ys. This can solve for Ys given
% Xs or Xs given Ys, or even enumerate Xs and Ys together. The
% predicate `permutation/2` is primarily intended to generate
% permutations. Note that a list of length N has N! permutations,
% and unbounded permutation generation becomes prohibitively
% expensive, even for rather short lists (10! = 3,628,800).
%
% The example below illustrates that Xs and Ys being proper lists
% is not a sufficient condition to use the above replacement.
%
% ```
% ?- permutation([1,2], [X,Y]).
% X = 1, Y = 2
% ; X = 2, Y = 1
% ; false.
% ```
%
% Throws `type_error(list, Arg)` if either argument is not a proper
% or partial list.
permutation(Xs, Ys) :-
'$skip_max_list'(Xlen, _, Xs, XTail),
'$skip_max_list'(Ylen, _, Ys, YTail),
( XTail == [], YTail == [] % both proper lists
-> Xlen == Ylen
; var(XTail), YTail == [] % partial, proper
-> length(Xs, Ylen)
; XTail == [], var(YTail) % proper, partial
-> length(Ys, Xlen)
; var(XTail), var(YTail) % partial, partial
-> length(Xs, Len),
length(Ys, Len)
; must_be(list, Xs), % either is not a list
must_be(list, Ys)
),
perm(Xs, Ys).
perm([], []).
perm(List, [First|Perm]) :-
select(First, List, Rest),
perm(Rest, Perm).

View File

@@ -0,0 +1,129 @@
:- op(400, yfx, /).
% module resolution operator.
:- op(600, xfy, :).
:- op(1199, fx, meta_predicate).
/* this is an implementation specific declarative operator used to implement call_with_inference_limit/3
and setup_call_cleanup/3. switches to the default trust_me and retry_me_else. Indexing choice
instructions are unchanged. */
:- op(700, fx, non_counted_backtracking).
% arithmetic operators.
:- op(700, xfx, is).
:- op(500, yfx, +).
:- op(500, yfx, -).
:- op(400, yfx, *).
:- op(200, xfx, **).
:- op(200, xfy, ^).
:- op(500, yfx, /\).
:- op(500, yfx, \/).
:- op(500, yfx, xor).
:- op(400, yfx, div).
:- op(400, yfx, //).
:- op(400, yfx, rdiv).
:- op(400, yfx, <<).
:- op(400, yfx, >>).
:- op(400, yfx, mod).
:- op(400, yfx, rem).
:- op(200, fy, +).
:- op(200, fy, -).
:- op(200, fy, \).
% arithmetic comparison operators.
:- op(700, xfx, >).
:- op(700, xfx, <).
:- op(700, xfx, =\=).
:- op(700, xfx, =:=).
:- op(700, xfx, >=).
:- op(700, xfx, =<).
% term comparison.
:- op(700, xfx, ==).
:- op(700, xfx, \==).
:- op(700, xfx, @=<).
:- op(700, xfx, @>=).
:- op(700, xfx, @<).
:- op(700, xfx, @>).
% conditional operators.
:- op(1050, xfy, ->).
:- op(1100, xfy, ;).
% control.
:- op(700, xfx, =).
:- op(700, xfx, =..).
:- op(700, xfx, \=).
:- op(900, fy, \+).
:- op(1200, xfx, -->).
% meta_predicate declarations for call/{1, 66}.
:- meta_predicate call(0).
:- meta_predicate call(1, ?).
:- meta_predicate call(2, ?, ?).
:- meta_predicate call(3, ?, ?, ?).
:- meta_predicate call(4, ?, ?, ?, ?).
:- meta_predicate call(5, ?, ?, ?, ?, ?).
:- meta_predicate call(6, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(7, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(8, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(9, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(10, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(11, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(12, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(13, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(14, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(15, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(16, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(17, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(18, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(19, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(20, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(21, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(22, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(23, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(24, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(25, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(26, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(27, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(28, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(29, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(30, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(31, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(32, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(33, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(34, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(35, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(36, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(37, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(38, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(39, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(40, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(41, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(42, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(43, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(44, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(45, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(46, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(47, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(48, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(49, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(50, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(51, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(52, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(53, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(54, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(55, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(56, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(57, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(58, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(59, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(60, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(60, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(61, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(62, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(63, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(64, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).
:- meta_predicate call(65, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?).

View File

@@ -54,42 +54,41 @@
:- use_module(library(lists)). :- use_module(library(lists)).
/** <module> Ordered set manipulation /** Ordered set manipulation
Ordered sets are lists with unique elements sorted to the standard order Ordered sets are lists with unique elements sorted to the standard order
of terms (see sort/2). Exploiting ordering, many of the set operations of terms (see `sort/2`). Exploiting ordering, many of the set operations
can be expressed in order N rather than N^2 when dealing with unordered can be expressed in order N rather than N^2 when dealing with unordered
sets that may contain duplicates. The library(ordsets) is available in a sets that may contain duplicates. The library(ordsets) is available in a
number of Prolog implementations. Our predicates are designed to be number of Prolog implementations. Our predicates are designed to be
compatible with common practice in the Prolog community. The compatible with common practice in the Prolog community.
implementation is incomplete and relies partly on library(oset), an
older ordered set library distributed with SWI-Prolog. New applications
are advised to use library(ordsets).
Some of these predicates match directly to corresponding list Some of these predicates match directly to corresponding list
operations. It is advised to use the versions from this library to make operations. It is advised to use the versions from this library to make
clear you are operating on ordered sets. An exception is member/2. See clear you are operating on ordered sets. An exception is `member/2`. See
ord_memberchk/2. `ord_memberchk/2`.
The ordsets library is based on the standard order of terms. This The ordsets library is based on the standard order of terms. This
implies it can handle all Prolog terms, including variables. Note implies it can handle all Prolog terms, including variables. Note
however, that the ordering is not stable if a term inside the set is however, that the ordering is not stable if a term inside the set is
further instantiated. Also note that variable ordering changes if further instantiated. Also note that variable ordering changes if
variables in the set are unified with each other or a variable in the variables in the set are unified with each other or a variable in the
set is unified with a variable that is `older' than the newest variable set is unified with a variable that is _older_ than the newest variable
in the set. In practice, this implies that it is allowed to use in the set. In practice, this implies that it is allowed to use
member(X, OrdSet) on an ordered set that holds variables only if X is a member(X, OrdSet) on an ordered set that holds variables only if X is a
fresh variable. In other cases one should cease using it as an ordset fresh variable. In other cases one should cease using it as an ordset
because the order it relies on may have been changed. because the order it relies on may have been changed.
*/ */
%! is_ordset(@Term) is semidet. %% is_ordset(@Term) is semidet.
% %
% True if Term is an ordered set. All predicates in this library % True if Term is an ordered set. All predicates in this library
% expect ordered sets as input arguments. Failing to fullfil this % expect ordered sets as input arguments. Failing to fullfil this
% assumption results in undefined behaviour. Typically, ordered % assumption results in undefined behaviour. Typically, ordered
% sets are created by predicates from this library, sort/2 or % sets are created by predicates from this library, `sort/2` or
% setof/3. % `setof/3`.
is_ordset(Term) :- is_ordset(Term) :-
'$skip_max_list'(_, -1, Term, Tail), Tail == [], %% is_list(Term), '$skip_max_list'(_, _, Term, Tail), Tail == [], %% is_list(Term),
is_ordset2(Term). is_ordset2(Term).
is_ordset2([]). is_ordset2([]).
@@ -102,7 +101,7 @@ is_ordset3([H2|T], H) :-
is_ordset3(T, H2). is_ordset3(T, H2).
%! ord_empty(?List) is semidet. %% ord_empty(?List) is semidet.
% %
% True when List is the empty ordered set. Simply unifies list % True when List is the empty ordered set. Simply unifies list
% with the empty list. Not part of Quintus. % with the empty list. Not part of Quintus.
@@ -110,18 +109,16 @@ is_ordset3([H2|T], H) :-
ord_empty([]). ord_empty([]).
%! ord_seteq(+Set1, +Set2) is semidet. %% ord_seteq(+Set1, +Set2) is semidet.
% %
% True if Set1 and Set2 have the same elements. As both are % True if Set1 and Set2 have the same elements. As both are
% canonical sorted lists, this is the same as ==/2. % canonical sorted lists, this is the same as `==/2`.
%
% @compat sicstus
ord_seteq(Set1, Set2) :- ord_seteq(Set1, Set2) :-
Set1 == Set2. Set1 == Set2.
%! list_to_ord_set(+List, -OrdSet) is det. %% list_to_ord_set(+List, -OrdSet) is det.
% %
% Transform a list into an ordered set. This is the same as % Transform a list into an ordered set. This is the same as
% sorting the list. % sorting the list.
@@ -130,7 +127,7 @@ list_to_ord_set(List, Set) :-
sort(List, Set). sort(List, Set).
%! ord_intersect(+Set1, +Set2) is semidet. %% ord_intersect(+Set1, +Set2) is semidet.
% %
% True if both ordered sets have a non-empty intersection. % True if both ordered sets have a non-empty intersection.
@@ -148,31 +145,29 @@ ord_intersect__(>, H1, T1, _H2, T2) :-
ord_intersect_(T2, H1, T1). ord_intersect_(T2, H1, T1).
%! ord_disjoint(+Set1, +Set2) is semidet. %% ord_disjoint(+Set1, +Set2) is semidet.
% %
% True if Set1 and Set2 have no common elements. This is the % True if Set1 and Set2 have no common elements. This is the
% negation of ord_intersect/2. % negation of `ord_intersect/2`.
ord_disjoint(Set1, Set2) :- ord_disjoint(Set1, Set2) :-
\+ ord_intersect(Set1, Set2). \+ ord_intersect(Set1, Set2).
%! ord_intersect(+Set1, +Set2, -Intersection) %% ord_intersect(+Set1, +Set2, -Intersection)
% %
% Intersection holds the common elements of Set1 and Set2. % Intersection holds the common elements of Set1 and Set2.
% %
% @deprecated Use ord_intersection/3 % This predicate is *deprecated*. Use `ord_intersection/3`
ord_intersect(Set1, Set2, Intersection) :- ord_intersect(Set1, Set2, Intersection) :-
oset_int(Set1, Set2, Intersection). oset_int(Set1, Set2, Intersection).
%! ord_intersection(+PowerSet, -Intersection) %% ord_intersection(+PowerSet, -Intersection)
% %
% Intersection of a powerset. True when Intersection is an ordered % Intersection of a powerset. True when Intersection is an ordered
% set holding all elements common to all sets in PowerSet. % set holding all elements common to all sets in PowerSet.
%
% @compat sicstus
ord_intersection(PowerSet, Intersection) :- ord_intersection(PowerSet, Intersection) :-
key_by_length(PowerSet, Pairs), key_by_length(PowerSet, Pairs),
@@ -190,10 +185,10 @@ l_int([_-H|T], S0, S) :-
l_int(T, S1, S). l_int(T, S1, S).
%! ord_intersection(+Set1, +Set2, -Intersection) is det. %% ord_intersection(+Set1, +Set2, -Intersection) is det.
% %
% Intersection holds the common elements of Set1 and Set2. Uses % Intersection holds the common elements of Set1 and Set2. Uses
% ord_disjoint/2 if Intersection is bound to `[]` on entry. % `ord_disjoint/2` if Intersection is bound to `[]` on entry.
ord_intersection(Set1, Set2, Intersection) :- ord_intersection(Set1, Set2, Intersection) :-
( Intersection == [] ( Intersection == []
@@ -202,13 +197,11 @@ ord_intersection(Set1, Set2, Intersection) :-
). ).
%! ord_intersection(+Set1, +Set2, ?Intersection, ?Difference) is det. %% ord_intersection(+Set1, +Set2, ?Intersection, ?Difference) is det.
% %
% Intersection and difference between two ordered sets. % Intersection and difference between two ordered sets.
% Intersection is the intersection between Set1 and Set2, while % Intersection is the intersection between Set1 and Set2, while
% Difference is defined by ord_subtract(Set2, Set1, Difference). % Difference is defined by `ord_subtract(Set2, Set1, Difference)`.
%
% @see ord_intersection/3 and ord_subtract/3.
ord_intersection([], L, [], L) :- !. ord_intersection([], L, [], L) :- !.
ord_intersection([_|_], [], [], []) :- !. ord_intersection([_|_], [], [], []) :- !.
@@ -224,35 +217,35 @@ ord_intersection2(>, H1, T1, H2, T2, Intersection, [H2|HDiff]) :-
ord_intersection([H1|T1], T2, Intersection, HDiff). ord_intersection([H1|T1], T2, Intersection, HDiff).
%! ord_add_element(+Set1, +Element, ?Set2) is det. %% ord_add_element(+Set1, +Element, ?Set2) is det.
% %
% Insert an element into the set. This is the same as % Insert an element into the set. This is the same as
% ord_union(Set1, [Element], Set2). % `ord_union(Set1, [Element], Set2)`.
ord_add_element(Set1, Element, Set2) :- ord_add_element(Set1, Element, Set2) :-
oset_addel(Set1, Element, Set2). oset_addel(Set1, Element, Set2).
%! ord_del_element(+Set, +Element, -NewSet) is det. %% ord_del_element(+Set, +Element, -NewSet) is det.
% %
% Delete an element from an ordered set. This is the same as % Delete an element from an ordered set. This is the same as
% ord_subtract(Set, [Element], NewSet). % `ord_subtract(Set, [Element], NewSet)`.
ord_del_element(Set, Element, NewSet) :- ord_del_element(Set, Element, NewSet) :-
oset_delel(Set, Element, NewSet). oset_delel(Set, Element, NewSet).
%! ord_selectchk(+Item, ?Set1, ?Set2) is semidet. %% ord_selectchk(+Item, ?Set1, ?Set2) is semidet.
% %
% Selectchk/3, specialised for ordered sets. Is true when % `selectchk/3`, specialised for ordered sets. Is true when
% select(Item, Set1, Set2) and Set1, Set2 are both sorted lists % select(Item, Set1, Set2) and Set1, Set2 are both sorted lists
% without duplicates. This implementation is only expected to work % without duplicates. This implementation is only expected to work
% for Item ground and either Set1 or Set2 ground. The "chk" suffix % for Item ground and either Set1 or Set2 ground. The "chk" suffix
% is meant to remind you of memberchk/2, which also expects its % is meant to remind you of `memberchk/2`, which also expects its
% first argument to be ground. ord_selectchk(X, S, T) => % first argument to be ground. `ord_selectchk(X, S, T) =>
% ord_memberchk(X, S) & \+ ord_memberchk(X, T). % ord_memberchk(X, S) & \+ ord_memberchk(X, T).`
% %
% @author Richard O'Keefe % Author: Richard O'Keefe
ord_selectchk(Item, [X|Set1], [X|Set2]) :- ord_selectchk(Item, [X|Set1], [X|Set2]) :-
X @< Item, X @< Item,
@@ -266,19 +259,19 @@ ord_selectchk(Item, [Item|Set1], Set1) :-
). ).
%! ord_memberchk(+Element, +OrdSet) is semidet. %% ord_memberchk(+Element, +OrdSet) is semidet.
% %
% True if Element is a member of OrdSet, compared using ==. Note % True if Element is a member of OrdSet, compared using ==. Note
% that _enumerating_ elements of an ordered set can be done using % that _enumerating_ elements of an ordered set can be done using
% member/2. % `member/2`.
% %
% Some Prolog implementations also provide ord_member/2, with the % Some Prolog implementations also provide `ord_member/2`, with the
% same semantics as ord_memberchk/2. We believe that having a % same semantics as `ord_memberchk/2`. We believe that having a
% semidet ord_member/2 is unacceptably inconsistent with the *_chk % semidet `ord_member/2` is unacceptably inconsistent with the \*\_chk
% convention. Portable code should use ord_memberchk/2 or % convention. Portable code should use `ord_memberchk/2` or
% member/2. % `member/2`.
% %
% @author Richard O'Keefe % Author: Richard O'Keefe
ord_memberchk(Item, [X1,X2,X3,X4|Xs]) :- ord_memberchk(Item, [X1,X2,X3,X4|Xs]) :-
!, !,
@@ -303,7 +296,7 @@ ord_memberchk(Item, [X1]) :-
Item == X1. Item == X1.
%! ord_subset(+Sub, +Super) is semidet. %% ord_subset(+Sub, +Super) is semidet.
% %
% Is true if all elements of Sub are in Super % Is true if all elements of Sub are in Super
@@ -319,7 +312,7 @@ ord_subset_(=, _, T1, T2) :-
ord_subset(T1, T2). ord_subset(T1, T2).
%! ord_subtract(+InOSet, +NotInOSet, -Diff) is det. %% ord_subtract(+InOSet, +NotInOSet, -Diff) is det.
% %
% Diff is the set holding all elements of InOSet that are not in % Diff is the set holding all elements of InOSet that are not in
% NotInOSet. % NotInOSet.
@@ -328,13 +321,11 @@ ord_subtract(InOSet, NotInOSet, Diff) :-
oset_diff(InOSet, NotInOSet, Diff). oset_diff(InOSet, NotInOSet, Diff).
%! ord_union(+SetOfSets, -Union) is det. %% ord_union(+SetOfSets, -Union) is det.
% %
% True if Union is the union of all elements in the superset % True if Union is the union of all elements in the superset
% SetOfSets. Each member of SetOfSets must be an ordered set, the % SetOfSets. Each member of SetOfSets must be an ordered set, the
% sets need not be ordered in any way. % sets need not be ordered in any way.
%
% @author Copied from YAP, probably originally by Richard O'Keefe.
ord_union([], []). ord_union([], []).
ord_union([Set|Sets], Union) :- ord_union([Set|Sets], Union) :-
@@ -355,7 +346,7 @@ ord_union_all(N, Sets0, Union, Sets) :-
). ).
%! ord_union(+Set1, +Set2, ?Union) is det. %% ord_union(+Set1, +Set2, ?Union) is det.
% %
% Union is the union of Set1 and Set2 % Union is the union of Set1 and Set2
@@ -363,10 +354,10 @@ ord_union(Set1, Set2, Union) :-
oset_union(Set1, Set2, Union). oset_union(Set1, Set2, Union).
%! ord_union(+Set1, +Set2, -Union, -New) is det. %% ord_union(+Set1, +Set2, -Union, -New) is det.
% %
% True iff ord_union(Set1, Set2, Union) and % True iff `ord_union(Set1, Set2, Union)` and
% ord_subtract(Set2, Set1, New). % `ord_subtract(Set2, Set1, New)`.
ord_union([], Set2, Set2, Set2). ord_union([], Set2, Set2, Set2).
ord_union([H|T], Set2, Union, New) :- ord_union([H|T], Set2, Union, New) :-
@@ -390,7 +381,7 @@ ord_union_2([H|T], H2, T2, Union, New) :-
ord_union(Order, H, T, H2, T2, Union, New). ord_union(Order, H, T, H2, T2, Union, New).
%! ord_symdiff(+Set1, +Set2, ?Difference) is det. %% ord_symdiff(+Set1, +Set2, ?Difference) is det.
% %
% Is true when Difference is the symmetric difference of Set1 and % Is true when Difference is the symmetric difference of Set1 and
% Set2. I.e., Difference contains all elements that are not in the % Set2. I.e., Difference contains all elements that are not in the
@@ -398,18 +389,18 @@ ord_union_2([H|T], H2, T2, Union, New) :-
% sequence below (but the actual implementation requires only a % sequence below (but the actual implementation requires only a
% single scan). % single scan).
% %
% == % ```
% ord_union(Set1, Set2, Union), % ord_union(Set1, Set2, Union),
% ord_intersection(Set1, Set2, Intersection), % ord_intersection(Set1, Set2, Intersection),
% ord_subtract(Union, Intersection, Difference). % ord_subtract(Union, Intersection, Difference).
% == % ```
% %
% For example: % For example:
% %
% == % ```
% ?- ord_symdiff([1,2], [2,3], X). % ?- ord_symdiff([1,2], [2,3], X).
% X = [1,3]. % X = [1,3].
% == % ```
ord_symdiff([], Set2, Set2). ord_symdiff([], Set2, Set2).
ord_symdiff([H1|T1], Set2, Difference) :- ord_symdiff([H1|T1], Set2, Difference) :-
@@ -457,7 +448,7 @@ ord_symdiff(>, H1, T1, H2, Set2, [H2|Difference]) :-
*/ */
/** <module> Ordered set manipulation /* Ordered set manipulation
This library defines set operations on sets represented as ordered This library defines set operations on sets represented as ordered
lists. lists.

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

@@ -0,0 +1,112 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Predicates for reasoning about the operating system (OS) environment.
Written July 2020 by Markus Triska (triska@metalevel.at).
Lists of characters are used throughout to represent keys and values.
Example:
?- getenv("LANG", Ls).
Ls = "en_US.UTF-8".
Public domain code.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/** Predicates for reasoning about the operating system (OS) environment.
This includes predicates about environment variables, calls to shell and
finding out the PID of the running system.
*/
:- module(os, [getenv/2,
setenv/2,
unsetenv/1,
shell/1,
shell/2,
pid/1]).
:- use_module(library(error)).
:- use_module(library(charsio)).
:- use_module(library(lists)).
:- use_module(library(si)).
%% getenv(+Key, -Value).
%
% True iff Value contains the value of the environment variable Key.
% Example:
%
% ```
% ?- getenv("LANG", Ls).
% Ls = "en_US.UTF-8".
% ```
getenv(Key, Value) :-
must_be_env_var(Key),
'$getenv'(Key, Value).
%% setenv(+Key, +Value).
%
% Sets the environment variable Key to Value
setenv(Key, Value) :-
must_be_env_var(Key),
must_be_chars(Value),
'$setenv'(Key, Value).
%% unsetenv(+Key).
%
% Unsets the environment variable Key
unsetenv(Key) :-
must_be_env_var(Key),
'$unsetenv'(Key).
%% shell(+Command)
%
% Equivalent to `shell(Command, 0)`.
shell(Command) :- shell(Command, 0).
%% shell(+Command, -Status).
%
% True iff executes Command in a shell of the operating system and the exit code is Status.
% Keep in mind the shell syntax is dependant on the operating system, so it should be
% used very carefully.
%
% Example (using Linux and fish shell):
%
% ```
% ?- shell("echo $SHELL", Status).
% /bin/fish
% Status = 0.
% ```
shell(Command, Status) :-
must_be_chars(Command),
can_be(integer, Status),
'$shell'(Command, Status).
%% pid(-PID).
%
% True iff PID is the process identification number of current Scryer Prolog instance.
pid(PID) :-
can_be(integer, PID),
'$pid'(PID).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
For now, we only support a restricted subset of variable names.
The reason is that Rust may panic if a key is empty, contains an
ASCII equals sign '=' or the NUL character '\0', or when the value
contains the NUL character.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
must_be_env_var(Cs) :-
must_be_chars(Cs),
Cs = [_|_],
( maplist(permitted, Cs) -> true
; domain_error(env_var, Cs, os)
).
permitted(C) :- char_type(C, alnum).
permitted(C) :- char_type(C, ascii_punctuation).
permitted('_').
must_be_chars(Cs) :-
must_be(list, Cs),
maplist(must_be(character), Cs).

View File

@@ -1,3 +1,10 @@
/** Reasoning about pairs.
Pairs are Prolog terms with principal functor `(-)/2`. A pair
often has the form `Key-Value`. The predicates of this library
relate pairs to keys and values.
*/
:- module(pairs, [pairs_keys_values/3, :- module(pairs, [pairs_keys_values/3,
pairs_keys/2, pairs_keys/2,
pairs_values/2, pairs_values/2,
@@ -5,12 +12,27 @@
map_list_to_pairs/3]). map_list_to_pairs/3]).
:- meta_predicate map_list_to_pairs(2, ?, ?).
%% pairs_keys_values(?Pairs, ?Keys, ?Values)
%
% The first argument is a list of Pairs, the second the corresponding
% Keys, and the third argument the corresponding values.
pairs_keys_values([], [], []). pairs_keys_values([], [], []).
pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :- pairs_keys_values([A-B|ABs], [A|As], [B|Bs]) :-
pairs_keys_values(ABs, As, Bs). pairs_keys_values(ABs, As, Bs).
%% pairs_keys(?Pairs, ?Keys)
%
% Same as `pairs_keys_values(Pairs, Keys, _)`.
pairs_keys(Ps, Ks) :- pairs_keys_values(Ps, Ks, _). pairs_keys(Ps, Ks) :- pairs_keys_values(Ps, Ks, _).
%% pairs_values(?Pairs, ?Values)
%
% Same as `pairs_keys_values(Pairs, _, Values)`.
pairs_values(Ps, Vs) :- pairs_keys_values(Ps, _, Vs). pairs_values(Ps, Vs) :- pairs_keys_values(Ps, _, Vs).
map_list_to_pairs(Pred, Ls, Ps) :- map_list_to_pairs(Pred, Ls, Ps) :-

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

@@ -0,0 +1,118 @@
/** Pure I/O.
Our goal is to encourage the use of definite clause grammars (DCGs)
for describing strings. The predicates `phrase_from_file/[2,3]`,
`phrase_to_file/[2,3]` and `phrase_to_stream/2` let us apply DCGs
transparently to files and streams, and therefore decouple side-effects
from declarative descriptions.
*/
:- module(pio, [phrase_from_file/2,
phrase_from_file/3,
phrase_to_file/2,
phrase_to_file/3,
phrase_to_stream/2
]).
:- use_module(library(dcgs)).
:- use_module(library(error)).
:- use_module(library(freeze)).
:- use_module(library(iso_ext), [setup_call_cleanup/3, partial_string/3]).
:- use_module(library(lists), [member/2, maplist/2]).
:- use_module(library(charsio), [get_n_chars/3]).
:- meta_predicate(phrase_from_file(2, ?)).
:- meta_predicate(phrase_from_file(2, ?, ?)).
:- meta_predicate(phrase_to_file(2, ?)).
:- meta_predicate(phrase_to_file(2, ?, ?)).
:- meta_predicate(phrase_to_stream(2, ?)).
%% phrase_from_file(+GRBody, +File)
%
% True if grammar rule body GRBody covers the contents of File,
% represented as a list of characters.
phrase_from_file(NT, File) :-
phrase_from_file(NT, File, []).
%% phrase_from_file(+GRBody, +File, +Options)
%
% Like `phrase_from_file/2`, using Options to open the file.
phrase_from_file(NT, File, Options) :-
( var(File) -> instantiation_error(phrase_from_file/3)
; must_be(list, Options),
( member(Var, Options), var(Var) -> instantiation_error(phrase_from_file/3)
; member(type(Type), Options) ->
must_be(atom, Type),
member(Type, [text,binary])
; Type = text
),
setup_call_cleanup(open(File, read, Stream, [reposition(true)|Options]),
( stream_to_lazy_list(Stream, Xs),
phrase(NT, Xs) ),
close(Stream))
).
stream_to_lazy_list(Stream, Xs) :-
stream_property(Stream, position(Pos)),
freeze(Xs, reader_step(Stream, Pos, Xs)).
reader_step(Stream, Pos, Xs0) :-
set_stream_position(Stream, Pos),
( at_end_of_stream(Stream)
-> Xs0 = []
; get_n_chars(Stream, 4096, Cs),
partial_string(Cs, Xs0, Xs),
stream_to_lazy_list(Stream, Xs)
).
%% phrase_to_stream(+GRBody, +Stream)
%
% Emit the list of characters described by the grammar rule body
% GRBody to Stream.
%
% An ideal implementation of `phrase_to_stream/2` writes each
% character as soon as it becomes known and no choice-points remain,
% and thus avoids the manifestation of the entire string in memory.
% See [#691](https://github.com/mthom/scryer-prolog/issues/691) for
% more information.
%
% The current preliminary implementation is provided so that Prolog
% programmers can already get used to describing output with DCGs,
% and then writing it to a file when necessary. This simple
% implementation suffices as long as the entire contents can be
% represented in memory, and thus covers a large number of use cases.
phrase_to_stream(GRBody, Stream) :-
phrase(GRBody, Cs),
must_be(chars, Cs),
( stream_property(Stream, type(binary)) ->
( '$first_non_octet'(Cs, N) ->
domain_error(octet_character, N, phrase_to_stream/2)
; true
)
; true
),
% we use a specialised internal predicate that uses only a
% single "write" operation for efficiency. It is equivalent to
% maplist(put_char(Stream), Cs). It also works for binary streams.
'$put_chars'(Stream, Cs).
%% phrase_to_file(+GRBody, +File)
%
% Write the string described by GRBody to File.
phrase_to_file(GRBody, File) :-
phrase_to_file(GRBody, File, []).
%% phrase_to_file(+GRBody, +File, +Options)
%
% Like `phrase_to_file/2`, using Options to open the file.
phrase_to_file(GRBody, File, Options) :-
setup_call_cleanup(open(File, write, Stream, Options),
phrase_to_stream(GRBody, Stream),
close(Stream)).

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

@@ -0,0 +1,77 @@
/**
This library provides probabilistic predicates and random number generators.
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.
*/
:- module(random, [maybe/0, random/1, random_integer/3, set_random/1]).
:- use_module(library(error)).
%% maybe.
%
% Succeeds with probability 0.5.
maybe :- '$maybe'.
% The higher the precision, the slower it gets.
random_number_precision(64).
%% random(-R).
%
% Generates a random floating number between 0 (inclusive) and 1 (exclusive).
random(R) :-
var(R),
random_number_precision(N),
rnd(N, R).
%% random_integer(+Lower, +Upper, -R).
%
% Generates a random integer number between Lower (inclusive) and Upper (exclusive).
%
% Throws `instantiation_error` if Lower or Upper are variables.
%
% Throws `type_error` if Lower or Upper aren't integers.
random_integer(Lower, Upper, R) :-
var(R),
( (var(Lower) ; var(Upper)) ->
instantiation_error(random_integer/3)
; \+ integer(Lower) ->
type_error(integer, Lower, random_integer/3)
; \+ integer(Upper) ->
type_error(integer, Upper, random_integer/3)
; Upper > Lower,
random(R0),
R is floor((Upper - Lower) * R0 + Lower)
).
rnd(N, R) :-
rnd_(N, 0, R).
rnd_(0, R, R) :- !.
rnd_(N, R0, R) :-
maybe,
!,
N1 is N - 1,
rnd_(N1, R0, R).
rnd_(N, R0, R) :-
N1 is N - 1,
R1 is R0 + 1.0 / 2.0 ^ N,
rnd_(N1, R1, R).
%% set_random(+Seed).
%
% Sets a seed that will be used for subsequent random generations in this library.
% It's necessary to set a seed to provide reproducible executions using this library.
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)
).

View File

@@ -1,9 +1,24 @@
/** Predicates from [*Indexing dif/2*](https://arxiv.org/abs/1607.01590).
Example:
```
?- tfilter(=(a), [X,Y], Es).
X = a, Y = a, Es = "aa"
; X = a, Es = "a", dif:dif(a,Y)
; Y = a, Es = "a", dif:dif(a,X)
; Es = [], dif:dif(a,X), dif:dif(a,Y).
```
*/
:- module(reif, [if_/3, (=)/3, (',')/3, (;)/3, cond_t/3, dif/3, :- module(reif, [if_/3, (=)/3, (',')/3, (;)/3, cond_t/3, dif/3,
memberd_t/3, tfilter/3, tmember/2, tmember_t/3, memberd_t/3, tfilter/3, tmember/2, tmember_t/3,
tpartition/4]). tpartition/4]).
:- use_module(library(dif)). :- use_module(library(dif)).
:- meta_predicate(if_(1, 0, 0)).
if_(If_1, Then_0, Else_0) :- if_(If_1, Then_0, Else_0) :-
call(If_1, T), call(If_1, T),
( T == true -> call(Then_0) ( T == true -> call(Then_0)
@@ -26,13 +41,14 @@ dif(X, Y, T) :-
non(true, false). non(true, false).
non(false, true). non(false, true).
tfilter(C_2, Es, Fs) :- :- meta_predicate(tfilter(2, ?, ?)).
i_tfilter(Es, C_2, Fs).
i_tfilter([], _, []). tfilter(_, [], []).
i_tfilter([E|Es], C_2, Fs0) :- tfilter(C_2, [E|Es], Fs0) :-
if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs), if_(call(C_2, E), Fs0 = [E|Fs], Fs0 = Fs),
i_tfilter(Es, C_2, Fs). tfilter(C_2, Es, Fs).
:- meta_predicate(tpartition(2, ?, ?, ?)).
tpartition(P_2, Xs, Ts, Fs) :- tpartition(P_2, Xs, Ts, Fs) :-
i_tpartition(Xs, P_2, Ts, Fs). i_tpartition(Xs, P_2, Ts, Fs).
@@ -44,12 +60,18 @@ i_tpartition([X|Xs], P_2, Ts0, Fs0) :-
, ( Fs0 = [X|Fs], Ts0 = Ts ) ), , ( Fs0 = [X|Fs], Ts0 = Ts ) ),
i_tpartition(Xs, P_2, Ts, Fs). i_tpartition(Xs, P_2, Ts, Fs).
:- meta_predicate(','(1, 1, ?)).
','(A_1, B_1, T) :- ','(A_1, B_1, T) :-
if_(A_1, call(B_1, T), T = false). if_(A_1, call(B_1, T), T = false).
:- meta_predicate(';'(1, 1, ?)).
';'(A_1, B_1, T) :- ';'(A_1, B_1, T) :-
if_(A_1, T = true, call(B_1, T)). if_(A_1, T = true, call(B_1, T)).
:- meta_predicate(cond_t(1, 0, ?)).
cond_t(If_1, Then_0, T) :- cond_t(If_1, Then_0, T) :-
if_(If_1, ( Then_0, T = true ), T = false ). if_(If_1, ( Then_0, T = true ), T = false ).
@@ -60,8 +82,13 @@ i_memberd_t([], _, false).
i_memberd_t([X|Xs], E, T) :- i_memberd_t([X|Xs], E, T) :-
if_( X = E, T = true, i_memberd_t(Xs, E, T) ). if_( X = E, T = true, i_memberd_t(Xs, E, T) ).
:- meta_predicate(tmember(2, ?)).
tmember(P_2, [X|Xs]) :- tmember(P_2, [X|Xs]) :-
if_( call(P_2, X), true, tmember(P_2, Xs) ). if_( call(P_2, X), true, tmember(P_2, Xs) ).
:- meta_predicate(tmember_t(2, ?, ?)).
tmember_t(_P_2, [], false).
tmember_t(P_2, [X|Xs], T) :- tmember_t(P_2, [X|Xs], T) :-
if_( call(P_2, X), T = true, tmember_t(P_2, Xs, T) ). if_( call(P_2, X), T = true, tmember_t(P_2, Xs, T) ).

View File

@@ -0,0 +1,166 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written Apr 2021 by Aram Panasenco (panasenco@ucla.edu)
Part of Scryer Prolog.
[Core Rules](https://tools.ietf.org/html/rfc5234#appendix-B.1) of the
Augmented Backus-Naur Form specification (ABNF - RFC 5234). ABNF commonly
serves as the definition language for IETF communication protocols, so
having these DCGs can be extremely useful for reasoning about most IETF
syntaxes. The DCGs are presented in the order they appear in the RFC.
While some DCGs below use `char_type/2`, the most common ones are defined
manually in order to take advantage of Prolog's first-argument indexing.
BSD 3-Clause License
Copyright (c) 2021, Aram Panasenco
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(abnf, [abnf_alpha//1,
abnf_bit//1,
abnf_char//1,
abnf_cr//0,
abnf_crlf//0,
abnf_ctl//1,
abnf_digit//1,
abnf_dquote//0,
abnf_hexdig//1,
abnf_htab//0,
abnf_lf//0,
abnf_lwsp//0,
abnf_octet//1,
abnf_sp//0,
abnf_vchar//1,
abnf_wsp//0 ]).
:- use_module(library(charsio)).
:- use_module(library(dcgs)).
:- use_module(library(dif)).
:- use_module(library(lists)).
abnf_alpha('a') --> "a".
abnf_alpha('b') --> "b".
abnf_alpha('c') --> "c".
abnf_alpha('d') --> "d".
abnf_alpha('e') --> "e".
abnf_alpha('f') --> "f".
abnf_alpha('g') --> "g".
abnf_alpha('h') --> "h".
abnf_alpha('i') --> "i".
abnf_alpha('j') --> "j".
abnf_alpha('k') --> "k".
abnf_alpha('l') --> "l".
abnf_alpha('m') --> "m".
abnf_alpha('n') --> "n".
abnf_alpha('o') --> "o".
abnf_alpha('p') --> "p".
abnf_alpha('q') --> "q".
abnf_alpha('r') --> "r".
abnf_alpha('s') --> "s".
abnf_alpha('t') --> "t".
abnf_alpha('u') --> "u".
abnf_alpha('v') --> "v".
abnf_alpha('w') --> "w".
abnf_alpha('x') --> "x".
abnf_alpha('y') --> "y".
abnf_alpha('z') --> "z".
abnf_alpha('A') --> "A".
abnf_alpha('B') --> "B".
abnf_alpha('C') --> "C".
abnf_alpha('D') --> "D".
abnf_alpha('E') --> "E".
abnf_alpha('F') --> "F".
abnf_alpha('G') --> "G".
abnf_alpha('H') --> "H".
abnf_alpha('I') --> "I".
abnf_alpha('J') --> "J".
abnf_alpha('K') --> "K".
abnf_alpha('L') --> "L".
abnf_alpha('M') --> "M".
abnf_alpha('N') --> "N".
abnf_alpha('O') --> "O".
abnf_alpha('P') --> "P".
abnf_alpha('Q') --> "Q".
abnf_alpha('R') --> "R".
abnf_alpha('S') --> "S".
abnf_alpha('T') --> "T".
abnf_alpha('U') --> "U".
abnf_alpha('V') --> "V".
abnf_alpha('W') --> "W".
abnf_alpha('X') --> "X".
abnf_alpha('Y') --> "Y".
abnf_alpha('Z') --> "Z".
abnf_bit('0') --> "0".
abnf_bit('1') --> "1".
abnf_char(Char) --> [Char], { dif(Char, '\x0000\'), char_type(Char, ascii) }. %'
abnf_cr --> "\r".
abnf_crlf --> "\r\n".
abnf_ctl(Char) --> [Char], { char_type(Char, ascii), char_type(Char, control) }.
abnf_digit('0') --> "0".
abnf_digit('1') --> "1".
abnf_digit('2') --> "2".
abnf_digit('3') --> "3".
abnf_digit('4') --> "4".
abnf_digit('5') --> "5".
abnf_digit('6') --> "6".
abnf_digit('7') --> "7".
abnf_digit('8') --> "8".
abnf_digit('9') --> "9".
abnf_dquote --> "\"".
abnf_hexdig(Char) --> abnf_digit(Char).
abnf_hexdig('A') --> "A".
abnf_hexdig('B') --> "B".
abnf_hexdig('C') --> "C".
abnf_hexdig('D') --> "D".
abnf_hexdig('E') --> "E".
abnf_hexdig('F') --> "F".
abnf_htab --> "\t".
abnf_lf --> "\n".
abnf_lwsp --> "".
abnf_lwsp --> abnf_wsp, abnf_lwsp.
abnf_lwsp --> abnf_crlf, abnf_wsp, abnf_lwsp.
abnf_octet(Char) --> [Char], char_type(Char, octet).
abnf_sp --> " ".
abnf_vchar(Char) --> [Char], char_type(Char, ascii_graphic).
abnf_wsp --> abnf_sp.
abnf_wsp --> abnf_htab.

View File

@@ -0,0 +1,273 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written Apr 2021 by Aram Panasenco (panasenco@ucla.edu)
Part of Scryer Prolog.
`json_chars//1` can be used with [`phrase_from_file/2`](src/lib/pio.pl)
or [`phrase/2`](src/lib/dcgs.pl) to parse and generate [JSON](https://www.json.org/json-en.html).
BSD 3-Clause License
Copyright (c) 2021, Aram Panasenco
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(json, [
json_chars//1
]).
:- use_module(library(dcgs)).
:- use_module(library(dif)).
:- use_module(library(lists)).
/* The DCGs are written to match the McKeeman form presented on the right side of https://www.json.org/json-en.html
as closely as possible. Note that the names in the McKeeman form conflict with the pictures on the site. */
json_chars(Internal) --> json_element(Internal).
/* Because it's impossible to distinguish between an empty array [] and an empty string "", we distinguish between
different types of values based on their principal functor. The principal functors match the types defined in
the JSON Schema spec here: https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.1.1
EXCEPT we don't yet support the integer type. There are plans for more JSON Schema support in the near future. */
json_value(pairs(Pairs)) --> json_object(Pairs).
json_value(list(List)) --> json_array(List).
json_value(string(Chars)) --> json_string(Chars).
json_value(number(Number)) --> json_number(Number).
json_value(boolean(Bool)) --> json_boolean(Bool).
json_value(null) --> "null".
/* We pull json_boolean out into its own predicate in order to take advantage of first argument indexing and not leave
choice points. For more details, watch this video on decomposing arguments: https://youtu.be/FZLofckPu4A?t=1648 */
json_boolean(true) --> "true".
json_boolean(false) --> "false".
json_object([]) --> "{", json_ws, "}".
json_object([Pair|Pairs]) -->
"{",
json_members(Pairs, Pair),
"}".
/* `json_members//2` below is implemented with a lagged argument to take advantage of first argument indexing.
This is a pure performance-driven decision that doesn't affect the logic. The predicate could equivalently be
implementes as `json_members//1` below:
```
json_members([Key-Value, Pair2 | Pairs]) --> json_member(Key, Value), ",", json_members([Pair2 | Pairs]).
```
That's a logically equivalent and equally clean representation to the lagged argument. However, it leaves
choice points, while using the lagged argument doesn't. For more info, watch: https://youtu.be/FZLofckPu4A?t=1737
*/
json_members([], Key-Value) --> json_member(Key, Value).
json_members([NextPair|Pairs], Key-Value) -->
json_member(Key, Value),
",",
json_members(Pairs, NextPair).
json_member(string(Key), Value) --> json_ws, json_string(Key), json_ws, ":", json_element(Value).
json_array([]) --> "[", json_ws, "]".
json_array([Value|Values]) --> "[", json_elements(Values, Value), "]".
/* Also using a lagged argument with `json_elements//2` to take advantage of first-argument indexing */
json_elements([], Value) --> json_element(Value).
json_elements([NextValue|Values], Value) -->
json_element(Value),
",",
json_elements(Values, NextValue).
json_element(Value) --> json_ws, json_value(Value), json_ws.
json_string(Chars) --> "\"", json_characters(Chars), "\"".
json_characters("") --> "".
json_characters([Char|Chars]) --> json_character(Char), json_characters(Chars).
/* Note on variable instantiation checks (`var/1` and `nonvar/1`) used below and in Prolog in general.
Instantiation checks should never be used to change the logic of your program. Instead, they are one of
many tools to adjust the 'control' or 'search strategy' used by Prolog to execute the logic of your program.
For a general overview of the idea, read Bob Kowalski's "Algorithm = Logic + Control":
https://www.doc.ic.ac.uk/~rak/papers/algorithm%20=%20logic%20+%20control.pdf
For an introduction to search strategies in Prolog, read: https://www.metalevel.at/prolog/sorting#searching
It's tempting to use instantiation checks to be more strict while generating and more relaxed while parsing.
In fact, the early version of this library aimed to return exactly one result when generating. However, doing that
is **wrong** and leads to difficult-to-catch bugs. Instead, adjust the search strategy to return the most ideal
and strictest answer FIRST and then return less ideal answers on backtracking.
As an example, consider a string containing just the forward slash. The JSON standard recommends the forward slash
be escaped with a backslash, but allows it to not be escaped. Attempting to force stricter behavior with
instantiation checks can lead to this confusing mess:
```
phrase(json:json_characters("/"), External).
External = "\\/".
?- phrase(json:json_characters(Internal), "/").
Internal = "/"
; false.
?- phrase(json:json_characters("/"), "/").
false.
```
To avoid such bugs, never use instantiation checks to reduce the number of right answers, but rather to adjust
the *path* used to traverse those answers. */
escape_char('"', '"').
escape_char('\\', '\\').
escape_char('/', '/').
escape_char('\b', 'b').
escape_char('\f', 'f').
escape_char('\n', 'n').
escape_char('\r', 'r').
escape_char('\t', 't').
json_character(EscapeChar) -->
{ escape_char(EscapeChar, PrintChar) },
"\\",
[PrintChar].
json_character(PrintChar) -->
[PrintChar],
{ dif(PrintChar, '\\'),
dif(PrintChar, '"'),
char_code(PrintChar, PrintCharCode),
PrintCharCode >= 32 }.
json_character(EscapeChar) -->
"\\u",
json_hex(H1),
json_hex(H2),
json_hex(H3),
json_hex(H4),
{ ( nonvar(H1) ->
EscapeCharCode is H1 * 16^3 + H2 * 16^2 + H3 * 16 + H4,
char_code(EscapeChar, EscapeCharCode)
; char_code(EscapeChar, EscapeCharCode),
H1 is (EscapeCharCode // 16^3) mod 16,
H2 is (EscapeCharCode // 16^2) mod 16,
H3 is (EscapeCharCode // 16^1) mod 16,
H4 is (EscapeCharCode // 16^0) mod 16
) }.
json_hex(Digit) --> json_digit(Digit).
json_hex(10) --> "a".
json_hex(11) --> "b".
json_hex(12) --> "c".
json_hex(13) --> "d".
json_hex(14) --> "e".
json_hex(15) --> "f".
json_hex(10) --> "A".
json_hex(11) --> "B".
json_hex(12) --> "C".
json_hex(13) --> "D".
json_hex(14) --> "E".
json_hex(15) --> "F".
/* I can't think of any alternatives to using `number_chars/2` when generating, though this leads
to under-reporting of correct solutions. At least matching solutions unify when both are instantiated...
```
?- phrase(json:json_number(N), "123E2").
N = 12300
; false.
?- phrase(json:json_number(12300), Cs).
Cs = "12300".
?- phrase(json:json_number(12300), "123E2").
true
; false.
```
*/
parsing, [C] --> [C], { nonvar(C) }.
json_number(Number) -->
( parsing ->
json_sign_noplus(Sign),
json_integer(Integer),
json_fraction(Fraction),
json_exponent(Exponent),
{ ( Exponent >= 0 ->
Base = 10
; Base = 10.0
),
Number is Sign * (Integer + Fraction) * Base ^ Exponent }
; { number_chars(Number, NumberChars) },
NumberChars
).
json_integer(Digit) --> json_digit(Digit).
json_integer(TotalValue) -->
json_onenine(FirstDigit),
json_digits(RemainingValue, Power),
{ TotalValue is FirstDigit * 10 ^ (Power + 1) + RemainingValue }.
json_digits(Digit, 0) --> json_digit(Digit).
json_digits(Value, Power) -->
json_digit(FirstDigit),
json_digits(RemainingValue, NextPower),
{ Power is NextPower + 1,
Value is FirstDigit * 10^Power + RemainingValue }.
json_digit(0) --> "0".
json_digit(Digit) --> json_onenine(Digit).
json_onenine(1) --> "1".
json_onenine(2) --> "2".
json_onenine(3) --> "3".
json_onenine(4) --> "4".
json_onenine(5) --> "5".
json_onenine(6) --> "6".
json_onenine(7) --> "7".
json_onenine(8) --> "8".
json_onenine(9) --> "9".
json_fraction(0) --> "".
json_fraction(Fraction) -->
".",
json_digits(Value, Power),
{ Fraction is Value / 10.0 ^ (Power + 1) }.
json_exponent(0) --> "".
json_exponent(Exponent) -->
json_exponent_signifier,
json_sign(Sign),
json_digits(Value, _),
{ Exponent is Sign * Value }.
json_exponent_signifier --> "E".
json_exponent_signifier --> "e".
json_sign_noplus(1) --> "".
json_sign_noplus(-1) --> "-".
json_sign(Sign) --> json_sign_noplus(Sign).
json_sign(1) --> "+".
/* Make `json_ws/0` greedy when parsing, lazy when generating */
json_ws_empty --> "".
json_ws_nonempty --> " ".
json_ws_nonempty --> "\n".
json_ws_nonempty --> "\r".
json_ws_nonempty --> "\t".
json_ws_greedy --> json_ws_nonempty, json_ws_greedy.
json_ws_greedy --> json_ws_empty.
json_ws_lazy --> json_ws_empty.
json_ws_lazy --> json_ws_nonempty, json_ws_lazy.
json_ws -->
( parsing ->
json_ws_greedy
; json_ws_lazy
).

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

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

94
src/lib/si.pl Normal file
View File

@@ -0,0 +1,94 @@
/** Safe type tests.
"si" stands for "sufficiently instantiated". It can also be read as
"safe inference", so possibly also other predicates are candidates
for this library.
A safe type test:
- throws an *instantiation error* if the argument is
not sufficiently instantiated to make a sound decision
- *succeeds* if the argument is of the specified type
- *fails* otherwise.
For instance, `atom_si(A)` yields an *instantiation error* if `A` is a
variable. This is logically sound, since in that case the argument
is not sufficiently instantiated to make any decision.
The definitions are taken from [Safer type tests in Prolog](https://stackoverflow.com/questions/27306453/safer-type-tests-in-prolog).
Examples:
```
?- chars_si(Cs).
error(instantiation_error,list_si/1).
?- chars_si([h|Cs]).
error(instantiation_error,list_si/1).
?- chars_si("hello").
true.
?- chars_si(hello).
false.
```
*/
:- module(si, [atom_si/1,
integer_si/1,
atomic_si/1,
list_si/1,
character_si/1,
chars_si/1,
dif_si/2]).
:- use_module(library(lists)).
atom_si(A) :-
functor(A, _, 0), % for the instantiation error
atom(A).
integer_si(I) :-
functor(I, _, 0),
integer(I).
atomic_si(AC) :-
functor(AC,_,0).
% list_si(L) :-
% \+ \+ length(L, _),
% sort(L, _).
list_si(L0) :-
'$skip_max_list'(_,_, L0,L),
( nonvar(L) -> L = []
; throw(error(instantiation_error, list_si/1))
).
character_si(Ch) :-
functor(Ch,Ch,0),
atom(Ch),
atom_length(Ch,1).
chars_si(Chs0) :-
'$skip_max_list'(_,_, Chs0,Chs),
( nonvar(Chs) -> Chs == [] ; true ), % fails for infinite lists too
failnochars(Chs0, Uninstantiated),
( nonvar(Uninstantiated)
-> throw(error(instantiation_error, chars_si/1))
; true
).
failnochars(Chs0, U) :-
( var(Chs0) -> U = true
; Chs0 == [] -> true
; Chs0 = [Ch|Chs1],
( nonvar(Ch) -> atom(Ch), atom_length(Ch,1)
; U = true
),
failnochars(Chs1, U)
).
dif_si(X, Y) :-
X \== Y,
( X \= Y -> true
; throw(error(instantiation_error,dif_si/2))
).

1379
src/lib/simplex.pl Normal file

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,94 @@
/**
Predicates for handling network sockets, both as a server and as a client.
As a server, you should open a socket an call `socket_server_accept/4` to get a stream for each connection.
As a client, you should just open a socket and you will receive a stream.
In both cases, with a stream, you can use the usual predicates to read and write to the stream.
*/
:- module(sockets, [socket_client_open/3,
socket_server_open/2,
socket_server_accept/4,
socket_server_close/1,
current_hostname/1]).
:- use_module(library(error)).
%% socket_client_open(+Addr, -Stream, +Options).
%
% Open a socket to a server, returning a stream. Addr must satisfy `Addr = Address:Port`.
%
% The following options are available:
%
% * `alias(+Alias)`: Set an alias to the stream
% * `eof_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`.
% * `reposition(+Boolean)`: Specifies whether repositioning is required for the stream. `false` is the default.
% * `type(+Type)`: Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text
% or just binary
%
socket_client_open(Addr, Stream, Options) :-
( var(Addr) ->
throw(error(instantiation_error, socket_client_open/3))
;
true
),
must_be(var, Stream),
must_be(list, Options),
( Addr = Address:Port,
atom(Address),
( atom(Port) ; integer(Port) ) ->
true
;
throw(error(type_error(socket_address, Addr), socket_client_open/3))
),
builtins:parse_stream_options(Options,
[Alias, EOFAction, Reposition, Type],
socket_client_open/3),
'$socket_client_open'(Address, Port, Stream, Alias, EOFAction, Reposition, Type).
%% socket_server_open(+Addr, -ServerSocket).
%
% Open a server socket, returning a ServerSocket. Use that ServerSocket to accept incoming connections in
% `socket_server_accept/4`. Addr must satisfy `Addr = Address:Port`. Depending on the operating system
% configuration, some ports might be reserved for superusers.
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).
%
% Given a ServerSocket and a list of Options, accepts a incoming connection, returning data from the Client and
% a Stream to read or write data.
%
% The following options are available:
%
% * `alias(+Alias)`: Set an alias to the stream
% * `eof_action(+Action)`: Defined what happens if the end of the stream is reached. Values: `error`, `eof_code` and `reset`.
% * `reposition(+Boolean)`: Specifies whether repositioning is required for the stream. `false` is the default.
% * `type(+Type)`: Type can be `text` or `binary`. Defines the type of the stream, if it's optimized for plain text
% or just binary
%
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).
%
% Stops listening on that ServerSocket. It's recommended to always close a ServerSocket once it's no longer needed
socket_server_close(ServerSocket) :-
'$socket_server_close'(ServerSocket).
%% current_hostname(-HostName).
%
% Returns the current hostname of the computer in which Scryer Prolog is executing right now
current_hostname(HostName) :-
'$current_hostname'(HostName).

View File

@@ -1,3 +1,29 @@
/** Tabling, also called SLG resolution.
SLG resolution is an alternative execution strategy that sometimes
helps to improve termination and performance characters of Prolog
predicates.
To enable this execution strategy for a Prolog predicate, add a
`(table)/1` directive, using the prefix operator `table` that this
module defines. For example, to enable tabling for the predicate
`p/2`, use:
```
:- use_module(library(tabling)).
:- table p/2.
...
```
The possibility to apply different execution strategies is one of
the greatest attractions of pure Prolog code, and one of the
strongest arguments for keeping to the pure core of Prolog as far
as possible.
Scryer Prolog implements tabling as described by Desouter et al. in [*Tabling as a Library with Delimited Control*](https://www.ijcai.org/Proceedings/16/Papers/619.pdf).
*/
:- module(tabling, :- module(tabling,
[ start_tabling/2, % +Wrapper, :Worker. [ start_tabling/2, % +Wrapper, :Worker.
@@ -8,12 +34,12 @@
op(1150, fx, table) op(1150, fx, table)
]). ]).
:- use_module('tabling/double_linked_list'). :- use_module(library(tabling/double_linked_list)).
:- use_module('tabling/table_data_structure'). :- use_module(library(tabling/table_data_structure)).
:- use_module('tabling/batched_worklist'). :- use_module(library(tabling/batched_worklist)).
:- use_module('tabling/wrapper'). :- use_module(library(tabling/global_worklist)).
:- use_module('tabling/global_worklist'). :- use_module(library(tabling/table_link_manager)).
:- use_module('tabling/table_link_manager'). :- use_module(library(tabling/wrapper)).
:- use_module(library(cont)). :- use_module(library(cont)).
:- use_module(library(lists)). :- use_module(library(lists)).
@@ -66,6 +92,9 @@ table_and_status_for_variant(V,T,S) :-
table_for_variant(V,T), table_for_variant(V,T),
tbd_table_status(T,S). tbd_table_status(T,S).
:- meta_predicate start_tabling(?, :).
start_tabling(Wrapper,Worker) :- start_tabling(Wrapper,Worker) :-
put_new_trie_table_link, put_new_trie_table_link,
put_new_global_worklist, put_new_global_worklist,
@@ -135,7 +164,9 @@ activate(Wrapper,Worker,T) :-
delim(Wrapper,Worker,Table) :- delim(Wrapper,Worker,Table) :-
% debug(tabling, 'ACT: ~p on ~p', [Wrapper, Table]), % debug(tabling, 'ACT: ~p on ~p', [Wrapper, Table]),
reset(Worker,SourceCall,Continuation), catch(reset(Worker,SourceCall,Continuation),
_,
fail),
( Continuation = none -> ( Continuation = none ->
( add_answer(Table,Wrapper) ( add_answer(Table,Wrapper)
-> true %debug(tabling, 'ADD: ~p', [Wrapper]) -> true %debug(tabling, 'ADD: ~p', [Wrapper])

View File

@@ -45,14 +45,24 @@
wkl_worklist_work_done/1 % +WorkList wkl_worklist_work_done/1 % +WorkList
]). ]).
:- use_module(global_worklist). :- use_module(library(tabling/global_worklist)).
:- use_module(double_linked_list). :- use_module(library(tabling/double_linked_list)).
:- use_module(library(atts)). :- use_module(library(atts)).
:- use_module(library(dcgs)).
:- use_module(library(lists)). :- use_module(library(lists)).
:- attribute executing_all_work/1, worklist_presence/1, wkl_answer_cluster/1, wkl_suspension_cluster/1, wkl_answer_cluster_pointer_flag/1. :- attribute executing_all_work/1, worklist_presence/1, wkl_answer_cluster/1, wkl_suspension_cluster/1, wkl_answer_cluster_pointer_flag/1.
verify_attributes(_, _, []).
attribute_goals(X) -->
{ put_atts(X, -executing_all_work(_)),
put_atts(X, -worklist_presence(_)),
put_atts(X, -wkl_answer_cluster(_)),
put_atts(X, -wkl_suspension_cluster(_)),
put_atts(X, -wkl_answer_cluster_pointer_flag(_)) }.
/** <module> Tabling Worklist management /** <module> Tabling Worklist management
A batched worklist: a worklist that clusters suspensions and answers as A batched worklist: a worklist that clusters suspensions and answers as
@@ -161,6 +171,8 @@ wkl_p_swap_answer_continuation(Worklist,InnerAnswerClusterPointer,SuspensionClus
wkl_p_update_righmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer). wkl_p_update_righmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer).
% Update the pointer if the answer cluster it points to is no longer the rightmost inner answer cluster. % Update the pointer if the answer cluster it points to is no longer the rightmost inner answer cluster.
% Strangely, this predicate was intentionally named "wkl_p_update_righmost_inner_answer_cluster_pointer"
% in the original library.
wkl_p_update_righmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer) :- wkl_p_update_righmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer) :-
( wkl_p_answer_cluster_currently_moved_completely(Worklist,InnerAnswerClusterPointer) -> ( wkl_p_answer_cluster_currently_moved_completely(Worklist,InnerAnswerClusterPointer) ->
wkl_p_find_new_rightmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer,NewRiacPointer), wkl_p_find_new_rightmost_inner_answer_cluster_pointer(Worklist,InnerAnswerClusterPointer,NewRiacPointer),

View File

@@ -49,9 +49,15 @@
]). ]).
:- use_module(library(atts)). :- use_module(library(atts)).
:- use_module(library(dcgs)).
:- attribute dll_element/1, dll_next/1, dll_prev/1. :- attribute dll_element/1, dll_next/1, dll_prev/1.
attribute_goals(X) -->
{ put_atts(X, -dll_element(_)),
put_atts(X, -dll_next(_)),
put_atts(X, -dll_prev(_)) }.
% A circular double linked list % A circular double linked list
% ============================= % =============================

View File

@@ -9,10 +9,15 @@
]). ]).
:- use_module(library(atts)). :- use_module(library(atts)).
:- use_module(library(dcgs)).
:- use_module(library(iso_ext)). :- use_module(library(iso_ext)).
:- attribute table_global_worklist/1. :- attribute table_global_worklist/1.
verify_attributes(_, _, []).
attribute_goals(X) --> { put_atts(X, -table_global_worklist(_)) }.
put_new_global_worklist :- put_new_global_worklist :-
( bb_get(table_global_worklist_initialized, _) -> ( bb_get(table_global_worklist_initialized, _) ->
true true

View File

@@ -15,8 +15,8 @@
get_nb_identifiers/3 % +Table, -NbWorklistID, -NbAnswerTreeID get_nb_identifiers/3 % +Table, -NbWorklistID, -NbAnswerTreeID
]). ]).
:- use_module(table_link_manager). :- use_module(library(tabling/table_link_manager)).
:- use_module(trie). :- use_module(library(tabling/trie)).
/* Part of SWI-Prolog /* Part of SWI-Prolog
@@ -53,14 +53,21 @@
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
:- use_module(batched_worklist). :- use_module(library(tabling/batched_worklist)).
:- use_module(library(atts)). :- use_module(library(atts)).
:- use_module(library(dcgs)).
:- use_module(library(gensym)). :- use_module(library(gensym)).
:- use_module(library(iso_ext)). :- use_module(library(iso_ext)).
:- attribute table_status/1, newly_created_table_identifiers/1. :- attribute table_status/1, newly_created_table_identifiers/1.
verify_attributes(_, _, []).
attribute_goals(X) -->
{ put_atts(X, -table_status(_)),
put_atts(X, -newly_created_table_identifiers(_)) }.
% This file defines the table datastructure. % This file defines the table datastructure.
% %
% The table datastructure contains the following sub-structures: % The table datastructure contains the following sub-structures:

View File

@@ -43,14 +43,20 @@
]). ]).
:- use_module(library(atts)). :- use_module(library(atts)).
:- use_module(library(dcgs)).
:- use_module(library(lists)). :- use_module(library(lists)).
:- use_module(library(iso_ext)). :- use_module(library(iso_ext)).
:- use_module(library(terms)). :- use_module(library(terms)).
:- use_module(trie). :- use_module(library(tabling/trie)).
:- attribute trie_table_link/1. :- attribute trie_table_link/1.
verify_attributes(_, _, []).
attribute_goals(X) -->
{ put_atts(X, -trie_table_link(_)) }.
% This file defines a call pattern trie. % This file defines a call pattern trie.
% %
% This data structure keeps the relation between a variant and the % This data structure keeps the relation between a variant and the

View File

@@ -41,12 +41,21 @@
trie_get_all_values/2 % +Trie, -Value trie_get_all_values/2 % +Trie, -Value
]). ]).
:- use_module(library(format)).
:- use_module(library(assoc)). :- use_module(library(assoc)).
:- use_module(library(atts)). :- use_module(library(atts)).
:- use_module(library(dcgs)).
:- use_module(library(lists)). :- use_module(library(lists)).
:- attribute maybe_just/1, children/1. :- attribute maybe_just/1, children/1.
verify_attributes(_, _, []).
attribute_goals(X) -->
{ put_atts(X, -maybe_just(_)),
put_atts(X, -children(_)) }.
% Implementation of a prefix tree, a.k.a. trie % % Implementation of a prefix tree, a.k.a. trie %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -66,7 +75,7 @@
% p_trie_arity_univ(+Term,-FunctorData,-ArgumentsList). % p_trie_arity_univ(+Term,-FunctorData,-ArgumentsList).
p_trie_arity_univ(Term,functor_data(Name,Arity),Arguments) :- p_trie_arity_univ(Term,functor_data(Name,Arity),Arguments) :-
( var(Term) -> ( var(Term) ->
Name = var, Name = Term,
Arity = 0, Arity = 0,
Arguments = [] Arguments = []
; Term =.. [Name|Arguments], ; Term =.. [Name|Arguments],

View File

@@ -40,6 +40,8 @@
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
:- use_module(library(error)). :- use_module(library(error)).
:- multifile(tabled/2).
%%:- multifile %%:- multifile
%% system:term_expansion/2, %% system:term_expansion/2,
%% tabled/2. %% tabled/2.
@@ -54,8 +56,8 @@
%% table(PIList) :- %% table(PIList) :-
%% throw(error(context_error(nodirective, table(PIList)), _)). %% throw(error(context_error(nodirective, table(PIList)), _)).
instantiation_error(Var) :- %% instantiation_error(Var) :-
throw(error(instantiation_error(Var), _)). %% throw(error(instantiation_error(Var), _)).
wrappers(Var) --> wrappers(Var) -->
{ var(Var), !, { var(Var), !,
@@ -75,7 +77,7 @@ wrappers(Name/Arity) -->
atom_concat(Name, ' tabled', WrapName), atom_concat(Name, ' tabled', WrapName),
Head =.. [Name|Args], Head =.. [Name|Args],
WrappedHead =.. [WrapName|Args], WrappedHead =.. [WrapName|Args],
'$module_of'(Module, Name) %prolog_load_context(module, Module) prolog_load_context(module, Module)
}, },
[ ( Head :- [ ( Head :-
start_tabling(Module:Head, WrappedHead) start_tabling(Module:Head, WrappedHead)
@@ -93,10 +95,15 @@ rename((Head --> Body), (NewHead --> Body), Module) :- !,
functor(Head, Name, Arity), functor(Head, Name, Arity),
PlainArity is Arity+1, PlainArity is Arity+1,
functor(PlainHead, Name, PlainArity), functor(PlainHead, Name, PlainArity),
table_wrapper:tabled(PlainHead, Module), catch(table_wrapper:tabled(PlainHead, Module),
error(existence_error(procedure, tabled/2), _),
false),
rename_term(Head, NewHead). rename_term(Head, NewHead).
rename(Head, NewHead, Module) :- rename(Head, NewHead, Module) :-
table_wrapper:tabled(Head, Module), !, catch(table_wrapper:tabled(Head, Module),
error(existence_error(procedure, tabled/2), _),
false),
!,
rename_term(Head, NewHead). rename_term(Head, NewHead).
rename_term(Compound0, Compound) :- rename_term(Compound0, Compound) :-
@@ -114,5 +121,5 @@ user:term_expansion(Term0, Clauses) :-
phrase(wrappers(Preds), Clauses). phrase(wrappers(Preds), Clauses).
user:term_expansion(Clause, NewClause) :- user:term_expansion(Clause, NewClause) :-
nonvar(Clause), nonvar(Clause),
'$module_of'(Module, Clause), prolog_load_context(module, Module),
rename(Clause, NewClause, Module). rename(Clause, NewClause, Module).

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

@@ -0,0 +1,171 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written 2020-2023 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/** This library provides predicates for reasoning about time.
*/
:- module(time, [max_sleep_time/1, sleep/1, time/1, current_time/1, format_time//2]).
:- use_module(library(format)).
:- use_module(library(iso_ext)).
:- use_module(library(error)).
:- use_module(library(dcgs)).
:- use_module(library(lists)).
:- use_module(library(charsio), [read_from_chars/2]).
%% current_time(-T)
%
% Yields the current system time _T_ in an opaque form, called a
% _time stamp_. Use `format_time//2` to describe strings that contain
% attributes of the time stamp.
current_time(T) :-
'$current_time'(T0),
read_from_chars(T0, T).
%% format_time(FormatString, TimeStamp)//
%
% 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)".
% ```
format_time([], _) --> [].
format_time(['%','%'|Fs], T) --> !, "%", format_time(Fs, T).
format_time(['%',Spec|Fs], T) --> !,
( { member(Spec=Value, T) } ->
seq(Value)
; { domain_error(time_specifier, Spec, format_time//2) }
),
format_time(Fs, T).
format_time([F|Fs], T) --> [F], format_time(Fs, T).
%% max_sleep_time(T)
%
% The maximum admissible time span for `sleep/1`.
max_sleep_time(0xfffffffffffffbff).
%% sleep(S)
%
% Sleeps for S seconds (a floating point number or integer).
sleep(T) :-
builtins:must_be_number(T, sleep),
( T < 0 ->
domain_error(not_less_than_zero, T, sleep/1)
; max_sleep_time(N), T > N ->
throw(error(representation_error(max_sleep_time), sleep/1))
; '$sleep'(T)
).
% '$cpu_now' can be replaced by statistics/2 once that is implemented.
:- meta_predicate time(0).
:- dynamic(time_id/1).
:- dynamic(time_state/2).
time_next_id(N) :-
( retract(time_id(N0)) ->
N is N0 + 1
; N = 0
),
asserta(time_id(N)).
%% time(Goal)
%
% Reports the execution time of Goal.
time(Goal) :-
'$cpu_now'(T0),
time_next_id(ID),
setup_call_cleanup(asserta(time_state(ID, T0)),
( call_cleanup(catch(Goal, E, (report_time(ID),throw(E))),
Det = true),
time_true(ID),
( Det == true -> !
; true
)
; report_time(ID),
false
),
retract(time_state(ID, _))).
time_true(ID) :-
report_time(ID).
time_true(ID) :-
% on backtracking, update the stored CPU time for this ID
retract(time_state(ID, _)),
'$cpu_now'(T0),
asserta(time_state(ID, T0)),
false.
report_time(ID) :-
time_state(ID, T0),
'$cpu_now'(T),
Time is T - T0,
( bb_get('$answer_count', 0) ->
Pre = " ", Post = ""
; Pre = "", Post = " "
),
format("~s% CPU time: ~3fs~n~s", [Pre,Time,Post]).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- time((true;false)).
%@ % CPU time: 0.006s
%@ true
%@ ; % CPU time: 0.001s
%@ false.
:- time(use_module(library(clpz))).
%@ % CPU time: 3.711s
%@ true.
:- time(use_module(library(lists))).
%@ % CPU time: 0.006s
%@ true.
?- time(member(X, "abc")).
%@ % CPU time: 0.005s
%@ X = a
%@ ; % CPU time: 0.000s
%@ X = b
%@ ; % CPU time: 0.000s
%@ X = c
%@ ; % CPU time: 0.000s
%@ false.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

110
src/lib/tls.pl Normal file
View File

@@ -0,0 +1,110 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Negotiation of TLS connections.
Written Dec. 2021 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(tls, [tls_client_context/2, % -Context, +Options
tls_client_negotiate/3, % +Context, +Stream0, -Stream
tls_server_context/2, % -Context, +Options
tls_server_negotiate/3 % +Context, +Stream0, -Stream
]).
:- use_module(library(lists)).
:- use_module(library(error)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TLS Clients
===========
Use tls_client_context/2 to create a TLS context, for example with:
tls_client_context(Context, [hostname("metalevel.at")])
Using the context and an existing stream S0 (for example, the
result of socket_client_open/3), a TLS stream S can be negotiated
with:
tls_client_negotiate(Context, S0, S)
S will be an encrypted and authenticated stream with the server.
The advantage of separating the creation of the client context from
negotiating a connection is that the context can be created only once,
and quickly reused if needed. This is currently not implemented: In
the present implementation, a new internal "Connector" is created for
every connection, using the specified hostname.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
tls_client_context(tls_context(Host), Options) :-
must_be(list, Options),
( member(hostname(Host), Options) ->
must_be(chars, Host)
; Host = ""
).
tls_client_negotiate(tls_context(Host), S0, S) :-
'$tls_client_connect'(Host, S0, S).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TLS Servers
===========
Use tls_server_context/2 to create a TLS context, for example with:
tls_server_context(Context, [pkcs12(Chars)])
where Chars is a list of characters with the contents of a
DER-formatted PKCS #12 archive. The option password(Ps) can be used
to specify the password Ps (also a string) for decrypting the key.
On some versions of OSX, and potentially also on other platforms,
empty passwords are not supported.
The archive should contain a leaf certificate and its private key,
as well any intermediate certificates that should be sent to
clients to allow them to build a chain to a trusted root. The chain
certificates should be in order from the leaf certificate towards
the root.
PKCS #12 archives typically have the file extension .p12 or .pfx,
and can be created with the OpenSSL pkcs12 tool:
$ openssl pkcs12 -export -out identity.pfx \
-inkey key.pem -in cert.pem -certfile chain_certs.pem
You can use phrase_from_file/3 from library(pio) and seq//1 from
library(dcgs) to read the contents of "identity.pfx" into a string:
phrase_from_file(seq(Chars), "identity.pfx", [type(binary)])
The obtained context should be treated as an opaque Prolog term.
Using the context and an existing stream S0 (for example, the
result of socket_server_accept/4), a TLS stream S can be negotiated
by a Prolog-based server with:
tls_server_negotiate(Context, S0, S)
S will be an encrypted and authenticated stream with the client.
The advantage of separating the creation of the server context from
negotiating a connection is that the context can be created only
once, and quickly cloned for every incoming connection. This is
currently not implemented: In the present implementation, a new context
is created for every connection, using the specified parameters.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
tls_server_context(tls_context(Cert,Password), Options) :-
( member(pcks12(Cert), Options) ->
must_be(chars, Cert)
; domain_error(contains_pcks12, Options, tls_server_context/2)
),
( member(password(Password), Options) ->
must_be(chars, Password)
; Password = ""
).
tls_server_negotiate(tls_context(Cert,Password), S0, S) :-
'$tls_accept_client'(Cert, Password, S0, S).

633
src/lib/ugraphs.pl Normal file
View File

@@ -0,0 +1,633 @@
/* Author: R.A.O'Keefe, Vitor Santos Costa, Jan Wielemaker
E-mail: J.Wielemaker@vu.nl
WWW: http://www.swi-prolog.org
Copyright (c) 1984-2021, VU University Amsterdam
CWI, Amsterdam
SWI-Prolog Solutions .b.v
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
:- module(ugraphs,
[ add_edges/3, % +Graph, +Edges, -NewGraph
add_vertices/3, % +Graph, +Vertices, -NewGraph
complement/2, % +Graph, -NewGraph
compose/3, % +LeftGraph, +RightGraph, -NewGraph
del_edges/3, % +Graph, +Edges, -NewGraph
del_vertices/3, % +Graph, +Vertices, -NewGraph
edges/2, % +Graph, -Edges
neighbors/3, % +Vertex, +Graph, -Vertices
neighbours/3, % +Vertex, +Graph, -Vertices
reachable/3, % +Vertex, +Graph, -Vertices
top_sort/2, % +Graph, -Sort
top_sort/3, % +Graph, -Sort0, -Sort
transitive_closure/2, % +Graph, -Closure
transpose_ugraph/2, % +Graph, -NewGraph
vertices/2, % +Graph, -Vertices
vertices_edges_to_ugraph/3, % +Vertices, +Edges, -Graph
ugraph_union/3, % +Graph1, +Graph2, -Graph
connect_ugraph/3 % +Graph1, -Start, -Graph
]).
/** Graph manipulation library
The S-representation of a graph is a list of (vertex-neighbours) pairs,
where the pairs are in standard order (as produced by keysort) and the
neighbours of each vertex are also in standard order (as produced by
sort). This form is convenient for many calculations.
A new UGraph from raw data can be created using
`vertices_edges_to_ugraph/3`.
Adapted to support some of the functionality of the SICStus ugraphs
library by Vitor Santos Costa.
Ported from YAP 5.0.1 to SWI-Prolog by Jan Wielemaker.
Ported from SWI-Prolog to Scryer by [Adrián Arroyo Calle](https://adrianistan.eu)
License: BSD-2 or Artistic 2.0
*/
:- use_module(library(lists)).
:- use_module(library(pairs)).
:- use_module(library(ordsets)).
%% vertices(+Graph, -Vertices)
%
% Unify Vertices with all vertices appearing in Graph. Example:
%
% ```
% ?- vertices([1-[3,5],2-[4],3-[],4-[5],5-[]], L).
% L = [1, 2, 3, 4, 5]
% ```
vertices([], []) :- !.
vertices([Vertex-_|Graph], [Vertex|Vertices]) :-
vertices(Graph, Vertices).
%% vertices_edges_to_ugraph(+Vertices, +Edges, -UGraph) is det.
%
% Create a UGraph from Vertices and edges. Given a graph with a
% set of Vertices and a set of Edges, Graph must unify with the
% corresponding S-representation. Note that the vertices without
% edges will appear in Vertices but not in Edges. Moreover, it is
% sufficient for a vertice to appear in Edges.
%
% ```
% ?- vertices_edges_to_ugraph([],[1-3,2-4,4-5,1-5], L).
% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[]]
% ```
%
% In this case all vertices are defined implicitly. The next
% example shows three unconnected vertices:
%
% ```
% ?- vertices_edges_to_ugraph([6,7,8],[1-3,2-4,4-5,1-5], L).
% L = [1-[3,5], 2-[4], 3-[], 4-[5], 5-[], 6-[], 7-[], 8-[]]
% ```
vertices_edges_to_ugraph(Vertices, Edges, Graph) :-
sort(Edges, EdgeSet),
p_to_s_vertices(EdgeSet, IVertexBag),
append(Vertices, IVertexBag, VertexBag),
sort(VertexBag, VertexSet),
p_to_s_group(VertexSet, EdgeSet, Graph).
%% add_vertices(+Graph, +Vertices, -NewGraph)
%
% Unify NewGraph with a new graph obtained by adding the list of
% Vertices to Graph. Example:
%
% ```
% ?- add_vertices([1-[3,5],2-[]], [0,1,2,9], NG).
% NG = [0-[], 1-[3,5], 2-[], 9-[]]
% ```
% replace with real msort/2 when available
msort_(List, Sorted) :-
pairs_keys(Pairs, List),
keysort(Pairs, SortedPairs),
pairs_keys(SortedPairs, Sorted).
add_vertices(Graph, Vertices, NewGraph) :-
% msort/2 not available in Scryer Prolog yet: msort(Vertices, V1),
msort_(Vertices, V1),
add_vertices_to_s_graph(V1, Graph, NewGraph).
add_vertices_to_s_graph(L, [], NL) :-
!,
add_empty_vertices(L, NL).
add_vertices_to_s_graph([], L, L) :- !.
add_vertices_to_s_graph([V1|VL], [V-Edges|G], NGL) :-
compare(Res, V1, V),
add_vertices_to_s_graph(Res, V1, VL, V, Edges, G, NGL).
add_vertices_to_s_graph(=, _, VL, V, Edges, G, [V-Edges|NGL]) :-
add_vertices_to_s_graph(VL, G, NGL).
add_vertices_to_s_graph(<, V1, VL, V, Edges, G, [V1-[]|NGL]) :-
add_vertices_to_s_graph(VL, [V-Edges|G], NGL).
add_vertices_to_s_graph(>, V1, VL, V, Edges, G, [V-Edges|NGL]) :-
add_vertices_to_s_graph([V1|VL], G, NGL).
add_empty_vertices([], []).
add_empty_vertices([V|G], [V-[]|NG]) :-
add_empty_vertices(G, NG).
%% del_vertices(+Graph, +Vertices, -NewGraph) is det.
%
% Unify NewGraph with a new graph obtained by deleting the list of
% Vertices and all the edges that start from or go to a vertex in
% Vertices to the Graph. Example:
%
% ```
% ?- del_vertices([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[2,6],8-[]],
% [2,1],
% NL).
% NL = [3-[],4-[5],5-[],6-[],7-[6],8-[]]
% ```
del_vertices(Graph, Vertices, NewGraph) :-
sort(Vertices, V1), % JW: was msort
( V1 = []
-> Graph = NewGraph
; del_vertices(Graph, V1, V1, NewGraph)
).
del_vertices(G, [], V1, NG) :-
!,
del_remaining_edges_for_vertices(G, V1, NG).
del_vertices([], _, _, []).
del_vertices([V-Edges|G], [V0|Vs], V1, NG) :-
compare(Res, V, V0),
split_on_del_vertices(Res, V,Edges, [V0|Vs], NVs, V1, NG, NGr),
del_vertices(G, NVs, V1, NGr).
del_remaining_edges_for_vertices([], _, []).
del_remaining_edges_for_vertices([V0-Edges|G], V1, [V0-NEdges|NG]) :-
ord_subtract(Edges, V1, NEdges),
del_remaining_edges_for_vertices(G, V1, NG).
split_on_del_vertices(<, V, Edges, Vs, Vs, V1, [V-NEdges|NG], NG) :-
ord_subtract(Edges, V1, NEdges).
split_on_del_vertices(>, V, Edges, [_|Vs], Vs, V1, [V-NEdges|NG], NG) :-
ord_subtract(Edges, V1, NEdges).
split_on_del_vertices(=, _, _, [_|Vs], Vs, _, NG, NG).
%% add_edges(+Graph, +Edges, -NewGraph)
%
% Unify NewGraph with a new graph obtained by adding the list of Edges
% to Graph. Example:
%
% ```
% ?- add_edges([1-[3,5],2-[4],3-[],4-[5],
% 5-[],6-[],7-[],8-[]],
% [1-6,2-3,3-2,5-7,3-2,4-5],
% NL).
% NL = [1-[3,5,6], 2-[3,4], 3-[2], 4-[5],
% 5-[7], 6-[], 7-[], 8-[]]
% ```
add_edges(Graph, Edges, NewGraph) :-
p_to_s_graph(Edges, G1),
ugraph_union(Graph, G1, NewGraph).
%% ugraph_union(+Graph1, +Graph2, -NewGraph)
%
% NewGraph is the union of Graph1 and Graph2. Example:
%
% ```
% ?- ugraph_union([1-[2],2-[3]],[2-[4],3-[1,2,4]],L).
% L = [1-[2], 2-[3,4], 3-[1,2,4]]
% ```
ugraph_union(Set1, [], Set1) :- !.
ugraph_union([], Set2, Set2) :- !.
ugraph_union([Head1-E1|Tail1], [Head2-E2|Tail2], Union) :-
compare(Order, Head1, Head2),
ugraph_union(Order, Head1-E1, Tail1, Head2-E2, Tail2, Union).
ugraph_union(=, Head-E1, Tail1, _-E2, Tail2, [Head-Es|Union]) :-
ord_union(E1, E2, Es),
ugraph_union(Tail1, Tail2, Union).
ugraph_union(<, Head1, Tail1, Head2, Tail2, [Head1|Union]) :-
ugraph_union(Tail1, [Head2|Tail2], Union).
ugraph_union(>, Head1, Tail1, Head2, Tail2, [Head2|Union]) :-
ugraph_union([Head1|Tail1], Tail2, Union).
%% del_edges(+Graph, +Edges, -NewGraph)
%
% Unify NewGraph with a new graph obtained by removing the list of
% Edges from Graph. Notice that no vertices are deleted. Example:
%
% ```
% ?- del_edges([1-[3,5],2-[4],3-[],4-[5],5-[],6-[],7-[],8-[]],
% [1-6,2-3,3-2,5-7,3-2,4-5,1-3],
% NL).
% NL = [1-[5],2-[4],3-[],4-[],5-[],6-[],7-[],8-[]]
% ```
del_edges(Graph, Edges, NewGraph) :-
p_to_s_graph(Edges, G1),
graph_subtract(Graph, G1, NewGraph).
%% graph_subtract(+Set1, +Set2, ?Difference)
%
% Is based on `ord_subtract/3`
graph_subtract(Set1, [], Set1) :- !.
graph_subtract([], _, []).
graph_subtract([Head1-E1|Tail1], [Head2-E2|Tail2], Difference) :-
compare(Order, Head1, Head2),
graph_subtract(Order, Head1-E1, Tail1, Head2-E2, Tail2, Difference).
graph_subtract(=, H-E1, Tail1, _-E2, Tail2, [H-E|Difference]) :-
ord_subtract(E1,E2,E),
graph_subtract(Tail1, Tail2, Difference).
graph_subtract(<, Head1, Tail1, Head2, Tail2, [Head1|Difference]) :-
graph_subtract(Tail1, [Head2|Tail2], Difference).
graph_subtract(>, Head1, Tail1, _, Tail2, Difference) :-
graph_subtract([Head1|Tail1], Tail2, Difference).
%% edges(+Graph, -Edges)
%
% Unify Edges with all edges appearing in Graph. Example:
%
% ```
% ?- edges([1-[3,5],2-[4],3-[],4-[5],5-[]], L).
% L = [1-3, 1-5, 2-4, 4-5]
% ```
edges(Graph, Edges) :-
s_to_p_graph(Graph, Edges).
p_to_s_graph(P_Graph, S_Graph) :-
sort(P_Graph, EdgeSet),
p_to_s_vertices(EdgeSet, VertexBag),
sort(VertexBag, VertexSet),
p_to_s_group(VertexSet, EdgeSet, S_Graph).
p_to_s_vertices([], []).
p_to_s_vertices([A-Z|Edges], [A,Z|Vertices]) :-
p_to_s_vertices(Edges, Vertices).
p_to_s_group([], _, []).
p_to_s_group([Vertex|Vertices], EdgeSet, [Vertex-Neibs|G]) :-
p_to_s_group(EdgeSet, Vertex, Neibs, RestEdges),
p_to_s_group(Vertices, RestEdges, G).
p_to_s_group([V1-X|Edges], V2, [X|Neibs], RestEdges) :- V1 == V2,
!,
p_to_s_group(Edges, V2, Neibs, RestEdges).
p_to_s_group(Edges, _, [], Edges).
s_to_p_graph([], []) :- !.
s_to_p_graph([Vertex-Neibs|G], P_Graph) :-
s_to_p_graph(Neibs, Vertex, P_Graph, Rest_P_Graph),
s_to_p_graph(G, Rest_P_Graph).
s_to_p_graph([], _, P_Graph, P_Graph) :- !.
s_to_p_graph([Neib|Neibs], Vertex, [Vertex-Neib|P], Rest_P) :-
s_to_p_graph(Neibs, Vertex, P, Rest_P).
%% transitive_closure(+Graph, -Closure)
%
% Generate the graph Closure as the transitive closure of Graph.
% Example:
%
% ```
% ?- transitive_closure([1-[2,3],2-[4,5],4-[6]],L).
% L = [1-[2,3,4,5,6], 2-[4,5,6], 4-[6]]
% ```
transitive_closure(Graph, Closure) :-
warshall(Graph, Graph, Closure).
warshall([], Closure, Closure) :- !.
warshall([V-_|G], E, Closure) :-
memberchk(V-Y, E), % Y := E(v)
warshall(E, V, Y, NewE),
warshall(G, NewE, Closure).
warshall([X-Neibs|G], V, Y, [X-NewNeibs|NewG]) :-
memberchk(V, Neibs),
!,
ord_union(Neibs, Y, NewNeibs),
warshall(G, V, Y, NewG).
warshall([X-Neibs|G], V, Y, [X-Neibs|NewG]) :-
!,
warshall(G, V, Y, NewG).
warshall([], _, _, []).
%% transpose_ugraph(Graph, NewGraph) is det.
%
% Unify NewGraph with a new graph obtained from Graph by replacing
% all edges of the form V1-V2 by edges of the form V2-V1. The cost
% is O(|V|\*log(|V|)). Notice that an undirected graph is its own
% transpose. Example:
%
% ```
% ?- transpose([1-[3,5],2-[4],3-[],4-[5],
% 5-[],6-[],7-[],8-[]], NL).
% NL = [1-[],2-[],3-[1],4-[2],5-[1,4],6-[],7-[],8-[]]
% ```
transpose_ugraph(Graph, NewGraph) :-
edges(Graph, Edges),
vertices(Graph, Vertices),
flip_edges(Edges, TransposedEdges),
vertices_edges_to_ugraph(Vertices, TransposedEdges, NewGraph).
flip_edges([], []).
flip_edges([Key-Val|Pairs], [Val-Key|Flipped]) :-
flip_edges(Pairs, Flipped).
%% compose(+LeftGraph, +RightGraph, -NewGraph)
%
% Compose NewGraph by connecting the _drains_ of LeftGraph to the
% _sources_ of RightGraph. Example:
%
% ```
% ?- compose([1-[2],2-[3]],[2-[4],3-[1,2,4]],L).
% L = [1-[4], 2-[1,2,4], 3-[]]
% ```
compose(G1, G2, Composition) :-
vertices(G1, V1),
vertices(G2, V2),
ord_union(V1, V2, V),
compose(V, G1, G2, Composition).
compose([], _, _, []) :- !.
compose([Vertex|Vertices], [Vertex-Neibs|G1], G2,
[Vertex-Comp|Composition]) :-
!,
compose1(Neibs, G2, [], Comp),
compose(Vertices, G1, G2, Composition).
compose([Vertex|Vertices], G1, G2, [Vertex-[]|Composition]) :-
compose(Vertices, G1, G2, Composition).
compose1([V1|Vs1], [V2-N2|G2], SoFar, Comp) :-
compare(Rel, V1, V2),
!,
compose1(Rel, V1, Vs1, V2, N2, G2, SoFar, Comp).
compose1(_, _, Comp, Comp).
compose1(<, _, Vs1, V2, N2, G2, SoFar, Comp) :-
!,
compose1(Vs1, [V2-N2|G2], SoFar, Comp).
compose1(>, V1, Vs1, _, _, G2, SoFar, Comp) :-
!,
compose1([V1|Vs1], G2, SoFar, Comp).
compose1(=, V1, Vs1, V1, N2, G2, SoFar, Comp) :-
ord_union(N2, SoFar, Next),
compose1(Vs1, G2, Next, Comp).
%% top_sort(+Graph, -Sorted) is semidet.
%
% Sorted is a topological sorted list of nodes in Graph. A
% toplogical sort is possible if the graph is connected and
% acyclic. In the example we show how topological sorting works
% for a linear graph:
%
% ```
% ?- top_sort([1-[2], 2-[3], 3-[]], L).
% L = [1, 2, 3]
% ```
top_sort(Graph, Sorted) :-
vertices_and_zeros(Graph, Vertices, Counts0),
count_edges(Graph, Vertices, Counts0, Counts1),
select_zeros(Counts1, Vertices, Zeros),
top_sort(Zeros, Sorted, Graph, Vertices, Counts1).
%% top_sort(+Graph, -Sorted, ?Tail) is semidet.
%
% The predicate `top_sort/3` is a difference list version of
% `top_sort/2`.
top_sort(Graph, Sorted0, Sorted) :-
vertices_and_zeros(Graph, Vertices, Counts0),
count_edges(Graph, Vertices, Counts0, Counts1),
select_zeros(Counts1, Vertices, Zeros),
top_sort(Zeros, Sorted, Sorted0, Graph, Vertices, Counts1).
vertices_and_zeros([], [], []) :- !.
vertices_and_zeros([Vertex-_|Graph], [Vertex|Vertices], [0|Zeros]) :-
vertices_and_zeros(Graph, Vertices, Zeros).
count_edges([], _, Counts, Counts) :- !.
count_edges([_-Neibs|Graph], Vertices, Counts0, Counts2) :-
incr_list(Neibs, Vertices, Counts0, Counts1),
count_edges(Graph, Vertices, Counts1, Counts2).
incr_list([], _, Counts, Counts) :- !.
incr_list([V1|Neibs], [V2|Vertices], [M|Counts0], [N|Counts1]) :-
V1 == V2,
!,
N is M+1,
incr_list(Neibs, Vertices, Counts0, Counts1).
incr_list(Neibs, [_|Vertices], [N|Counts0], [N|Counts1]) :-
incr_list(Neibs, Vertices, Counts0, Counts1).
select_zeros([], [], []) :- !.
select_zeros([0|Counts], [Vertex|Vertices], [Vertex|Zeros]) :-
!,
select_zeros(Counts, Vertices, Zeros).
select_zeros([_|Counts], [_|Vertices], Zeros) :-
select_zeros(Counts, Vertices, Zeros).
top_sort([], [], Graph, _, Counts) :-
!,
vertices_and_zeros(Graph, _, Counts).
top_sort([Zero|Zeros], [Zero|Sorted], Graph, Vertices, Counts1) :-
graph_memberchk(Zero-Neibs, Graph),
decr_list(Neibs, Vertices, Counts1, Counts2, Zeros, NewZeros),
top_sort(NewZeros, Sorted, Graph, Vertices, Counts2).
top_sort([], Sorted0, Sorted0, Graph, _, Counts) :-
!,
vertices_and_zeros(Graph, _, Counts).
top_sort([Zero|Zeros], [Zero|Sorted], Sorted0, Graph, Vertices, Counts1) :-
graph_memberchk(Zero-Neibs, Graph),
decr_list(Neibs, Vertices, Counts1, Counts2, Zeros, NewZeros),
top_sort(NewZeros, Sorted, Sorted0, Graph, Vertices, Counts2).
graph_memberchk(Element1-Edges, [Element2-Edges2|_]) :-
Element1 == Element2,
!,
Edges = Edges2.
graph_memberchk(Element, [_|Rest]) :-
graph_memberchk(Element, Rest).
decr_list([], _, Counts, Counts, Zeros, Zeros) :- !.
decr_list([V1|Neibs], [V2|Vertices], [1|Counts1], [0|Counts2], Zi, Zo) :-
V1 == V2,
!,
decr_list(Neibs, Vertices, Counts1, Counts2, [V2|Zi], Zo).
decr_list([V1|Neibs], [V2|Vertices], [N|Counts1], [M|Counts2], Zi, Zo) :-
V1 == V2,
!,
M is N-1,
decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo).
decr_list(Neibs, [_|Vertices], [N|Counts1], [N|Counts2], Zi, Zo) :-
decr_list(Neibs, Vertices, Counts1, Counts2, Zi, Zo).
%% neighbours(+Vertex, +Graph, -Neigbours) is det.
%
% Neigbours is a sorted list of the neighbours of Vertex in Graph.
% Example:
%
% ```
% ?- neighbours(4,[1-[3,5],2-[4],3-[],
% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL).
% NL = [1,2,7,5]
% ```
%% neighbors(+Vertex, +Graph, -Neigbours) is det.
%
% Same as `neighbours/3`.
neighbors(Vertex, Graph, Neig) :-
neighbours(Vertex, Graph, Neig).
neighbours(V,[V0-Neig|_],Neig) :-
V == V0,
!.
neighbours(V,[_|G],Neig) :-
neighbours(V,G,Neig).
%% connect_ugraph(+UGraphIn, -Start, -UGraphOut) is det.
%
% Adds Start as an additional vertex that is connected to all vertices
% in UGraphIn. This can be used to create an topological sort for a
% not connected graph. Start is before any vertex in UGraphIn in the
% standard order of terms. No vertex in UGraphIn can be a variable.
%
% Can be used to order a not-connected graph as follows:
%
% ```
% top_sort_unconnected(Graph, Vertices) :-
% ( top_sort(Graph, Vertices)
% -> true
% ; connect_ugraph(Graph, Start, Connected),
% top_sort(Connected, Ordered0),
% Ordered0 = [Start|Vertices]
% ).
% ```
connect_ugraph([], 0, []) :- !.
connect_ugraph(Graph, Start, [Start-Vertices|Graph]) :-
vertices(Graph, Vertices),
Vertices = [First|_],
before(First, Start).
%% before(+Term, -Before) is det.
%
% Unify Before to a term that comes before Term in the standard
% order of terms.
%
% Throws `instantiation_error` if Term is unbound.
before(X, _) :-
var(X),
!,
instantiation_error(X).
before(Number, Start) :-
number(Number),
!,
Start is Number - 1.
before(_, 0).
%% complement(+UGraphIn, -UGraphOut)
%
% UGraphOut is a ugraph with an edge between all vertices that are
% _not_ connected in UGraphIn and all edges from UGraphIn removed.
% Example:
%
% ```
% ?- complement([1-[3,5],2-[4],3-[],
% 4-[1,2,7,5],5-[],6-[],7-[],8-[]], NL).
% NL = [1-[2,4,6,7,8],2-[1,3,5,6,7,8],3-[1,2,4,5,6,7,8],
% 4-[3,5,6,8],5-[1,2,3,4,6,7,8],6-[1,2,3,4,5,7,8],
% 7-[1,2,3,4,5,6,8],8-[1,2,3,4,5,6,7]]
% ```
% TODO: Simple two-step algorithm. You could be smarter, I suppose.
complement(G, NG) :-
vertices(G,Vs),
complement(G,Vs,NG).
complement([], _, []).
complement([V-Ns|G], Vs, [V-INs|NG]) :-
ord_add_element(Ns,V,Ns1),
ord_subtract(Vs,Ns1,INs),
complement(G, Vs, NG).
%% reachable(+Vertex, +UGraph, -Vertices)
%
% True when Vertices is an ordered set of vertices reachable in
% UGraph, including Vertex. Example:
%
% ```
% ?- reachable(1,[1-[3,5],2-[4],3-[],4-[5],5-[]],V).
% V = [1, 3, 5]
% ```
reachable(N, G, Rs) :-
reachable([N], G, [N], Rs).
reachable([], _, Rs, Rs).
reachable([N|Ns], G, Rs0, RsF) :-
neighbours(N, G, Nei),
ord_union(Rs0, Nei, Rs1, D),
append(Ns, D, Nsi),
reachable(Nsi, G, Rs1, RsF).

125
src/lib/uuid.pl Normal file
View File

@@ -0,0 +1,125 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written in February 2021 by Adrián Arroyo (adrian.arroyocalle@gmail.com)
Part of Scryer-Prolog
I place this code in the public domain. Use it in any way you want.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
This library provides reasoning and working with [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier)
(only version 4 right now).
There are three predicates:
* `uuidv4/1`, to generate a new UUIDv4
* `uuidv4_string/1`, to generate a new UUIDv4 in string hex representation
* `uuid_string/2`, to converte between UUID list of bytes and UUID hex representation
Examples:
```
?- uuidv4(X).
X = [42,147,248,242,117,196,79,2,129,159|...].
?- uuidv4_string(X).
X = "428499fc-76e3-4240- ...".
?- uuidv4(X), uuid_string(X, S).
X = [173,12,244,152,139,118,64,139,137,4|...], S = "ad0cf498-8b76-408b- ...".
?- uuid_string(X, "61ae692e-eaf6-4199-8dd3-9f01db70a20b").
X = [97,174,105,46,234,246,65,153,141,211|...].
*/
:- module(uuid, [
uuidv4/1,
uuidv4_string/1,
uuid_string/2
]).
:- use_module(library(crypto)).
:- use_module(library(dcgs)).
:- use_module(library(lists)).
/*
An UUID is made of 16 bytes, composed of 5 sections:
time_low - 4
time_mid - 2
time_hi_and_version - 2
clock_seq_hi_and_res_clock_seq_low - 2
node - 6
UUID v4 can be generated from a set of 16 random bytes: https://www.rfc-archive.org/getrfc.php?rfc=4122#gsc.tab=0 (section 4.4)
*/
%% uuidv4(-Uuid).
%
% Generates a new UUID v4 (random). It unifies with a list of bytes.
uuidv4(Uuid) :-
crypto_n_random_bytes(16, Bytes),
Bytes = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16],
byte_bits(B9, BitsClockSeqHi0),
BitsClockSeqHi0 = [_X7, _X6, X5, X4, X3, X2, X1, X0],
NewBitsClockSeqHi0 = [1, 0, X5, X4, X3, X2, X1, X0],
byte_bits(NewClockSeqHi0, NewBitsClockSeqHi0),
byte_bits(B7, BitsTimeHi),
BitsTimeHi = [_Y7, _Y6, _Y5, _Y4, Y3, Y2, Y1, Y0],
NewBitsTimeHi = [0, 1, 0, 0, Y3, Y2, Y1, Y0],
byte_bits(NewTimeHi, NewBitsTimeHi),
Uuid = [B1, B2, B3, B4, B5, B6, NewTimeHi, B8, NewClockSeqHi0, B10, B11, B12, B13, B14, B15, B16].
%% uuidv4_string(-UuidString).
%
% Generates a new UUID v4 (random). It unifies with a string representation of the UUID.
% It is equivalent of calling `uuidv4/1` followed by `uuid_string/2`.
uuidv4_string(String) :- uuidv4(Uuid), uuid_string(Uuid, String).
%% uuid_string(?UuidBytes, ?UuidString).
%
% Translates between the bytes representation and the string representation of the same UUID.
uuid_string(Uuid, String) :-
Uuid = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16],
phrase(uuid_([S1, S2, S3, S4, S5]), String),
hex_bytes(S1, [B1, B2, B3, B4]),
hex_bytes(S2, [B5, B6]),
hex_bytes(S3, [B7, B8]),
hex_bytes(S4, [B9, B10]),
hex_bytes(S5, [B11, B12, B13, B14, B15, B16]).
uuid_([S1, S2, S3, S4, S5]) -->
{
length(S1, 8),
length(S2, 4),
length(S3, 4),
length(S4, 4),
length(S5, 12)
},
S1,
"-",
S2,
"-",
S3,
"-",
S4,
"-",
S5.
byte_bits(Byte, Bits) :-
\+ var(Byte),
byte_bits_(Byte, Bits),
length(Bits, 8),!.
byte_bits(Byte, Bits) :-
\+ var(Bits),
length(Bits, 8),
byte_bits__(Byte, Bits),!.
byte_bits_(0, [0]).
byte_bits_(1, [1]).
byte_bits_(Byte, Bits) :-
R is Byte // 2,
M is Byte mod 2,
byte_bits_(R, Bits0),
append(Bits0, [M], Bits).
byte_bits__(0, []).
byte_bits__(Byte, Bits) :-
length(Bits, N),
Bits = [Bit|Bits0],
byte_bits__(Byte0, Bits0),
Byte is Byte0 + Bit*(2 ^ (N-1)).

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

@@ -0,0 +1,663 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The original copyright and licence information is shown below.
Adapted in June 2020 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog.
In order to improve portability and reliability of this library, I
have changed various parts of the original code to make it
conforming to the Prolog ISO standard. I have removed all
occurrences of proprietary non-standard ad hoc data types.
In Scryer Prolog, we represent strings as lists of characters.
This makes them amenable to processing with DCGs and other
built-in mechanisms such as existing predicates on lists.
Scryer Prolog represents lists of characters very compactly,
making it an ideal representation for large amounts of text. It
fully conforms to the ISO standard and is very portable.
This library is intended to be used in tandem with load_html/3
from library(sgml), which creates DOM trees from markup documents.
It can be combined with http_open/3 from library(http/http_open)
to read such documents directly from network connections.
For instance, to inspect all links to Prolog files (*.pl) on
Scryer Prolog's project page, we can use:
:- use_module(library(http/http_open)).
:- use_module(library(sgml)).
:- use_module(library(xpath)).
:- use_module(library(dcgs)).
link_to_pl_file(File) :-
http_open("https://github.com/mthom/scryer-prolog", S, []),
load_html(stream(S), DOM, []),
xpath(DOM, //a(@href), File),
phrase((...,".pl"), File).
Yielding:
?- link_to_pl_file(File).
%@ File = "/mthom/scryer-prolog/blob/master/src/lib/dcgs.pl"
%@ ; File = "/mthom/scryer-prolog/blob/master/src/lib/pio.pl"
%@ ; File = "/mthom/scryer-prolog/blob/master/src/lib/tabling.pl"
%@ ; ... .
Parts of the original functionality may not yet work. Please
consider such parts opportunities for improvements, and file
issues in case you need additional features.
The original copyright information follows.
======================================================================
Author: Jan Wielemaker
E-mail: J.Wielemaker@vu.nl
WWW: http://www.swi-prolog.org
Copyright (c) 2009-2019, University of Amsterdam
VU University Amsterdam
CWI, Amsterdam
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- module(xpath,
[ xpath/3, % +DOM, +Spec, -Value
xpath_chk/3, % +DOM, +Spec, -Value
op(400, fx, //),
op(400, fx, /),
op(200, fy, @)
]).
:- use_module(library(lists),[member/2,memberchk/2,reverse/2]).
:- use_module(library(charsio)).
:- use_module(library(error)).
:- use_module(library(dcgs)).
:- use_module(library(si)).
/** 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](http://www.w3.org/TR/xpath).
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.
*/
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">`:
%
% ```
% //table(@align(lower) = center)
% ```
%
% Get the `width` and `height` of a `div` element as a number,
% and the `div` node itself:
%
% ```
% xpath(DOM, //div(@width(number)=W, @height(number)=H), Div)
% ```
%
% Note that `div` is an infix operator, so parentheses must be
% used in cases like the following:
%
% ```
% xpath(DOM, //(div), Div)
% ```
xpath(DOM, Spec, Content) :-
in_dom(Spec, DOM, Content).
in_dom(//Spec, DOM, Value) :-
!,
element_spec(Spec, Name, Modifiers),
sub_dom(I, Len, Name, E, DOM),
modifiers(Modifiers, I, Len, E, Value).
in_dom(/Spec, E, Value) :-
!,
element_spec(Spec, Name, Modifiers),
( Name == self
-> true
; element_name(E, Name)
),
modifiers(Modifiers, 1, 1, E, Value).
in_dom(A/B, DOM, Value) :-
!,
in_dom(A, DOM, Value0),
in_dom(B, Value0, Value).
in_dom(A//B, DOM, Value) :-
!,
in_dom(A, DOM, Value0),
in_dom(//B, Value0, Value).
in_dom(Spec, element(_, _, Content), Value) :-
element_spec(Spec, Name, Modifiers),
count_named_elements(Content, Name, CLen),
CLen > 0,
nth_element(N, Name, E, Content),
modifiers(Modifiers, N, CLen, E, Value).
element_spec(Var, _, _) :-
var(Var),
!,
instantiation_error(Var).
element_spec(NS:Term, NS:Name, Modifiers) :-
!,
callable_name_arguments(Term, Name0, Modifiers),
star(Name0, Name).
element_spec(Term, Name, Modifiers) :-
!,
callable_name_arguments(Term, Name0, Modifiers),
star(Name0, Name).
callable_name_arguments(Atom, Name, Arguments) :-
atom(Atom),
!,
Name = Atom, Arguments = [].
callable_name_arguments(Compound, Name, Arguments) :-
Compound =.. [Name|Arguments].
star(*, _) :- !.
star(Name, Name).
%! sub_dom(-Index, -Count, +Name, -Sub, +DOM) is nondet.
%
% Sub is a node in DOM with Name.
%
% @param Count is the total number of nodes in the content
% list Sub appears that have the same name.
% @param Index is the 1-based index of Sub of nodes with
% Name.
sub_dom(1, 1, Name, DOM, DOM) :-
element_name(DOM, Name0),
\+ Name \= Name0.
sub_dom(N, Len, Name, E, element(_,_,Content)) :-
!,
sub_dom_2(N, Len, Name, E, Content).
sub_dom(N, Len, Name, E, Content) :-
list_si(Content),
sub_dom_2(N, Len, Name, E, Content).
sub_dom_2(N, Len, Name, Element, Content) :-
( count_named_elements(Content, Name, Len),
nth_element(N, Name, Element, Content)
; member(element(_,_,C2), Content),
sub_dom_2(N, Len, Name, Element, C2)
).
%! count_named_elements(+Content, +Name, -Count) is det.
%
% Count is the number of nodes with Name in Content.
count_named_elements(Content, Name, Count) :-
count_named_elements(Content, Name, 0, Count).
count_named_elements([], _, Count, Count).
count_named_elements([element(Name,_,_)|T], Name0, C0, C) :-
\+ Name \= Name0,
!,
C1 is C0+1,
count_named_elements(T, Name0, C1, C).
count_named_elements([_|T], Name, C0, C) :-
count_named_elements(T, Name, C0, C).
%! nth_element(?N, +Name, -Element, +Content:list) is nondet.
%
% True if Element is the N-th element with name in Content.
nth_element(N, Name, Element, Content) :-
nth_element_(1, N, Name, Element, Content).
nth_element_(I, N, Name, E, [H|T]) :-
element_name(H, Name0),
\+ Name \= Name0,
!,
( N = I,
E = H
; I2 is I + 1,
( nonvar(N), I2 > N
-> !, fail
; true
),
nth_element_(I2, N, Name, E, T)
).
nth_element_(I, N, Name, E, [_|T]) :-
nth_element_(I, N, Name, E, T).
%! modifiers(+Modifiers, +I, +Clen, +DOM, -Value)
%
%
modifiers([], _, _, Value, Value).
modifiers([H|T], I, L, Value0, Value) :-
modifier(H, I, L, Value0, Value1),
modifiers(T, I, L, Value1, Value).
modifier(M, _, _, _, _) :-
var(M),
!,
instantiation_error(M).
modifier(Index, I, L, Value0, Value) :-
implicit_index_modifier(Index),
!,
Value = Value0,
index_modifier(Index, I, L).
modifier(index(Index), I, L, Value, Value) :-
!,
index_modifier(Index, I, L).
modifier(Function, _, _, In, Out) :-
xpath_function(Function),
!,
xpath_function(Function, In, Out).
modifier(Function, _, _, In, Out) :-
xpath_condition(Function, In),
Out = In.
implicit_index_modifier(I) :-
integer(I),
!.
implicit_index_modifier(last).
implicit_index_modifier(last-_Expr).
index_modifier(Var, I, _L) :-
var(Var),
!,
Var = I.
index_modifier(last, I, L) :-
!,
I =:= L.
index_modifier(last-Expr, I, L) :-
!,
I =:= L-Expr.
index_modifier(N, I, _) :-
N =:= I.
xpath_function(self, DOM, DOM). % self
xpath_function(content, Element, Value) :- % content
element_content(Element, Value).
xpath_function(text, DOM, Text) :- % text
text_of_dom(DOM, chars, Text).
xpath_function(text(As), DOM, Text) :- % text(atom)
text_of_dom(DOM, As, Text).
xpath_function(normalize_space, DOM, Text) :- % normalize_space
text_of_dom(DOM, chars, Text0),
normalize_space(Text0, Text).
xpath_function(number, DOM, Number) :- % number
text_of_dom(DOM, chars, Text0),
normalize_space(Text0, Text),
catch(xsd_number_chars(Number, Text), _, fail).
xpath_function(@Name, element(_, Attrs, _), Value) :- % @Name
( atom(Name)
-> memberchk(Name=Value, Attrs)
; compound(Name)
-> Name =.. [AName|AOps],
memberchk(AName=Value0, Attrs),
translate_attribute(AOps, Value0, Value)
; member(Name=Value, Attrs)
).
xpath_function(quote(Value), _, Value). % quote(Value)
xpath_function(self).
xpath_function(content).
xpath_function(text).
xpath_function(text(_)).
xpath_function(normalize_space).
xpath_function(number).
xpath_function(@_).
xpath_function(quote(_)).
translate_attribute([], Value, Value).
translate_attribute([H|T], Value0, Value) :-
translate_attr(H, Value0, Value1),
translate_attribute(T, Value1, Value).
translate_attr(number, Value0, Value) :-
xsd_number_chars(Value, Value0).
translate_attr(integer, Value0, Value) :-
xsd_number_chars(Value1, Value0),
Value is round(Value1).
translate_attr(float, Value0, Value) :-
xsd_number_chars(Value1, Value0),
Value is float(Value1).
% The implementation of these translations is left for later.
% translate_attr(lower, Value0, Value) :- ...
% translate_attr(upper, Value0, Value) :- ...
xpath_condition(Left = Right, Value) :- % =
!,
var_or_function(Left, Value, LeftValue),
process_equality(LeftValue, Right).
xpath_condition(contains(Haystack, Needle), Value) :- % contains(Haystack, Needle)
!,
val_or_function(Haystack, Value, HaystackValue),
val_or_function(Needle, Value, NeedleValue),
( phrase((...,seq(NeedleValue),...), HaystackValue)
-> true
).
xpath_condition(Spec, Dom) :-
in_dom(Spec, Dom, _).
%! process_equality(+Left, +Right) is semidet.
%
% Provides (very) partial support for XSLT functions that can be
% applied according to the XPath 2 specification.
%
% For example the XPath expression in [1], and the equivalent
% Prolog expression in [2], would both match the HTML element in
% [3].
%
% ==
% [1] //table[align=lower-case(center)]
% [2] //table(@align=lower_case(center))
% [3] <table align="CENTER">
% ==
process_equality(Left, Right) :-
var(Right),
!,
Left = Right.
process_equality(Left, lower_case(Right)) :-
!,
downcase_atom(Left, Right).
process_equality(Left, upper_case(Right)) :-
!,
upcase_atom(Left, Right).
process_equality(Left, Right) :-
Left = Right.
var_or_function(Arg, _, Arg) :-
var(Arg),
!.
var_or_function(Func, Value0, Value) :-
xpath_function(Func),
!,
xpath_function(Func, Value0, Value).
var_or_function(Value, _, Value).
val_or_function(Arg, _, Arg) :-
var(Arg),
!,
instantiation_error(Arg).
val_or_function(Func, Value0, Value) :- % TBD
xpath_function(Func, Value0, Value),
!.
val_or_function(Value, _, Value).
%! text_of_dom(+DOM, +As, -Text:(chars | atom)) is det.
%
% Text is the joined textual content of DOM.
text_of_dom(DOM, As, Text) :-
phrase(text_of(DOM), Text0),
( As == chars ->
Text = Text0
; As == atom ->
atom_chars(Text, Text0)
; domain_error(As, atom_or_chars, xpath)
).
text_of(element(_,_,Content)) -->
text_of_list(Content).
text_of([]) -->
[].
text_of([H|T]) -->
text_of(H),
text_of(T).
text_of_list([]) -->
[].
text_of_list([H|T]) -->
text_of_1(H),
text_of_list(T).
text_of_1(element(_,_,Content)) -->
text_of_list(Content).
text_of_1([C|Cs]) --> seq([C|Cs]).
% For now, we use number_chars/2 to parse XML numbers.
% If the need arises, we can extend this to additional
% float constants and syntax that may occur in XML files.
xsd_number_chars(Number, Chars) :-
number_chars(Number, Chars).
normalize_space(Cs0, Cs) :-
must_be(chars, Cs0),
no_leading_whitespace(Cs0, Cs1),
reverse(Cs1, Cs2),
no_leading_whitespace(Cs2, Cs3),
reverse(Cs3, Cs4),
single_intermediate_space(Cs4, Cs).
no_leading_whitespace([], []).
no_leading_whitespace([C0|Cs0], Cs) :-
( char_type(C0, whitespace) ->
no_leading_whitespace(Cs0, Cs)
; Cs = [C0|Cs0]
).
single_intermediate_space([], []).
single_intermediate_space([C0|Cs0], [C|Cs]) :-
( char_type(C0, whitespace) ->
no_leading_whitespace(Cs0, Cs1),
C = ' ',
single_intermediate_space(Cs1, Cs)
; C = C0,
single_intermediate_space(Cs0, Cs)
).

1765
src/loader.pl Normal file

File diff suppressed because it is too large Load Diff

16
src/machine/args.rs Normal file
View File

@@ -0,0 +1,16 @@
use std::collections::BTreeSet;
use std::env;
#[derive(Debug)]
pub struct MachineArgs {
pub add_history: bool,
}
impl MachineArgs {
pub fn new() -> Self {
let args: BTreeSet<String> = env::args().collect();
Self {
add_history: !args.contains("--no-add-history"),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -13,9 +13,9 @@ iterate([Var|VarBindings], [Value|ValueBindings], [ListOfGoalLists | ListsCubed]
iterate(VarBindings, ValueBindings, ListsCubed). iterate(VarBindings, ValueBindings, ListsCubed).
iterate([], [], []). iterate([], [], []).
gather_modules(Attrs, []) :- var(Attrs), !. gather_modules(Attrs, []) :- var(Attrs), !.
gather_modules([Attr|Attrs], [Module|Modules]) :- gather_modules([Module:_|Attrs], [Module|Modules]) :-
'$module_of'(Module, Attr), % write the owning module of Attr to Module.
gather_modules(Attrs, Modules). gather_modules(Attrs, Modules).
call_verify_attributes(Attrs, _, _, []) :- call_verify_attributes(Attrs, _, _, []) :-
@@ -26,27 +26,32 @@ call_verify_attributes([Attr|Attrs], Var, Value, ListOfGoalLists) :-
sort(Modules0, Modules), sort(Modules0, Modules),
verify_attrs(Modules, Var, Value, ListOfGoalLists). verify_attrs(Modules, Var, Value, ListOfGoalLists).
verify_attrs([Module|Modules], Var, Value, [Goals|ListOfGoalLists]) :- error_handler(M, evaluation_error((M:verify_attributes)/3), []).
% error_handler(_, existence_error(procedure, verify_attributes/3), []).
verify_attrs([Module|Modules], Var, Value, [Module-Goals|ListOfGoalLists]) :-
catch(Module:verify_attributes(Var, Value, Goals), catch(Module:verify_attributes(Var, Value, Goals),
error(evaluation_error((Module:verify_attributes)/3), verify_attributes/3), error(E, verify_attributes/3),
Goals = []), error_handler(Module, E, Goals)),
verify_attrs(Modules, Var, Value, ListOfGoalLists). verify_attrs(Modules, Var, Value, ListOfGoalLists).
verify_attrs([], _, _, []). verify_attrs([], _, _, []).
call_goals([ListOfGoalLists | ListsCubed]) :- call_goals([ListOfGoalLists | ListsCubed]) :-
call_goals_0(ListOfGoalLists), call_goals_0(ListOfGoalLists),
call_goals(ListsCubed). call_goals(ListsCubed).
call_goals([]). call_goals([]).
call_goals_0([GoalList | GoalLists]) :- call_goals_0([Module-GoalList | GoalLists]) :-
( var(GoalList), throw(error(instantiation_error, call_goals_0/1)) ( var(GoalList),
throw(error(instantiation_error, call_goals_0/1))
; true ; true
), ),
call_goals_1(GoalList), call_goals_1(GoalList, Module),
call_goals_0(GoalLists). call_goals_0(GoalLists).
call_goals_0([]). call_goals_0([]).
call_goals_1([Goal | Goals]) :- call_goals_1([Goal | Goals], Module) :-
call(Goal), call(Module:Goal),
call_goals_1(Goals). call_goals_1(Goals, Module).
call_goals_1([]). call_goals_1([], _).

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