253 Commits

Author SHA1 Message Date
Mark
b70f121e46 use scryer-modular-bitfield on github, version bump
Some checks failed
CI / build-test (ubuntu-22.04, true, stable, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (windows-latest, true, stable, x86_64-pc-windows-msvc) (push) Has been cancelled
CI / logtalk-test (push) Has been cancelled
CI / style (push) Has been cancelled
CI / build-test (--no-default-features, ubuntu-22.04, true, nightly, wasm32-unknown-unknown, --no-run --no-default-features) (push) Has been cancelled
CI / build-test (macos-11, true, stable, x86_64-apple-darwin) (push) Has been cancelled
CI / build-test (ubuntu-20.04, true, stable, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-22.04, 1.70, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-22.04, beta, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-22.04, nightly, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (ubuntu-22.04, true, stable, i686-unknown-linux-gnu) (push) Has been cancelled
CI / report (push) Has been cancelled
CI / release (push) Has been cancelled
Docker Publish / build (push) Has been cancelled
2024-02-29 10:08:14 -07:00
Mark
641765858f fix misuse of TypeError trait while fixing #2345 2024-02-28 20:47:36 -07:00
Mark
ba362e2fe0 issue callable type error from dynamic_module_resolution if module is not an atom (#2345) 2024-02-28 20:35:38 -07:00
Mark Thom
84d5ce0d1e Merge pull request #2309 from coasys/library-use-case
Iron-out edge cases for library use-case, adding extensive real-world test assertions
2024-02-28 14:34:54 -07:00
Mark Thom
419623d158 Merge pull request #2342 from triska/curve_doc
DOC: Add DocLog comments for reasoning about elliptic curves.
2024-02-24 14:53:18 -07:00
Markus Triska
1dd0c599c6 DOC: Add DocLog comments for reasoning about elliptic curves. 2024-02-24 20:43:57 +01:00
Mark Thom
c7934ca246 Merge pull request #2338 from triska/hmac
ADDED: Hash-based message authentication code (HMAC), using hmac(Key).
2024-02-22 14:48:12 -07:00
Markus Triska
27852eafd7 ADDED: Hash-based message authentication code (HMAC), using hmac(Key). 2024-02-22 20:53:27 +01:00
Mark Thom
d4bde5008d Merge pull request #2337 from triska/master
link to newly available paper on analysis of dose-escalation protocols
2024-02-18 10:05:09 -07:00
Markus Triska
74a525a672 link to newly available paper on analysis of dose-escalation protocols
Many thanks to @dcnorris for this great application and cooperation!
2024-02-18 09:25:29 +01:00
Bennet Bleßmann
e9982dc447 fix build of run_iai bench for wasm32 2024-02-17 00:15:03 +01:00
Bennet Bleßmann
7028baaf8f fix benchmarks being broken for every target except wam32 2024-02-17 00:01:10 +01:00
Bennet Bleßmann
7837c76c74 cfg out benches for wasm32 2024-02-16 23:43:22 +01:00
Bennet Bleßmann
e5ad70c093 fix compilation of wasm32 test and skip to run wasm32 tests 2024-02-16 23:18:21 +01:00
Bennet Bleßmann
7fce688769 undo continue-on-error
- currently  `continue-on-error` is not shown in a usefull way on failiure see <https://github.com/orgs/community/discussions/15452>
2024-02-16 22:23:22 +01:00
Bennet Bleßmann
90c874777c bump ahash lock to 0.8.7
- fix nightly build
- not bumping to latest aka. 0.8.8 as that has a msrv of 1.72.0 and we are only at 1.70.0
2024-02-16 22:15:32 +01:00
Mark Thom
70795134af Merge pull request #2327 from triska/char_type
FIXED: char_type/2 for unbound first argument.
2024-02-11 17:33:07 -07:00
Markus Triska
8f2e9c6b94 FIXED: char_type/2 for unbound first argument.
Surrogate pairs form a gap in valid character codes, see:

    https://github.com/mthom/scryer-prolog/issues/2326#issuecomment-1937864665

Many thanks to @Skgland for the pointer, and to @librarianmage for the
question that spawned this!

This addresses #2326.
2024-02-11 22:19:19 +01:00
Nicolas Luck
ce34ca8f1f continue-on-error if target=wasm32 or rust=nightly 2024-02-09 13:48:45 +01:00
Nicolas Luck
7c632cf165 Reactivate nightly test job with continue-on-error set 2024-02-09 13:44:37 +01:00
Nicolas Luck
b3c5a8db80 Use stable Rust for style/report and deactivate nightly x86_64 target in CI 2024-02-09 13:14:06 +01:00
Nicolas Luck
1d2961e047 Merge branch 'master' into library-use-case 2024-02-09 12:41:01 +01:00
Mark Thom
b43f0979d9 Update src/machine/parsed_results.rs
Co-authored-by: Bennet Bleßmann <bennet.blessmann+github@googlemail.com>
2024-02-08 19:10:29 -07:00
Mark
539a1aee2c fix tests broken by singleton 2024-02-07 21:46:32 -07:00
Mark Thom
8e323b9dfa Merge pull request #2322 from triska/char_code
ENHANCED: Bi-directional char_code/2, addressing #2321.
2024-02-07 18:49:13 -07:00
Markus Triska
53b7d9eec9 ENHANCED: Bidirectional char_type/2, addressing #2321.
Suggested by @librarianmage, many thanks!
2024-02-07 20:57:28 +01:00
Mark Thom
89d3451767 Merge pull request #2319 from triska/clpz_t
ADDED: clpz_t/2, generalizing support for use with library(reif)
2024-02-06 18:11:40 -07:00
Markus Triska
aa98a7e7d6 ADDED: clpz_t/2, generalizing support for use with library(reif)
This was suggested and contributed by @librarianmage in:

    https://github.com/mthom/scryer-prolog/issues/2225#issuecomment-1890801923

Many thanks!

If anyone can find a better predicate name, please let us know any time!
2024-02-06 19:05:29 +01:00
Mark
614850ab1d fmt 2024-02-05 18:34:57 -07:00
Mark Thom
140a15f9cb Merge pull request #2317 from aarroyoc/all-modules-test
Add all_mdoules test and fix library(csv)
2024-02-04 15:09:13 -07:00
Adrián Arroyo Calle
ec4a8745e7 Add all_mdoules test and fix library(csv) 2024-02-04 21:34:01 +01:00
Mark
673329ddb7 fix reverted tests, ensure files are loaded before goals (#2315) 2024-02-03 11:49:26 -07:00
Mark
6a421dd8b0 fix broken tests 2024-02-02 17:04:47 -07:00
Mark
de6c460a51 treat consultation of command line modules as regular goals (#2314) 2024-02-02 15:35:00 -07:00
Mark
75a94fd0b3 fmt 2024-02-02 11:49:02 -07:00
Mark
7573c64087 load .scryerrc before files and goals (#1775, #2313) 2024-02-02 10:54:28 -07:00
Mark
cbb422f69d record stub choice point as block 2024-02-01 09:26:58 -07:00
Nicolas Luck
06f198bc57 Test show problem with nonexistent predicate 2024-02-01 14:14:50 +01:00
Nicolas Luck
6586657658 fmt 2024-02-01 13:53:21 +01:00
Nicolas Luck
a0e598b97e clippy 2024-02-01 13:51:37 +01:00
Nicolas Luck
8a6ea29c45 Fix all orderings in integration assertions 2024-02-01 13:43:51 +01:00
Nicolas Luck
e1b0ba466b Remove test with long program literals, not needed 2024-02-01 13:00:49 +01:00
Nicolas Luck
f35d6287ab More ordering adjustments 2024-02-01 12:59:10 +01:00
Mark
53028a9c2a fix style errors 2024-01-31 17:32:32 -07:00
Mark
33fc2ed10c index stub choice point correctly 2024-01-31 17:30:13 -07:00
Nicolas Luck
bc02fb3754 Extract failing assertion as single test case 2024-01-31 15:03:28 +01:00
Nicolas Luck
48b2379fe5 Adjust some first result orderings in integration assertions 2024-01-30 12:54:12 +01:00
Mark
7de693eb23 check for True or False Query Resolution unconditionally 2024-01-29 15:06:56 -07:00
Nicolas Luck
7bb9c00356 fmt 2024-01-29 18:44:20 +01:00
Nicolas Luck
9aacfff35d Add more test cases to differentiate usage of discontiguous 2024-01-29 16:49:03 +01:00
Nicolas Luck
2e728c7051 Minimal reproduction of faulty behaviour seen in integration tests 2024-01-29 15:26:06 +01:00
Nicolas Luck
7e973a6da6 Merge branch 'master' into library-use-case 2024-01-26 17:21:47 +01:00
Nicolas Luck
cb014095ad Add expected results to integration test
Results are logs of what we get with old toplevel-based version of lib_machine. These are also congruent with what our tests logged out based on SWI.
2024-01-26 17:18:57 +01:00
Mark
6fe8f64835 scan registers of instructions leading to verify_attributes interrupt to find the number of registers to save (#2307) 2024-01-25 23:59:54 -07:00
Mark
6111f72b24 add non chunk traversing cut family instructions to is_head_instr (#2307) 2024-01-25 23:25:06 -07:00
Mark Thom
43041971b7 Merge pull request #2308 from triska/small_format_improvements
Small format improvements
2024-01-25 14:17:18 -07:00
Markus Triska
3505cc3ba0 update answers using latest toplevel output 2024-01-25 21:33:27 +01:00
Markus Triska
05ba5f4358 rely on recently improved argument indexing for determinism
This is possible as of 4fd37335f5.
2024-01-25 21:31:08 +01:00
Mark
bc616ca7d8 move call_residue_vars/2 from atts.pl to iso_ext.pl 2024-01-25 12:40:24 -07:00
Mark
99131131af don't mark temp variables as safe through argument_to_value (#2307) 2024-01-25 12:34:21 -07:00
Mark Thom
44b945a5f3 Merge pull request #2306 from triska/dcg_representation
No longer use (->)//2 in library(clpb)
2024-01-23 15:22:17 -07:00
Mark Thom
3bff923331 Merge pull request #2305 from mthom/dependabot/cargo/h2-0.3.24
Bump h2 from 0.3.22 to 0.3.24
2024-01-23 13:50:17 -07:00
Markus Triska
a6ccf95076 improve error context
Source: https://github.com/mthom/scryer-prolog/issues/2304#issuecomment-1906434756
2024-01-23 21:43:34 +01:00
Markus Triska
665f319a0e no longer use (->)//2, since it is implementation defined
This addresses #2304.
2024-01-23 21:42:36 +01:00
dependabot[bot]
fe31afce6b Bump h2 from 0.3.22 to 0.3.24
Bumps [h2](https://github.com/hyperium/h2) from 0.3.22 to 0.3.24.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/v0.3.24/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.22...v0.3.24)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-23 18:20:39 +00:00
Mark Thom
ac07d2dfc8 Merge pull request #2303 from mthom/dependabot/cargo/shlex-1.3.0
Bump shlex from 1.2.0 to 1.3.0
2024-01-23 11:19:53 -07:00
dependabot[bot]
71165c6984 Bump shlex from 1.2.0 to 1.3.0
Bumps [shlex](https://github.com/comex/rust-shlex) from 1.2.0 to 1.3.0.
- [Changelog](https://github.com/comex/rust-shlex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/comex/rust-shlex/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 21:53:43 +00:00
Mark Thom
eab3bff78b Merge pull request #2302 from triska/dcg_representation
Throw representation errors for unsupported DCG grammar control constructs
2024-01-22 00:26:23 -07:00
Markus Triska
81dba11ab1 use newly available false//0 2024-01-21 22:53:22 +01:00
Markus Triska
388fa5baa9 no longer use (->)//2, since it is implementation dependent 2024-01-21 22:50:36 +01:00
Markus Triska
fe3241c07c remove checks that are now no longer needed due to representation errors 2024-01-21 10:28:19 +01:00
Markus Triska
0559ddca2a ENHANCED: Throw representation errors for DCG bodies that use unsupported constructs.
This addresses #2285.
2024-01-21 10:28:19 +01:00
Markus Triska
98a046500f MODIFIED: (->)/2 in DCGs is now only supported in the context of if-then-else.
That is, ( If -> Then ; Else ) is still supported, but ( If -> Then ) not.
2024-01-21 10:28:19 +01:00
Mark Thom
58cd0d1669 Merge pull request #2301 from aarroyoc/docs-copy-term-3
Move copy_term/3 to library(iso_ext)
2024-01-19 10:18:59 -07:00
Mark Thom
6421fe10f8 Merge pull request #2300 from aarroyoc/fix-2298
Throw error in call_residue_vars/2
2024-01-19 10:15:35 -07:00
Adrián Arroyo Calle
99c85459a7 Move copy_term/3 to library(iso_ext) 2024-01-19 17:33:45 +01:00
Adrián Arroyo Calle
958bf51648 Throw error in call_residue_vars/2 2024-01-19 16:58:26 +01:00
Mark
e3aa85e2a2 add better no op detection in char_reader.rs (#2297) 2024-01-16 21:50:53 -07:00
Mark
29ced36a79 add #2293 tests, advance continuable HeapPStrIters away from unpacked compounds (#2293) 2024-01-14 15:07:33 -07:00
Mark
f9a5c2d341 fix additional tests, make better use of existing code (#2293) 2024-01-13 12:01:54 -07:00
Mark
dde03718e1 fix additional tests (#2293) 2024-01-12 22:01:01 -07:00
Mark
60d34bea70 add missing CStr branch to unify_partial_string (#2293) 2024-01-12 13:58:46 -07:00
Mark Thom
6fb3b61441 Merge pull request #2276 from triska/hophrase
ADDED: Support for higher-order non-terminals phrase//[2,3]
2024-01-11 11:56:10 -07:00
Mark Thom
11ca168175 Merge pull request #2281 from bakaq/miri
Initial Miri support
2024-01-10 12:13:14 -07:00
bakaq
f02c0eacd8 Make "cargo miri test" actually run 2024-01-09 04:38:53 -03:00
Mark
72a566d2f8 fix cargo fmt --check 2024-01-08 18:12:31 -07:00
Mark Thom
b56ae28c45 Merge pull request #2280 from notoria/clpz
Special case for `(^)/2`
2024-01-08 17:39:18 -07:00
Mark
cd89d71e0c check for PStr in eager stackful preorder iterator before adding tail of PStrOffset (#2272) 2024-01-08 17:36:41 -07:00
notoria
902cd5c3ea Special case for (^)/2
The only solutions for `Z #= X^Z` are `X = Z, X in -1\/1`. Also helps
avoid big numbers.
2024-01-08 21:50:02 +01:00
Mark Thom
f9eadc8e6a Merge pull request #2278 from notoria/clpz
Small fixes for `CLP(ℤ)`
2024-01-07 18:59:38 -07:00
Mark Thom
7b8c8fdda7 Merge pull request #2277 from triska/master
Correct arithmetic disequalities accidentally broken in f5d9a67f36
2024-01-07 18:59:18 -07:00
notoria
1f3de74cbd Fix bound computation in (^)/2
?- A in 0..1, B in-1..0, A^2 #= B.
   false. % unexpected.
?- A^2 #= B, A in 0..1, B in-1..0.
   A = 0, B = 0.
2024-01-07 20:19:40 +01:00
notoria
770ead9c05 Remove min/2 and max/2 when possible
?- A #= min(A,B).
   clpz:(B#>=A), clpz:(A#=min(A,B)). % unexpected.
?- A #= min(A,0).
   clpz:(A in inf..0), clpz:(A#=min(A,0)). % unexpected.
2024-01-07 20:15:28 +01:00
notoria
fe371ff1d1 Fix min/2 and max/2
?- X in 0..1, Y in 2..4, Z #= max(X,Y).
   clpz:(X in 0..1), clpz:(Z#=max(X,Y)), clpz:(Z in 2..4), clpz:(Z#>=Y), clpz:(Y in 2..4). % unexpected.
?- X in 0..1, Y in 2..4, Z #= max(Y,X).
   Y = Z, clpz:(X in 0..1), clpz:(Y in 2..4).
2024-01-07 20:14:32 +01:00
Markus Triska
2f783f0aef Correct arithmetic inequalities accidentally broken in f5d9a67f36.
Example:

    ?- X #>= Y.
    %@    clpz:(X#>=Y).
2024-01-07 09:33:54 +01:00
Mark Thom
92b85d4ba6 Merge pull request #2264 from Skgland/fix-os-argv-1
Fix os:argv/1 in case the raw args contain multiple `--`
2024-01-06 15:52:53 -07:00
Markus Triska
e702fe5c68 ADDED: Support for higher-order non-terminals phrase//[2,3]
These non-terminals take a grammar rule body and additional arguments
as arguments. These arguments are appended to the first argument.

A key motivation for the introduction of these non-terminals is found
in the discussion and sample code provided by @bakaq in:

    https://github.com/mthom/scryer-prolog/discussions/2260

In this way, portable higher-order DCG programming is possible while
keeping the logical grammar rule expansion implementation dependent.

Example:

    ?- phrase(phrase('.', a, []), Cs).
       Cs = "a".
2024-01-06 14:02:13 +01:00
Mark Thom
ed92e1b4f4 Merge pull request #2275 from triska/master
Prevent assertion of clauses for (-->)/2 when DCGs are enabled.
2024-01-05 12:33:05 -07:00
Markus Triska
f2b63d1689 Prevent assertion of clauses for (-->)/2 when DCGs are enabled.
This addresses #1508.
2024-01-05 19:04:53 +01:00
Mark
d51defed06 fix cargo --fmt check error 2024-01-04 13:56:57 -07:00
Mark
851ea2c45b add missing permission error in compile_assert (#2271) 2024-01-04 13:53:46 -07:00
Mark
ec97ee5d41 fix acceptance of invalid syntax, update iso_conformity_tests.pl (#2270) 2024-01-04 13:14:58 -07:00
Skgland
78b83ca1e1 simplify os:argv/1 fix 2024-01-04 19:26:10 +01:00
Skgland
ea0130d114 use once to limit solutions instead of cut 2024-01-04 18:14:32 +01:00
Skgland
e66e7c5034 add another test case for os:argv/1 2024-01-04 18:12:38 +01:00
Mark Thom
ff010678c3 Merge pull request #2269 from aarroyoc/fix-2267
Use reqwest async and use futures::executor
2024-01-03 15:40:48 -07:00
Mark Thom
5abc72cc8b Merge pull request #2268 from aarroyoc/docs-toplevel
Small fixes in documentation and toplevel.pl
2024-01-03 14:52:34 -07:00
Adrián Arroyo Calle
fac8866fb4 Use reqwest async and use futures::executor 2024-01-03 22:43:22 +01:00
Adrián Arroyo Calle
d58e91303b Small fixes in documentation and toplevel.pl 2024-01-03 19:52:50 +01:00
Skgland
8bd0317e9c fix os:argv(V) not completing 2024-01-02 21:43:38 +01:00
Skgland
76b1167842 fix os:argv 2024-01-02 20:54:37 +01:00
Skgland
7537686d8f cover both args ending in -- and args not ending in -- for os:argv/1 test 2024-01-02 20:48:38 +01:00
Skgland
6fc340c4db remove unnecessary true 2024-01-02 20:27:14 +01:00
Skgland
38f25af47d cover trailing -- and os:argv([]) in os_argv test 2024-01-02 20:23:53 +01:00
Skgland
91df533834 fix os:argv/1 2024-01-02 19:55:27 +01:00
Skgland
9a05ca0667 add failing test for os:argv issue
See https://github.com/mthom/scryer-prolog/pull/2263#issuecomment-1874400820
2024-01-02 19:51:09 +01:00
Mark
ccf581d86f expand module names in transitive goals (#2255) 2024-01-02 11:34:38 -07:00
Mark
1dc546ac36 fix cargo fmt --check 2024-01-02 10:08:00 -07:00
Mark
372b4bca46 mark anonymous variables in compile_is_call (#2257) 2024-01-02 10:06:07 -07:00
Mark Thom
5c89029462 Merge pull request #2263 from aarroyoc/docs-toplevel
Move argv/1 to library(os)
2024-01-02 09:19:10 -07:00
Mark Thom
a475a8a899 Merge pull request #2262 from triska/faster_global_cardinality
ENHANCED: Suspend propagation during filtering in global_cardinality/2.
2024-01-02 09:18:42 -07:00
Adrián Arroyo Calle
47b5ae7984 Move argv/1 to library(os) 2024-01-02 13:16:11 +01:00
Markus Triska
f5d9a67f36 ENHANCED: Suspend propagation during filtering in global_cardinality/2.
This allows subsequently invoked constraints to take the entire
filtering results into account, instead of being invoked when the
obtained information is not yet entirely used.

The SICStus-style attributed variables mechanism of Scryer Prolog
automatically prevents very subtle interaction problems that can arise
in systems that do not give all variables that are involved in a
unification an opportunity to schedule their propagators.

An example of such a subtle interaction is:

    ?- tuples_in([[A,C,B]], [[3,1,3],[4,2,4]]),
       global_cardinality([A,B,D], [3-1,4-2]),
       A = 4.

A = 4 causes pgcc_check/1 and pgcc/2 to be queued in the fast and slow
queue, respectively. In the fast queue, there is also rel_tuple/2,
which is worked off after gcc_check/1 and simultaneously instantiates
both C and B (to 2 and 4, respectively). Instantiation of C schedules
do_queue//0 from verify_attributes/3. Note that C does not participate
in the global_cardinality/2 constraint.

Critically, B also gets an opportunity to schedule its propagators in
this case, so another gcc_check/1 is run before gcc_global/2!
2024-01-01 10:50:06 +01:00
Mark Thom
1f90dbec20 Merge pull request #2259 from bakaq/comment_warnings
Emit warnings as Prolog comments
2024-01-01 00:33:58 -07:00
Mark Thom
d713456e29 Merge pull request #2258 from triska/faster_scalar_product
ENHANCED: Suspend propagation during filtering in scalar product constraints.
2024-01-01 00:33:36 -07:00
bakaq
d9d90d1ae8 Emit warnings as Prolog comments 2023-12-30 17:39:20 -03:00
Markus Triska
62b61107e0 ENHANCED: Suspend propagation during filtering in scalar product constraints.
This allows subsequently invoked constraints to take the entire
filtering results into account, instead of being invoked when the
obtained information is not yet entirely used.

It speeds up programs such as the one in:

    https://github.com/triska/clpz/issues/26
2023-12-30 10:30:25 +01:00
Mark Thom
1e0fa56786 Merge pull request #2252 from aarroyoc/bump-versions
Update ring and bump versions in Cargo.lock
2023-12-29 13:10:19 -07:00
Mark Thom
97d5bf7e97 Merge pull request #2254 from bakaq/issue_1404
Parse as much of a list as possible as a partial string
2023-12-29 10:40:19 -07:00
Mark
75302ab716 check that Target is an atom in scoped_clause_to_evacuable (#2248) 2023-12-29 10:12:08 -07:00
Adrián Arroyo Calle
742de9e77c Update ring and bump versions in Cargo.lock 2023-12-28 20:23:01 +01:00
bakaq
921046e886 Parse as much of a list as possible as a partial string #1404 2023-12-28 15:12:32 -03:00
Mark Thom
781f4afe25 Merge pull request #2251 from triska/crrl_ed25519
improve structure, naming and explanation of PKCS#8v2 encoding
2023-12-27 17:37:42 -07:00
Markus Triska
10158f62e0 improve structure, naming and explanation of PKCS#8v2 encoding 2023-12-27 21:12:52 +01:00
Mark
42d6749501 throw errors from char_reader.rs and get_n_chars when reading bad UTF8 data (#2244) 2023-12-26 12:31:04 -07:00
Mark Thom
2fa46e6b6c Merge pull request #2245 from triska/crrl_ed25519
ENHANCED: Use crrl for Ed25519 signing and signature verification.
2023-12-26 10:09:08 -07:00
Markus Triska
47ec5eb6c6 ENHANCED: Use crrl for Ed25519 signing and signature verification.
The main motivation for this change is the introduction of the newly
available predicate ed25519_seed_keypair/2, allowing to generate a key
pair from a given seed. In this way, a key pair can be dynamically
generated from (for example) a password, using crypto_password_hash/3
in combination with crypto_data_hkdf/4 to generate the seed. The
advantage of this method is that the private key need not be stored at
all anywhere.

It is not possible to add a corresponding feature to ring, since it is
closed as "not planned": https://github.com/briansmith/ring/issues/1003

I also used this opportunity to move more of the logic to Prolog. We
now have total control of the key pair representation, and I also
changed the representation to conform to the PKCS#8 v2 standard,
something that only later ring versions do, while still being
backwards compatible with tools that produce a wrong representation
including earlier ring versions.

Another great advantage we get from this change is that the Ed25519
predicates now also run on the 32-bit and WASM versions of Scryer.
2023-12-26 07:33:44 +01:00
Mark Thom
799035c692 Merge pull request #2243 from bakaq/issue_2241
Fix anonymous attributed variables not showing in toplevel
2023-12-22 09:53:40 -07:00
Mark Thom
6c07256dc5 Merge pull request #2242 from triska/starting
explain starting Scryer Prolog
2023-12-22 09:52:56 -07:00
bakaq
20c6a327ea Fix anonymous attributed variables not showing in toplevel #2241 2023-12-22 13:07:09 -03:00
Markus Triska
bcfdc812f9 explain starting Scryer Prolog 2023-12-22 08:36:29 +01:00
Mark
373b08a91e fix off-by-one error caused logtalk test to crash 2023-12-21 12:14:12 -07:00
Mark
a7154968d8 fix cargo fmt --check 2023-12-21 10:24:01 -07:00
Mark
46e432c4d9 restore [o|i]ip zeroing to trust and fix '$get_clause_p'/3 (#2238) 2023-12-20 20:45:15 -07:00
Mark Thom
44a61fa72c Merge pull request #2218 from bakaq/when
Add when/2 and when_si/2
2023-12-20 13:42:46 -07:00
Mark
299df50066 clamp lower_bound_of_target_clause index (#840) 2023-12-18 15:21:30 -07:00
Mark
335330690f don't replace undefined module predicates (#2232) 2023-12-18 14:57:22 -07:00
Mark
f30cee7655 fix retraction of local dynamic predicates (#2215, #2232) 2023-12-18 13:28:45 -07:00
Mark
b943afb7c7 fix '$get_clause_p' whose old implementation was broken by the introduction
of lookahead indexing
2023-12-18 13:28:45 -07:00
Mark
30f3818c66 improve performance of lower_bound_of_target_clause (#1598) 2023-12-18 13:28:45 -07:00
Mark Thom
13624568b5 Merge pull request #2233 from aarroyoc/fix-functor-2214
Throw ValidType::Atom error in functor
2023-12-17 20:20:50 -07:00
Mark Thom
e4f64581ec Merge pull request #2229 from aarroyoc/fix-backslash-docs
Fix backslash docs
2023-12-17 20:20:41 -07:00
Adrián Arroyo Calle
d132227860 Throw ValidType::Atom error in functor 2023-12-17 12:38:46 +01:00
Adrián Arroyo Calle
45abb4703d Fix backslash docs 2023-12-16 19:01:39 +01:00
Mark Thom
a66b93c96f Merge pull request #2226 from mthom/dependabot/cargo/zerocopy-0.7.31
Bump zerocopy from 0.7.25 to 0.7.31
2023-12-15 11:33:03 -07:00
Mark
e5ea95db8d Revert "improve performance of lower_bound_of_target_clause (#1598)"
This reverts commit 8938331915.
2023-12-15 00:42:58 -07:00
Mark
8938331915 improve performance of lower_bound_of_target_clause (#1598) 2023-12-15 00:39:36 -07:00
dependabot[bot]
749767539a Bump zerocopy from 0.7.25 to 0.7.31
Bumps [zerocopy](https://github.com/google/zerocopy) from 0.7.25 to 0.7.31.
- [Release notes](https://github.com/google/zerocopy/releases)
- [Changelog](https://github.com/google/zerocopy/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google/zerocopy/compare/v0.7.25...v0.7.31)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-15 03:52:00 +00:00
Mark
399b50b4d7 fix use of copy_term/3 for attribute goals in toplevel (#888, #2057, #2217) 2023-12-13 12:27:59 -07:00
Mark
82f3731c97 fix cargo style failure 2023-12-11 15:17:02 -07:00
Mark
658258c8c3 dereference port address in socket_server_open (#2222) 2023-12-11 14:38:20 -07:00
Mark
d14cce2374 restore print_tcp_listener in printer (#2221) 2023-12-11 13:29:27 -07:00
bakaq
f411fb1eb3 Tests for when/2 and solved bug 2023-12-11 12:28:59 -03:00
bakaq
6a30ccc907 Add when/2 and when_si/2 2023-12-10 20:55:50 -03:00
Mark Thom
1e1426cab0 Merge pull request #2207 from infogulch/ci-build-before-test
CI: Make test step output easier to read
2023-12-06 13:45:46 -07:00
Mark Thom
2e750671ca Merge pull request #2211 from Skgland/file-based-cli-tests
fix missing .pl file for singleton test case and move .pl files under `<testcase>.in/`
2023-12-06 13:27:14 -07:00
Skgland
147e47c8eb fix missing .pl file for singleton test case and move .pl files under <testcase>.in/
follow up to #2205
2023-12-06 21:21:28 +01:00
infogulch
7208916230 Clean up test step by compiling in build step 2023-12-05 20:51:04 -06:00
Mark Thom
43556f6df0 Merge pull request #2205 from Skgland/file-based-cli-tests
File based cli tests using trycmd
2023-12-05 14:45:00 -07:00
Mark Thom
c0c33392f1 Merge pull request #2203 from coasys/library-use-case
Don’t include unbound variables in results returned from run_query()
2023-12-05 13:14:30 -07:00
Nicolas Luck
77ce5a9586 Result with one empty match should be false 2023-12-05 15:00:07 +01:00
Mark Thom
8665722367 Merge pull request #2204 from mthom/remove_not_as_non_terminal
remove (\+)//1 (#1511)
2023-12-04 15:49:26 -07:00
Skgland
57b52fbe90 fix windows test by not disabling normalization
- this re-enables line ending and file path normalization
  - the latter might be unwanted, but there is only a shared flag
  - this might result in too loose matching of / and \ in stdout and stderr as prolog syntax may be mistaken for a path
2023-12-04 22:59:52 +01:00
Skgland
b052b62b16 fix formatting 2023-12-04 22:41:25 +01:00
Skgland
4df7e94d56 rename to match toml file
- goals.pl is used by both compound_goal.toml and multiple_goals.toml therefor renamed the later to share a common prefix with goals.pl, to keep them together
2023-12-04 22:23:04 +01:00
Skgland
fcf2e2db05 move .pl files from tests-pl next to the .toml of the test that uses them 2023-12-04 22:16:04 +01:00
Skgland
1c52de9ab1 File-oriented testing
inspired by #2191 but for the `run_top_level_test_with_args`s and  `run_top_level_test_no_args instead of the `load_module_test`s
2023-12-04 22:02:46 +01:00
Mark
9cd762d88a remove (\+)//1 (#1511) 2023-12-04 13:51:44 -07:00
Mark
eddcdaabef correct cargo fmt errors 2023-12-04 12:20:54 -07:00
Mark
2c76943d35 emit callable type errors from call/1 and phrase/3 when appropriate (#2202) 2023-12-04 12:20:14 -07:00
Nicolas Luck
24e3e1794e cargo fmt 2023-12-04 20:18:41 +01:00
Nicolas Luck
99055b553a Don’t include unbound variables in results returned from run_query 2023-12-04 20:10:06 +01:00
Nicolas Luck
576f13df60 Merge branch 'master' into library-use-case 2023-12-04 20:06:14 +01:00
Mark
004201abda add module name and missing predicate key to module_does_not_contain_claimed_export error (#2195) 2023-12-04 11:19:44 -07:00
Mark Thom
6899c6b63c Merge pull request #2201 from wkral/revert-2200-percent-graphic-char
Revert "Add '%' to the list of graphic_chars (#2199)"
2023-12-04 10:46:37 -07:00
William Kral
7f5c56a2a8 Revert "Add '%' to the list of graphic_chars (#2199)" 2023-12-03 22:32:49 -08:00
Mark Thom
92e07d2be1 Merge pull request #2200 from wkral/percent-graphic-char
Add '%' to the list of graphic_chars (#2119)
2023-12-03 23:02:05 -07:00
William Kral
aff1fdc6aa Add '%' to the list of graphic_chars (#2199) 2023-12-03 21:30:05 -08:00
Mark Thom
b0b1efcc49 Merge pull request #2194 from triska/clpz_monotonic
support (#=)/3 and (#<)/3 also in the monotonic execution mode
2023-12-02 20:16:59 -07:00
Markus Triska
eb575b9882 support (#=)/3 and (#<)/3 also in the monotonic execution mode 2023-12-02 22:33:56 +01:00
Mark
e667abb143 move propagated_var_nums insertion to proper scope (#2183) 2023-12-01 22:35:36 -07:00
Mark
347ee0211b propagate variable hits to parent branches in push_missing_vars (#2183) 2023-11-30 19:46:32 -07:00
Mark Thom
4f4feba6fc Merge pull request #2187 from triska/term_si
ADDED: term_si/1, true for terms
2023-11-29 13:52:38 -07:00
Markus Triska
8cb4dfef62 ADDED: term_si/1, true for terms
One use case is to ensure that once/1 is safe to use:

    term_si(Goal),
    once(Goal)

In such cases, Goal is ground and can yield at most one solution,
therefore once/1 does not remove any solutions.
2023-11-29 21:25:09 +01:00
Mark
de452bb2c2 fix overeager detection of (->)/2 in if position of (;)/2 (#2183) 2023-11-29 11:05:04 -07:00
Mark Thom
e8d8b09e52 Merge pull request #2186 from aarroyoc/cpu-now-wasm32
Implement $cpu_now in wasm32
2023-11-28 16:17:44 -07:00
Mark
631674db29 open pl files instead of directories (#2014) 2023-11-28 15:32:06 -07:00
Mark Thom
bc3ae52c7a Merge pull request #2184 from mthom/dependabot/cargo/openssl-0.10.60
Bump openssl from 0.10.57 to 0.10.60
2023-11-28 15:31:53 -07:00
Adrián Arroyo Calle
4c795018b4 Implement $cpu_now in wasm32 2023-11-28 23:30:02 +01:00
dependabot[bot]
651b7b2f59 Bump openssl from 0.10.57 to 0.10.60
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.57 to 0.10.60.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.57...openssl-v0.10.60)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 21:43:23 +00:00
Mark
5cb207d8b3 allocate cut variables properly (#2183) 2023-11-28 14:38:48 -07:00
Mark
de5de0cfaf allocate cut variables as temporaries when in non-allocating clause (#2183) 2023-11-28 12:58:54 -07:00
Mark
b8ef367824 bind missing module atom in retract/1 (#2182) 2023-11-25 20:59:04 -07:00
Mark Thom
f1458b772e Merge pull request #2169 from infogulch/flame
Benchmark improvements
2023-11-24 18:57:13 +01:00
Mark Thom
4181c9fab2 Merge branch 'master' into flame 2023-11-24 18:00:55 +01:00
Mark
1fbc7f9842 remove redundant running count increment in mark_non_callable (#2176) 2023-11-23 22:05:31 -07:00
Mark Thom
73ed08a412 Merge pull request #2179 from mthom/fix_stress_integration_test
Fix stress integration test
2023-11-23 21:31:12 +01:00
Mark
7cac13aefa set b0 to b for run_query call 2023-11-23 12:55:55 -07:00
Mark Thom
2e114df4c7 Merge pull request #2173 from aarroyoc/js-eval
Add js_eval/2 for wasm32
2023-11-22 21:52:49 +01:00
Adrián Arroyo Calle
36ab590c4e Add js_eval/2 for wasm32 2023-11-22 21:21:19 +01:00
Mark Thom
20da3427c1 Merge pull request #2177 from mthom/remove_assertx
Remove assert(a,z)/2 (#1975)
2023-11-22 17:54:55 +01:00
Mark
2811660fa7 remove assert(a,z)/2 (#1975) 2023-11-22 09:24:07 -07:00
Mark
ef8eb935d4 add CutPoint to select_switch_on_term_index (#2176) 2023-11-22 08:57:23 -07:00
infogulch
11832b524b Expose inference counts on Machine; publish to CI 2023-11-21 21:53:29 -06:00
infogulch
503e45eae1 Upgrade to latest iai for summary output 2023-11-21 21:53:19 -06:00
Mark Thom
041ec06dbc Merge pull request #1999 from triska/time_inferences
ENHANCED: time/1 now shows the number of inferences
2023-11-21 23:04:26 +01:00
Mark
3841b29db8 fix copy_term/2 variable copying bug in lists (#923, #2127) 2023-11-21 14:52:35 -07:00
infogulch
1c94624958 Benchmark improvements
* CI: Clean offline; display cleaned bytes; one artifact upload
* Add missing csv bench to iai benchmarks
* Generate flamegraphs for benchmarks
* Validate benchmark results separately
2023-11-21 13:54:16 -06:00
infogulch
c6f3700de1 Remove accidentally committed files 2023-11-21 13:54:16 -06:00
Markus Triska
186bba9d75 ENHANCED: time/1 now shows the number of inferences
Example:

    ?- time(member(X, "abc")).
       % CPU time: 0.000s, 1 inference
       X = a
    ;  % CPU time: 0.000s, 3 inferences
       X = b
    ;  % CPU time: 0.000s, 3 inferences
       X = c.

This is an initial step towards addressing #1039.
2023-11-21 20:36:44 +01:00
Mark
a3e83d59be fix error detection in int_pow (#2161) 2023-11-21 08:32:26 -07:00
Mark
54166b91eb add global_count inference counter that is always incremented (#2009) 2023-11-20 16:24:31 -07:00
Mark
1e5bb2d3da fix dif:vars_remove_goal/2 (#2175), untabify forms.rs to appease format check 2023-11-20 14:21:18 -07:00
Mark
8d9a759a7d remove unnecessary spaces around (|) (#2164) 2023-11-19 08:46:20 -07:00
Mark Thom
f32b035287 Merge pull request #2168 from aarroyoc/update-web-2023-11-15
Remove Scryer Prolog meetup banner :(
2023-11-16 21:07:25 +01:00
Mark Thom
a8e6930d19 Merge pull request #2159 from mthom/dependabot/cargo/rustix-0.38.21
Bump rustix from 0.38.14 to 0.38.21
2023-11-16 21:07:03 +01:00
dependabot[bot]
7fa4cb7ee8 Bump rustix from 0.38.14 to 0.38.21
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.38.14 to 0.38.21.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v0.38.14...v0.38.21)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-16 19:27:40 +00:00
Mark Thom
df5855dbda Merge pull request #2167 from infogulch/ci-checks-permission
Add checks permission to ci workflow
2023-11-16 09:30:27 +01:00
Adrián Arroyo Calle
e7f5ba6ca1 Remove Scryer Prolog meetup banner :( 2023-11-15 22:42:18 +01:00
Mark Thom
bcb68fcc3d fix sign/1 (#2157) 2023-11-15 20:38:24 +01:00
infogulch
e5ee918063 Add checks permission to ci workflow
This should allow dependabot builds to complete
2023-11-15 13:07:45 -06:00
Mark Thom
4be320abc8 fix shr crash (#2158) 2023-11-14 08:15:19 +01:00
Mark Thom
300f5f817f Merge pull request #2165 from infogulch/bench-csv
Bench csv
2023-11-14 07:32:07 +01:00
Mark Thom
574a4d0758 Merge pull request #2166 from triska/more_tests
mention more passing tests
2023-11-14 07:31:52 +01:00
Markus Triska
a02bd46094 mention more passing tests 2023-11-14 04:09:03 +01:00
infogulch
cab80d3fa2 Fix lint 2023-11-13 15:41:15 -06:00
infogulch
98d8b00d7a Add csv parsing benchmark #1403 2023-11-13 15:41:06 -06:00
infogulch
40608dc8d4 Simplify benchmark setup; support mutating machine 2023-11-13 15:40:27 -06:00
Mark Thom
3b7d4a7b36 Merge pull request #2153 from infogulch/benchmark
Add benchmarks
2023-11-12 07:21:25 +01:00
Joe Taber
f704fcb41d Fix formatting issues 2023-11-11 13:33:19 -06:00
infogulch
8570f119c0 Add benchmarks using library interface 2023-11-11 13:32:34 -06:00
infogulch
1656d9d7ad CI: Setup rust in new action; split style & report actions 2023-11-11 13:32:34 -06:00
Mark Thom
50c64b8512 correct style failures 2023-11-11 18:57:47 +01:00
Mark Thom
83b9c6184c fix permanent variable handling over branches and cells (#2154) 2023-11-11 17:20:22 +01:00
Nicolas Luck
0ad479498c Activate run_query() integration test 2023-11-07 10:28:36 +01:00
Mark
2dd1f6e880 fix compare_pstr_to_string (#2148) 2023-11-04 19:32:23 -06:00
Mark Thom
da0018efdf Merge pull request #2147 from infogulch/lint-format
Resolve all lints and formating; split style checks into separate job
2023-11-04 11:34:11 -06:00
infogulch
16f768197b Split style checks, test reports into separate job 2023-11-04 02:22:24 -05:00
infogulch
d6fe5b5aad Bump Cargo.lock to reflect new scryer version 2023-11-04 02:20:55 -05:00
infogulch
dfe4fbfdc5 Add previous commit to .git-blame-ignore-revs 2023-11-04 02:20:37 -05:00
infogulch
9444e62df9 Resolve lints and format 2023-11-04 02:16:54 -05:00
Mark
dddffb01a6 update release version linked on website 2023-11-02 17:34:49 -06:00
Mark
4520bedf8d update release link 2023-11-02 17:32:33 -06:00
178 changed files with 19061 additions and 5156 deletions

2
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,2 @@
# Resolved all lints and formatted the codebase
9444e62df9820d6bfd96dbd8849e177bc5cecc2e

52
.github/actions/setup-rust/action.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: 'Setup Rust'
inputs:
rust-version:
required: true
type: string
targets:
required: true
type: string
components:
required: false
default:
cache-context:
required: true
type: string
runs:
using: "composite"
steps:
- uses: dtolnay/rust-toolchain@master
id: toolchain
with:
toolchain: ${{ inputs.rust-version }}
targets: ${{ inputs.targets }}
components: ${{ inputs.components }}
- name: Install i686 dependencies
if: "contains(inputs.targets,'i686')"
shell: bash
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libssl-dev:i386 gcc-multilib clang -y
echo "CC=clang" >> $GITHUB_ENV
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
- uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ inputs.cache-context }}_${{ inputs.targets }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
# Remove build artifacts for the current crate, since it will be rebuilt every
# run anyway, but keep dependency artifacts to cache them.
# Must be placed after actions/cache so its post step runs first.
- uses: pyTooling/Actions/with-post-step@v0.4.6
with:
main: bash ./.github/actions/setup-rust/cleanup.sh
post: bash ./.github/actions/setup-rust/cleanup.sh

13
.github/actions/setup-rust/cleanup.sh vendored Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e
echo Cleanup workspace build artifacts and extra target output
# clean just the direct members of the current workspace, use cargo metadata to generalize to all rust projects
cargo clean -p `cargo metadata --no-deps --offline --format-version 1 | jq -r '[.workspace_members[]|split(" ")|.[0]]|join(" ")'`
# remove directories in /target/ that are not named `debug` or `release`
before=`du -s target | awk '{print $1}'`
find ./target -maxdepth 1 -type d ! -name debug ! -name release ! -name target -exec rm -r {} \;
after=`du -s target | awk '{print $1}'`
echo Deleted $(($before - $after)) bytes from target directory

View File

@@ -10,7 +10,27 @@ on:
- cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday - cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday
workflow_dispatch: workflow_dispatch:
permissions:
checks: write
jobs: jobs:
style:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
rust-version: stable
targets: x86_64-unknown-linux-gnu
components: clippy, rustfmt
cache-context: style
- name: Check formatting
run: cargo fmt --check
- name: Check clippy
run: cargo clippy --no-deps --all-targets
build-test: build-test:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
@@ -18,13 +38,14 @@ jobs:
matrix: matrix:
include: include:
# operating systems # operating systems
- { os: windows-latest, rust-version: stable, publish: true, target: 'x86_64-pc-windows-msvc'} - { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc', publish: true }
- { os: macos-11, rust-version: stable, publish: true, target: 'x86_64-apple-darwin' } - { os: macos-11, rust-version: stable, target: 'x86_64-apple-darwin', publish: true }
- { os: ubuntu-20.04, rust-version: stable, publish: true, target: 'x86_64-unknown-linux-gnu' } - { os: ubuntu-20.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
# architectures # architectures
- { os: ubuntu-22.04, rust-version: stable, publish: true, target: 'x86_64-unknown-linux-gnu', extra: true } - { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
- { os: ubuntu-22.04, rust-version: stable, publish: true, target: 'i686-unknown-linux-gnu' } - { os: ubuntu-22.04, rust-version: stable, target: 'i686-unknown-linux-gnu', publish: true }
- { os: ubuntu-22.04, rust-version: nightly, publish: true, target: 'wasm32-unknown-unknown', args: '--no-default-features' } # FIXME(issue #2138): run wasm tests, failing to run since https://github.com/mthom/scryer-prolog/pull/2137 removed wasm-pack
- { os: ubuntu-22.04, rust-version: nightly, target: 'wasm32-unknown-unknown', publish: true, args: '--no-default-features' , test-args: '--no-run --no-default-features' }
# rust versions # rust versions
- { os: ubuntu-22.04, rust-version: "1.70", target: 'x86_64-unknown-linux-gnu'} - { os: ubuntu-22.04, rust-version: "1.70", target: 'x86_64-unknown-linux-gnu'}
- { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'} - { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'}
@@ -34,63 +55,18 @@ jobs:
shell: bash shell: bash
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master - name: Setup Rust
id: toolchain uses: ./.github/actions/setup-rust
with: with:
toolchain: ${{ matrix.rust-version }} rust-version: ${{ matrix.rust-version }}
targets: ${{ matrix.target }} targets: ${{ matrix.target }}
components: clippy, rustfmt cache-context: ${{ matrix.os }}
- 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: 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. # Build and test.
- name: Build library - name: Build library
run: cargo rustc --lib --target ${{ matrix.target }} ${{ matrix.args }} --verbose run: cargo build --all-targets --target ${{ matrix.target }} ${{ matrix.args }} --verbose
- name: Test - name: Test
if: "!matrix.extra" run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
run: cargo test --target ${{ matrix.target }} ${{ matrix.args }} --all --verbose || echo "::warning ::Tests failed"
# 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 # On stable rust builds, build a binary and publish as a github actions
# artifact. These binaries could be useful for testing the pipeline but # artifact. These binaries could be useful for testing the pipeline but
@@ -154,6 +130,53 @@ jobs:
fail_on: nothing fail_on: nothing
comment_mode: off comment_mode: off
report:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
rust-version: stable
targets: x86_64-unknown-linux-gnu
cache-context: report
- run: |
cargo install cargo2junit --force
# cargo install iai-callgrind-runner --force --version `cargo metadata --format-version 1 | jq -r '.resolve.nodes[].id|split(" ")|select(.[0]=="iai-callgrind")|.[1]'`
cargo install iai-callgrind-runner --force --git https://github.com/iai-callgrind/iai-callgrind --rev c77bc3c83d7f4e976cc42d4597236a8db259e772
sudo apt install valgrind -y
- name: Test and report
run: |
RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml
- name: Publish cargo test results artifact
uses: actions/upload-artifact@v3
with:
name: cargo-test-results
path: cargo_test_results.xml
- name: Publish cargo test summary
uses: EnricoMi/publish-unit-test-result-action/composite@master
with:
check_name: Cargo test summary
files: cargo_test_results.xml
fail_on: nothing
comment_mode: off
- run: cargo build --all-targets --release
- run: cargo test --bench setup --release
- run: cargo bench --bench run_iai -- --save-summary=json
- run: cargo bench --bench run_criterion
- run: cargo bench --bench run_criterion -- --profile-time 60
- name: Publish benchmark results
uses: actions/upload-artifact@v3
with:
name: benchmark-results
path: |
target/criterion/*
target/iai/*
target/benchmark_inference_counts.json
# Publish binaries when building for a tag # Publish binaries when building for a tag
release: release:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

1387
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "scryer-prolog" name = "scryer-prolog"
version = "0.9.3" version = "0.9.4"
authors = ["Mark Thom <markjordanthom@gmail.com>"] authors = ["Mark Thom <markjordanthom@gmail.com>"]
edition = "2021" edition = "2021"
description = "A modern Prolog implementation written mostly in Rust." description = "A modern Prolog implementation written mostly in Rust."
@@ -37,59 +37,68 @@ to-syn-value_derive = "0.1.1"
walkdir = "2" walkdir = "2"
[dependencies] [dependencies]
base64 = "0.12.3"
bit-set = "0.5.3" bit-set = "0.5.3"
bitvec = "1" bitvec = "1"
blake2 = "0.8.1"
bytes = "1"
chrono = "0.4.11"
cpu-time = "1.0.0" cpu-time = "1.0.0"
crrl = "0.6.0"
dashu = "0.4.0"
derive_deref = "1.1.1"
dirs-next = "2.0.0" dirs-next = "2.0.0"
divrem = "0.1.0" divrem = "0.1.0"
futures = "0.3"
fxhash = "0.2.1" fxhash = "0.2.1"
git-version = "0.3.4" git-version = "0.3.4"
indexmap = "1.0.2" indexmap = "1.0.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
lexical = "5.2.2" lexical = "5.2.2"
libc = "0.2.62" libc = "0.2.62"
modular-bitfield = "0.11.2" libloading = "0.7"
scryer-modular-bitfield = "0.11.4"
num-order = { version = "1.2.0" }
ordered-float = "2.6.0" ordered-float = "2.6.0"
phf = { version = "0.9", features = ["macros"] } phf = { version = "0.9", features = ["macros"] }
rand = "0.8.5"
ref_thread_local = "0.0.0" ref_thread_local = "0.0.0"
regex = "1.9.1"
ring = { version = "0.17.5", features = ["wasm32_unknown_unknown_js"] }
ripemd160 = "0.8.0" ripemd160 = "0.8.0"
sha3 = "0.8.2"
blake2 = "0.8.1"
crrl = "0.6.0"
chrono = "0.4.11"
select = "0.6.0"
roxmltree = "0.11.0" roxmltree = "0.11.0"
base64 = "0.12.3" ryu = "1.0.9"
select = "0.6.0"
sha3 = "0.8.2"
smallvec = "1.8.0" smallvec = "1.8.0"
static_assertions = "1.1.0" static_assertions = "1.1.0"
ryu = "1.0.9"
futures = "0.3" serde_json = "1.0.95"
regex = "1.9.1" serde = "1.0.159"
libloading = "0.7"
derive_deref = "1.1.1"
bytes = "1"
dashu = "0.4.0"
num-order = { version = "1.2.0" }
rand = "0.8.5"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
libffi = { version = "3.2.0", optional = true }
hostname = { version = "0.3.1", optional = true }
crossterm = { version = "0.20.0", optional = true } crossterm = { version = "0.20.0", optional = true }
ctrlc = { version = "3.2.2", optional = true } ctrlc = { version = "3.2.2", optional = true }
rustyline = { version = "12.0.0", optional = true } hostname = { version = "0.3.1", optional = true }
libffi = { version = "3.2.0", optional = true }
native-tls = { version = "0.2.4", optional = true } native-tls = { version = "0.2.4", optional = true }
warp = { version = "=0.3.5", features = ["tls"], optional = true } reqwest = { version = "0.11.18", optional = true }
reqwest = { version = "0.11.18", features = ["blocking"], optional = true } rustyline = { version = "12.0.0", optional = true }
tokio = { version = "1.28.2", features = ["full"] } tokio = { version = "1.28.2", features = ["full"] }
warp = { version = "=0.3.5", features = ["tls"], optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2.10", features = ["js"] } getrandom = { version = "0.2.10", features = ["js"] }
tokio = { version = "1.28.2", features = ["sync", "macros", "io-util", "rt", "time"] } tokio = { version = "1.28.2", features = [
"sync",
"macros",
"io-util",
"rt",
"time",
] }
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
console_log = "1.0"
wasm-bindgen = "0.2.87" wasm-bindgen = "0.2.87"
wasm-bindgen-futures = "0.4" wasm-bindgen-futures = "0.4"
serde-wasm-bindgen = "0.5" serde-wasm-bindgen = "0.5"
@@ -97,19 +106,36 @@ web-sys = { version = "0.3", features = [
"Document", "Document",
"Window", "Window",
"Element", "Element",
]} "Performance",
] }
[target.'cfg(target_os = "wasi")'.dependencies] js-sys = "0.3"
ring-wasi = { version = "0.16.25" }
[target.'cfg(not(target_os = "wasi"))'.dependencies]
ring = { version = "0.16.13" }
[dev-dependencies] [dev-dependencies]
assert_cmd = "1.0.3"
predicates-core = "1.0.2"
maplit = "1.0.2" maplit = "1.0.2"
predicates-core = "1.0.2"
serial_test = "2.0.0" serial_test = "2.0.0"
[patch.crates-io] [target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies]
modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" } assert_cmd = "1.0.3"
criterion = "0.5.1"
iai-callgrind = "0.9.0"
trycmd = "0.14.19"
[target.'cfg(not(any(target_os = "windows", all(target_arch = "wasm32", target_os = "unknown"))))'.dev-dependencies]
pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] }
[profile.bench]
lto = true
opt-level = 3
[profile.release]
lto = true
opt-level = 3
[[bench]]
name = "run_criterion"
harness = false
[[bench]]
name = "run_iai"
harness = false

View File

@@ -5,13 +5,6 @@
X = "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 ![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 strength production environment *and* a testbed for bleeding edge research in
logic and constraint programming. logic and constraint programming.
@@ -28,6 +21,7 @@ Some of the Scryer Prolog features are:
* [Cryptographical predicates](/crypto.html) * [Cryptographical predicates](/crypto.html)
* [Foreign Function Interface](/ffi.html) * [Foreign Function Interface](/ffi.html)
* WebAssembly support * WebAssembly support
* Usable as a library
* WAM based engine, cross-platform made in Rust * WAM based engine, cross-platform made in Rust
* _and more..._ * _and more..._
@@ -61,11 +55,11 @@ the builtin Prolog modules and libraries in Scryer, check the documentation site
## Downloads ## Downloads
The latest version of Scryer Prolog is *0.9.2*. And it's already useful for lots of tasks. The latest version of Scryer Prolog is *0.9.3*. And it's already useful for lots of tasks.
| Windows | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.2/scryer-prolog_windows-latest.zip) | | Windows | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.3/scryer-prolog_windows-latest.zip) |
| macOS (Intel) | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.2/scryer-prolog_macos-11.zip) | | macOS (Intel) | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.3/scryer-prolog_macos-11.zip) |
| Linux | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.2/scryer-prolog_ubuntu-20.04.zip) | | Linux | [Download](https://github.com/mthom/scryer-prolog/releases/download/v0.9.3/scryer-prolog_ubuntu-20.04.zip) |
Scryer Prolog can also be compiled from source, instructions are on the [GitHub README](https://github.com/mthom/scryer-prolog). It runs on Linux, macOS and Windows. Other operating systems may work but they're not regularly tested. Scryer Prolog can also be compiled from source, instructions are on the [GitHub README](https://github.com/mthom/scryer-prolog). It runs on Linux, macOS and Windows. Other operating systems may work but they're not regularly tested.
@@ -80,4 +74,4 @@ an [issue](https://github.com/mthom/scryer-prolog/issues).
To get in touch with the Scryer Prolog community, participate in To get in touch with the Scryer Prolog community, participate in
[discussions](https://github.com/mthom/scryer-prolog/discussions) [discussions](https://github.com/mthom/scryer-prolog/discussions)
or visit our #scryer IRC channel on [Libera](https://libera.chat)! or visit our #scryer IRC channel on [Libera](https://libera.chat)!

View File

@@ -6,7 +6,10 @@ 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)**. **Scryer Prolog passes all tests** of
[syntactic&nbsp;conformity](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing),
[`variable_names/1`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/variable_names) and
[`dif/2`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dif).
The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl) The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl)
@@ -110,7 +113,7 @@ strings.
Precompiled binaries for several platforms are available for download Precompiled binaries for several platforms are available for download
at: at:
**https://github.com/mthom/scryer-prolog/releases/tag/v0.9.2** **https://github.com/mthom/scryer-prolog/releases/tag/v0.9.3**
### Native Compilation ### Native Compilation
@@ -313,6 +316,35 @@ To quit Scryer Prolog, use the standard predicate `halt/0`:
?- halt. ?- halt.
``` ```
### Starting Scryer Prolog
Scryer Prolog can be started from the command line by specifying
options, files and additional arguments. All components are optional:
<pre>
scryer-prolog [OPTIONS] [FILES] [-- ARGUMENTS]
</pre>
The supported options are:
```
-h, --help Display help message
-v, --version Print version information and exit
-g, --goal GOAL Run the query GOAL after consulting files
-f Fast startup. Do not load initialization file (~/.scryerrc)
--no-add-history Prevent adding input to history file (~/.scryer_history)
```
All specified Prolog files are consulted.
After Prolog files, application-specific arguments can be specified on
the command line. These arguments can be accessed from within Prolog
applications with the predicate&nbsp;`argv/1`, which yields the list
of arguments represented as strings.
Prolog files can also be turned into *shell&nbsp;scripts* as explained in
https://github.com/mthom/scryer-prolog/issues/2170#issuecomment-1821713993.
### Dynamic operators ### Dynamic operators
Scryer supports dynamic operators. Using the built-in Scryer supports dynamic operators. Using the built-in
@@ -773,7 +805,8 @@ by the Austrian Federal Computing Center, and parts of the
[precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog) [precautionary](https://github.com/dcnorris/precautionary/tree/main/exec/prolog)
package for the analysis of dose-escalation trials in the package for the analysis of dose-escalation trials in the
safety-critical and highly regulated domain of oncology safety-critical and highly regulated domain of oncology
trial&nbsp;design. trial&nbsp;design, described in [*An Executable Specification of
Oncology Dose-Escalation Protocols with&nbsp;Prolog*](https://arxiv.org/abs/2402.08334).
Scryer Prolog is also very well suited for teaching and learning Scryer Prolog is also very well suited for teaching and learning
Prolog, and for testing syntactic conformance and hence portability of Prolog, and for testing syntactic conformance and hence portability of

95
benches/README.md Normal file
View File

@@ -0,0 +1,95 @@
# About benches
The `benches` directory contains benchmarks that test scryer-prolog performance.
Benchmarks are run via two harnesses:
* `criterion` - criterion performs statistical analysis of benchmark runs and is
great for benchmarking locally.
* `iai-callgrind` - this runs the benchmark with callgrind, which is able to
precisely track the number of instructions executed during the run. This is
especially helpful in a public CI runner context where neighboring VMs can
cause a very high wall time variance. While instructions executed is only
correlated with the desired metric (wall time), this is a good tradeoff for CI
where that metric is unreliable.
Run benchmarks with the following commands:
```
cargo bench --bench run_criterion
# run a particular criterion benchmark
cargo bench --bench run_criterion -- <benchmark_name>
# run in profiling mode which outputs flamegraphs. Set profile time in seconds:
cargo bench --bench run_criterion -- --profile-time <time>
# to run iai, you need valgrind installed and to install iai-callgrind-runner
# at the same version as is in Cargo.toml:
cargo install iai-callgrind-runner --version 0.7.3
cargo bench --bench run_iai
```
For consistency, both runners -- `run_iai.rs` and `run_criterion.rs` -- import
the same setup code from `setup.rs`.
## Setup
`setup.rs` contains the setup code to run benchmarks. `fn prolog_benches()` at
the top of the file is where the benchmarks are defined.
Benchmarks are organized around running queries against a prolog module file.
Before a benchmark starts, `benchmark.setup()` is called which reads the module
file and initializes a new `scryer_prolog::machine::Machine`.
Each benchmark measurement is done by running a query against the machine. In
the case of criterion each query is run many times, in the case of iai it's run
once.
## Adding benchmarks
This design is meant to suppoort defining lots of benchmarks.
To add a new benchmark:
* Add a new file `benches/[module].pl` that contains setup prolog code. Import
libraries, define predicates, etc.
* Add a new section in `setup.rs::prolog_benchmarks()` that refers to to the
file and write a query to be benchmarked.
* If the query mutates the machine, then use `Strategy::Fresh` so the criterion
benchmark will recreate a new machine for each benchmark run, otherwise use
`Strategy::Reuse` which has lower overhead. (This is not used by the iai
benchmark because it only runs once anyway.)
Some tips:
* The goal of benchmarking is to know if a library or engine change improved
performance or not.
* Once a benchmark is defined and named, avoid changing it's definition. If a
benchmark needs to change to be more useful, give the new definition a new
name instead. This will prevent charts from showing wild changes in
performance just because the definition changed (see previous).
* Aim for queries to execute in less than 0.5s realtime. Longer runtimes make it
easier for humans to see big differences, but benchmarks either run 10x slower
(iai) or execute repeatedly to attain statistical significance (criterion) and
in both cases benchmarking queries that take longer than about 0.5s are
cumbersome to run.
* Consider that the library runtime actually parses the text output of the top
level. So don't use custom outputs or it will fail to parse. Also keep the
output small so it doesn't just benchmark the ouput parsing code.
* DO test the output of the benchmark run, we don't want to count broken
benchmarks.
## CI
Both benchmark harnesses are run in `.github/workflows/ci.yaml` in the `report`
job, and the results are published as build artifacts.
## Todo
- [ ] Currently, the execution time to load a module is not benchmarked. It
would be nice to have at least one benchmark for loading a module (probably a
big one).
- [ ] Write a new action that downloads the test and benchmark results
artifacts, plots them over time, and publishes a report to github pages.

41
benches/csv.pl Normal file

File diff suppressed because one or more lines are too long

130
benches/edges.pl Normal file
View File

@@ -0,0 +1,130 @@
:- use_module(library(clpb)).
:- use_module(library(assoc)).
:- use_module(library(lists)).
:- use_module(library(pairs)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Contiguous United States and DC as they appear in SGB:
http://www-cs-faculty.stanford.edu/~uno/sgb.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
edge(al, fl).
edge(al, ga).
edge(al, ms).
edge(al, tn).
edge(ar, la).
edge(ar, mo).
edge(ar, ms).
edge(ar, ok).
edge(ar, tn).
edge(ar, tx).
edge(az, ca).
edge(az, nm).
edge(az, nv).
edge(az, ut).
edge(ca, nv).
edge(ca, or).
edge(co, ks).
edge(co, ne).
edge(co, nm).
edge(co, ok).
edge(co, ut).
edge(co, wy).
edge(ct, ma).
edge(ct, ny).
edge(ct, ri).
edge(dc, md).
edge(dc, va).
edge(de, md).
edge(de, nj).
edge(de, pa).
edge(fl, ga).
edge(ga, nc).
edge(ga, sc).
edge(ga, tn).
edge(ia, il).
edge(ia, mn).
edge(ia, mo).
edge(ia, ne).
edge(ia, sd).
edge(ia, wi).
edge(id, mt).
edge(id, nv).
edge(id, or).
edge(id, ut).
edge(id, wa).
edge(id, wy).
edge(il, in).
edge(il, ky).
edge(il, mo).
edge(il, wi).
edge(in, ky).
edge(in, mi).
edge(in, oh).
edge(ks, mo).
edge(ks, ne).
edge(ks, ok).
edge(ky, mo).
edge(ky, oh).
edge(ky, tn).
edge(ky, va).
edge(ky, wv).
edge(la, ms).
edge(la, tx).
edge(ma, nh).
edge(ma, ny).
edge(ma, ri).
edge(ma, vt).
edge(md, pa).
edge(md, va).
edge(md, wv).
edge(me, nh).
edge(mi, oh).
edge(mi, wi).
edge(mn, nd).
edge(mn, sd).
edge(mn, wi).
edge(mo, ne).
edge(mo, ok).
edge(mo, tn).
edge(ms, tn).
edge(mt, nd).
edge(mt, sd).
edge(mt, wy).
edge(nc, sc).
edge(nc, tn).
edge(nc, va).
edge(nd, sd).
edge(ne, sd).
edge(ne, wy).
edge(nh, vt).
edge(nj, ny).
edge(nj, pa).
edge(nm, ok).
edge(nm, tx).
edge(nv, or).
edge(nv, ut).
edge(ny, pa).
edge(ny, vt).
edge(oh, pa).
edge(oh, wv).
edge(ok, tx).
edge(or, wa).
edge(pa, wv).
edge(sd, wy).
edge(tn, va).
edge(ut, wy).
edge(va, wv).
independent_set(G, *(NBs)) :-
findall(U-V, (edge(U, V),G@<U), Edges),
setof(U, V^(member(U-V, Edges);member(V-U, Edges)), Nodes),
pairs_keys_values(Pairs, Nodes, _),
list_to_assoc(Pairs, Assoc),
maplist(not_both(Assoc), Edges, NBs).
not_both(Assoc, U-V, ~BU + ~BV) :-
get_assoc(U, Assoc, BU),
get_assoc(V, Assoc, BV).
independent_set_count(G, Count) :- independent_set(G, Sat), sat_count(Sat, Count).

2
benches/numlist.pl Normal file
View File

@@ -0,0 +1,2 @@
:- use_module(library(between)).
run_numlist(Upper, Head) :- numlist(1, Upper, L), L = [Head|_].

46
benches/run_criterion.rs Normal file
View File

@@ -0,0 +1,46 @@
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[cfg(not(target_os = "windows"))]
use pprof::criterion::{Output, PProfProfiler};
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
mod setup;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
fn bench_criterion(c: &mut Criterion) {
for (&name, bench) in setup::prolog_benches().iter() {
match bench.strategy {
setup::Strategy::Fresh => c.bench_function(name, |b| {
b.iter_batched(|| bench.setup(), |mut r| r(), BatchSize::LargeInput)
}),
setup::Strategy::Reuse => c.bench_function(name, |b| b.iter(bench.setup())),
};
}
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[cfg(not(target_os = "windows"))]
fn config() -> Criterion {
Criterion::default()
.sample_size(20)
.with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)))
}
#[cfg(target_os = "windows")]
fn config() -> Criterion {
Criterion::default().sample_size(20)
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
criterion_group!(
name = benches;
config = config();
targets = bench_criterion
);
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
criterion_main!(benches);
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
fn main() {}

38
benches/run_iai.rs Normal file
View File

@@ -0,0 +1,38 @@
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
mod setup;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
mod iai {
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
use scryer_prolog::machine::parsed_results::QueryResolution;
use super::setup;
#[library_benchmark]
#[bench::count_edges(setup::prolog_benches()["count_edges"].setup())]
#[bench::numlist(setup::prolog_benches()["numlist"].setup())]
#[bench::csv_codename(setup::prolog_benches()["csv_codename"].setup())]
fn bench(mut run: impl FnMut() -> QueryResolution) -> QueryResolution {
run()
}
library_benchmark_group!(
name = benches;
benchmarks = bench
);
main!(library_benchmark_groups = benches);
pub fn call_main() {
main()
}
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
fn main() {
iai::call_main();
}
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
fn main() {}

133
benches/setup.rs Normal file
View File

@@ -0,0 +1,133 @@
use std::{collections::BTreeMap, fs, path::Path};
use maplit::btreemap;
use scryer_prolog::machine::{
parsed_results::{QueryResolution, Value},
Machine,
};
pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
[
(
"count_edges", // name of the benchmark
"benches/edges.pl", // name of the prolog module file to load. use the same file in multiple benchmarks
"independent_set_count(ky, Count).", // query to benchmark in the context of the loaded module. consider making the query adjustable to tune the run time to ~0.1s
Strategy::Reuse,
btreemap! { "Count" => Value::try_from("2869176".to_string()).unwrap() },
),
(
"numlist",
"benches/numlist.pl",
"run_numlist(1000000, Head).",
Strategy::Reuse,
btreemap! { "Head" => Value::try_from("1".to_string()).unwrap()},
),
(
"csv_codename",
"benches/csv.pl",
"get_codename(\"0020\",Name).",
Strategy::Reuse,
btreemap! { "Name" => Value::try_from("SPACE".to_string()).unwrap()},
),
]
.map(|b| {
(
b.0,
PrologBenchmark {
name: b.0,
filename: b.1,
query: b.2,
strategy: b.3,
bindings: b.4,
},
)
})
.into()
}
pub enum Strategy {
#[allow(dead_code)]
Fresh,
Reuse,
}
pub struct PrologBenchmark {
pub name: &'static str,
pub filename: &'static str,
pub query: &'static str,
pub strategy: Strategy,
pub bindings: BTreeMap<&'static str, Value>,
}
impl PrologBenchmark {
pub fn make_machine(&self) -> Machine {
let program = fs::read_to_string(self.filename).unwrap();
let module_name = Path::new(self.filename)
.file_stem()
.and_then(|s| s.to_str())
.unwrap();
let mut machine = Machine::new_lib();
machine.load_module_string(module_name, program);
machine
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn setup(&self) -> impl FnMut() -> QueryResolution {
let mut machine = self.make_machine();
let query = self.query;
move || {
use criterion::black_box;
black_box(machine.run_query(black_box(query.to_string()))).unwrap()
}
}
}
#[cfg(test)]
mod test {
#[test]
fn validate_benchmarks() {
use super::prolog_benches;
use scryer_prolog::machine::parsed_results::{QueryMatch, QueryResolution};
use std::{fmt::Write, fs};
struct BenchResult {
pub name: &'static str,
pub setup_inference_count: u64,
pub query_inference_count: u64,
}
let mut results: Vec<BenchResult> = vec![];
for (_, r) in prolog_benches() {
let mut machine = r.make_machine();
let setup_inference_count = machine.get_inference_count();
let result = machine.run_query(r.query.to_string()).unwrap();
let query_inference_count = machine.get_inference_count() - setup_inference_count;
let expected = QueryResolution::Matches(vec![QueryMatch::from(r.bindings.clone())]);
assert_eq!(result, expected, "validating benchmark {}", r.name);
results.push(BenchResult {
name: r.name,
setup_inference_count,
query_inference_count,
})
}
let mut json: String = Default::default();
json.push('[');
for r in results {
json.push('\n');
write!(
json,
r#"{{"name":"{}","setup_inference_count":{},"query_inference_count":{}}},"#,
r.name, r.setup_inference_count, r.query_inference_count
)
.unwrap();
}
json.pop(); // trailing comma
json.push_str("\n]");
fs::write("target/benchmark_inference_counts.json", json).expect("Unable to write file");
}
}

View File

@@ -31,6 +31,7 @@ struct Level;
struct NextOrFail; struct NextOrFail;
struct RegType; struct RegType;
#[allow(clippy::enum_variant_names)]
#[allow(dead_code)] #[allow(dead_code)]
#[derive(ToDeriveInput, EnumDiscriminants)] #[derive(ToDeriveInput, EnumDiscriminants)]
#[strum_discriminants(derive(EnumProperty, EnumString))] #[strum_discriminants(derive(EnumProperty, EnumString))]
@@ -49,6 +50,7 @@ enum CompareNumber {
NumberEqual(ArithmeticTerm, ArithmeticTerm), NumberEqual(ArithmeticTerm, ArithmeticTerm),
} }
#[allow(clippy::enum_variant_names)]
#[allow(dead_code)] #[allow(dead_code)]
#[derive(ToDeriveInput, EnumDiscriminants)] #[derive(ToDeriveInput, EnumDiscriminants)]
#[strum_discriminants(derive(EnumProperty, EnumString))] #[strum_discriminants(derive(EnumProperty, EnumString))]
@@ -206,6 +208,7 @@ enum REPLCodePtr {
AddNonCountedBacktracking, AddNonCountedBacktracking,
} }
#[allow(clippy::upper_case_acronyms)]
#[allow(dead_code)] #[allow(dead_code)]
#[derive(ToDeriveInput, EnumDiscriminants)] #[derive(ToDeriveInput, EnumDiscriminants)]
#[strum_discriminants(derive(EnumProperty, EnumString))] #[strum_discriminants(derive(EnumProperty, EnumString))]
@@ -326,6 +329,8 @@ enum SystemClauseType {
InstallSCCCleaner, InstallSCCCleaner,
#[strum_discriminants(strum(props(Arity = "3", Name = "$install_inference_counter")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$install_inference_counter")))]
InstallInferenceCounter, InstallInferenceCounter,
#[strum_discriminants(strum(props(Arity = "1", Name = "$inference_count")))]
InferenceCount,
#[strum_discriminants(strum(props(Arity = "1", Name = "$lh_length")))] #[strum_discriminants(strum(props(Arity = "1", Name = "$lh_length")))]
LiftedHeapLength, LiftedHeapLength,
#[strum_discriminants(strum(props(Arity = "3", Name = "$load_library_as_stream")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$load_library_as_stream")))]
@@ -492,6 +497,8 @@ enum SystemClauseType {
CryptoRandomByte, CryptoRandomByte,
#[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_data_hash")))] #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_data_hash")))]
CryptoDataHash, CryptoDataHash,
#[strum_discriminants(strum(props(Arity = "5", Name = "$crypto_hmac")))]
CryptoHMAC,
#[strum_discriminants(strum(props(Arity = "7", Name = "$crypto_data_hkdf")))] #[strum_discriminants(strum(props(Arity = "7", Name = "$crypto_data_hkdf")))]
CryptoDataHKDF, CryptoDataHKDF,
#[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_password_hash")))] #[strum_discriminants(strum(props(Arity = "4", Name = "$crypto_password_hash")))]
@@ -506,18 +513,12 @@ enum SystemClauseType {
#[cfg(feature = "crypto-full")] #[cfg(feature = "crypto-full")]
#[strum_discriminants(strum(props(Arity = "6", Name = "$crypto_data_decrypt")))] #[strum_discriminants(strum(props(Arity = "6", Name = "$crypto_data_decrypt")))]
CryptoDataDecrypt, CryptoDataDecrypt,
#[cfg(feature = "crypto-full")] #[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_sign_raw")))]
#[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_sign")))] Ed25519SignRaw,
Ed25519Sign, #[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_verify_raw")))]
#[cfg(feature = "crypto-full")] Ed25519VerifyRaw,
#[strum_discriminants(strum(props(Arity = "4", Name = "$ed25519_verify")))] #[strum_discriminants(strum(props(Arity = "2", Name = "$ed25519_seed_to_public_key")))]
Ed25519Verify, Ed25519SeedToPublicKey,
#[cfg(feature = "crypto-full")]
#[strum_discriminants(strum(props(Arity = "1", Name = "$ed25519_new_keypair")))]
Ed25519NewKeyPair,
#[cfg(feature = "crypto-full")]
#[strum_discriminants(strum(props(Arity = "2", Name = "$ed25519_keypair_public_key")))]
Ed25519KeyPairPublicKey,
#[strum_discriminants(strum(props(Arity = "2", Name = "$first_non_octet")))] #[strum_discriminants(strum(props(Arity = "2", Name = "$first_non_octet")))]
FirstNonOctet, FirstNonOctet,
#[strum_discriminants(strum(props(Arity = "3", Name = "$load_html")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$load_html")))]
@@ -570,6 +571,8 @@ enum SystemClauseType {
ForeignCall, ForeignCall,
#[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))] #[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))]
DefineForeignStruct, DefineForeignStruct,
#[strum_discriminants(strum(props(Arity = "2", Name = "$js_eval")))]
JsEval,
#[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))]
PredicateDefined, PredicateDefined,
#[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))]
@@ -603,6 +606,8 @@ enum SystemClauseType {
KeySortWithConstantVarOrdering, KeySortWithConstantVarOrdering,
#[strum_discriminants(strum(props(Arity = "0", Name = "$inference_limit_exceeded")))] #[strum_discriminants(strum(props(Arity = "0", Name = "$inference_limit_exceeded")))]
InferenceLimitExceeded, InferenceLimitExceeded,
#[strum_discriminants(strum(props(Arity = "1", Name = "$argv")))]
Argv,
REPL(REPLCodePtr), REPL(REPLCodePtr),
} }
@@ -789,8 +794,8 @@ enum InstructionTemplate {
#[strum_discriminants(strum(props(Arity = "0", Name = "install_verify_attr")))] #[strum_discriminants(strum(props(Arity = "0", Name = "install_verify_attr")))]
InstallVerifyAttr, InstallVerifyAttr,
// call verify_attrs. // call verify_attrs.
#[strum_discriminants(strum(props(Arity = "0", Name = "verify_attr_interrupt")))] #[strum_discriminants(strum(props(Arity = "1", Name = "verify_attr_interrupt")))]
VerifyAttrInterrupt, VerifyAttrInterrupt(usize),
// procedures // procedures
CallClause(ClauseType, usize, usize, bool, bool), // ClauseType, CallClause(ClauseType, usize, usize, bool, bool), // ClauseType,
// arity, // arity,
@@ -897,13 +902,13 @@ fn generate_instruction_preface() -> TokenStream {
} }
impl ArithmeticTerm { impl ArithmeticTerm {
fn into_functor(&self, arena: &mut Arena) -> MachineStub { fn into_functor(self, arena: &mut Arena) -> MachineStub {
match self { match self {
&ArithmeticTerm::Reg(r) => reg_type_into_functor(r), ArithmeticTerm::Reg(r) => reg_type_into_functor(r),
&ArithmeticTerm::Interm(i) => { ArithmeticTerm::Interm(i) => {
functor!(atom!("intermediate"), [fixnum(i)]) functor!(atom!("intermediate"), [fixnum(i)])
} }
&ArithmeticTerm::Number(n) => { ArithmeticTerm::Number(n) => {
vec![HeapCellValue::from((n, arena))] vec![HeapCellValue::from((n, arena))]
} }
} }
@@ -925,26 +930,17 @@ fn generate_instruction_preface() -> TokenStream {
impl NextOrFail { impl NextOrFail {
#[inline] #[inline]
pub fn is_next(&self) -> bool { pub fn is_next(&self) -> bool {
if let NextOrFail::Next(_) = self { matches!(self, NextOrFail::Next(_))
true
} else {
false
}
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Death { pub enum Death {
Finite(usize), Finite(usize),
#[default]
Infinity, Infinity,
} }
impl Default for Death {
fn default() -> Self {
Death::Infinity
}
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum IndexedChoiceInstruction { pub enum IndexedChoiceInstruction {
Retry(usize), Retry(usize),
@@ -956,30 +952,30 @@ fn generate_instruction_preface() -> TokenStream {
impl IndexedChoiceInstruction { impl IndexedChoiceInstruction {
pub(crate) fn offset(&self) -> usize { pub(crate) fn offset(&self) -> usize {
match self { match *self {
&IndexedChoiceInstruction::Retry(offset) => offset, IndexedChoiceInstruction::Retry(offset) => offset,
&IndexedChoiceInstruction::Trust(offset) => offset, IndexedChoiceInstruction::Trust(offset) => offset,
&IndexedChoiceInstruction::Try(offset) => offset, IndexedChoiceInstruction::Try(offset) => offset,
&IndexedChoiceInstruction::DefaultRetry(offset) => offset, IndexedChoiceInstruction::DefaultRetry(offset) => offset,
&IndexedChoiceInstruction::DefaultTrust(offset) => offset, IndexedChoiceInstruction::DefaultTrust(offset) => offset,
} }
} }
pub(crate) fn to_functor(&self) -> MachineStub { pub(crate) fn to_functor(self) -> MachineStub {
match self { match self {
&IndexedChoiceInstruction::Try(offset) => { IndexedChoiceInstruction::Try(offset) => {
functor!(atom!("try"), [fixnum(offset)]) functor!(atom!("try"), [fixnum(offset)])
} }
&IndexedChoiceInstruction::Trust(offset) => { IndexedChoiceInstruction::Trust(offset) => {
functor!(atom!("trust"), [fixnum(offset)]) functor!(atom!("trust"), [fixnum(offset)])
} }
&IndexedChoiceInstruction::Retry(offset) => { IndexedChoiceInstruction::Retry(offset) => {
functor!(atom!("retry"), [fixnum(offset)]) functor!(atom!("retry"), [fixnum(offset)])
} }
&IndexedChoiceInstruction::DefaultTrust(offset) => { IndexedChoiceInstruction::DefaultTrust(offset) => {
functor!(atom!("default_trust"), [fixnum(offset)]) functor!(atom!("default_trust"), [fixnum(offset)])
} }
&IndexedChoiceInstruction::DefaultRetry(offset) => { IndexedChoiceInstruction::DefaultRetry(offset) => {
functor!(atom!("default_retry"), [fixnum(offset)]) functor!(atom!("default_retry"), [fixnum(offset)])
} }
} }
@@ -1038,7 +1034,7 @@ fn generate_instruction_preface() -> TokenStream {
] ]
) )
} }
&IndexingInstruction::SwitchOnConstant(ref constants) => { IndexingInstruction::SwitchOnConstant(constants) => {
let mut key_value_list_stub = vec![]; let mut key_value_list_stub = vec![];
let orig_h = h; let orig_h = h;
@@ -1066,7 +1062,7 @@ fn generate_instruction_preface() -> TokenStream {
[key_value_list_stub] [key_value_list_stub]
) )
} }
&IndexingInstruction::SwitchOnStructure(ref structures) => { IndexingInstruction::SwitchOnStructure(structures) => {
let mut key_value_list_stub = vec![]; let mut key_value_list_stub = vec![];
let orig_h = h; let orig_h = h;
@@ -1159,6 +1155,33 @@ fn generate_instruction_preface() -> TokenStream {
pub type CodeDeque = VecDeque<Instruction>; pub type CodeDeque = VecDeque<Instruction>;
impl Instruction { impl Instruction {
#[inline]
pub fn registers(&self) -> Vec<RegType> {
match *self {
Instruction::GetConstant(_, _, r) => vec![r],
Instruction::GetList(_, r) => vec![r],
Instruction::GetPartialString(_, _, r, _) => vec![r],
Instruction::GetStructure(_, _, _, r) => vec![r],
Instruction::GetVariable(r, t) => vec![r, temp_v!(t)],
Instruction::GetValue(r, t) => vec![r, temp_v!(t)],
Instruction::UnifyLocalValue(r) => vec![r],
Instruction::UnifyVariable(r) => vec![r],
Instruction::PutConstant(_, _, r) => vec![r],
Instruction::PutList(_, r) => vec![r],
Instruction::PutPartialString(_, _, r, _) => vec![r],
Instruction::PutStructure(_, _, r) => vec![r],
Instruction::PutValue(r, t) => vec![r, temp_v!(t)],
Instruction::PutVariable(r, t) => vec![r, temp_v!(t)],
Instruction::SetLocalValue(r) => vec![r],
Instruction::SetVariable(r) => vec![r],
Instruction::SetValue(r) => vec![r],
Instruction::GetLevel(r) => vec![r],
Instruction::GetPrevLevel(r) => vec![r],
Instruction::GetCutPoint(r) => vec![r],
_ => vec![],
}
}
#[inline] #[inline]
pub fn to_indexing_line_mut(&mut self) -> Option<&mut Vec<IndexingLine>> { pub fn to_indexing_line_mut(&mut self) -> Option<&mut Vec<IndexingLine>> {
match self { match self {
@@ -1177,7 +1200,7 @@ fn generate_instruction_preface() -> TokenStream {
#[inline] #[inline]
pub fn is_head_instr(&self) -> bool { pub fn is_head_instr(&self) -> bool {
match self { matches!(self,
Instruction::Deallocate | Instruction::Deallocate |
Instruction::GetConstant(..) | Instruction::GetConstant(..) |
Instruction::GetList(..) | Instruction::GetList(..) |
@@ -1201,9 +1224,10 @@ fn generate_instruction_preface() -> TokenStream {
Instruction::SetLocalValue(..) | Instruction::SetLocalValue(..) |
Instruction::SetVariable(..) | Instruction::SetVariable(..) |
Instruction::SetValue(..) | Instruction::SetValue(..) |
Instruction::SetVoid(..) => true, Instruction::SetVoid(..) |
_ => false, Instruction::GetLevel(..) |
} Instruction::GetPrevLevel(..) |
Instruction::GetCutPoint(..))
} }
pub fn enqueue_functors( pub fn enqueue_functors(
@@ -1213,7 +1237,7 @@ fn generate_instruction_preface() -> TokenStream {
functors: &mut Vec<MachineStub>, functors: &mut Vec<MachineStub>,
) { ) {
match self { match self {
&Instruction::IndexingCode(ref indexing_instrs) => { Instruction::IndexingCode(indexing_instrs) => {
for indexing_instr in indexing_instrs { for indexing_instr in indexing_instrs {
match indexing_instr { match indexing_instr {
IndexingLine::Indexing(indexing_instr) => { IndexingLine::Indexing(indexing_instr) => {
@@ -1248,8 +1272,8 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::InstallVerifyAttr => { &Instruction::InstallVerifyAttr => {
functor!(atom!("install_verify_attr")) functor!(atom!("install_verify_attr"))
} }
&Instruction::VerifyAttrInterrupt => { &Instruction::VerifyAttrInterrupt(arity) => {
functor!(atom!("verify_attr_interrupt")) functor!(atom!("verify_attr_interrupt"), [fixnum(arity)])
} }
&Instruction::DynamicElse(birth, death, next_or_fail) => { &Instruction::DynamicElse(birth, death, next_or_fail) => {
match (death, next_or_fail) { match (death, next_or_fail) {
@@ -1728,6 +1752,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::CallHeadIsDynamic | &Instruction::CallHeadIsDynamic |
&Instruction::CallInstallSCCCleaner | &Instruction::CallInstallSCCCleaner |
&Instruction::CallInstallInferenceCounter | &Instruction::CallInstallInferenceCounter |
&Instruction::CallInferenceCount |
&Instruction::CallLiftedHeapLength | &Instruction::CallLiftedHeapLength |
&Instruction::CallLoadLibraryAsStream | &Instruction::CallLoadLibraryAsStream |
&Instruction::CallModuleExists | &Instruction::CallModuleExists |
@@ -1782,6 +1807,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::CallLoadForeignLib | &Instruction::CallLoadForeignLib |
&Instruction::CallForeignCall | &Instruction::CallForeignCall |
&Instruction::CallDefineForeignStruct | &Instruction::CallDefineForeignStruct |
&Instruction::CallJsEval |
&Instruction::CallPredicateDefined | &Instruction::CallPredicateDefined |
&Instruction::CallStripModule | &Instruction::CallStripModule |
&Instruction::CallCurrentTime | &Instruction::CallCurrentTime |
@@ -1822,6 +1848,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::CallScryerPrologVersion | &Instruction::CallScryerPrologVersion |
&Instruction::CallCryptoRandomByte | &Instruction::CallCryptoRandomByte |
&Instruction::CallCryptoDataHash | &Instruction::CallCryptoDataHash |
&Instruction::CallCryptoHMAC |
&Instruction::CallCryptoDataHKDF | &Instruction::CallCryptoDataHKDF |
&Instruction::CallCryptoPasswordHash | &Instruction::CallCryptoPasswordHash |
&Instruction::CallCryptoCurveScalarMult | &Instruction::CallCryptoCurveScalarMult |
@@ -1876,18 +1903,18 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::CallFlushTermQueue | &Instruction::CallFlushTermQueue |
&Instruction::CallRemoveModuleExports | &Instruction::CallRemoveModuleExports |
&Instruction::CallAddNonCountedBacktracking | &Instruction::CallAddNonCountedBacktracking |
&Instruction::CallPopCount => { &Instruction::CallPopCount |
&Instruction::CallArgv |
&Instruction::CallEd25519SignRaw |
&Instruction::CallEd25519VerifyRaw |
&Instruction::CallEd25519SeedToPublicKey => {
let (name, arity) = self.to_name_and_arity(); let (name, arity) = self.to_name_and_arity();
functor!(atom!("call"), [atom(name), fixnum(arity)]) functor!(atom!("call"), [atom(name), fixnum(arity)])
} }
// //
#[cfg(feature = "crypto-full")] #[cfg(feature = "crypto-full")]
&Instruction::CallCryptoDataEncrypt | &Instruction::CallCryptoDataEncrypt |
&Instruction::CallCryptoDataDecrypt | &Instruction::CallCryptoDataDecrypt => {
&Instruction::CallEd25519Sign |
&Instruction::CallEd25519Verify |
&Instruction::CallEd25519NewKeyPair |
&Instruction::CallEd25519KeyPairPublicKey => {
let (name, arity) = self.to_name_and_arity(); let (name, arity) = self.to_name_and_arity();
functor!(atom!("call"), [atom(name), fixnum(arity)]) functor!(atom!("call"), [atom(name), fixnum(arity)])
} }
@@ -1962,6 +1989,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::ExecuteHeadIsDynamic | &Instruction::ExecuteHeadIsDynamic |
&Instruction::ExecuteInstallSCCCleaner | &Instruction::ExecuteInstallSCCCleaner |
&Instruction::ExecuteInstallInferenceCounter | &Instruction::ExecuteInstallInferenceCounter |
&Instruction::ExecuteInferenceCount |
&Instruction::ExecuteLiftedHeapLength | &Instruction::ExecuteLiftedHeapLength |
&Instruction::ExecuteLoadLibraryAsStream | &Instruction::ExecuteLoadLibraryAsStream |
&Instruction::ExecuteModuleExists | &Instruction::ExecuteModuleExists |
@@ -2016,6 +2044,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::ExecuteLoadForeignLib | &Instruction::ExecuteLoadForeignLib |
&Instruction::ExecuteForeignCall | &Instruction::ExecuteForeignCall |
&Instruction::ExecuteDefineForeignStruct | &Instruction::ExecuteDefineForeignStruct |
&Instruction::ExecuteJsEval |
&Instruction::ExecutePredicateDefined | &Instruction::ExecutePredicateDefined |
&Instruction::ExecuteStripModule | &Instruction::ExecuteStripModule |
&Instruction::ExecuteCurrentTime | &Instruction::ExecuteCurrentTime |
@@ -2056,6 +2085,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::ExecuteScryerPrologVersion | &Instruction::ExecuteScryerPrologVersion |
&Instruction::ExecuteCryptoRandomByte | &Instruction::ExecuteCryptoRandomByte |
&Instruction::ExecuteCryptoDataHash | &Instruction::ExecuteCryptoDataHash |
&Instruction::ExecuteCryptoHMAC |
&Instruction::ExecuteCryptoDataHKDF | &Instruction::ExecuteCryptoDataHKDF |
&Instruction::ExecuteCryptoPasswordHash | &Instruction::ExecuteCryptoPasswordHash |
&Instruction::ExecuteCryptoCurveScalarMult | &Instruction::ExecuteCryptoCurveScalarMult |
@@ -2110,18 +2140,18 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::ExecuteFlushTermQueue | &Instruction::ExecuteFlushTermQueue |
&Instruction::ExecuteRemoveModuleExports | &Instruction::ExecuteRemoveModuleExports |
&Instruction::ExecuteAddNonCountedBacktracking | &Instruction::ExecuteAddNonCountedBacktracking |
&Instruction::ExecutePopCount => { &Instruction::ExecutePopCount |
&Instruction::ExecuteArgv |
&Instruction::ExecuteEd25519SignRaw |
&Instruction::ExecuteEd25519VerifyRaw |
&Instruction::ExecuteEd25519SeedToPublicKey => {
let (name, arity) = self.to_name_and_arity(); let (name, arity) = self.to_name_and_arity();
functor!(atom!("execute"), [atom(name), fixnum(arity)]) functor!(atom!("execute"), [atom(name), fixnum(arity)])
} }
// //
#[cfg(feature = "crypto-full")] #[cfg(feature = "crypto-full")]
&Instruction::ExecuteCryptoDataEncrypt | &Instruction::ExecuteCryptoDataEncrypt |
&Instruction::ExecuteCryptoDataDecrypt | &Instruction::ExecuteCryptoDataDecrypt => {
&Instruction::ExecuteEd25519Sign |
&Instruction::ExecuteEd25519Verify |
&Instruction::ExecuteEd25519NewKeyPair |
&Instruction::ExecuteEd25519KeyPairPublicKey => {
let (name, arity) = self.to_name_and_arity(); let (name, arity) = self.to_name_and_arity();
functor!(atom!("execute"), [atom(name), fixnum(arity)]) functor!(atom!("execute"), [atom(name), fixnum(arity)])
} }
@@ -2331,7 +2361,7 @@ pub fn generate_instructions_rs() -> TokenStream {
let mut is_inlined_arms = vec![]; let mut is_inlined_arms = vec![];
is_inbuilt_arms.push(quote! { is_inbuilt_arms.push(quote! {
(atom!(":-"), 1 | 2) => true (atom!(":-"), 1 | 2)
}); });
for (name, arity, variant) in instr_data.compare_number_variants { for (name, arity, variant) in instr_data.compare_number_variants {
@@ -2388,11 +2418,11 @@ pub fn generate_instructions_rs() -> TokenStream {
}); });
is_inbuilt_arms.push(quote! { is_inbuilt_arms.push(quote! {
(atom!(#name), #arity) => true (atom!(#name), #arity)
}); });
is_inlined_arms.push(quote! { is_inlined_arms.push(quote! {
(atom!(#name), #arity) => true (atom!(#name), #arity)
}); });
} }
@@ -2421,7 +2451,7 @@ pub fn generate_instructions_rs() -> TokenStream {
}); });
is_inbuilt_arms.push(quote! { is_inbuilt_arms.push(quote! {
(atom!(#name), #arity) => true (atom!(#name), #arity)
}); });
} }
@@ -2487,7 +2517,7 @@ pub fn generate_instructions_rs() -> TokenStream {
}); });
is_inbuilt_arms.push(quote! { is_inbuilt_arms.push(quote! {
(atom!(#name), #arity) => true (atom!(#name), #arity)
}); });
} }
@@ -2549,16 +2579,17 @@ pub fn generate_instructions_rs() -> TokenStream {
}); });
is_inbuilt_arms.push(quote! { is_inbuilt_arms.push(quote! {
(atom!(#name), #arity) => true (atom!(#name), #arity)
}); });
is_inlined_arms.push(quote! { is_inlined_arms.push(quote! {
(atom!(#name), #arity) => true (atom!(#name), #arity)
}); });
} }
for (name, arity, variant) in instr_data.system_clause_type_variants { for (name, arity, variant) in instr_data.system_clause_type_variants {
let ident = variant.ident.clone(); let ident = variant.ident.clone();
let ident_s = ident.to_string();
let variant_fields: Vec<_> = variant let variant_fields: Vec<_> = variant
.fields .fields
@@ -2574,19 +2605,13 @@ pub fn generate_instructions_rs() -> TokenStream {
.collect(); .collect();
clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() { clause_type_from_name_and_arity_arms.push(if !variant_fields.is_empty() {
if ident.to_string() == "SetCutPoint" { if ident_s == "SetCutPoint" || ident_s == "SetCutPointByDefault" {
quote! { quote! {
(atom!(#name), #arity) => ClauseType::System( (atom!(#name), #arity) => ClauseType::System(
SystemClauseType::#ident(temp_v!(1)) SystemClauseType::#ident(temp_v!(1))
) )
} }
} else if ident.to_string() == "SetCutPointByDefault" { } else if ident_s == "InlineCallN" {
quote! {
(atom!(#name), #arity) => ClauseType::System(
SystemClauseType::#ident(temp_v!(1))
)
}
} else if ident.to_string() == "InlineCallN" {
quote! { quote! {
(atom!(#name), arity) => ClauseType::System( (atom!(#name), arity) => ClauseType::System(
SystemClauseType::#ident(arity) SystemClauseType::#ident(arity)
@@ -2649,11 +2674,11 @@ pub fn generate_instructions_rs() -> TokenStream {
is_inbuilt_arms.push(if let Arity::Ident("arity") = &arity { is_inbuilt_arms.push(if let Arity::Ident("arity") = &arity {
quote! { quote! {
(atom!(#name), _arity) => true (atom!(#name), _)
} }
} else { } else {
quote! { quote! {
(atom!(#name), #arity) => true (atom!(#name), #arity)
} }
}); });
} }
@@ -2724,7 +2749,7 @@ pub fn generate_instructions_rs() -> TokenStream {
}); });
is_inbuilt_arms.push(quote! { is_inbuilt_arms.push(quote! {
(atom!(#name), #arity) => true (atom!(#name), #arity)
}); });
} }
@@ -2798,7 +2823,7 @@ pub fn generate_instructions_rs() -> TokenStream {
}); });
is_inbuilt_arms.push(quote! { is_inbuilt_arms.push(quote! {
(atom!(#name), _arity) => true (atom!(#name), _)
}); });
} }
@@ -2819,8 +2844,8 @@ pub fn generate_instructions_rs() -> TokenStream {
let placeholder_ids: Vec<_> = let placeholder_ids: Vec<_> =
(0..enum_arity).map(|n| format_ident!("f_{}", n)).collect(); (0..enum_arity).map(|n| format_ident!("f_{}", n)).collect();
if variant_string.starts_with("Call") { if let Some(variant_suffix) = variant_string.strip_prefix("Call") {
let execute_ident = format_ident!("Execute{}", variant_string["Call".len()..]); let execute_ident = format_ident!("Execute{}", variant_suffix);
Some(if enum_arity == 0 { Some(if enum_arity == 0 {
quote! { quote! {
@@ -2833,9 +2858,8 @@ pub fn generate_instructions_rs() -> TokenStream {
Instruction::#execute_ident(#(#placeholder_ids),*) Instruction::#execute_ident(#(#placeholder_ids),*)
} }
}) })
} else if variant_string.starts_with("DefaultCall") { } else if let Some(variant_suffix) = variant_string.strip_prefix("DefaultCall") {
let execute_ident = let execute_ident = format_ident!("DefaultExecute{}", variant_suffix);
format_ident!("DefaultExecute{}", variant_string["DefaultCall".len()..]);
Some(if enum_arity == 0 { Some(if enum_arity == 0 {
quote! { quote! {
@@ -2868,29 +2892,20 @@ pub fn generate_instructions_rs() -> TokenStream {
0 0
}; };
if variant_string.starts_with("Execute") { if variant_string.starts_with("Execute") || variant_string.starts_with("DefaultExecute")
{
Some(if enum_arity == 0 { Some(if enum_arity == 0 {
quote! { quote! {
Instruction::#variant_ident => true Instruction::#variant_ident
} }
} else { } else {
quote! { quote! {
Instruction::#variant_ident(..) => true Instruction::#variant_ident(..)
}
})
} else if variant_string.starts_with("DefaultExecute") {
Some(if enum_arity == 0 {
quote! {
Instruction::#variant_ident => true
}
} else {
quote! {
Instruction::#variant_ident(..) => true
} }
}) })
} else if variant_string == "JmpByExecute" { } else if variant_string == "JmpByExecute" {
Some(quote! { Some(quote! {
Instruction::#variant_ident(..) => true Instruction::#variant_ident(..)
}) })
} else { } else {
None None
@@ -2955,11 +2970,11 @@ pub fn generate_instructions_rs() -> TokenStream {
Some(if enum_arity == 0 { Some(if enum_arity == 0 {
quote! { quote! {
Instruction::#variant_ident => true Instruction::#variant_ident
} }
} else { } else {
quote! { quote! {
Instruction::#variant_ident(..) => true Instruction::#variant_ident(..)
} }
}) })
}) })
@@ -2970,7 +2985,7 @@ pub fn generate_instructions_rs() -> TokenStream {
.iter() .iter()
.rev() // produce default, execute & default & execute cases first. .rev() // produce default, execute & default & execute cases first.
.cloned() .cloned()
.filter_map(|(name, arity, _, variant)| { .map(|(name, arity, _, variant)| {
let variant_ident = variant.ident.clone(); let variant_ident = variant.ident.clone();
let variant_string = variant.ident.to_string(); let variant_string = variant.ident.to_string();
let arity = match arity { let arity = match arity {
@@ -2978,6 +2993,7 @@ pub fn generate_instructions_rs() -> TokenStream {
_ => 1, _ => 1,
}; };
#[allow(clippy::collapsible_else_if)]
Some(if variant_string.starts_with("Execute") { Some(if variant_string.starts_with("Execute") {
if arity == 0 { if arity == 0 {
quote! { quote! {
@@ -3066,10 +3082,10 @@ pub fn generate_instructions_rs() -> TokenStream {
match arity { match arity {
Arity::Static(_) if enum_arity == 0 => { Arity::Static(_) if enum_arity == 0 => {
quote! { &Instruction::#ident => (atom!(#name), #arity) } quote! { Instruction::#ident => (atom!(#name), #arity) }
} }
Arity::Static(_) => { Arity::Static(_) => {
quote! { &Instruction::#ident(..) => (atom!(#name), #arity) } quote! { Instruction::#ident(..) => (atom!(#name), #arity) }
} }
Arity::Ident(_) if enum_arity == 0 => { Arity::Ident(_) if enum_arity == 0 => {
quote! { &Instruction::#ident(#arity) => (atom!(#name), #arity) } quote! { &Instruction::#ident(#arity) => (atom!(#name), #arity) }
@@ -3169,12 +3185,9 @@ pub fn generate_instructions_rs() -> TokenStream {
} }
pub fn is_inbuilt(name: Atom, arity: usize) -> bool { pub fn is_inbuilt(name: Atom, arity: usize) -> bool {
match (name, arity) { matches!((name, arity),
#( #(#is_inbuilt_arms)|*
#is_inbuilt_arms, )
)*
_ => false,
}
} }
pub fn name(&self) -> Atom { pub fn name(&self) -> Atom {
@@ -3186,12 +3199,9 @@ pub fn generate_instructions_rs() -> TokenStream {
} }
pub fn is_inlined(name: Atom, arity: usize) -> bool { pub fn is_inlined(name: Atom, arity: usize) -> bool {
match (name, arity) { matches!((name, arity),
#( #(#is_inlined_arms)|*
#is_inlined_arms, )
)*
_ => false,
}
} }
} }
@@ -3230,29 +3240,23 @@ pub fn generate_instructions_rs() -> TokenStream {
} }
pub fn is_execute(&self) -> bool { pub fn is_execute(&self) -> bool {
match self { matches!(self,
#( #(#is_execute_arms)|*
#is_execute_arms, )
)*
_ => false,
}
} }
pub fn is_ctrl_instr(&self) -> bool { pub fn is_ctrl_instr(&self) -> bool {
match self { matches!(self,
&Instruction::Allocate(_) | Instruction::Allocate(_) |
&Instruction::Deallocate | Instruction::Deallocate |
&Instruction::Proceed | Instruction::Proceed |
&Instruction::RevJmpBy(_) => true, Instruction::RevJmpBy(_) |
#( #(#control_flow_arms)|*
#control_flow_arms, )
)*
_ => false,
}
} }
pub fn is_query_instr(&self) -> bool { pub fn is_query_instr(&self) -> bool {
match self { matches!(self,
&Instruction::GetVariable(..) | &Instruction::GetVariable(..) |
&Instruction::PutConstant(..) | &Instruction::PutConstant(..) |
&Instruction::PutList(..) | &Instruction::PutList(..) |
@@ -3265,9 +3269,8 @@ pub fn generate_instructions_rs() -> TokenStream {
&Instruction::SetLocalValue(..) | &Instruction::SetLocalValue(..) |
&Instruction::SetVariable(..) | &Instruction::SetVariable(..) |
&Instruction::SetValue(..) | &Instruction::SetValue(..) |
&Instruction::SetVoid(..) => true, &Instruction::SetVoid(..)
_ => false, )
}
} }
} }
@@ -3335,7 +3338,8 @@ enum Arity {
impl From<&'static str> for Arity { impl From<&'static str> for Arity {
fn from(arity: &'static str) -> Self { fn from(arity: &'static str) -> Self {
usize::from_str_radix(&arity, 10) arity
.parse::<usize>()
.map(Arity::Static) .map(Arity::Static)
.unwrap_or_else(|_| Arity::Ident(arity)) .unwrap_or_else(|_| Arity::Ident(arity))
} }
@@ -3442,13 +3446,12 @@ impl InstructionData {
panic!("type ID is: {}", id); panic!("type ID is: {}", id);
}; };
let v_string = variant.ident.to_string(); let v_ident = variant
.ident
let v_ident = if v_string.starts_with("Call") { .to_string()
format_ident!("{}", v_string["Call".len()..]) .strip_prefix("Call")
} else { .map(|s| format_ident!("{}", s))
variant.ident.clone() .unwrap_or_else(|| variant.ident.clone());
};
let generated_variant = let generated_variant =
create_instr_variant(format_ident!("{}{}", prefix, v_ident), variant.clone()); create_instr_variant(format_ident!("{}{}", prefix, v_ident), variant.clone());

View File

@@ -55,7 +55,7 @@ fn main() {
let out_dir = env::var("OUT_DIR").unwrap(); let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("libraries.rs"); let dest_path = Path::new(&out_dir).join("libraries.rs");
let mut libraries = File::create(&dest_path).unwrap(); let mut libraries = File::create(dest_path).unwrap();
let lib_path = Path::new("src/lib"); let lib_path = Path::new("src/lib");
libraries libraries
@@ -66,7 +66,7 @@ fn main() {
) )
.unwrap(); .unwrap();
find_prolog_files(&mut libraries, "", &lib_path); find_prolog_files(&mut libraries, "", lib_path);
libraries.write_all(b"\n m\n };\n}\n").unwrap(); libraries.write_all(b"\n m\n };\n}\n").unwrap();
let instructions_path = Path::new(&out_dir).join("instructions.rs"); let instructions_path = Path::new(&out_dir).join("instructions.rs");

View File

@@ -35,7 +35,7 @@ impl Parse for ReadHeapCellExprAndArms {
arms.push(input.parse()?); arms.push(input.parse()?);
while !input.is_empty() { while !input.is_empty() {
if let Ok(_) = input.parse::<Token![,]>() {} let _ = input.parse::<Token![,]>();
arms.push(input.parse()?); arms.push(input.parse()?);
} }
@@ -52,7 +52,7 @@ impl Parse for MacroFnArgs {
} }
while !input.is_empty() { while !input.is_empty() {
if let Ok(_) = input.parse::<Token![,]>() {} let _ = input.parse::<Token![,]>();
args.push(input.parse()?); args.push(input.parse()?);
} }
@@ -65,22 +65,20 @@ impl<'ast> Visit<'ast> for StaticStrVisitor {
let Macro { path, .. } = m; let Macro { path, .. } = m;
if path.is_ident("atom") { if path.is_ident("atom") {
if let Some(Lit::Str(string)) = m.parse_body::<Lit>().ok() { if let Ok(Lit::Str(string)) = m.parse_body::<Lit>() {
self.static_strs.insert(string.value()); self.static_strs.insert(string.value());
} }
} else if path.is_ident("read_heap_cell") || path.is_ident("match_untyped_arena_ptr") { } else if path.is_ident("read_heap_cell") || path.is_ident("match_untyped_arena_ptr") {
if let Some(m) = m.parse_body::<ReadHeapCellExprAndArms>().ok() { if let Ok(m) = m.parse_body::<ReadHeapCellExprAndArms>() {
self.visit_expr(&m.expr); self.visit_expr(&m.expr);
for e in m.arms { for e in m.arms {
self.visit_arm(&e); self.visit_arm(&e);
} }
} }
} else { } else if let Ok(m) = m.parse_body::<MacroFnArgs>() {
if let Some(m) = m.parse_body::<MacroFnArgs>().ok() { for e in m.args {
for e in m.args { self.visit_expr(&e);
self.visit_expr(&e);
}
} }
} }
} }
@@ -147,9 +145,8 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
visitor.visit_file(&syntax); visitor.visit_file(&syntax);
} }
match process_filepath(instruction_rs_path) { if let Ok(syntax) = process_filepath(instruction_rs_path) {
Ok(syntax) => visitor.visit_file(&syntax), visitor.visit_file(&syntax)
Err(_) => {}
} }
let indices = (0..visitor.static_strs.len()).map(|i| (i << 3) as u64); let indices = (0..visitor.static_strs.len()).map(|i| (i << 3) as u64);
@@ -161,7 +158,7 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
quote! { quote! {
use phf; use phf;
static STRINGS: [&'static str; #static_strs_len] = [ static STRINGS: [&str; #static_strs_len] = [
#( #(
#static_strs, #static_strs,
)* )*

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 4.8 MiB

View File

@@ -24,11 +24,12 @@ pub(crate) trait Allocator {
code: &mut CodeDeque, code: &mut CodeDeque,
); );
#[allow(clippy::too_many_arguments)]
fn mark_reserved_var<'a, Target: CompilationTarget<'a>>( fn mark_reserved_var<'a, Target: CompilationTarget<'a>>(
&mut self, &mut self,
var_num: usize, var_num: usize,
lvl: Level, lvl: Level,
cell: &'a Cell<VarReg>, cell: &Cell<VarReg>,
term_loc: GenContext, term_loc: GenContext,
code: &mut CodeDeque, code: &mut CodeDeque,
r: RegType, r: RegType,
@@ -41,51 +42,16 @@ pub(crate) trait Allocator {
&mut self, &mut self,
var_num: usize, var_num: usize,
lvl: Level, lvl: Level,
cell: &'a Cell<VarReg>, cell: &Cell<VarReg>,
context: GenContext, context: GenContext,
code: &mut CodeDeque, code: &mut CodeDeque,
); );
fn reset(&mut self); fn reset(&mut self);
fn reset_arg(&mut self, arg_num: usize); fn reset_arg(&mut self, arg_num: usize);
fn reset_at_head(&mut self, args: &Vec<Term>); fn reset_at_head(&mut self, args: &[Term]);
fn reset_contents(&mut self); fn reset_contents(&mut self);
fn advance_arg(&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; 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
}
*/
} }

View File

@@ -91,14 +91,14 @@ pub fn lookup_float(
) -> RcuRef<RawBlock<F64Table>, UnsafeCell<OrderedFloat<f64>>> { ) -> RcuRef<RawBlock<F64Table>, UnsafeCell<OrderedFloat<f64>>> {
let f64table = global_f64table() let f64table = global_f64table()
.read() .read()
.unwrap() .unwrap()
.upgrade() .upgrade()
.expect("We should only be looking up floats while there is a float table"); .expect("We should only be looking up floats while there is a float table");
RcuRef::try_map(f64table.block.active_epoch(), |raw_block| unsafe { RcuRef::try_map(f64table.block.active_epoch(), |raw_block| unsafe {
raw_block raw_block
.base .base
.offset(offset.0 as isize) .add(offset.0)
.cast_mut() .cast_mut()
.cast::<UnsafeCell<OrderedFloat<f64>>>() .cast::<UnsafeCell<OrderedFloat<f64>>>()
.as_ref() .as_ref()
@@ -129,6 +129,7 @@ impl F64Table {
} }
} }
#[allow(clippy::missing_safety_doc)]
pub unsafe fn build_with(&self, value: f64) -> F64Offset { pub unsafe fn build_with(&self, value: f64) -> F64Offset {
let update_guard = self.update.lock(); let update_guard = self.update.lock();
@@ -152,9 +153,7 @@ impl F64Table {
ptr::write(ptr as *mut OrderedFloat<f64>, OrderedFloat(value)); ptr::write(ptr as *mut OrderedFloat<f64>, OrderedFloat(value));
let float = F64Offset { let float = F64Offset(ptr as usize - block_epoch.base as usize);
0: ptr as usize - block_epoch.base as usize,
};
// atometable would have to update the index table at this point // atometable would have to update the index table at this point
@@ -230,7 +229,7 @@ impl<T: ?Sized + PartialOrd> PartialOrd for TypedArenaPtr<T> {
impl<T: ?Sized + PartialEq> PartialEq for TypedArenaPtr<T> { impl<T: ?Sized + PartialEq> PartialEq for TypedArenaPtr<T> {
fn eq(&self, other: &TypedArenaPtr<T>) -> bool { fn eq(&self, other: &TypedArenaPtr<T>) -> bool {
self.0 == other.0 || &**self == &**other self.0 == other.0 || **self == **other
} }
} }
@@ -245,13 +244,13 @@ impl<T: ?Sized + Ord> Ord for TypedArenaPtr<T> {
impl<T: ?Sized + Hash> Hash for TypedArenaPtr<T> { impl<T: ?Sized + Hash> Hash for TypedArenaPtr<T> {
#[inline(always)] #[inline(always)]
fn hash<H: Hasher>(&self, hasher: &mut H) { fn hash<H: Hasher>(&self, hasher: &mut H) {
(&*self as &T).hash(hasher) (self as &T).hash(hasher)
} }
} }
impl<T: ?Sized> Clone for TypedArenaPtr<T> { impl<T: ?Sized> Clone for TypedArenaPtr<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
TypedArenaPtr(self.0) *self
} }
} }
@@ -279,10 +278,10 @@ impl<T: fmt::Display> fmt::Display for TypedArenaPtr<T> {
impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> { impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> {
// data must be allocated in the arena already. // data must be allocated in the arena already.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline] #[inline]
pub const fn new(data: *mut T) -> Self { pub const fn new(data: *mut T) -> Self {
let result = unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }; unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }
result
} }
#[inline] #[inline]
@@ -347,6 +346,7 @@ pub trait ArenaAllocated: Sized {
mem::size_of::<ArenaHeader>() mem::size_of::<ArenaHeader>()
} }
#[allow(clippy::missing_safety_doc)]
unsafe fn alloc(arena: &mut Arena, value: Self) -> Self::PtrToAllocated { unsafe fn alloc(arena: &mut Arena, value: Self) -> Self::PtrToAllocated {
let size = value.size() + mem::size_of::<AllocSlab>(); let size = value.size() + mem::size_of::<AllocSlab>();
@@ -363,7 +363,7 @@ pub trait ArenaAllocated: Sized {
(*slab).header = ArenaHeader::build_with(value.size() as u64, Self::tag()); (*slab).header = ArenaHeader::build_with(value.size() as u64, Self::tag());
let offset = (*slab).payload_offset(); let offset = (*slab).payload_offset();
let result = value.copy_to_arena(offset as *mut Self); let result = value.copy_to_arena(offset);
arena.base = slab; arena.base = slab;
@@ -390,7 +390,7 @@ impl Eq for F64Ptr {}
impl PartialOrd for F64Ptr { impl PartialOrd for F64Ptr {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(&**other) Some(self.cmp(other))
} }
} }
@@ -403,13 +403,13 @@ impl Ord for F64Ptr {
impl Hash for F64Ptr { impl Hash for F64Ptr {
#[inline(always)] #[inline(always)]
fn hash<H: Hasher>(&self, hasher: &mut H) { fn hash<H: Hasher>(&self, hasher: &mut H) {
(&*self as &OrderedFloat<f64>).hash(hasher) (self as &OrderedFloat<f64>).hash(hasher)
} }
} }
impl fmt::Display for F64Ptr { impl fmt::Display for F64Ptr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", *self) write!(f, "{}", self as &OrderedFloat<f64>)
} }
} }
@@ -418,7 +418,7 @@ impl Deref for F64Ptr {
#[inline] #[inline]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
unsafe { &*self.0.get().as_ref().unwrap() } unsafe { self.0.get().as_ref().unwrap() }
} }
} }
@@ -478,7 +478,7 @@ impl Eq for F64Offset {}
impl PartialOrd for F64Offset { impl PartialOrd for F64Offset {
#[inline(always)] #[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.as_ptr().partial_cmp(&other.as_ptr()) Some(self.cmp(other))
} }
} }
@@ -515,11 +515,12 @@ impl ArenaAllocated for Integer {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline] #[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated { fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe { unsafe {
ptr::write(dst, self); ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self) TypedArenaPtr::new(dst)
} }
} }
} }
@@ -537,11 +538,12 @@ impl ArenaAllocated for Rational {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline] #[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated { fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe { unsafe {
ptr::write(dst, self); ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self) TypedArenaPtr::new(dst)
} }
} }
} }
@@ -559,11 +561,12 @@ impl ArenaAllocated for LiveLoadState {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline] #[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated { fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe { unsafe {
ptr::write(dst, self); ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self) TypedArenaPtr::new(dst)
} }
} }
} }
@@ -581,11 +584,12 @@ impl ArenaAllocated for TcpListener {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline] #[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated { fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe { unsafe {
ptr::write(dst, self); ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self) TypedArenaPtr::new(dst)
} }
} }
} }
@@ -604,11 +608,12 @@ impl ArenaAllocated for HttpListener {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline] #[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated { fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe { unsafe {
ptr::write(dst, self); ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self) TypedArenaPtr::new(dst)
} }
} }
} }
@@ -627,11 +632,12 @@ impl ArenaAllocated for HttpResponse {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline] #[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated { fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe { unsafe {
ptr::write(dst, self); ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self) TypedArenaPtr::new(dst)
} }
} }
} }
@@ -649,11 +655,12 @@ impl ArenaAllocated for IndexPtr {
mem::size_of::<Self>() mem::size_of::<Self>()
} }
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline] #[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated { fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe { unsafe {
ptr::write(dst, self); ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self) TypedArenaPtr::new(dst)
} }
} }
@@ -672,7 +679,10 @@ impl ArenaAllocated for IndexPtr {
(*slab).next = arena.base; (*slab).next = arena.base;
let result = value.copy_to_arena(mem::transmute::<_, *mut IndexPtr>(&(*slab).header)); let result = value.copy_to_arena(
&(*slab).header as *const crate::arena::ArenaHeader
as *mut crate::machine::machine_indices::IndexPtr,
);
arena.base = slab; arena.base = slab;
result result
@@ -697,6 +707,7 @@ pub struct Arena {
unsafe impl Send for Arena {} unsafe impl Send for Arena {}
unsafe impl Sync for Arena {} unsafe impl Sync for Arena {}
#[allow(clippy::new_without_default)]
impl Arena { impl Arena {
#[inline] #[inline]
pub fn new() -> Self { pub fn new() -> Self {
@@ -810,9 +821,9 @@ impl AllocSlab {
} }
fn payload_offset<T>(&self) -> *mut T { fn payload_offset<T>(&self) -> *mut T {
let mut ptr = (self as *const AllocSlab) as usize; // This looks really scary, should this method be marked as unsafe?
ptr += mem::size_of::<AllocSlab>(); // Also, this seems to cause UB.
ptr as *mut T unsafe { (self as *const AllocSlab).add(1) as *mut T }
} }
} }
@@ -837,12 +848,12 @@ mod tests {
let mut cell = HeapCellValue::from(fp.clone()); let mut cell = HeapCellValue::from(fp.clone());
assert_eq!(cell.get_tag(), HeapCellValueTag::F64); assert_eq!(cell.get_tag(), HeapCellValueTag::F64);
assert_eq!(cell.get_mark_bit(), false); assert!(!cell.get_mark_bit());
assert_eq!(fp.deref(), &OrderedFloat(f)); assert_eq!(fp.deref(), &OrderedFloat(f));
cell.set_mark_bit(true); cell.set_mark_bit(true);
assert_eq!(cell.get_mark_bit(), true); assert!(cell.get_mark_bit());
read_heap_cell!(cell, read_heap_cell!(cell,
(HeapCellValueTag::F64, ptr) => { (HeapCellValueTag::F64, ptr) => {
@@ -853,6 +864,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn heap_cell_value_const_cast() { fn heap_cell_value_const_cast() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
@@ -874,7 +886,7 @@ mod tests {
); );
} }
None => { None => {
assert!(false); unreachable!();
} }
} }
@@ -890,12 +902,13 @@ mod tests {
); );
} }
None => { None => {
assert!(false); unreachable!();
} }
} }
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on arena.rs UB")]
fn heap_put_literal_tests() { fn heap_put_literal_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
@@ -912,7 +925,6 @@ mod tests {
let untyped_arena_ptr = match cell.to_untyped_arena_ptr() { let untyped_arena_ptr = match cell.to_untyped_arena_ptr() {
Some(ptr) => ptr, Some(ptr) => ptr,
None => { None => {
assert!(false);
unreachable!() unreachable!()
} }
}; };
@@ -954,7 +966,7 @@ mod tests {
); );
} }
None => { None => {
assert!(false); // we fail. unreachable!();
} }
} }
@@ -991,7 +1003,7 @@ mod tests {
assert_eq!(&*atom.as_str(), "f"); assert_eq!(&*atom.as_str(), "f");
} }
None => { None => {
assert!(false); unreachable!();
} }
} }
@@ -1026,7 +1038,7 @@ mod tests {
assert_eq!(&*pstr.as_str_from(0), "ronan"); assert_eq!(&*pstr.as_str_from(0), "ronan");
} }
None => { None => {
assert!(false); unreachable!();
} }
} }
@@ -1046,7 +1058,7 @@ mod tests {
match fixnum_cell.to_fixnum() { match fixnum_cell.to_fixnum() {
Some(n) => assert_eq!(n.get_num(), 3), Some(n) => assert_eq!(n.get_num(), 3),
None => assert!(false), None => unreachable!(),
} }
read_heap_cell!(fixnum_cell, read_heap_cell!(fixnum_cell,
@@ -1062,52 +1074,48 @@ mod tests {
match fixnum_b_cell.to_fixnum() { match fixnum_b_cell.to_fixnum() {
Some(n) => assert_eq!(n.get_num(), 1 << 54), Some(n) => assert_eq!(n.get_num(), 1 << 54),
None => assert!(false), None => unreachable!(),
} }
match Fixnum::build_with_checked(1 << 56) { if Fixnum::build_with_checked(1 << 56).is_ok() {
Ok(_) => assert!(false), unreachable!()
_ => assert!(true),
} }
match Fixnum::build_with_checked(i64::MAX) { if Fixnum::build_with_checked(i64::MAX).is_ok() {
Ok(_) => assert!(false), unreachable!()
_ => assert!(true),
} }
match Fixnum::build_with_checked(i64::MIN) { if Fixnum::build_with_checked(i64::MIN).is_ok() {
Ok(_) => assert!(false), unreachable!()
_ => assert!(true),
} }
match Fixnum::build_with_checked(-1) { match Fixnum::build_with_checked(-1) {
Ok(n) => assert_eq!(n.get_num(), -1), Ok(n) => assert_eq!(n.get_num(), -1),
_ => assert!(false), _ => unreachable!(),
} }
match Fixnum::build_with_checked((1 << 55) - 1) { match Fixnum::build_with_checked((1 << 55) - 1) {
Ok(n) => assert_eq!(n.get_num(), (1 << 55) - 1), Ok(n) => assert_eq!(n.get_num(), (1 << 55) - 1),
_ => assert!(false), _ => unreachable!(),
} }
match Fixnum::build_with_checked(-(1 << 55)) { match Fixnum::build_with_checked(-(1 << 55)) {
Ok(n) => assert_eq!(n.get_num(), -(1 << 55)), Ok(n) => assert_eq!(n.get_num(), -(1 << 55)),
_ => assert!(false), _ => unreachable!(),
} }
match Fixnum::build_with_checked(-(1 << 55) - 1) { if Fixnum::build_with_checked(-(1 << 55) - 1).is_ok() {
Ok(_n) => assert!(false), unreachable!()
_ => assert!(true),
} }
match Fixnum::build_with_checked(-1) { match Fixnum::build_with_checked(-1) {
Ok(n) => assert_eq!(-n, Fixnum::build_with(1)), Ok(n) => assert_eq!(-n, Fixnum::build_with(1)),
_ => assert!(false), _ => unreachable!(),
} }
// float // float
let float = 3.1415926f64; let float = std::f64::consts::PI;
let float_ptr = float_alloc!(float, wam.machine_st.arena); let float_ptr = float_alloc!(float, wam.machine_st.arena);
let cell = HeapCellValue::from(float_ptr); let cell = HeapCellValue::from(float_ptr);

View File

@@ -268,7 +268,7 @@ impl<'a> ArithmeticEvaluator<'a> {
let ninterm = if a1.interm_or(0) == 0 { let ninterm = if a1.interm_or(0) == 0 {
self.incr_interm() self.incr_interm()
} else { } else {
self.interm.push(a1.clone()); self.interm.push(a1);
a1.interm_or(0) a1.interm_or(0)
}; };
@@ -312,9 +312,8 @@ impl<'a> ArithmeticEvaluator<'a> {
arg: usize, arg: usize,
) -> Result<ArithCont, ArithmeticError> { ) -> Result<ArithCont, ArithmeticError> {
let mut code = CodeDeque::new(); let mut code = CodeDeque::new();
let mut iter = src.iter()?;
while let Some(term_ref) = iter.next() { for term_ref in src.iter()? {
match term_ref? { match term_ref? {
ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?, ArithTermRef::Literal(c) => push_literal(&mut self.interm, c)?,
ArithTermRef::Var(lvl, cell, name) => { ArithTermRef::Var(lvl, cell, name) => {
@@ -353,17 +352,17 @@ impl<'a> ArithmeticEvaluator<'a> {
} }
// integer division rounding function -- 9.1.3.1. // integer division rounding function -- 9.1.3.1.
pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number { pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Number {
match n { match n {
&Number::Integer(i) => { &Number::Integer(i) => {
let result = (&*i).try_into(); let result = (&*i).try_into();
if let Ok(value) = result{ if let Ok(value) = result {
fixnum!(Number, value, arena) fixnum!(Number, value, arena)
} else { } else {
*n *n
} }
} }
&Number::Fixnum(_) => *n, Number::Fixnum(_) => *n,
&Number::Float(f) => { &Number::Float(f) => {
let f = f.floor(); let f = f.floor();
@@ -376,7 +375,7 @@ pub(crate) fn rnd_i<'a>(n: &'a Number, arena: &mut Arena) -> Number {
Number::Integer(arena_alloc!(Integer::from(f.0 as i64), arena)) Number::Integer(arena_alloc!(Integer::from(f.0 as i64), arena))
} }
} }
&Number::Rational(ref r) => { Number::Rational(ref r) => {
let (_, floor) = (r.fract(), r.floor()); let (_, floor) = (r.fract(), r.floor());
if let Ok(value) = (&floor).try_into() { if let Ok(value) = (&floor).try_into() {
@@ -399,9 +398,9 @@ impl From<Fixnum> for Integer {
pub(crate) fn rnd_f(n: &Number) -> f64 { pub(crate) fn rnd_f(n: &Number) -> f64 {
match n { match n {
&Number::Fixnum(n) => n.get_num() as f64, &Number::Fixnum(n) => n.get_num() as f64,
&Number::Integer(ref n) => n.to_f64().value(), Number::Integer(ref n) => n.to_f64().value(),
&Number::Float(OrderedFloat(f)) => f, &Number::Float(OrderedFloat(f)) => f,
&Number::Rational(ref r) => r.to_f64().value(), Number::Rational(ref r) => r.to_f64().value(),
} }
} }
@@ -529,47 +528,51 @@ impl PartialEq for Number {
fn eq(&self, rhs: &Self) -> bool { fn eq(&self, rhs: &Self) -> bool {
match (self, rhs) { match (self, rhs) {
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.eq(&n2), (&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.eq(&n2),
(&Number::Fixnum(n1), &Number::Integer(ref n2)) => n1.get_num().num_eq(&**n2), (&Number::Fixnum(n1), Number::Integer(ref n2)) => n1.get_num().num_eq(&**n2),
(&Number::Integer(ref n1), &Number::Fixnum(n2)) => (&**n1).num_eq(&n2.get_num()), (Number::Integer(ref n1), &Number::Fixnum(n2)) => n1.num_eq(&n2.get_num()),
(&Number::Fixnum(n1), &Number::Rational(ref n2)) => Integer::from(n1.get_num()).num_eq(&**n2), (&Number::Fixnum(n1), Number::Rational(ref n2)) => {
(&Number::Rational(ref n1), &Number::Fixnum(n2)) => (&**n1).num_eq(&Integer::from(n2.get_num())), Integer::from(n1.get_num()).num_eq(&**n2)
}
(Number::Rational(ref n1), &Number::Fixnum(n2)) => {
n1.num_eq(&Integer::from(n2.get_num()))
}
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).eq(&n2), (&Number::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::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::Integer(ref n2)) => n1.eq(n2),
(&Number::Integer(ref n1), Number::Float(n2)) => { (Number::Integer(ref n1), Number::Float(n2)) => {
OrderedFloat(n1.to_f64().value()).eq(n2) OrderedFloat(n1.to_f64().value()).eq(n2)
} }
(&Number::Float(n1), &Number::Integer(ref n2)) => { (&Number::Float(n1), Number::Integer(ref n2)) => {
n1.eq(&OrderedFloat(n2.to_f64().value())) n1.eq(&OrderedFloat(n2.to_f64().value()))
} }
(&Number::Integer(ref n1), &Number::Rational(ref n2)) => { (Number::Integer(ref n1), Number::Rational(ref n2)) => {
#[cfg(feature = "num")] #[cfg(feature = "num")]
{ {
&Rational::from(&**n1) == &**n2 &Rational::from(&**n1) == &**n2
} }
#[cfg(not(feature = "num"))] #[cfg(not(feature = "num"))]
{ {
(&**n1).num_eq(&**n2) n1.num_eq(&**n2)
} }
} }
(&Number::Rational(ref n1), &Number::Integer(ref n2)) => { (Number::Rational(ref n1), Number::Integer(ref n2)) => {
#[cfg(feature = "num")] #[cfg(feature = "num")]
{ {
&**n1 == &Rational::from(&**n2) n1 == &Rational::from(&**n2)
} }
#[cfg(not(feature = "num"))] #[cfg(not(feature = "num"))]
{ {
(&**n1).num_eq(&**n2) n1.num_eq(&**n2)
} }
} }
(&Number::Rational(ref n1), &Number::Float(n2)) => { (Number::Rational(ref n1), &Number::Float(n2)) => {
OrderedFloat(n1.to_f64().value()).eq(&n2) OrderedFloat(n1.to_f64().value()).eq(&n2)
} }
(&Number::Float(n1), &Number::Rational(ref n2)) => { (&Number::Float(n1), Number::Rational(ref n2)) => {
n1.eq(&OrderedFloat(n2.to_f64().value())) n1.eq(&OrderedFloat(n2.to_f64().value()))
} }
(&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2), (&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2),
(&Number::Rational(ref r1), &Number::Rational(ref r2)) => r1.eq(&r2), (Number::Rational(ref r1), Number::Rational(ref r2)) => r1.eq(r2),
} }
} }
} }
@@ -589,8 +592,8 @@ impl PartialOrd<usize> for Number {
(n as usize).partial_cmp(rhs) (n as usize).partial_cmp(rhs)
} }
} }
Number::Integer(n) => Some((&**n).num_cmp(rhs)), Number::Integer(n) => Some((n).num_cmp(rhs)),
Number::Rational(r) => Some((&**r).num_cmp(&Integer::from(*rhs))), Number::Rational(r) => Some((r).num_cmp(&Integer::from(*rhs))),
Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)), Number::Float(f) => f.partial_cmp(&OrderedFloat(*rhs as f64)),
} }
} }
@@ -609,8 +612,8 @@ impl PartialEq<usize> for Number {
(n as usize).eq(rhs) (n as usize).eq(rhs)
} }
} }
Number::Integer(n) => (&**n).num_eq(rhs), Number::Integer(n) => (n).num_eq(rhs),
Number::Rational(r) => (&**r).num_eq(&Integer::from(*rhs)), Number::Rational(r) => (r).num_eq(&Integer::from(*rhs)),
Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)), Number::Float(f) => f.eq(&OrderedFloat(*rhs as f64)),
} }
} }
@@ -626,17 +629,17 @@ impl Ord for Number {
fn cmp(&self, rhs: &Number) -> Ordering { fn cmp(&self, rhs: &Number) -> Ordering {
match (self, rhs) { match (self, rhs) {
(&Number::Fixnum(n1), &Number::Fixnum(n2)) => n1.get_num().cmp(&n2.get_num()), (&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::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::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::Fixnum(n1), Number::Rational(n2)) => Rational::from(n1.get_num()).cmp(n2),
(Number::Rational(n1), &Number::Fixnum(n2)) => { (Number::Rational(n1), &Number::Fixnum(n2)) => {
(&**n1).cmp(&Rational::from(n2.get_num())) (**n1).cmp(&Rational::from(n2.get_num()))
} }
(&Number::Fixnum(n1), &Number::Float(n2)) => OrderedFloat(n1.get_num() as f64).cmp(&n2), (&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::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::Integer(n2)) => (*n1).cmp(&*n2),
(&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(n2), (&Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).cmp(n2),
(&Number::Float(n1), &Number::Integer(ref n2)) => { (&Number::Float(n1), Number::Integer(ref n2)) => {
n1.cmp(&OrderedFloat(n2.to_f64().value())) n1.cmp(&OrderedFloat(n2.to_f64().value()))
} }
(&Number::Integer(n1), &Number::Rational(n2)) => { (&Number::Integer(n1), &Number::Rational(n2)) => {
@@ -646,7 +649,7 @@ impl Ord for Number {
} }
#[cfg(not(feature = "num"))] #[cfg(not(feature = "num"))]
{ {
(&*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less) (*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
} }
} }
(&Number::Rational(n1), &Number::Integer(n2)) => { (&Number::Rational(n1), &Number::Integer(n2)) => {
@@ -656,7 +659,7 @@ impl Ord for Number {
} }
#[cfg(not(feature = "num"))] #[cfg(not(feature = "num"))]
{ {
(&*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less) (*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
} }
} }
(&Number::Rational(n1), &Number::Float(n2)) => { (&Number::Rational(n1), &Number::Float(n2)) => {

View File

@@ -17,7 +17,7 @@ use std::sync::Weak;
use indexmap::IndexSet; use indexmap::IndexSet;
use modular_bitfield::prelude::*; use scryer_modular_bitfield::prelude::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Atom { pub struct Atom {
@@ -186,7 +186,7 @@ impl Atom {
unsafe { unsafe {
AtomTableRef::try_map(atom_table.buf(), |buf| { AtomTableRef::try_map(atom_table.buf(), |buf| {
(buf as *const u8) (buf as *const u8)
.offset(((self.index as usize) - (STRINGS.len() << 3)) as isize) .add((self.index as usize) - (STRINGS.len() << 3))
.as_ref() .as_ref()
}) })
} }
@@ -209,9 +209,13 @@ impl Atom {
} }
} }
pub fn is_empty(self) -> bool {
self.len() == 0
}
#[inline(always)] #[inline(always)]
pub fn flat_index(self) -> u64 { pub fn flat_index(self) -> u64 {
(self.index >> 3) as u64 self.index >> 3
} }
pub fn as_char(self) -> Option<char> { pub fn as_char(self) -> Option<char> {
@@ -232,20 +236,18 @@ impl Atom {
pub fn as_str(&self) -> AtomString<'static> { pub fn as_str(&self) -> AtomString<'static> {
if self.is_static() { if self.is_static() {
AtomString::Static(STRINGS[(self.index >> 3) as usize]) AtomString::Static(STRINGS[(self.index >> 3) as usize])
} else { } else if let Some(ptr) = self.as_ptr() {
if let Some(ptr) = self.as_ptr() { AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| {
AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| { let header =
let header = // Miri seems to hit this line a lot
unsafe { ptr::read::<AtomHeader>(ptr as *const u8 as *const AtomHeader) }; unsafe { ptr::read::<AtomHeader>(ptr as *const u8 as *const AtomHeader) };
let len = header.len() as usize; let len = header.len() as usize;
let buf = let buf = unsafe { (ptr as *const u8).add(mem::size_of::<AtomHeader>()) };
unsafe { (ptr as *const u8).offset(mem::size_of::<AtomHeader>() as isize) };
unsafe { str::from_utf8_unchecked(slice::from_raw_parts(buf, len)) } unsafe { str::from_utf8_unchecked(slice::from_raw_parts(buf, len)) }
})) }))
} else { } else {
AtomString::Static(&STRINGS[(self.index >> 3) as usize]) AtomString::Static(STRINGS[(self.index >> 3) as usize])
}
} }
} }
@@ -258,14 +260,14 @@ impl Atom {
return *self; return *self;
}; };
AtomTable::build_with(&atom_tbl, &sub_str) AtomTable::build_with(atom_tbl, sub_str)
} }
} }
unsafe fn write_to_ptr(string: &str, ptr: *mut u8) { unsafe fn write_to_ptr(string: &str, ptr: *mut u8) {
ptr::write(ptr as *mut _, AtomHeader::build_with(string.len() as u64)); 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; let str_ptr = ptr.add(mem::size_of::<AtomHeader>());
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr as *mut u8, string.len()); ptr::copy_nonoverlapping(string.as_ptr(), str_ptr, string.len());
} }
impl PartialOrd for Atom { impl PartialOrd for Atom {

View File

@@ -1,11 +1,10 @@
fn main() -> std::process::ExitCode { fn main() -> std::process::ExitCode {
use scryer_prolog::*;
use scryer_prolog::atom_table::Atom; use scryer_prolog::atom_table::Atom;
use std::sync::atomic::Ordering; use scryer_prolog::*;
#[cfg(feature = "repl")] #[cfg(feature = "repl")]
ctrlc::set_handler(move || { ctrlc::set_handler(move || {
scryer_prolog::machine::INTERRUPT.store(true, Ordering::Relaxed); scryer_prolog::machine::INTERRUPT.store(true, std::sync::atomic::Ordering::Relaxed);
}) })
.unwrap(); .unwrap();
@@ -23,6 +22,6 @@ fn main() -> std::process::ExitCode {
runtime.block_on(async move { runtime.block_on(async move {
let mut wam = machine::Machine::new(Default::default()); let mut wam = machine::Machine::new(Default::default());
wam.run_top_level(atom!("$toplevel"), (atom!("$repl"), 1)) wam.run_module_predicate(atom!("$toplevel"), (atom!("$repl"), 0))
}) })
} }

View File

@@ -10,6 +10,7 @@ use crate::parser::ast::*;
use crate::targets::*; use crate::targets::*;
use crate::temp_v; use crate::temp_v;
use crate::types::*; use crate::types::*;
use crate::variable_records::*;
use crate::instr; use crate::instr;
use crate::machine::disjuncts::*; use crate::machine::disjuncts::*;
@@ -60,6 +61,7 @@ impl BranchCodeStack {
marker: &mut DebrayAllocator, marker: &mut DebrayAllocator,
) -> SubsumedBranchHits { ) -> SubsumedBranchHits {
let mut subsumed_hits = SubsumedBranchHits::with_hasher(FxBuildHasher::default()); let mut subsumed_hits = SubsumedBranchHits::with_hasher(FxBuildHasher::default());
let mut propagated_var_nums = IndexSet::with_hasher(FxBuildHasher::default());
for idx in (self.stack.len() - depth..self.stack.len()).rev() { for idx in (self.stack.len() - depth..self.stack.len()).rev() {
let branch = &mut marker.branch_stack[idx]; let branch = &mut marker.branch_stack[idx];
@@ -85,9 +87,17 @@ impl BranchCodeStack {
} }
} }
if idx > self.stack.len() - depth {
propagated_var_nums.insert(var_num);
}
subsumed_hits.insert(var_num); subsumed_hits.insert(var_num);
} }
} }
for var_num in propagated_var_nums.drain(..) {
marker.branch_stack[idx - 1].add_branch_occurrence(var_num);
}
} }
subsumed_hits subsumed_hits
@@ -111,7 +121,7 @@ impl BranchCodeStack {
jump_span -= code.len() + 1; jump_span -= code.len() + 1;
} else { } else {
jump_span -= code.len() + 1; jump_span -= code.len() + 1;
code.push_back(instr!("jmp_by_call", jump_span as usize)); code.push_back(instr!("jmp_by_call", jump_span));
jump_span -= 1; jump_span -= 1;
} }
@@ -124,9 +134,9 @@ impl BranchCodeStack {
for mut branch_arm in self.stack.drain(self.stack.len() - depth..).rev() { for mut branch_arm in self.stack.drain(self.stack.len() - depth..).rev() {
let num_branch_arms = branch_arm.len(); let num_branch_arms = branch_arm.len();
branch_arm if let Some(code) = branch_arm.last_mut() {
.last_mut() code.extend(combined_code.drain(..))
.map(|code| code.extend(combined_code.drain(..))); }
for (idx, code) in branch_arm.into_iter().enumerate() { for (idx, code) in branch_arm.into_iter().enumerate() {
combined_code.push_back(if idx == 0 { combined_code.push_back(if idx == 0 {
@@ -277,7 +287,6 @@ impl DebrayAllocator {
code: &mut CodeDeque, code: &mut CodeDeque,
) -> RegType { ) -> RegType {
self.mark_var::<QueryInstruction>(var_num, Level::Shallow, vr, term_loc, code); self.mark_var::<QueryInstruction>(var_num, Level::Shallow, vr, term_loc, code);
vr.get().norm() vr.get().norm()
} }
@@ -296,7 +305,14 @@ impl DebrayAllocator {
self.mark_var_in_non_callable(var_num, term_loc, vr, code); self.mark_var_in_non_callable(var_num, term_loc, vr, code);
temp_v!(arg) temp_v!(arg)
} else { } else {
self.increment_running_count(var_num); if let VarAlloc::Perm(_, PermVarAllocation::Pending) =
&self.var_data.records[var_num].allocation
{
self.mark_var_in_non_callable(var_num, term_loc, vr, code);
} else {
self.increment_running_count(var_num);
}
RegType::Perm(p) RegType::Perm(p)
} }
} }
@@ -376,7 +392,7 @@ impl<'b> CodeGenerator<'b> {
Target: crate::targets::CompilationTarget<'a>, Target: crate::targets::CompilationTarget<'a>,
{ {
if let Some(ref mut instr) = target.back_mut() { if let Some(ref mut instr) = target.back_mut() {
if Target::is_void_instr(&*instr) { if Target::is_void_instr(instr) {
Target::incr_void_instr(instr); Target::incr_void_instr(instr);
return; return;
} }
@@ -418,10 +434,10 @@ impl<'b> CodeGenerator<'b> {
.mark_non_var::<Target>(Level::Deep, term_loc, cell, target); .mark_non_var::<Target>(Level::Deep, term_loc, cell, target);
target.push_back(Target::clause_arg_to_instr(cell.get())); target.push_back(Target::clause_arg_to_instr(cell.get()));
} }
&Term::Literal(_, ref constant) => { Term::Literal(_, ref constant) => {
target.push_back(Target::constant_subterm(constant.clone())); target.push_back(Target::constant_subterm(*constant));
} }
&Term::Var(ref cell, ref var_ptr) => { Term::Var(ref cell, ref var_ptr) => {
self.deep_var_instr::<Target>( self.deep_var_instr::<Target>(
cell, cell,
var_ptr.to_var_num().unwrap(), var_ptr.to_var_num().unwrap(),
@@ -509,7 +525,7 @@ impl<'b> CodeGenerator<'b> {
TermRef::PartialString(lvl, cell, string, tail) => { TermRef::PartialString(lvl, cell, string, tail) => {
self.marker self.marker
.mark_non_var::<Target>(lvl, term_loc, cell, &mut target); .mark_non_var::<Target>(lvl, term_loc, cell, &mut target);
let atom = AtomTable::build_with(&self.atom_tbl, &string); let atom = AtomTable::build_with(self.atom_tbl, string);
target.push_back(Target::to_pstr(lvl, atom, cell.get(), true)); target.push_back(Target::to_pstr(lvl, atom, cell.get(), true));
self.subterm_to_instr::<Target>(tail, term_loc, &mut target); self.subterm_to_instr::<Target>(tail, term_loc, &mut target);
@@ -558,10 +574,10 @@ impl<'b> CodeGenerator<'b> {
} }
} }
fn compile_inlined<'a>( fn compile_inlined(
&mut self, &mut self,
ct: &InlinedClauseType, ct: &InlinedClauseType,
terms: &'a Vec<Term>, terms: &'_ [Term],
term_loc: GenContext, term_loc: GenContext,
code: &mut CodeDeque, code: &mut CodeDeque,
) -> Result<(), CompilationError> { ) -> Result<(), CompilationError> {
@@ -585,13 +601,13 @@ impl<'b> CodeGenerator<'b> {
compare_number_instr!(cmp, at_1, at_2) compare_number_instr!(cmp, at_1, at_2)
} }
&InlinedClauseType::IsAtom(..) => match &terms[0] { InlinedClauseType::IsAtom(..) => match &terms[0] {
&Term::Literal(_, Literal::Char(_)) Term::Literal(_, Literal::Char(_))
| &Term::Literal(_, Literal::Atom(atom!("[]"))) | Term::Literal(_, Literal::Atom(atom!("[]")))
| &Term::Literal(_, Literal::Atom(..)) => { | Term::Literal(_, Literal::Atom(..)) => {
instr!("$succeed") instr!("$succeed")
} }
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1); self.marker.reset_arg(1);
let r = self.marker.mark_non_callable( let r = self.marker.mark_non_callable(
@@ -608,21 +624,21 @@ impl<'b> CodeGenerator<'b> {
instr!("$fail") instr!("$fail")
} }
}, },
&InlinedClauseType::IsAtomic(..) => match &terms[0] { InlinedClauseType::IsAtomic(..) => match &terms[0] {
&Term::AnonVar Term::AnonVar
| &Term::Clause(..) | Term::Clause(..)
| &Term::Cons(..) | Term::Cons(..)
| &Term::PartialString(..) | Term::PartialString(..)
| &Term::CompleteString(..) => { | Term::CompleteString(..) => {
instr!("$fail") instr!("$fail")
} }
&Term::Literal(_, Literal::String(_)) => { Term::Literal(_, Literal::String(_)) => {
instr!("$fail") instr!("$fail")
} }
&Term::Literal(..) => { Term::Literal(..) => {
instr!("$succeed") instr!("$succeed")
} }
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1); self.marker.reset_arg(1);
let r = self.marker.mark_non_callable( let r = self.marker.mark_non_callable(
@@ -636,15 +652,15 @@ impl<'b> CodeGenerator<'b> {
instr!("atomic", r) instr!("atomic", r)
} }
}, },
&InlinedClauseType::IsCompound(..) => match &terms[0] { InlinedClauseType::IsCompound(..) => match &terms[0] {
&Term::Clause(..) Term::Clause(..)
| &Term::Cons(..) | Term::Cons(..)
| &Term::PartialString(..) | Term::PartialString(..)
| &Term::CompleteString(..) | Term::CompleteString(..)
| &Term::Literal(_, Literal::String(..)) => { | Term::Literal(_, Literal::String(..)) => {
instr!("$succeed") instr!("$succeed")
} }
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1); self.marker.reset_arg(1);
let r = self.marker.mark_non_callable( let r = self.marker.mark_non_callable(
@@ -661,11 +677,11 @@ impl<'b> CodeGenerator<'b> {
instr!("$fail") instr!("$fail")
} }
}, },
&InlinedClauseType::IsRational(..) => match &terms[0] { InlinedClauseType::IsRational(..) => match terms[0] {
&Term::Literal(_, Literal::Rational(_)) => { Term::Literal(_, Literal::Rational(_)) => {
instr!("$succeed") instr!("$succeed")
} }
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1); self.marker.reset_arg(1);
let r = self.marker.mark_non_callable( let r = self.marker.mark_non_callable(
name.to_var_num().unwrap(), name.to_var_num().unwrap(),
@@ -680,11 +696,11 @@ impl<'b> CodeGenerator<'b> {
instr!("$fail") instr!("$fail")
} }
}, },
&InlinedClauseType::IsFloat(..) => match &terms[0] { InlinedClauseType::IsFloat(..) => match terms[0] {
&Term::Literal(_, Literal::Float(_)) => { Term::Literal(_, Literal::Float(_)) => {
instr!("$succeed") instr!("$succeed")
} }
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1); self.marker.reset_arg(1);
let r = self.marker.mark_non_callable( let r = self.marker.mark_non_callable(
@@ -701,14 +717,14 @@ impl<'b> CodeGenerator<'b> {
instr!("$fail") instr!("$fail")
} }
}, },
&InlinedClauseType::IsNumber(..) => match &terms[0] { InlinedClauseType::IsNumber(..) => match terms[0] {
&Term::Literal(_, Literal::Float(_)) Term::Literal(_, Literal::Float(_))
| &Term::Literal(_, Literal::Rational(_)) | Term::Literal(_, Literal::Rational(_))
| &Term::Literal(_, Literal::Integer(_)) | Term::Literal(_, Literal::Integer(_))
| &Term::Literal(_, Literal::Fixnum(_)) => { | Term::Literal(_, Literal::Fixnum(_)) => {
instr!("$succeed") instr!("$succeed")
} }
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1); self.marker.reset_arg(1);
let r = self.marker.mark_non_callable( let r = self.marker.mark_non_callable(
@@ -725,11 +741,11 @@ impl<'b> CodeGenerator<'b> {
instr!("$fail") instr!("$fail")
} }
}, },
&InlinedClauseType::IsNonVar(..) => match &terms[0] { InlinedClauseType::IsNonVar(..) => match terms[0] {
&Term::AnonVar => { Term::AnonVar => {
instr!("$fail") instr!("$fail")
} }
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1); self.marker.reset_arg(1);
let r = self.marker.mark_non_callable( let r = self.marker.mark_non_callable(
@@ -746,11 +762,11 @@ impl<'b> CodeGenerator<'b> {
instr!("$succeed") instr!("$succeed")
} }
}, },
&InlinedClauseType::IsInteger(..) => match &terms[0] { InlinedClauseType::IsInteger(..) => match &terms[0] {
&Term::Literal(_, Literal::Integer(_)) | &Term::Literal(_, Literal::Fixnum(_)) => { Term::Literal(_, Literal::Integer(_)) | Term::Literal(_, Literal::Fixnum(_)) => {
instr!("$succeed") instr!("$succeed")
} }
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, name) => {
self.marker.reset_arg(1); self.marker.reset_arg(1);
let r = self.marker.mark_non_callable( let r = self.marker.mark_non_callable(
@@ -767,18 +783,18 @@ impl<'b> CodeGenerator<'b> {
instr!("$fail") instr!("$fail")
} }
}, },
&InlinedClauseType::IsVar(..) => match &terms[0] { InlinedClauseType::IsVar(..) => match terms[0] {
&Term::Literal(..) Term::Literal(..)
| &Term::Clause(..) | Term::Clause(..)
| &Term::Cons(..) | Term::Cons(..)
| &Term::PartialString(..) | Term::PartialString(..)
| &Term::CompleteString(..) => { | Term::CompleteString(..) => {
instr!("$fail") instr!("$fail")
} }
&Term::AnonVar => { Term::AnonVar => {
instr!("$succeed") instr!("$succeed")
} }
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
self.marker.reset_arg(1); self.marker.reset_arg(1);
let r = self.marker.mark_non_callable( let r = self.marker.mark_non_callable(
@@ -813,7 +829,7 @@ impl<'b> CodeGenerator<'b> {
fn compile_is_call( fn compile_is_call(
&mut self, &mut self,
terms: &Vec<Term>, terms: &[Term],
code: &mut CodeDeque, code: &mut CodeDeque,
term_loc: GenContext, term_loc: GenContext,
call_policy: CallPolicy, call_policy: CallPolicy,
@@ -828,8 +844,8 @@ impl<'b> CodeGenerator<'b> {
self.marker.reset_arg(2); self.marker.reset_arg(2);
let at = match &terms[0] { let at = match terms[0] {
&Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
let var_num = name.to_var_num().unwrap(); let var_num = name.to_var_num().unwrap();
if self.marker.var_data.records[var_num].num_occurrences > 1 { if self.marker.var_data.records[var_num].num_occurrences > 1 {
@@ -844,6 +860,9 @@ impl<'b> CodeGenerator<'b> {
self.marker.mark_safe_var_unconditionally(var_num); self.marker.mark_safe_var_unconditionally(var_num);
compile_expr!(self, &terms[1], term_loc, code) compile_expr!(self, &terms[1], term_loc, code)
} else { } else {
self.marker
.mark_anon_var::<QueryInstruction>(Level::Shallow, term_loc, code);
if let Term::Var(ref vr, ref var) = &terms[1] { if let Term::Var(ref vr, ref var) = &terms[1] {
let var_num = var.to_var_num().unwrap(); let var_num = var.to_var_num().unwrap();
@@ -871,7 +890,7 @@ impl<'b> CodeGenerator<'b> {
compile_expr!(self, &terms[1], term_loc, code) compile_expr!(self, &terms[1], term_loc, code)
} }
} }
&Term::Literal( Term::Literal(
_, _,
c @ Literal::Integer(_) c @ Literal::Integer(_)
| c @ Literal::Float(_) | c @ Literal::Float(_)
@@ -896,7 +915,7 @@ impl<'b> CodeGenerator<'b> {
Ok(()) Ok(())
} }
fn compile_seq<'a>( fn compile_seq(
&mut self, &mut self,
clauses: &ChunkedTermVec, clauses: &ChunkedTermVec,
code: &mut CodeDeque, code: &mut CodeDeque,
@@ -1066,7 +1085,7 @@ impl<'b> CodeGenerator<'b> {
self.marker.reset_at_head(args); self.marker.reset_at_head(args);
let iter = FactIterator::from_rule_head_clause(&args); let iter = FactIterator::from_rule_head_clause(args);
let fact = self.compile_target::<FactInstruction, _>(iter, GenContext::Head); let fact = self.compile_target::<FactInstruction, _>(iter, GenContext::Head);
if self.marker.max_reg_allocated() > MAX_ARITY { if self.marker.max_reg_allocated() > MAX_ARITY {
@@ -1074,7 +1093,7 @@ impl<'b> CodeGenerator<'b> {
} }
self.marker.reset_free_list(); self.marker.reset_free_list();
code.extend(fact.into_iter()); code.extend(fact);
self.compile_seq(clauses, &mut code)?; self.compile_seq(clauses, &mut code)?;
@@ -1099,7 +1118,7 @@ impl<'b> CodeGenerator<'b> {
return Err(CompilationError::ExceededMaxArity); return Err(CompilationError::ExceededMaxArity);
} }
code.extend(compiled_fact.into_iter()); code.extend(compiled_fact);
} }
code.push(instr!("proceed")); code.push(instr!("proceed"));
@@ -1112,7 +1131,7 @@ impl<'b> CodeGenerator<'b> {
let iter = QueryIterator::new(term); let iter = QueryIterator::new(term);
let query = self.compile_target::<QueryInstruction, _>(iter, term_loc); let query = self.compile_target::<QueryInstruction, _>(iter, term_loc);
code.extend(query.into_iter()); code.extend(query);
match term { match term {
&QueryTerm::Clause(_, ref ct, _, call_policy) => { &QueryTerm::Clause(_, ref ct, _, call_policy) => {
@@ -1204,12 +1223,12 @@ impl<'b> CodeGenerator<'b> {
let clause_code = match clause { let clause_code = match clause {
PredicateClause::Fact(fact, var_data) => { PredicateClause::Fact(fact, var_data) => {
let var_data = std::mem::replace(var_data, VarData::default()); let var_data = std::mem::take(var_data);
self.compile_fact(&fact, var_data)? self.compile_fact(fact, var_data)?
} }
PredicateClause::Rule(rule, var_data) => { PredicateClause::Rule(rule, var_data) => {
let var_data = std::mem::replace(var_data, VarData::default()); let var_data = std::mem::take(var_data);
self.compile_rule(&rule, var_data)? self.compile_rule(rule, var_data)?
} }
}; };
@@ -1237,9 +1256,7 @@ impl<'b> CodeGenerator<'b> {
skip_stub_try_me_else = !self.settings.is_dynamic(); skip_stub_try_me_else = !self.settings.is_dynamic();
} }
let arg = clause let arg = clause.args().and_then(|args| args.get(optimal_index));
.args()
.and_then(|args| args.iter().nth(optimal_index));
if let Some(arg) = arg { if let Some(arg) = arg {
let index = code.len(); let index = code.len();

View File

@@ -39,6 +39,19 @@ impl BranchOccurrences {
subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()), subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
} }
} }
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
debug_assert!(self.current_branch < self.num_branches);
let num_branches = self.num_branches;
let entry = self
.hits
.entry(var_num)
.or_insert_with(|| BitVec::repeat(false, num_branches));
entry.set(self.current_branch, true);
self.subsumed_hits.insert(var_num);
}
} }
#[derive(Debug)] #[derive(Debug)]
@@ -92,17 +105,7 @@ impl BranchStack {
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) { pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
if let Some(occurrences) = self.last_mut() { if let Some(occurrences) = self.last_mut() {
debug_assert!(occurrences.current_branch < occurrences.num_branches); occurrences.add_branch_occurrence(var_num);
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);
} }
} }
@@ -166,30 +169,26 @@ impl DebrayAllocator {
for var_num in subsumed_hits { for var_num in subsumed_hits {
match &mut self.var_data.records[var_num].allocation { match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, ref mut allocation) => { VarAlloc::Perm(_, ref mut allocation) => {
match allocation { if let PermVarAllocation::Done {
PermVarAllocation::Done { shallow_safety,
shallow_safety, deep_safety,
deep_safety, ..
.. } = allocation
} => { {
if !self if !self
.branch_stack .branch_stack
.safety_unneeded_in_branch(shallow_safety, &branch_designator) .safety_unneeded_in_branch(shallow_safety, &branch_designator)
{ {
let branch_occurrences = self.branch_stack.last_mut().unwrap(); let branch_occurrences = self.branch_stack.last_mut().unwrap();
branch_occurrences.shallow_safety.insert(var_num); 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!(); 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);
} }
} }
@@ -295,11 +294,9 @@ impl DebrayAllocator {
let mut result = 0; let mut result = 0;
for reg in self.temp_lb.. { for reg in self.temp_lb.. {
if !self.is_in_use(reg) { if !self.is_in_use(reg) && !temp_var_data.no_use_set.contains(reg) {
if !temp_var_data.no_use_set.contains(reg) { result = reg;
result = reg; break;
break;
}
} }
} }
@@ -321,13 +318,12 @@ impl DebrayAllocator {
let mut result = 0; let mut result = 0;
for reg in self.temp_lb.. { for reg in self.temp_lb.. {
if !self.is_in_use(reg) { if !self.is_in_use(reg)
if !temp_var_data.no_use_set.contains(reg) { && !temp_var_data.no_use_set.contains(reg)
if !temp_var_data.conflict_set.contains(reg) { && !temp_var_data.conflict_set.contains(reg)
result = reg; {
break; result = reg;
} break;
}
} }
} }
@@ -349,16 +345,15 @@ impl DebrayAllocator {
// consider its use set. T == par_k iff // consider its use set. T == par_k iff
// (GenContext::Last(_), k) is in t_var.use_set. // (GenContext::Last(_), k) is in t_var.use_set.
match &self.var_data.records[t_var].allocation { if let VarAlloc::Temp { temp_var_data, .. } =
VarAlloc::Temp { temp_var_data, .. } => { &self.var_data.records[t_var].allocation
if !temp_var_data {
.use_set if !temp_var_data
.contains(&(GenContext::Last(chunk_num), k)) .use_set
{ .contains(&(GenContext::Last(chunk_num), k))
return Some((t_var, self.alloc_with_ca(t_var))); {
} return Some((t_var, self.alloc_with_ca(t_var)));
} }
_ => {}
} }
None None
@@ -372,25 +367,22 @@ impl DebrayAllocator {
chunk_num: usize, chunk_num: usize,
code: &mut CodeDeque, code: &mut CodeDeque,
) { ) {
match self.alloc_in_last_goal_hint(chunk_num) { if let Some((var_num, r)) = self.alloc_in_last_goal_hint(chunk_num) {
Some((var_num, r)) => { let k = self.arg_c;
let k = self.arg_c;
if r != k { if r != k {
let r = RegType::Temp(r); let r = RegType::Temp(r);
code.push_back(Target::move_to_register(r, k)); code.push_back(Target::move_to_register(r, k));
self.shallow_temp_mappings.swap_remove(&k); self.shallow_temp_mappings.swap_remove(&k);
self.shallow_temp_mappings.insert(r.reg_num(), var_num); self.shallow_temp_mappings.insert(r.reg_num(), var_num);
self.var_data.records[var_num] self.var_data.records[var_num]
.allocation .allocation
.set_register(r.reg_num()); .set_register(r.reg_num());
self.in_use.insert(r.reg_num()); self.in_use.insert(r.reg_num());
}
} }
_ => {}
}; };
} }
@@ -493,11 +485,8 @@ impl DebrayAllocator {
} }
fn add_perm_to_free_list(&mut self, chunk_num: usize, var_num: usize) { fn add_perm_to_free_list(&mut self, chunk_num: usize, var_num: usize) {
match &self.var_data.records[var_num].allocation { if let VarAlloc::Perm(..) = &self.var_data.records[var_num].allocation {
VarAlloc::Perm(..) => { self.perm_free_list.push_back((chunk_num, var_num));
self.perm_free_list.push_back((chunk_num, var_num));
}
_ => {}
} }
} }
@@ -521,12 +510,9 @@ impl DebrayAllocator {
} }
pub(crate) fn free_var(&mut self, chunk_num: usize, var_num: usize) { pub(crate) fn free_var(&mut self, chunk_num: usize, var_num: usize) {
match &mut self.var_data.records[var_num].allocation { if let VarAlloc::Perm(_, allocation) = &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, allocation) => { *allocation = PermVarAllocation::Pending;
*allocation = PermVarAllocation::Pending; self.add_perm_to_free_list(chunk_num, var_num);
self.add_perm_to_free_list(chunk_num, var_num);
}
_ => {}
} }
} }
@@ -570,18 +556,15 @@ impl DebrayAllocator {
*shallow_safety = VarSafetyStatus::unneeded(branch_designator); *shallow_safety = VarSafetyStatus::unneeded(branch_designator);
} else if term_loc == GenContext::Head { } else if term_loc == GenContext::Head {
*shallow_safety = VarSafetyStatus::GloballyUnneeded; *shallow_safety = VarSafetyStatus::GloballyUnneeded;
} else { } else if let Some(&temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c) {
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 {
match &mut self.var_data.records[temp_var_num].allocation { ref mut to_perm_var_num,
VarAlloc::Temp { ..
ref mut to_perm_var_num, } => {
.. *to_perm_var_num = Some(var_num);
} => {
*to_perm_var_num = Some(var_num);
}
_ => unreachable!(),
} }
_ => unreachable!(),
} }
} }
} }
@@ -621,16 +604,9 @@ impl DebrayAllocator {
Target::unsafe_argument_to_value(r, arg_c) Target::unsafe_argument_to_value(r, arg_c)
} }
} }
VarAlloc::Temp { ref mut safety, .. } => { VarAlloc::Temp { .. } => {
if self debug_assert!(matches!(r, RegType::Temp(_)));
.branch_stack Target::argument_to_value(r, arg_c)
.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!() unreachable!()
@@ -756,7 +732,7 @@ impl Allocator for DebrayAllocator {
&mut self, &mut self,
var_num: usize, var_num: usize,
lvl: Level, lvl: Level,
cell: &'a Cell<VarReg>, cell: &Cell<VarReg>,
term_loc: GenContext, term_loc: GenContext,
code: &mut CodeDeque, code: &mut CodeDeque,
) { ) {
@@ -764,11 +740,11 @@ impl Allocator for DebrayAllocator {
RegType::Temp(0) => { RegType::Temp(0) => {
let o = self.alloc_reg_to_var::<Target>(var_num, lvl, term_loc, code); let o = self.alloc_reg_to_var::<Target>(var_num, lvl, term_loc, code);
cell.set(VarReg::Norm(RegType::Temp(o))); cell.set(VarReg::Norm(RegType::Temp(o)));
(RegType::Temp(o), true) (RegType::Temp(o), true)
} }
RegType::Perm(0) => { RegType::Perm(0) => {
let p = self.alloc_perm_var(var_num, term_loc.chunk_num()); let p = self.alloc_perm_var(var_num, term_loc.chunk_num());
cell.set(VarReg::Norm(RegType::Perm(p)));
(RegType::Perm(p), true) (RegType::Perm(p), true)
} }
r @ RegType::Perm(_) => { r @ RegType::Perm(_) => {
@@ -796,7 +772,7 @@ impl Allocator for DebrayAllocator {
&mut self, &mut self,
var_num: usize, var_num: usize,
lvl: Level, lvl: Level,
cell: &'a Cell<VarReg>, cell: &Cell<VarReg>,
term_loc: GenContext, term_loc: GenContext,
code: &mut CodeDeque, code: &mut CodeDeque,
r: RegType, r: RegType,
@@ -862,8 +838,21 @@ impl Allocator for DebrayAllocator {
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType { fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType {
match self.get_binding(var_num) { match self.get_binding(var_num) {
RegType::Perm(0) | RegType::Temp(0) => { RegType::Perm(0) => RegType::Perm(self.alloc_perm_var(var_num, chunk_num)),
RegType::Perm(self.alloc_perm_var(var_num, chunk_num)) RegType::Temp(0) => {
let t = self.alloc_reg_to_non_var();
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Temp {
temp_reg, safety, ..
} => {
*temp_reg = t;
*safety = VarSafetyStatus::GloballyUnneeded;
}
_ => unreachable!(),
};
RegType::Temp(t)
} }
r => r, r => r,
} }
@@ -886,12 +875,12 @@ impl Allocator for DebrayAllocator {
self.arg_c += 1; self.arg_c += 1;
} }
fn reset_at_head(&mut self, args: &Vec<Term>) { fn reset_at_head(&mut self, args: &[Term]) {
self.reset_arg(args.len()); self.reset_arg(args.len());
self.arity = args.len(); self.arity = args.len();
for (idx, arg) in args.iter().enumerate() { for (idx, arg) in args.iter().enumerate() {
if let &Term::Var(_, ref var) = arg { if let Term::Var(_, ref var) = arg {
let var_num = var.to_var_num().unwrap(); let var_num = var.to_var_num().unwrap();
let r = self.get_binding(var_num); let r = self.get_binding(var_num);

View File

@@ -27,8 +27,10 @@ use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::error::Error; use std::error::Error;
use std::ffi::{c_void, CString}; use std::ffi::{c_void, CString};
use std::ptr::addr_of_mut;
use libffi::low::{ffi_abi_FFI_DEFAULT_ABI, ffi_cif, ffi_type, prep_cif, type_tag, types, CodePtr}; use libffi::low::type_tag::STRUCT;
use libffi::low::{ffi_abi_FFI_DEFAULT_ABI, ffi_cif, ffi_type, prep_cif, types, CodePtr};
use libloading::{Library, Symbol}; use libloading::{Library, Symbol};
pub struct FunctionDefinition { pub struct FunctionDefinition {
@@ -69,11 +71,13 @@ impl ForeignFunctionTable {
} }
pub fn define_struct(&mut self, name: &str, atom_fields: Vec<Atom>) { 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(); let mut fields: Vec<_> = atom_fields.iter().map(|x| self.map_type_ffi(x)).collect();
fields.push(std::ptr::null_mut::<ffi_type>()); fields.push(std::ptr::null_mut::<ffi_type>());
let mut struct_type: ffi_type = Default::default(); let struct_type = ffi_type {
struct_type.type_ = type_tag::STRUCT; type_: STRUCT,
struct_type.elements = fields.as_mut_ptr(); elements: fields.as_mut_ptr(),
..Default::default()
};
self.structs.insert( self.structs.insert(
name.to_string(), name.to_string(),
StructImpl { StructImpl {
@@ -87,20 +91,20 @@ impl ForeignFunctionTable {
fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type { fn map_type_ffi(&mut self, source: &Atom) -> *mut ffi_type {
unsafe { unsafe {
match source { match source {
atom!("sint64") => &mut types::sint64, atom!("sint64") => addr_of_mut!(types::sint64),
atom!("sint32") => &mut types::sint32, atom!("sint32") => addr_of_mut!(types::sint32),
atom!("sint16") => &mut types::sint16, atom!("sint16") => addr_of_mut!(types::sint16),
atom!("sint8") => &mut types::sint8, atom!("sint8") => addr_of_mut!(types::sint8),
atom!("uint64") => &mut types::uint64, atom!("uint64") => addr_of_mut!(types::uint64),
atom!("uint32") => &mut types::uint32, atom!("uint32") => addr_of_mut!(types::uint32),
atom!("uint16") => &mut types::uint16, atom!("uint16") => addr_of_mut!(types::uint16),
atom!("uint8") => &mut types::uint8, atom!("uint8") => addr_of_mut!(types::uint8),
atom!("bool") => &mut types::sint8, atom!("bool") => addr_of_mut!(types::sint8),
atom!("void") => &mut types::void, atom!("void") => addr_of_mut!(types::void),
atom!("cstr") => &mut types::pointer, atom!("cstr") => addr_of_mut!(types::pointer),
atom!("ptr") => &mut types::pointer, atom!("ptr") => addr_of_mut!(types::pointer),
atom!("f32") => &mut types::float, atom!("f32") => addr_of_mut!(types::float),
atom!("f64") => &mut types::double, atom!("f64") => addr_of_mut!(types::double),
struct_name => match self.structs.get_mut(&*struct_name.as_str()) { struct_name => match self.structs.get_mut(&*struct_name.as_str()) {
Some(ref mut struct_type) => &mut struct_type.ffi_type, Some(ref mut struct_type) => &mut struct_type.ffi_type,
None => unreachable!(), None => unreachable!(),
@@ -121,11 +125,7 @@ impl ForeignFunctionTable {
let symbol_name: CString = CString::new(function.name.clone())?; let symbol_name: CString = CString::new(function.name.clone())?;
let code_ptr: Symbol<*mut c_void> = let code_ptr: Symbol<*mut c_void> =
library.get(&symbol_name.into_bytes_with_nul())?; library.get(&symbol_name.into_bytes_with_nul())?;
let mut args: Vec<_> = function let mut args: Vec<_> = function.args.iter().map(|x| self.map_type_ffi(x)).collect();
.args
.iter()
.map(|x| self.map_type_ffi(&x))
.collect();
let mut cif: ffi_cif = Default::default(); let mut cif: ffi_cif = Default::default();
prep_cif( prep_cif(
&mut cif, &mut cif,
@@ -162,8 +162,8 @@ impl ForeignFunctionTable {
} }
fn build_pointer_args( fn build_pointer_args(
args: &mut Vec<Value>, args: &mut [Value],
type_args: &Vec<*mut ffi_type>, type_args: &[*mut ffi_type],
structs_table: &mut HashMap<String, StructImpl>, structs_table: &mut HashMap<String, StructImpl>,
) -> Result<PointerArgs, FFIError> { ) -> Result<PointerArgs, FFIError> {
let mut pointers = Vec::with_capacity(args.len()); let mut pointers = Vec::with_capacity(args.len());
@@ -237,6 +237,7 @@ impl ForeignFunctionTable {
let ptr = alloc(layout) as *mut c_void; let ptr = alloc(layout) as *mut c_void;
let mut field_ptr = ptr; let mut field_ptr = ptr;
#[allow(clippy::needless_range_loop)]
for i in 0..(struct_type.fields.len() - 1) { for i in 0..(struct_type.fields.len() - 1) {
macro_rules! try_write_int { macro_rules! try_write_int {
($type:ty) => {{ ($type:ty) => {{
@@ -283,7 +284,7 @@ impl ForeignFunctionTable {
std::ptr::copy( std::ptr::copy(
&*struct_ptr as *const _ as *const c_void, &*struct_ptr as *const _ as *const c_void,
field_ptr as *mut c_void, field_ptr,
struct_size, struct_size,
); );
field_ptr = field_ptr.add(struct_size); field_ptr = field_ptr.add(struct_size);
@@ -293,12 +294,13 @@ impl ForeignFunctionTable {
} }
} }
} }
return Ok((Box::from_raw(ptr), size, align)); #[allow(clippy::from_raw_with_void_ptr)]
Ok((Box::from_raw(ptr), size, align))
} else { } else {
return Err(FFIError::InvalidStructName); Err(FFIError::InvalidStructName)
} }
} }
_ => return Err(FFIError::ValueCast), _ => Err(FFIError::ValueCast),
} }
} }
} }
@@ -336,7 +338,7 @@ impl ForeignFunctionTable {
&mut function_impl.cif, &mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()), Some(*function_impl.code_ptr.as_safe_fun()),
&mut *n as *mut _ as *mut c_void, &mut *n as *mut _ as *mut c_void,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void, pointer_args.pointers.as_mut_ptr(),
); );
Ok(Value::Int( Ok(Value::Int(
i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?, i64::try_from(*n).map_err(|_| FFIError::ValueDontFit)?,
@@ -350,7 +352,7 @@ impl ForeignFunctionTable {
&mut function_impl.cif, &mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()), Some(*function_impl.code_ptr.as_safe_fun()),
&mut *n as *mut _ as *mut c_void, &mut *n as *mut _ as *mut c_void,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void, pointer_args.pointers.as_mut_ptr(),
); );
Ok(Value::Float((*n).into())) Ok(Value::Float((*n).into()))
} }
@@ -360,7 +362,7 @@ impl ForeignFunctionTable {
&mut function_impl.cif, &mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()), Some(*function_impl.code_ptr.as_safe_fun()),
&mut *n as *mut _ as *mut c_void, &mut *n as *mut _ as *mut c_void,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void, pointer_args.pointers.as_mut_ptr(),
); );
Ok(Value::Float(*n)) Ok(Value::Float(*n))
} }
@@ -380,10 +382,11 @@ impl ForeignFunctionTable {
libffi::raw::ffi_call( libffi::raw::ffi_call(
&mut function_impl.cif, &mut function_impl.cif,
Some(*function_impl.code_ptr.as_safe_fun()), Some(*function_impl.code_ptr.as_safe_fun()),
&mut *ptr as *mut _ as *mut c_void, &mut *ptr as *mut _,
pointer_args.pointers.as_mut_ptr() as *mut *mut c_void, pointer_args.pointers.as_mut_ptr(),
); );
let struct_val = self.read_struct(ptr, name, struct_type); let struct_val = self.read_struct(ptr, name, struct_type);
#[allow(clippy::from_raw_with_void_ptr)]
drop(Box::from_raw(ptr)); drop(Box::from_raw(ptr));
struct_val struct_val
} }
@@ -441,7 +444,7 @@ impl ForeignFunctionTable {
.ok_or(FFIError::StructNotFound)?; .ok_or(FFIError::StructNotFound)?;
field_ptr = field_ptr field_ptr = field_ptr
.add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize)); .add(field_ptr.align_offset(struct_type.ffi_type.alignment as usize));
let struct_val = self.read_struct(field_ptr, &*substruct, struct_type); let struct_val = self.read_struct(field_ptr, &substruct, struct_type);
returns.push(struct_val?); returns.push(struct_val?);
field_ptr = field_ptr.add(struct_type.ffi_type.size); field_ptr = field_ptr.add(struct_type.ffi_type.size);
} }

View File

@@ -11,6 +11,7 @@ use crate::parser::dashu::{Integer, Rational};
use crate::parser::parser::CompositeOpDesc; use crate::parser::parser::CompositeOpDesc;
use crate::types::*; use crate::types::*;
use dashu::base::Signed;
use fxhash::FxBuildHasher; use fxhash::FxBuildHasher;
use indexmap::{IndexMap, IndexSet}; use indexmap::{IndexMap, IndexSet};
@@ -99,11 +100,7 @@ pub enum RootIterationPolicy {
impl RootIterationPolicy { impl RootIterationPolicy {
#[inline(always)] #[inline(always)]
pub fn iterable(&self) -> bool { pub fn iterable(&self) -> bool {
if let RootIterationPolicy::Iterated = self { matches!(self, RootIterationPolicy::Iterated)
true
} else {
false
}
} }
} }
@@ -151,6 +148,7 @@ impl DerefMut for ChunkedTermVec {
} }
impl ChunkedTermVec { impl ChunkedTermVec {
#[allow(clippy::new_without_default)]
#[inline] #[inline]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
@@ -202,8 +200,8 @@ pub enum QueryTerm {
// register, clause type, subterms, clause call policy. // register, clause type, subterms, clause call policy.
Clause(Cell<RegType>, ClauseType, Vec<Term>, CallPolicy), Clause(Cell<RegType>, ClauseType, Vec<Term>, CallPolicy),
Fail, Fail,
LocalCut { var_num: usize, cut_prev: bool }, // var_num LocalCut { var_num: usize, cut_prev: bool }, // var_num
GlobalCut(usize), // var_num GlobalCut(usize), // var_num
GetCutPoint { var_num: usize, prev_b: bool }, GetCutPoint { var_num: usize, prev_b: bool },
GetLevel(usize), // var_num GetLevel(usize), // var_num
} }
@@ -211,7 +209,7 @@ pub enum QueryTerm {
impl QueryTerm { impl QueryTerm {
pub(crate) fn arity(&self) -> usize { pub(crate) fn arity(&self) -> usize {
match self { match self {
&QueryTerm::Clause(_, _, ref subterms, ..) => subterms.len(), QueryTerm::Clause(_, _, subterms, ..) => subterms.len(),
&QueryTerm::GetLevel(_) | &QueryTerm::GetCutPoint { .. } => 1, &QueryTerm::GetLevel(_) | &QueryTerm::GetCutPoint { .. } => 1,
_ => 0, _ => 0,
} }
@@ -316,15 +314,15 @@ impl ClauseInfo for Rule {
impl ClauseInfo for PredicateClause { impl ClauseInfo for PredicateClause {
fn name(&self) -> Option<Atom> { fn name(&self) -> Option<Atom> {
match self { match self {
&PredicateClause::Fact(ref term, ..) => term.head.name(), PredicateClause::Fact(ref term, ..) => term.head.name(),
&PredicateClause::Rule(ref rule, ..) => rule.name(), PredicateClause::Rule(ref rule, ..) => rule.name(),
} }
} }
fn arity(&self) -> usize { fn arity(&self) -> usize {
match self { match self {
&PredicateClause::Fact(ref term, ..) => term.head.arity(), PredicateClause::Fact(ref term, ..) => term.head.arity(),
&PredicateClause::Rule(ref rule, ..) => rule.arity(), PredicateClause::Rule(ref rule, ..) => rule.arity(),
} }
} }
} }
@@ -339,7 +337,7 @@ impl PredicateClause {
pub(crate) fn args(&self) -> Option<&[Term]> { pub(crate) fn args(&self) -> Option<&[Term]> {
match self { match self {
PredicateClause::Fact(term, ..) => match &term.head { PredicateClause::Fact(term, ..) => match &term.head {
Term::Clause(_, _, args) => Some(&args), Term::Clause(_, _, args) => Some(args),
_ => None, _ => None,
}, },
PredicateClause::Rule(rule, ..) => { PredicateClause::Rule(rule, ..) => {
@@ -433,13 +431,10 @@ impl OpDecl {
pub(crate) fn insert_into_op_dir(&self, op_dir: &mut OpDir) -> Option<OpDesc> { pub(crate) fn insert_into_op_dir(&self, op_dir: &mut OpDir) -> Option<OpDesc> {
let key = (self.name, fixity(self.op_desc.get_spec() as u32)); let key = (self.name, fixity(self.op_desc.get_spec() as u32));
match op_dir.get_mut(&key) { if let Some(cell) = op_dir.get_mut(&key) {
Some(cell) => { let (old_prec, old_spec) = cell.get();
let (old_prec, old_spec) = cell.get(); cell.set(self.op_desc.get_prec(), self.op_desc.get_spec());
cell.set(self.op_desc.get_prec(), self.op_desc.get_spec()); return Some(OpDesc::build_with(old_prec, old_spec));
return Some(OpDesc::build_with(old_prec, old_spec));
}
None => {}
} }
op_dir.insert(key, self.op_desc) op_dir.insert(key, self.op_desc)
@@ -450,7 +445,7 @@ impl OpDecl {
existing_desc: Option<CompositeOpDesc>, existing_desc: Option<CompositeOpDesc>,
op_dir: &mut OpDir, op_dir: &mut OpDir,
) -> Result<(), SessionError> { ) -> Result<(), SessionError> {
let (spec, name) = (self.op_desc.get_spec(), self.name.clone()); let (spec, name) = (self.op_desc.get_spec(), self.name);
if is_infix!(spec as u32) { if is_infix!(spec as u32) {
if let Some(desc) = existing_desc { if let Some(desc) = existing_desc {
@@ -484,7 +479,7 @@ impl AtomOrString {
pub fn as_atom(&self, atom_tbl: &AtomTable) -> Atom { pub fn as_atom(&self, atom_tbl: &AtomTable) -> Atom {
match self { match self {
&AtomOrString::Atom(atom) => atom, &AtomOrString::Atom(atom) => atom,
AtomOrString::String(string) => AtomTable::build_with(atom_tbl, &string), AtomOrString::String(string) => AtomTable::build_with(atom_tbl, string),
} }
} }
@@ -496,10 +491,11 @@ impl AtomOrString {
AtomOrString::String(string) => AtomString::Static(string.as_str()), AtomOrString::String(string) => AtomString::Static(string.as_str()),
} }
} }
}
#[inline] impl From<AtomOrString> for String {
pub fn to_string(self) -> String { fn from(val: AtomOrString) -> Self {
match self { match val {
AtomOrString::Atom(atom) => atom.as_str().to_owned(), AtomOrString::Atom(atom) => atom.as_str().to_owned(),
AtomOrString::String(string) => string, AtomOrString::String(string) => string,
} }
@@ -543,7 +539,7 @@ pub(crate) fn fetch_op_spec(name: Atom, arity: usize, op_dir: &OpDir) -> Option<
} }
}), }),
1 => { 1 => {
if let Some(op_desc) = op_dir.get(&(name.clone(), Fixity::Pre)) { if let Some(op_desc) = op_dir.get(&(name, Fixity::Pre)) {
if op_desc.get_prec() > 0 { if op_desc.get_prec() > 0 {
return Some(*op_desc); return Some(*op_desc);
} }
@@ -744,11 +740,15 @@ impl ArenaFrom<Number> for HeapCellValue {
impl Number { impl Number {
pub(crate) fn sign(&self) -> Number { pub(crate) fn sign(&self) -> Number {
match self { match self {
&Number::Float(f) if f == 0.0 => Number::Float(OrderedFloat(0f64)), Number::Float(f) if *f == 0.0 => Number::Float(OrderedFloat(0f64)),
&Number::Float(f) => Number::Float(OrderedFloat(f.signum())), Number::Float(f) => Number::Float(OrderedFloat(f.signum())),
_ => { _ => {
if self.is_positive() { if self.is_positive() {
Number::Fixnum(Fixnum::build_with(1)) if self.is_zero() {
Number::Fixnum(Fixnum::build_with(0))
} else {
Number::Fixnum(Fixnum::build_with(1))
}
} else if self.is_negative() { } else if self.is_negative() {
Number::Fixnum(Fixnum::build_with(-1)) Number::Fixnum(Fixnum::build_with(-1))
} else { } else {
@@ -761,39 +761,36 @@ impl Number {
#[inline] #[inline]
pub(crate) fn is_positive(&self) -> bool { pub(crate) fn is_positive(&self) -> bool {
match self { match self {
&Number::Fixnum(n) => n.get_num() > 0, Number::Fixnum(n) => n.get_num() > 0,
&Number::Integer(ref n) => &**n > &Integer::from(0), Number::Integer(ref n) => n.is_positive(),
&Number::Float(f) => f.is_sign_positive(), Number::Float(f) => f.is_sign_positive(),
&Number::Rational(ref r) => &**r > &Rational::from(0), Number::Rational(ref r) => r.is_positive(),
} }
} }
#[inline] #[inline]
pub(crate) fn is_negative(&self) -> bool { pub(crate) fn is_negative(&self) -> bool {
match self { match self {
&Number::Fixnum(n) => n.get_num() < 0, Number::Fixnum(n) => n.get_num() < 0,
&Number::Integer(ref n) => &**n < &Integer::from(0), Number::Integer(ref n) => n.is_negative(),
&Number::Float(OrderedFloat(f)) => f.is_sign_negative() && OrderedFloat(f) != -0f64, &Number::Float(OrderedFloat(f)) => f.is_sign_negative() && f != -0f64,
&Number::Rational(ref r) => &**r < &Rational::from(0), Number::Rational(ref r) => r.is_negative(),
} }
} }
#[inline] #[inline]
pub(crate) fn is_zero(&self) -> bool { pub(crate) fn is_zero(&self) -> bool {
match self { match self {
&Number::Fixnum(n) => n.get_num() == 0, Number::Fixnum(n) => n.get_num() == 0,
&Number::Integer(ref n) => &**n == &Integer::from(0), Number::Integer(ref n) => n.is_zero(),
&Number::Float(f) => f == OrderedFloat(0f64) || f == OrderedFloat(-0f64), &Number::Float(OrderedFloat(f)) => f == 0.0 || f == -0.0,
&Number::Rational(ref r) => &**r == &Rational::from(0), Number::Rational(ref r) => r.is_zero(),
} }
} }
#[inline] #[inline]
pub(crate) fn is_integer(&self) -> bool { pub(crate) fn is_integer(&self) -> bool {
match self { matches!(self, Number::Fixnum(_) | Number::Integer(_))
Number::Fixnum(_) | Number::Integer(_) => true,
_ => false,
}
} }
} }
@@ -883,7 +880,7 @@ impl ClauseIndexInfo {
} }
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Default)]
pub(crate) struct PredicateInfo { pub(crate) struct PredicateInfo {
pub(crate) is_extensible: bool, pub(crate) is_extensible: bool,
pub(crate) is_discontiguous: bool, pub(crate) is_discontiguous: bool,
@@ -892,19 +889,6 @@ pub(crate) struct PredicateInfo {
pub(crate) has_clauses: bool, pub(crate) has_clauses: bool,
} }
impl Default for PredicateInfo {
#[inline]
fn default() -> Self {
PredicateInfo {
is_extensible: false,
is_discontiguous: false,
is_dynamic: false,
is_multifile: false,
has_clauses: false,
}
}
}
impl PredicateInfo { impl PredicateInfo {
#[inline] #[inline]
pub(crate) fn compile_incrementally(&self) -> bool { pub(crate) fn compile_incrementally(&self) -> bool {
@@ -963,7 +947,7 @@ impl LocalPredicateSkeleton {
#[inline] #[inline]
pub(crate) fn add_retracted_dynamic_clause_info(&mut self, clause_info: ClauseIndexInfo) { pub(crate) fn add_retracted_dynamic_clause_info(&mut self, clause_info: ClauseIndexInfo) {
debug_assert_eq!(self.is_dynamic, true); debug_assert!(self.is_dynamic);
if self.retracted_dynamic_clauses.is_none() { if self.retracted_dynamic_clauses.is_none() {
self.retracted_dynamic_clauses = Some(vec![]); self.retracted_dynamic_clauses = Some(vec![]);
@@ -1008,7 +992,7 @@ impl PredicateSkeleton {
) -> Option<usize> { ) -> Option<usize> {
let search_result = self.core.clause_clause_locs.make_contiguous() let search_result = self.core.clause_clause_locs.make_contiguous()
[0..self.core.clause_assert_margin] [0..self.core.clause_assert_margin]
.binary_search_by(|loc| clause_clause_loc.cmp(&loc)); .binary_search_by(|loc| clause_clause_loc.cmp(loc));
match search_result { match search_result {
Ok(loc) => Some(loc), Ok(loc) => Some(loc),

View File

@@ -8,7 +8,7 @@ use crate::machine::stack::*;
use crate::types::*; use crate::types::*;
use core::marker::PhantomData; use core::marker::PhantomData;
use modular_bitfield::prelude::*; use scryer_modular_bitfield::prelude::*;
use std::ops::Deref; use std::ops::Deref;
use std::vec::Vec; use std::vec::Vec;
@@ -43,7 +43,7 @@ impl<'a> Drop for EagerStackfulPreOrderHeapIter<'a> {
self.start_value.set_mark_bit(true); self.start_value.set_mark_bit(true);
self.iter_stack.push(self.start_value); self.iter_stack.push(self.start_value);
while let Some(_) = self.follow() {} while self.follow().is_some() {}
} }
} }
@@ -87,28 +87,28 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> {
let arity = cell_as_atom_cell!(self.heap[s]).get_arity(); let arity = cell_as_atom_cell!(self.heap[s]).get_arity();
for idx in (s + 1 .. s + arity + 1).rev() { for idx in (s + 1 .. s + arity + 1).rev() {
if self.heap[idx].get_mark_bit() != self.mark_phase { if self.heap[idx].get_mark_bit() != self.mark_phase {
self.iter_stack.push(self.heap[idx]); self.iter_stack.push(self.heap[idx]);
self.heap[idx].set_mark_bit(self.mark_phase); self.heap[idx].set_mark_bit(self.mark_phase);
} }
} }
} }
(HeapCellValueTag::Lis, l) => { (HeapCellValueTag::Lis, l) => {
if self.heap[l+1].get_mark_bit() != self.mark_phase { if self.heap[l+1].get_mark_bit() != self.mark_phase {
self.iter_stack.push(self.heap[l+1]); self.iter_stack.push(self.heap[l+1]);
self.heap[l+1].set_mark_bit(self.mark_phase); self.heap[l+1].set_mark_bit(self.mark_phase);
} }
if self.heap[l].get_mark_bit() != self.mark_phase { if self.heap[l].get_mark_bit() != self.mark_phase {
self.iter_stack.push(self.heap[l]); self.iter_stack.push(self.heap[l]);
self.heap[l].set_mark_bit(self.mark_phase); self.heap[l].set_mark_bit(self.mark_phase);
} }
} }
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
let var_value = self.heap[h]; let var_value = self.heap[h];
self.heap[h].set_mark_bit(self.mark_phase); self.heap[h].set_mark_bit(self.mark_phase);
if !(self.heap[h].is_var() && self.heap[h].get_value() as usize == h) { if var_value.get_mark_bit() || !(self.heap[h].is_var() && self.heap[h].get_value() as usize == h) {
self.iter_stack.push(var_value); self.iter_stack.push(var_value);
continue; continue;
} }
@@ -125,12 +125,13 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> {
continue; continue;
} }
let value = self.heap[h+1];
self.heap[h].set_mark_bit(self.mark_phase); self.heap[h].set_mark_bit(self.mark_phase);
self.heap[h+1].set_mark_bit(self.mark_phase);
self.iter_stack.push(value); if self.heap[h].get_tag() == HeapCellValueTag::PStr {
let value = self.heap[h+1];
self.heap[h+1].set_mark_bit(self.mark_phase);
self.iter_stack.push(value);
}
} }
_ => { _ => {
} }
@@ -270,7 +271,9 @@ pub trait FocusedHeapIter: Iterator<Item = HeapCellValue> {
fn focus(&self) -> IterStackLoc; fn focus(&self) -> IterStackLoc;
} }
impl<'a, ElideLists: ListElisionPolicy> FocusedHeapIter for StackfulPreOrderHeapIter<'a, ElideLists> { impl<'a, ElideLists: ListElisionPolicy> FocusedHeapIter
for StackfulPreOrderHeapIter<'a, ElideLists>
{
#[inline] #[inline]
fn focus(&self) -> IterStackLoc { fn focus(&self) -> IterStackLoc {
self.h self.h
@@ -506,10 +509,10 @@ impl<'a, ElideLists: ListElisionPolicy> Iterator for StackfulPreOrderHeapIter<'a
} }
#[inline(always)] #[inline(always)]
pub(crate) fn cycle_detecting_stackless_preorder_iter<'a>( pub(crate) fn cycle_detecting_stackless_preorder_iter(
heap: &'a mut [HeapCellValue], heap: &'_ mut [HeapCellValue],
start: usize, start: usize,
) -> CycleDetectingIter<'a, true> { ) -> CycleDetectingIter<'_, true> {
// const generics argument of true so that cycle discovery stops // const generics argument of true so that cycle discovery stops
// the iterator. // the iterator.
CycleDetectingIter::new(heap, start) CycleDetectingIter::new(heap, start)
@@ -660,29 +663,30 @@ pub(crate) fn stackful_post_order_iter<'a, ElideLists: ListElisionPolicy>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::machine::gc::IteratorUMP;
use crate::machine::mock_wam::*; use crate::machine::mock_wam::*;
use crate::machine::gc::{IteratorUMP};
pub(crate) type RightistPostOrderHeapIter<'a> = pub(crate) type RightistPostOrderHeapIter<'a> =
PostOrderIterator<StacklessPreOrderHeapIter<'a, IteratorUMP>>; PostOrderIterator<StacklessPreOrderHeapIter<'a, IteratorUMP>>;
#[inline(always)] #[inline(always)]
pub(crate) fn stackless_preorder_iter( pub(crate) fn stackless_preorder_iter(
heap: &mut Vec<HeapCellValue>, heap: &mut [HeapCellValue],
start: usize, start: usize,
) -> StacklessPreOrderHeapIter<IteratorUMP> { ) -> StacklessPreOrderHeapIter<IteratorUMP> {
StacklessPreOrderHeapIter::<IteratorUMP>::new(heap, start) StacklessPreOrderHeapIter::<IteratorUMP>::new(heap, start)
} }
#[inline] #[inline]
pub(crate) fn stackless_post_order_iter<'a>( pub(crate) fn stackless_post_order_iter(
heap: &'a mut Heap, heap: &'_ mut Heap,
start: usize, start: usize,
) -> RightistPostOrderHeapIter<'a> { ) -> RightistPostOrderHeapIter {
PostOrderIterator::new(stackless_preorder_iter(heap, start)) PostOrderIterator::new(stackless_preorder_iter(heap, start))
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "it takes too long to run")]
fn heap_stackless_iter_tests() { fn heap_stackless_iter_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
@@ -954,7 +958,10 @@ mod tests {
let pstr_offset_cell = pstr_offset_as_cell!(0); let pstr_offset_cell = pstr_offset_as_cell!(0);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), fixnum_as_cell!(Fixnum::build_with(2))); assert_eq!(
unmark_cell_bits!(iter.next().unwrap()),
fixnum_as_cell!(Fixnum::build_with(2))
);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
@@ -999,11 +1006,17 @@ mod tests {
let pstr_offset_cell = pstr_offset_as_cell!(0); let pstr_offset_cell = pstr_offset_as_cell!(0);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_loc_as_cell!(4)); assert_eq!(
unmark_cell_bits!(iter.next().unwrap()),
pstr_loc_as_cell!(4)
);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_offset_cell);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), fixnum_as_cell!(Fixnum::build_with(0))); assert_eq!(
unmark_cell_bits!(iter.next().unwrap()),
fixnum_as_cell!(Fixnum::build_with(0))
);
assert_eq!(iter.next(), None); assert_eq!(iter.next(), None);
} }
@@ -1016,7 +1029,10 @@ mod tests {
let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 6); let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 6);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_loc_as_cell!(4)); assert_eq!(
unmark_cell_bits!(iter.next().unwrap()),
pstr_loc_as_cell!(4)
);
assert_eq!( assert_eq!(
unmark_cell_bits!(iter.next().unwrap()), unmark_cell_bits!(iter.next().unwrap()),
@@ -1035,7 +1051,10 @@ mod tests {
} }
assert_eq!(wam.machine_st.heap[4], pstr_offset_as_cell!(0)); assert_eq!(wam.machine_st.heap[4], pstr_offset_as_cell!(0));
assert_eq!(wam.machine_st.heap[5], fixnum_as_cell!(Fixnum::build_with(1i64))); assert_eq!(
wam.machine_st.heap[5],
fixnum_as_cell!(Fixnum::build_with(1i64))
);
all_cells_unmarked(&wam.machine_st.heap); all_cells_unmarked(&wam.machine_st.heap);
@@ -1501,7 +1520,9 @@ mod tests {
wam.machine_st.heap.clear(); wam.machine_st.heap.clear();
{ {
wam.machine_st.heap.push(fixnum_as_cell!(Fixnum::build_with(0))); wam.machine_st
.heap
.push(fixnum_as_cell!(Fixnum::build_with(0)));
let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0); let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 0);
@@ -1536,7 +1557,10 @@ mod tests {
atom_as_cell!(atom!("y")) atom_as_cell!(atom!("y"))
); );
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), heap_loc_as_cell!(0)); assert_eq!(
unmark_cell_bits!(iter.next().unwrap()),
heap_loc_as_cell!(0)
);
assert!(iter.next().is_none()); assert!(iter.next().is_none());
} }
@@ -1685,10 +1709,7 @@ mod tests {
wam.machine_st.heap.push(heap_loc_as_cell!(0)); wam.machine_st.heap.push(heap_loc_as_cell!(0));
{ {
let mut iter = stackless_preorder_iter( let mut iter = stackless_preorder_iter(&mut wam.machine_st.heap, 9);
&mut wam.machine_st.heap,
9,
);
/* /*
while let Some(_) = iter.next() { while let Some(_) = iter.next() {
@@ -1737,6 +1758,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
fn heap_stackful_iter_tests() { fn heap_stackful_iter_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
@@ -2329,6 +2351,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
fn heap_stackful_post_order_iter() { fn heap_stackful_post_order_iter() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
@@ -2812,6 +2835,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
fn heap_stackless_post_order_iter() { fn heap_stackless_post_order_iter() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
@@ -2889,8 +2913,7 @@ mod tests {
{ {
wam.machine_st.heap.push(heap_loc_as_cell!(0)); wam.machine_st.heap.push(heap_loc_as_cell!(0));
let mut iter = let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0);
stackless_post_order_iter(&mut wam.machine_st.heap, 0);
assert_eq!( assert_eq!(
unmark_cell_bits!(iter.next().unwrap()), unmark_cell_bits!(iter.next().unwrap()),
@@ -2931,8 +2954,7 @@ mod tests {
wam.machine_st.heap.push(empty_list_as_cell!()); wam.machine_st.heap.push(empty_list_as_cell!());
{ {
let mut iter = let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0);
stackless_post_order_iter(&mut wam.machine_st.heap, 0);
assert_eq!( assert_eq!(
unmark_cell_bits!(iter.next().unwrap()), unmark_cell_bits!(iter.next().unwrap()),
@@ -2964,8 +2986,7 @@ mod tests {
wam.machine_st.heap.push(heap_loc_as_cell!(0)); wam.machine_st.heap.push(heap_loc_as_cell!(0));
{ {
let mut iter = let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0);
stackless_post_order_iter(&mut wam.machine_st.heap, 0);
// the cycle will be iterated twice before being detected. // the cycle will be iterated twice before being detected.
assert_eq!( assert_eq!(
@@ -2993,8 +3014,7 @@ mod tests {
} }
{ {
let mut iter = let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 0);
stackless_post_order_iter(&mut wam.machine_st.heap, 0);
// cut the iteration short to check that all cells are // cut the iteration short to check that all cells are
// unmarked and unforwarded by the Drop instance of // unmarked and unforwarded by the Drop instance of
@@ -3031,8 +3051,7 @@ mod tests {
wam.machine_st.heap.push(pstr_loc_as_cell!(0)); wam.machine_st.heap.push(pstr_loc_as_cell!(0));
{ {
let mut iter = let mut iter = stackless_post_order_iter(&mut wam.machine_st.heap, 2);
stackless_post_order_iter(&mut wam.machine_st.heap, 2);
assert_eq!( assert_eq!(
unmark_cell_bits!(iter.next().unwrap()), unmark_cell_bits!(iter.next().unwrap()),
@@ -3119,10 +3138,7 @@ mod tests {
unmark_cell_bits!(iter.next().unwrap()), unmark_cell_bits!(iter.next().unwrap()),
heap_loc_as_cell!(3) heap_loc_as_cell!(3)
); );
assert_eq!( assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_second_cell);
unmark_cell_bits!(iter.next().unwrap()),
pstr_second_cell
);
assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell); assert_eq!(unmark_cell_bits!(iter.next().unwrap()), pstr_cell);
assert_eq!(iter.next(), None); assert_eq!(iter.next(), None);
} }

View File

@@ -1,9 +1,9 @@
use crate::arena::*; use crate::arena::*;
use crate::atom_table::*; use crate::atom_table::*;
use crate::parser::ast::*; use crate::parser::ast::*;
use crate::parser::dashu::{ibig, Integer, Rational};
use crate::parser::dashu::base::RemEuclid; use crate::parser::dashu::base::RemEuclid;
use crate::parser::dashu::integer::Sign; use crate::parser::dashu::integer::Sign;
use crate::parser::dashu::{ibig, Integer, Rational};
use crate::{ use crate::{
alpha_numeric_char, capital_letter_char, cut_char, decimal_digit_char, graphic_token_char, alpha_numeric_char, capital_letter_char, cut_char, decimal_digit_char, graphic_token_char,
is_fx, is_infix, is_postfix, is_prefix, is_xf, is_xfx, is_xfy, is_yfx, semicolon_char, is_fx, is_infix, is_postfix, is_prefix, is_xf, is_xfx, is_xfy, is_yfx, semicolon_char,
@@ -20,6 +20,7 @@ use crate::machine::stack::*;
use crate::machine::streams::*; use crate::machine::streams::*;
use crate::types::*; use crate::types::*;
use dashu::base::Signed;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use indexmap::IndexMap; use indexmap::IndexMap;
@@ -258,9 +259,7 @@ pub(crate) fn requires_space(atom: &str, op: &str) -> bool {
oc == '(' || alpha_numeric_char!(oc) oc == '(' || alpha_numeric_char!(oc)
} else if graphic_token_char!(ac) { } else if graphic_token_char!(ac) {
graphic_token_char!(oc) graphic_token_char!(oc)
} else if variable_indicator_char!(ac) { } else if variable_indicator_char!(ac) || capital_letter_char!(ac) {
alpha_numeric_char!(oc)
} else if capital_letter_char!(ac) {
alpha_numeric_char!(oc) alpha_numeric_char!(oc)
} else if sign_char!(ac) { } else if sign_char!(ac) {
sign_char!(oc) || decimal_digit_char!(oc) sign_char!(oc) || decimal_digit_char!(oc)
@@ -277,7 +276,7 @@ pub(crate) fn requires_space(atom: &str, op: &str) -> bool {
fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char) -> bool { fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char) -> bool {
if c == '/' { if c == '/' {
return match iter.next() { match iter.next() {
None => true, None => true,
Some('*') => false, // if we start with comment token, we must quote. Some('*') => false, // if we start with comment token, we must quote.
Some(c) => { Some(c) => {
@@ -287,9 +286,9 @@ fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char
false false
} }
} }
}; }
} else if c == '.' { } else if c == '.' {
return match iter.next() { match iter.next() {
None => false, None => false,
Some(c) => { Some(c) => {
if graphic_token_char!(c) { if graphic_token_char!(c) {
@@ -298,7 +297,7 @@ fn non_quoted_graphic_token<Iter: Iterator<Item = char>>(mut iter: Iter, c: char
false false
} }
} }
}; }
} else { } else {
iter.all(|c| graphic_token_char!(c)) iter.all(|c| graphic_token_char!(c))
} }
@@ -310,9 +309,7 @@ pub(super) fn non_quoted_token<Iter: Iterator<Item = char>>(mut iter: Iter) -> b
iter.all(|c| alpha_numeric_char!(c)) iter.all(|c| alpha_numeric_char!(c))
} else if graphic_token_char!(c) { } else if graphic_token_char!(c) {
non_quoted_graphic_token(iter, c) non_quoted_graphic_token(iter, c)
} else if semicolon_char!(c) { } else if semicolon_char!(c) || cut_char!(c) {
iter.next().is_none()
} else if cut_char!(c) {
iter.next().is_none() iter.next().is_none()
} else if c == '[' { } else if c == '[' {
iter.next() == Some(']') && iter.next().is_none() iter.next() == Some(']') && iter.next().is_none()
@@ -328,6 +325,7 @@ pub(super) fn non_quoted_token<Iter: Iterator<Item = char>>(mut iter: Iter) -> b
} }
} }
#[allow(clippy::len_without_is_empty)]
pub trait HCValueOutputter { pub trait HCValueOutputter {
type Output; type Output;
@@ -370,7 +368,7 @@ impl HCValueOutputter for PrinterOutputter {
} }
fn begin_new_var(&mut self) { fn begin_new_var(&mut self) {
if self.contents.len() != 0 { if !self.contents.is_empty() {
self.contents += ", "; self.contents += ", ";
} }
} }
@@ -415,9 +413,9 @@ fn negated_op_needs_bracketing(
op.is_negative_sign() op.is_negative_sign()
&& iter.leftmost_leaf_has_property(op_dir, |addr| match Number::try_from(addr) { && iter.leftmost_leaf_has_property(op_dir, |addr| match Number::try_from(addr) {
Ok(Number::Fixnum(n)) => n.get_num() > 0, Ok(Number::Fixnum(n)) => n.get_num() > 0,
Ok(Number::Float(f)) => f > OrderedFloat(0f64), Ok(Number::Float(OrderedFloat(f))) => f > 0f64,
Ok(Number::Integer(n)) => &*n > &Integer::from(0), Ok(Number::Integer(n)) => n.is_positive(),
Ok(Number::Rational(n)) => &*n > &Rational::from(0), Ok(Number::Rational(n)) => n.is_positive(),
_ => false, _ => false,
}) })
} else { } else {
@@ -537,20 +535,10 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option<String>
} }
match Number::try_from(addr) { match Number::try_from(addr) {
Ok(Number::Fixnum(n)) => { Ok(Number::Fixnum(n)) if n.get_num() >= 0 => {
if n.get_num() >= 0 { Some(numbervar(offset + Integer::from(n.get_num())))
Some(numbervar(offset + Integer::from(n.get_num())))
} else {
None
}
}
Ok(Number::Integer(n)) => {
if &*n >= &Integer::from(0) {
Some(numbervar(Integer::from(offset + &*n)))
} else {
None
}
} }
Ok(Number::Integer(n)) if !n.is_negative() => Some(numbervar(Integer::from(offset + &*n))),
_ => None, _ => None,
} }
} }
@@ -640,12 +628,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
self.state_stack.push(TokenOrRedirect::Op(name, spec)); self.state_stack.push(TokenOrRedirect::Op(name, spec));
} }
} else { } else {
match &*name.as_str() { if let "|" = &*name.as_str() {
"|" => { self.format_bar_separator_op(max_depth, name, spec);
self.format_bar_separator_op(max_depth, name, spec); return;
return;
}
_ => {}
}; };
if self.max_depth_exhausted(max_depth) { if self.max_depth_exhausted(max_depth) {
@@ -657,22 +642,19 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
if is_xfy!(spec.get_spec()) { if is_xfy!(spec.get_spec()) {
let left_directed_op = DirectedOp::Left(name, spec); let left_directed_op = DirectedOp::Left(name, spec);
self.state_stack.push(TokenOrRedirect::CompositeRedirect( self.state_stack
0, .push(TokenOrRedirect::CompositeRedirect(0, left_directed_op));
left_directed_op,
));
self.state_stack.push(TokenOrRedirect::Op(name, spec)); self.state_stack.push(TokenOrRedirect::Op(name, spec));
self.state_stack.push(TokenOrRedirect::StackPop); self.state_stack.push(TokenOrRedirect::StackPop);
} else { // is_yfx! } else {
// is_yfx!
let right_directed_op = DirectedOp::Right(name, spec); let right_directed_op = DirectedOp::Right(name, spec);
self.state_stack.push(TokenOrRedirect::StackPop); self.state_stack.push(TokenOrRedirect::StackPop);
self.state_stack.push(TokenOrRedirect::Op(name, spec)); self.state_stack.push(TokenOrRedirect::Op(name, spec));
self.state_stack.push(TokenOrRedirect::CompositeRedirect( self.state_stack
0, .push(TokenOrRedirect::CompositeRedirect(0, right_directed_op));
right_directed_op,
));
} }
} else { } else {
let left_directed_op = DirectedOp::Left(name, spec); let left_directed_op = DirectedOp::Left(name, spec);
@@ -783,7 +765,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
let h = self.iter.stack_last().unwrap(); let h = self.iter.stack_last().unwrap();
let cell = self.iter.read_cell(h); let cell = self.iter.read_cell(h);
let cell = heap_bound_store(&self.iter.heap, heap_bound_deref(&self.iter.heap, cell)); let cell = heap_bound_store(self.iter.heap, heap_bound_deref(self.iter.heap, cell));
// 7.10.4 // 7.10.4
if let Some(var) = numbervar(&self.numbervars_offset, cell) { if let Some(var) = numbervar(&self.numbervars_offset, cell) {
@@ -802,20 +784,16 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
name: Atom, name: Atom,
op_desc: Option<OpDesc>, op_desc: Option<OpDesc>,
) -> bool { ) -> bool {
if self.numbervars && is_numbered_var(name, arity) { if self.numbervars && is_numbered_var(name, arity) && self.format_numbered_vars() {
if self.format_numbered_vars() { return true;
return true;
}
} }
let dot_atom = atom!("."); let dot_atom = atom!(".");
if let Some(spec) = op_desc { if let Some(spec) = op_desc {
if dot_atom == name && is_infix!(spec.get_spec()) { if dot_atom == name && is_infix!(spec.get_spec()) && !self.ignore_ops {
if !self.ignore_ops { self.push_list(max_depth);
self.push_list(max_depth); return true;
return true;
}
} }
if !self.ignore_ops && spec.get_prec() > 0 { if !self.ignore_ops && spec.get_prec() > 0 {
@@ -824,10 +802,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
} }
} }
return match (name, arity) { match (name, arity) {
(atom!("{}"), 1) if !self.ignore_ops => self.format_curly_braces(max_depth), (atom!("{}"), 1) if !self.ignore_ops => self.format_curly_braces(max_depth),
_ => self.format_struct(max_depth, arity, name), _ => self.format_struct(max_depth, arity, name),
}; }
} }
fn offset_as_string(&mut self, h: IterStackLoc) -> Option<String> { fn offset_as_string(&mut self, h: IterStackLoc) -> Option<String> {
@@ -866,7 +844,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
loop { loop {
let is_cyclic = orig_cell.get_forwarding_bit(); let is_cyclic = orig_cell.get_forwarding_bit();
let cell = heap_bound_store(self.iter.heap, heap_bound_deref(self.iter.heap, orig_cell)); let cell =
heap_bound_store(self.iter.heap, heap_bound_deref(self.iter.heap, orig_cell));
let cell = unmark_cell_bits!(cell); let cell = unmark_cell_bits!(cell);
match self.var_names.get(&cell).cloned() { match self.var_names.get(&cell).cloned() {
@@ -933,7 +912,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
} }
let h = cell.get_value() as usize; let h = cell.get_value() as usize;
self.iter.push_stack(IterStackLoc::iterable_loc(h, HeapOrStackTag::Heap)); self.iter.push_stack(IterStackLoc::iterable_loc(
h,
HeapOrStackTag::Heap,
));
if let Some(cell) = self.iter.next() { if let Some(cell) = self.iter.next() {
orig_cell = cell; orig_cell = cell;
@@ -951,13 +933,13 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
} }
} }
} else { } else {
while let Some(_) = self.iter.pop_stack() {} while self.iter.pop_stack().is_none() {}
None None
} }
} }
fn print_impromptu_atom(&mut self, atom: Atom) { fn print_impromptu_atom(&mut self, atom: Atom) {
let result = self.print_op_addendum(&*atom.as_str()); let result = self.print_op_addendum(&atom.as_str());
push_space_if_amb!(self, result.as_str(), { push_space_if_amb!(self, result.as_str(), {
append_str!(self, &result); append_str!(self, &result);
@@ -1145,8 +1127,6 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
self.state_stack.push(TokenOrRedirect::Open); self.state_stack.push(TokenOrRedirect::Open);
self.state_stack.push(TokenOrRedirect::Atom(rdiv_ct)); self.state_stack.push(TokenOrRedirect::Atom(rdiv_ct));
} }
return;
} }
_ => { _ => {
unreachable!() unreachable!()
@@ -1242,7 +1222,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus.value() as usize); let mut heap_pstr_iter = HeapPStrIter::new(self.iter.heap, focus.value() as usize);
if heap_pstr_iter.next().is_some() { if heap_pstr_iter.next().is_some() {
while let Some(_) = heap_pstr_iter.next() {} for _ in heap_pstr_iter.by_ref() {}
} else { } else {
return self.push_list(max_depth); return self.push_list(max_depth);
} }
@@ -1258,11 +1238,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
let at_cdr = self.outputter.ends_with("|"); let at_cdr = self.outputter.ends_with("|");
if self.double_quotes { if self.double_quotes && !self.ignore_ops && end_cell.is_string_terminator(self.iter.heap) {
if !self.ignore_ops && end_cell.is_string_terminator(&self.iter.heap) { self.remove_list_children(focus.value() as usize);
self.remove_list_children(focus.value() as usize); return self.print_proper_string(focus.value() as usize, max_depth);
return self.print_proper_string(focus.value() as usize, max_depth);
}
} }
if self.ignore_ops { if self.ignore_ops {
@@ -1275,8 +1253,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
append_str!(self, "[]"); append_str!(self, "[]");
} }
} else { } else {
self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); self.state_stack
self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); .push(TokenOrRedirect::FunctorRedirect(max_depth));
self.iter
.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap));
} }
} }
} else { } else {
@@ -1287,7 +1267,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
read_heap_cell!(value, read_heap_cell!(value,
(HeapCellValueTag::Lis) => { (HeapCellValueTag::Lis) => {
return self.push_list(max_depth); self.push_list(max_depth)
} }
_ => { _ => {
let switch = Rc::new(Cell::new((!at_cdr, 0))); let switch = Rc::new(Cell::new((!at_cdr, 0)));
@@ -1386,13 +1366,16 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
let switch = self.close_list(cell); let switch = self.close_list(cell);
self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth)); self.state_stack
.push(TokenOrRedirect::FunctorRedirect(max_depth));
self.state_stack.push(TokenOrRedirect::HeadTailSeparator); // bar self.state_stack.push(TokenOrRedirect::HeadTailSeparator); // bar
self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth + 1)); self.state_stack
.push(TokenOrRedirect::FunctorRedirect(max_depth + 1));
self.open_list(switch); self.open_list(switch);
} }
#[allow(clippy::too_many_arguments)]
fn handle_op_as_struct( fn handle_op_as_struct(
&mut self, &mut self,
name: Atom, name: Atom,
@@ -1448,7 +1431,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
for op in &[op, parent_op] { for op in &[op, parent_op] {
if let Some(ref op) = &op { if let Some(ref op) = &op {
if op.is_left() if op.is_left()
&& (op.is_prefix() || requires_space(&*op.as_atom().as_str(), "(")) && (op.is_prefix() || requires_space(&op.as_atom().as_str(), "("))
{ {
self.state_stack.push(TokenOrRedirect::Space); self.state_stack.push(TokenOrRedirect::Space);
return; return;
@@ -1461,9 +1444,8 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
} }
} }
#[allow(dead_code)]
fn print_tcp_listener(&mut self, tcp_listener: &TcpListener, max_depth: usize) { fn print_tcp_listener(&mut self, tcp_listener: &TcpListener, max_depth: usize) {
let (ip, port) = if let Some(addr) = tcp_listener.local_addr().ok() { let (ip, port) = if let Ok(addr) = tcp_listener.local_addr() {
(addr.ip(), addr.port()) (addr.ip(), addr.port())
} else { } else {
let disconnected_atom = atom!("$disconnected_tcp_listener"); let disconnected_atom = atom!("$disconnected_tcp_listener");
@@ -1545,24 +1527,33 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
} }
fn print_comma_separated_char_list(&mut self, char_list: CommaSeparatedCharList) { fn print_comma_separated_char_list(&mut self, char_list: CommaSeparatedCharList) {
let CommaSeparatedCharList { pstr, offset, max_depth, end_cell, end_h } = char_list; let CommaSeparatedCharList {
pstr,
offset,
max_depth,
end_cell,
end_h,
} = char_list;
let pstr_str = pstr.as_str_from(offset); let pstr_str = pstr.as_str_from(offset);
if let Some(c) = pstr_str.chars().next() { if let Some(c) = pstr_str.chars().next() {
let offset = offset + c.len_utf8(); let offset = offset + c.len_utf8();
if !self.max_depth_exhausted(max_depth) { if !self.max_depth_exhausted(max_depth) {
self.state_stack.push(TokenOrRedirect::CommaSeparatedCharList(CommaSeparatedCharList { self.state_stack
pstr, .push(TokenOrRedirect::CommaSeparatedCharList(
offset, CommaSeparatedCharList {
max_depth: max_depth.saturating_sub(1), pstr,
end_cell, offset,
end_h, max_depth: max_depth.saturating_sub(1),
})); end_cell,
end_h,
},
));
let max_depth_allows = self.max_depth == 0 || max_depth > 1; let max_depth_allows = self.max_depth == 0 || max_depth > 1;
if max_depth_allows && pstr_str.chars().skip(1).next().is_some() { if max_depth_allows && pstr_str.chars().nth(1).is_some() {
self.state_stack.push(TokenOrRedirect::Comma); self.state_stack.push(TokenOrRedirect::Comma);
} }
@@ -1576,10 +1567,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
self.state_stack.push(TokenOrRedirect::HeadTailSeparator); self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
} else if end_cell != empty_list_as_cell!() { } else if end_cell != empty_list_as_cell!() {
if let Some(end_h) = end_h { if let Some(end_h) = end_h {
self.iter.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap)); self.iter
.push_stack(IterStackLoc::iterable_loc(end_h, HeapOrStackTag::Heap));
} }
self.state_stack.push(TokenOrRedirect::FunctorRedirect(max_depth + 1)); self.state_stack
.push(TokenOrRedirect::FunctorRedirect(max_depth + 1));
self.state_stack.push(TokenOrRedirect::HeadTailSeparator); self.state_stack.push(TokenOrRedirect::HeadTailSeparator);
} }
} }
@@ -1599,13 +1592,12 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
let print_struct = |printer: &mut Self, name: Atom, arity: usize| { let print_struct = |printer: &mut Self, name: Atom, arity: usize| {
if name == atom!("[]") && arity == 0 { if name == atom!("[]") && arity == 0 {
match printer.state_stack.last() { if let Some(TokenOrRedirect::CloseList(_) | TokenOrRedirect::ChildCloseList) =
Some(TokenOrRedirect::CloseList(_) | TokenOrRedirect::ChildCloseList) => { printer.state_stack.last()
if printer.at_cdr("") { {
return; if printer.at_cdr("") {
} return;
} }
_ => {}
} }
append_str!(printer, "[]"); append_str!(printer, "[]");
@@ -1642,7 +1634,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
result.push('('); result.push('(');
} }
result += &printer.print_op_addendum(&*name.as_str()); result += &printer.print_op_addendum(&name.as_str());
if op.is_some() { if op.is_some() {
result.push(')'); result.push(')');
@@ -1652,14 +1644,14 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
append_str!(printer, &result); append_str!(printer, &result);
}); });
} else { } else {
push_space_if_amb!(printer, &*name.as_str(), { push_space_if_amb!(printer, &name.as_str(), {
printer.print_impromptu_atom(name); printer.print_impromptu_atom(name);
}); });
} }
}; };
if !addr.is_var() if !addr.is_var()
&& !addr.is_compound(&self.iter.heap) && !addr.is_compound(self.iter.heap)
&& self.max_depth_exhausted(max_depth) && self.max_depth_exhausted(max_depth)
{ {
if !(addr == atom_as_cell!(atom!("[]")) && self.at_cdr("")) { if !(addr == atom_as_cell!(atom!("[]")) && self.at_cdr("")) {
@@ -1734,6 +1726,9 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
(ArenaHeaderTag::Stream, stream) => { (ArenaHeaderTag::Stream, stream) => {
self.print_stream(stream, max_depth); self.print_stream(stream, max_depth);
} }
(ArenaHeaderTag::TcpListener, listener) => {
self.print_tcp_listener(&listener, max_depth);
}
(ArenaHeaderTag::Dropped, _value) => { (ArenaHeaderTag::Dropped, _value) => {
self.print_impromptu_atom(atom!("$dropped_value")); self.print_impromptu_atom(atom!("$dropped_value"));
} }
@@ -1769,10 +1764,10 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
while let Some(loc_data) = self.state_stack.pop() { while let Some(loc_data) = self.state_stack.pop() {
match loc_data { match loc_data {
TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom), TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom),
TokenOrRedirect::BarAsOp => append_str!(self, " | "), TokenOrRedirect::BarAsOp => append_str!(self, "|"),
TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c), TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c),
TokenOrRedirect::Op(atom, op) => { TokenOrRedirect::Op(atom, op) => {
self.print_op(&*atom.as_str()); self.print_op(&atom.as_str());
if is_prefix!(op.get_spec()) { if is_prefix!(op.get_spec()) {
self.set_parent_of_first_op(Some(DirectedOp::Left(atom, op))); self.set_parent_of_first_op(Some(DirectedOp::Left(atom, op)));
@@ -1840,6 +1835,7 @@ mod tests {
use crate::machine::mock_wam::*; use crate::machine::mock_wam::*;
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn term_printing_tests() { fn term_printing_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();

View File

@@ -1,5 +1,5 @@
use std::sync::{Arc, Mutex, Condvar};
use std::io::BufRead; use std::io::BufRead;
use std::sync::{Arc, Condvar, Mutex};
use warp::http; use warp::http;

View File

@@ -665,7 +665,7 @@ pub(crate) fn merge_clause_index(
pub(crate) fn remove_constant_indices( pub(crate) fn remove_constant_indices(
constant: Literal, constant: Literal,
overlapping_constants: &[Literal], overlapping_constants: &[Literal],
indexing_code: &mut Vec<IndexingLine>, indexing_code: &mut [IndexingLine],
offset: usize, offset: usize,
) { ) {
let mut index = 0; let mut index = 0;
@@ -811,7 +811,7 @@ pub(crate) fn remove_constant_indices(
pub(crate) fn remove_structure_index( pub(crate) fn remove_structure_index(
name: Atom, name: Atom,
arity: usize, arity: usize,
indexing_code: &mut Vec<IndexingLine>, indexing_code: &mut [IndexingLine],
offset: usize, offset: usize,
) { ) {
let mut index = 0; let mut index = 0;
@@ -843,10 +843,10 @@ pub(crate) fn remove_structure_index(
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => {
structures_index = index; structures_index = index;
match structures.get(&(name.clone(), arity)).cloned() { match structures.get(&(name, arity)).cloned() {
Some(IndexingCodePtr::DynamicExternal(_)) Some(IndexingCodePtr::DynamicExternal(_))
| Some(IndexingCodePtr::External(_)) => { | Some(IndexingCodePtr::External(_)) => {
structures.remove(&(name.clone(), arity)); structures.remove(&(name, arity));
break; break;
} }
Some(IndexingCodePtr::Internal(o)) => { Some(IndexingCodePtr::Internal(o)) => {
@@ -877,7 +877,7 @@ pub(crate) fn remove_structure_index(
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure( IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
ref mut structures, ref mut structures,
)) => { )) => {
structures.insert((name.clone(), arity), ext); structures.insert((name, arity), ext);
} }
_ => { _ => {
unreachable!() unreachable!()
@@ -908,7 +908,7 @@ pub(crate) fn remove_structure_index(
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure( IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
ref mut structures, ref mut structures,
)) => { )) => {
structures.insert((name.clone(), arity), ext); structures.insert((name, arity), ext);
} }
_ => { _ => {
unreachable!() unreachable!()
@@ -948,7 +948,7 @@ pub(crate) fn remove_structure_index(
} }
} }
pub(crate) fn remove_list_index(indexing_code: &mut Vec<IndexingLine>, offset: usize) { pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usize) {
let mut index = 0; let mut index = 0;
match &mut indexing_code[index] { match &mut indexing_code[index] {
@@ -1028,7 +1028,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut Vec<IndexingLine>, offset: u
pub(crate) fn remove_index( pub(crate) fn remove_index(
opt_arg_index_key: &OptArgIndexKey, opt_arg_index_key: &OptArgIndexKey,
indexing_code: &mut Vec<IndexingLine>, indexing_code: &mut [IndexingLine],
clause_loc: usize, clause_loc: usize,
) { ) {
match opt_arg_index_key { match opt_arg_index_key {
@@ -1049,46 +1049,50 @@ pub(crate) fn remove_index(
#[inline] #[inline]
fn cap_choice_seq(prelude: &mut [IndexedChoiceInstruction]) { fn cap_choice_seq(prelude: &mut [IndexedChoiceInstruction]) {
prelude.first_mut().map(|instr| { if let Some(instr) = prelude.first_mut() {
*instr = IndexedChoiceInstruction::Try(instr.offset()); *instr = IndexedChoiceInstruction::Try(instr.offset());
}); }
cap_choice_seq_with_trust(prelude); cap_choice_seq_with_trust(prelude);
} }
#[inline] #[inline]
fn cap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) { fn cap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) {
prelude.last_mut().map(|instr| match instr { if let Some(instr) = prelude.last_mut() {
IndexedChoiceInstruction::Retry(i) => { match instr {
*instr = IndexedChoiceInstruction::Trust(*i); IndexedChoiceInstruction::Retry(i) => {
*instr = IndexedChoiceInstruction::Trust(*i);
}
IndexedChoiceInstruction::DefaultRetry(i) => {
*instr = IndexedChoiceInstruction::DefaultTrust(*i);
}
_ => {}
} }
IndexedChoiceInstruction::DefaultRetry(i) => { }
*instr = IndexedChoiceInstruction::DefaultTrust(*i);
}
_ => {}
});
} }
#[inline] #[inline]
fn uncap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) { fn uncap_choice_seq_with_trust(prelude: &mut [IndexedChoiceInstruction]) {
prelude.last_mut().map(|instr| match instr { if let Some(instr) = prelude.last_mut() {
IndexedChoiceInstruction::Trust(i) => { match instr {
*instr = IndexedChoiceInstruction::Retry(*i); IndexedChoiceInstruction::Trust(i) => {
*instr = IndexedChoiceInstruction::Retry(*i);
}
IndexedChoiceInstruction::DefaultTrust(i) => {
*instr = IndexedChoiceInstruction::DefaultRetry(*i);
}
_ => {}
} }
IndexedChoiceInstruction::DefaultTrust(i) => { }
*instr = IndexedChoiceInstruction::DefaultRetry(*i);
}
_ => {}
});
} }
#[inline] #[inline]
fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) { fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) {
prelude.first_mut().map(|instr| { if let Some(instr) = prelude.first_mut() {
if let IndexedChoiceInstruction::Try(i) = instr { if let IndexedChoiceInstruction::Try(i) = instr {
*instr = IndexedChoiceInstruction::Retry(*i); *instr = IndexedChoiceInstruction::Retry(*i);
} }
}); }
} }
pub(crate) fn constant_key_alternatives( pub(crate) fn constant_key_alternatives(
@@ -1105,7 +1109,7 @@ pub(crate) fn constant_key_alternatives(
} }
} }
Literal::Char(c) => { Literal::Char(c) => {
let atom = AtomTable::build_with(&atom_tbl, &c.to_string()); let atom = AtomTable::build_with(atom_tbl, &c.to_string());
constants.push(Literal::Atom(atom)); constants.push(Literal::Atom(atom));
} }
/* /*
@@ -1124,9 +1128,11 @@ pub(crate) fn constant_key_alternatives(
Literal::Integer(ref n) => { Literal::Integer(ref n) => {
let result = (&**n).try_into(); let result = (&**n).try_into();
if let Ok(value) = result { if let Ok(value) = result {
Fixnum::build_with_checked(value).map(|n| { Fixnum::build_with_checked(value)
constants.push(Literal::Fixnum(n)); .map(|n| {
}).unwrap(); constants.push(Literal::Fixnum(n));
})
.unwrap();
} }
} }
_ => {} _ => {}
@@ -1245,10 +1251,8 @@ impl Indexer for StaticCodeIndices {
index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1)); index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1));
cap_choice_seq_with_trust(code.make_contiguous()); cap_choice_seq_with_trust(code.make_contiguous());
prelude.push_back(IndexingLine::from(code)); prelude.push_back(IndexingLine::from(code));
} else { } else if let Some(i) = code.front() {
code.front().map(|i| { index_locs.insert(key, IndexingCodePtr::External(i.offset()));
index_locs.insert(key, IndexingCodePtr::External(i.offset()));
});
} }
} }
@@ -1285,7 +1289,7 @@ impl Indexer for StaticCodeIndices {
) -> IndexingCodePtr { ) -> IndexingCodePtr {
if lists.len() > 1 { if lists.len() > 1 {
cap_choice_seq_with_trust(lists.make_contiguous()); cap_choice_seq_with_trust(lists.make_contiguous());
let lists = mem::replace(lists, VecDeque::new()); let lists = std::mem::take(lists);
prelude.push_back(IndexingLine::from(lists)); prelude.push_back(IndexingLine::from(lists));
IndexingCodePtr::Internal(1) IndexingCodePtr::Internal(1)
@@ -1361,10 +1365,8 @@ impl Indexer for DynamicCodeIndices {
prelude.push_back(IndexingLine::DynamicIndexedChoice( prelude.push_back(IndexingLine::DynamicIndexedChoice(
code.into_iter().collect(), code.into_iter().collect(),
)); ));
} else { } else if let Some(i) = code.front() {
code.front().map(|i| { index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
});
} }
} }
@@ -1400,7 +1402,7 @@ impl Indexer for DynamicCodeIndices {
prelude: &mut VecDeque<IndexingLine>, prelude: &mut VecDeque<IndexingLine>,
) -> IndexingCodePtr { ) -> IndexingCodePtr {
if lists.len() > 1 { if lists.len() > 1 {
let lists = mem::replace(lists, VecDeque::new()); let lists = std::mem::take(lists);
prelude.push_back(IndexingLine::DynamicIndexedChoice( prelude.push_back(IndexingLine::DynamicIndexedChoice(
lists.into_iter().collect(), lists.into_iter().collect(),
)); ));
@@ -1458,11 +1460,7 @@ impl<I: Indexer> CodeOffsets<I> {
index: usize, index: usize,
) -> Vec<Literal> { ) -> Vec<Literal> {
let overlapping_constants = constant_key_alternatives(constant, atom_tbl); let overlapping_constants = constant_key_alternatives(constant, atom_tbl);
let code = self let code = self.indices.constants().entry(constant).or_default();
.indices
.constants()
.entry(constant)
.or_insert(VecDeque::new());
let is_initial_index = code.is_empty(); let is_initial_index = code.is_empty();
code.push_back(I::compute_index( code.push_back(I::compute_index(
@@ -1472,11 +1470,7 @@ impl<I: Indexer> CodeOffsets<I> {
)); ));
for constant in &overlapping_constants { for constant in &overlapping_constants {
let code = self let code = self.indices.constants().entry(*constant).or_default();
.indices
.constants()
.entry(*constant)
.or_insert(VecDeque::new());
let is_initial_index = code.is_empty(); let is_initial_index = code.is_empty();
let index = I::compute_index(is_initial_index, index, self.non_counted_bt); let index = I::compute_index(is_initial_index, index, self.non_counted_bt);
@@ -1488,11 +1482,7 @@ impl<I: Indexer> CodeOffsets<I> {
} }
fn index_structure(&mut self, name: Atom, arity: usize, index: usize) -> usize { fn index_structure(&mut self, name: Atom, arity: usize, index: usize) -> usize {
let code = self let code = self.indices.structures().entry((name, arity)).or_default();
.indices
.structures()
.entry((name.clone(), arity))
.or_insert(VecDeque::new());
let code_len = code.len(); let code_len = code.len();
let is_initial_index = code.is_empty(); let is_initial_index = code.is_empty();
@@ -1523,7 +1513,7 @@ impl<I: Indexer> CodeOffsets<I> {
} }
&Term::Clause(_, name, ref terms) => { &Term::Clause(_, name, ref terms) => {
clause_index_info.opt_arg_index_key = clause_index_info.opt_arg_index_key =
OptArgIndexKey::Structure(self.optimal_index, 0, name.clone(), terms.len()); OptArgIndexKey::Structure(self.optimal_index, 0, name, terms.len());
self.index_structure(name, terms.len(), index); self.index_structure(name, terms.len(), index);
} }
@@ -1575,20 +1565,14 @@ impl<I: Indexer> CodeOffsets<I> {
&mut prelude, &mut prelude,
); );
match &mut str_loc { if let IndexingCodePtr::Internal(ref mut i) = &mut str_loc {
IndexingCodePtr::Internal(ref mut i) => { *i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize; }
}
_ => {}
};
match &mut lst_loc { if let IndexingCodePtr::Internal(ref mut i) = &mut lst_loc {
IndexingCodePtr::Internal(ref mut i) => { *i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize;
*i += emitted_switch_on_constant as usize; // con_loc.is_internal() as usize; *i += emitted_switch_on_structure as usize; // str_loc.is_internal() as usize;
*i += emitted_switch_on_structure as usize; // str_loc.is_internal() as usize; }
}
_ => {}
};
let var_offset = 1 + skip_stub_try_me_else as usize; let var_offset = 1 + skip_stub_try_me_else as usize;

View File

@@ -8,6 +8,7 @@ use std::collections::VecDeque;
use std::iter::*; use std::iter::*;
use std::vec::Vec; use std::vec::Vec;
#[allow(clippy::borrowed_box)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum TermRef<'a> { pub(crate) enum TermRef<'a> {
AnonVar(Level), AnonVar(Level),
@@ -35,6 +36,7 @@ impl<'a> TermRef<'a> {
} }
*/ */
#[allow(clippy::borrowed_box)]
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum TermIterState<'a> { pub(crate) enum TermIterState<'a> {
AnonVar(Level), AnonVar(Level),
@@ -113,11 +115,11 @@ impl<'a> QueryIterator<'a> {
fn extend_state(&mut self, lvl: Level, term: &'a QueryTerm) { fn extend_state(&mut self, lvl: Level, term: &'a QueryTerm) {
match term { match term {
&QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => { QueryTerm::Clause(ref cell, ClauseType::CallN(_), ref terms, _) => {
self.state_stack self.state_stack
.push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms)); .push(TermIterState::Clause(lvl, 1, cell, atom!("$call"), terms));
} }
&QueryTerm::Clause(ref cell, ref ct, ref terms, _) => { QueryTerm::Clause(ref cell, ref ct, ref terms, _) => {
self.state_stack self.state_stack
.push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms)); .push(TermIterState::Clause(lvl, 0, cell, ct.name(), terms));
} }
@@ -214,7 +216,7 @@ impl<'a> FactIterator<'a> {
.push_back(TermIterState::subterm_to_state(lvl, term)); .push_back(TermIterState::subterm_to_state(lvl, term));
} }
pub(crate) fn from_rule_head_clause(terms: &'a Vec<Term>) -> Self { pub(crate) fn from_rule_head_clause(terms: &'a [Term]) -> Self {
let state_queue = terms let state_queue = terms
.iter() .iter()
.map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt)) .map(|bt| TermIterState::subterm_to_state(Level::Shallow, bt))
@@ -312,14 +314,14 @@ impl<'a> Iterator for FactIterator<'a> {
} }
} }
pub(crate) fn post_order_iter<'a>(term: &'a Term) -> QueryIterator<'a> { pub(crate) fn post_order_iter(term: &'_ Term) -> QueryIterator {
QueryIterator::from_term(term) QueryIterator::from_term(term)
} }
pub(crate) fn breadth_first_iter<'a>( pub(crate) fn breadth_first_iter(
term: &'a Term, term: &'_ Term,
iterable_root: RootIterationPolicy, iterable_root: RootIterationPolicy,
) -> FactIterator<'a> { ) -> FactIterator {
FactIterator::new(term, iterable_root) FactIterator::new(term, iterable_root)
} }
@@ -343,7 +345,7 @@ pub(crate) struct ClauseIterator<'a> {
remaining_chunks_on_stack: usize, remaining_chunks_on_stack: usize,
} }
fn state_from_chunked_terms<'a>(chunk_vec: &'a VecDeque<ChunkedTerms>) -> ClauseIteratorState<'a> { fn state_from_chunked_terms(chunk_vec: &'_ VecDeque<ChunkedTerms>) -> ClauseIteratorState {
if chunk_vec.len() == 1 { if chunk_vec.len() == 1 {
if let Some(ChunkedTerms::Branch(ref branches)) = chunk_vec.front() { if let Some(ChunkedTerms::Branch(ref branches)) = chunk_vec.front() {
return ClauseIteratorState::RemainingBranches(branches, 0); return ClauseIteratorState::RemainingBranches(branches, 0);
@@ -422,7 +424,7 @@ impl<'a> Iterator for ClauseIterator<'a> {
if focus < branches.len() => if focus < branches.len() =>
{ {
self.state_stack self.state_stack
.push(ClauseIteratorState::RemainingBranches(&branches, focus + 1)); .push(ClauseIteratorState::RemainingBranches(branches, focus + 1));
let state = state_from_chunked_terms(&branches[focus]); let state = state_from_chunked_terms(&branches[focus]);
if let ClauseIteratorState::RemainingChunks(..) = &state { if let ClauseIteratorState::RemainingChunks(..) = &state {

View File

@@ -3,7 +3,8 @@
#[macro_use] #[macro_use]
extern crate static_assertions; extern crate static_assertions;
#[cfg(test)] #[cfg(test)]
#[macro_use] extern crate maplit; #[macro_use]
extern crate maplit;
#[macro_use] #[macro_use]
pub mod macros; pub mod macros;
@@ -51,6 +52,8 @@ use wasm_bindgen::prelude::*;
pub fn eval_code(s: &str) -> String { pub fn eval_code(s: &str) -> String {
use machine::mock_wam::*; use machine::mock_wam::*;
console_error_panic_hook::set_once();
let mut wam = Machine::with_test_streams(); let mut wam = Machine::with_test_streams();
let bytes = wam.test_load_string(s); let bytes = wam.test_load_string(s);
String::from_utf8_lossy(&bytes).to_string() String::from_utf8_lossy(&bytes).to_string()

View File

@@ -1,8 +1,8 @@
:- module(atts, [op(1199, fx, attribute), :- module(atts, [op(1199, fx, attribute),
call_residue_vars/2,
term_attributed_variables/2]). term_attributed_variables/2]).
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
:- use_module(library(error)).
:- use_module(library(terms)). :- use_module(library(terms)).
/* represent the list of attributes belonging to a variable, /* represent the list of attributes belonging to a variable,
@@ -110,12 +110,5 @@ user:goal_expansion(Term, M:get_atts(Var, Attr)) :-
nonvar(Term), nonvar(Term),
Term = get_atts(Var, M, Attr). 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) :-
'$term_attributed_variables'(Term, Vars). '$term_attributed_variables'(Term, Vars).

View File

@@ -1173,8 +1173,13 @@ clause(H, B) :-
% The clause will be inserted at the beginning of the module. % The clause will be inserted at the beginning of the module.
asserta(Clause0) :- asserta(Clause0) :-
loader:strip_subst_module(Clause0, user, Module, Clause), loader:strip_subst_module(Clause0, user, Module, Clause),
iso_ext:asserta(Module, Clause). asserta_(Module, Clause).
asserta_(Module, (Head :- Body)) :-
!,
'$asserta'(Module, Head, Body).
asserta_(Module, Fact) :-
'$asserta'(Module, Fact, true).
:- meta_predicate assertz(:). :- meta_predicate assertz(:).
@@ -1184,7 +1189,13 @@ asserta(Clause0) :-
% The clase will be inserted at the end of the module. % The clase will be inserted at the end of the module.
assertz(Clause0) :- assertz(Clause0) :-
loader:strip_subst_module(Clause0, user, Module, Clause), loader:strip_subst_module(Clause0, user, Module, Clause),
iso_ext:assertz(Module, Clause). assertz_(Module, Clause).
assertz_(Module, (Head :- Body)) :-
!,
'$assertz'(Module, Head, Body).
assertz_(Module, Fact) :-
'$assertz'(Module, Fact, true).
:- meta_predicate retract(:). :- meta_predicate retract(:).
@@ -1203,6 +1214,9 @@ retract(Clause0) :-
Body = true, Body = true,
retract_module_clause(Head, Body, Module) retract_module_clause(Head, Body, Module)
; Clause = (Head :- Body) -> ; Clause = (Head :- Body) ->
( var(Module) -> Module = user
; true
),
retract_module_clause(Head, Body, Module) retract_module_clause(Head, Body, Module)
). ).
@@ -1225,7 +1239,10 @@ call_retract_helper(Head, Body, P, Module) :-
; ClauseQualifier = Module ; ClauseQualifier = Module
), ),
ClauseQualifier:'$clause'(Head, Body), ClauseQualifier:'$clause'(Head, Body),
'$get_clause_p'(Head, P, Module). % ensure '$get_clause_p'/3 is not the last clause so it can
% recover the choice point of '$clause' if necessary.
'$get_clause_p'(Head, P, Module),
true.
call_retract(Head, Body, Name, Arity, Module) :- call_retract(Head, Body, Name, Arity, Module) :-
findall(P, builtins:call_retract_helper(Head, Body, P, Module), Ps), findall(P, builtins:call_retract_helper(Head, Body, P, Module), Ps),

View File

@@ -20,6 +20,7 @@ read and write chars.
:- use_module(library(iso_ext)). :- use_module(library(iso_ext)).
:- use_module(library(error)). :- use_module(library(error)).
:- use_module(library(lists)). :- use_module(library(lists)).
:- use_module(library(between)).
:- use_module(library(iso_ext), [partial_string/1,partial_string/3]). :- use_module(library(iso_ext), [partial_string/1,partial_string/3]).
fabricate_var_name(VarType, VarName, N) :- fabricate_var_name(VarType, VarName, N) :-
@@ -74,9 +75,10 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
). ).
%% char_type(+Char, -Type). %% char_type(?Char, ?Type).
% %
% Given a Char, Type is one of the categories that char fits in. % Type is one of the categories that Char fits in.
% At least one of the arguments must be ground.
% Possible categories are: % Possible categories are:
% %
% - `alnum` % - `alnum`
@@ -132,17 +134,27 @@ extend_var_list_([V|Vs], N, VarList, NewVarList, VarType) :-
% Note that uppercase and lowercase transformations use a string. This is because % Note that uppercase and lowercase transformations use a string. This is because
% some characters do not map 1:1 between lowercase and uppercase. % some characters do not map 1:1 between lowercase and uppercase.
char_type(Char, Type) :- char_type(Char, Type) :-
must_be(character, Char), can_be(character, Char),
( ground(Type) -> ( \+ ctype(Type) ->
( ctype(Type) -> domain_error(char_type, Type, char_type/2)
'$char_type'(Char, Type) ; true
; domain_error(char_type, Type, char_type/2) ),
) ( ground(Char) ->
; ctype(Type), ctype(Type),
'$char_type'(Char, Type) '$char_type'(Char, Type)
; ground(Type) ->
ccode(Code),
char_code(Char, Code),
'$char_type'(Char, Type)
; must_be(character, Char)
). ).
% 0xD800 to 0xDFFF are surrogate code points used by UTF-16.
ccode(Code) :- between(0, 0xD7FF, Code).
ccode(Code) :- between(0xE000, 0x10FFFF, Code).
ctype(alnum). ctype(alnum).
ctype(alpha). ctype(alpha).
ctype(alphabetic). ctype(alphabetic).

View File

@@ -189,18 +189,18 @@ A _Boolean expression_ is one of:
| `1` | true | | `1` | true |
| _variable_ | unknown truth value | | _variable_ | unknown truth value |
| _atom_ | universally quantified variable | | _atom_ | universally quantified variable |
| ~ _Expr_ | logical NOT | | `~` _Expr_ | logical NOT |
| _Expr_ + _Expr_ | logical OR | | _Expr_ `+` _Expr_ | logical OR |
| _Expr_ * _Expr_ | logical AND | | _Expr_ `*` _Expr_ | logical AND |
| _Expr_ # _Expr_ | exclusive OR | | _Expr_ `#` _Expr_ | exclusive OR |
| _Var_ ^ _Expr_ | existential quantification | | _Var_ `^` _Expr_ | existential quantification |
| _Expr_ =:= _Expr_ | equality | | _Expr_ `=:=` _Expr_ | equality |
| _Expr_ =\= _Expr_ | disequality (same as #) | | _Expr_ `=\=` _Expr_ | disequality (same as #) |
| _Expr_ =< _Expr_ | less or equal (implication) | | _Expr_ `=<` _Expr_ | less or equal (implication) |
| _Expr_ >= _Expr_ | greater or equal | | _Expr_ `>=` _Expr_ | greater or equal |
| _Expr_ < _Expr_ | less than | | _Expr_ `<` _Expr_ | less than |
| _Expr_ > _Expr_ | greater than | | _Expr_ `>` _Expr_ | greater than |
| card(Is,Exprs) | cardinality constraint (_see below_) | | `card(Is,Exprs)` | cardinality constraint (_see below_) |
| `+(Exprs)` | n-fold disjunction (_see below_) | | `+(Exprs)` | n-fold disjunction (_see below_) |
| `*(Exprs)` | n-fold conjunction (_see below_) | | `*(Exprs)` | n-fold conjunction (_see below_) |
@@ -1251,7 +1251,7 @@ bdd_restriction_(Node, VI, Value, Res) -->
node_id(Node, ID) }, node_id(Node, ID) },
( { I0 =:= VI } -> ( { I0 =:= VI } ->
( { Value =:= 0 } -> { Res = Low } ( { Value =:= 0 } -> { Res = Low }
; { Value =:= 1 } -> { Res = High } ; { Res = High }
) )
; { I0 > VI } -> { Res = Node } ; { I0 > VI } -> { Res = Node }
; state(G0), { get_assoc(ID, G0, Res) } -> [] ; state(G0), { get_assoc(ID, G0, Res) } -> []

View File

@@ -3,7 +3,7 @@
Author: Markus Triska Author: Markus Triska
E-mail: triska@metalevel.at E-mail: triska@metalevel.at
WWW: https://www.metalevel.at WWW: https://www.metalevel.at
Copyright (C): 2016-2023 Markus Triska Copyright (C): 2016-2024 Markus Triska
This library provides CLP(): This library provides CLP():
@@ -102,6 +102,7 @@
fd_dom/2, fd_dom/2,
% for use in predicates from library(reif) % for use in predicates from library(reif)
clpz_t/2,
(#=)/3, (#=)/3,
(#<)/3 (#<)/3
@@ -1015,6 +1016,9 @@ X in inf..sup.
needed to schedule the propagators! needed to schedule the propagators!
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
:- meta_predicate(duophrase(4, ?, ?)).
:- meta_predicate(duophrase(4, ?, ?, ?, ?)).
duophrase(NT, As, Bs) :- duophrase(NT, As, Bs) :-
duophrase(NT, As, [], Bs, []). duophrase(NT, As, [], Bs, []).
@@ -1735,7 +1739,7 @@ intervals_to_domain(Is, D) :-
% _Lower_ must be an integer or the atom *inf*, which % _Lower_ must be an integer or the atom *inf*, which
% denotes negative infinity. _Upper_ must be an integer or % denotes negative infinity. _Upper_ must be an integer or
% the atom *sup*, which denotes positive infinity. % the atom *sup*, which denotes positive infinity.
% * Domain1 \/ Domain2 % * Domain1 `\/` Domain2
% The union of Domain1 and Domain2. % The union of Domain1 and Domain2.
Var in Dom :- clpz_in(Var, Dom). Var in Dom :- clpz_in(Var, Dom).
@@ -2409,22 +2413,22 @@ sum_finite_domains([C|Cs], [V|Vs], Inf0, Sup0, Inf, Sup) ++>
), ),
sum_finite_domains(Cs, Vs, Inf2, Sup2, Inf, Sup). sum_finite_domains(Cs, Vs, Inf2, Sup2, Inf, Sup).
remove_dist_upper_lower([], _, _, _). remove_dist_upper_lower([], _, _, _) --> [].
remove_dist_upper_lower([C|Cs], [V|Vs], D1, D2) :- remove_dist_upper_lower([C|Cs], [V|Vs], D1, D2) -->
( fd_get(V, VD, VPs) -> ( { fd_get(V, VD, VPs) } ->
( C < 0 -> ( C < 0 ->
domain_supremum(VD, n(Sup)), { domain_supremum(VD, n(Sup)),
L is Sup + D1//C, L is Sup + D1//C,
domain_remove_smaller_than(VD, L, VD1), domain_remove_smaller_than(VD, L, VD1),
domain_infimum(VD1, n(Inf)), domain_infimum(VD1, n(Inf)),
G is Inf - D2//C, G is Inf - D2//C,
domain_remove_greater_than(VD1, G, VD2) domain_remove_greater_than(VD1, G, VD2) }
; domain_infimum(VD, n(Inf)), ; { domain_infimum(VD, n(Inf)),
G is Inf + D1//C, G is Inf + D1//C,
domain_remove_greater_than(VD, G, VD1), domain_remove_greater_than(VD, G, VD1),
domain_supremum(VD1, n(Sup)), domain_supremum(VD1, n(Sup)),
L is Sup - D2//C, L is Sup - D2//C,
domain_remove_smaller_than(VD1, L, VD2) domain_remove_smaller_than(VD1, L, VD2) }
), ),
fd_put(V, VD2, VPs) fd_put(V, VD2, VPs)
; true ; true
@@ -2432,16 +2436,16 @@ remove_dist_upper_lower([C|Cs], [V|Vs], D1, D2) :-
remove_dist_upper_lower(Cs, Vs, D1, D2). remove_dist_upper_lower(Cs, Vs, D1, D2).
remove_dist_upper_leq([], _, _). remove_dist_upper_leq([], _, _) --> [].
remove_dist_upper_leq([C|Cs], [V|Vs], D1) :- remove_dist_upper_leq([C|Cs], [V|Vs], D1) -->
( fd_get(V, VD, VPs) -> ( { fd_get(V, VD, VPs) } ->
( C < 0 -> ( C < 0 ->
domain_supremum(VD, n(Sup)), { domain_supremum(VD, n(Sup)),
L is Sup + D1//C, L is Sup + D1//C,
domain_remove_smaller_than(VD, L, VD1) domain_remove_smaller_than(VD, L, VD1) }
; domain_infimum(VD, n(Inf)), ; { domain_infimum(VD, n(Inf)),
G is Inf + D1//C, G is Inf + D1//C,
domain_remove_greater_than(VD, G, VD1) domain_remove_greater_than(VD, G, VD1) }
), ),
fd_put(V, VD1, VPs) fd_put(V, VD1, VPs)
; true ; true
@@ -2449,18 +2453,18 @@ remove_dist_upper_leq([C|Cs], [V|Vs], D1) :-
remove_dist_upper_leq(Cs, Vs, D1). remove_dist_upper_leq(Cs, Vs, D1).
remove_dist_upper([], _). remove_dist_upper([], _) --> [].
remove_dist_upper([C*V|CVs], D) :- remove_dist_upper([C*V|CVs], D) -->
( fd_get(V, VD, VPs) -> ( { fd_get(V, VD, VPs) } ->
( C < 0 -> ( C < 0 ->
( domain_supremum(VD, n(Sup)) -> ( { domain_supremum(VD, n(Sup)) } ->
L is Sup + D//C, { L is Sup + D//C,
domain_remove_smaller_than(VD, L, VD1) domain_remove_smaller_than(VD, L, VD1) }
; VD1 = VD ; VD1 = VD
) )
; ( domain_infimum(VD, n(Inf)) -> ; ( { domain_infimum(VD, n(Inf)) } ->
G is Inf + D//C, { G is Inf + D//C,
domain_remove_greater_than(VD, G, VD1) domain_remove_greater_than(VD, G, VD1) }
; VD1 = VD ; VD1 = VD
) )
), ),
@@ -2469,18 +2473,18 @@ remove_dist_upper([C*V|CVs], D) :-
), ),
remove_dist_upper(CVs, D). remove_dist_upper(CVs, D).
remove_dist_lower([], _). remove_dist_lower([], _) --> [].
remove_dist_lower([C*V|CVs], D) :- remove_dist_lower([C*V|CVs], D) -->
( fd_get(V, VD, VPs) -> ( { fd_get(V, VD, VPs) } ->
( C < 0 -> ( C < 0 ->
( domain_infimum(VD, n(Inf)) -> ( { domain_infimum(VD, n(Inf)) } ->
G is Inf - D//C, { G is Inf - D//C,
domain_remove_greater_than(VD, G, VD1) domain_remove_greater_than(VD, G, VD1) }
; VD1 = VD ; VD1 = VD
) )
; ( domain_supremum(VD, n(Sup)) -> ; ( { domain_supremum(VD, n(Sup)) } ->
L is Sup - D//C, { L is Sup - D//C,
domain_remove_smaller_than(VD, L, VD1) domain_remove_smaller_than(VD, L, VD1) }
; VD1 = VD ; VD1 = VD
) )
), ),
@@ -2489,26 +2493,26 @@ remove_dist_lower([C*V|CVs], D) :-
), ),
remove_dist_lower(CVs, D). remove_dist_lower(CVs, D).
remove_upper([], _). remove_upper([], _) --> [].
remove_upper([C*X|CXs], Max) :- remove_upper([C*X|CXs], Max) -->
( fd_get(X, XD, XPs) -> ( { fd_get(X, XD, XPs) } ->
D is Max//C, D is Max//C,
( C < 0 -> ( C < 0 ->
domain_remove_smaller_than(XD, D, XD1) { domain_remove_smaller_than(XD, D, XD1) }
; domain_remove_greater_than(XD, D, XD1) ; { domain_remove_greater_than(XD, D, XD1) }
), ),
fd_put(X, XD1, XPs) fd_put(X, XD1, XPs)
; true ; true
), ),
remove_upper(CXs, Max). remove_upper(CXs, Max).
remove_lower([], _). remove_lower([], _) --> [].
remove_lower([C*X|CXs], Min) :- remove_lower([C*X|CXs], Min) -->
( fd_get(X, XD, XPs) -> ( { fd_get(X, XD, XPs) } ->
D is -Min//C, D is -Min//C,
( C < 0 -> ( C < 0 ->
domain_remove_greater_than(XD, D, XD1) { domain_remove_greater_than(XD, D, XD1) }
; domain_remove_smaller_than(XD, D, XD1) ; { domain_remove_smaller_than(XD, D, XD1) }
), ),
fd_put(X, XD1, XPs) fd_put(X, XD1, XPs)
; true ; true
@@ -2747,20 +2751,24 @@ propagator_init_trigger(Vs, P) :-
prop_init(Prop, V) :- init_propagator(V, Prop). prop_init(Prop, V) :- init_propagator(V, Prop).
geq(A, B) :- geq(A, B) :-
( fd_get(A, AD, APs) -> new_queue(Q),
domain_infimum(AD, AI), phrase((geq(A, B),do_queue), [Q], _).
( fd_get(B, BD, _) ->
domain_supremum(BD, BS), geq(A, B) -->
( AI cis_geq BS -> true ( { fd_get(A, AD, APs) } ->
; propagator_init_trigger(pgeq(A,B)) { domain_infimum(AD, AI) },
( { fd_get(B, BD, _) } ->
{ domain_supremum(BD, BS) },
( { AI cis_geq BS } -> true
; { propagator_init_trigger(pgeq(A,B)) }
) )
; ( AI cis_geq n(B) -> true ; ( { AI cis_geq n(B) } -> true
; domain_remove_smaller_than(AD, B, AD1), ; { domain_remove_smaller_than(AD, B, AD1) },
fd_put(A, AD1, APs) fd_put(A, AD1, APs)
) )
) )
; fd_get(B, BD, BPs) -> ; { fd_get(B, BD, BPs) } ->
domain_remove_greater_than(BD, A, BD1), { domain_remove_greater_than(BD, A, BD1) },
fd_put(B, BD1, BPs) fd_put(B, BD1, BPs)
; A >= B ; A >= B
). ).
@@ -4164,6 +4172,7 @@ var(V) --> { var(V) }.
ground(T) --> { ground(T) }. ground(T) --> { ground(T) }.
true --> []. true --> [].
false --> { false }.
X >= Y --> { X >= Y }. X >= Y --> { X >= Y }.
X =< Y --> { X =< Y }. X =< Y --> { X =< Y }.
@@ -4221,10 +4230,7 @@ activate_propagator(propagator(P,State)) -->
) )
). ).
enable_queue :- true. % NOP %do_queue --> print_queue, false.
disable_queue :- true. % NOP
%do_queue --> print_queue, { false }.
do_queue --> do_queue -->
( queue_enabled -> ( queue_enabled ->
( queue_get_goal(Goal) -> { call(Goal) }, do_queue ( queue_get_goal(Goal) -> { call(Goal) }, do_queue
@@ -4507,13 +4513,13 @@ run_propagator(pelement(N, Is, V), MState) -->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
run_propagator(pgcc_single(Vs, Pairs), _) --> { gcc_global(Vs, Pairs) }. run_propagator(pgcc_single(Vs, Pairs), _) --> gcc_global(Vs, Pairs).
run_propagator(pgcc_check_single(Pairs), _) --> { gcc_check(Pairs) }. run_propagator(pgcc_check_single(Pairs), _) --> gcc_check(Pairs).
run_propagator(pgcc_check(Pairs), _) --> { gcc_check(Pairs) }. run_propagator(pgcc_check(Pairs), _) --> gcc_check(Pairs).
run_propagator(pgcc(Vs, _, Pairs), _) --> { gcc_global(Vs, Pairs) }. run_propagator(pgcc(Vs, _, Pairs), _) --> gcc_global(Vs, Pairs).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -4588,7 +4594,7 @@ run_propagator(pserialized(S_I, D_I, S_J, D_J, _), MState) -->
kill(MState), kill(MState),
( S_I + D_I =< S_J -> [] ( S_I + D_I =< S_J -> []
; S_J + D_J =< S_I -> [] ; S_J + D_J =< S_I -> []
; { false } ; false
) )
; serialize_lower_upper(S_I, D_I, S_J, D_J, MState), ; serialize_lower_upper(S_I, D_I, S_J, D_J, MState),
serialize_lower_upper(S_J, D_J, S_I, D_I, MState) serialize_lower_upper(S_J, D_J, S_I, D_I, MState)
@@ -4661,7 +4667,7 @@ run_propagator(x_eq_abs_plus_v(X,V), MState) -->
( nonvar(V) -> ( nonvar(V) ->
( V =:= 0 -> kill(MState), { X in 0..sup } ( V =:= 0 -> kill(MState), { X in 0..sup }
; V < 0 -> kill(MState), { X #= V / 2 } ; V < 0 -> kill(MState), { X #= V / 2 }
; V > 0 -> { false } ; false % V > 0
) )
; nonvar(X) -> ; nonvar(X) ->
kill(MState), kill(MState),
@@ -4753,55 +4759,55 @@ run_propagator(scalar_product_neq(Cs0,Vs0,P0), MState) -->
) }. ) }.
run_propagator(scalar_product_leq(Cs0,Vs0,P0), MState) --> run_propagator(scalar_product_leq(Cs0,Vs0,P0), MState) -->
{ coeffs_variables_const(Cs0, Vs0, Cs, Vs, 0, I), { coeffs_variables_const(Cs0, Vs0, Cs, Vs, 0, I) },
P is P0 - I, P is P0 - I,
( Vs = [] -> kill(MState), P >= 0 ( Vs = [] -> kill(MState), P >= 0
; duophrase(sum_finite_domains(Cs, Vs, 0, 0, Inf, Sup), Infs, Sups), ; { duophrase(sum_finite_domains(Cs, Vs, 0, 0, Inf, Sup), Infs, Sups) },
D1 is P - Inf, D1 is P - Inf,
disable_queue, disable_queue,
( Infs == [], Sups == [] -> ( Infs == [], Sups == [] ->
Inf =< P, Inf =< P,
( Sup =< P -> kill(MState) ( Sup =< P -> kill(MState)
; remove_dist_upper_leq(Cs, Vs, D1) ; remove_dist_upper_leq(Cs, Vs, D1)
) )
; Infs == [] -> Inf =< P, remove_dist_upper(Sups, D1) ; Infs == [] -> Inf =< P, remove_dist_upper(Sups, D1)
; Infs = [_] -> remove_upper(Infs, D1) ; Infs = [_] -> remove_upper(Infs, D1)
; true ; true
), ),
enable_queue enable_queue
) }. ).
run_propagator(scalar_product_eq(Cs0,Vs0,P0), MState) --> run_propagator(scalar_product_eq(Cs0,Vs0,P0), MState) -->
{ coeffs_variables_const(Cs0, Vs0, Cs, Vs, 0, I), { coeffs_variables_const(Cs0, Vs0, Cs, Vs, 0, I) },
P is P0 - I, P is P0 - I,
( Vs = [] -> kill(MState), P =:= 0 ( Vs = [] -> kill(MState), P =:= 0
; Vs = [V], Cs = [C] -> kill(MState), P mod C =:= 0, V is P // C ; Vs = [V], Cs = [C] -> kill(MState), P mod C =:= 0, V is P // C
; Cs == [1,1] -> kill(MState), Vs = [A,B], A + B #= P ; Cs == [1,1] -> kill(MState), Vs = [A,B], { A + B #= P }
; Cs == [1,-1] -> kill(MState), Vs = [A,B], A #= P + B ; Cs == [1,-1] -> kill(MState), Vs = [A,B], { A #= P + B }
; Cs == [-1,1] -> kill(MState), Vs = [A,B], B #= P + A ; Cs == [-1,1] -> kill(MState), Vs = [A,B], { B #= P + A }
; Cs == [-1,-1] -> kill(MState), Vs = [A,B], P1 is -P, A + B #= P1 ; Cs == [-1,-1] -> kill(MState), Vs = [A,B], P1 is -P, { A + B #= P1 }
; P =:= 0, Cs == [1,1,-1] -> kill(MState), Vs = [A,B,C], A + B #= C ; P =:= 0, Cs == [1,1,-1] -> kill(MState), Vs = [A,B,C], { A + B #= C }
; P =:= 0, Cs == [1,-1,1] -> kill(MState), Vs = [A,B,C], A + C #= B ; P =:= 0, Cs == [1,-1,1] -> kill(MState), Vs = [A,B,C], { A + C #= B }
; P =:= 0, Cs == [-1,1,1] -> kill(MState), Vs = [A,B,C], B + C #= A ; P =:= 0, Cs == [-1,1,1] -> kill(MState), Vs = [A,B,C], { B + C #= A }
; duophrase(sum_finite_domains(Cs, Vs, 0, 0, Inf, Sup), Infs, Sups), ; { duophrase(sum_finite_domains(Cs, Vs, 0, 0, Inf, Sup), Infs, Sups) },
% nl, writeln(Infs-Sups-Inf-Sup), % { nl, writeln(Infs-Sups-Inf-Sup) },
D1 is P - Inf, D1 is P - Inf,
D2 is Sup - P, D2 is Sup - P,
disable_queue, disable_queue,
( Infs == [], Sups == [] -> ( Infs == [], Sups == [] ->
between(Inf, Sup, P), { between(Inf, Sup, P) },
remove_dist_upper_lower(Cs, Vs, D1, D2) remove_dist_upper_lower(Cs, Vs, D1, D2)
; Sups = [] -> P =< Sup, remove_dist_lower(Infs, D2) ; Sups = [] -> P =< Sup, remove_dist_lower(Infs, D2)
; Infs = [] -> Inf =< P, remove_dist_upper(Sups, D1) ; Infs = [] -> Inf =< P, remove_dist_upper(Sups, D1)
; Sups = [_], Infs = [_] -> ; Sups = [_], Infs = [_] ->
remove_lower(Sups, D2), remove_lower(Sups, D2),
remove_upper(Infs, D1) remove_upper(Infs, D1)
; Infs = [_] -> remove_upper(Infs, D1) ; Infs = [_] -> remove_upper(Infs, D1)
; Sups = [_] -> remove_lower(Sups, D2) ; Sups = [_] -> remove_lower(Sups, D2)
; true ; true
), ),
enable_queue enable_queue
) }. ).
% X + Y = Z % X + Y = Z
run_propagator(pplus(X,Y,Z,Morph), MState) --> run_propagator(pplus(X,Y,Z,Morph), MState) -->
@@ -5048,8 +5054,8 @@ run_propagator(ptzdiv(X,Y,Z,Morph), MState) -->
%% % Z = X mod Y %% % Z = X mod Y
run_propagator(pmod(X,Y,Z), MState) --> run_propagator(pmod(X,Y,Z), MState) -->
( Y == 0 -> { false } ( Y == 0 -> false
; Y == Z -> { false } ; Y == Z -> false
; X == Y -> kill(MState), queue_goal(Z = 0) ; X == Y -> kill(MState), queue_goal(Z = 0)
; true ; true
), ),
@@ -5058,7 +5064,7 @@ run_propagator(pmod(X,Y,Z), MState) -->
Z is X mod Y Z is X mod Y
; nonvar(Y), nonvar(Z) -> ; nonvar(Y), nonvar(Z) ->
( Y > 0 -> Z >= 0, Z < Y ( Y > 0 -> Z >= 0, Z < Y
; Y < 0 -> Z =< 0, Z > Y ; Z =< 0, Z > Y % Y < 0
), ),
( { fd_get(X, _, n(XL), _, _) } -> ( { fd_get(X, _, n(XL), _, _) } ->
( (XL - Z) mod Y =\= 0 -> ( (XL - Z) mod Y =\= 0 ->
@@ -5127,7 +5133,7 @@ run_propagator(pmodz(X,Y,Z), MState) -->
fd_put(Z, ZD2, ZPs) fd_put(Z, ZD2, ZPs)
% queue_goal(Z #=< X) % queue_goal(Z #=< X)
) )
; X < 0 -> ; X < 0,
( { fd_get(Y, _, _, n(YU), _), YU < X } -> ( { fd_get(Y, _, _, n(YU), _), YU < X } ->
kill(MState), kill(MState),
queue_goal(Z = X) queue_goal(Z = X)
@@ -5167,7 +5173,7 @@ run_propagator(pmodz(X,Y,Z), MState) -->
fd_put(Z, ZD5, ZPs) fd_put(Z, ZD5, ZPs)
% queue_goal(Z in ZMin..0) % queue_goal(Z in ZMin..0)
) )
; Y > 0 -> ; Y > 0,
( { fd_get(X, _, n(XL), n(XU), _), XL >= 0, Y > XU } -> ( { fd_get(X, _, n(XL), n(XU), _), XL >= 0, Y > XU } ->
kill(MState), kill(MState),
queue_goal(Z = X) queue_goal(Z = X)
@@ -5378,8 +5384,9 @@ run_propagator(pmax(X,Y,Z), MState) -->
; nonvar(Z) -> ; nonvar(Z) ->
( Z =:= X -> kill(MState), queue_goal(X #>= Y) ( Z =:= X -> kill(MState), queue_goal(X #>= Y)
; Z > X -> queue_goal(Z = Y) ; Z > X -> queue_goal(Z = Y)
; { false } % Z < X ; false % Z < X
) )
; Y == Z -> kill(MState), queue_goal(Y #>= X)
; { fd_get(Y, _, YInf, YSup, _) }, ; { fd_get(Y, _, YInf, YSup, _) },
( { YInf cis_gt n(X) } -> queue_goal(Z = Y) ( { YInf cis_gt n(X) } -> queue_goal(Z = Y)
; { YSup cis_lt n(X) } -> queue_goal(Z = X) ; { YSup cis_lt n(X) } -> queue_goal(Z = X)
@@ -5394,7 +5401,7 @@ run_propagator(pmax(X,Y,Z), MState) -->
; { fd_get(Z, ZD, ZPs) } -> ; { fd_get(Z, ZD, ZPs) } ->
{ fd_get(X, _, XInf, XSup, _), { fd_get(X, _, XInf, XSup, _),
fd_get(Y, _, YInf, YSup, _) }, fd_get(Y, _, YInf, YSup, _) },
( { YInf cis_gt YSup } -> kill(MState), queue_goal(Z = Y) ( { YInf cis_gt XSup } -> kill(MState), queue_goal(Z = Y)
; { YSup cis_lt XInf } -> kill(MState), queue_goal(Z = X) ; { YSup cis_lt XInf } -> kill(MState), queue_goal(Z = X)
; { n(M) cis max(XSup, YSup) } -> ; { n(M) cis max(XSup, YSup) } ->
{ domain_remove_greater_than(ZD, M, ZD1) }, { domain_remove_greater_than(ZD, M, ZD1) },
@@ -5413,8 +5420,9 @@ run_propagator(pmin(X,Y,Z), MState) -->
; nonvar(Z) -> ; nonvar(Z) ->
( Z =:= X -> kill(MState), { X #=< Y } ( Z =:= X -> kill(MState), { X #=< Y }
; Z < X -> Z = Y ; Z < X -> Z = Y
; { false } % Z > X ; false % Z > X
) )
; Y == Z -> kill(MState), queue_goal(Y #=< X)
; { fd_get(Y, _, YInf, YSup, _) }, ; { fd_get(Y, _, YInf, YSup, _) },
( { YSup cis_lt n(X) } -> Z = Y ( { YSup cis_lt n(X) } -> Z = Y
; { YInf cis_gt n(X) } -> Z = X ; { YInf cis_gt n(X) } -> Z = X
@@ -5429,7 +5437,7 @@ run_propagator(pmin(X,Y,Z), MState) -->
; { fd_get(Z, ZD, ZPs) } -> ; { fd_get(Z, ZD, ZPs) } ->
{ fd_get(X, _, XInf, XSup, _), { fd_get(X, _, XInf, XSup, _),
fd_get(Y, _, YInf, YSup, _) }, fd_get(Y, _, YInf, YSup, _) },
( { YSup cis_lt YInf } -> kill(MState), Z = Y ( { YSup cis_lt XInf } -> kill(MState), Z = Y
; { YInf cis_gt XSup } -> kill(MState), Z = X ; { YInf cis_gt XSup } -> kill(MState), Z = X
; { n(M) cis min(XInf, YInf) } -> ; { n(M) cis min(XInf, YInf) } ->
{ domain_remove_smaller_than(ZD, M, ZD1) }, { domain_remove_smaller_than(ZD, M, ZD1) },
@@ -5448,6 +5456,7 @@ run_propagator(pexp(X,Y,Z,Morph), MState) -->
morph_into_propagator(MState, [Y,Z], reified_eq(1,Y,1,0,[],Z), Morph) morph_into_propagator(MState, [Y,Z], reified_eq(1,Y,1,0,[],Z), Morph)
; Y == 0 -> kill(MState), Z = 1 ; Y == 0 -> kill(MState), Z = 1
; Y == 1 -> kill(MState), Z = X ; Y == 1 -> kill(MState), Z = X
; Y == Z -> kill(MState), X = Y, queue_goal(X in -1\/1)
; nonvar(X) -> ; nonvar(X) ->
( nonvar(Y) -> ( nonvar(Y) ->
( Y >= 0 -> true ; X =:= -1 ), ( Y >= 0 -> true ; X =:= -1 ),
@@ -5536,7 +5545,7 @@ run_propagator(pexp(X,Y,Z,Morph), MState) -->
fd_put(Z, ZD2, ZPs), fd_put(Z, ZD2, ZPs),
{ ( even(Y), ZU = n(Num) -> { ( even(Y), ZU = n(Num) ->
integer_kth_root_leq(Num, Y, RU), integer_kth_root_leq(Num, Y, RU),
( XL cis_geq n(0), ZL = n(Num1) -> ( XL cis_geq n(0), ZL = n(Num1), Num1 >= 0 ->
integer_kth_root_leq(Num1, Y, RL0), integer_kth_root_leq(Num1, Y, RL0),
( RL0^Y < Num1 -> RL is RL0 + 1 ( RL0^Y < Num1 -> RL is RL0 + 1
; RL = RL0 ; RL = RL0
@@ -5726,8 +5735,7 @@ run_propagator(reified_fd(V,B), MState) -->
B = 1 B = 1
; { B == 0 } -> ; { B == 0 } ->
( { fd_inf(V, inf) } -> [] ( { fd_inf(V, inf) } -> []
; { fd_sup(V, sup) } -> [] ; { fd_sup(V, sup) }
; { false }
) )
; [] ; []
). ).
@@ -6788,13 +6796,20 @@ gcc_pairs([Key-Num0|KNs], Vs, [Key-Num|Rest]) :-
Constraint", AAAI-96 Portland, OR, USA, pp 209--215, 1996 Constraint", AAAI-96 Portland, OR, USA, pp 209--215, 1996
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
gcc_global(Vs, KNs) :- gcc_global(Vs, KNs) -->
gcc_check(KNs), % at this point, all elements of clpz_gcc_vs must be
% previously: call do_queue/0 (now a NOP) here to reach a % variables, which a previously scheduled and called
% fix-point: all elements of clpz_gcc_vs must be variables. We % gcc_check//1 ensures. Note that gcc_check//1 disables the
% must ensure this holds if gcc_check/1 is later rewritten to % queue and accumulates constraints in the queue. Do we need
% actually disable the queue. % to insert a call of do_queue//0 here to reach a fixpoint? I
with_local_attributes(Vs, % think not, because verify_attributes/3 gives each variable
% that is involved in a unification an opportunity to schedule
% its propagators, even if the unifications happen
% simultaneously (such as [A,B] = [0,1], which can happen in
% the propagator of tuples_in/2). Hence: We need this only if
% an example shows it, ideally found by a systematic search
% that can be used to test the implementation.
{ with_local_attributes(Vs,
(gcc_arcs(KNs, S, Vals), (gcc_arcs(KNs, S, Vals),
variables_with_num_occurrences(Vs, VNs), variables_with_num_occurrences(Vs, VNs),
maplist(target_to_v(T), VNs), maplist(target_to_v(T), VNs),
@@ -6805,9 +6820,9 @@ gcc_global(Vs, KNs) :-
gcc_consistent(T), gcc_consistent(T),
scc(Vals, gcc_successors), scc(Vals, gcc_successors),
phrase(gcc_goals(Vals), Gs) phrase(gcc_goals(Vals), Gs)
; Gs = [] )), Gs), ; Gs = [] )), Gs) },
disable_queue, disable_queue,
maplist(call, Gs), neq_nums(Gs),
enable_queue. enable_queue.
gcc_consistent(T) :- gcc_consistent(T) :-
@@ -6834,7 +6849,7 @@ gcc_edge_goal(arc_to(_,_,V,F), Val) -->
get_attr(Val, lowlink, L2), get_attr(Val, lowlink, L2),
L1 =\= L2, L1 =\= L2,
get_attr(Val, value, Value) } -> get_attr(Val, value, Value) } ->
[clpz:neq_num(V, Value)] [neq_num(V, Value)]
; [] ; []
). ).
@@ -7004,7 +7019,7 @@ gcc_succ_edge(arc_from(_,_,V,F)) -->
consistency. consistency.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
gcc_check(Pairs) :- gcc_check(Pairs) -->
disable_queue, disable_queue,
gcc_check_(Pairs), gcc_check_(Pairs),
enable_queue. enable_queue.
@@ -7014,36 +7029,36 @@ gcc_done(Num) :-
del_attr(Num, clpz_gcc_num), del_attr(Num, clpz_gcc_num),
del_attr(Num, clpz_gcc_occurred). del_attr(Num, clpz_gcc_occurred).
gcc_check_([]). gcc_check_([]) --> [].
gcc_check_([Key-Num0|KNs]) :- gcc_check_([Key-Num0|KNs]) -->
( get_attr(Num0, clpz_gcc_vs, Vs) -> ( { get_attr(Num0, clpz_gcc_vs, Vs) } ->
get_attr(Num0, clpz_gcc_num, Num), { get_attr(Num0, clpz_gcc_num, Num),
get_attr(Num0, clpz_gcc_occurred, Occ0), get_attr(Num0, clpz_gcc_occurred, Occ0),
vs_key_min_others(Vs, Key, 0, Min, Os), vs_key_min_others(Vs, Key, 0, Min, Os),
put_attr(Num0, clpz_gcc_vs, Os), put_attr(Num0, clpz_gcc_vs, Os),
put_attr(Num0, clpz_gcc_occurred, Occ1), put_attr(Num0, clpz_gcc_occurred, Occ1),
Occ1 is Occ0 + Min, Occ1 is Occ0 + Min },
geq(Num, Occ1), geq(Num, Occ1),
% The queue is disabled for efficiency here in any case. % The queue is disabled for efficiency here in any case.
% If it were enabled, make sure to retain the invariant % If it were enabled, make sure to retain the invariant
% that gcc_global is never triggered during an % that gcc_global is never triggered during an
% inconsistent state (after gcc_done/1 but before all % inconsistent state (after gcc_done/1 but before all
% relevant constraints are posted). % relevant constraints are posted).
( Occ1 == Num -> all_neq(Os, Key), gcc_done(Num0) ( Occ1 == Num -> all_neq(Os, Key), { gcc_done(Num0) }
; Os == [] -> gcc_done(Num0), Num = Occ1 ; Os == [] -> { gcc_done(Num0) }, Num = Occ1
; length(Os, L), ; { length(Os, L),
Max is Occ1 + L, Max is Occ1 + L },
geq(Max, Num), geq(Max, Num),
( nonvar(Num) -> Diff is Num - Occ1 ( { nonvar(Num) } -> Diff is Num - Occ1
; fd_get(Num, ND, _), ; { fd_get(Num, ND, _),
domain_infimum(ND, n(NInf)), domain_infimum(ND, n(NInf)) },
Diff is NInf - Occ1 Diff is NInf - Occ1
), ),
L >= Diff, L >= Diff,
( L =:= Diff -> ( L =:= Diff ->
Num is Occ1 + Diff, Num is Occ1 + Diff,
maplist(=(Key), Os), { maplist(=(Key), Os),
gcc_done(Num0) gcc_done(Num0) }
; true ; true
) )
) )
@@ -7967,13 +7982,13 @@ coeff_var_term(C-V, T) :- ( C =:= 1 -> T = #V ; T = C * #V ).
Reified predicates for use with predicates from library(reif). Reified predicates for use with predicates from library(reif).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#=(X, Y, T) :- clpz_t(Expr, T) :-
X #= Y #<==> B, Expr #<==> #B,
zo_t(B, T). zo_t(B, T).
#<(X, Y, T) :- #=(X, Y, T) :- clpz_t(X #= Y, T).
X #< Y #<==> B,
zo_t(B, T). #<(X, Y, T) :- clpz_t(X #< Y, T).
zo_t(0, false). zo_t(0, false).
zo_t(1, true). zo_t(1, true).

View File

@@ -1,5 +1,5 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written 2020-2023 by Markus Triska (triska@metalevel.at) Written 2020-2024 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog. Part of Scryer Prolog.
/** Predicates for cryptographic applications. /** Predicates for cryptographic applications.
@@ -25,6 +25,7 @@
crypto_password_hash/3, % +Password, -Hash, +Options crypto_password_hash/3, % +Password, -Hash, +Options
crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options crypto_data_encrypt/6, % +PlainText, +Algorithm, +Key, +IV, -CipherText, +Options
crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options crypto_data_decrypt/6, % +CipherText, +Algorithm, +Key, +IV, -PlainText, +Options
ed25519_seed_keypair/2, % +Seed, -KeyPair
ed25519_new_keypair/1, % -KeyPair ed25519_new_keypair/1, % -KeyPair
ed25519_keypair_public_key/2, % +KeyPair, +PublicKey ed25519_keypair_public_key/2, % +KeyPair, +PublicKey
ed25519_sign/4, % +KeyPair, +Data, -Signature, +Options ed25519_sign/4, % +KeyPair, +Data, -Signature, +Options
@@ -191,6 +192,11 @@ crypto_random_byte(B) :- '$crypto_random_byte'(B).
% The default encoding is `utf8`. The alternative is `octet`, to % The default encoding is `utf8`. The alternative is `octet`, to
% treat the input as a list of raw bytes. % treat the input as a list of raw bytes.
% %
% - `hmac(+Key)`
% Compute a hash-based message authentication code (HMAC) using
% Key, a list of bytes. This option is currently supported for
% algorithms `sha256`, `sha384` and `sha512`.
%
% Example: % Example:
% %
% ``` % ```
@@ -213,9 +219,18 @@ crypto_data_hash(Data0, Hash, Options0) :-
( hash_algorithm(A) -> true ( hash_algorithm(A) -> true
; domain_error(hash_algorithm, A, crypto_data_hash/3) ; domain_error(hash_algorithm, A, crypto_data_hash/3)
), ),
'$crypto_data_hash'(Data, Encoding, HashBytes, A), ( member(HMAC, Options0), nonvar(HMAC), HMAC = hmac(Ks) ->
must_be_bytes(Ks, crypto_data_hash/3),
hmac_algorithm(A),
'$crypto_hmac'(Data, Encoding, Ks, HashBytes, A)
; '$crypto_data_hash'(Data, Encoding, HashBytes, A)
),
hex_bytes(Hash, HashBytes). hex_bytes(Hash, HashBytes).
hmac_algorithm(sha256).
hmac_algorithm(sha384).
hmac_algorithm(sha512).
options_data_chars(Options, Data, Chars, Encoding) :- options_data_chars(Options, Data, Chars, Encoding) :-
option(encoding(Encoding), Options, utf8), option(encoding(Encoding), Options, utf8),
must_be(atom, Encoding), must_be(atom, Encoding),
@@ -612,6 +627,50 @@ encoding_chars(utf8, Cs, Cs) :-
=============================== ===============================
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
%% ed25519_seed_keypair(+Seed, -Pair)
%
% Use Seed to deterministically generate an Ed25519 key pair Pair, a
% list of characters. Seed must be a list of 32 bytes. It can be
% chosen at random (using for example `crypto_n_random_bytes/2`) or
% derived from input keying material (IKM) using for example
% `crypto_data_hkdf/4`. The pair contains the private key and must be
% kept absolutely secret. Pair can be used for signing. Its public
% key can be obtained with `ed25519_keypair_public_key/2`.
ed25519_seed_keypair(Seed, Pair) :-
must_be_bytes(Seed, ed25519_keypair_from_seed/2),
length(Seed, 32),
'$ed25519_seed_to_public_key'(Seed, Public),
maplist(char_code, Public, PublicBytes),
phrase(ed25519_PKCS8v2(Seed,PublicBytes), DERs),
maplist(char_code, Pair, DERs).
% DER (and hence BER) encoding of an Ed25519 private key and
% corresponding public key in PKCS#8v2 format (RFC 5958) as specified
% in RFC 8410.
ed25519_PKCS8v2(Seed, PublicBytes) -->
[0x30,81], % a SEQUENCE of 81 bytes follows
% the publicKey is present, hence we set version to v2
[2,1,1], % the integer 1 denoting version 2 (awesome design!)
% privateKeyAlgorithm: SEQUENCE
[0x30,5], % a SEQUENCE of 5 bytes follows
[6,3], % an OBJECT IDENTIFIER of 3 bytes follows
[43,101,112], % OID of Ed25519
% privateKey: OCTET STRING
[4,34], % an OCTET STRING of 34 bytes follows
[4,32], % an OCTET STRING of 32 bytes follows
seq(Seed), % the seed is the private key
% publicKey: [1] IMPLICIT BIT STRING; context-specific, hence bit 7 set
[0b10000001], % the public key follows
[33], % a BIT STRING of length 33 follows
[0], % 32 bytes is divisible by 8, hence 0 unused bits
seq(PublicBytes).
%% ed25519_new_keypair(-Pair) %% ed25519_new_keypair(-Pair)
% %
% Yields a new Ed25519 key pair Pair, a list of characters. The % Yields a new Ed25519 key pair Pair, a list of characters. The
@@ -620,7 +679,8 @@ encoding_chars(utf8, Cs, Cs) :-
% with `ed25519_keypair_public_key/2`. % with `ed25519_keypair_public_key/2`.
ed25519_new_keypair(Pair) :- ed25519_new_keypair(Pair) :-
'$ed25519_new_keypair'(Pair). crypto_n_random_bytes(32, Bytes),
ed25519_seed_keypair(Bytes, Pair).
%% ed25519_keypair_public_key(+Pair, -PublicKey) %% ed25519_keypair_public_key(+Pair, -PublicKey)
% %
@@ -629,8 +689,11 @@ ed25519_new_keypair(Pair) :-
% The public key is represented as a list of characters. % The public key is represented as a list of characters.
ed25519_keypair_public_key(Pair, PublicKey) :- ed25519_keypair_public_key(Pair, PublicKey) :-
must_be_octet_chars(Pair, ed25519_keypair_public_key), must_be_octet_chars(Pair, ed25519_keypair_public_key/2),
'$ed25519_keypair_public_key'(Pair, PublicKey). reverse(Pair, RPs),
length(RPublicKey, 32),
phrase((seq(RPublicKey),...), RPs),
reverse(RPublicKey, PublicKey).
%% ed25519_sign(+Key, +Data, -Signature, +Options) %% ed25519_sign(+Key, +Data, -Signature, +Options)
% %
@@ -638,10 +701,14 @@ ed25519_keypair_public_key(Pair, PublicKey) :-
% PKCS#8 v2 format as generated by `ed25519_new_keypair/1`. Sign Data % PKCS#8 v2 format as generated by `ed25519_new_keypair/1`. Sign Data
% with Key, yielding Signature as a list of hexadecimal characters. % with Key, yielding Signature as a list of hexadecimal characters.
ed25519_sign(Key, Data0, Signature, Options) :- ed25519_sign(KeyPair, Data0, Signature, Options) :-
must_be_octet_chars(Key, ed25519_sign), must_be_octet_chars(KeyPair, ed25519_sign/4),
length(Prefix, 16),
length(PrivateKeyChars, 32),
phrase((seq(Prefix),seq(PrivateKeyChars),...), KeyPair),
maplist(char_code, PrivateKeyChars, PrivateKey),
options_data_chars(Options, Data0, Data, Encoding), options_data_chars(Options, Data0, Data, Encoding),
'$ed25519_sign'(Key, Data, Encoding, Signature0), '$ed25519_sign_raw'(PrivateKey, Data, Encoding, Signature0),
hex_bytes(Signature, Signature0). hex_bytes(Signature, Signature0).
%% ed25519_verify(+Key, +Data, +Signature, +Options) %% ed25519_verify(+Key, +Data, +Signature, +Options)
@@ -658,10 +725,10 @@ ed25519_sign(Key, Data0, Signature, Options) :-
% which treats Data as a list of raw bytes. % which treats Data as a list of raw bytes.
ed25519_verify(Key, Data0, Signature0, Options) :- ed25519_verify(Key, Data0, Signature0, Options) :-
must_be_octet_chars(Key, ed25519_verify), must_be_octet_chars(Key, ed25519_verify/4),
options_data_chars(Options, Data0, Data, Encoding), options_data_chars(Options, Data0, Data, Encoding),
hex_bytes(Signature0, Signature), hex_bytes(Signature0, Signature),
'$ed25519_verify'(Key, Data, Encoding, Signature). '$ed25519_verify_raw'(Key, Data, Encoding, Signature).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
X25519: ECDH key exchange over Curve25519 X25519: ECDH key exchange over Curve25519
@@ -761,9 +828,26 @@ curve_a(curve(_,_,A,_,_,_,_,_), A).
curve_b(curve(_,_,_,B,_,_,_,_), B). curve_b(curve(_,_,_,B,_,_,_,_), B).
curve_field_length(curve(_,_,_,_,_,_,FieldLength,_), FieldLength). curve_field_length(curve(_,_,_,_,_,_,FieldLength,_), FieldLength).
%% crypto_curve_generator(+Curve, -G)
%
% Yields the generator point G of Curve.
crypto_curve_generator(curve(_,_,_,_,G,_,_,_), G). crypto_curve_generator(curve(_,_,_,_,G,_,_,_), G).
%% crypto_curve_order(+Curve, -Order)
%
% Yields the order of Curve.
crypto_curve_order(curve(_,_,_,_,_,Order,_,_), Order). crypto_curve_order(curve(_,_,_,_,_,Order,_,_), Order).
%% crypto_curve_scalar_mult(+Curve, +Scalar, +Point, -Result)
%
% Computes the point _Result = Scalar*Point_. Scalar must be an
% integer, and Point must be a point on Curve. This operation can be
% used to negotiate a shared secret over a public channel. Consider
% using `curve25519_scalar_mult/3` instead for more desirable
% security properties.
crypto_curve_scalar_mult(Curve, Scalar, point(X,Y), point(RX, RY)) :- crypto_curve_scalar_mult(Curve, Scalar, point(X,Y), point(RX, RY)) :-
must_be(integer, Scalar), must_be(integer, Scalar),
must_be_on_curve(Curve, point(X,Y)), must_be_on_curve(Curve, point(X,Y)),
@@ -830,6 +914,12 @@ fitting_exponent(N, E0, E) :-
fitting_exponent(N, E1, E) fitting_exponent(N, E1, E)
). ).
%% crypto_name_curve(+Name, -Curve)
%
% Yields a representation of the elliptic curve with name Name.
% Currently, the only supported name is `secp256k1`, a Koblitz curve
% regarded as secure.
crypto_name_curve(secp256k1, crypto_name_curve(secp256k1,
curve(secp256k1, curve(secp256k1,
0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f, 0x00fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,

View File

@@ -221,7 +221,7 @@ row([X | Y], Opt) -->
!, !,
( separator(Opt) -> ( separator(Opt) ->
row(Y, Opt) row(Y, Opt)
; end_token -> ; end_token,
{ Y = [] }). { Y = [] }).

View File

@@ -12,9 +12,12 @@ to learn more about them.
[op(1105, xfy, '|'), [op(1105, xfy, '|'),
phrase/2, phrase/2,
phrase/3, phrase/3,
phrase/4,
phrase/5,
seq//1, seq//1,
seqq//1, seqq//1,
... //0 ... //0,
(-->)/2
]). ]).
:- use_module(library(error)). :- use_module(library(error)).
@@ -26,6 +29,10 @@ to learn more about them.
:- meta_predicate phrase(2, ?, ?). :- meta_predicate phrase(2, ?, ?).
:- meta_predicate phrase(2, ?, ?, ?).
:- meta_predicate phrase(2, ?, ?, ?, ?).
%% phrase(+Body, ?Ls). %% phrase(+Body, ?Ls).
% %
% True iff Body describes the list Ls. Body must be a DCG body. % True iff Body describes the list Ls. Body must be a DCG body.
@@ -75,6 +82,34 @@ phrase(GRBody, S0, S) :-
; call(M:GRBody1, S0, S) ; call(M:GRBody1, S0, S)
). ).
phrase(GRBody, Arg, S0, S) :-
strip_module(GRBody, M, GRBody1),
( var(GRBody) ->
instantiation_error(phrase/4)
; nonvar(GRBody1),
GRBody1 =.. GRBodys1,
append(GRBodys1, [Arg], GRBodys2),
GRBody2 =.. GRBodys2,
dcg_constr(GRBody2),
dcg_body(GRBody2, S0, S, GRBody3) ->
call(M:GRBody3)
; call(M:GRBody1, Arg, S0, S)
).
phrase(GRBody, Arg1, Arg2, S0, S) :-
strip_module(GRBody, M, GRBody1),
( var(GRBody) ->
instantiation_error(phrase/5)
; nonvar(GRBody1),
GRBody1 =.. GRBodys1,
append(GRBodys1, [Arg1,Arg2], GRBodys2),
GRBody2 =.. GRBodys2,
dcg_constr(GRBody2),
dcg_body(GRBody2, S0, S, GRBody3) ->
call(M:GRBody3)
; call(M:GRBody1, Arg1, Arg2, S0, S)
).
% The same version of the below two dcg_rule clauses, but with module scoping. % 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),
@@ -101,7 +136,10 @@ dcg_rule(( NonTerminal --> GRBody ), ( Head :- Body )) :-
dcg_non_terminal(NonTerminal, S0, S, Goal) :- dcg_non_terminal(NonTerminal, S0, S, Goal) :-
NonTerminal =.. NonTerminalUniv, NonTerminal =.. NonTerminalUniv,
append(NonTerminalUniv, [S0, S], GoalUniv), append(NonTerminalUniv, [S0, S], GoalUniv),
Goal =.. GoalUniv. ( callable(NonTerminal) ->
Goal =.. GoalUniv
; Goal = NonTerminal % let call/N throw an error instead of throwing one here.
).
dcg_terminals(Terminals, S0, S, S0 = List) :- dcg_terminals(Terminals, S0, S, S0 = List) :-
append(Terminals, S, List). append(Terminals, S, List).
@@ -116,8 +154,6 @@ dcg_body(GRBody, S0, S, Body) :-
dcg_body(NonTerminal, S0, S, Goal1) :- dcg_body(NonTerminal, S0, S, Goal1) :-
nonvar(NonTerminal), nonvar(NonTerminal),
\+ dcg_constr(NonTerminal), \+ dcg_constr(NonTerminal),
NonTerminal \= ( _ -> _ ),
NonTerminal \= ( \+ _ ),
loader:strip_module(NonTerminal, M, NonTerminal0), loader:strip_module(NonTerminal, M, NonTerminal0),
dcg_non_terminal(NonTerminal0, S0, S, Goal0), dcg_non_terminal(NonTerminal0, S0, S, Goal0),
( functor(NonTerminal, (:), 2) -> ( functor(NonTerminal, (:), 2) ->
@@ -135,9 +171,13 @@ dcg_constr(( _'|'_ )). % 7.14.6 - alternative
dcg_constr({_}). % 7.14.7 dcg_constr({_}). % 7.14.7
dcg_constr(call(_)). % 7.14.8 dcg_constr(call(_)). % 7.14.8
dcg_constr(phrase(_)). % 7.14.9 dcg_constr(phrase(_)). % 7.14.9
dcg_constr(phrase(_,_)). % extension of 7.14.9
dcg_constr(phrase(_,_,_)). % extension of 7.14.9
dcg_constr(!). % 7.14.10 dcg_constr(!). % 7.14.10
%% dcg_constr(\+ _). % 7.14.11 - not (existence implementation dep.) dcg_constr(\+ G_0) :- % 7.14.11 - not (existence implementation def.)
dcg_constr((_->_)). % 7.14.12 - if-then (existence implementation dep.) throw(error(representation_error(dcg_body), [culprit- (\+ G_0)])).
dcg_constr((If->Then)) :- % 7.14.12 - if-then (existence implementation def.)
throw(error(representation_error(dcg_body), [culprit- (If->Then)])).
% The principal functor of the first argument indicates % The principal functor of the first argument indicates
% the construct to be expanded. % the construct to be expanded.
@@ -162,8 +202,10 @@ dcg_cbody(( GREither '|' GROr ), S0, S, ( Either ; Or )) :-
dcg_cbody({Goal}, S0, S, ( Goal, S0 = S )). dcg_cbody({Goal}, S0, S, ( Goal, S0 = S )).
dcg_cbody(call(Cont), S0, S, call(Cont, S0, S)). dcg_cbody(call(Cont), S0, S, call(Cont, S0, S)).
dcg_cbody(phrase(Body), S0, S, phrase(Body, S0, S)). dcg_cbody(phrase(Body), S0, S, phrase(Body, S0, S)).
dcg_cbody(phrase(Body, Arg), S0, S, phrase(Body, Arg, S0, S)).
dcg_cbody(phrase(Body, Arg1, Arg2), S0, S, phrase(Body, Arg1, Arg2, S0, S)).
dcg_cbody(!, S0, S, ( !, S0 = S )). dcg_cbody(!, S0, S, ( !, S0 = S )).
dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )). % dcg_cbody(\+ GRBody, S0, S, ( \+ phrase(GRBody,S0,_), S0 = S )).
dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :- dcg_cbody(( GRIf -> GRThen ), S0, S, ( If -> Then )) :-
dcg_body(GRIf, S0, S1, If), dcg_body(GRIf, S0, S1, If),
dcg_body(GRThen, S1, S, Then). dcg_body(GRThen, S1, S, Then).
@@ -202,6 +244,8 @@ seqq([Es|Ess]) --> seq(Es), seqq(Ess).
error_goal(error(E, must_be/2), error(E, must_be/2)). error_goal(error(E, must_be/2), error(E, must_be/2)).
error_goal(error(E, (=..)/2), error(E, (=..)/2)). error_goal(error(E, (=..)/2), error(E, (=..)/2)).
error_goal(error(representation_error(dcg_body), Context),
error(representation_error(dcg_body), Context)).
error_goal(E, _) :- throw(E). error_goal(E, _) :- throw(E).
user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :- user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
@@ -217,3 +261,10 @@ user:goal_expansion(phrase(GRBody, S, S0), GRBody2) :-
). ).
user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])). user:goal_expansion(phrase(GRBody, S), phrase(GRBody, S, [])).
% (-->)/2 behaves as if it didn't exist. We export (and define) it
% only so that clauses for (-->)/2 cannot be asserted when
% library(dcgs) is loaded.
(_-->_) :- throw(error(existence_error(procedure,(-->)/2),(-->)/2)).

View File

@@ -53,5 +53,4 @@ $(G_0) :-
% %
% Generalize away Goal. % Generalize away Goal.
*(_). *(_).

View File

@@ -33,11 +33,13 @@ remove_goal([G0|G0s], Goal0, Goals) :-
vars_remove_goal([], _). vars_remove_goal([], _).
vars_remove_goal([Var|Vars], Goal0) :- vars_remove_goal([Var|Vars], Goal0) :-
get_atts(Var, +dif(Goals0)), ( get_atts(Var, +dif(Goals0)) ->
remove_goal(Goals0, Goal0, Goals), remove_goal(Goals0, Goal0, Goals),
( Goals = [] -> ( Goals = [] ->
put_atts(Var, -dif(_)) put_atts(Var, -dif(_))
; put_atts(Var, +dif(Goals)) ; put_atts(Var, +dif(Goals))
)
; true
), ),
vars_remove_goal(Vars, Goal0). vars_remove_goal(Vars, Goal0).

View File

@@ -178,7 +178,7 @@ directory_must_exist(Directory, Context) :-
; throw(error(existence_error(directory, Directory), Context)) ; throw(error(existence_error(directory, Directory), Context))
). ).
%% workind_directory(Dir0, Dir). %% working_directory(Dir0, Dir).
% %
% Dir0 is the current working directory, and the working directory % Dir0 is the current working directory, and the working directory
% is changed to Dir. % is changed to Dir.

View File

@@ -1,5 +1,5 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Written 2020-2023 by Markus Triska (triska@metalevel.at) Written 2020-2024 by Markus Triska (triska@metalevel.at)
Part of Scryer Prolog. Part of Scryer Prolog.
I place this code in the public domain. Use it in any way you want. I place this code in the public domain. Use it in any way you want.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -302,14 +302,14 @@ format_number_chars(N0, Chars) :-
N is N0, % evaluate compound expression N is N0, % evaluate compound expression
number_chars(N, Chars). number_chars(N, Chars).
n_newlines(0) --> !.
n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N). n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N).
n_newlines(0) --> [].
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- phrase(upto_what(Cs, ~), "abc~test", Rest). ?- phrase(format:upto_what(Cs, ~), "abc~test", Rest).
Cs = [a,b,c], Rest = [~,t,e,s,t]. Cs = "abc", Rest = "~test".
?- phrase(upto_what(Cs, ~), "abc", Rest). ?- phrase(format:upto_what(Cs, ~), "abc", Rest).
Cs = [a,b,c], Rest = []. Cs = "abc", Rest = [].
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
separate_digits_fractional(Arg, Sep, Num, Cs) :- separate_digits_fractional(Arg, Sep, Num, Cs) :-
@@ -444,9 +444,9 @@ format(Stream, Fs, Args) :-
?- phrase(format:cells("~`at~50|", [], 0, [], []), Cs), ?- phrase(format:cells("~`at~50|", [], 0, [], []), Cs),
phrase(format:format_cells(Cs), Ls). phrase(format:format_cells(Cs), Ls).
?- phrase(format:cells("~ta~t~tb~tc~21|", [], 0, [], []), Cs). ?- 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 ...")])] 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). ?- phrase(format:cells("~ta~t~4|", [], 0, [], []), Cs).
Cs = [cell(0,4,[glue(' ',_A),chars("a"),glue(' ',_B)])] 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,1,[glue(a,_94)])), Ls).

View File

@@ -9,6 +9,7 @@ but they're not part of the ISO Prolog standard at the moment.
bb_put/2, bb_put/2,
call_cleanup/2, call_cleanup/2,
call_with_inference_limit/3, call_with_inference_limit/3,
call_residue_vars/2,
forall/2, forall/2,
partial_string/1, partial_string/1,
partial_string/3, partial_string/3,
@@ -18,8 +19,7 @@ but they're not part of the ISO Prolog standard at the moment.
call_nth/2, call_nth/2,
countall/2, countall/2,
copy_term_nat/2, copy_term_nat/2,
asserta/2, copy_term/3]).
assertz/2]).
:- use_module(library(error), [can_be/2, :- use_module(library(error), [can_be/2,
domain_error/3, domain_error/3,
@@ -28,6 +28,8 @@ but they're not part of the ISO Prolog standard at the moment.
:- use_module(library(lists), [maplist/3]). :- use_module(library(lists), [maplist/3]).
:- use_module(library('$project_atts')).
:- meta_predicate(forall(0, 0)). :- meta_predicate(forall(0, 0)).
%% forall(Generate, Test). %% forall(Generate, Test).
@@ -384,21 +386,23 @@ countall(Goal, N) :-
copy_term_nat(Source, Dest) :- copy_term_nat(Source, Dest) :-
'$copy_term_without_attr_vars'(Source, Dest). '$copy_term_without_attr_vars'(Source, Dest).
%% asserta(Module, Rule_Fact). %% copy_term(+Term, -Copy, -Gs).
% %
% Similar to `asserta/1` but allows specifying a Module % Produce a deep copy of Term and unify it to Copy, without attributes.
asserta(Module, (Head :- Body)) :- % Unify Gs with a list of goals that represent the attributes of Term.
!, % Similar to `copy_term/2` but splitting the attributes.
'$asserta'(Module, Head, Body). copy_term(Term, Copy, Gs) :-
asserta(Module, Fact) :- can_be(list, Gs),
'$asserta'(Module, Fact, true). findall(Term-Rs, '$project_atts':term_residual_goals(Term,Rs), [Copy-Gs]),
( var(Gs) ->
Gs = []
; true
).
%% assertz(Module, Rule_Fact). :- meta_predicate call_residue_vars(0, ?).
%
% Similar to `assertz/1` but allows specifying a Module
assertz(Module, (Head :- Body)) :-
!,
'$assertz'(Module, Head, Body).
assertz(Module, Fact) :-
'$assertz'(Module, Fact, true).
call_residue_vars(Goal, Vars) :-
can_be(list, Vars),
'$get_attr_var_queue_delim'(B),
call(Goal),
'$get_attr_var_queue_beyond'(B, Vars).

View File

@@ -23,7 +23,9 @@ finding out the PID of the running system.
unsetenv/1, unsetenv/1,
shell/1, shell/1,
shell/2, shell/2,
pid/1]). pid/1,
raw_argv/1,
argv/1]).
:- use_module(library(error)). :- use_module(library(error)).
:- use_module(library(charsio)). :- use_module(library(charsio)).
@@ -110,3 +112,34 @@ permitted('_').
must_be_chars(Cs) :- must_be_chars(Cs) :-
must_be(list, Cs), must_be(list, Cs),
maplist(must_be(character), Cs). maplist(must_be(character), Cs).
%% raw_argv(-Argv)
%
% True iff Argv is the list of arguments that this program was started with (usually passed via command line).
% In contrast to `argv/1`, this version includes every argument, without any postprocessing, just as the operating
% system reports it to the system. This includes-flags of Scryer itself, which are not needed in general.
raw_argv(Argv) :-
can_be(list, Argv),
'$argv'(Argv).
%% argv(-Argv)
%
% True if Argv is the list of arguments that this program was started with (usually passed via command line).
% In this version, only arguments specific to the program are passed. To differentiate between the system
% arguments and the program arguments, we use `--` as a separator.
%
% Example:
%
% ```
% % Call with scryer-prolog -f -- -t hello
% ?- argv(X).
% X = ["-t", "hello"].
% ```
argv(Argv) :-
can_be(list, Argv),
'$argv'(Argv0),
( append(_, ["--"|Argv1], Argv0) ->
Argv = Argv1
;
Argv = []
).

View File

@@ -37,8 +37,10 @@
atomic_si/1, atomic_si/1,
list_si/1, list_si/1,
character_si/1, character_si/1,
term_si/1,
chars_si/1, chars_si/1,
dif_si/2]). dif_si/2,
when_si/2]).
:- use_module(library(lists)). :- use_module(library(lists)).
@@ -68,6 +70,11 @@ character_si(Ch) :-
atom(Ch), atom(Ch),
atom_length(Ch,1). atom_length(Ch,1).
term_si(Term) :-
( ground(Term) -> acyclic_term(Term)
; throw(error(instantiation_error, term_si/1))
).
chars_si(Chs0) :- chars_si(Chs0) :-
'$skip_max_list'(_,_, Chs0,Chs), '$skip_max_list'(_,_, Chs0,Chs),
( nonvar(Chs) -> Chs == [] ; true ), % fails for infinite lists too ( nonvar(Chs) -> Chs == [] ; true ), % fails for infinite lists too
@@ -92,3 +99,31 @@ dif_si(X, Y) :-
( X \= Y -> true ( X \= Y -> true
; throw(error(instantiation_error,dif_si/2)) ; throw(error(instantiation_error,dif_si/2))
). ).
:- meta_predicate(when_si(+, 0)).
%% when_si(Condition, Goal).
%
% Executes Goal when Condition becomes true. Throws an instantiation error if
% it can't decide.
when_si(Condition, Goal) :-
% Taken from https://stackoverflow.com/a/40449516
( when_condition_si(Condition) ->
( Condition ->
Goal
; throw(error(instantiation_error,when_si/2))
)
; throw(error(domain_error(when_condition_si, Condition),_))
).
when_condition_si(Cond) :-
var(Cond), !, throw(error(instantiation_error,when_condition_si/2)).
when_condition_si(ground(_)).
when_condition_si(nonvar(_)).
when_condition_si((A, B)) :-
when_condition_si(A),
when_condition_si(B).
when_condition_si((A ; B)) :-
when_condition_si(A),
when_condition_si(B).

View File

@@ -96,7 +96,7 @@ sleep(T) :-
:- meta_predicate time(0). :- meta_predicate time(0).
:- dynamic(time_id/1). :- dynamic(time_id/1).
:- dynamic(time_state/2). :- dynamic(time_state/3).
time_next_id(N) :- time_next_id(N) :-
( retract(time_id(N0)) -> ( retract(time_id(N0)) ->
@@ -111,9 +111,9 @@ time_next_id(N) :-
% Reports the execution time of Goal. % Reports the execution time of Goal.
time(Goal) :- time(Goal) :-
'$cpu_now'(T0), cputime_inferences(T0, I0),
time_next_id(ID), time_next_id(ID),
setup_call_cleanup(asserta(time_state(ID, T0)), setup_call_cleanup(asserta(time_state(ID, T0, I0)),
( call_cleanup(catch(Goal, E, (report_time(ID),throw(E))), ( call_cleanup(catch(Goal, E, (report_time(ID),throw(E))),
Det = true), Det = true),
time_true(ID), time_true(ID),
@@ -123,49 +123,72 @@ time(Goal) :-
; report_time(ID), ; report_time(ID),
false false
), ),
retract(time_state(ID, _))). retract(time_state(ID, _, _))).
cputime_inferences(T, I) :-
'$cpu_now'(T),
'$inference_count'(I).
time_true(ID) :- time_true(ID) :-
report_time(ID). report_time(ID).
time_true(ID) :- time_true(ID) :-
% on backtracking, update the stored CPU time for this ID % on backtracking, update the stored CPU time for this ID
retract(time_state(ID, _)), retract(time_state(ID, _, _)),
'$cpu_now'(T0), cputime_inferences(T0, I0),
asserta(time_state(ID, T0)), asserta(time_state(ID, T0, I0)),
false. false.
report_time(ID) :- report_time(ID) :-
time_state(ID, T0), time_state(ID, T0, I0),
'$cpu_now'(T), cputime_inferences(T, I),
Time is T - T0, Time is T - T0,
Inferences0 is I - I0,
% we must subtract the number of inferences that time/1 itself takes;
% this may have to be adapted if the implementation changes,
% so that (for example) true/1 takes exactly 1 inference.
( bb_get('$answer_count', 0) -> ( bb_get('$answer_count', 0) ->
Inferences is Inferences0 - 60,
Pre = " ", Post = "" Pre = " ", Post = ""
; Pre = "", Post = " " ; Inferences is Inferences0 - 9,
Pre = "", Post = " "
), ),
format("~s% CPU time: ~3fs~n~s", [Pre,Time,Post]). phrase((Pre,"% CPU time: ", format_("~3f", [Time]), "s, ",
format_("~U", [Inferences])," inference",s_if_necessary(Inferences),"\n",
Post), Cs),
format("~s", [Cs]).
s_if_necessary(Inferences) -->
{ compare(C, 1, Inferences) },
s_(C).
s_(=) --> "".
s_(<) --> "s".
s_(>) --> " (exception?)".
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- time((true;false)). ?- time((true;false)).
%@ % CPU time: 0.006s % CPU time: 0.000s, 1 inference
%@ true true
%@ ; % CPU time: 0.001s ; % CPU time: 0.000s, 0 inference (exception?)
%@ false. false.
:- time(use_module(library(clpz))). :- time(use_module(library(clpz))).
%@ % CPU time: 3.711s % CPU time: 0.343s, 409_874 inferences
%@ true. true.
:- time(use_module(library(lists))). :- time(use_module(library(lists))).
%@ % CPU time: 0.006s % CPU time: 0.000s, 19 inferences
%@ true. true.
?- time(member(X, "abc")). ?- time(member(X, "abc")).
%@ % CPU time: 0.005s % CPU time: 0.000s, 1 inference
%@ X = a X = a
%@ ; % CPU time: 0.000s ; % CPU time: 0.000s, 3 inferences
%@ X = b X = b
%@ ; % CPU time: 0.000s ; % CPU time: 0.000s, 3 inferences
%@ X = c X = c.
%@ ; % CPU time: 0.000s
%@ false. ?- time((repeat,false)).
% CPU time: 2.726s, 53_330_502 inferences
error('$interrupt_thrown',repl/0).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

29
src/lib/wasm.pl Normal file
View File

@@ -0,0 +1,29 @@
/** Predicates for the WebAssembly platform
This module contains predicates that are only available in
the WASM (WebAssembly) version of Scryer Prolog.
*/
:- module(wasm, [js_eval/2]).
:- use_module(library(error)).
%% js_eval(+JsCode, -Result).
%
% Executes a JavaScript snippet `JsCode` using the platform
% `eval` function. `Result` takes the return value of that code.
% Strings, booleans, numbers, null and undefined are directly mapped to Prolog.
% Arrays, objects, bigints, symbols and functions are not mapped.
% Instead, a `js_{type}` atom will be returned.
%
% Example (on a browser):
%
% ```
% ?- js_eval("prompt('What is your name?')", Name).
% % A prompt is showed, with a textbox.
% Name = "Whatever was written on the textbox".
% ```
js_eval(JsCode, Result) :-
must_be(chars, JsCode),
can_be(chars, Result),
'$js_eval'(JsCode, Result).

106
src/lib/when.pl Normal file
View File

@@ -0,0 +1,106 @@
/**
Provides the predicate `when/2`.
*/
:- module(when, [when/2]).
:- use_module(library(atts)).
:- use_module(library(dcgs)).
:- use_module(library(lists)).
:- use_module(library(lambda)).
:- use_module(library(format)).
:- use_module(library(debug)).
:- attribute when_list/1.
:- meta_predicate(when(+, 0)).
%% when(Condition, Goal).
%
% Executes Goal when Condition becomes true.
when(Condition, Goal) :-
( when_condition(Condition) ->
( Condition ->
Goal
; term_variables(Condition, Vars),
maplist(
[Goal, Condition]+\Var^(
get_atts(Var, when_list(Whens0)) ->
Whens = [when(Condition, Goal) | Whens0],
put_atts(Var, when_list(Whens))
; put_atts(Var, when_list([when(Condition, Goal)]))
),
Vars
)
)
; throw(error(domain_error(when_condition, Condition),_))
).
when_condition(Cond) :-
% Should this be delayed?
var(Cond), !, throw(error(instantiation_error,when_condition/1)).
when_condition(ground(_)).
when_condition(nonvar(_)).
when_condition((A, B)) :-
when_condition(A),
when_condition(B).
when_condition((A ; B)) :-
when_condition(A),
when_condition(B).
remove_goal([], _, []).
remove_goal([G0|G0s], Goal, Goals) :-
( G0 == Goal ->
remove_goal(G0s, Goal, Goals)
; Goals = [G0|Goals1],
remove_goal(G0s, Goal, Goals1)
).
vars_remove_goal(Vars, Goal) :-
maplist(
Goal+\Var^(
get_atts(Var, when_list(Whens0)) ->
remove_goal(Whens0, Goal, Whens),
( Whens = [] ->
put_atts(Var, -when_list(_))
; put_atts(Var, when_list(Whens))
)
; true
),
Vars
).
reinforce_goal(Goal0, Goal) :-
Goal = (
term_variables(Goal0, Vars),
when:vars_remove_goal(Vars, Goal0),
Goal0
).
verify_attributes(Var, Value, Goals) :-
( get_atts(Var, when_list(Whens)) ->
( var(Value) ->
( get_atts(Value, when_list(WhensValue)) ->
append(Whens, WhensValue, WhensNew),
put_atts(Value, when_list(WhensNew))
; put_atts(Value, when_list(Whens))
),
Goals = []
; maplist(reinforce_goal, Whens, Goals)
)
; Goals = []
).
gather_when_goals([], _) --> [].
gather_when_goals([When|Whens], Var) -->
( { term_variables(When, [V0|_]), Var == V0 } ->
[when:When]
; []
),
gather_when_goals(Whens, Var).
attribute_goals(Var) -->
{ get_atts(Var, when_list(Whens)) },
gather_when_goals(Whens, Var),
{ put_atts(Var, -when_list(_)) }.

View File

@@ -112,7 +112,7 @@ success_or_warning(Goal) :-
( call(Goal) -> ( call(Goal) ->
true true
; %% initialization goals can fail without thwarting the load. ; %% initialization goals can fail without thwarting the load.
write('Warning: initialization/1 failed for: '), write('% Warning: initialization/1 failed for: '),
writeq(Goal), writeq(Goal),
nl nl
). ).
@@ -138,7 +138,7 @@ file_load_cleanup(Evacuable, Error) :-
load_context(Module), load_context(Module),
abolish(Module:'$initialization_goals'/1), abolish(Module:'$initialization_goals'/1),
unload_evacuable(Evacuable), unload_evacuable(Evacuable),
( clause('$toplevel':argv(_), _) -> ( clause('$toplevel':started, _) ->
% let the toplevel call loader:write_error/1 % let the toplevel call loader:write_error/1
throw(Error) throw(Error)
; '$print_message_and_fail'(Error) ; '$print_message_and_fail'(Error)
@@ -188,7 +188,7 @@ warn_about_singletons([], _).
warn_about_singletons([Singleton|Singletons], LinesRead) :- warn_about_singletons([Singleton|Singletons], LinesRead) :-
( filter_anonymous_vars([Singleton|Singletons], VarEqs), ( filter_anonymous_vars([Singleton|Singletons], VarEqs),
VarEqs \== [] -> VarEqs \== [] ->
write('Warning: singleton variables '), write('% Warning: singleton variables '),
print_comma_separated_list(VarEqs), print_comma_separated_list(VarEqs),
write(' at line '), write(' at line '),
write(LinesRead), write(LinesRead),
@@ -231,14 +231,17 @@ complete_partial_goal(N, HeadArg, InnerHeadArgs, SuppArgs, CompleteHeadArg) :-
integer(N), integer(N),
N >= 0, N >= 0,
HeadArg =.. [Functor | InnerHeadArgs], HeadArg =.. [Functor | InnerHeadArgs],
% the next two lines are equivalent to length(SuppArgs, N) but ( callable(Functor) ->
% avoid length/2 so that copy_term/3 (which is invoked by % the next two lines are equivalent to length(SuppArgs, N) but
% length/2) can be bootstrapped without self-reference. % avoid length/2 so that copy_term/3 (which is invoked by
functor(SuppArgsFunctor, '.', N), % length/2) can be bootstrapped without self-reference.
SuppArgsFunctor =.. [_ | SuppArgs], functor(SuppArgsFunctor, '.', N),
% length(SuppArgs, N), SuppArgsFunctor =.. [_ | SuppArgs],
append(InnerHeadArgs, SuppArgs, InnerHeadArgs0), % length(SuppArgs, N),
CompleteHeadArg =.. [Functor | InnerHeadArgs0]. append(InnerHeadArgs, SuppArgs, InnerHeadArgs0),
CompleteHeadArg =.. [Functor | InnerHeadArgs0]
; type_error(callable, Functor, _)
).
inner_meta_specs(0, HeadArg, InnerHeadArgs, InnerMetaSpecs) :- inner_meta_specs(0, HeadArg, InnerHeadArgs, InnerMetaSpecs) :-
!, !,
@@ -283,7 +286,7 @@ module_expanded_head_variables(Head, HeadVars) :-
print_goal_expansion_warning(Pred) :- print_goal_expansion_warning(Pred) :-
nl, nl,
write('Warning: clause body goal expansion failed because '), write('% Warning: clause body goal expansion failed because '),
writeq(Pred), writeq(Pred),
write(' is not callable.'), write(' is not callable.'),
nl. nl.
@@ -296,7 +299,7 @@ expand_term_goals(Terms0, Terms) :-
( atom(Module) -> ( atom(Module) ->
prolog_load_context(module, Target), prolog_load_context(module, Target),
module_expanded_head_variables(Head2, HeadVars), module_expanded_head_variables(Head2, HeadVars),
catch(expand_goal(Body0, Target, Body1, HeadVars), catch(expand_goal(Body0, Target, Body1, HeadVars, []),
error(type_error(callable, Pred), _), error(type_error(callable, Pred), _),
( loader:print_goal_expansion_warning(Pred), ( loader:print_goal_expansion_warning(Pred),
builtins:(Body1 = Body0) builtins:(Body1 = Body0)
@@ -306,7 +309,7 @@ expand_term_goals(Terms0, Terms) :-
) )
; module_expanded_head_variables(Head1, HeadVars), ; module_expanded_head_variables(Head1, HeadVars),
prolog_load_context(module, Target), prolog_load_context(module, Target),
catch(expand_goal(Body0, Target, Body1, HeadVars), catch(expand_goal(Body0, Target, Body1, HeadVars, []),
error(type_error(callable, Pred), _), error(type_error(callable, Pred), _),
( loader:print_goal_expansion_warning(Pred), ( loader:print_goal_expansion_warning(Pred),
builtins:(Body1 = Body0) builtins:(Body1 = Body0)
@@ -726,9 +729,9 @@ subgoal_expansion(Goal, Module, ExpandedGoal) :-
). ).
:- non_counted_backtracking expand_subgoal/5. :- non_counted_backtracking expand_subgoal/6.
expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars) :- expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars, TGs) :-
strip_subst_module(UnexpandedGoals, M, Module, UnexpandedGoals0), strip_subst_module(UnexpandedGoals, M, Module, UnexpandedGoals0),
nonvar(UnexpandedGoals0), nonvar(UnexpandedGoals0),
complete_partial_goal(MS, UnexpandedGoals0, _, SuppArgs, UnexpandedGoals1), complete_partial_goal(MS, UnexpandedGoals0, _, SuppArgs, UnexpandedGoals1),
@@ -740,7 +743,7 @@ expand_subgoal(UnexpandedGoals, MS, M, ExpandedGoals, HeadVars) :-
), ),
strip_subst_module(UnexpandedGoals3, Module, Module1, UnexpandedGoals4), strip_subst_module(UnexpandedGoals3, Module, Module1, UnexpandedGoals4),
( inner_meta_specs(0, UnexpandedGoals4, _, MetaSpecs) -> ( inner_meta_specs(0, UnexpandedGoals4, _, MetaSpecs) ->
expand_module_names(UnexpandedGoals4, MetaSpecs, Module1, ExpandedGoals0, HeadVars) expand_module_names(UnexpandedGoals4, MetaSpecs, Module1, ExpandedGoals0, HeadVars, TGs)
; ExpandedGoals0 = UnexpandedGoals4 ; ExpandedGoals0 = UnexpandedGoals4
), ),
'$compile_inline_or_expanded_goal'(ExpandedGoals0, SuppArgs, ExpandedGoals1, Module1, UnexpandedGoals0), '$compile_inline_or_expanded_goal'(ExpandedGoals0, SuppArgs, ExpandedGoals1, Module1, UnexpandedGoals0),
@@ -769,10 +772,10 @@ expand_module_name(ESG0, MS, M, ESG) :-
:- non_counted_backtracking eq_member/2. :- non_counted_backtracking eq_member/2.
eq_member(V, [L-_|Ls]) :- eq_member(V-M, [L-M|Ls]) :-
V == L. V == L.
eq_member(V, [_|Ls]) :- eq_member(V-M, [_|Ls]) :-
eq_member(V, Ls). eq_member(V-M, Ls).
:- non_counted_backtracking qualified_spec/1. :- non_counted_backtracking qualified_spec/1.
@@ -782,11 +785,19 @@ qualified_spec(MS) :- integer(MS), MS >= 0.
:- non_counted_backtracking expand_meta_predicate_subgoals/5. :- non_counted_backtracking expand_meta_predicate_subgoals/5.
expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars) :- expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars, TGs) :-
( var(SG) -> ( var(SG) ->
( qualified_spec(MS) -> ( qualified_spec(MS) ->
( eq_member(SG, HeadVars) -> ( eq_member(SG-_, HeadVars) ->
ESG = SG ESG = SG
; eq_member(SG-TG, TGs),
% transitive goals come about from previous equalities:
% if SG was bound by (=)/2 to a potential goal TG earlier
% in the goal sequence, expand TG and substitute SG with it
% in this subgoal context. the binding to SG must not be
% changed.
expand_subgoal(TG, MS, M, ESG, HeadVars, TGs) ->
true
; expand_module_name(SG, MS, M, ESG) ; expand_module_name(SG, MS, M, ESG)
) )
; ESG = SG ; ESG = SG
@@ -795,26 +806,26 @@ expand_meta_predicate_subgoals([SG | SGs], [MS | MSs], M, [ESG | ESGs], HeadVars
expand_module_name(SG, MS, M, ESG) expand_module_name(SG, MS, M, ESG)
; '$is_expanded_or_inlined'(SG) -> ; '$is_expanded_or_inlined'(SG) ->
ESG = SG ESG = SG
; expand_subgoal(SG, MS, M, ESG, HeadVars) -> ; expand_subgoal(SG, MS, M, ESG, HeadVars, TGs) ->
true true
; integer(MS), ; integer(MS),
MS >= 0 -> MS >= 0 ->
expand_module_name(SG, MS, M, ESG) expand_module_name(SG, MS, M, ESG)
; SG = ESG ; SG = ESG
), ),
expand_meta_predicate_subgoals(SGs, MSs, M, ESGs, HeadVars). expand_meta_predicate_subgoals(SGs, MSs, M, ESGs, HeadVars, TGs).
expand_meta_predicate_subgoals([], _, _, [], _). expand_meta_predicate_subgoals([], _, _, [], _, _).
:- non_counted_backtracking expand_module_names/5. :- non_counted_backtracking expand_module_names/6.
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) :- expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars, TGs) :-
Goals =.. [GoalFunctor | SubGoals], Goals =.. [GoalFunctor | SubGoals],
( GoalFunctor == (:), ( GoalFunctor == (:),
SubGoals = [M, SubGoal] -> SubGoals = [M, SubGoal] ->
expand_module_names(SubGoal, MetaSpecs, M, ExpandedSubGoal, HeadVars), expand_module_names(SubGoal, MetaSpecs, M, ExpandedSubGoal, HeadVars, TGs),
expand_module_name(ExpandedSubGoal, 0, M, ExpandedGoals) expand_module_name(ExpandedSubGoal, 0, M, ExpandedGoals)
; expand_meta_predicate_subgoals(SubGoals, MetaSpecs, Module, ExpandedGoalList, HeadVars), ; expand_meta_predicate_subgoals(SubGoals, MetaSpecs, Module, ExpandedGoalList, HeadVars, TGs),
ExpandedGoals =.. [GoalFunctor | ExpandedGoalList] ExpandedGoals =.. [GoalFunctor | ExpandedGoalList]
). ).
@@ -822,26 +833,26 @@ expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) :-
:- non_counted_backtracking expand_goal/3. :- non_counted_backtracking expand_goal/3.
expand_goal(UnexpandedGoals, Module, ExpandedGoals) :- expand_goal(UnexpandedGoals, Module, ExpandedGoals) :-
catch(loader:expand_goal(UnexpandedGoals, Module, ExpandedGoals, []), catch(loader:expand_goal(UnexpandedGoals, Module, ExpandedGoals, [], []),
error(type_error(callable, _), _), error(type_error(callable, _), _),
UnexpandedGoals = ExpandedGoals), UnexpandedGoals = ExpandedGoals),
!. !.
:- non_counted_backtracking expand_goal/4. :- non_counted_backtracking expand_goal/5.
expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars) :- expand_goal(UnexpandedGoals, Module, ExpandedGoals, HeadVars, TGs) :-
( var(UnexpandedGoals) -> ( var(UnexpandedGoals) ->
expand_module_names(call(UnexpandedGoals), [0], Module, ExpandedGoals, HeadVars) expand_module_names(call(UnexpandedGoals), [0], Module, ExpandedGoals, HeadVars, TGs)
; goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1), ; goal_expansion(UnexpandedGoals, Module, UnexpandedGoals1),
( Module \== user -> ( Module \== user ->
goal_expansion(UnexpandedGoals1, user, Goals) goal_expansion(UnexpandedGoals1, user, Goals)
; Goals = UnexpandedGoals1 ; Goals = UnexpandedGoals1
), ),
( expand_goal_cases(Goals, Module, ExpandedGoals, HeadVars) -> ( expand_goal_cases(Goals, Module, ExpandedGoals, HeadVars, TGs) ->
true true
; predicate_property(Module:Goals, meta_predicate(MetaSpecs0)), ; predicate_property(Module:Goals, meta_predicate(MetaSpecs0)),
MetaSpecs0 =.. [_ | MetaSpecs] -> MetaSpecs0 =.. [_ | MetaSpecs] ->
expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars) expand_module_names(Goals, MetaSpecs, Module, ExpandedGoals, HeadVars, TGs)
; thread_goals(Goals, ExpandedGoals, (',')) ; thread_goals(Goals, ExpandedGoals, (','))
; Goals = ExpandedGoals ; Goals = ExpandedGoals
) )
@@ -874,28 +885,40 @@ expand_call_goal_(UnexpandedGoals, Module, ExpandedGoals) :-
) )
). ).
:- non_counted_backtracking expand_goal_cases/4. :- non_counted_backtracking transitive_goal/3.
expand_goal_cases((Goal0, Goals0), Module, ExpandedGoals, HeadVars) :- transitive_goal(G, TGs0, TGs1) :-
( expand_goal(Goal0, Module, Goal1, HeadVars) -> ( G = (G1 = PotentialGoal),
expand_goal(Goals0, Module, Goals1, HeadVars), callable(PotentialGoal),
subsumes_term(G1, PotentialGoal) ->
TGs1 = [G1-PotentialGoal|TGs0]
; TGs1 = TGs0
).
:- non_counted_backtracking expand_goal_cases/5.
expand_goal_cases((Goal0, Goals0), Module, ExpandedGoals, HeadVars, TGs) :-
( expand_goal(Goal0, Module, Goal1, HeadVars, TGs) ->
transitive_goal(Goal0, TGs, TGs1),
expand_goal(Goals0, Module, Goals1, HeadVars, TGs1),
thread_goals(Goal1, ExpandedGoals, Goals1, (',')) thread_goals(Goal1, ExpandedGoals, Goals1, (','))
; expand_goal(Goals0, Module, Goals1, HeadVars), ; expand_goal(Goals0, Module, Goals1, HeadVars, TGs),
ExpandedGoals = (Goal0, Goals1) ExpandedGoals = (Goal0, Goals1)
). ).
expand_goal_cases((Goals0 -> Goals1), Module, ExpandedGoals, HeadVars) :- expand_goal_cases((Goals0 -> Goals1), Module, ExpandedGoals, HeadVars, TGs) :-
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars), expand_goal(Goals0, Module, ExpandedGoals0, HeadVars, TGs),
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars), transitive_goal(ExpandedGoals0, TGs, TGs1),
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars, TGs1),
ExpandedGoals = (ExpandedGoals0 -> ExpandedGoals1). ExpandedGoals = (ExpandedGoals0 -> ExpandedGoals1).
expand_goal_cases((Goals0 ; Goals1), Module, ExpandedGoals, HeadVars) :- expand_goal_cases((Goals0 ; Goals1), Module, ExpandedGoals, HeadVars, TGs) :-
expand_goal(Goals0, Module, ExpandedGoals0, HeadVars), expand_goal(Goals0, Module, ExpandedGoals0, HeadVars, TGs),
expand_goal(Goals1, Module, ExpandedGoals1, HeadVars), expand_goal(Goals1, Module, ExpandedGoals1, HeadVars, TGs),
ExpandedGoals = (ExpandedGoals0 ; ExpandedGoals1). ExpandedGoals = (ExpandedGoals0 ; ExpandedGoals1).
expand_goal_cases((\+ Goals0), Module, ExpandedGoals, HeadVars) :- expand_goal_cases((\+ Goals0), Module, ExpandedGoals, HeadVars, TGs) :-
expand_goal(Goals0, Module, Goals1, HeadVars), expand_goal(Goals0, Module, Goals1, HeadVars, TGs),
ExpandedGoals = (\+ Goals1). ExpandedGoals = (\+ Goals1).
expand_goal_cases((Module:Goals0), _, ExpandedGoals, HeadVars) :- expand_goal_cases((Module:Goals0), _, ExpandedGoals, HeadVars, TGs) :-
expand_goal(Goals0, Module, Goals1, HeadVars), expand_goal(Goals0, Module, Goals1, HeadVars, TGs),
ExpandedGoals = (Module:Goals1). ExpandedGoals = (Module:Goals1).
:- non_counted_backtracking thread_goals/3. :- non_counted_backtracking thread_goals/3.

View File

@@ -14,3 +14,9 @@ impl MachineArgs {
} }
} }
} }
impl Default for MachineArgs {
fn default() -> Self {
Self::new()
}
}

View File

@@ -1,6 +1,6 @@
use dashu::base::{Abs, Gcd, Signed, UnsignedAbs}; use dashu::base::{Abs, Gcd, Signed, UnsignedAbs};
use dashu::integer::IBig;
use dashu::integer::fast_div::ConstDivisor; use dashu::integer::fast_div::ConstDivisor;
use dashu::integer::IBig;
use divrem::*; use divrem::*;
use num_order::NumOrd; use num_order::NumOrd;
@@ -84,18 +84,18 @@ fn numerical_type_error(
fn isize_gcd(n1: isize, n2: isize) -> Option<isize> { fn isize_gcd(n1: isize, n2: isize) -> Option<isize> {
if n1 == 0 { if n1 == 0 {
return n2.checked_abs().map(|n| n as isize); return n2.checked_abs();
} }
if n2 == 0 { if n2 == 0 {
return n1.checked_abs().map(|n| n as isize); return n1.checked_abs();
} }
let n1 = n1.checked_abs(); let n1 = n1.checked_abs();
let n2 = n2.checked_abs(); let n2 = n2.checked_abs();
let mut n1 = if let Some(n1) = n1 { n1 } else { return None }; let mut n1 = n1?;
let mut n2 = if let Some(n2) = n2 { n2 } else { return None }; let mut n2 = n2?;
let mut shift = 0; let mut shift = 0;
@@ -115,9 +115,7 @@ fn isize_gcd(n1: isize, n2: isize) -> Option<isize> {
} }
if n1 > n2 { if n1 > n2 {
let t = n2; std::mem::swap(&mut n2, &mut n1);
n2 = n1;
n1 = t;
} }
n2 -= n1; n2 -= n1;
@@ -350,22 +348,18 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
(Number::Fixnum(n1), Number::Integer(n2)) => { (Number::Fixnum(n1), Number::Integer(n2)) => {
let n1_i = n1.get_num(); let n1_i = n1.get_num();
if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && &*n2 < &Integer::from(0) { if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && n2.is_negative() {
let n = Number::Fixnum(n1); let n = Number::Fixnum(n1);
Err(numerical_type_error(ValidType::Float, n, stub_gen)) Err(numerical_type_error(ValidType::Float, n, stub_gen))
} else { } else {
let n1 = Integer::from(n1_i); let n1 = Integer::from(n1_i);
Ok(Number::arena_from(binary_pow(n1, &*n2), arena)) Ok(Number::arena_from(binary_pow(n1, &n2), arena))
} }
} }
(Number::Integer(n1), Number::Fixnum(n2)) => { (Number::Integer(n1), Number::Fixnum(n2)) => {
let n2_i = n2.get_num(); let n2_i = n2.get_num();
if !(&*n1 == &Integer::from(1) if !(n1.is_one() || n1.is_zero() || n1.num_eq(&-1)) && n2_i < 0 {
|| &*n1 == &Integer::from(0)
|| &*n1 == &Integer::from(-1))
&& n2_i < 0
{
let n = Number::Integer(n1); let n = Number::Integer(n1);
Err(numerical_type_error(ValidType::Float, n, stub_gen)) Err(numerical_type_error(ValidType::Float, n, stub_gen))
} else { } else {
@@ -374,15 +368,11 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
} }
} }
(Number::Integer(n1), Number::Integer(n2)) => { (Number::Integer(n1), Number::Integer(n2)) => {
if !(&*n1 == &Integer::from(1) if !(n1.is_one() || n1.is_zero() || n1.num_eq(&-1)) && n2.is_negative() {
|| &*n1 == &Integer::from(0)
|| &*n1 == &Integer::from(-1))
&& &*n2 < &Integer::from(0)
{
let n = Number::Integer(n1); let n = Number::Integer(n1);
Err(numerical_type_error(ValidType::Float, n, stub_gen)) Err(numerical_type_error(ValidType::Float, n, stub_gen))
} else { } else {
Ok(Number::arena_from(binary_pow((*n1).clone(), &*n2), arena)) Ok(Number::arena_from(binary_pow((*n1).clone(), &n2), arena))
} }
} }
(n1, Number::Integer(n2)) => { (n1, Number::Integer(n2)) => {
@@ -455,14 +445,14 @@ pub(crate) fn max(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
} }
} }
(Number::Fixnum(n1), Number::Integer(n2)) => { (Number::Fixnum(n1), Number::Integer(n2)) => {
if (&*n2).num_gt(&n1.get_num()) { if (*n2).num_gt(&n1.get_num()) {
Ok(Number::Integer(n2)) Ok(Number::Integer(n2))
} else { } else {
Ok(Number::Fixnum(n1)) Ok(Number::Fixnum(n1))
} }
} }
(Number::Integer(n1), Number::Fixnum(n2)) => { (Number::Integer(n1), Number::Fixnum(n2)) => {
if (&*n1).num_gt(&n2.get_num()) { if (*n1).num_gt(&n2.get_num()) {
Ok(Number::Integer(n1)) Ok(Number::Integer(n1))
} else { } else {
Ok(Number::Fixnum(n2)) Ok(Number::Fixnum(n2))
@@ -499,14 +489,14 @@ pub(crate) fn min(n1: Number, n2: Number) -> Result<Number, MachineStubGen> {
} }
} }
(Number::Fixnum(n1), Number::Integer(n2)) => { (Number::Fixnum(n1), Number::Integer(n2)) => {
if (&*n2).num_lt(&n1.get_num()) { if (*n2).num_lt(&n1.get_num()) {
Ok(Number::Integer(n2)) Ok(Number::Integer(n2))
} else { } else {
Ok(Number::Fixnum(n1)) Ok(Number::Fixnum(n1))
} }
} }
(Number::Integer(n1), Number::Fixnum(n2)) => { (Number::Integer(n1), Number::Fixnum(n2)) => {
if (&*n1).num_lt(&n2.get_num()) { if (*n1).num_lt(&n2.get_num()) {
Ok(Number::Integer(n1)) Ok(Number::Integer(n1))
} else { } else {
Ok(Number::Fixnum(n2)) Ok(Number::Fixnum(n2))
@@ -583,15 +573,13 @@ pub(crate) fn idiv(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number,
(Number::Fixnum(n1), Number::Fixnum(n2)) => { (Number::Fixnum(n1), Number::Fixnum(n2)) => {
if n2.get_num() == 0 { if n2.get_num() == 0 {
Err(zero_divisor_eval_error(stub_gen)) Err(zero_divisor_eval_error(stub_gen))
} else if let Some(result) = n1.get_num().checked_div(n2.get_num()) {
Ok(Number::arena_from(result, arena))
} else { } else {
if let Some(result) = n1.get_num().checked_div(n2.get_num()) { let n1 = Integer::from(n1.get_num());
Ok(Number::arena_from(result, arena)) let n2 = Integer::from(n2.get_num());
} else {
let n1 = Integer::from(n1.get_num());
let n2 = Integer::from(n2.get_num());
Ok(Number::arena_from(n1 / n2, arena)) Ok(Number::arena_from(n1 / n2, arena))
}
} }
} }
(Number::Fixnum(n1), Number::Integer(n2)) => { (Number::Fixnum(n1), Number::Integer(n2)) => {
@@ -656,9 +644,9 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
let n1 = Integer::from(n1_i); let n1 = Integer::from(n1_i);
if let Ok(n2) = usize::try_from(n2_i) { if let Ok(n2) = usize::try_from(n2_i) {
return Ok(Number::arena_from(n1 >> n2, arena)); Ok(Number::arena_from(n1 >> n2, arena))
} else { } else {
return Ok(Number::arena_from(n1 >> usize::max_value(), arena)); Ok(Number::arena_from(n1 >> usize::max_value(), arena))
} }
} }
(Number::Fixnum(n1), Number::Integer(n2)) => { (Number::Fixnum(n1), Number::Integer(n2)) => {
@@ -667,12 +655,8 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
let result: Result<usize, _> = (&*n2).try_into(); let result: Result<usize, _> = (&*n2).try_into();
match result { match result {
Ok(n2) => { Ok(n2) => Ok(Number::arena_from(n1 >> n2, arena)),
Ok(Number::arena_from(n1 >> n2, arena)) Err(_) => Ok(Number::arena_from(n1 >> usize::max_value(), arena)),
}
Err(_) => {
Ok(Number::arena_from(n1 >> usize::max_value(), arena))
}
} }
} }
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) { (Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
@@ -686,14 +670,13 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
let result: Result<usize, _> = (&*n2).try_into(); let result: Result<usize, _> = (&*n2).try_into();
match result { match result {
Ok(n2) => { Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)) Err(_) => Ok(Number::arena_from(
} Integer::from(&*n1 >> usize::max_value()),
Err(_) => { arena,
Ok(Number::arena_from(Integer::from(&*n1 >> usize::max_value()), arena)) )),
}
} }
}, }
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)), (Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)), (Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
(n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)), (n1, _) => Err(numerical_type_error(ValidType::Integer, n1, stub_gen)),
@@ -718,22 +701,17 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
let n1 = Integer::from(n1_i); let n1 = Integer::from(n1_i);
if let Ok(n2) = usize::try_from(n2_i) { if let Ok(n2) = usize::try_from(n2_i) {
return Ok(Number::arena_from(n1 << n2, arena)); Ok(Number::arena_from(n1 << n2, arena))
} else { } else {
return Ok(Number::arena_from(n1 << usize::max_value(), arena)); Ok(Number::arena_from(n1 << usize::max_value(), arena))
} }
} }
(Number::Fixnum(n1), Number::Integer(n2)) => { (Number::Fixnum(n1), Number::Integer(n2)) => {
let n1 = Integer::from(n1.get_num()); let n1 = Integer::from(n1.get_num());
match (&*n2).try_into() as Result<u32, _> { match (&*n2).try_into() as Result<usize, _> {
Ok(n2) => { Ok(n2) => Ok(Number::arena_from(n1 << n2, arena)),
let n1: u64 = n1.try_into().unwrap(); _ => Ok(Number::arena_from(n1 << usize::max_value(), arena)),
Ok(Number::arena_from(n1 << n2, arena))
},
_ => {
Ok(Number::arena_from(n1 << usize::max_value(), arena))
}
} }
} }
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) { (Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
@@ -743,14 +721,12 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
arena, arena,
)), )),
}, },
(Number::Integer(n1), Number::Integer(n2)) => match (&*n2).try_into() as Result<u32, _> { (Number::Integer(n1), Number::Integer(n2)) => match (&*n2).try_into() as Result<usize, _> {
Ok(n2) => { Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
let n1: u64 = (&*n1).try_into().unwrap(); _ => Ok(Number::arena_from(
Ok(Number::arena_from(Integer::from(n1 << n2), arena)) Integer::from(&*n1 << usize::max_value()),
}, arena,
_ => { )),
Ok(Number::arena_from(Integer::from(&*n1 << usize::max_value()),arena))
}
}, },
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)), (Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)), (Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
@@ -882,7 +858,7 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result<Number,
Err(zero_divisor_eval_error(stub_gen)) Err(zero_divisor_eval_error(stub_gen))
} else { } else {
let n1 = Integer::from(n1.get_num()); let n1 = Integer::from(n1.get_num());
Ok(Number::arena_from(ibig_rem_floor(&n1, &*n2), arena)) Ok(Number::arena_from(ibig_rem_floor(&n1, &n2), arena))
} }
} }
(Number::Integer(n1), Number::Fixnum(n2)) => { (Number::Integer(n1), Number::Fixnum(n2)) => {
@@ -892,14 +868,14 @@ pub(crate) fn modulus(x: Number, y: Number, arena: &mut Arena) -> Result<Number,
Err(zero_divisor_eval_error(stub_gen)) Err(zero_divisor_eval_error(stub_gen))
} else { } else {
let n2 = Integer::from(n2_i); let n2 = Integer::from(n2_i);
Ok(Number::arena_from(ibig_rem_floor(&*n1, &n2), arena)) Ok(Number::arena_from(ibig_rem_floor(&n1, &n2), arena))
} }
} }
(Number::Integer(n1), Number::Integer(n2)) => { (Number::Integer(n1), Number::Integer(n2)) => {
if n2.is_zero() { if n2.is_zero() {
Err(zero_divisor_eval_error(stub_gen)) Err(zero_divisor_eval_error(stub_gen))
} else { } else {
Ok(Number::arena_from(ibig_rem_floor(&*n1, &*n2), arena)) Ok(Number::arena_from(ibig_rem_floor(&n1, &n2), arena))
} }
} }
(Number::Integer(_), n2) | (Number::Fixnum(_), n2) => { (Number::Integer(_), n2) | (Number::Fixnum(_), n2) => {
@@ -1145,7 +1121,7 @@ impl MachineState {
&mut self.interms[i - 1], &mut self.interms[i - 1],
Number::Fixnum(Fixnum::build_with(0)), Number::Fixnum(Fixnum::build_with(0)),
)), )),
&ArithmeticTerm::Number(n) => Ok(n), ArithmeticTerm::Number(n) => Ok(*n),
} }
} }
@@ -1167,8 +1143,8 @@ impl MachineState {
value: HeapCellValue, value: HeapCellValue,
) -> Result<Number, MachineStub> { ) -> Result<Number, MachineStub> {
let stub_gen = || functor_stub(atom!("is"), 2); let stub_gen = || functor_stub(atom!("is"), 2);
let mut iter = stackful_post_order_iter::<NonListElider> let mut iter =
(&mut self.heap, &mut self.stack, value); stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, value);
while let Some(value) = iter.next() { while let Some(value) = iter.next() {
if value.get_forwarding_bit() { if value.get_forwarding_bit() {
@@ -1447,6 +1423,7 @@ mod tests {
use crate::machine::mock_wam::*; use crate::machine::mock_wam::*;
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn arith_eval_by_metacall_tests() { fn arith_eval_by_metacall_tests() {
let mut wam = MachineState::new(); let mut wam = MachineState::new();
let mut op_dir = default_op_dir(); let mut op_dir = default_op_dir();

View File

@@ -133,8 +133,8 @@ impl MachineState {
let mut seen_set = IndexSet::new(); let mut seen_set = IndexSet::new();
let mut seen_vars = vec![]; let mut seen_vars = vec![];
let mut iter = stackful_preorder_iter::<NonListElider> let mut iter =
(&mut self.heap, &mut self.stack, cell); stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, cell);
while let Some(value) = iter.next() { while let Some(value) = iter.next() {
read_heap_cell!(value, read_heap_cell!(value,

View File

@@ -42,42 +42,35 @@ pub(super) fn bootstrapping_compile(
Ok(()) Ok(())
} }
fn lower_bound_of_target_clause(skeleton: &PredicateSkeleton, target_pos: usize) -> usize { fn lower_bound_of_target_clause(skeleton: &mut PredicateSkeleton, target_pos: usize) -> usize {
if target_pos == 0 { if target_pos == 0 {
return 0; return 0;
} }
let arg_num = skeleton.clauses[target_pos - 1].opt_arg_index_key.arg_num(); debug_assert!(skeleton.clauses.len() >= 2);
if arg_num == 0 { let index = target_pos - 1;
return target_pos - 1;
}
let mut index_loc_opt = None; let index = if let Some(index_loc) = skeleton.clauses[index]
.opt_arg_index_key
.switch_on_term_loc()
{
let search_result = skeleton.clauses.make_contiguous()
[0..skeleton.core.clause_assert_margin]
.partition_point(|clause_index_info| clause_index_info.clause_start > index_loc);
for index in (0..target_pos).rev() { if search_result < skeleton.core.clause_assert_margin {
let current_arg_num = skeleton.clauses[index].opt_arg_index_key.arg_num(); search_result
if current_arg_num == 0 || current_arg_num != arg_num {
return index + 1;
}
if let Some(index_loc) = index_loc_opt {
let current_index_loc = skeleton.clauses[index]
.opt_arg_index_key
.switch_on_term_loc();
if Some(index_loc) != current_index_loc {
return index + 1;
}
} else { } else {
index_loc_opt = skeleton.clauses[index] skeleton.clauses.make_contiguous()[skeleton.core.clause_assert_margin..]
.opt_arg_index_key .partition_point(|clause_index_info| clause_index_info.clause_start < index_loc)
.switch_on_term_loc(); + skeleton.core.clause_assert_margin
} }
} } else {
index
};
0 index.clamp(0, skeleton.clauses.len() - 2)
} }
fn derelictize_try_me_else( fn derelictize_try_me_else(
@@ -282,28 +275,25 @@ fn merge_indexed_subsequences(
.unwrap(), .unwrap(),
); );
match &mut code[inner_try_me_else_loc] { if let Instruction::TryMeElse(ref mut o) = &mut code[inner_try_me_else_loc] {
Instruction::TryMeElse(ref mut o) => { retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse( inner_try_me_else_loc,
inner_try_me_else_loc, *o,
*o, ));
));
match *o { match *o {
0 => { 0 => {
code[inner_try_me_else_loc] = Instruction::TrustMe(0); code[inner_try_me_else_loc] = Instruction::TrustMe(0);
}
o => match &code[inner_try_me_else_loc + o] {
Instruction::RevJmpBy(0) => {
code[inner_try_me_else_loc] = Instruction::TrustMe(o);
}
_ => {
code[inner_try_me_else_loc] = Instruction::RetryMeElse(o);
}
},
} }
o => match &code[inner_try_me_else_loc + o] {
Instruction::RevJmpBy(0) => {
code[inner_try_me_else_loc] = Instruction::TrustMe(o);
}
_ => {
code[inner_try_me_else_loc] = Instruction::RetryMeElse(o);
}
},
} }
_ => {}
} }
thread_choice_instr_at_to( thread_choice_instr_at_to(
@@ -333,8 +323,8 @@ fn merge_indexed_subsequences(
retraction_info, retraction_info,
); );
} }
None => match &mut code[outer_threaded_choice_instr_loc] { None => {
Instruction::TryMeElse(ref mut o) => { if let Instruction::TryMeElse(ref mut o) = &mut code[outer_threaded_choice_instr_loc] {
retraction_info retraction_info
.push_record(RetractionRecord::ModifiedTryMeElse(inner_trust_me_loc, *o)); .push_record(RetractionRecord::ModifiedTryMeElse(inner_trust_me_loc, *o));
@@ -342,8 +332,7 @@ fn merge_indexed_subsequences(
return Some(IndexPtr::index(outer_threaded_choice_instr_loc + 1)); return Some(IndexPtr::index(outer_threaded_choice_instr_loc + 1));
} }
_ => {} }
},
} }
None None
@@ -919,7 +908,7 @@ fn prepend_compiled_clause(
retraction_info, retraction_info,
); );
code.extend(prepend_queue.into_iter()); code.extend(prepend_queue);
if skeleton.core.is_dynamic { if skeleton.core.is_dynamic {
clause_loc clause_loc
@@ -975,7 +964,7 @@ fn prepend_compiled_clause(
internalize_choice_instr_at(code, old_clause_start, retraction_info); internalize_choice_instr_at(code, old_clause_start, retraction_info);
code.extend(prepend_queue.into_iter()); code.extend(prepend_queue);
clause_loc // + (outer_thread_choice_offset == 0 as usize) clause_loc // + (outer_thread_choice_offset == 0 as usize)
} }
@@ -1004,7 +993,7 @@ fn prepend_compiled_clause(
internalize_choice_instr_at(code, old_clause_start, retraction_info); internalize_choice_instr_at(code, old_clause_start, retraction_info);
code.extend(prepend_queue.into_iter()); code.extend(prepend_queue);
// skeleton.clauses[0].opt_arg_index_key += clause_loc; // skeleton.clauses[0].opt_arg_index_key += clause_loc;
skeleton.clauses[0].clause_start = clause_loc; skeleton.clauses[0].clause_start = clause_loc;
@@ -1029,7 +1018,7 @@ fn prepend_compiled_clause(
internalize_choice_instr_at(code, old_clause_start, retraction_info); internalize_choice_instr_at(code, old_clause_start, retraction_info);
code.extend(prepend_queue.into_iter()); code.extend(prepend_queue);
// skeleton.clauses[0].opt_arg_index_key += clause_loc; // skeleton.clauses[0].opt_arg_index_key += clause_loc;
skeleton.clauses[0].clause_start = clause_loc; skeleton.clauses[0].clause_start = clause_loc;
@@ -1134,21 +1123,18 @@ fn append_compiled_clause(
skeleton.clauses[target_pos].opt_arg_index_key += clause_loc; skeleton.clauses[target_pos].opt_arg_index_key += clause_loc;
code.extend(clause_code.drain(1..)); code.extend(clause_code.drain(1..));
match skeleton.clauses[target_pos] if let Some(index_loc) = skeleton.clauses[target_pos]
.opt_arg_index_key .opt_arg_index_key
.switch_on_term_loc() .switch_on_term_loc()
{ {
Some(index_loc) => { // point to the inner-threaded TryMeElse(0) if target_pos is
// point to the inner-threaded TryMeElse(0) if target_pos is // indexed, and make switch_on_term point one line after it in
// indexed, and make switch_on_term point one line after it in // its variable offset.
// its variable offset. skeleton.clauses[target_pos].clause_start += 2;
skeleton.clauses[target_pos].clause_start += 2;
if !skeleton.core.is_dynamic { if !skeleton.core.is_dynamic {
set_switch_var_offset(code, index_loc, 2, retraction_info); set_switch_var_offset(code, index_loc, 2, retraction_info);
}
} }
None => {}
} }
match skeleton.clauses[lower_bound] match skeleton.clauses[lower_bound]
@@ -1222,7 +1208,7 @@ fn print_overwrite_warning(
} }
println!( println!(
"Warning: overwriting {}/{} because the clauses are discontiguous", "% Warning: overwriting {}/{} because the clauses are discontiguous",
key.0.as_str(), key.0.as_str(),
key.1 key.1
); );
@@ -1302,11 +1288,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
clause_clause_locs.push_back(clause_index_info.clause_start); clause_clause_locs.push_back(clause_index_info.clause_start);
} }
match &mut code[0] { if let Instruction::TryMeElse(0) = &mut code[0] {
Instruction::TryMeElse(0) => { code_ptr += 1;
code_ptr += 1;
}
_ => {}
} }
match self match self
@@ -1317,7 +1300,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
Some(skeleton) => { Some(skeleton) => {
let skeleton_clause_len = skeleton.clauses.len(); let skeleton_clause_len = skeleton.clauses.len();
skeleton.clauses.extend(cg.skeleton.clauses.into_iter()); skeleton.clauses.extend(cg.skeleton.clauses);
skeleton skeleton
.core .core
.clause_clause_locs .clause_clause_locs
@@ -1337,7 +1320,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
.clause_clause_locs .clause_clause_locs
.extend(&clause_clause_locs.make_contiguous()[0..]); .extend(&clause_clause_locs.make_contiguous()[0..]);
let skeleton = cg.skeleton; let mut skeleton = cg.skeleton;
skeleton.core.is_dynamic = settings.is_dynamic();
self.add_extensible_predicate(key, skeleton, predicates.compilation_target); self.add_extensible_predicate(key, skeleton, predicates.compilation_target);
} }
@@ -1371,7 +1355,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
index_ptr, index_ptr,
); );
self.wam_prelude.code.extend(code.into_iter()); self.wam_prelude.code.extend(code);
Ok(code_index) Ok(code_index)
} }
@@ -1537,6 +1521,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
let code_len = self.wam_prelude.code.len(); let code_len = self.wam_prelude.code.len();
standalone_skeleton.clauses[0].clause_start += code_len;
let skeleton = match self let skeleton = match self
.wam_prelude .wam_prelude
.indices .indices
@@ -1549,8 +1535,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
match append_or_prepend { match append_or_prepend {
AppendOrPrepend::Append => { AppendOrPrepend::Append => {
let clause_index_info = standalone_skeleton.clauses.pop_back().unwrap(); let clause_index_info = standalone_skeleton.clauses.pop_back().unwrap();
skeleton.clauses.push_back(clause_index_info);
skeleton.clauses.push_back(clause_index_info);
skeleton.core.clause_clause_locs.push_back(code_len); skeleton.core.clause_clause_locs.push_back(code_len);
self.payload self.payload
@@ -1563,7 +1549,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
let global_clock = LS::machine_st(&mut self.payload).global_clock; let global_clock = LS::machine_st(&mut self.payload).global_clock;
let result = append_compiled_clause( let result = append_compiled_clause(
&mut self.wam_prelude.code, self.wam_prelude.code,
clause_code, clause_code,
skeleton, skeleton,
&mut self.payload.retraction_info, &mut self.payload.retraction_info,
@@ -1603,7 +1589,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
let global_clock = LS::machine_st(&mut self.payload).global_clock; let global_clock = LS::machine_st(&mut self.payload).global_clock;
let new_code_ptr = prepend_compiled_clause( let new_code_ptr = prepend_compiled_clause(
&mut self.wam_prelude.code, self.wam_prelude.code,
compilation_target, compilation_target,
key, key,
clause_code, clause_code,
@@ -1646,7 +1632,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
.switch_on_term_loc() .switch_on_term_loc()
{ {
Some(index_loc) => find_inner_choice_instr( Some(index_loc) => find_inner_choice_instr(
&self.wam_prelude.code, self.wam_prelude.code,
skeleton.clauses[target_pos].clause_start, skeleton.clauses[target_pos].clause_start,
index_loc, index_loc,
), ),
@@ -1687,115 +1673,109 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
if target_pos == 0 || (lower_bound + 1 == target_pos && lower_bound_is_unindexed) { if target_pos == 0 || (lower_bound + 1 == target_pos && lower_bound_is_unindexed) {
// the clause preceding target_pos, if there is one, is of // the clause preceding target_pos, if there is one, is of
// key type OptArgIndexKey::None. // key type OptArgIndexKey::None.
match skeleton.clauses[target_pos] if let Some(index_loc) = skeleton.clauses[target_pos]
.opt_arg_index_key .opt_arg_index_key
.switch_on_term_loc() .switch_on_term_loc()
{ {
Some(index_loc) => { let inner_clause_start = find_inner_choice_instr(
let inner_clause_start = find_inner_choice_instr( code,
code, skeleton.clauses[target_pos].clause_start,
skeleton.clauses[target_pos].clause_start, index_loc,
index_loc, );
);
remove_index_from_subsequence( remove_index_from_subsequence(
code, code,
&skeleton.clauses[target_pos].opt_arg_index_key, &skeleton.clauses[target_pos].opt_arg_index_key,
inner_clause_start, inner_clause_start,
&mut self.payload.retraction_info, &mut self.payload.retraction_info,
); );
match derelictize_try_me_else( match derelictize_try_me_else(
code, code,
inner_clause_start, inner_clause_start,
&mut self.payload.retraction_info, &mut self.payload.retraction_info,
) { ) {
Some(offset) => { Some(offset) => {
let instr_loc = find_inner_choice_instr( let instr_loc =
code, find_inner_choice_instr(code, inner_clause_start + offset, index_loc);
inner_clause_start + offset,
index_loc,
);
let clause_loc = blunt_leading_choice_instr( let clause_loc = blunt_leading_choice_instr(
code, code,
instr_loc, instr_loc,
&mut self.payload.retraction_info, &mut self.payload.retraction_info,
); );
set_switch_var_offset( set_switch_var_offset(
code, code,
index_loc, index_loc,
clause_loc - index_loc, clause_loc - index_loc,
&mut self.payload.retraction_info, &mut self.payload.retraction_info,
); );
self.payload.retraction_info.push_record( self.payload.retraction_info.push_record(
RetractionRecord::SkeletonClauseStartReplaced( RetractionRecord::SkeletonClauseStartReplaced(
payload_compilation_target,
key,
target_pos + 1,
skeleton.clauses[target_pos + 1].clause_start,
),
);
skeleton.clauses[target_pos + 1].clause_start =
skeleton.clauses[target_pos].clause_start;
let update_code_index = target_pos == 0
&& skeleton.clauses[target_pos + 1]
.opt_arg_index_key
.switch_on_term_loc()
.is_none();
let index_ptr_opt = if update_code_index {
Some(IndexPtr::index(clause_loc))
} else {
None
};
return finalize_retract(
key,
payload_compilation_target, payload_compilation_target,
skeleton,
code_index,
target_pos,
index_ptr_opt,
&mut self.payload.retraction_info,
);
}
None => {
let index_ptr_opt = if target_pos > 0 {
let preceding_choice_instr_loc =
skeleton.clauses[target_pos - 1].clause_start;
remove_non_leading_clause(
code,
preceding_choice_instr_loc,
skeleton.clauses[target_pos].clause_start - 2,
&mut self.payload.retraction_info,
)
} else {
remove_leading_unindexed_clause(
code,
skeleton.clauses[target_pos].clause_start - 2,
&mut self.payload.retraction_info,
)
};
return finalize_retract(
key, key,
payload_compilation_target, target_pos + 1,
skeleton, skeleton.clauses[target_pos + 1].clause_start,
code_index, ),
target_pos, );
index_ptr_opt,
skeleton.clauses[target_pos + 1].clause_start =
skeleton.clauses[target_pos].clause_start;
let update_code_index = target_pos == 0
&& skeleton.clauses[target_pos + 1]
.opt_arg_index_key
.switch_on_term_loc()
.is_none();
let index_ptr_opt = if update_code_index {
Some(IndexPtr::index(clause_loc))
} else {
None
};
return finalize_retract(
key,
payload_compilation_target,
skeleton,
code_index,
target_pos,
index_ptr_opt,
&mut self.payload.retraction_info,
);
}
None => {
let index_ptr_opt = if target_pos > 0 {
let preceding_choice_instr_loc =
skeleton.clauses[target_pos - 1].clause_start;
remove_non_leading_clause(
code,
preceding_choice_instr_loc,
skeleton.clauses[target_pos].clause_start - 2,
&mut self.payload.retraction_info, &mut self.payload.retraction_info,
); )
} } else {
remove_leading_unindexed_clause(
code,
skeleton.clauses[target_pos].clause_start - 2,
&mut self.payload.retraction_info,
)
};
return finalize_retract(
key,
payload_compilation_target,
skeleton,
code_index,
target_pos,
index_ptr_opt,
&mut self.payload.retraction_info,
);
} }
} }
None => {}
} }
} }
@@ -1824,16 +1804,13 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
Instruction::RevJmpBy(target_indexing_loc - later_indexing_loc), Instruction::RevJmpBy(target_indexing_loc - later_indexing_loc),
); );
match target_indexing_line { if let Instruction::IndexingCode(indexing_code) = target_indexing_line {
Instruction::IndexingCode(indexing_code) => { self.payload.retraction_info.push_record(
self.payload.retraction_info.push_record( RetractionRecord::ReplacedIndexingLine(
RetractionRecord::ReplacedIndexingLine( target_indexing_loc,
target_indexing_loc, indexing_code,
indexing_code, ),
), );
);
}
_ => {}
} }
result = merge_indexed_subsequences( result = merge_indexed_subsequences(
@@ -1977,16 +1954,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
&mut self.payload.retraction_info, &mut self.payload.retraction_info,
); );
match &mut code[preceding_choice_instr_loc] { if let Instruction::TryMeElse(0) =
Instruction::TryMeElse(0) => { &mut code[preceding_choice_instr_loc]
set_switch_var_offset( {
code, set_switch_var_offset(
index_loc, code,
preceding_choice_instr_loc + 1 - index_loc, index_loc,
&mut self.payload.retraction_info, preceding_choice_instr_loc + 1 - index_loc,
); &mut self.payload.retraction_info,
} );
_ => {}
} }
} }
} }
@@ -2068,16 +2044,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
{ {
Some(skeleton) if append_or_prepend.is_append() => { Some(skeleton) if append_or_prepend.is_append() => {
let tail_num = skeleton.core.clause_clause_locs.len() - num_clause_predicates; let tail_num = skeleton.core.clause_clause_locs.len() - num_clause_predicates;
skeleton.core.clause_clause_locs.make_contiguous()[tail_num..] skeleton.core.clause_clause_locs.make_contiguous()[tail_num..].to_vec()
.iter()
.cloned()
.collect()
} }
Some(skeleton) => skeleton.core.clause_clause_locs.make_contiguous() Some(skeleton) => skeleton.core.clause_clause_locs.make_contiguous()
[0..num_clause_predicates] [0..num_clause_predicates]
.iter() .to_vec(),
.cloned()
.collect(),
None => { None => {
unreachable!() unreachable!()
} }
@@ -2173,7 +2144,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
.map(|skeleton| skeleton.predicate_info()) .map(|skeleton| skeleton.predicate_info())
.unwrap_or_default(); .unwrap_or_default();
let mut predicate_info = self let predicate_info = self
.wam_prelude .wam_prelude
.indices .indices
.get_predicate_skeleton(&self.payload.predicates.compilation_target, &key) .get_predicate_skeleton(&self.payload.predicates.compilation_target, &key)
@@ -2205,46 +2176,45 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
)?; )?;
} }
} else { } else {
if is_cross_module_clause { if is_cross_module_clause && !local_predicate_info.is_extensible {
if !local_predicate_info.is_extensible { if predicate_info.is_multifile {
if predicate_info.is_multifile { println!(
println!( "% Warning: overwriting multifile predicate {}:{}/{} because \
"Warning: overwriting multifile predicate {}:{}/{} because \ it was not locally declared multifile.",
it was not locally declared multifile.", self.payload.predicates.compilation_target,
self.payload.predicates.compilation_target, key.0.as_str(),
key.0.as_str(), key.1
key.1 );
}
if let Some(skeleton) = self
.wam_prelude
.indices
.remove_predicate_skeleton(&self.payload.predicates.compilation_target, &key)
{
let compilation_target = self.payload.predicates.compilation_target;
if predicate_info.is_dynamic {
let clause_clause_compilation_target = match compilation_target {
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
module => module,
};
self.retract_local_clauses_by_locs(
clause_clause_compilation_target,
(atom!("$clause"), 2),
(0..skeleton.clauses.len()).map(Some).collect(),
false, // the builtin M:'$clause'/2 is never dynamic.
); );
} }
if let Some(skeleton) = self.wam_prelude.indices.remove_predicate_skeleton( self.payload
&self.payload.predicates.compilation_target, .retraction_info
&key, .push_record(RetractionRecord::RemovedSkeleton(
) { compilation_target,
let compilation_target = self.payload.predicates.compilation_target; key,
skeleton,
if predicate_info.is_dynamic { ));
let clause_clause_compilation_target = match compilation_target {
CompilationTarget::User => {
CompilationTarget::Module(atom!("builtins"))
}
module => module,
};
self.retract_local_clauses_by_locs(
clause_clause_compilation_target,
(atom!("$clause"), 2),
(0..skeleton.clauses.len()).map(Some).collect(),
false, // the builtin M:'$clause'/2 is never dynamic.
);
predicate_info.is_dynamic = false;
}
self.payload.retraction_info.push_record(
RetractionRecord::RemovedSkeleton(compilation_target, key, skeleton),
);
}
} }
} }
@@ -2262,20 +2232,17 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
let code_index = self.compile(key, predicates, settings)?; let code_index = self.compile(key, predicates, settings)?;
if let Some(filename) = self.listing_src_file_name() { if let Some(filename) = self.listing_src_file_name() {
match self.wam_prelude.indices.modules.get_mut(&filename) { if let Some(ref mut module) = self.wam_prelude.indices.modules.get_mut(&filename) {
Some(ref mut module) => { let index_ptr = code_index.get();
let index_ptr = code_index.get(); let code_index = *module.code_dir.entry(key).or_insert(code_index);
let code_index = module.code_dir.entry(key).or_insert(code_index).clone();
set_code_index( set_code_index(
&mut self.payload.retraction_info, &mut self.payload.retraction_info,
&CompilationTarget::Module(filename), &CompilationTarget::Module(filename),
key, key,
code_index, code_index,
index_ptr, index_ptr,
); );
}
None => {}
} }
} }
} }
@@ -2344,7 +2311,7 @@ impl Machine {
}; };
let StandaloneCompileResult { clause_code, .. } = compile()?; let StandaloneCompileResult { clause_code, .. } = compile()?;
self.code.extend(clause_code.into_iter()); self.code.extend(clause_code);
Ok(()) Ok(())
} }

View File

@@ -18,6 +18,7 @@ pub trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
fn store(&self, value: HeapCellValue) -> HeapCellValue; fn store(&self, value: HeapCellValue) -> HeapCellValue;
fn deref(&self, value: HeapCellValue) -> HeapCellValue; fn deref(&self, value: HeapCellValue) -> HeapCellValue;
fn push(&mut self, value: HeapCellValue); fn push(&mut self, value: HeapCellValue);
fn push_attr_var_queue(&mut self, attr_var_loc: usize);
fn stack(&mut self) -> &mut Stack; fn stack(&mut self) -> &mut Stack;
fn threshold(&self) -> usize; fn threshold(&self) -> usize;
} }
@@ -73,7 +74,6 @@ impl<T: CopierTarget> CopyTermState<T> {
if h >= self.old_h { if h >= self.old_h {
*self.value_at_scan() = list_loc_as_cell!(h); *self.value_at_scan() = list_loc_as_cell!(h);
self.scan += 1; self.scan += 1;
return; return;
} }
} }
@@ -96,14 +96,19 @@ impl<T: CopierTarget> CopyTermState<T> {
.store(self.target.deref(heap_loc_as_cell!(addr + 1))); .store(self.target.deref(heap_loc_as_cell!(addr + 1)));
if !cdr.is_var() { if !cdr.is_var() {
// mark addr + 1 as a list back edge in the cdr of the list
self.trail_list_cell(addr + 1, threshold); self.trail_list_cell(addr + 1, threshold);
self.target[addr + 1].set_mark_bit(true);
self.target[addr + 1].set_forwarding_bit(true);
} else { } else {
let car = self let car = self
.target .target
.store(self.target.deref(heap_loc_as_cell!(addr))); .store(self.target.deref(heap_loc_as_cell!(addr)));
if !car.is_var() { if !car.is_var() {
// mark addr as a list back edge in the car of the list
self.trail_list_cell(addr, threshold); self.trail_list_cell(addr, threshold);
self.target[addr].set_mark_bit(true);
} }
} }
@@ -174,10 +179,11 @@ impl<T: CopierTarget> CopyTermState<T> {
fn copy_attr_var_lists(&mut self) { fn copy_attr_var_lists(&mut self) {
while !self.attr_var_list_locs.is_empty() { while !self.attr_var_list_locs.is_empty() {
let iter = mem::replace(&mut self.attr_var_list_locs, vec![]); let iter = std::mem::take(&mut self.attr_var_list_locs);
for (threshold, list_loc) in iter { for (threshold, list_loc) in iter {
self.target[threshold] = list_loc_as_cell!(self.target.threshold()); self.target[threshold] = list_loc_as_cell!(self.target.threshold());
self.target.push_attr_var_queue(threshold - 1);
self.copy_attr_var_list(list_loc); self.copy_attr_var_list(list_loc);
} }
} }
@@ -263,6 +269,7 @@ impl<T: CopierTarget> CopyTermState<T> {
} }
fn copy_var(&mut self, addr: HeapCellValue) { fn copy_var(&mut self, addr: HeapCellValue) {
let index = addr.get_value() as usize;
let rd = self.target.deref(addr); let rd = self.target.deref(addr);
let ra = self.target.store(rd); let ra = self.target.store(rd);
@@ -271,7 +278,20 @@ impl<T: CopierTarget> CopyTermState<T> {
if h >= self.old_h { if h >= self.old_h {
*self.value_at_scan() = ra; *self.value_at_scan() = ra;
self.scan += 1; self.scan += 1;
return;
}
}
(HeapCellValueTag::Lis, h) => {
if h >= self.old_h && self.target[index].get_mark_bit() {
*self.value_at_scan() = heap_loc_as_cell!(
if ra.get_forwarding_bit() {
h + 1
} else {
h
}
);
self.scan += 1;
return; return;
} }
} }
@@ -356,12 +376,16 @@ impl<T: CopierTarget> CopyTermState<T> {
} }
} }
fn unwind_trail(&mut self) { fn unwind_trail(mut self) {
for (r, value) in self.trail.drain(0..) { for (r, value) in self.trail {
let index = r.get_value() as usize; let index = r.get_value() as usize;
match r.get_tag() { match r.get_tag() {
RefTag::AttrVar | RefTag::HeapCell => self.target[index] = value, RefTag::AttrVar | RefTag::HeapCell => {
self.target[index] = value;
self.target[index].set_mark_bit(false);
self.target[index].set_forwarding_bit(false);
}
RefTag::StackCell => self.target.stack()[index] = value, RefTag::StackCell => self.target.stack()[index] = value,
} }
} }
@@ -374,6 +398,7 @@ mod tests {
use crate::machine::mock_wam::*; use crate::machine::mock_wam::*;
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
fn copier_tests() { fn copier_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();

View File

@@ -73,7 +73,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
fn traverse_subterm(&mut self, h: usize, arity: usize) -> Option<usize> { fn traverse_subterm(&mut self, h: usize, arity: usize) -> Option<usize> {
let mut last_cell_loc = h + arity - 1; let mut last_cell_loc = h + arity - 1;
for idx in (h .. h + arity).rev() { for idx in (h..h + arity).rev() {
if self.heap[idx].get_forwarding_bit() { if self.heap[idx].get_forwarding_bit() {
if self.cycle_detection_active() { if self.cycle_detection_active() {
self.cycle_found = true; self.cycle_found = true;
@@ -93,8 +93,8 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
#[inline] #[inline]
fn continue_forwarding(&self) -> bool { fn continue_forwarding(&self) -> bool {
self.heap[self.current].get_mark_bit() != self.mark_phase || self.heap[self.current].get_mark_bit() != self.mark_phase
self.heap[self.current].get_forwarding_bit() || self.heap[self.current].get_forwarding_bit()
} }
fn forward(&mut self) -> Option<HeapCellValue> { fn forward(&mut self) -> Option<HeapCellValue> {
@@ -150,7 +150,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
} }
if self.cycle_detection_active() { if self.cycle_detection_active() {
for idx in (h + 1 .. last_cell_loc).rev() { for idx in (h + 1..last_cell_loc).rev() {
if self.heap[idx].get_forwarding_bit() { if self.heap[idx].get_forwarding_bit() {
self.cycle_found = true; self.cycle_found = true;
return None; return None;
@@ -176,7 +176,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
}; };
if self.cycle_detection_active() { if self.cycle_detection_active() {
for idx in (self.next as usize .. last_cell_loc).rev() { for idx in (self.next as usize..last_cell_loc).rev() {
if self.heap[idx].get_forwarding_bit() { if self.heap[idx].get_forwarding_bit() {
self.cycle_found = true; self.cycle_found = true;
return None; return None;
@@ -309,19 +309,19 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
HeapCellValueTag::Str => { HeapCellValueTag::Str => {
let mut new_str_back_link = self.current; let mut new_str_back_link = self.current;
for idx in (0 .. self.current).rev() { for idx in (0..self.current).rev() {
if self.heap[idx].get_tag() == HeapCellValueTag::Atom { if self.heap[idx].get_tag() == HeapCellValueTag::Atom
if cell_as_atom_cell!(self.heap[idx]).get_arity() > 0 { && cell_as_atom_cell!(self.heap[idx]).get_arity() > 0
new_str_back_link = idx; {
break; new_str_back_link = idx;
} break;
} }
if self.heap[idx].get_mark_bit() != self.mark_phase { if self.heap[idx].get_mark_bit() != self.mark_phase
if !self.heap[idx].get_forwarding_bit() { && !self.heap[idx].get_forwarding_bit()
new_str_back_link = idx; {
break; new_str_back_link = idx;
} break;
} }
} }
@@ -402,7 +402,7 @@ impl<'a, const STOP_AT_CYCLES: bool> CycleDetectingIter<'a, STOP_AT_CYCLES> {
self.next = self.heap[self.start].get_value(); self.next = self.heap[self.start].get_value();
self.current = self.start; self.current = self.start;
while let Some(_) = self.forward() {} while self.forward().is_some() {}
} }
} }
@@ -415,7 +415,6 @@ impl<'a, const STOP_AT_CYCLES: bool> Iterator for CycleDetectingIter<'a, STOP_AT
} }
} }
impl<'a, const STOP_AT_CYCLES: bool> Drop for CycleDetectingIter<'a, STOP_AT_CYCLES> { impl<'a, const STOP_AT_CYCLES: bool> Drop for CycleDetectingIter<'a, STOP_AT_CYCLES> {
fn drop(&mut self) { fn drop(&mut self) {
self.invert_marker(); self.invert_marker();

View File

@@ -226,7 +226,7 @@ fn merge_branch_seq(branches: impl Iterator<Item = BranchInfo>) -> BranchInfo {
for mut branch in branches { for mut branch in branches {
branch_info.branch_num = branch.branch_num; branch_info.branch_num = branch.branch_num;
branch_info.chunks.extend(branch.chunks.drain(..)); branch_info.chunks.append(&mut branch.chunks);
} }
branch_info.branch_num.delta = branch_info.branch_num.delta * Integer::from(2); branch_info.branch_num.delta = branch_info.branch_num.delta * Integer::from(2);
@@ -298,7 +298,7 @@ impl VariableClassifier {
fn merge_branches(&mut self) { fn merge_branches(&mut self) {
for branches in self.branch_map.values_mut() { for branches in self.branch_map.values_mut() {
let mut old_branches = std::mem::replace(branches, vec![]); let mut old_branches = std::mem::take(branches);
while let Some(last_branch_num) = old_branches.last().map(|bi| &bi.branch_num) { while let Some(last_branch_num) = old_branches.last().map(|bi| &bi.branch_num) {
let mut old_branches_len = old_branches.len(); let mut old_branches_len = old_branches.len();
@@ -361,10 +361,7 @@ impl VariableClassifier {
.current_chunk_type .current_chunk_type
.to_gen_context(self.current_chunk_num); .to_gen_context(self.current_chunk_num);
let branch_info_v = self let branch_info_v = self.branch_map.entry(var_info.var_ptr.clone()).or_default();
.branch_map
.entry(var_info.var_ptr.clone())
.or_insert_with(|| vec![]);
let needs_new_branch = if let Some(last_bi) = branch_info_v.last() { let needs_new_branch = if let Some(last_bi) = branch_info_v.last() {
!self.root_set.contains(&last_bi.branch_num) !self.root_set.contains(&last_bi.branch_num)
@@ -420,56 +417,49 @@ impl VariableClassifier {
arity: term.arity(), arity: term.arity(),
}; };
match term { if let Term::Clause(_, _, terms) = term {
Term::Clause(_, _, terms) => { for term in terms.iter() {
for term in terms.into_iter() { for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) {
for term_ref in breadth_first_iter(term, RootIterationPolicy::Iterated) { if let TermRef::Var(lvl, _, var_ptr) = term_ref {
if let TermRef::Var(lvl, _, var_ptr) = term_ref { // a body term, so we need the child level here.
// a body term, so we need the child level here. let lvl = lvl.child_level();
let lvl = lvl.child_level();
// the body of the if let here is an inlined // the body of the if let here is an inlined
// "probe_head_var". note the difference between it // "probe_head_var". note the difference between it
// and "probe_body_var". // and "probe_body_var".
let branch_info_v = self let branch_info_v = self.branch_map.entry(var_ptr.clone()).or_default();
.branch_map
.entry(var_ptr.clone())
.or_insert_with(|| vec![]);
let needs_new_branch = branch_info_v.is_empty(); let needs_new_branch = branch_info_v.is_empty();
if needs_new_branch { if needs_new_branch {
branch_info_v branch_info_v.push(BranchInfo::new(self.current_branch_num.clone()));
.push(BranchInfo::new(self.current_branch_num.clone()));
}
let branch_info = branch_info_v.last_mut().unwrap();
let needs_new_chunk = branch_info.chunks.is_empty();
if needs_new_chunk {
branch_info.chunks.push(ChunkInfo {
chunk_num: self.current_chunk_num,
term_loc: GenContext::Head,
vars: vec![],
});
}
let chunk_info = branch_info.chunks.last_mut().unwrap();
let var_info = VarInfo {
var_ptr,
classify_info,
chunk_type: self.current_chunk_type,
lvl,
};
chunk_info.vars.push(var_info);
} }
}
classify_info.arg_c += 1; let branch_info = branch_info_v.last_mut().unwrap();
let needs_new_chunk = branch_info.chunks.is_empty();
if needs_new_chunk {
branch_info.chunks.push(ChunkInfo {
chunk_num: self.current_chunk_num,
term_loc: GenContext::Head,
vars: vec![],
});
}
let chunk_info = branch_info.chunks.last_mut().unwrap();
let var_info = VarInfo {
var_ptr,
classify_info,
chunk_type: self.current_chunk_type,
lvl,
};
chunk_info.vars.push(var_info);
}
} }
classify_info.arg_c += 1;
} }
_ => {}
} }
Ok(()) Ok(())
@@ -538,7 +528,10 @@ impl VariableClassifier {
build_stack.push_chunk_term(if is_global { build_stack.push_chunk_term(if is_global {
QueryTerm::GlobalCut(var_num) QueryTerm::GlobalCut(var_num)
} else { } else {
QueryTerm::LocalCut { var_num, cut_prev: false } QueryTerm::LocalCut {
var_num,
cut_prev: false,
}
}); });
} }
TraversalState::CutPrev(var_num) => { TraversalState::CutPrev(var_num) => {
@@ -548,7 +541,10 @@ impl VariableClassifier {
self.probe_in_situ_var(var_num); self.probe_in_situ_var(var_num);
build_stack.push_chunk_term(QueryTerm::LocalCut { var_num, cut_prev: true }); build_stack.push_chunk_term(QueryTerm::LocalCut {
var_num,
cut_prev: true,
});
} }
TraversalState::Fail => { TraversalState::Fail => {
build_stack.push_chunk_term(QueryTerm::Fail); build_stack.push_chunk_term(QueryTerm::Fail);
@@ -667,12 +663,16 @@ impl VariableClassifier {
state_stack.last(), state_stack.last(),
Some(TraversalState::RemoveBranchNum) Some(TraversalState::RemoveBranchNum)
) { ) {
// check if the second-to-last element is a regular BuildDisjunct, as we don't // check if the second-to-last element
// want to add GetPrevLevel in case of a TrustMe. // is a regular BuildDisjunct, as we
matches!( // don't want to add GetPrevLevel in
state_stack.iter().rev().nth(1), // case of a TrustMe.
Some(TraversalState::BuildDisjunct(..)) match state_stack.iter().rev().nth(1) {
) Some(&TraversalState::BuildDisjunct(preceding_len)) => {
preceding_len + 1 == build_stack.len()
}
_ => false,
}
} else { } else {
false false
}; };
@@ -705,7 +705,9 @@ impl VariableClassifier {
state_stack.push(TraversalState::BuildDisjunct(build_stack_len)); state_stack.push(TraversalState::BuildDisjunct(build_stack_len));
state_stack.push(TraversalState::Fail); state_stack.push(TraversalState::Fail);
state_stack.push(TraversalState::CutPrev(self.var_num)); state_stack.push(TraversalState::CutPrev(self.var_num));
state_stack.push(TraversalState::ResetGlobalCutVarOverride(self.global_cut_var_num_override)); state_stack.push(TraversalState::ResetGlobalCutVarOverride(
self.global_cut_var_num_override,
));
state_stack.push(TraversalState::Term(not_term)); state_stack.push(TraversalState::Term(not_term));
state_stack.push(TraversalState::OverrideGlobalCutVar(self.var_num)); state_stack.push(TraversalState::OverrideGlobalCutVar(self.var_num));
state_stack.push(TraversalState::GetCutPoint { state_stack.push(TraversalState::GetCutPoint {

View File

@@ -36,7 +36,7 @@ macro_rules! try_or_throw {
macro_rules! increment_call_count { macro_rules! increment_call_count {
($s:expr) => {{ ($s:expr) => {{
if !($s.increment_call_count_fn)(&mut $s) { if !$s.increment_call_count() {
$s.backtrack(); $s.backtrack();
continue; continue;
} }
@@ -208,6 +208,7 @@ impl MachineState {
l l
} }
(HeapCellValueTag::Fixnum | (HeapCellValueTag::Fixnum |
HeapCellValueTag::CutPoint |
HeapCellValueTag::Char | HeapCellValueTag::Char |
HeapCellValueTag::F64) => { HeapCellValueTag::F64) => {
c c
@@ -313,8 +314,8 @@ impl MachineState {
impl Machine { impl Machine {
pub(super) fn find_living_dynamic_else(&self, mut p: usize) -> Option<(usize, usize)> { pub(super) fn find_living_dynamic_else(&self, mut p: usize) -> Option<(usize, usize)> {
loop { loop {
match &self.code[p] { match self.code[p] {
&Instruction::DynamicElse(birth, death, NextOrFail::Next(i)) => { Instruction::DynamicElse(birth, death, NextOrFail::Next(i)) => {
if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death { if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
return Some((p, i)); return Some((p, i));
} else if i > 0 { } else if i > 0 {
@@ -323,14 +324,14 @@ impl Machine {
return None; return None;
} }
} }
&Instruction::DynamicElse(birth, death, NextOrFail::Fail(_)) => { Instruction::DynamicElse(birth, death, NextOrFail::Fail(_)) => {
if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death { if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
return Some((p, 0)); return Some((p, 0));
} else { } else {
return None; return None;
} }
} }
&Instruction::DynamicInternalElse(birth, death, NextOrFail::Next(i)) => { Instruction::DynamicInternalElse(birth, death, NextOrFail::Next(i)) => {
if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death { if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
return Some((p, i)); return Some((p, i));
} else if i > 0 { } else if i > 0 {
@@ -339,14 +340,14 @@ impl Machine {
return None; return None;
} }
} }
&Instruction::DynamicInternalElse(birth, death, NextOrFail::Fail(_)) => { Instruction::DynamicInternalElse(birth, death, NextOrFail::Fail(_)) => {
if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death { if birth < self.machine_st.cc && Death::Finite(self.machine_st.cc) <= death {
return Some((p, 0)); return Some((p, 0));
} else { } else {
return None; return None;
} }
} }
&Instruction::RevJmpBy(i) => { Instruction::RevJmpBy(i) => {
p -= i; p -= i;
} }
_ => { _ => {
@@ -395,25 +396,14 @@ impl Machine {
fn execute_switch_on_term(&mut self) { fn execute_switch_on_term(&mut self) {
#[inline(always)] #[inline(always)]
fn dynamic_external_of_clause_is_valid(machine: &mut Machine, p: usize) -> bool { fn dynamic_external_of_clause_is_valid(machine: &mut Machine, p: usize) -> bool {
match &machine.code[p] { if let Instruction::DynamicInternalElse(..) = machine.code[p] {
Instruction::DynamicInternalElse(..) => { machine.machine_st.dynamic_mode = FirstOrNext::First;
machine.machine_st.dynamic_mode = FirstOrNext::First; return true;
return true;
}
_ => {}
} }
match &machine.code[p - 1] { if let Instruction::DynamicInternalElse(birth, death, _) = machine.code[p - 1] {
&Instruction::DynamicInternalElse(birth, death, _) => { return birth < machine.machine_st.cc
if birth < machine.machine_st.cc && Death::Finite(machine.machine_st.cc) <= death;
&& Death::Finite(machine.machine_st.cc) <= death
{
return true;
} else {
return false;
}
}
_ => {}
} }
true true
@@ -567,20 +557,29 @@ impl Machine {
} }
let mut p = self.machine_st.p; let mut p = self.machine_st.p;
let mut arity = 0;
while self.code[p].is_head_instr() { while self.code[p].is_head_instr() {
for r in self.code[p].registers() {
if let RegType::Temp(t) = r {
arity = std::cmp::max(arity, t);
}
}
p += 1; p += 1;
} }
let instr = let instr = std::mem::replace(
std::mem::replace(&mut self.code[p], Instruction::VerifyAttrInterrupt); &mut self.code[p],
Instruction::VerifyAttrInterrupt(arity),
);
self.code[VERIFY_ATTR_INTERRUPT_LOC] = instr; self.code[VERIFY_ATTR_INTERRUPT_LOC] = instr;
self.machine_st.attr_var_init.cp = p; self.machine_st.attr_var_init.cp = p;
} }
&Instruction::VerifyAttrInterrupt => { &Instruction::VerifyAttrInterrupt(arity) => {
let (_, arity) = self.code[VERIFY_ATTR_INTERRUPT_LOC].to_name_and_arity(); // let (_, arity) = self.code[VERIFY_ATTR_INTERRUPT_LOC].to_name_and_arity();
let arity = std::cmp::max(arity, self.machine_st.num_of_args); // let arity = std::cmp::max(arity, self.machine_st.num_of_args);
self.run_verify_attr_interrupt(arity); self.run_verify_attr_interrupt(arity);
} }
&Instruction::Add(ref a1, ref a2, t) => { &Instruction::Add(ref a1, ref a2, t) => {
@@ -1896,7 +1895,7 @@ impl Machine {
self.machine_st.backtrack(); self.machine_st.backtrack();
} }
} }
&Instruction::CallNumberLessThanOrEqual(ref at_1, ref at_2) => { Instruction::CallNumberLessThanOrEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -1910,7 +1909,7 @@ impl Machine {
} }
} }
} }
&Instruction::ExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => { Instruction::ExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -1924,7 +1923,7 @@ impl Machine {
} }
} }
} }
&Instruction::CallNumberEqual(ref at_1, ref at_2) => { Instruction::CallNumberEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -1938,7 +1937,7 @@ impl Machine {
} }
} }
} }
&Instruction::ExecuteNumberEqual(ref at_1, ref at_2) => { Instruction::ExecuteNumberEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -1952,7 +1951,7 @@ impl Machine {
} }
} }
} }
&Instruction::CallNumberNotEqual(ref at_1, ref at_2) => { Instruction::CallNumberNotEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -1966,7 +1965,7 @@ impl Machine {
} }
} }
} }
&Instruction::ExecuteNumberNotEqual(ref at_1, ref at_2) => { Instruction::ExecuteNumberNotEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -1980,7 +1979,7 @@ impl Machine {
} }
} }
} }
&Instruction::CallNumberGreaterThanOrEqual(ref at_1, ref at_2) => { Instruction::CallNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -1994,7 +1993,7 @@ impl Machine {
} }
} }
} }
&Instruction::ExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => { Instruction::ExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2008,7 +2007,7 @@ impl Machine {
} }
} }
} }
&Instruction::CallNumberGreaterThan(ref at_1, ref at_2) => { Instruction::CallNumberGreaterThan(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2022,7 +2021,7 @@ impl Machine {
} }
} }
} }
&Instruction::ExecuteNumberGreaterThan(ref at_1, ref at_2) => { Instruction::ExecuteNumberGreaterThan(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2036,7 +2035,7 @@ impl Machine {
} }
} }
} }
&Instruction::CallNumberLessThan(ref at_1, ref at_2) => { Instruction::CallNumberLessThan(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2050,7 +2049,7 @@ impl Machine {
} }
} }
} }
&Instruction::ExecuteNumberLessThan(ref at_1, ref at_2) => { Instruction::ExecuteNumberLessThan(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2064,7 +2063,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultCallNumberLessThanOrEqual(ref at_1, ref at_2) => { Instruction::DefaultCallNumberLessThanOrEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2077,7 +2076,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => { Instruction::DefaultExecuteNumberLessThanOrEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2090,7 +2089,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultCallNumberNotEqual(ref at_1, ref at_2) => { Instruction::DefaultCallNumberNotEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2103,7 +2102,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultExecuteNumberNotEqual(ref at_1, ref at_2) => { Instruction::DefaultExecuteNumberNotEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2116,7 +2115,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultCallNumberEqual(ref at_1, ref at_2) => { Instruction::DefaultCallNumberEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2129,7 +2128,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultExecuteNumberEqual(ref at_1, ref at_2) => { Instruction::DefaultExecuteNumberEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2142,7 +2141,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultCallNumberGreaterThanOrEqual(ref at_1, ref at_2) => { Instruction::DefaultCallNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2155,7 +2154,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => { Instruction::DefaultExecuteNumberGreaterThanOrEqual(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2168,7 +2167,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultCallNumberGreaterThan(ref at_1, ref at_2) => { Instruction::DefaultCallNumberGreaterThan(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2181,7 +2180,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultExecuteNumberGreaterThan(ref at_1, ref at_2) => { Instruction::DefaultExecuteNumberGreaterThan(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2194,7 +2193,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultCallNumberLessThan(ref at_1, ref at_2) => { Instruction::DefaultCallNumberLessThan(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2207,7 +2206,7 @@ impl Machine {
} }
} }
} }
&Instruction::DefaultExecuteNumberLessThan(ref at_1, ref at_2) => { Instruction::DefaultExecuteNumberLessThan(ref at_1, ref at_2) => {
let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1)); let n1 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_1));
let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2)); let n2 = try_or_throw!(self.machine_st, self.machine_st.get_number(at_2));
@@ -2955,7 +2954,7 @@ impl Machine {
self.machine_st.p += 1; self.machine_st.p += 1;
} }
&Instruction::IndexingCode(ref indexing_lines) => { Instruction::IndexingCode(ref indexing_lines) => {
match &indexing_lines[self.machine_st.oip as usize] { match &indexing_lines[self.machine_st.oip as usize] {
IndexingLine::Indexing(_) => { IndexingLine::Indexing(_) => {
self.execute_switch_on_term(); self.execute_switch_on_term();
@@ -2965,22 +2964,22 @@ impl Machine {
} }
} }
IndexingLine::IndexedChoice(ref indexed_choice) => { IndexingLine::IndexedChoice(ref indexed_choice) => {
match &indexed_choice[self.machine_st.iip as usize] { match indexed_choice[self.machine_st.iip as usize] {
&IndexedChoiceInstruction::Try(offset) => { IndexedChoiceInstruction::Try(offset) => {
self.indexed_try(offset); self.indexed_try(offset);
} }
&IndexedChoiceInstruction::Retry(l) => { IndexedChoiceInstruction::Retry(l) => {
self.retry(l); self.retry(l);
increment_call_count!(self.machine_st); increment_call_count!(self.machine_st);
} }
&IndexedChoiceInstruction::DefaultRetry(l) => { IndexedChoiceInstruction::DefaultRetry(l) => {
self.retry(l); self.retry(l);
} }
&IndexedChoiceInstruction::Trust(l) => { IndexedChoiceInstruction::Trust(l) => {
self.trust(l); self.trust(l);
increment_call_count!(self.machine_st); increment_call_count!(self.machine_st);
} }
&IndexedChoiceInstruction::DefaultTrust(l) => { IndexedChoiceInstruction::DefaultTrust(l) => {
self.trust(l); self.trust(l);
} }
} }
@@ -3686,6 +3685,16 @@ impl Machine {
try_or_throw!(self.machine_st, self.install_inference_counter()); try_or_throw!(self.machine_st, self.install_inference_counter());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
&Instruction::CallInferenceCount => {
let global_count = self.machine_st.cwil.global_count.clone();
self.inference_count(self.machine_st.registers[1], global_count);
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteInferenceCount => {
let global_count = self.machine_st.cwil.global_count.clone();
self.inference_count(self.machine_st.registers[1], global_count);
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallLiftedHeapLength => { &Instruction::CallLiftedHeapLength => {
self.lifted_heap_length(); self.lifted_heap_length();
step_or_fail!(self, self.machine_st.p += 1); step_or_fail!(self, self.machine_st.p += 1);
@@ -4139,6 +4148,22 @@ impl Machine {
try_or_throw!(self.machine_st, self.define_foreign_struct()); try_or_throw!(self.machine_st, self.define_foreign_struct());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
&Instruction::CallJsEval => {
try_or_throw!(self.machine_st, self.js_eval());
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteJsEval => {
try_or_throw!(self.machine_st, self.js_eval());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallArgv => {
try_or_throw!(self.machine_st, self.argv());
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteArgv => {
try_or_throw!(self.machine_st, self.argv());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallCurrentTime => { &Instruction::CallCurrentTime => {
self.current_time(); self.current_time();
step_or_fail!(self, self.machine_st.p += 1); step_or_fail!(self, self.machine_st.p += 1);
@@ -4439,6 +4464,14 @@ impl Machine {
self.crypto_data_hash(); self.crypto_data_hash();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
&Instruction::CallCryptoHMAC => {
self.crypto_hmac();
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteCryptoHMAC => {
self.crypto_hmac();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallCryptoDataHKDF => { &Instruction::CallCryptoDataHKDF => {
self.crypto_data_hkdf(); self.crypto_data_hkdf();
step_or_fail!(self, self.machine_st.p += 1); step_or_fail!(self, self.machine_st.p += 1);
@@ -4483,44 +4516,28 @@ impl Machine {
self.crypto_curve_scalar_mult(); self.crypto_curve_scalar_mult();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
#[cfg(feature = "crypto-full")] &Instruction::CallEd25519SignRaw => {
&Instruction::CallEd25519Sign => { self.ed25519_sign_raw();
self.ed25519_sign();
step_or_fail!(self, self.machine_st.p += 1); step_or_fail!(self, self.machine_st.p += 1);
} }
#[cfg(feature = "crypto-full")] &Instruction::ExecuteEd25519SignRaw => {
&Instruction::ExecuteEd25519Sign => { self.ed25519_sign_raw();
self.ed25519_sign();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
#[cfg(feature = "crypto-full")] &Instruction::CallEd25519VerifyRaw => {
&Instruction::CallEd25519Verify => { self.ed25519_verify_raw();
self.ed25519_verify();
step_or_fail!(self, self.machine_st.p += 1); step_or_fail!(self, self.machine_st.p += 1);
} }
#[cfg(feature = "crypto-full")] &Instruction::ExecuteEd25519VerifyRaw => {
&Instruction::ExecuteEd25519Verify => { self.ed25519_verify_raw();
self.ed25519_verify();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
#[cfg(feature = "crypto-full")] &Instruction::CallEd25519SeedToPublicKey => {
&Instruction::CallEd25519NewKeyPair => { self.ed25519_seed_to_public_key();
self.ed25519_new_key_pair();
step_or_fail!(self, self.machine_st.p += 1); step_or_fail!(self, self.machine_st.p += 1);
} }
#[cfg(feature = "crypto-full")] &Instruction::ExecuteEd25519SeedToPublicKey => {
&Instruction::ExecuteEd25519NewKeyPair => { self.ed25519_seed_to_public_key();
self.ed25519_new_key_pair();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
#[cfg(feature = "crypto-full")]
&Instruction::CallEd25519KeyPairPublicKey => {
self.ed25519_key_pair_public_key();
step_or_fail!(self, self.machine_st.p += 1);
}
#[cfg(feature = "crypto-full")]
&Instruction::ExecuteEd25519KeyPairPublicKey => {
self.ed25519_key_pair_public_key();
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
&Instruction::CallCurve25519ScalarMult => { &Instruction::CallCurve25519ScalarMult => {
@@ -5089,16 +5106,13 @@ impl Machine {
.get_predicate_skeleton_mut(&compilation_target, &key) .get_predicate_skeleton_mut(&compilation_target, &key)
.unwrap(); .unwrap();
match skeleton.target_pos_of_clause_clause_loc(l) { if let Some(n) = skeleton.target_pos_of_clause_clause_loc(l) {
Some(n) => { let r = self
let r = self .machine_st
.machine_st .store(self.machine_st.deref(self.machine_st.registers[5]));
.store(self.machine_st.deref(self.machine_st.registers[5]));
self.machine_st self.machine_st
.unify_fixnum(Fixnum::build_with(n as i64), r); .unify_fixnum(Fixnum::build_with(n as i64), r);
}
None => {}
} }
self.machine_st.call_at_index(2, p); self.machine_st.call_at_index(2, p);
@@ -5135,16 +5149,13 @@ impl Machine {
.get_predicate_skeleton_mut(&compilation_target, &key) .get_predicate_skeleton_mut(&compilation_target, &key)
.unwrap(); .unwrap();
match skeleton.target_pos_of_clause_clause_loc(l) { if let Some(n) = skeleton.target_pos_of_clause_clause_loc(l) {
Some(n) => { let r = self
let r = self .machine_st
.machine_st .store(self.machine_st.deref(self.machine_st.registers[5]));
.store(self.machine_st.deref(self.machine_st.registers[5]));
self.machine_st self.machine_st
.unify_fixnum(Fixnum::build_with(n as i64), r); .unify_fixnum(Fixnum::build_with(n as i64), r);
}
None => {}
} }
self.machine_st.execute_at_index(2, p); self.machine_st.execute_at_index(2, p);
@@ -5226,7 +5237,7 @@ impl Machine {
// So we only have access to a runtime handle in here and can't shut it down. // So we only have access to a runtime handle in here and can't shut it down.
// Since I'm not aware of the consequences of deactivating this new code which came in while PR 1880 // Since I'm not aware of the consequences of deactivating this new code which came in while PR 1880
// was not merged, I'm only deactivating it for now. // was not merged, I'm only deactivating it for now.
//#[cfg(not(target_arch = "wasm32"))] //#[cfg(not(target_arch = "wasm32"))]
//let runtime = tokio::runtime::Runtime::new().unwrap(); //let runtime = tokio::runtime::Runtime::new().unwrap();
//#[cfg(target_arch = "wasm32")] //#[cfg(target_arch = "wasm32")]

View File

@@ -9,14 +9,22 @@ pub(crate) trait UnmarkPolicy {
fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter<Self>) -> Option<HeapCellValue> fn forward_attr_var(iter: &mut StacklessPreOrderHeapIter<Self>) -> Option<HeapCellValue>
where where
Self: Sized; Self: Sized;
fn invert_marker(iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized; fn invert_marker(iter: &mut StacklessPreOrderHeapIter<Self>)
where
Self: Sized;
fn mark_phase(&self) -> bool; fn mark_phase(&self) -> bool;
#[inline] #[inline]
fn report_var_link(iter: &StacklessPreOrderHeapIter<Self>) -> bool where Self: Sized { fn report_var_link(iter: &StacklessPreOrderHeapIter<Self>) -> bool
where
Self: Sized,
{
iter.heap[iter.next as usize].get_mark_bit() == iter.iter_state.mark_phase() iter.heap[iter.next as usize].get_mark_bit() == iter.iter_state.mark_phase()
} }
#[inline(always)] #[inline(always)]
fn record_focus(_iter: &mut StacklessPreOrderHeapIter<Self>) where Self: Sized { fn record_focus(_iter: &mut StacklessPreOrderHeapIter<Self>)
where
Self: Sized,
{
} }
} }
@@ -34,7 +42,7 @@ fn invert_marker<UMP: UnmarkPolicy>(iter: &mut StacklessPreOrderHeapIter<UMP>) {
iter.next = iter.heap[iter.start].get_value(); iter.next = iter.heap[iter.start].get_value();
iter.current = iter.start; iter.current = iter.start;
while let Some(_) = iter.forward() {} while iter.forward().is_some() {}
} }
impl UnmarkPolicy for IteratorUMP { impl UnmarkPolicy for IteratorUMP {
@@ -139,7 +147,7 @@ impl<'a> StacklessPreOrderHeapIter<'a, IteratorUMP> {
start, start,
current: start, current: start,
next, next,
iter_state: IteratorUMP { mark_phase: true,}, iter_state: IteratorUMP { mark_phase: true },
} }
} }
} }
@@ -189,11 +197,9 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
return Some(cell); return Some(cell);
} }
if self.next < self.heap.len() as u64 { if self.next < self.heap.len() as u64 && UMP::report_var_link(self) {
if UMP::report_var_link(self) { let tag = HeapCellValueTag::AttrVar;
let tag = HeapCellValueTag::AttrVar; return Some(HeapCellValue::build_with(tag, next as u64));
return Some(HeapCellValue::build_with(tag, next as u64));
}
} }
} }
HeapCellValueTag::Var => { HeapCellValueTag::Var => {
@@ -203,11 +209,9 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
return Some(cell); return Some(cell);
} }
if self.next < self.heap.len() as u64 { if self.next < self.heap.len() as u64 && UMP::report_var_link(self) {
if UMP::report_var_link(self) { let tag = HeapCellValueTag::Var;
let tag = HeapCellValueTag::Var; return Some(HeapCellValue::build_with(tag, next as u64));
return Some(HeapCellValue::build_with(tag, next as u64));
}
} }
} }
HeapCellValueTag::Str => { HeapCellValueTag::Str => {
@@ -311,10 +315,8 @@ impl<'a, UMP: UnmarkPolicy> StacklessPreOrderHeapIter<'a, UMP> {
return Some(self.backward_and_return()); return Some(self.backward_and_return());
} }
} }
} else { } else if self.backward() {
if self.backward() { return None;
return None;
}
} }
} }
} }
@@ -358,7 +360,7 @@ impl<'a, UMP: UnmarkPolicy> Iterator for StacklessPreOrderHeapIter<'a, UMP> {
pub fn mark_cells(heap: &mut Heap, start: usize) { pub fn mark_cells(heap: &mut Heap, start: usize) {
let mut iter = StacklessPreOrderHeapIter::<MarkerUMP>::new(heap, start); let mut iter = StacklessPreOrderHeapIter::<MarkerUMP>::new(heap, start);
while let Some(_) = iter.forward() {} while iter.forward().is_some() {}
} }
#[cfg(test)] #[cfg(test)]
@@ -367,6 +369,7 @@ mod tests {
use crate::machine::mock_wam::*; use crate::machine::mock_wam::*;
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn heap_marking_tests() { fn heap_marking_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
@@ -665,14 +668,18 @@ mod tests {
wam.machine_st.heap.push(pstr_loc_as_cell!(1)); wam.machine_st.heap.push(pstr_loc_as_cell!(1));
let pstr_var_cell = put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl); let pstr_var_cell =
put_partial_string(&mut wam.machine_st.heap, "abc ", &wam.machine_st.atom_tbl);
let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize]; let pstr_cell = wam.machine_st.heap[pstr_var_cell.get_value() as usize];
mark_cells(&mut wam.machine_st.heap, 0); mark_cells(&mut wam.machine_st.heap, 0);
all_cells_marked_and_unforwarded(&wam.machine_st.heap); all_cells_marked_and_unforwarded(&wam.machine_st.heap);
assert_eq!(unmark_cell_bits!(wam.machine_st.heap[0]), pstr_loc_as_cell!(1)); assert_eq!(
unmark_cell_bits!(wam.machine_st.heap[0]),
pstr_loc_as_cell!(1)
);
assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell);
assert_eq!( assert_eq!(
unmark_cell_bits!(wam.machine_st.heap[2]), unmark_cell_bits!(wam.machine_st.heap[2]),
@@ -720,7 +727,7 @@ mod tests {
mark_cells(&mut wam.machine_st.heap, 7); mark_cells(&mut wam.machine_st.heap, 7);
all_cells_marked_and_unforwarded(&wam.machine_st.heap[1 ..]); all_cells_marked_and_unforwarded(&wam.machine_st.heap[1..]);
assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell); assert_eq!(unmark_cell_bits!(wam.machine_st.heap[1]), pstr_cell);
assert_eq!( assert_eq!(
@@ -1536,10 +1543,10 @@ mod tests {
mark_cells(&mut wam.machine_st.heap, 0); mark_cells(&mut wam.machine_st.heap, 0);
all_cells_marked_and_unforwarded(&mut wam.machine_st.heap[0..24]); all_cells_marked_and_unforwarded(&wam.machine_st.heap[0..24]);
for cell in &wam.machine_st.heap[24..] { for cell in &wam.machine_st.heap[24..] {
assert_eq!(cell.get_mark_bit(), false); assert!(!cell.get_mark_bit());
} }
assert_eq!( assert_eq!(

View File

@@ -169,7 +169,7 @@ pub(crate) fn allocate_pstr(heap: &mut Heap, mut src: &str, atom_tbl: &AtomTable
let orig_h = heap.len(); let orig_h = heap.len();
loop { loop {
if src == "" { if src.is_empty() {
return if orig_h == heap.len() { return if orig_h == heap.len() {
None None
} else { } else {
@@ -199,7 +199,7 @@ pub(crate) fn allocate_pstr(heap: &mut Heap, mut src: &str, atom_tbl: &AtomTable
heap.push(string_as_pstr_cell!(pstr)); heap.push(string_as_pstr_cell!(pstr));
if rest_src != "" { if !rest_src.is_empty() {
heap.push(pstr_loc_as_cell!(h + 2)); heap.push(pstr_loc_as_cell!(h + 2));
src = rest_src; src = rest_src;
} else { } else {
@@ -249,7 +249,7 @@ pub(crate) fn to_local_code_ptr(heap: &Heap, addr: HeapCellValue) -> Option<usiz
Ok(Number::Integer(n)) => { Ok(Number::Integer(n)) => {
let value: usize = (&*n).try_into().unwrap(); let value: usize = (&*n).try_into().unwrap();
Some(value) Some(value)
}, }
_ => None, _ => None,
} }
}; };

File diff suppressed because one or more lines are too long

View File

@@ -3,18 +3,17 @@ use std::sync::Arc;
use crate::atom_table; use crate::atom_table;
use crate::heap_print::{HCPrinter, HCValueOutputter, PrinterOutputter}; use crate::heap_print::{HCPrinter, HCValueOutputter, PrinterOutputter};
use crate::machine::{BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS}; use crate::machine::machine_indices::VarKey;
use crate::machine::mock_wam::CompositeOpDir; use crate::machine::mock_wam::CompositeOpDir;
use crate::machine::{BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS};
use crate::parser::ast::{Var, VarPtr};
use crate::parser::parser::{Parser, Tokens}; use crate::parser::parser::{Parser, Tokens};
use crate::read::write_term_to_heap; use crate::read::write_term_to_heap;
use crate::machine::machine_indices::VarKey;
use crate::parser::ast::{Var, VarPtr};
use indexmap::IndexMap; use indexmap::IndexMap;
use super::{ use super::{
Machine, MachineConfig, QueryResult, QueryResolutionLine, streams::Stream, Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine, MachineConfig,
Atom, AtomCell, HeapCellValue, HeapCellValueTag, Value, QueryResolution, QueryResolution, QueryResolutionLine, QueryResult, Value,
streams::Stream
}; };
impl Machine { impl Machine {
@@ -30,17 +29,20 @@ impl Machine {
pub fn consult_module_string(&mut self, module_name: &str, program: String) { pub fn consult_module_string(&mut self, module_name: &str, program: String) {
let stream = Stream::from_owned_string(program, &mut self.machine_st.arena); let stream = Stream::from_owned_string(program, &mut self.machine_st.arena);
self.machine_st.registers[1] = stream_as_cell!(stream); self.machine_st.registers[1] = stream_as_cell!(stream);
self.machine_st.registers[2] = atom_as_cell!(&atom_table::AtomTable::build_with(&self.machine_st.atom_tbl, module_name)); self.machine_st.registers[2] = atom_as_cell!(&atom_table::AtomTable::build_with(
&self.machine_st.atom_tbl,
module_name
));
self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2)); self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2));
} }
fn allocate_stub_choice_point(&mut self) { fn allocate_stub_choice_point(&mut self) {
// NOTE: create a choice point to terminate the dispatch_loop // NOTE: create a choice point to terminate the dispatch_loop
// if an exception is thrown. since the and/or stack is presumed empty, // if an exception is thrown.
let stub_b = self.machine_st.stack.allocate_or_frame(0); let stub_b = self.machine_st.stack.allocate_or_frame(0);
let or_frame = self.machine_st.stack.index_or_frame_mut(0); let or_frame = self.machine_st.stack.index_or_frame_mut(stub_b);
or_frame.prelude.num_cells = 0; or_frame.prelude.num_cells = 0;
or_frame.prelude.e = 0; or_frame.prelude.e = 0;
@@ -55,28 +57,32 @@ impl Machine {
or_frame.prelude.attr_var_queue_len = 0; or_frame.prelude.attr_var_queue_len = 0;
self.machine_st.b = stub_b; self.machine_st.b = stub_b;
self.machine_st.hb = self.machine_st.heap.len();
self.machine_st.block = stub_b;
} }
pub fn run_query(&mut self, query: String) -> QueryResult { pub fn run_query(&mut self, query: String) -> QueryResult {
println!("Query: {}", query); // println!("Query: {}", query);
// Parse the query so we can analyze and then call the term // Parse the query so we can analyze and then call the term
let mut parser = Parser::new( let mut parser = Parser::new(
Stream::from_owned_string(query, &mut self.machine_st.arena), Stream::from_owned_string(query, &mut self.machine_st.arena),
&mut self.machine_st &mut self.machine_st,
); );
let op_dir = CompositeOpDir::new(&self.indices.op_dir, None); let op_dir = CompositeOpDir::new(&self.indices.op_dir, None);
let term = parser.read_term(&op_dir, Tokens::Default).expect("Failed to parse query"); let term = parser
.read_term(&op_dir, Tokens::Default)
.expect("Failed to parse query");
self.allocate_stub_choice_point();
// Write parsed term to heap // Write parsed term to heap
let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap, &mut self.machine_st.atom_tbl).expect("couldn't write term to heap"); let term_write_result =
write_term_to_heap(&term, &mut self.machine_st.heap, &self.machine_st.atom_tbl)
.expect("couldn't write term to heap");
// Write term to heap let var_names: IndexMap<_, _> = term_write_result
self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc]; .var_dict
.iter()
self.machine_st.cp = LIB_QUERY_SUCCESS; // BREAK_FROM_DISPATCH_LOOP_LOC;
self.machine_st.p = self.indices.code_dir.get(&(atom!("call"), 1)).expect("couldn't get code index").local().unwrap();
let var_names: IndexMap<_, _> = term_write_result.var_dict.iter()
.map(|(var_key, cell)| match var_key { .map(|(var_key, cell)| match var_key {
// NOTE: not the intention behind Var::InSitu here but // NOTE: not the intention behind Var::InSitu here but
// we can hijack it to store anonymous variables // we can hijack it to store anonymous variables
@@ -86,7 +92,19 @@ impl Machine {
}) })
.collect(); .collect();
self.allocate_stub_choice_point(); // Write term to heap
self.machine_st.registers[1] = self.machine_st.heap[term_write_result.heap_loc];
self.machine_st.cp = LIB_QUERY_SUCCESS; // BREAK_FROM_DISPATCH_LOOP_LOC;
let call_index_p = self
.indices
.code_dir
.get(&(atom!("call"), 1))
.expect("couldn't get code index")
.local()
.unwrap();
self.machine_st.execute_at_index(1, call_index_p);
let stub_b = self.machine_st.b; let stub_b = self.machine_st.b;
@@ -99,27 +117,29 @@ impl Machine {
//println!("stub_b: {}", stub_b); //println!("stub_b: {}", stub_b);
//println!("fail: {}", self.machine_st.fail); //println!("fail: {}", self.machine_st.fail);
if self.machine_st.ball.stub.len() != 0 { if !self.machine_st.ball.stub.is_empty() {
// NOTE: this means an exception was thrown, at which // NOTE: this means an exception was thrown, at which
// point we backtracked to the stub choice point. // point we backtracked to the stub choice point.
// this should halt the search for solutions as it // this should halt the search for solutions as it
// does in the Scryer top-level. the exception term is // does in the Scryer top-level. the exception term is
// contained in self.machine_st.ball. // contained in self.machine_st.ball.
let error_string = self.machine_st.ball.stub let error_string = self
.machine_st
.ball
.stub
.iter() .iter()
.filter(|h| match h.get_tag() { .filter(|h| {
HeapCellValueTag::Atom => true, matches!(
HeapCellValueTag::Fixnum => true, h.get_tag(),
_ => false, HeapCellValueTag::Atom | HeapCellValueTag::Fixnum
)
}) })
.map(|h| match h.get_tag() { .map(|h| match h.get_tag() {
HeapCellValueTag::Atom => { HeapCellValueTag::Atom => {
let (name, _) = cell_as_atom_cell!(h).get_name_and_arity(); let (name, _) = cell_as_atom_cell!(h).get_name_and_arity();
name.as_str().to_string() name.as_str().to_string()
} }
HeapCellValueTag::Fixnum => { HeapCellValueTag::Fixnum => h.get_value().clone().to_string(),
h.get_value().clone().to_string()
},
_ => unreachable!(), _ => unreachable!(),
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
@@ -138,23 +158,23 @@ impl Machine {
}; };
*/ */
if term_write_result.var_dict.is_empty() { if self.machine_st.p == LIB_QUERY_SUCCESS {
if self.machine_st.p == LIB_QUERY_SUCCESS { if term_write_result.var_dict.is_empty() {
matches.push(QueryResolutionLine::True); matches.push(QueryResolutionLine::True);
break; break;
} else if self.machine_st.p == BREAK_FROM_DISPATCH_LOOP_LOC {
// NOTE: only print results on success
// self.machine_st.fail = false;
// println!("b == stub_b");
matches.push(QueryResolutionLine::False);
break;
} }
} else if self.machine_st.p == BREAK_FROM_DISPATCH_LOOP_LOC {
// NOTE: only print results on success
// self.machine_st.fail = false;
// println!("b == stub_b");
matches.push(QueryResolutionLine::False);
break;
} }
let mut bindings: BTreeMap<String, Value> = BTreeMap::new(); let mut bindings: BTreeMap<String, Value> = BTreeMap::new();
for (var_key, term_to_be_printed) in &term_write_result.var_dict { for (var_key, term_to_be_printed) in &term_write_result.var_dict {
if var_key.to_string().starts_with("_") { if var_key.to_string().starts_with('_') {
continue; continue;
} }
let mut printer = HCPrinter::new( let mut printer = HCPrinter::new(
@@ -176,9 +196,14 @@ impl Machine {
let outputter = printer.print(); let outputter = printer.print();
let output: String = outputter.result(); let output: String = outputter.result();
println!("Result: {} = {}", var_key.to_string(), output); // println!("Result: {} = {}", var_key.to_string(), output);
bindings.insert(var_key.to_string(), Value::try_from(output).expect("asdfs")); if var_key.to_string() != output {
bindings.insert(
var_key.to_string(),
Value::try_from(output).expect("Couldn't convert Houtput to Value"),
);
}
} }
matches.push(QueryResolutionLine::Match(bindings)); matches.push(QueryResolutionLine::Match(bindings));
@@ -210,9 +235,10 @@ mod tests {
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use super::*; use super::*;
use crate::machine::{QueryMatch, Value, QueryResolution}; use crate::machine::{QueryMatch, QueryResolution, Value};
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn programatic_query() { fn programatic_query() {
let mut machine = Machine::new_lib(); let mut machine = Machine::new_lib();
@@ -252,17 +278,21 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn failing_query() { fn failing_query() {
let mut machine = Machine::new_lib(); let mut machine = Machine::new_lib();
let query = String::from(r#"triple("a",P,"b")."#); let query = String::from(r#"triple("a",P,"b")."#);
let output = machine.run_query(query); let output = machine.run_query(query);
assert_eq!( assert_eq!(
output, output,
Err(String::from("error existence_error procedure / triple 3 / triple 3")) Err(String::from(
"error existence_error procedure / triple 3 / triple 3"
))
); );
} }
#[test] #[test]
#[cfg_attr(miri, ignore)]
fn complex_results() { fn complex_results() {
let mut machine = Machine::new_lib(); let mut machine = Machine::new_lib();
machine.load_module_string( machine.load_module_string(
@@ -278,26 +308,30 @@ mod tests {
constructor(xyz, '[{action: "addLink", source: "this", predicate: "recipe://title", target: "literal://string:Meta%20Muffins"}]'). constructor(xyz, '[{action: "addLink", source: "this", predicate: "recipe://title", target: "literal://string:Meta%20Muffins"}]').
"#.to_string()); "#.to_string());
let result = machine.run_query(String::from("subject_class(\"Todo\", C), constructor(C, Actions).")); let result = machine.run_query(String::from(
"subject_class(\"Todo\", C), constructor(C, Actions).",
));
assert_eq!( assert_eq!(
result, result,
Ok(QueryResolution::Matches(vec![ Ok(QueryResolution::Matches(vec![QueryMatch::from(
QueryMatch::from(btreemap! { btreemap! {
"C" => Value::from("c"), "C" => Value::from("c"),
"Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"todo://state\", target: \"todo://ready\"}]"), "Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"todo://state\", target: \"todo://ready\"}]"),
}), }
])) ),]))
); );
let result = machine.run_query(String::from("subject_class(\"Recipe\", C), constructor(C, Actions).")); let result = machine.run_query(String::from(
"subject_class(\"Recipe\", C), constructor(C, Actions).",
));
assert_eq!( assert_eq!(
result, result,
Ok(QueryResolution::Matches(vec![ Ok(QueryResolution::Matches(vec![QueryMatch::from(
QueryMatch::from(btreemap! { btreemap! {
"C" => Value::from("xyz"), "C" => Value::from("xyz"),
"Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"recipe://title\", target: \"literal://string:Meta%20Muffins\"}]"), "Actions" => Value::from("[{action: \"addLink\", source: \"this\", predicate: \"recipe://title\", target: \"literal://string:Meta%20Muffins\"}]"),
}), }
])) ),]))
); );
let result = machine.run_query(String::from("subject_class(Class, _).")); let result = machine.run_query(String::from("subject_class(Class, _)."));
@@ -315,19 +349,38 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn empty_predicate() {
let mut machine = Machine::new_lib();
machine.load_module_string(
"facts",
r#"
:- discontiguous(subject_class/2).
"#
.to_string(),
);
let result = machine.run_query(String::from("subject_class(X, _)."));
assert_eq!(result, Ok(QueryResolution::False));
}
#[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn list_results() { fn list_results() {
let mut machine = Machine::new_lib(); let mut machine = Machine::new_lib();
machine.load_module_string( machine.load_module_string(
"facts", "facts",
r#" r#"
list([1,2,3]). list([1,2,3]).
"#.to_string()); "#
.to_string(),
);
let result = machine.run_query(String::from("list(X).")); let result = machine.run_query(String::from("list(X)."));
assert_eq!( assert_eq!(
result, result,
Ok(QueryResolution::Matches(vec![ Ok(QueryResolution::Matches(vec![QueryMatch::from(
QueryMatch::from(btreemap! { btreemap! {
"X" => Value::List( "X" => Value::List(
Vec::from([ Vec::from([
Value::Float(OrderedFloat::from(1.0)), Value::Float(OrderedFloat::from(1.0)),
@@ -335,13 +388,13 @@ mod tests {
Value::Float(OrderedFloat::from(3.0)) Value::Float(OrderedFloat::from(3.0))
]) ])
) )
}), }
])) ),]))
); );
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn consult() { fn consult() {
let mut machine = Machine::new_lib(); let mut machine = Machine::new_lib();
@@ -397,12 +450,11 @@ mod tests {
machine.run_query(String::from(r#"triple("a","new","b")."#)), machine.run_query(String::from(r#"triple("a","new","b")."#)),
Ok(QueryResolution::True) Ok(QueryResolution::True)
); );
} }
#[ignore = "fails on windows"]
#[test] #[test]
fn stress_integration_test() { #[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn integration_test() {
let mut machine = Machine::new_lib(); let mut machine = Machine::new_lib();
// File with test commands, i.e. program code to consult and queries to run // File with test commands, i.e. program code to consult and queries to run
@@ -412,6 +464,7 @@ mod tests {
let blocks = code.split("====="); let blocks = code.split("=====");
let mut i = 0; let mut i = 0;
let mut last_result: Option<_> = None;
// Iterate over the blocks // Iterate over the blocks
for block in blocks { for block in blocks {
// Trim the block to remove any leading or trailing whitespace // Trim the block to remove any leading or trailing whitespace
@@ -423,32 +476,31 @@ mod tests {
} }
// Check if the block is a query // Check if the block is a query
if block.starts_with("query") { if let Some(query) = block.strip_prefix("query") {
// Extract the query from the block
let query = &block[5..];
i += 1;
println!("query #{}: {}", i, query);
// Parse and execute the query // Parse and execute the query
let result = machine.run_query(query.to_string()); let result = machine.run_query(query.to_string());
assert!(result.is_ok()); assert!(result.is_ok());
// Print the result last_result = Some(result);
println!("{:?}", result); } else if let Some(code) = block.strip_prefix("consult") {
} else if block.starts_with("consult") {
// Extract the code from the block
let code = &block[7..];
println!("load code: {}", code);
// Load the code into the machine // Load the code into the machine
machine.consult_module_string("facts", code.to_string()); machine.consult_module_string("facts", code.to_string());
} else if let Some(result) = block.strip_prefix("result") {
i += 1;
if let Some(Ok(ref last_result)) = last_result {
println!(
"\n\n=====Result No. {}=======\n{}\n===============",
i,
last_result.to_string().trim()
);
assert_eq!(last_result.to_string().trim(), result.to_string().trim(),)
}
} }
} }
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn findall() { fn findall() {
let mut machine = Machine::new_lib(); let mut machine = Machine::new_lib();
@@ -462,23 +514,99 @@ mod tests {
), ),
); );
let query = String::from(r#"findall([Predicate, Target], triple(_,Predicate,Target), Result)."#); let query =
String::from(r#"findall([Predicate, Target], triple(_,Predicate,Target), Result)."#);
let output = machine.run_query(query); let output = machine.run_query(query);
assert_eq!( assert_eq!(
output, output,
Ok(QueryResolution::Matches(vec![ Ok(QueryResolution::Matches(vec![QueryMatch::from(
QueryMatch::from(btreemap! { btreemap! {
"Predicate" => Value::from("Predicate"),
"Result" => Value::List( "Result" => Value::List(
Vec::from([ Vec::from([
Value::List([Value::from("p1"), Value::from("b")].into()), Value::List([Value::from("p1"), Value::from("b")].into()),
Value::List([Value::from("p2"), Value::from("b")].into()), Value::List([Value::from("p2"), Value::from("b")].into()),
]) ])
), ),
"Target" => Value::from("Target"), }
}), ),]))
])) );
}
#[test]
fn dont_return_partial_matches() {
let mut machine = Machine::new_lib();
machine.consult_module_string(
"facts",
String::from(
r#"
:- discontiguous(property_resolve/2).
subject_class("Todo", c).
"#,
),
); );
let query = String::from(r#"property_resolve(C, "isLiked"), subject_class("Todo", C)."#);
let output = machine.run_query(query);
assert_eq!(output, Ok(QueryResolution::False));
let query = String::from(r#"subject_class("Todo", C), property_resolve(C, "isLiked")."#);
let output = machine.run_query(query);
assert_eq!(output, Ok(QueryResolution::False));
}
#[test]
fn dont_return_partial_matches_without_discountiguous() {
let mut machine = Machine::new_lib();
machine.consult_module_string(
"facts",
String::from(
r#"
a("true for a").
b("true for b").
"#,
),
);
let query = String::from(r#"a("true for a")."#);
let output = machine.run_query(query);
assert_eq!(output, Ok(QueryResolution::True));
let query = String::from(r#"a("true for a"), b("true for b")."#);
let output = machine.run_query(query);
assert_eq!(output, Ok(QueryResolution::True));
let query = String::from(r#"a("true for b"), b("true for b")."#);
let output = machine.run_query(query);
assert_eq!(output, Ok(QueryResolution::False));
let query = String::from(r#"a("true for a"), b("true for a")."#);
let output = machine.run_query(query);
assert_eq!(output, Ok(QueryResolution::False));
}
#[test]
fn non_existent_predicate_should_not_cause_panic_when_other_predicates_are_defined() {
let mut machine = Machine::new_lib();
machine.consult_module_string(
"facts",
String::from(
r#"
triple("a", "p1", "b").
triple("a", "p2", "b").
"#,
),
);
let query = String::from("non_existent_predicate(\"a\",\"p1\",\"b\").");
let result = machine.run_query(query);
assert_eq!(
result,
Err(String::from("error existence_error procedure / non_existent_predicate 3 / non_existent_predicate 3"))
);
} }
} }

View File

@@ -137,10 +137,9 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>(
if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() { if let Some(src_code_index) = imported_module.code_dir.get(&key).cloned() {
let arena = &mut LS::machine_st(payload).arena; let arena = &mut LS::machine_st(payload).arena;
let target_code_index = code_dir let target_code_index = *code_dir
.entry(key) .entry(key)
.or_insert_with(|| CodeIndex::default(arena)) .or_insert_with(|| CodeIndex::default(arena));
.clone();
set_code_index( set_code_index(
&mut payload.retraction_info, &mut payload.retraction_info,
@@ -189,16 +188,15 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>(
let key = (*name, *arity); let key = (*name, *arity);
if let Some(meta_specs) = imported_module.meta_predicates.get(&key) { if let Some(meta_specs) = imported_module.meta_predicates.get(&key) {
meta_predicates.insert(key.clone(), meta_specs.clone()); meta_predicates.insert(key, meta_specs.clone());
} }
if let Some(src_code_index) = imported_module.code_dir.get(&key) { if let Some(src_code_index) = imported_module.code_dir.get(&key) {
let arena = &mut LS::machine_st(payload).arena; let arena = &mut LS::machine_st(payload).arena;
let target_code_index = code_dir let target_code_index = *code_dir
.entry(key) .entry(key)
.or_insert_with(|| CodeIndex::default(arena)) .or_insert_with(|| CodeIndex::default(arena));
.clone();
set_code_index( set_code_index(
&mut payload.retraction_info, &mut payload.retraction_info,
@@ -209,7 +207,7 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>(
); );
} else { } else {
return Err(SessionError::ModuleDoesNotContainExport( return Err(SessionError::ModuleDoesNotContainExport(
imported_module.module_decl.name.clone(), imported_module.module_decl.name,
(*name, *arity), (*name, *arity),
)); ));
} }
@@ -243,18 +241,17 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>(
wam_prelude wam_prelude
.indices .indices
.meta_predicates .meta_predicates
.insert(key.clone(), meta_specs.clone()); .insert(key, meta_specs.clone());
} }
if let Some(src_code_index) = imported_module.code_dir.get(&key) { if let Some(src_code_index) = imported_module.code_dir.get(&key) {
let arena = &mut LS::machine_st(payload).arena; let arena = &mut LS::machine_st(payload).arena;
let target_code_index = wam_prelude let target_code_index = *wam_prelude
.indices .indices
.code_dir .code_dir
.entry(key.clone()) .entry(key)
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)) .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
.clone();
set_code_index( set_code_index(
&mut payload.retraction_info, &mut payload.retraction_info,
@@ -265,7 +262,7 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>(
); );
} else { } else {
return Err(SessionError::ModuleDoesNotContainExport( return Err(SessionError::ModuleDoesNotContainExport(
imported_module.module_decl.name.clone(), imported_module.module_decl.name,
(*name, *arity), (*name, *arity),
)); ));
} }
@@ -311,10 +308,9 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>(
if let Some(src_code_index) = imported_module.code_dir.get(&key) { if let Some(src_code_index) = imported_module.code_dir.get(&key) {
let arena = &mut LS::machine_st(payload).arena; let arena = &mut LS::machine_st(payload).arena;
let target_code_index = code_dir let target_code_index = *code_dir
.entry(key) .entry(key)
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)) .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
.clone();
set_code_index( set_code_index(
&mut payload.retraction_info, &mut payload.retraction_info,
@@ -325,7 +321,7 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>(
); );
} else { } else {
return Err(SessionError::ModuleDoesNotContainExport( return Err(SessionError::ModuleDoesNotContainExport(
imported_module.module_decl.name.clone(), imported_module.module_decl.name,
(*name, *arity), (*name, *arity),
)); ));
} }
@@ -423,7 +419,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
payload_compilation_target, payload_compilation_target,
clause_clause_compilation_target, clause_clause_compilation_target,
key, key,
mem::replace(&mut skeleton.clause_clause_locs, VecDeque::new()), std::mem::take(&mut skeleton.clause_clause_locs),
), ),
); );
@@ -436,7 +432,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
} }
}; };
self.retract_local_clauses_impl(clause_clause_compilation_target, key, &clause_locs); self.retract_local_clauses_impl(clause_clause_compilation_target, key, clause_locs);
} }
pub(super) fn try_term_to_tl( pub(super) fn try_term_to_tl(
@@ -470,24 +466,48 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
None => return, None => return,
}; };
for (key, code_index) in removed_module.code_dir.iter_mut() { let mut skipped_local_predicates = IndexSet::with_hasher(FxBuildHasher::default());
match removed_module
.local_extensible_predicates for ((local_compilation_target, key), skeleton) in
.get(&(CompilationTarget::User, *key)) removed_module.local_extensible_predicates.iter()
{ {
Some(skeleton) if skeleton.is_multifile => continue, skipped_local_predicates.insert(key);
_ => {}
if skeleton.is_multifile {
continue;
} }
let old_index_ptr = code_index.replace(IndexPtr::undefined()); if let Some(code_index) = removed_module.code_dir.get_mut(key) {
if let Some(global_skeleton) = self
.wam_prelude
.indices
.get_predicate_skeleton(local_compilation_target, key)
{
let old_index_ptr = code_index.replace(if global_skeleton.core.is_dynamic {
IndexPtr::dynamic_undefined()
} else {
IndexPtr::undefined()
});
self.payload self.payload.retraction_info.push_record(
.retraction_info RetractionRecord::ReplacedModulePredicate(module_name, *key, old_index_ptr),
.push_record(RetractionRecord::ReplacedModulePredicate( );
module_name, }
*key, }
old_index_ptr, }
));
for (key, code_index) in removed_module.code_dir.iter_mut() {
if skipped_local_predicates.contains(key) {
continue;
}
if !code_index.is_undefined() && !code_index.is_dynamic_undefined() {
let old_index_ptr = code_index.replace(IndexPtr::undefined());
self.payload.retraction_info.push_record(
RetractionRecord::ReplacedModulePredicate(module_name, *key, old_index_ptr),
);
}
} }
for (key, skeleton) in removed_module.extensible_predicates.drain(..) { for (key, skeleton) in removed_module.extensible_predicates.drain(..) {
@@ -600,30 +620,22 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
key: PredicateKey, key: PredicateKey,
) -> CodeIndex { ) -> CodeIndex {
match self.wam_prelude.indices.modules.get_mut(&module_name) { match self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(ref mut module) => module Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| {
.code_dir CodeIndex::new(
.entry(key) IndexPtr::undefined(),
.or_insert_with(|| { &mut LS::machine_st(&mut self.payload).arena,
CodeIndex::new( )
IndexPtr::undefined(), }),
&mut LS::machine_st(&mut self.payload).arena,
)
})
.clone(),
None => { None => {
self.add_dynamically_generated_module(module_name); self.add_dynamically_generated_module(module_name);
match self.wam_prelude.indices.modules.get_mut(&module_name) { match self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(ref mut module) => module Some(ref mut module) => *module.code_dir.entry(key).or_insert_with(|| {
.code_dir CodeIndex::new(
.entry(key) IndexPtr::undefined(),
.or_insert_with(|| { &mut LS::machine_st(&mut self.payload).arena,
CodeIndex::new( )
IndexPtr::undefined(), }),
&mut LS::machine_st(&mut self.payload).arena,
)
})
.clone(),
None => { None => {
unreachable!() unreachable!()
} }
@@ -640,13 +652,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
let arena = &mut LS::machine_st(&mut self.payload).arena; let arena = &mut LS::machine_st(&mut self.payload).arena;
match compilation_target { match compilation_target {
CompilationTarget::User => self CompilationTarget::User => *self
.wam_prelude .wam_prelude
.indices .indices
.code_dir .code_dir
.entry(key) .entry(key)
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)) .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)),
.clone(),
CompilationTarget::Module(module_name) => { CompilationTarget::Module(module_name) => {
self.get_or_insert_local_code_index(module_name, key) self.get_or_insert_local_code_index(module_name, key)
} }
@@ -661,13 +672,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
let arena = &mut LS::machine_st(&mut self.payload).arena; let arena = &mut LS::machine_st(&mut self.payload).arena;
if module_name == atom!("user") { if module_name == atom!("user") {
return self return *self
.wam_prelude .wam_prelude
.indices .indices
.code_dir .code_dir
.entry(key) .entry(key)
.or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena)) .or_insert_with(|| CodeIndex::new(IndexPtr::undefined(), arena));
.clone();
} else { } else {
self.get_or_insert_local_code_index(module_name, key) self.get_or_insert_local_code_index(module_name, key)
} }
@@ -694,7 +704,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
} }
CompilationTarget::Module(module_name) => { CompilationTarget::Module(module_name) => {
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
module.extensible_predicates.insert(key.clone(), skeleton); module.extensible_predicates.insert(key, skeleton);
let record = RetractionRecord::AddedExtensiblePredicate( let record = RetractionRecord::AddedExtensiblePredicate(
CompilationTarget::Module(module_name), CompilationTarget::Module(module_name),
@@ -747,11 +757,10 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
match payload_compilation_target { match payload_compilation_target {
CompilationTarget::User => { CompilationTarget::User => {
if let Some(filename) = listing_src_file_name { if let Some(filename) = listing_src_file_name {
match self.wam_prelude.indices.modules.get_mut(&filename) { if let Some(ref mut module) =
Some(ref mut module) => { self.wam_prelude.indices.modules.get_mut(&filename)
op_decl.insert_into_op_dir(&mut module.op_dir); {
} op_decl.insert_into_op_dir(&mut module.op_dir);
None => {}
} }
} }
@@ -855,48 +864,43 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
} }
} }
} }
_ => { _ => match self.wam_prelude.indices.modules.get_mut(&module_name) {
match self.wam_prelude.indices.modules.get_mut(&module_name) { Some(ref mut module) => match module.meta_predicates.insert(key, meta_specs) {
Some(ref mut module) => { Some(old_meta_specs) => {
match module.meta_predicates.insert(key.clone(), meta_specs) {
Some(old_meta_specs) => {
self.payload.retraction_info.push_record(
RetractionRecord::ReplacedMetaPredicate(
module_name,
key.0,
old_meta_specs,
),
);
}
None => {
self.payload.retraction_info.push_record(
RetractionRecord::AddedMetaPredicate(module_name, key),
);
}
}
}
None => {
self.add_dynamically_generated_module(module_name);
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name)
{
module.meta_predicates.insert(key.clone(), meta_specs);
} else {
unreachable!()
}
self.payload.retraction_info.push_record( self.payload.retraction_info.push_record(
RetractionRecord::AddedMetaPredicate(module_name.clone(), key), RetractionRecord::ReplacedMetaPredicate(
module_name,
key.0,
old_meta_specs,
),
); );
} }
None => {
self.payload
.retraction_info
.push_record(RetractionRecord::AddedMetaPredicate(module_name, key));
}
},
None => {
self.add_dynamically_generated_module(module_name);
if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
module.meta_predicates.insert(key, meta_specs);
} else {
unreachable!()
}
self.payload
.retraction_info
.push_record(RetractionRecord::AddedMetaPredicate(module_name, key));
} }
} },
} }
} }
pub(super) fn add_dynamically_generated_module(&mut self, module_name: Atom) { pub(super) fn add_dynamically_generated_module(&mut self, module_name: Atom) {
let module_decl = ModuleDecl { let module_decl = ModuleDecl {
name: module_name.clone(), name: module_name,
exports: vec![], exports: vec![],
}; };
@@ -912,12 +916,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
self.payload self.payload
.retraction_info .retraction_info
.push_record(RetractionRecord::AddedModule(module_name.clone())); .push_record(RetractionRecord::AddedModule(module_name));
self.wam_prelude self.wam_prelude.indices.modules.insert(module_name, module);
.indices
.modules
.insert(module_name.clone(), module);
} }
fn import_builtins_in_module( fn import_builtins_in_module(
@@ -956,51 +957,48 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
self.remove_module_exports(module_name); self.remove_module_exports(module_name);
self.remove_replaced_in_situ_module(module_name); self.remove_replaced_in_situ_module(module_name);
match self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(module) = self.wam_prelude.indices.modules.get_mut(&module_name) {
Some(module) => { let old_module_decl = mem::replace(&mut module.module_decl, module_decl.clone());
let old_module_decl = mem::replace(&mut module.module_decl, module_decl.clone());
let local_extensible_predicates = mem::replace( let local_extensible_predicates = mem::replace(
&mut module.local_extensible_predicates, &mut module.local_extensible_predicates,
LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()), LocalExtensiblePredicates::with_hasher(FxBuildHasher::default()),
);
for ((compilation_target, key), skeleton) in local_extensible_predicates.iter() {
self.retract_local_clauses_impl(
*compilation_target,
*key,
&skeleton.clause_clause_locs,
); );
for ((compilation_target, key), skeleton) in local_extensible_predicates.iter() { let is_dynamic = self
self.retract_local_clauses_impl( .wam_prelude
*compilation_target, .indices
*key, .get_predicate_skeleton(compilation_target, key)
.map(|skeleton| skeleton.core.is_dynamic)
.unwrap_or(false);
if is_dynamic {
let clause_clause_compilation_target = match compilation_target {
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
module => *module,
};
self.retract_local_clause_clauses(
clause_clause_compilation_target,
&skeleton.clause_clause_locs, &skeleton.clause_clause_locs,
); );
let is_dynamic = self
.wam_prelude
.indices
.get_predicate_skeleton(compilation_target, key)
.map(|skeleton| skeleton.core.is_dynamic)
.unwrap_or(false);
if is_dynamic {
let clause_clause_compilation_target = match compilation_target {
CompilationTarget::User => CompilationTarget::Module(atom!("builtins")),
module => module.clone(),
};
self.retract_local_clause_clauses(
clause_clause_compilation_target,
&skeleton.clause_clause_locs,
);
}
} }
self.payload
.retraction_info
.push_record(RetractionRecord::ReplacedModule(
old_module_decl,
listing_src.clone(),
local_extensible_predicates,
));
} }
None => {}
self.payload
.retraction_info
.push_record(RetractionRecord::ReplacedModule(
old_module_decl,
listing_src.clone(),
local_extensible_predicates,
));
} }
} }
@@ -1180,11 +1178,11 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
} }
ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) { ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) {
Some(code) => { Some(code) => {
if let Some(ref module) = self.wam_prelude.indices.modules.get(&library) { if let Some(module) = self.wam_prelude.indices.modules.get(&library) {
if let ListingSource::DynamicallyGenerated = &module.listing_src { if let ListingSource::DynamicallyGenerated = &module.listing_src {
( (
Stream::from_static_string( Stream::from_static_string(
*code, code,
&mut LS::machine_st(&mut self.payload).arena, &mut LS::machine_st(&mut self.payload).arena,
), ),
ListingSource::User, ListingSource::User,
@@ -1195,7 +1193,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
} else { } else {
( (
Stream::from_static_string( Stream::from_static_string(
*code, code,
&mut LS::machine_st(&mut self.payload).arena, &mut LS::machine_st(&mut self.payload).arena,
), ),
ListingSource::User, ListingSource::User,
@@ -1266,7 +1264,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
} else { } else {
( (
Stream::from_static_string( Stream::from_static_string(
*code, code,
&mut LS::machine_st(&mut self.payload).arena, &mut LS::machine_st(&mut self.payload).arena,
), ),
ListingSource::User, ListingSource::User,

View File

@@ -19,7 +19,6 @@ use std::cell::Cell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
use std::mem;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
/* /*
@@ -136,7 +135,7 @@ impl RetractionInfo {
Self { Self {
orig_code_extent, orig_code_extent,
records: mem::replace(&mut self.records, vec![]), records: std::mem::take(&mut self.records),
} }
} }
} }
@@ -149,9 +148,10 @@ impl<'a, LS: LoadState<'a>> Drop for Loader<'a, LS> {
} }
} }
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
pub enum CompilationTarget { pub enum CompilationTarget {
Module(Atom), Module(Atom),
#[default]
User, User,
} }
@@ -164,13 +164,6 @@ impl fmt::Display for CompilationTarget {
} }
} }
impl Default for CompilationTarget {
#[inline]
fn default() -> Self {
CompilationTarget::User
}
}
impl CompilationTarget { impl CompilationTarget {
#[inline] #[inline]
pub(crate) fn module_name(&self) -> Atom { pub(crate) fn module_name(&self) -> Atom {
@@ -207,8 +200,8 @@ impl PredicateQueue {
#[inline] #[inline]
pub(super) fn take(&mut self) -> Self { pub(super) fn take(&mut self) -> Self {
Self { Self {
predicates: mem::replace(&mut self.predicates, vec![]), predicates: std::mem::take(&mut self.predicates),
compilation_target: self.compilation_target.clone(), compilation_target: self.compilation_target,
} }
} }
@@ -404,7 +397,7 @@ impl<'a> LoadState<'a> for BootstrappingLoadState<'a> {
#[inline(always)] #[inline(always)]
fn machine_st(loader: &mut Self::LoaderFieldType) -> &mut MachineState { fn machine_st(loader: &mut Self::LoaderFieldType) -> &mut MachineState {
&mut loader.term_stream.parser.lexer.machine_st loader.term_stream.parser.lexer.machine_st
} }
#[inline(always)] #[inline(always)]
@@ -467,7 +460,7 @@ impl<'a> LoadState<'a> for InlineLoadState<'a> {
#[inline(always)] #[inline(always)]
fn machine_st(load_state: &mut Self::LoaderFieldType) -> &mut MachineState { fn machine_st(load_state: &mut Self::LoaderFieldType) -> &mut MachineState {
&mut load_state.machine_st load_state.machine_st
} }
#[inline(always)] #[inline(always)]
@@ -639,22 +632,19 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
RetractionRecord::AddedDiscontiguousPredicate(compilation_target, key) => { RetractionRecord::AddedDiscontiguousPredicate(compilation_target, key) => {
match compilation_target { match compilation_target {
CompilationTarget::User => { CompilationTarget::User => {
self.wam_prelude if let Some(skeleton) =
.indices self.wam_prelude.indices.extensible_predicates.get_mut(&key)
.extensible_predicates {
.get_mut(&key) skeleton.core.is_discontiguous = false;
.map(|skeleton| { }
skeleton.core.is_discontiguous = false;
});
} }
CompilationTarget::Module(module_name) => { CompilationTarget::Module(module_name) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(ref mut module) =
Some(ref mut module) => { self.wam_prelude.indices.modules.get_mut(&module_name)
module.extensible_predicates.get_mut(&key).map(|skeleton| { {
skeleton.core.is_discontiguous = false; if let Some(skeleton) = module.extensible_predicates.get_mut(&key) {
}); skeleton.core.is_discontiguous = false;
} }
None => {}
} }
} }
} }
@@ -662,23 +652,20 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
RetractionRecord::AddedDynamicPredicate(compilation_target, key) => { RetractionRecord::AddedDynamicPredicate(compilation_target, key) => {
match compilation_target { match compilation_target {
CompilationTarget::User => { CompilationTarget::User => {
self.wam_prelude if let Some(skeleton) =
.indices self.wam_prelude.indices.extensible_predicates.get_mut(&key)
.extensible_predicates {
.get_mut(&key) skeleton.core.is_dynamic = false;
.map(|skeleton| { }
skeleton.core.is_dynamic = false;
});
} }
CompilationTarget::Module(module_name) => { CompilationTarget::Module(module_name) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(ref mut module) =
Some(ref mut module) => { self.wam_prelude.indices.modules.get_mut(&module_name)
module.extensible_predicates.get_mut(&key).map(|skeleton| { {
skeleton.core.is_dynamic = false; if let Some(skeleton) = module.extensible_predicates.get_mut(&key) {
skeleton.core.retracted_dynamic_clauses = None; skeleton.core.is_dynamic = false;
}); skeleton.core.retracted_dynamic_clauses = None;
} };
None => {}
} }
} }
} }
@@ -686,60 +673,52 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
RetractionRecord::AddedMultifilePredicate(compilation_target, key) => { RetractionRecord::AddedMultifilePredicate(compilation_target, key) => {
match compilation_target { match compilation_target {
CompilationTarget::User => { CompilationTarget::User => {
self.wam_prelude if let Some(skeleton) =
.indices self.wam_prelude.indices.extensible_predicates.get_mut(&key)
.extensible_predicates {
.get_mut(&key) skeleton.core.is_multifile = false;
.map(|skeleton| { }
skeleton.core.is_multifile = false;
});
} }
CompilationTarget::Module(module_name) => { CompilationTarget::Module(module_name) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(ref mut module) =
Some(ref mut module) => { self.wam_prelude.indices.modules.get_mut(&module_name)
module.extensible_predicates.get_mut(&key).map(|skeleton| { {
skeleton.core.is_multifile = false; if let Some(skeleton) = module.extensible_predicates.get_mut(&key) {
}); skeleton.core.is_multifile = false;
} }
None => {}
} }
} }
} }
} }
RetractionRecord::AddedModuleOp(module_name, mut op_decl) => { RetractionRecord::AddedModuleOp(module_name, mut op_decl) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(ref mut module) =
Some(ref mut module) => { self.wam_prelude.indices.modules.get_mut(&module_name)
op_decl.remove(&mut module.op_dir); {
} op_decl.remove(&mut module.op_dir);
None => {}
} }
} }
RetractionRecord::ReplacedModuleOp(module_name, mut op_decl, op_desc) => { RetractionRecord::ReplacedModuleOp(module_name, mut op_decl, op_desc) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(ref mut module) =
Some(ref mut module) => { self.wam_prelude.indices.modules.get_mut(&module_name)
op_decl.op_desc = op_desc; {
op_decl.insert_into_op_dir(&mut module.op_dir); op_decl.op_desc = op_desc;
} op_decl.insert_into_op_dir(&mut module.op_dir);
None => {}
} }
} }
RetractionRecord::AddedModulePredicate(module_name, key) => { RetractionRecord::AddedModulePredicate(module_name, key) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(ref mut module) =
Some(ref mut module) => { self.wam_prelude.indices.modules.get_mut(&module_name)
module.code_dir.remove(&key); {
} module.code_dir.remove(&key);
None => {}
} }
} }
RetractionRecord::ReplacedModulePredicate(module_name, key, old_code_idx) => { RetractionRecord::ReplacedModulePredicate(module_name, key, old_code_idx) => {
match self.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(ref mut module) =
Some(ref mut module) => { self.wam_prelude.indices.modules.get_mut(&module_name)
module {
.code_dir if let Some(code_idx) = module.code_dir.get_mut(&key) {
.get_mut(&key) code_idx.set(old_code_idx)
.map(|code_idx| code_idx.set(old_code_idx));
} }
None => {}
} }
} }
RetractionRecord::AddedExtensiblePredicate(compilation_target, key) => { RetractionRecord::AddedExtensiblePredicate(compilation_target, key) => {
@@ -758,11 +737,9 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
self.wam_prelude.indices.code_dir.remove(&key); self.wam_prelude.indices.code_dir.remove(&key);
} }
RetractionRecord::ReplacedUserPredicate(key, old_code_idx) => { RetractionRecord::ReplacedUserPredicate(key, old_code_idx) => {
self.wam_prelude if let Some(code_idx) = self.wam_prelude.indices.code_dir.get_mut(&key) {
.indices code_idx.set(old_code_idx)
.code_dir }
.get_mut(&key)
.map(|code_idx| code_idx.set(old_code_idx));
} }
RetractionRecord::AddedIndex(index_key, clause_loc) => { RetractionRecord::AddedIndex(index_key, clause_loc) => {
if let Some(index_loc) = index_key.switch_on_term_loc() { if let Some(index_loc) = index_key.switch_on_term_loc() {
@@ -832,20 +809,17 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
}; };
} }
RetractionRecord::ReplacedSwitchOnTermVarIndex(index_loc, old_v) => { RetractionRecord::ReplacedSwitchOnTermVarIndex(index_loc, old_v) => {
match self.wam_prelude.code[index_loc] { if let Instruction::IndexingCode(ref mut indexing_code) =
Instruction::IndexingCode(ref mut indexing_code) => { self.wam_prelude.code[index_loc]
match &mut indexing_code[0] { {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm( if let IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_, _,
ref mut v, ref mut v,
.., ..,
)) => { )) = &mut indexing_code[0]
*v = old_v; {
} *v = old_v;
_ => {}
}
} }
_ => {}
} }
} }
RetractionRecord::ModifiedTryMeElse(instr_loc, o) => { RetractionRecord::ModifiedTryMeElse(instr_loc, o) => {
@@ -858,30 +832,24 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
self.wam_prelude.code[instr_loc] = Instruction::RevJmpBy(o); self.wam_prelude.code[instr_loc] = Instruction::RevJmpBy(o);
} }
RetractionRecord::SkeletonClausePopBack(compilation_target, key) => { RetractionRecord::SkeletonClausePopBack(compilation_target, key) => {
match self if let Some(skeleton) = self
.wam_prelude .wam_prelude
.indices .indices
.get_predicate_skeleton_mut(&compilation_target, &key) .get_predicate_skeleton_mut(&compilation_target, &key)
{ {
Some(skeleton) => { skeleton.clauses.pop_back();
skeleton.clauses.pop_back(); skeleton.core.clause_clause_locs.pop_back();
skeleton.core.clause_clause_locs.pop_back();
}
None => {}
} }
} }
RetractionRecord::SkeletonClausePopFront(compilation_target, key) => { RetractionRecord::SkeletonClausePopFront(compilation_target, key) => {
match self if let Some(skeleton) = self
.wam_prelude .wam_prelude
.indices .indices
.get_predicate_skeleton_mut(&compilation_target, &key) .get_predicate_skeleton_mut(&compilation_target, &key)
{ {
Some(skeleton) => { skeleton.clauses.pop_front();
skeleton.clauses.pop_front(); skeleton.core.clause_clause_locs.pop_front();
skeleton.core.clause_clause_locs.pop_front(); skeleton.core.clause_assert_margin -= 1;
skeleton.core.clause_assert_margin -= 1;
}
None => {}
} }
} }
RetractionRecord::SkeletonLocalClauseClausePopFront( RetractionRecord::SkeletonLocalClauseClausePopFront(
@@ -891,16 +859,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
) => { ) => {
let listing_src_file_name = self.listing_src_file_name(); let listing_src_file_name = self.listing_src_file_name();
match self.wam_prelude.indices.get_local_predicate_skeleton_mut( if let Some(skeleton) =
src_compilation_target, self.wam_prelude.indices.get_local_predicate_skeleton_mut(
local_compilation_target, src_compilation_target,
listing_src_file_name, local_compilation_target,
key, listing_src_file_name,
) { key,
Some(skeleton) => { )
skeleton.clause_clause_locs.pop_front(); {
} skeleton.clause_clause_locs.pop_front();
None => {}
} }
} }
RetractionRecord::SkeletonLocalClauseClausePopBack( RetractionRecord::SkeletonLocalClauseClausePopBack(
@@ -910,16 +877,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
) => { ) => {
let listing_src_file_name = self.listing_src_file_name(); let listing_src_file_name = self.listing_src_file_name();
match self.wam_prelude.indices.get_local_predicate_skeleton_mut( if let Some(skeleton) =
src_compilation_target, self.wam_prelude.indices.get_local_predicate_skeleton_mut(
local_compilation_target, src_compilation_target,
listing_src_file_name, local_compilation_target,
key, listing_src_file_name,
) { key,
Some(skeleton) => { )
skeleton.clause_clause_locs.pop_back(); {
} skeleton.clause_clause_locs.pop_back();
None => {}
} }
} }
RetractionRecord::SkeletonLocalClauseTruncateBack( RetractionRecord::SkeletonLocalClauseTruncateBack(
@@ -930,29 +896,25 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
) => { ) => {
let listing_src_file_name = self.listing_src_file_name(); let listing_src_file_name = self.listing_src_file_name();
match self.wam_prelude.indices.get_local_predicate_skeleton_mut( if let Some(skeleton) =
src_compilation_target, self.wam_prelude.indices.get_local_predicate_skeleton_mut(
local_compilation_target, src_compilation_target,
listing_src_file_name, local_compilation_target,
key, listing_src_file_name,
) { key,
Some(skeleton) => { )
skeleton.clause_clause_locs.truncate(len); {
} skeleton.clause_clause_locs.truncate(len);
None => {}
} }
} }
RetractionRecord::SkeletonClauseTruncateBack(compilation_target, key, len) => { RetractionRecord::SkeletonClauseTruncateBack(compilation_target, key, len) => {
match self if let Some(skeleton) = self
.wam_prelude .wam_prelude
.indices .indices
.get_predicate_skeleton_mut(&compilation_target, &key) .get_predicate_skeleton_mut(&compilation_target, &key)
{ {
Some(skeleton) => { skeleton.clauses.truncate(len);
skeleton.clauses.truncate(len); skeleton.core.clause_clause_locs.truncate(len);
skeleton.core.clause_clause_locs.truncate(len);
}
None => {}
} }
} }
RetractionRecord::SkeletonClauseStartReplaced( RetractionRecord::SkeletonClauseStartReplaced(
@@ -961,15 +923,12 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
target_pos, target_pos,
clause_start, clause_start,
) => { ) => {
match self if let Some(skeleton) = self
.wam_prelude .wam_prelude
.indices .indices
.get_predicate_skeleton_mut(&compilation_target, &key) .get_predicate_skeleton_mut(&compilation_target, &key)
{ {
Some(skeleton) => { skeleton.clauses[target_pos].clause_start = clause_start;
skeleton.clauses[target_pos].clause_start = clause_start;
}
None => {}
} }
} }
RetractionRecord::RemovedDynamicSkeletonClause( RetractionRecord::RemovedDynamicSkeletonClause(
@@ -978,26 +937,22 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
target_pos, target_pos,
clause_clause_loc, clause_clause_loc,
) => { ) => {
match self if let Some(skeleton) = self
.wam_prelude .wam_prelude
.indices .indices
.get_predicate_skeleton_mut(&compilation_target, &key) .get_predicate_skeleton_mut(&compilation_target, &key)
{ {
Some(skeleton) => { if let Some(removed_clauses) = &mut skeleton.core.retracted_dynamic_clauses
if let Some(removed_clauses) = {
&mut skeleton.core.retracted_dynamic_clauses let clause_index_info = removed_clauses.pop().unwrap();
{
let clause_index_info = removed_clauses.pop().unwrap();
skeleton skeleton
.core .core
.clause_clause_locs .clause_clause_locs
.insert(target_pos, clause_clause_loc); .insert(target_pos, clause_clause_loc);
skeleton.clauses.insert(target_pos, clause_index_info); skeleton.clauses.insert(target_pos, clause_index_info);
}
} }
None => {}
} }
} }
RetractionRecord::RemovedSkeletonClause( RetractionRecord::RemovedSkeletonClause(
@@ -1007,19 +962,16 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
clause_index_info, clause_index_info,
clause_clause_loc, clause_clause_loc,
) => { ) => {
match self if let Some(skeleton) = self
.wam_prelude .wam_prelude
.indices .indices
.get_predicate_skeleton_mut(&compilation_target, &key) .get_predicate_skeleton_mut(&compilation_target, &key)
{ {
Some(skeleton) => { skeleton
skeleton .core
.core .clause_clause_locs
.clause_clause_locs .insert(target_pos, clause_clause_loc);
.insert(target_pos, clause_clause_loc); skeleton.clauses.insert(target_pos, clause_index_info);
skeleton.clauses.insert(target_pos, clause_index_info);
}
None => {}
} }
} }
RetractionRecord::ReplacedIndexingLine(index_loc, indexing_code) => { RetractionRecord::ReplacedIndexingLine(index_loc, indexing_code) => {
@@ -1033,14 +985,15 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
) => { ) => {
let listing_src_file_name = self.listing_src_file_name(); let listing_src_file_name = self.listing_src_file_name();
match self.wam_prelude.indices.get_local_predicate_skeleton_mut( if let Some(skeleton) =
compilation_target, self.wam_prelude.indices.get_local_predicate_skeleton_mut(
local_compilation_target, compilation_target,
listing_src_file_name, local_compilation_target,
key, listing_src_file_name,
) { key,
Some(skeleton) => skeleton.clause_clause_locs = clause_locs, )
None => {} {
skeleton.clause_clause_locs = clause_locs
} }
} }
RetractionRecord::RemovedSkeleton(compilation_target, key, skeleton) => { RetractionRecord::RemovedSkeleton(compilation_target, key, skeleton) => {
@@ -1091,7 +1044,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
let export_list = machine_st.read_term_from_heap(cell); let export_list = machine_st.read_term_from_heap(cell);
let atom_tbl = &mut LS::machine_st(&mut self.payload).atom_tbl; let atom_tbl = &mut LS::machine_st(&mut self.payload).atom_tbl;
let export_list = setup_module_export_list(export_list, &atom_tbl)?; let export_list = setup_module_export_list(export_list, atom_tbl)?;
Ok(export_list.into_iter().collect()) Ok(export_list.into_iter().collect())
} }
@@ -1363,7 +1316,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
*key, *key,
) { ) {
Some(skeleton) if !skeleton.clause_clause_locs.is_empty() => { Some(skeleton) if !skeleton.clause_clause_locs.is_empty() => {
mem::replace(&mut skeleton.clause_clause_locs, VecDeque::new()) std::mem::take(&mut skeleton.clause_clause_locs)
} }
_ => return, _ => return,
}; };
@@ -1400,9 +1353,7 @@ impl<'a> MachinePreludeView<'a> {
CompilationTarget::User => CompositeOpDir::new(&self.indices.op_dir, None), CompilationTarget::User => CompositeOpDir::new(&self.indices.op_dir, None),
CompilationTarget::Module(ref module_name) => { CompilationTarget::Module(ref module_name) => {
match self.indices.modules.get(module_name) { match self.indices.modules.get(module_name) {
Some(ref module) => { Some(module) => CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir)),
CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir))
}
None => { None => {
unreachable!() unreachable!()
} }
@@ -1413,13 +1364,10 @@ impl<'a> MachinePreludeView<'a> {
} }
impl MachineState { impl MachineState {
pub(super) fn read_term_from_heap( pub(super) fn read_term_from_heap(&mut self, term_addr: HeapCellValue) -> Term {
&mut self,
term_addr: HeapCellValue,
) -> Term {
let mut term_stack = vec![]; let mut term_stack = vec![];
let mut iter = stackful_post_order_iter::<NonListElider> let mut iter =
(&mut self.heap, &mut self.stack, term_addr); stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, term_addr);
while let Some(addr) = iter.next() { while let Some(addr) = iter.next() {
let addr = unmark_cell_bits!(addr); let addr = unmark_cell_bits!(addr);
@@ -1652,10 +1600,10 @@ impl Machine {
let arity = self.deref_register(3); let arity = self.deref_register(3);
let arity = match Number::try_from(arity) { let arity = match Number::try_from(arity) {
Ok(Number::Integer(n)) if &*n >= &Integer::ZERO && &*n <= &Integer::from(MAX_ARITY) => { Ok(Number::Integer(n)) if *n >= Integer::ZERO && *n <= Integer::from(MAX_ARITY) => {
let value: usize = (&*n).try_into().unwrap(); let value: usize = (&*n).try_into().unwrap();
Ok(value) Ok(value)
}, }
Ok(Number::Fixnum(n)) if n.get_num() >= 0 && n.get_num() <= MAX_ARITY as i64 => { Ok(Number::Fixnum(n)) if n.get_num() >= 0 && n.get_num() <= MAX_ARITY as i64 => {
Ok(usize::try_from(n.get_num()).unwrap()) Ok(usize::try_from(n.get_num()).unwrap())
} }
@@ -1770,14 +1718,11 @@ impl Machine {
&ListingSource::DynamicallyGenerated, &ListingSource::DynamicallyGenerated,
); );
match loader.wam_prelude.indices.modules.get_mut(&module_name) { if let Some(module) = loader.wam_prelude.indices.modules.get_mut(&module_name) {
Some(module) => { for (key, value) in module.op_dir.drain(0..) {
for (key, value) in module.op_dir.drain(0..) { let mut op_decl = OpDecl::new(value, key.0);
let mut op_decl = OpDecl::new(value, key.0); op_decl.remove(&mut loader.wam_prelude.indices.op_dir);
op_decl.remove(&mut loader.wam_prelude.indices.op_dir);
}
} }
None => {}
} }
} }
} }
@@ -1789,10 +1734,10 @@ impl Machine {
self.restore_load_state_payload(result) self.restore_load_state_payload(result)
} }
pub(crate) fn loader_from_heap_evacuable<'a>( pub(crate) fn loader_from_heap_evacuable(
&'a mut self, &mut self,
r: RegType, r: RegType,
) -> Loader<'a, LiveLoadAndMachineState<'a>> { ) -> Loader<'_, LiveLoadAndMachineState<'_>> {
let mut load_state = cell_as_load_state_payload!(self let mut load_state = cell_as_load_state_payload!(self
.machine_st .machine_st
.store(self.machine_st.deref(self.machine_st[r]))); .store(self.machine_st.deref(self.machine_st[r])));
@@ -1868,7 +1813,7 @@ impl Machine {
let path = cell_as_atom!(self.deref_register(2)); let path = cell_as_atom!(self.deref_register(2));
self.load_contexts self.load_contexts
.push(LoadContext::new(&*path.as_str(), stream)); .push(LoadContext::new(&path.as_str(), stream));
Ok(()) Ok(())
} }
@@ -1888,9 +1833,33 @@ impl Machine {
} }
pub(crate) fn scoped_clause_to_evacuable(&mut self) -> CallResult { pub(crate) fn scoped_clause_to_evacuable(&mut self) -> CallResult {
let module_name = cell_as_atom!(self let target = self.deref_register(1);
.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); let mut permission_error = || {
let err = self.machine_st.permission_error(
Permission::Modify,
atom!("static_procedure"),
functor_stub(atom!(":"), 2)
.into_iter()
.collect::<MachineStub>(),
);
self.machine_st
.error_form(err, functor_stub(atom!("load"), 1))
};
let module_name = read_heap_cell!(target,
(HeapCellValueTag::Atom, (name, arity)) => {
if arity == 0 {
name
} else {
return Err(permission_error());
}
}
_ => {
return Err(permission_error());
}
);
let loader = self.loader_from_heap_evacuable(temp_v!(3)); let loader = self.loader_from_heap_evacuable(temp_v!(3));
@@ -2003,11 +1972,13 @@ impl Machine {
_ => CompilationTarget::Module(module_name), _ => CompilationTarget::Module(module_name),
}; };
let stub_gen = || match append_or_prepend { let key = match append_or_prepend {
AppendOrPrepend::Append => functor_stub(atom!("assertz"), 1), AppendOrPrepend::Append => (atom!("assertz"), 1),
AppendOrPrepend::Prepend => functor_stub(atom!("asserta"), 1), AppendOrPrepend::Prepend => (atom!("asserta"), 1),
}; };
let stub_gen = || functor_stub(key.0, key.1);
let head = self.deref_register(2); let head = self.deref_register(2);
if head.is_var() { if head.is_var() {
@@ -2021,8 +1992,8 @@ impl Machine {
loader.payload.compilation_target = compilation_target; loader.payload.compilation_target = compilation_target;
let head = LiveLoadAndMachineState::machine_st(&mut loader.payload) let head =
.read_term_from_heap(head); LiveLoadAndMachineState::machine_st(&mut loader.payload).read_term_from_heap(head);
let name = if let Some(name) = head.name() { let name = if let Some(name) = head.name() {
name name
@@ -2046,7 +2017,11 @@ impl Machine {
.map(|code_idx| code_idx.get_tag()) .map(|code_idx| code_idx.get_tag())
.unwrap_or(IndexPtrTag::DynamicUndefined); .unwrap_or(IndexPtrTag::DynamicUndefined);
idx_tag == IndexPtrTag::DynamicUndefined || idx_tag == IndexPtrTag::Undefined if idx_tag == IndexPtrTag::Index {
return Err(SessionError::CannotOverwriteStaticProcedure((name, arity)));
} else {
idx_tag == IndexPtrTag::Undefined || idx_tag == IndexPtrTag::DynamicUndefined
}
} else if is_builtin { } else if is_builtin {
return Err(SessionError::CannotOverwriteBuiltIn((name, arity))); return Err(SessionError::CannotOverwriteBuiltIn((name, arity)));
} else { } else {
@@ -2218,7 +2193,7 @@ impl Machine {
Ok(Number::Integer(n)) => { Ok(Number::Integer(n)) => {
let value: usize = (&*n).try_into().unwrap(); let value: usize = (&*n).try_into().unwrap();
value value
}, }
Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(), Ok(Number::Fixnum(n)) => usize::try_from(n.get_num()).unwrap(),
_ => unreachable!(), _ => unreachable!(),
}; };
@@ -2520,7 +2495,7 @@ pub(super) fn load_module(
import_module_exports::<LiveLoadAndMachineState>( import_module_exports::<LiveLoadAndMachineState>(
&mut payload, &mut payload,
&compilation_target, compilation_target,
module, module,
code_dir, code_dir,
op_dir, op_dir,

View File

@@ -80,7 +80,7 @@ impl ValidType {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) enum ResourceError { pub(crate) enum ResourceError {
FiniteMemory(HeapCellValue), FiniteMemory(HeapCellValue),
OutOfFiles OutOfFiles,
} }
pub(crate) trait TypeError { pub(crate) trait TypeError {
@@ -170,7 +170,11 @@ impl PermissionError for Atom {
) -> MachineError { ) -> MachineError {
let stub = functor!( let stub = functor!(
atom!("permission_error"), atom!("permission_error"),
[atom(perm.as_atom()), atom(index_atom), cell(atom_as_cell!(self))] [
atom(perm.as_atom()),
atom(index_atom),
cell(atom_as_cell!(self))
]
); );
MachineError { MachineError {
@@ -319,10 +323,7 @@ impl MachineState {
) )
} }
ResourceError::OutOfFiles => { ResourceError::OutOfFiles => {
functor!( functor!(atom!("resource_error"), [atom(atom!("file_descriptors"))])
atom!("resource_error"),
[atom(atom!("file_descriptors"))]
)
} }
}; };
@@ -355,13 +356,21 @@ impl MachineState {
from: ErrorProvenance::Received, from: ErrorProvenance::Received,
} }
} }
ExistenceError::QualifiedProcedure { module_name, name, arity } => { ExistenceError::QualifiedProcedure {
module_name,
name,
arity,
} => {
let h = self.heap.len(); let h = self.heap.len();
let ind_stub = functor!(atom!("/"), [atom(name), fixnum(arity)]); let ind_stub = functor!(atom!("/"), [atom(name), fixnum(arity)]);
let res_stub = functor!(atom!(":"), [atom(module_name), str(h + 3, 0)], [ind_stub]); let res_stub = functor!(atom!(":"), [atom(module_name), str(h + 3, 0)], [ind_stub]);
let stub = functor!(atom!("existence_error"), [atom(atom!("procedure")), str(h, 0)], [res_stub]); let stub = functor!(
atom!("existence_error"),
[atom(atom!("procedure")), str(h, 0)],
[res_stub]
);
MachineError { MachineError {
stub, stub,
@@ -472,31 +481,34 @@ impl MachineState {
pub(super) fn session_error(&mut self, err: SessionError) -> MachineError { pub(super) fn session_error(&mut self, err: SessionError) -> MachineError {
match err { match err {
SessionError::CannotOverwriteBuiltIn(key) => { SessionError::CannotOverwriteBuiltIn(key) => self.permission_error(
self.permission_error( Permission::Modify,
Permission::Modify, atom!("static_procedure"),
atom!("static_procedure"), functor_stub(key.0, key.1)
functor_stub(key.0, key.1) .into_iter()
.into_iter() .collect::<MachineStub>(),
.collect::<MachineStub>(), ),
) SessionError::CannotOverwriteStaticProcedure(key) => self.permission_error(
} Permission::Modify,
atom!("static_procedure"),
functor_stub(key.0, key.1)
.into_iter()
.collect::<MachineStub>(),
),
SessionError::CannotOverwriteBuiltInModule(module) => { SessionError::CannotOverwriteBuiltInModule(module) => {
self.permission_error( self.permission_error(Permission::Modify, atom!("static_module"), module)
Permission::Modify,
atom!("static_module"),
module,
)
} }
SessionError::ExistenceError(err) => self.existence_error(err), SessionError::ExistenceError(err) => self.existence_error(err),
SessionError::ModuleDoesNotContainExport(..) => { SessionError::ModuleDoesNotContainExport(module_name, key) => {
let error_atom = atom!("module_does_not_contain_claimed_export"); let functor_stub = functor_stub(key.0, key.1);
self.permission_error( let stub = functor!(
Permission::Access, atom!("module_does_not_contain_claimed_export"),
atom!("private_procedure"), [atom(module_name), str(self.heap.len() + 4, 0)],
functor!(error_atom), [functor_stub]
) );
self.permission_error(Permission::Access, atom!("private_procedure"), stub)
} }
SessionError::ModuleCannotImportSelf(module_name) => { SessionError::ModuleCannotImportSelf(module_name) => {
let error_atom = atom!("module_cannot_import_self"); let error_atom = atom!("module_cannot_import_self");
@@ -641,7 +653,7 @@ impl MachineState {
self.ball.boundary = 0; self.ball.boundary = 0;
self.ball.stub.truncate(0); self.ball.stub.truncate(0);
self.heap.extend(err.into_iter()); self.heap.extend(err);
self.registers[1] = if err_len == 1 { self.registers[1] = if err_len == 1 {
heap_loc_as_cell!(h) heap_loc_as_cell!(h)
@@ -705,58 +717,58 @@ impl From<ParserError> for CompilationError {
impl CompilationError { impl CompilationError {
pub(crate) fn line_and_col_num(&self) -> Option<(usize, usize)> { pub(crate) fn line_and_col_num(&self) -> Option<(usize, usize)> {
match self { match self {
&CompilationError::ParserError(ref err) => err.line_and_col_num(), CompilationError::ParserError(err) => err.line_and_col_num(),
_ => None, _ => None,
} }
} }
pub(crate) fn as_functor(&self) -> MachineStub { pub(crate) fn as_functor(&self) -> MachineStub {
match self { match self {
&CompilationError::Arithmetic(..) => { CompilationError::Arithmetic(..) => {
functor!(atom!("arithmetic_error")) functor!(atom!("arithmetic_error"))
} }
&CompilationError::CannotParseCyclicTerm => { CompilationError::CannotParseCyclicTerm => {
functor!(atom!("cannot_parse_cyclic_term")) functor!(atom!("cannot_parse_cyclic_term"))
} }
&CompilationError::ExceededMaxArity => { CompilationError::ExceededMaxArity => {
functor!(atom!("exceeded_max_arity")) functor!(atom!("exceeded_max_arity"))
} }
&CompilationError::ExpectedRel => { CompilationError::ExpectedRel => {
functor!(atom!("expected_relation")) functor!(atom!("expected_relation"))
} }
&CompilationError::InadmissibleFact => { CompilationError::InadmissibleFact => {
// TODO: type_error(callable, _). // TODO: type_error(callable, _).
functor!(atom!("inadmissible_fact")) functor!(atom!("inadmissible_fact"))
} }
&CompilationError::InadmissibleQueryTerm => { CompilationError::InadmissibleQueryTerm => {
// TODO: type_error(callable, _). // TODO: type_error(callable, _).
functor!(atom!("inadmissible_query_term")) functor!(atom!("inadmissible_query_term"))
} }
&CompilationError::InconsistentEntry => { CompilationError::InconsistentEntry => {
functor!(atom!("inconsistent_entry")) functor!(atom!("inconsistent_entry"))
} }
&CompilationError::InvalidMetaPredicateDecl => { CompilationError::InvalidMetaPredicateDecl => {
functor!(atom!("invalid_meta_predicate_decl")) functor!(atom!("invalid_meta_predicate_decl"))
} }
&CompilationError::InvalidModuleDecl => { CompilationError::InvalidModuleDecl => {
functor!(atom!("invalid_module_declaration")) functor!(atom!("invalid_module_declaration"))
} }
&CompilationError::InvalidModuleExport => { CompilationError::InvalidModuleExport => {
functor!(atom!("invalid_module_export")) functor!(atom!("invalid_module_export"))
} }
&CompilationError::InvalidModuleResolution(ref module_name) => { CompilationError::InvalidModuleResolution(ref module_name) => {
functor!(atom!("no_such_module"), [atom(module_name)]) functor!(atom!("no_such_module"), [atom(module_name)])
} }
&CompilationError::InvalidRuleHead => { CompilationError::InvalidRuleHead => {
functor!(atom!("invalid_head_of_rule")) // TODO: type_error(callable, _). functor!(atom!("invalid_head_of_rule")) // TODO: type_error(callable, _).
} }
&CompilationError::InvalidUseModuleDecl => { CompilationError::InvalidUseModuleDecl => {
functor!(atom!("invalid_use_module_declaration")) functor!(atom!("invalid_use_module_declaration"))
} }
&CompilationError::ParserError(ref err) => { CompilationError::ParserError(ref err) => {
functor!(err.as_atom()) functor!(err.as_atom())
} }
&CompilationError::UnreadableTerm => { CompilationError::UnreadableTerm => {
functor!(atom!("unreadable_term")) functor!(atom!("unreadable_term"))
} }
} }
@@ -986,7 +998,11 @@ pub enum ExistenceError {
Module(Atom), Module(Atom),
ModuleSource(ModuleSource), ModuleSource(ModuleSource),
Procedure(Atom, usize), Procedure(Atom, usize),
QualifiedProcedure { module_name: Atom, name: Atom, arity: usize }, QualifiedProcedure {
module_name: Atom,
name: Atom,
arity: usize,
},
SourceSink(HeapCellValue), SourceSink(HeapCellValue),
Stream(HeapCellValue), Stream(HeapCellValue),
} }
@@ -996,6 +1012,7 @@ pub enum SessionError {
CompilationError(CompilationError), CompilationError(CompilationError),
CannotOverwriteBuiltIn(PredicateKey), CannotOverwriteBuiltIn(PredicateKey),
CannotOverwriteBuiltInModule(Atom), CannotOverwriteBuiltInModule(Atom),
CannotOverwriteStaticProcedure(PredicateKey),
ExistenceError(ExistenceError), ExistenceError(ExistenceError),
ModuleDoesNotContainExport(Atom, PredicateKey), ModuleDoesNotContainExport(Atom, PredicateKey),
ModuleCannotImportSelf(Atom), ModuleCannotImportSelf(Atom),
@@ -1005,19 +1022,6 @@ pub enum SessionError {
QueryCannotBeDefinedAsFact, QueryCannotBeDefinedAsFact,
} }
#[derive(Debug)]
pub(crate) enum EvalSession {
// EntrySuccess,
Error(SessionError),
}
impl From<SessionError> for EvalSession {
#[inline]
fn from(err: SessionError) -> Self {
EvalSession::Error(err)
}
}
impl From<std::io::Error> for SessionError { impl From<std::io::Error> for SessionError {
#[inline] #[inline]
fn from(err: std::io::Error) -> SessionError { fn from(err: std::io::Error) -> SessionError {
@@ -1038,10 +1042,3 @@ impl From<CompilationError> for SessionError {
SessionError::CompilationError(err) SessionError::CompilationError(err)
} }
} }
impl From<ParserError> for EvalSession {
#[inline]
fn from(err: ParserError) -> Self {
EvalSession::from(SessionError::from(err))
}
}

View File

@@ -10,8 +10,8 @@ use crate::machine::ClauseType;
use fxhash::FxBuildHasher; use fxhash::FxBuildHasher;
use indexmap::{IndexMap, IndexSet}; use indexmap::{IndexMap, IndexSet};
use modular_bitfield::specifiers::*; use scryer_modular_bitfield::specifiers::*;
use modular_bitfield::{bitfield, BitfieldSpecifier}; use scryer_modular_bitfield::{bitfield, BitfieldSpecifier};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeSet; use std::collections::BTreeSet;
@@ -118,18 +118,12 @@ impl IndexPtr {
#[inline(always)] #[inline(always)]
pub(crate) fn is_undefined(&self) -> bool { pub(crate) fn is_undefined(&self) -> bool {
match self.tag() { matches!(self.tag(), IndexPtrTag::Undefined)
IndexPtrTag::Undefined => true,
_ => false,
}
} }
#[inline(always)] #[inline(always)]
pub(crate) fn is_dynamic_undefined(&self) -> bool { pub(crate) fn is_dynamic_undefined(&self) -> bool {
match self.tag() { matches!(self.tag(), IndexPtrTag::DynamicUndefined)
IndexPtrTag::DynamicUndefined => true,
_ => false,
}
} }
} }
@@ -231,6 +225,7 @@ pub enum VarKey {
} }
impl VarKey { impl VarKey {
#[allow(clippy::inherent_to_string)]
#[inline] #[inline]
pub(crate) fn to_string(&self) -> String { pub(crate) fn to_string(&self) -> String {
match self { match self {
@@ -241,11 +236,7 @@ impl VarKey {
#[inline(always)] #[inline(always)]
pub(crate) fn is_anon(&self) -> bool { pub(crate) fn is_anon(&self) -> bool {
if let VarKey::AnonVar(_) = self { matches!(self, VarKey::AnonVar(_))
true
} else {
false
}
} }
} }
@@ -429,9 +420,9 @@ impl IndexStore {
match compilation_target { match compilation_target {
CompilationTarget::User => self.meta_predicates.get(&(name, arity)), CompilationTarget::User => self.meta_predicates.get(&(name, arity)),
CompilationTarget::Module(ref module_name) => match self.modules.get(module_name) { CompilationTarget::Module(ref module_name) => match self.modules.get(module_name) {
Some(ref module) => module Some(module) => module
.meta_predicates .meta_predicates
.get(&(name.clone(), arity)) .get(&(name, arity))
.or_else(|| self.meta_predicates.get(&(name, arity))), .or_else(|| self.meta_predicates.get(&(name, arity))),
None => self.meta_predicates.get(&(name, arity)), None => self.meta_predicates.get(&(name, arity)),
}, },
@@ -446,7 +437,7 @@ impl IndexStore {
.map(|skeleton| skeleton.core.is_dynamic) .map(|skeleton| skeleton.core.is_dynamic)
.unwrap_or(false), .unwrap_or(false),
_ => match self.modules.get(&module_name) { _ => match self.modules.get(&module_name) {
Some(ref module) => module Some(module) => module
.extensible_predicates .extensible_predicates
.get(&key) .get(&key)
.map(|skeleton| skeleton.core.is_dynamic) .map(|skeleton| skeleton.core.is_dynamic)

View File

@@ -96,7 +96,6 @@ pub struct MachineState {
pub(crate) unify_fn: fn(&mut MachineState), pub(crate) unify_fn: fn(&mut MachineState),
pub(crate) bind_fn: fn(&mut MachineState, Ref, HeapCellValue), pub(crate) bind_fn: fn(&mut MachineState, Ref, HeapCellValue),
pub(crate) run_cleaners_fn: fn(&mut Machine) -> bool, pub(crate) run_cleaners_fn: fn(&mut Machine) -> bool,
pub(crate) increment_call_count_fn: fn(&mut MachineState) -> bool,
} }
impl fmt::Debug for MachineState { impl fmt::Debug for MachineState {
@@ -290,6 +289,11 @@ impl<'a> CopierTarget for CopyTerm<'a> {
self.state.heap.push(hcv); self.state.heap.push(hcv);
} }
#[inline(always)]
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
self.state.attr_var_init.attr_var_queue.push(attr_var_loc);
}
#[inline(always)] #[inline(always)]
fn store(&self, value: HeapCellValue) -> HeapCellValue { fn store(&self, value: HeapCellValue) -> HeapCellValue {
self.state.store(value) self.state.store(value)
@@ -308,6 +312,7 @@ impl<'a> CopierTarget for CopyTerm<'a> {
#[derive(Debug)] #[derive(Debug)]
pub(super) struct CopyBallTerm<'a> { pub(super) struct CopyBallTerm<'a> {
attr_var_queue: &'a mut Vec<usize>,
stack: &'a mut Stack, stack: &'a mut Stack,
heap: &'a mut Heap, heap: &'a mut Heap,
heap_boundary: usize, heap_boundary: usize,
@@ -315,10 +320,16 @@ pub(super) struct CopyBallTerm<'a> {
} }
impl<'a> CopyBallTerm<'a> { impl<'a> CopyBallTerm<'a> {
pub(super) fn new(stack: &'a mut Stack, heap: &'a mut Heap, stub: &'a mut Heap) -> Self { pub(super) fn new(
attr_var_queue: &'a mut Vec<usize>,
stack: &'a mut Stack,
heap: &'a mut Heap,
stub: &'a mut Heap,
) -> Self {
let hb = heap.len(); let hb = heap.len();
CopyBallTerm { CopyBallTerm {
attr_var_queue,
stack, stack,
heap, heap,
heap_boundary: hb, heap_boundary: hb,
@@ -360,6 +371,11 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
self.stub.push(value); self.stub.push(value);
} }
#[inline(always)]
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
self.attr_var_queue.push(attr_var_loc);
}
fn store(&self, value: HeapCellValue) -> HeapCellValue { fn store(&self, value: HeapCellValue) -> HeapCellValue {
read_heap_cell!(value, read_heap_cell!(value,
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => { (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
@@ -413,19 +429,21 @@ impl MachineState {
} }
pub(crate) fn increment_call_count(&mut self) -> bool { pub(crate) fn increment_call_count(&mut self) -> bool {
if self.cwil.inference_limit_exceeded || self.ball.stub.len() > 0 { if self.cwil.inference_limit_exceeded || !self.ball.stub.is_empty() {
return true; return true;
} }
self.cwil.global_count += 1;
if let Some(&(ref limit, block)) = self.cwil.limits.last() { if let Some(&(ref limit, block)) = self.cwil.limits.last() {
if self.cwil.count == *limit { if self.cwil.local_count == *limit {
self.cwil.inference_limit_exceeded = true; self.cwil.inference_limit_exceeded = true;
self.block = block; self.block = block;
self.unwind_stack(); self.unwind_stack();
return false; return false;
} else { } else {
self.cwil.count += 1; self.cwil.local_count += 1;
} }
} }
@@ -590,7 +608,9 @@ impl MachineState {
let mut singleton_var_set: IndexMap<Ref, bool> = IndexMap::new(); let mut singleton_var_set: IndexMap<Ref, bool> = IndexMap::new();
for cell in stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, heap_loc) { for cell in
stackful_preorder_iter::<NonListElider>(&mut self.heap, &mut self.stack, heap_loc)
{
let cell = unmark_cell_bits!(cell); let cell = unmark_cell_bits!(cell);
if let Some(var) = cell.as_var() { if let Some(var) = cell.as_var() {
@@ -644,10 +664,8 @@ impl MachineState {
) -> Result<OnEOF, MachineStub> { ) -> Result<OnEOF, MachineStub> {
self.eof_action(self.registers[2], stream, atom!("read_term"), 3)?; self.eof_action(self.registers[2], stream, atom!("read_term"), 3)?;
if stream.options().eof_action() == EOFAction::Reset { if stream.options().eof_action() == EOFAction::Reset && !self.fail {
if self.fail == false { return Ok(OnEOF::Continue);
return Ok(OnEOF::Continue);
}
} }
Ok(OnEOF::Return) Ok(OnEOF::Return)
@@ -674,10 +692,10 @@ impl MachineState {
if let Stream::Byte(_) = stream { if let Stream::Byte(_) = stream {
return self.read_term( return self.read_term(
stream, stream,
indices, indices,
MachineState::read_term_from_user_input_eof_handler MachineState::read_term_from_user_input_eof_handler,
) );
} }
unreachable!("Stream must be a Stream::Readline(_)") unreachable!("Stream must be a Stream::Readline(_)")
@@ -691,10 +709,8 @@ impl MachineState {
} else if stream.past_end_of_stream() { } else if stream.past_end_of_stream() {
self.eof_action(self.registers[2], stream, atom!("read_term"), 3)?; self.eof_action(self.registers[2], stream, atom!("read_term"), 3)?;
if stream.options().eof_action() == EOFAction::Reset { if stream.options().eof_action() == EOFAction::Reset && !self.fail {
if self.fail == false { return Ok(OnEOF::Continue);
return Ok(OnEOF::Continue);
}
} }
} }
@@ -716,11 +732,7 @@ impl MachineState {
)?; )?;
if stream.past_end_of_stream() { if stream.past_end_of_stream() {
if EOFAction::Reset != stream.options().eof_action() { return Ok(());
return Ok(());
} else if self.fail {
return Ok(());
}
} }
loop { loop {
@@ -970,9 +982,11 @@ impl MachineState {
} }
} }
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct CWIL { pub(crate) struct CWIL {
count: Integer, local_count: Integer,
pub(crate) global_count: Integer,
limits: Vec<(Integer, usize)>, limits: Vec<(Integer, usize)>,
pub(crate) inference_limit_exceeded: bool, pub(crate) inference_limit_exceeded: bool,
} }
@@ -980,22 +994,22 @@ pub(crate) struct CWIL {
impl CWIL { impl CWIL {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
CWIL { CWIL {
count: Integer::from(0), local_count: Integer::from(0),
global_count: Integer::from(0),
limits: vec![], limits: vec![],
inference_limit_exceeded: false, inference_limit_exceeded: false,
} }
} }
pub(crate) fn add_limit(&mut self, limit: usize, block: usize) -> &Integer { pub(crate) fn add_limit(&mut self, mut limit: Integer, block: usize) -> &Integer {
let mut limit = Integer::from(limit); limit += &self.local_count;
limit += &self.count;
match self.limits.last() { match self.limits.last() {
Some((ref inner_limit, _)) if *inner_limit <= limit => {} Some((ref inner_limit, _)) if *inner_limit <= limit => {}
_ => self.limits.push((limit, block)), _ => self.limits.push((limit, block)),
}; }
&self.count &self.local_count
} }
#[inline(always)] #[inline(always)]
@@ -1006,12 +1020,12 @@ impl CWIL {
} }
} }
&self.count &self.local_count
} }
#[inline(always)] #[inline(always)]
pub(crate) fn reset(&mut self) { pub(crate) fn reset(&mut self) {
self.count = Integer::from(0); self.local_count = Integer::from(0);
self.limits.clear(); self.limits.clear();
self.inference_limit_exceeded = false; self.inference_limit_exceeded = false;
} }

View File

@@ -60,7 +60,6 @@ impl MachineState {
unify_fn: MachineState::unify, unify_fn: MachineState::unify,
bind_fn: MachineState::bind, bind_fn: MachineState::bind,
run_cleaners_fn: |_| false, run_cleaners_fn: |_| false,
increment_call_count_fn: |_| true,
} }
} }
@@ -149,7 +148,7 @@ impl MachineState {
TrailRef::BlackboardEntry(key_atom) => { TrailRef::BlackboardEntry(key_atom) => {
self.trail.push(TrailEntry::build_with( self.trail.push(TrailEntry::build_with(
TrailEntryTag::TrailedBlackboardEntry, TrailEntryTag::TrailedBlackboardEntry,
key_atom.index as u64, key_atom.index,
)); ));
self.tr += 1; self.tr += 1;
@@ -157,7 +156,7 @@ impl MachineState {
TrailRef::BlackboardOffset(key_atom, value_cell) => { TrailRef::BlackboardOffset(key_atom, value_cell) => {
self.trail.push(TrailEntry::build_with( self.trail.push(TrailEntry::build_with(
TrailEntryTag::TrailedBlackboardOffset, TrailEntryTag::TrailedBlackboardOffset,
key_atom.index as u64, key_atom.index,
)); ));
self.trail self.trail
@@ -335,7 +334,12 @@ impl MachineState {
self.ball.boundary = self.heap.len(); self.ball.boundary = self.heap.len();
copy_term( copy_term(
CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut self.ball.stub), CopyBallTerm::new(
&mut self.attr_var_init.attr_var_queue,
&mut self.stack,
&mut self.heap,
&mut self.ball.stub,
),
addr, addr,
AttrVarPolicy::DeepCopy, AttrVarPolicy::DeepCopy,
); );
@@ -432,8 +436,7 @@ impl MachineState {
pub fn compare_term_test(&mut self, var_comparison: VarComparison) -> Option<Ordering> { pub fn compare_term_test(&mut self, var_comparison: VarComparison) -> Option<Ordering> {
let mut tabu_list = IndexSet::new(); let mut tabu_list = IndexSet::new();
while !self.pdl.is_empty() { while let Some(s1) = self.pdl.pop() {
let s1 = self.pdl.pop().unwrap();
let s1 = self.deref(s1); let s1 = self.deref(s1);
let s2 = self.pdl.pop().unwrap(); let s2 = self.pdl.pop().unwrap();
@@ -896,7 +899,7 @@ impl MachineState {
let s = string.as_str(); let s = string.as_str();
match heap_pstr_iter.compare_pstr_to_string(&*s) { match heap_pstr_iter.compare_pstr_to_string(&s) {
Some(PStrPrefixCmpResult { Some(PStrPrefixCmpResult {
focus, focus,
offset, offset,
@@ -1142,7 +1145,7 @@ impl MachineState {
let cycle_found = { let cycle_found = {
let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h); let mut iter = cycle_detecting_stackless_preorder_iter(&mut self.heap, h);
while let Some(_) = iter.next() {} for _ in iter.by_ref() {}
iter.cycle_found() iter.cycle_found()
}; };
@@ -1376,7 +1379,7 @@ impl MachineState {
let mut type_error = |arity| { let mut type_error = |arity| {
let err = self.type_error(ValidType::Integer, arity); let err = self.type_error(ValidType::Integer, arity);
return Err(self.error_form(err, stub_gen())); Err(self.error_form(err, stub_gen()))
}; };
let arity = match Number::try_from(arity) { let arity = match Number::try_from(arity) {
@@ -1447,10 +1450,15 @@ impl MachineState {
a1.as_var().unwrap(), a1.as_var().unwrap(),
); );
} }
(HeapCellValueTag::Cons | HeapCellValueTag::Fixnum |
HeapCellValueTag::F64) if arity != 0 => {
let err = self.type_error(ValidType::Atom, store_name);
return Err(self.error_form(err, stub_gen())); // 8.5.1.3 e)
}
_ => { _ => {
let err = self.type_error(ValidType::Atomic, store_name); let err = self.type_error(ValidType::Atomic, store_name);
return Err(self.error_form(err, stub_gen())); return Err(self.error_form(err, stub_gen())); // 8.5.1.3 c)
} // 8.5.1.3 c) }
); );
} }
_ => { _ => {
@@ -1573,7 +1581,7 @@ impl MachineState {
) -> Result<Vec<HeapCellValue>, MachineStub> { ) -> Result<Vec<HeapCellValue>, MachineStub> {
let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, h); let mut heap_pstr_iter = HeapPStrIter::new(&self.heap, h);
while let Some(iteratee) = heap_pstr_iter.next() { for iteratee in heap_pstr_iter.by_ref() {
match iteratee { match iteratee {
PStrIteratee::Char(_, c) => chars.push(char_as_cell!(c)), PStrIteratee::Char(_, c) => chars.push(char_as_cell!(c)),
PStrIteratee::PStrSegment(_, pstr_atom, n) => { PStrIteratee::PStrSegment(_, pstr_atom, n) => {
@@ -1644,10 +1652,11 @@ impl MachineState {
let addr = self.store(self.deref(addr)); let addr = self.store(self.deref(addr));
match Number::try_from(addr) { match Number::try_from(addr) {
Ok(Number::Fixnum(n)) => match u8::try_from(n.get_num()) { Ok(Number::Fixnum(n)) => {
Ok(b) => bytes.push(b), if let Ok(b) = u8::try_from(n.get_num()) {
Err(_) => {} bytes.push(b)
}, }
}
Ok(Number::Integer(n)) => { Ok(Number::Integer(n)) => {
let b: u8 = (&*n).try_into().unwrap(); let b: u8 = (&*n).try_into().unwrap();

View File

@@ -82,6 +82,12 @@ impl MockWAM {
} }
} }
impl Default for MockWAM {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)] #[cfg(test)]
pub struct TermCopyingMockWAM<'a> { pub struct TermCopyingMockWAM<'a> {
pub wam: &'a mut MockWAM, pub wam: &'a mut MockWAM,
@@ -109,14 +115,14 @@ impl<'a> Deref for TermCopyingMockWAM<'a> {
type Target = MockWAM; type Target = MockWAM;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.wam self.wam
} }
} }
#[cfg(test)] #[cfg(test)]
impl<'a> DerefMut for TermCopyingMockWAM<'a> { impl<'a> DerefMut for TermCopyingMockWAM<'a> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.wam self.wam
} }
} }
@@ -153,6 +159,14 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
self.wam.machine_st.heap.push(val); self.wam.machine_st.heap.push(val);
} }
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
self.wam
.machine_st
.attr_var_init
.attr_var_queue
.push(attr_var_loc);
}
fn stack(&mut self) -> &mut Stack { fn stack(&mut self) -> &mut Stack {
&mut self.wam.machine_st.stack &mut self.wam.machine_st.stack
} }
@@ -165,9 +179,8 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
#[cfg(test)] #[cfg(test)]
pub fn all_cells_marked_and_unforwarded(heap: &[HeapCellValue]) { pub fn all_cells_marked_and_unforwarded(heap: &[HeapCellValue]) {
for (idx, cell) in heap.iter().enumerate() { for (idx, cell) in heap.iter().enumerate() {
assert_eq!( assert!(
cell.get_mark_bit(), cell.get_mark_bit(),
true,
"cell {:?} at index {} is not marked", "cell {:?} at index {} is not marked",
cell, cell,
idx idx
@@ -230,20 +243,16 @@ impl Machine {
&mut self.machine_st.arena, &mut self.machine_st.arena,
); );
self.load_file(file.into(), stream); self.load_file(file, stream);
self.user_output.bytes().map(|b| b.unwrap()).collect() self.user_output.bytes().map(|b| b.unwrap()).collect()
} }
pub fn test_load_string(&mut self, code: &str) -> Vec<u8> { pub fn test_load_string(&mut self, code: &str) -> Vec<u8> {
let stream = Stream::from_owned_string( let stream = Stream::from_owned_string(code.to_owned(), &mut self.machine_st.arena);
code.to_owned(),
&mut self.machine_st.arena,
);
self.load_file("<stdin>".into(), stream); self.load_file("<stdin>", stream);
self.user_output.bytes().map(|b| b.unwrap()).collect() self.user_output.bytes().map(|b| b.unwrap()).collect()
} }
} }
#[cfg(test)] #[cfg(test)]
@@ -251,6 +260,7 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn unify_tests() { fn unify_tests() {
let mut wam = MachineState::new(); let mut wam = MachineState::new();
let mut op_dir = default_op_dir(); let mut op_dir = default_op_dir();
@@ -472,6 +482,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn test_unify_with_occurs_check() { fn test_unify_with_occurs_check() {
let mut wam = MachineState::new(); let mut wam = MachineState::new();
let mut op_dir = default_op_dir(); let mut op_dir = default_op_dir();

View File

@@ -53,6 +53,8 @@ use indexmap::IndexMap;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use rand::rngs::StdRng;
use rand::SeedableRng;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::env; use std::env;
use std::io::Read; use std::io::Read;
@@ -61,8 +63,6 @@ use std::sync::atomic::AtomicBool;
use self::config::MachineConfig; use self::config::MachineConfig;
use self::parsed_results::*; use self::parsed_results::*;
use rand::rngs::StdRng;
use rand::SeedableRng;
lazy_static! { lazy_static! {
pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false); pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
@@ -172,7 +172,7 @@ pub(crate) fn import_builtin_impls(code_dir: &CodeDir, builtins: &mut Module) {
for key in keys { for key in keys {
let idx = code_dir.get(&key).unwrap(); let idx = code_dir.get(&key).unwrap();
builtins.code_dir.insert(key, idx.clone()); builtins.code_dir.insert(key, *idx);
builtins builtins
.module_decl .module_decl
.exports .exports
@@ -211,6 +211,15 @@ impl Machine {
) )
} }
pub fn get_inference_count(&mut self) -> u64 {
self.machine_st
.cwil
.global_count
.clone()
.try_into()
.unwrap()
}
pub fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) { pub fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) {
let err = self.machine_st.session_error(err); let err = self.machine_st.session_error(err);
let stub = functor_stub(key.0, key.1); let stub = functor_stub(key.0, key.1);
@@ -219,13 +228,13 @@ impl Machine {
self.machine_st.throw_exception(err); self.machine_st.throw_exception(err);
} }
fn run_module_predicate( pub fn run_module_predicate(
&mut self, &mut self,
module_name: Atom, module_name: Atom,
key: PredicateKey, key: PredicateKey,
) -> std::process::ExitCode { ) -> std::process::ExitCode {
if let Some(module) = self.indices.modules.get(&module_name) { if let Some(module) = self.indices.modules.get(&module_name) {
if let Some(ref code_index) = module.code_dir.get(&key) { if let Some(code_index) = module.code_dir.get(&key) {
let p = code_index.local().unwrap(); let p = code_index.local().unwrap();
self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC;
@@ -252,9 +261,7 @@ impl Machine {
path_buf.push("src/toplevel.pl"); path_buf.push("src/toplevel.pl");
let path = path_buf.to_str().unwrap(); let path = path_buf.to_str().unwrap();
let toplevel_stream = let toplevel_stream = Stream::from_static_string(program, &mut self.machine_st.arena);
Stream::from_static_string(program, &mut self.machine_st.arena);
self.load_file(path, toplevel_stream); self.load_file(path, toplevel_stream);
@@ -300,25 +307,6 @@ impl Machine {
} }
} }
pub fn run_top_level(&mut self, module_name: Atom, key: PredicateKey) -> std::process::ExitCode {
let mut arg_pstrs = vec![];
for arg in env::args() {
arg_pstrs.push(put_complete_string(
&mut self.machine_st.heap,
&arg,
&self.machine_st.atom_tbl,
));
}
self.machine_st.registers[1] = heap_loc_as_cell!(iter_to_heap_list(
&mut self.machine_st.heap,
arg_pstrs.into_iter()
));
self.run_module_predicate(module_name, key)
}
pub fn set_user_input(&mut self, input: String) { pub fn set_user_input(&mut self, input: String) {
self.user_input = Stream::from_owned_string(input, &mut self.machine_st.arena); self.user_input = Stream::from_owned_string(input, &mut self.machine_st.arena);
} }
@@ -400,57 +388,51 @@ impl Machine {
pub(crate) fn add_impls_to_indices(&mut self) { pub(crate) fn add_impls_to_indices(&mut self) {
let impls_offset = self.code.len() + 4; let impls_offset = self.code.len() + 4;
self.code.extend( self.code.extend(vec![
vec![ Instruction::BreakFromDispatchLoop,
Instruction::BreakFromDispatchLoop, Instruction::InstallVerifyAttr,
Instruction::InstallVerifyAttr, Instruction::VerifyAttrInterrupt(0),
Instruction::VerifyAttrInterrupt, Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS
Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS Instruction::ExecuteTermGreaterThan,
Instruction::ExecuteTermGreaterThan, Instruction::ExecuteTermLessThan,
Instruction::ExecuteTermLessThan, Instruction::ExecuteTermGreaterThanOrEqual,
Instruction::ExecuteTermGreaterThanOrEqual, Instruction::ExecuteTermLessThanOrEqual,
Instruction::ExecuteTermLessThanOrEqual, Instruction::ExecuteTermEqual,
Instruction::ExecuteTermEqual, Instruction::ExecuteTermNotEqual,
Instruction::ExecuteTermNotEqual, Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
Instruction::ExecuteNumberGreaterThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
Instruction::ExecuteNumberLessThan(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), Instruction::ExecuteNumberGreaterThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
Instruction::ExecuteNumberGreaterThanOrEqual( Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
ar_reg!(temp_v!(1)), Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
ar_reg!(temp_v!(2)), Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))),
), Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2))),
Instruction::ExecuteNumberLessThanOrEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), Instruction::ExecuteAcyclicTerm,
Instruction::ExecuteNumberEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), Instruction::ExecuteArg,
Instruction::ExecuteNumberNotEqual(ar_reg!(temp_v!(1)), ar_reg!(temp_v!(2))), Instruction::ExecuteCompare,
Instruction::ExecuteIs(temp_v!(1), ar_reg!(temp_v!(2))), Instruction::ExecuteCopyTerm,
Instruction::ExecuteAcyclicTerm, Instruction::ExecuteFunctor,
Instruction::ExecuteArg, Instruction::ExecuteGround,
Instruction::ExecuteCompare, Instruction::ExecuteKeySort,
Instruction::ExecuteCopyTerm, Instruction::ExecuteSort,
Instruction::ExecuteFunctor, Instruction::ExecuteN(1),
Instruction::ExecuteGround, Instruction::ExecuteN(2),
Instruction::ExecuteKeySort, Instruction::ExecuteN(3),
Instruction::ExecuteSort, Instruction::ExecuteN(4),
Instruction::ExecuteN(1), Instruction::ExecuteN(5),
Instruction::ExecuteN(2), Instruction::ExecuteN(6),
Instruction::ExecuteN(3), Instruction::ExecuteN(7),
Instruction::ExecuteN(4), Instruction::ExecuteN(8),
Instruction::ExecuteN(5), Instruction::ExecuteN(9),
Instruction::ExecuteN(6), Instruction::ExecuteIsAtom(temp_v!(1)),
Instruction::ExecuteN(7), Instruction::ExecuteIsAtomic(temp_v!(1)),
Instruction::ExecuteN(8), Instruction::ExecuteIsCompound(temp_v!(1)),
Instruction::ExecuteN(9), Instruction::ExecuteIsInteger(temp_v!(1)),
Instruction::ExecuteIsAtom(temp_v!(1)), Instruction::ExecuteIsNumber(temp_v!(1)),
Instruction::ExecuteIsAtomic(temp_v!(1)), Instruction::ExecuteIsRational(temp_v!(1)),
Instruction::ExecuteIsCompound(temp_v!(1)), Instruction::ExecuteIsFloat(temp_v!(1)),
Instruction::ExecuteIsInteger(temp_v!(1)), Instruction::ExecuteIsNonVar(temp_v!(1)),
Instruction::ExecuteIsNumber(temp_v!(1)), Instruction::ExecuteIsVar(temp_v!(1)),
Instruction::ExecuteIsRational(temp_v!(1)), ]);
Instruction::ExecuteIsFloat(temp_v!(1)),
Instruction::ExecuteIsNonVar(temp_v!(1)),
Instruction::ExecuteIsVar(temp_v!(1)),
]
.into_iter(),
);
for (p, instr) in self.code[impls_offset..].iter().enumerate() { for (p, instr) in self.code[impls_offset..].iter().enumerate() {
let key = instr.to_name_and_arity(); let key = instr.to_name_and_arity();
@@ -464,6 +446,7 @@ impl Machine {
} }
} }
#[allow(clippy::new_without_default)]
pub fn new(config: MachineConfig) -> Self { pub fn new(config: MachineConfig) -> Self {
use ref_thread_local::RefThreadLocal; use ref_thread_local::RefThreadLocal;
@@ -919,8 +902,8 @@ impl Machine {
self.machine_st.hb = self.machine_st.heap.len(); self.machine_st.hb = self.machine_st.heap.len();
self.machine_st.oip = 0; // self.machine_st.oip = 0;
self.machine_st.iip = 0; // self.machine_st.iip = 0;
} }
self.machine_st.p += offset; self.machine_st.p += offset;
@@ -1004,8 +987,22 @@ impl Machine {
self.machine_st.heap.truncate(target_h); self.machine_st.heap.truncate(target_h);
self.machine_st.oip = 0; // these registers don't need to be reset here and MUST
self.machine_st.iip = 0; // NOT be (nor in indexed_try! trust_epilogue is an
// exception, see next paragraph)! oip could be reset
// without any adverse effects but iip is needed by
// get_clause_p to find the last executed clause/2 clause.
// trust_epilogue must reset these for the sake of
// subsequent predicates beginning with
// switch_to_term. get_clause_p copes by checking
// self.machine_st.b > self.machine.e: if true, it is safe
// to use self.machine_st.iip; if false, use the choice
// point left at the top of the stack by '$clause'
// (specifically its biip value).
// self.machine_st.oip = 0;
// self.machine_st.iip = 0;
} else { } else {
self.trust_epilogue(offset); self.trust_epilogue(offset);
} }
@@ -1048,7 +1045,7 @@ impl Machine {
self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len); self.reset_attr_var_state(or_frame.prelude.attr_var_queue_len);
self.machine_st.hb = target_h; self.machine_st.hb = target_h;
self.machine_st.p = self.machine_st.p + offset; self.machine_st.p += offset;
self.machine_st.stack.truncate(b); self.machine_st.stack.truncate(b);
self.machine_st.heap.truncate(target_h); self.machine_st.heap.truncate(target_h);
@@ -1110,7 +1107,7 @@ impl Machine {
} }
Unknown::Warn => { Unknown::Warn => {
println!( println!(
"warning: predicate {}/{} is undefined", "% Warning: predicate {}/{} is undefined",
name.as_str(), name.as_str(),
arity arity
); );
@@ -1174,21 +1171,23 @@ impl Machine {
} else { } else {
Err(self.machine_st.throw_undefined_error(name, arity)) Err(self.machine_st.throw_undefined_error(name, arity))
} }
} else { } else if let Some(module) = self.indices.modules.get(&module_name) {
if let Some(module) = self.indices.modules.get(&module_name) { if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() { self.try_call(name, arity, idx.get())
self.try_call(name, arity, idx.get())
} else {
self.undefined_procedure(name, arity)
}
} else { } else {
let stub = functor_stub(name, arity); self.undefined_procedure(name, arity)
let err = self
.machine_st
.existence_error(ExistenceError::QualifiedProcedure { module_name, name, arity });
Err(self.machine_st.error_form(err, stub))
} }
} else {
let stub = functor_stub(name, arity);
let err = self
.machine_st
.existence_error(ExistenceError::QualifiedProcedure {
module_name,
name,
arity,
});
Err(self.machine_st.error_form(err, stub))
} }
} }
@@ -1202,21 +1201,23 @@ impl Machine {
} else { } else {
self.undefined_procedure(name, arity) self.undefined_procedure(name, arity)
} }
} else { } else if let Some(module) = self.indices.modules.get(&module_name) {
if let Some(module) = self.indices.modules.get(&module_name) { if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() {
if let Some(idx) = module.code_dir.get(&(name, arity)).cloned() { self.try_execute(name, arity, idx.get())
self.try_execute(name, arity, idx.get())
} else {
self.undefined_procedure(name, arity)
}
} else { } else {
let stub = functor_stub(name, arity); self.undefined_procedure(name, arity)
let err = self
.machine_st
.existence_error(ExistenceError::QualifiedProcedure { module_name, name, arity });
Err(self.machine_st.error_form(err, stub))
} }
} else {
let stub = functor_stub(name, arity);
let err = self
.machine_st
.existence_error(ExistenceError::QualifiedProcedure {
module_name,
name,
arity,
});
Err(self.machine_st.error_form(err, stub))
} }
} }

View File

@@ -1,6 +1,6 @@
use crate::atom_table::*; use crate::atom_table::*;
use ordered_float::OrderedFloat;
use dashu::*; use dashu::*;
use ordered_float::OrderedFloat;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
@@ -13,6 +13,84 @@ pub enum QueryResolution {
Matches(Vec<QueryMatch>), Matches(Vec<QueryMatch>),
} }
pub fn prolog_value_to_json_string(value: Value) -> String {
match value {
Value::Integer(i) => format!("{}", i),
Value::Float(f) => format!("{}", f),
Value::Rational(r) => format!("{}", r),
Value::Atom(a) => format!("{}", a.as_str()),
Value::String(s) => {
if let Err(_e) = serde_json::from_str::<serde_json::Value>(s.as_str()) {
//treat as string literal
//escape double quotes
format!(
"\"{}\"",
s.replace('\"', "\\\"")
.replace('\n', "\\n")
.replace('\t', "\\t")
.replace('\r', "\\r")
)
} else {
//return valid json string
s
}
}
Value::List(l) => {
let mut string_result = "[".to_string();
for (i, v) in l.iter().enumerate() {
if i > 0 {
string_result.push(',');
}
string_result.push_str(&prolog_value_to_json_string(v.clone()));
}
string_result.push(']');
string_result
}
Value::Structure(s, l) => {
let mut string_result = format!("\"{}\":[", s.as_str());
for (i, v) in l.iter().enumerate() {
if i > 0 {
string_result.push(',');
}
string_result.push_str(&prolog_value_to_json_string(v.clone()));
}
string_result.push(']');
string_result
}
_ => "null".to_string(),
}
}
fn prolog_match_to_json_string(query_match: &QueryMatch) -> String {
let mut string_result = "{".to_string();
for (i, (k, v)) in query_match.bindings.iter().enumerate() {
if i > 0 {
string_result.push(',');
}
string_result.push_str(&format!(
"\"{}\":{}",
k,
prolog_value_to_json_string(v.clone())
));
}
string_result.push('}');
string_result
}
impl ToString for QueryResolution {
fn to_string(&self) -> String {
match self {
QueryResolution::True => "true".to_string(),
QueryResolution::False => "false".to_string(),
QueryResolution::Matches(matches) => {
let matches_json: Vec<String> =
matches.iter().map(prolog_match_to_json_string).collect();
format!("[{}]", matches_json.join(","))
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct QueryMatch { pub struct QueryMatch {
pub bindings: BTreeMap<String, Value>, pub bindings: BTreeMap<String, Value>,
@@ -65,15 +143,12 @@ impl From<Vec<QueryResolutionLine>> for QueryResolution {
} }
} }
// If there is only one line, and it is an empty match, return true. // If there is only one line, and it is an empty match, return false.
if query_result_lines.len() == 1 { if query_result_lines.len() == 1 {
match query_result_lines[0].clone() { if let QueryResolutionLine::Match(m) = query_result_lines[0].clone() {
QueryResolutionLine::Match(m) => { if m.is_empty() {
if m.is_empty() { return QueryResolution::False;
return QueryResolution::True;
}
} }
_ => {}
} }
} }
@@ -81,13 +156,9 @@ impl From<Vec<QueryResolutionLine>> for QueryResolution {
if query_result_lines if query_result_lines
.iter() .iter()
.any(|l| l == &QueryResolutionLine::True) .any(|l| l == &QueryResolutionLine::True)
&& !query_result_lines.iter().any(|l| { && !query_result_lines
if let &QueryResolutionLine::Match(_) = l { .iter()
true .any(|l| matches!(l, QueryResolutionLine::Match(_)))
} else {
false
}
})
{ {
return QueryResolution::True; return QueryResolution::True;
} }
@@ -95,13 +166,7 @@ impl From<Vec<QueryResolutionLine>> for QueryResolution {
// If there is at least one match, return all matches. // If there is at least one match, return all matches.
let all_matches = query_result_lines let all_matches = query_result_lines
.into_iter() .into_iter()
.filter(|l| { .filter(|l| matches!(l, QueryResolutionLine::Match(_)))
if let &QueryResolutionLine::Match(_) = l {
true
} else {
false
}
})
.map(|l| match l { .map(|l| match l {
QueryResolutionLine::Match(m) => QueryMatch::from(m), QueryResolutionLine::Match(m) => QueryMatch::from(m),
_ => unreachable!(), _ => unreachable!(),
@@ -132,7 +197,11 @@ fn split_response_string(input: &str) -> Vec<String> {
')' => level_parenthesis -= 1, ')' => level_parenthesis -= 1,
'"' => in_double_quotes = !in_double_quotes, '"' => in_double_quotes = !in_double_quotes,
'\'' => in_single_quotes = !in_single_quotes, '\'' => in_single_quotes = !in_single_quotes,
',' if level_bracket == 0 && level_parenthesis == 0 && !in_double_quotes && !in_single_quotes => { ',' if level_bracket == 0
&& level_parenthesis == 0
&& !in_double_quotes
&& !in_single_quotes =>
{
result.push(input[start..i].trim().to_string()); result.push(input[start..i].trim().to_string());
start = i + 1; start = i + 1;
} }
@@ -167,13 +236,13 @@ fn parse_prolog_response(input: &str) -> HashMap<String, String> {
let key = result.0; let key = result.0;
let value = result.1; let value = result.1;
// cut off at given characters/strings: // cut off at given characters/strings:
let value = value.split("\n").next().unwrap().to_string(); let value = value.split('\n').next().unwrap().to_string();
let value = value.split(" ").next().unwrap().to_string(); let value = value.split(' ').next().unwrap().to_string();
let value = value.split("\t").next().unwrap().to_string(); let value = value.split('\t').next().unwrap().to_string();
let value = value.split("error").next().unwrap().to_string(); let value = value.split("error").next().unwrap().to_string();
map.insert(key, value); map.insert(key, value);
} }
map map
} }
@@ -192,9 +261,8 @@ impl TryFrom<String> for QueryResolutionLine {
Ok((key, Value::try_from(value)?)) Ok((key, Value::try_from(value)?))
}) })
.filter_map(Result::ok) .filter_map(Result::ok)
.collect::<BTreeMap<_, _>>() .collect::<BTreeMap<_, _>>(),
) )),
),
} }
} }
} }
@@ -229,25 +297,25 @@ impl TryFrom<String> for Value {
Ok(Value::Float(OrderedFloat(float_value))) Ok(Value::Float(OrderedFloat(float_value)))
} else if let Ok(int_value) = string.parse::<i128>() { } else if let Ok(int_value) = string.parse::<i128>() {
Ok(Value::Integer(int_value.into())) Ok(Value::Integer(int_value.into()))
} else if trimmed.starts_with("'") && trimmed.ends_with("'") { } else if trimmed.starts_with('\'') && trimmed.ends_with('\'')
|| trimmed.starts_with('"') && trimmed.ends_with('"')
{
Ok(Value::String(trimmed[1..trimmed.len() - 1].into())) Ok(Value::String(trimmed[1..trimmed.len() - 1].into()))
} else if trimmed.starts_with("\"") && trimmed.ends_with("\"") { } else if trimmed.starts_with('[') && trimmed.ends_with(']') {
Ok(Value::String(trimmed[1..trimmed.len() - 1].into()))
} else if trimmed.starts_with("[") && trimmed.ends_with("]") {
let split = split_nested_list(&trimmed[1..trimmed.len() - 1]); let split = split_nested_list(&trimmed[1..trimmed.len() - 1]);
let values = split let values = split
.into_iter() .into_iter()
.map(Value::try_from) .map(Value::try_from)
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
Ok(Value::List(values)) Ok(Value::List(values))
} else if trimmed.starts_with("{") && trimmed.ends_with("}") { } else if trimmed.starts_with('{') && trimmed.ends_with('}') {
let mut iter = trimmed[1..trimmed.len() - 1].split(","); let iter = trimmed[1..trimmed.len() - 1].split(',');
let mut values = vec![]; let mut values = vec![];
while let Some(value) = iter.next() { for value in iter {
let items: Vec<_> = value.split(":").collect(); let items: Vec<_> = value.split(':').collect();
if items.len() == 2 { if items.len() == 2 {
let _key = items[0].to_string(); let _key = items[0].to_string();
let value = items[1].to_string(); let value = items[1].to_string();
@@ -257,11 +325,11 @@ impl TryFrom<String> for Value {
Ok(Value::Structure(atom!("{}"), values)) Ok(Value::Structure(atom!("{}"), values))
} else if trimmed.starts_with("<<") && trimmed.ends_with(">>") { } else if trimmed.starts_with("<<") && trimmed.ends_with(">>") {
let mut iter = trimmed[2..trimmed.len() - 2].split(","); let iter = trimmed[2..trimmed.len() - 2].split(',');
let mut values = vec![]; let mut values = vec![];
while let Some(value) = iter.next() { for value in iter {
let items: Vec<_> = value.split(":").collect(); let items: Vec<_> = value.split(':').collect();
if items.len() == 2 { if items.len() == 2 {
let _key = items[0].to_string(); let _key = items[0].to_string();
let value = items[1].to_string(); let value = items[1].to_string();
@@ -270,7 +338,7 @@ impl TryFrom<String> for Value {
} }
Ok(Value::Structure(atom!("<<>>"), values)) Ok(Value::Structure(atom!("<<>>"), values))
} else if !trimmed.contains(",") && !trimmed.contains("'") && !trimmed.contains("\"") { } else if !trimmed.contains(',') && !trimmed.contains('\'') && !trimmed.contains('"') {
Ok(Value::String(trimmed.into())) Ok(Value::String(trimmed.into()))
} else { } else {
Err(()) Err(())

View File

@@ -34,10 +34,10 @@ impl From<Atom> for PartialString {
} }
} }
impl Into<Atom> for PartialString { impl From<PartialString> for Atom {
#[inline] #[inline]
fn into(self: Self) -> Atom { fn from(val: PartialString) -> Self {
self.0 val.0
} }
} }
@@ -45,7 +45,7 @@ impl PartialString {
#[inline] #[inline]
pub(super) fn new<'a>(src: &'a str, atom_tbl: &AtomTable) -> Option<(Self, &'a str)> { pub(super) fn new<'a>(src: &'a str, atom_tbl: &AtomTable) -> Option<(Self, &'a str)> {
let terminator_idx = scan_for_terminator(src.chars()); let terminator_idx = scan_for_terminator(src.chars());
let pstr = PartialString(AtomTable::build_with(&atom_tbl, &src[..terminator_idx])); let pstr = PartialString(AtomTable::build_with(atom_tbl, &src[..terminator_idx]));
Some(if terminator_idx < src.as_bytes().len() { Some(if terminator_idx < src.as_bytes().len() {
(pstr, &src[terminator_idx + 1..]) (pstr, &src[terminator_idx + 1..])
} else { } else {
@@ -68,7 +68,7 @@ pub struct HeapPStrIter<'a> {
stepper: fn(&mut HeapPStrIter<'a>) -> Option<PStrIteratee>, stepper: fn(&mut HeapPStrIter<'a>) -> Option<PStrIteratee>,
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub struct PStrPrefixCmpResult { pub struct PStrPrefixCmpResult {
pub focus: usize, pub focus: usize,
pub offset: usize, pub offset: usize,
@@ -154,13 +154,13 @@ impl<'a> HeapPStrIter<'a> {
let s = &s[result.prefix_len..]; let s = &s[result.prefix_len..];
if s.len() >= t.len() { if s.len() >= t.len() {
if (&*s).starts_with(&*t) { if s.starts_with(&*t) {
result.prefix_len += t.len(); result.prefix_len += t.len();
result.offset += t.len(); result.offset += t.len();
} else { } else {
return None; return None;
} }
} else if t.starts_with(&s) { } else if t.starts_with(s) {
result.prefix_len += s.len(); result.prefix_len += s.len();
result.offset += s.len(); result.offset += s.len();
@@ -193,7 +193,7 @@ impl<'a> HeapPStrIter<'a> {
} }
} }
final_result Some(result)
} }
fn walk_hare_to_cycle_end(&mut self) { fn walk_hare_to_cycle_end(&mut self) {
@@ -218,10 +218,11 @@ impl<'a> HeapPStrIter<'a> {
self.brent_st.hare = orig_hare; self.brent_st.hare = orig_hare;
} }
#[allow(clippy::inherent_to_string)]
pub fn to_string(&mut self) -> String { pub fn to_string(&mut self) -> String {
let mut buf = String::with_capacity(32); let mut buf = String::with_capacity(32);
while let Some(iteratee) = self.next() { for iteratee in self.by_ref() {
match iteratee { match iteratee {
PStrIteratee::Char(_, c) => { PStrIteratee::Char(_, c) => {
buf.push(c); buf.push(c);
@@ -334,14 +335,10 @@ impl<'a> HeapPStrIter<'a> {
heap_bound_deref(self.heap, self.heap[h]), heap_bound_deref(self.heap, self.heap[h]),
); );
return if let Some(c) = value.as_char() { return value.as_char().map(|c| PStrIterStep {
Some(PStrIterStep { iteratee: PStrIteratee::Char(curr_hare, c),
iteratee: PStrIteratee::Char(curr_hare, c), next_hare: h+1,
next_hare: h+1, });
})
} else {
None
};
} }
(HeapCellValueTag::Str, s) => { (HeapCellValueTag::Str, s) => {
let (name, arity) = cell_as_atom_cell!(self.heap[s]) let (name, arity) = cell_as_atom_cell!(self.heap[s])
@@ -353,14 +350,10 @@ impl<'a> HeapPStrIter<'a> {
heap_bound_deref(self.heap, self.heap[s+1]), heap_bound_deref(self.heap, self.heap[s+1]),
); );
if let Some(c) = value.as_char() { value.as_char().map(|c| PStrIterStep {
Some(PStrIterStep { iteratee: PStrIteratee::Char(curr_hare, c),
iteratee: PStrIteratee::Char(curr_hare, c), next_hare: s+2,
next_hare: s+2, })
})
} else {
None
}
} else { } else {
None None
}; };
@@ -405,10 +398,7 @@ impl<'a> HeapPStrIter<'a> {
match self.brent_st.step(next_hare) { match self.brent_st.step(next_hare) {
Some(cycle_result) => { Some(cycle_result) => {
debug_assert!(match cycle_result { debug_assert!(matches!(cycle_result, CycleSearchResult::Cyclic(..)));
CycleSearchResult::Cyclic(..) => true,
_ => false,
});
self.walk_hare_to_cycle_end(); self.walk_hare_to_cycle_end();
self.stepper = HeapPStrIter::post_cycle_discovery_stepper; self.stepper = HeapPStrIter::post_cycle_discovery_stepper;
@@ -550,11 +540,7 @@ pub enum PStrCmpResult {
impl PStrCmpResult { impl PStrCmpResult {
#[inline] #[inline]
pub fn is_second_iter(&self) -> bool { pub fn is_second_iter(&self) -> bool {
if let PStrCmpResult::SecondIterContinuable(_) = self { matches!(self, PStrCmpResult::SecondIterContinuable(_))
true
} else {
false
}
} }
} }
@@ -600,8 +586,8 @@ pub fn compare_pstr_prefixes<'a>(
return PStrCmpResult::Ordered(c1.cmp(&c2)); return PStrCmpResult::Ordered(c1.cmp(&c2));
} }
cycle_detection_step(i1, i2, &step_1); cycle_detection_step(i1, i2, step_1);
let both_cyclic = cycle_detection_step(i2, i1, &step_2); let both_cyclic = cycle_detection_step(i2, i1, step_2);
r1 = step(i1, i1.brent_st.hare); r1 = step(i1, i1.brent_st.hare);
r2 = step(i2, i2.brent_st.hare); r2 = step(i2, i2.brent_st.hare);
@@ -623,15 +609,15 @@ pub fn compare_pstr_prefixes<'a>(
if n1 < pstr_atom.len() { if n1 < pstr_atom.len() {
step_2.iteratee = PStrIteratee::PStrSegment(f2, pstr_atom, n1); step_2.iteratee = PStrIteratee::PStrSegment(f2, pstr_atom, n1);
let c1_result = cycle_detection_step(i1, i2, &step_1); let c1_result = cycle_detection_step(i1, i2, step_1);
r1 = step(i1, i1.brent_st.hare); r1 = step(i1, i1.brent_st.hare);
if !c1_result { if !c1_result {
continue; continue;
} }
} else { } else {
cycle_detection_step(i1, i2, &step_1); cycle_detection_step(i1, i2, step_1);
let both_cyclic = cycle_detection_step(i2, i1, &step_2); let both_cyclic = cycle_detection_step(i2, i1, step_2);
r1 = step(i1, i1.brent_st.hare); r1 = step(i1, i1.brent_st.hare);
r2 = step(i2, i2.brent_st.hare); r2 = step(i2, i2.brent_st.hare);
@@ -641,7 +627,7 @@ pub fn compare_pstr_prefixes<'a>(
} }
} }
} else { } else {
let c2_result = cycle_detection_step(i2, i1, &step_2); let c2_result = cycle_detection_step(i2, i1, step_2);
r2 = step(i2, i2.brent_st.hare); r2 = step(i2, i2.brent_st.hare);
if !c2_result { if !c2_result {
@@ -662,15 +648,15 @@ pub fn compare_pstr_prefixes<'a>(
if n1 < pstr_atom.len() { if n1 < pstr_atom.len() {
step_1.iteratee = PStrIteratee::PStrSegment(f1, pstr_atom, n1); step_1.iteratee = PStrIteratee::PStrSegment(f1, pstr_atom, n1);
let c2_result = cycle_detection_step(i2, i1, &step_2); let c2_result = cycle_detection_step(i2, i1, step_2);
r2 = step(i2, step_2.next_hare); r2 = step(i2, step_2.next_hare);
if !c2_result { if !c2_result {
continue; continue;
} }
} else { } else {
cycle_detection_step(i1, i2, &step_1); cycle_detection_step(i1, i2, step_1);
let both_cyclic = cycle_detection_step(i2, i1, &step_2); let both_cyclic = cycle_detection_step(i2, i1, step_2);
r1 = step(i1, i1.brent_st.hare); r1 = step(i1, i1.brent_st.hare);
r2 = step(i2, i2.brent_st.hare); r2 = step(i2, i2.brent_st.hare);
@@ -680,7 +666,7 @@ pub fn compare_pstr_prefixes<'a>(
} }
} }
} else { } else {
let c1_result = cycle_detection_step(i1, i2, &step_1); let c1_result = cycle_detection_step(i1, i2, step_1);
r1 = step(i1, i1.brent_st.hare); r1 = step(i1, i1.brent_st.hare);
if !c1_result { if !c1_result {
@@ -693,8 +679,8 @@ pub fn compare_pstr_prefixes<'a>(
PStrIteratee::PStrSegment(f2, pstr2_atom, n2), PStrIteratee::PStrSegment(f2, pstr2_atom, n2),
) => { ) => {
if pstr1_atom == pstr2_atom && n1 == n2 { if pstr1_atom == pstr2_atom && n1 == n2 {
cycle_detection_step(i1, i2, &step_1); cycle_detection_step(i1, i2, step_1);
let both_cyclic = cycle_detection_step(i2, i1, &step_2); let both_cyclic = cycle_detection_step(i2, i1, step_2);
r1 = step(i1, i1.brent_st.hare); r1 = step(i1, i1.brent_st.hare);
r2 = step(i2, i2.brent_st.hare); r2 = step(i2, i2.brent_st.hare);
@@ -713,9 +699,9 @@ pub fn compare_pstr_prefixes<'a>(
let str2 = pstr2.as_str_from(n2); let str2 = pstr2.as_str_from(n2);
match str1.len().cmp(&str2.len()) { match str1.len().cmp(&str2.len()) {
Ordering::Equal if &*str1 == &*str2 => { Ordering::Equal if *str1 == *str2 => {
cycle_detection_step(i1, i2, &step_1); cycle_detection_step(i1, i2, step_1);
let both_cyclic = cycle_detection_step(i2, i1, &step_2); let both_cyclic = cycle_detection_step(i2, i1, step_2);
r1 = step(i1, i1.brent_st.hare); r1 = step(i1, i1.brent_st.hare);
r2 = step(i2, i2.brent_st.hare); r2 = step(i2, i2.brent_st.hare);
@@ -727,7 +713,7 @@ pub fn compare_pstr_prefixes<'a>(
Ordering::Less if str2.starts_with(&*str1) => { Ordering::Less if str2.starts_with(&*str1) => {
step_2.iteratee = step_2.iteratee =
PStrIteratee::PStrSegment(f2, pstr2_atom, n2 + str1.len()); PStrIteratee::PStrSegment(f2, pstr2_atom, n2 + str1.len());
let c1_result = cycle_detection_step(i1, i2, &step_1); let c1_result = cycle_detection_step(i1, i2, step_1);
r1 = step(i1, i1.brent_st.hare); r1 = step(i1, i1.brent_st.hare);
if !c1_result { if !c1_result {
@@ -737,7 +723,7 @@ pub fn compare_pstr_prefixes<'a>(
Ordering::Greater if str1.starts_with(&*str2) => { Ordering::Greater if str1.starts_with(&*str2) => {
step_1.iteratee = step_1.iteratee =
PStrIteratee::PStrSegment(f1, pstr1_atom, n1 + str2.len()); PStrIteratee::PStrSegment(f1, pstr1_atom, n1 + str2.len());
let c2_result = cycle_detection_step(i2, i1, &step_2); let c2_result = cycle_detection_step(i2, i1, step_2);
r2 = step(i2, i2.brent_st.hare); r2 = step(i2, i2.brent_st.hare);
if !c2_result { if !c2_result {
@@ -778,20 +764,34 @@ pub fn compare_pstr_prefixes<'a>(
if i1.focus == empty_list_as_cell!() { if i1.focus == empty_list_as_cell!() {
PStrCmpResult::Ordered(Ordering::Less) PStrCmpResult::Ordered(Ordering::Less)
} else { } else {
PStrCmpResult::SecondIterContinuable(r2.unwrap().iteratee) let r2_step = r2.unwrap();
// advance i2 to the next character so the same character
// isn't repeated
if matches!(r2_step.iteratee, PStrIteratee::Char(..)) {
cycle_detection_step(i2, i1, &r2_step);
}
PStrCmpResult::SecondIterContinuable(r2_step.iteratee)
} }
} else if r2_at_end { } else if r2_at_end {
if i2.focus == empty_list_as_cell!() { if i2.focus == empty_list_as_cell!() {
PStrCmpResult::Ordered(Ordering::Greater) PStrCmpResult::Ordered(Ordering::Greater)
} else { } else {
PStrCmpResult::FirstIterContinuable(r1.unwrap().iteratee) let r1_step = r1.unwrap();
// advance i1 to the next character so the same character
// isn't repeated
if matches!(r1_step.iteratee, PStrIteratee::Char(..)) {
cycle_detection_step(i1, i2, &r1_step);
}
PStrCmpResult::FirstIterContinuable(r1_step.iteratee)
} }
} else if i1.is_continuable() && i2.is_continuable() {
PStrCmpResult::Ordered(Ordering::Equal)
} else { } else {
if i1.is_continuable() && i2.is_continuable() { PStrCmpResult::Unordered
PStrCmpResult::Ordered(Ordering::Equal)
} else {
PStrCmpResult::Unordered
}
} }
} }
@@ -801,6 +801,7 @@ mod test {
use crate::machine::mock_wam::*; use crate::machine::mock_wam::*;
#[test] #[test]
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
fn pstr_iter_tests() { fn pstr_iter_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
@@ -885,7 +886,7 @@ mod test {
{ {
let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);
while let Some(_) = iter.next() {} for _ in iter.by_ref() {}
assert!(!iter.at_string_terminator()); assert!(!iter.at_string_terminator());
} }
@@ -1009,7 +1010,7 @@ mod test {
unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1)); unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
assert_eq!(wam.machine_st.fail, false); assert!(!wam.machine_st.fail);
assert_eq!(wam.machine_st.heap[2], char_as_cell!('a'),); assert_eq!(wam.machine_st.heap[2], char_as_cell!('a'),);
@@ -1032,7 +1033,7 @@ mod test {
unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1)); unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
assert_eq!(wam.machine_st.fail, false); assert!(!wam.machine_st.fail);
// test "abc" = [X,b,Z]. // test "abc" = [X,b,Z].
@@ -1054,7 +1055,7 @@ mod test {
unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1)); unify!(wam.machine_st, cstr_var_cell, heap_loc_as_cell!(1));
assert_eq!(wam.machine_st.fail, false); assert!(!wam.machine_st.fail);
assert_eq!(wam.machine_st.heap[2], char_as_cell!('a'),); assert_eq!(wam.machine_st.heap[2], char_as_cell!('a'),);
@@ -1075,7 +1076,7 @@ mod test {
print_heap_terms(wam.machine_st.heap.iter(), 0); print_heap_terms(wam.machine_st.heap.iter(), 0);
assert_eq!(wam.machine_st.fail, false); assert!(!wam.machine_st.fail);
assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(5)); assert_eq!(wam.machine_st.heap[2], pstr_loc_as_cell!(5));
assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(1)); assert_eq!(wam.machine_st.heap[3], pstr_loc_as_cell!(1));
@@ -1105,9 +1106,119 @@ mod test {
Some(PStrIteratee::PStrSegment(2, atom!("abc"), 1)) Some(PStrIteratee::PStrSegment(2, atom!("abc"), 1))
); );
// assert!(iter.next().is_none()); for _ in iter {}
while let Some(_) = iter.next() {}
} }
// #2293, test1.
wam.machine_st.heap.clear();
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("a ")));
wam.machine_st.heap.push(heap_loc_as_cell!(1));
wam.machine_st.heap.push(list_loc_as_cell!(3));
wam.machine_st.heap.push(char_as_cell!(' '));
wam.machine_st.heap.push(empty_list_as_cell!());
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
assert!(!wam.machine_st.fail);
// #2293, test2.
wam.machine_st.heap.clear();
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a")));
wam.machine_st.heap.push(char_as_cell!(' '));
wam.machine_st.heap.push(list_loc_as_cell!(3));
wam.machine_st.heap.push(heap_loc_as_cell!(3));
wam.machine_st.heap.push(empty_list_as_cell!());
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
assert!(!wam.machine_st.fail);
// #2293, test3.
wam.machine_st.heap.clear();
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("a b")));
wam.machine_st.heap.push(heap_loc_as_cell!(1));
wam.machine_st.heap.push(list_loc_as_cell!(3));
wam.machine_st.heap.push(char_as_cell!(' '));
wam.machine_st.heap.push(list_loc_as_cell!(5));
wam.machine_st.heap.push(heap_loc_as_cell!(5));
wam.machine_st.heap.push(empty_list_as_cell!());
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
assert!(!wam.machine_st.fail);
// #2293, test4.
wam.machine_st.heap.clear();
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a ")));
wam.machine_st.heap.push(char_as_cell!(' '));
wam.machine_st.heap.push(list_loc_as_cell!(3));
wam.machine_st.heap.push(heap_loc_as_cell!(3));
wam.machine_st.heap.push(list_loc_as_cell!(5));
wam.machine_st.heap.push(char_as_cell!(' '));
wam.machine_st.heap.push(empty_list_as_cell!());
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
assert!(!wam.machine_st.fail);
// #2293, test5.
wam.machine_st.heap.clear();
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!(" a bc")));
wam.machine_st.heap.push(char_as_cell!(' '));
wam.machine_st.heap.push(list_loc_as_cell!(3));
wam.machine_st.heap.push(heap_loc_as_cell!(3));
wam.machine_st.heap.push(list_loc_as_cell!(5));
wam.machine_st.heap.push(char_as_cell!(' '));
wam.machine_st.heap.push(heap_loc_as_cell!(6));
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
assert!(!wam.machine_st.fail);
// #2293, test6.
wam.machine_st.heap.clear();
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("abc")));
wam.machine_st.heap.push(heap_loc_as_cell!(1));
wam.machine_st.heap.push(list_loc_as_cell!(3));
wam.machine_st.heap.push(char_as_cell!('b'));
wam.machine_st.heap.push(list_loc_as_cell!(5));
wam.machine_st.heap.push(heap_loc_as_cell!(5));
wam.machine_st.heap.push(empty_list_as_cell!());
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
assert!(!wam.machine_st.fail);
// #2293, test7.
wam.machine_st.heap.clear();
wam.machine_st.heap.push(atom_as_cstr_cell!(atom!("abcde")));
wam.machine_st.heap.push(char_as_cell!('a'));
wam.machine_st.heap.push(list_loc_as_cell!(3));
wam.machine_st.heap.push(heap_loc_as_cell!(3));
wam.machine_st.heap.push(list_loc_as_cell!(5));
wam.machine_st.heap.push(char_as_cell!('c'));
wam.machine_st.heap.push(list_loc_as_cell!(7));
wam.machine_st.heap.push(heap_loc_as_cell!(7));
wam.machine_st.heap.push(list_loc_as_cell!(9));
wam.machine_st.heap.push(char_as_cell!('e'));
wam.machine_st.heap.push(empty_list_as_cell!());
unify!(wam.machine_st, list_loc_as_cell!(1), heap_loc_as_cell!(0));
assert!(!wam.machine_st.fail);
} }
} }

View File

@@ -100,7 +100,7 @@ fn setup_module_export(
} }
pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term { pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term {
let head_term = Term::Clause(Cell::default(), atom!(""), vars.iter().cloned().collect()); let head_term = Term::Clause(Cell::default(), atom!(""), vars.to_vec());
let rule = vec![head_term, body_term]; let rule = vec![head_term, body_term];
Term::Clause(Cell::default(), atom!(":-"), rule) Term::Clause(Cell::default(), atom!(":-"), rule)
@@ -238,7 +238,7 @@ fn setup_meta_predicate<'a, LS: LoadState<'a>>(
) -> Result<(Atom, Vec<MetaSpec>), CompilationError> { ) -> Result<(Atom, Vec<MetaSpec>), CompilationError> {
let mut meta_specs = vec![]; let mut meta_specs = vec![];
for meta_spec in terms.into_iter() { for meta_spec in terms.iter_mut() {
match meta_spec { match meta_spec {
Term::Literal(_, Literal::Atom(meta_spec)) => { Term::Literal(_, Literal::Atom(meta_spec)) => {
let meta_spec = match meta_spec { let meta_spec = match meta_spec {
@@ -310,11 +310,11 @@ pub(super) fn setup_declaration<'a, LS: LoadState<'a>>(
} }
(atom!("module"), 2) => { (atom!("module"), 2) => {
let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl; let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
Ok(Declaration::Module(setup_module_decl(terms, &atom_tbl)?)) Ok(Declaration::Module(setup_module_decl(terms, atom_tbl)?))
} }
(atom!("op"), 3) => { (atom!("op"), 3) => {
let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl; let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
Ok(Declaration::Op(setup_op_decl(terms, &atom_tbl)?)) Ok(Declaration::Op(setup_op_decl(terms, atom_tbl)?))
} }
(atom!("non_counted_backtracking"), 1) => { (atom!("non_counted_backtracking"), 1) => {
let (name, arity) = setup_predicate_indicator(&mut terms.pop().unwrap())?; let (name, arity) = setup_predicate_indicator(&mut terms.pop().unwrap())?;
@@ -323,7 +323,7 @@ pub(super) fn setup_declaration<'a, LS: LoadState<'a>>(
(atom!("use_module"), 1) => Ok(Declaration::UseModule(setup_use_module_decl(terms)?)), (atom!("use_module"), 1) => Ok(Declaration::UseModule(setup_use_module_decl(terms)?)),
(atom!("use_module"), 2) => { (atom!("use_module"), 2) => {
let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl; let atom_tbl = &mut LS::machine_st(&mut loader.payload).atom_tbl;
let (name, exports) = setup_qualified_import(terms, &atom_tbl)?; let (name, exports) = setup_qualified_import(terms, atom_tbl)?;
Ok(Declaration::UseQualifiedModule(name, exports)) Ok(Declaration::UseQualifiedModule(name, exports))
} }
@@ -565,21 +565,6 @@ impl Preprocessor {
} }
} }
/*
fn try_term_to_query<'a, LS: LoadState<'a>>(
&mut self,
loader: &mut Loader<'a, LS>,
terms: Vec<Term>,
cut_context: CutContext,
) -> Result<TopLevel, CompilationError> {
Ok(TopLevel::Query(self.setup_query(
loader,
terms,
cut_context,
)?))
}
*/
pub(super) fn try_term_to_tl<'a, LS: LoadState<'a>>( pub(super) fn try_term_to_tl<'a, LS: LoadState<'a>>(
&mut self, &mut self,
loader: &mut Loader<'a, LS>, loader: &mut Loader<'a, LS>,
@@ -607,20 +592,4 @@ impl Preprocessor {
} }
} }
} }
/*
fn try_terms_to_tls<'a, I: IntoIterator<Item = Term>, LS: LoadState<'a>>(
&mut self,
loader: &mut Loader<'a, LS>,
terms: I,
) -> Result<VecDeque<TopLevel>, CompilationError> {
let mut results = VecDeque::new();
for term in terms.into_iter() {
results.push_back(self.try_term_to_tl(loader, term)?);
}
Ok(results)
}
*/
} }

View File

@@ -1,4 +1,4 @@
:- module('$project_atts', [copy_term/3]). :- module('$project_atts', []).
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
:- use_module(library(error), [can_be/2]). :- use_module(library(error), [can_be/2]).
@@ -100,14 +100,6 @@ gather_residual_goals([V|Vs]) -->
delete_all_attributes_from_var(V) :- '$delete_all_attributes_from_var'(V). delete_all_attributes_from_var(V) :- '$delete_all_attributes_from_var'(V).
copy_term(Term, Copy, Gs) :-
can_be(list, Gs),
findall(Term-Rs, term_residual_goals(Term,Rs), [Copy-Gs]),
( var(Gs) ->
Gs = []
; true
).
term_residual_goals(Term,Rs) :- term_residual_goals(Term,Rs) :-
'$term_attributed_variables'(Term, Vs), '$term_attributed_variables'(Term, Vs),
phrase(gather_residual_goals(Vs), Rs), phrase(gather_residual_goals(Vs), Rs),

View File

@@ -56,7 +56,7 @@ impl Index<usize> for AndFrame {
let index_offset = (index - 1) * mem::size_of::<HeapCellValue>(); let index_offset = (index - 1) * mem::size_of::<HeapCellValue>();
unsafe { unsafe {
let ptr = mem::transmute::<&AndFrame, *const u8>(self); let ptr = self as *const crate::machine::stack::AndFrame as *const u8;
let ptr = ptr as usize + prelude_offset + index_offset; let ptr = ptr as usize + prelude_offset + index_offset;
&*(ptr as *const HeapCellValue) &*(ptr as *const HeapCellValue)
@@ -70,7 +70,7 @@ impl IndexMut<usize> for AndFrame {
let index_offset = (index - 1) * mem::size_of::<HeapCellValue>(); let index_offset = (index - 1) * mem::size_of::<HeapCellValue>();
unsafe { unsafe {
let ptr = mem::transmute::<&mut AndFrame, *const u8>(self); let ptr = self as *mut crate::machine::stack::AndFrame as *const u8;
let ptr = ptr as usize + prelude_offset + index_offset; let ptr = ptr as usize + prelude_offset + index_offset;
&mut *(ptr as *mut HeapCellValue) &mut *(ptr as *mut HeapCellValue)
@@ -129,7 +129,7 @@ impl Index<usize> for OrFrame {
let index_offset = index * mem::size_of::<HeapCellValue>(); let index_offset = index * mem::size_of::<HeapCellValue>();
unsafe { unsafe {
let ptr = mem::transmute::<&OrFrame, *const u8>(self); let ptr = self as *const crate::machine::stack::OrFrame as *const u8;
let ptr = ptr as usize + prelude_offset + index_offset; let ptr = ptr as usize + prelude_offset + index_offset;
&*(ptr as *const HeapCellValue) &*(ptr as *const HeapCellValue)
@@ -144,7 +144,7 @@ impl IndexMut<usize> for OrFrame {
let index_offset = index * mem::size_of::<HeapCellValue>(); let index_offset = index * mem::size_of::<HeapCellValue>();
unsafe { unsafe {
let ptr = mem::transmute::<&mut OrFrame, *const u8>(self); let ptr = self as *mut crate::machine::stack::OrFrame as *const u8;
let ptr = ptr as usize + prelude_offset + index_offset; let ptr = ptr as usize + prelude_offset + index_offset;
&mut *(ptr as *mut HeapCellValue) &mut *(ptr as *mut HeapCellValue)
@@ -189,7 +189,7 @@ impl Stack {
for idx in 0..num_cells { for idx in 0..num_cells {
ptr::write( ptr::write(
(new_ptr as usize + offset) as *mut HeapCellValue, new_ptr.add(offset) as *mut HeapCellValue,
stack_loc_as_cell!(AndFrame, e, idx + 1), stack_loc_as_cell!(AndFrame, e, idx + 1),
); );
@@ -203,6 +203,10 @@ impl Stack {
} }
} }
pub(crate) fn top(&self) -> usize {
unsafe { (*self.buf.ptr.get()) as usize - self.buf.base as usize }
}
pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> usize { pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> usize {
let frame_size = OrFrame::size_of(num_cells); let frame_size = OrFrame::size_of(num_cells);
@@ -238,7 +242,8 @@ impl Stack {
#[inline(always)] #[inline(always)]
pub(crate) fn index_and_frame_mut(&mut self, e: usize) -> &mut AndFrame { pub(crate) fn index_and_frame_mut(&mut self, e: usize) -> &mut AndFrame {
unsafe { unsafe {
let ptr = self.buf.base as usize + e; // This is doing alignment wrong
let ptr = self.buf.base.add(e);
&mut *(ptr as *mut AndFrame) &mut *(ptr as *mut AndFrame)
} }
} }
@@ -276,6 +281,7 @@ mod tests {
use crate::machine::mock_wam::*; use crate::machine::mock_wam::*;
#[test] #[test]
#[cfg_attr(miri, ignore)]
fn stack_tests() { fn stack_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();

View File

@@ -12,7 +12,7 @@ use crate::machine::machine_indices::*;
use crate::machine::machine_state::*; use crate::machine::machine_state::*;
use crate::types::*; use crate::types::*;
pub use modular_bitfield::prelude::*; pub use scryer_modular_bitfield::prelude::*;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error; use std::error::Error;
@@ -21,12 +21,13 @@ use std::fmt::Debug;
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::hash::Hash; use std::hash::Hash;
use std::io; use std::io;
use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
#[cfg(feature = "http")] #[cfg(feature = "http")]
use std::io::BufRead; use std::io::BufRead;
use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
use std::mem; use std::mem;
use std::net::{Shutdown, TcpStream}; use std::net::{Shutdown, TcpStream};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::ptr; use std::ptr;
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
@@ -161,7 +162,7 @@ impl StreamLayout<CharReader<InputFileStream>> {
// its pending buffer length from position. // its pending buffer length from position.
self.get_mut() self.get_mut()
.file .file
.seek(SeekFrom::Current(0)) .stream_position()
.map(|pos| pos - self.stream.rem_buf_len() as u64) .map(|pos| pos - self.stream.rem_buf_len() as u64)
.ok() .ok()
} }
@@ -317,33 +318,31 @@ impl Write for HttpWriteStream {
#[inline] #[inline]
fn flush(&mut self) -> std::io::Result<()> { fn flush(&mut self) -> std::io::Result<()> {
Ok(()) Ok(())
} }
} }
#[cfg(feature = "http")] #[cfg(feature = "http")]
impl HttpWriteStream { impl HttpWriteStream {
fn drop(&mut self) { fn drop(&mut self) {
let headers = unsafe { mem::ManuallyDrop::take(&mut self.headers) }; let headers = unsafe { mem::ManuallyDrop::take(&mut self.headers) };
let buffer = unsafe { mem::ManuallyDrop::take(&mut self.buffer) }; let buffer = unsafe { mem::ManuallyDrop::take(&mut self.buffer) };
let (ready, response, cvar) = &**self.response;
let mut ready = ready.lock().unwrap(); let (ready, response, cvar) = &**self.response;
{
let mut response = response.lock().unwrap(); let mut ready = ready.lock().unwrap();
{
let mut response_ = warp::http::Response::builder() let mut response = response.lock().unwrap();
.status(self.status_code);
*response_.headers_mut().unwrap() = headers; let mut response_ = warp::http::Response::builder().status(self.status_code);
*response = Some(response_.body(warp::hyper::Body::from(buffer)).unwrap()); *response_.headers_mut().unwrap() = headers;
} *response = Some(response_.body(warp::hyper::Body::from(buffer)).unwrap());
*ready = true; }
cvar.notify_one(); *ready = true;
cvar.notify_one();
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct StandardOutputStream {} pub struct StandardOutputStream {}
@@ -389,7 +388,7 @@ impl StreamOptions {
#[inline] #[inline]
pub fn get_alias(self) -> Option<Atom> { pub fn get_alias(self) -> Option<Atom> {
if self.has_alias() { if self.has_alias() {
Some(Atom::from((self.alias() as u64) << 3)) Some(Atom::from(self.alias() << 3))
} else { } else {
None None
} }
@@ -466,9 +465,11 @@ macro_rules! arena_allocated_impl_for_stream {
mem::size_of::<StreamLayout<$stream_type>>() mem::size_of::<StreamLayout<$stream_type>>()
} }
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline] #[inline]
fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated { fn copy_to_arena(self, dst: *mut Self) -> Self::PtrToAllocated {
unsafe { unsafe {
// Miri seems to hit this a lot
ptr::write(dst, self); ptr::write(dst, self);
TypedArenaPtr::new(dst as *mut Self) TypedArenaPtr::new(dst as *mut Self)
} }
@@ -585,29 +586,17 @@ impl Stream {
#[inline] #[inline]
pub fn is_stderr(&self) -> bool { pub fn is_stderr(&self) -> bool {
if let Stream::StandardError(_) = self { matches!(self, Stream::StandardError(_))
true
} else {
false
}
} }
#[inline] #[inline]
pub fn is_stdout(&self) -> bool { pub fn is_stdout(&self) -> bool {
if let Stream::StandardOutput(_) = self { matches!(self, Stream::StandardOutput(_))
true
} else {
false
}
} }
#[inline] #[inline]
pub fn is_stdin(&self) -> bool { pub fn is_stdin(&self) -> bool {
if let Stream::Readline(_) = self { matches!(self, Stream::Readline(_))
true
} else {
false
}
} }
pub fn as_ptr(&self) -> *const ArenaHeader { pub fn as_ptr(&self) -> *const ArenaHeader {
@@ -831,7 +820,7 @@ impl CharRead for Stream {
impl Read for Stream { impl Read for Stream {
#[inline] #[inline]
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let bytes_read = match self { match self {
Stream::InputFile(file) => (*file).read(buf), Stream::InputFile(file) => (*file).read(buf),
Stream::NamedTcp(tcp_stream) => (*tcp_stream).read(buf), Stream::NamedTcp(tcp_stream) => (*tcp_stream).read(buf),
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
@@ -853,9 +842,7 @@ impl Read for Stream {
ErrorKind::PermissionDenied, ErrorKind::PermissionDenied,
StreamError::ReadFromOutputStream, StreamError::ReadFromOutputStream,
)), )),
}; }
bytes_read
} }
} }
@@ -984,18 +971,14 @@ fn cursor_position<T>(
cursor: &Cursor<T>, cursor: &Cursor<T>,
cursor_len: u64, cursor_len: u64,
) -> AtEndOfStream { ) -> AtEndOfStream {
let position = cursor.position(); match cursor.position().cmp(&cursor_len) {
let at_end_of_stream = match position.cmp(&cursor_len) {
Ordering::Equal => AtEndOfStream::At, Ordering::Equal => AtEndOfStream::At,
Ordering::Greater => { Ordering::Greater => {
*past_end_of_stream = true; *past_end_of_stream = true;
AtEndOfStream::Past AtEndOfStream::Past
} }
Ordering::Less => AtEndOfStream::Not, Ordering::Less => AtEndOfStream::Not,
}; }
at_end_of_stream
} }
impl Stream { impl Stream {
@@ -1021,26 +1004,23 @@ impl Stream {
#[inline] #[inline]
pub(crate) fn set_position(&mut self, position: u64) { pub(crate) fn set_position(&mut self, position: u64) {
match self { if let Stream::InputFile(stream_layout) = self {
Stream::InputFile(stream_layout) => { let StreamLayout {
let StreamLayout { past_end_of_stream,
past_end_of_stream, stream,
stream, ..
.. } = &mut **stream_layout;
} = &mut **stream_layout;
stream stream
.get_mut() .get_mut()
.file .file
.seek(SeekFrom::Start(position)) .seek(SeekFrom::Start(position))
.unwrap(); .unwrap();
stream.reset_buffer(); // flush the internal buffer. stream.reset_buffer(); // flush the internal buffer.
if let Ok(metadata) = stream.get_ref().file.metadata() { if let Ok(metadata) = stream.get_ref().file.metadata() {
*past_end_of_stream = position > metadata.len(); *past_end_of_stream = position > metadata.len();
}
} }
_ => {}
} }
} }
@@ -1257,15 +1237,15 @@ impl Stream {
headers: hyper::HeaderMap, headers: hyper::HeaderMap,
arena: &mut Arena, arena: &mut Arena,
) -> Self { ) -> Self {
Stream::HttpWrite(arena_alloc!( Stream::HttpWrite(arena_alloc!(
StreamLayout::new(CharReader::new(HttpWriteStream { StreamLayout::new(CharReader::new(HttpWriteStream {
response, response,
status_code, status_code,
headers: mem::ManuallyDrop::new(headers), headers: mem::ManuallyDrop::new(headers),
buffer: mem::ManuallyDrop::new(Vec::new()), buffer: mem::ManuallyDrop::new(Vec::new()),
})), })),
arena arena
)) ))
} }
#[inline] #[inline]
@@ -1313,8 +1293,8 @@ impl Stream {
Ok(()) Ok(())
} }
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpWrite(ref mut http_stream) => { Stream::HttpWrite(ref mut http_stream) => {
http_stream.inner_mut().drop(); http_stream.inner_mut().drop();
unsafe { unsafe {
http_stream.set_tag(ArenaHeaderTag::Dropped); http_stream.set_tag(ArenaHeaderTag::Dropped);
std::ptr::drop_in_place(&mut http_stream.inner_mut().buffer as *mut _); std::ptr::drop_in_place(&mut http_stream.inner_mut().buffer as *mut _);
@@ -1346,11 +1326,7 @@ impl Stream {
#[inline] #[inline]
pub(crate) fn is_null_stream(&self) -> bool { pub(crate) fn is_null_stream(&self) -> bool {
if let Stream::Null(_) = self { matches!(self, Stream::Null(_))
true
} else {
false
}
} }
#[inline] #[inline]
@@ -1391,29 +1367,25 @@ impl Stream {
self.set_lines_read(0); self.set_lines_read(0);
self.set_past_end_of_stream(false); self.set_past_end_of_stream(false);
loop { match self {
match self { Stream::Byte(ref mut cursor) => {
Stream::Byte(ref mut cursor) => { cursor.stream.get_mut().0.set_position(0);
cursor.stream.get_mut().0.set_position(0); true
return true;
}
Stream::InputFile(ref mut file_stream) => {
file_stream
.stream
.get_mut()
.file
.seek(SeekFrom::Start(0))
.unwrap();
return true;
}
Stream::Readline(ref mut readline_stream) => {
readline_stream.reset();
return true;
}
_ => {
return false;
}
} }
Stream::InputFile(ref mut file_stream) => {
file_stream
.stream
.get_mut()
.file
.seek(SeekFrom::Start(0))
.unwrap();
true
}
Stream::Readline(ref mut readline_stream) => {
readline_stream.reset();
true
}
_ => false,
} }
} }
@@ -1484,12 +1456,13 @@ impl MachineState {
stream.set_past_end_of_stream(true); stream.set_past_end_of_stream(true);
} }
Ok(self.fail = stream.past_end_of_stream()) self.fail = stream.past_end_of_stream();
Ok(())
} }
} }
} }
pub(crate) fn to_stream_options( pub(crate) fn get_stream_options(
&mut self, &mut self,
alias: HeapCellValue, alias: HeapCellValue,
eof_action: HeapCellValue, eof_action: HeapCellValue,
@@ -1782,9 +1755,9 @@ impl MachineState {
caller: Atom, caller: Atom,
arity: usize, arity: usize,
) -> CallResult { ) -> CallResult {
let opt_err = if input.is_some() && !stream.is_input_stream() { let opt_err = if input.is_some() && !stream.is_input_stream()
Some(atom!("stream")) // 8.14.2.3 g) || input.is_none() && !stream.is_output_stream()
} else if input.is_none() && !stream.is_output_stream() { {
Some(atom!("stream")) // 8.14.2.3 g) Some(atom!("stream")) // 8.14.2.3 g)
} else if stream.options().stream_type() != expected_type { } else if stream.options().stream_type() != expected_type {
Some(expected_type.other().as_atom()) // 8.14.2.3 h) Some(expected_type.other().as_atom()) // 8.14.2.3 h)
@@ -1866,42 +1839,55 @@ impl MachineState {
} }
}; };
let file = match open_options.open(&*file_spec.as_str()) { let mut path = PathBuf::from(&*file_spec.as_str());
Ok(file) => file,
Err(err) => {
match err.kind() {
ErrorKind::NotFound => {
// 8.11.5.3j)
let stub = functor_stub(atom!("open"), 4);
let err = loop {
self.existence_error(ExistenceError::SourceSink(self[temp_v!(1)])); let file = match open_options.open(&path) {
Ok(file) => file,
Err(err) => {
match err.kind() {
ErrorKind::NotFound => {
// 8.11.5.3j)
let stub = functor_stub(atom!("open"), 4);
return Err(self.error_form(err, stub)); let err =
self.existence_error(ExistenceError::SourceSink(self[temp_v!(1)]));
return Err(self.error_form(err, stub));
}
ErrorKind::PermissionDenied => {
// 8.11.5.3k)
return Err(self.open_permission_error(
self.registers[1],
atom!("open"),
4,
));
}
_ => {
// assume the OS is out of file descriptors.
let stub = functor_stub(atom!("open"), 4);
let err = self.resource_error(ResourceError::OutOfFiles);
return Err(self.error_form(err, stub));
}
} }
ErrorKind::PermissionDenied => { }
// 8.11.5.3k) };
return Err(self.open_permission_error(
self.registers[1],
atom!("open"),
4,
));
}
_ => {
// assume the OS is out of file descriptors.
let stub = functor_stub(atom!("open"), 4);
let err = self.resource_error(ResourceError::OutOfFiles);
return Err(self.error_form(err, stub)); if path.extension().is_none() {
if let Ok(metadata) = file.metadata() {
if metadata.is_dir() {
path.set_extension("pl");
continue;
} }
} }
} }
};
Ok(if is_input_file { return Ok(if is_input_file {
Stream::from_file_as_input(file_spec, file, &mut self.arena) Stream::from_file_as_input(file_spec, file, &mut self.arena)
} else { } else {
Stream::from_file_as_output(file_spec, file, in_append_mode, &mut self.arena) Stream::from_file_as_output(file_spec, file, in_append_mode, &mut self.arena)
}) });
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
use crate::arena::*; use crate::arena::*;
use crate::forms::*; use crate::forms::*;
use crate::heap_iter::{NonListElider, stackful_preorder_iter}; use crate::heap_iter::{stackful_preorder_iter, NonListElider};
use crate::machine::machine_state::*; use crate::machine::machine_state::*;
use crate::machine::partial_string::*; use crate::machine::partial_string::*;
use crate::machine::*; use crate::machine::*;
@@ -173,6 +173,98 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
let mut pstr_iter1 = HeapPStrIter::new(&machine_st.heap, s1); let mut pstr_iter1 = HeapPStrIter::new(&machine_st.heap, s1);
let mut pstr_iter2 = HeapPStrIter::new(&machine_st.heap, s1 + 1); let mut pstr_iter2 = HeapPStrIter::new(&machine_st.heap, s1 + 1);
fn unify_sequence(
machine_st: &mut MachineState,
iter: PStrIteratee,
source_cell: HeapCellValue,
) -> bool {
match iter {
PStrIteratee::Char(focus, _) => {
machine_st.pdl.push(machine_st.heap[focus]);
machine_st.pdl.push(source_cell);
}
PStrIteratee::PStrSegment(focus, _, n) => {
read_heap_cell!(machine_st.heap[focus],
(HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => {
if focus < machine_st.heap.len() - 2 {
machine_st.heap.pop();
machine_st.heap.pop();
}
if n == 0 {
let target_cell = match machine_st.heap[focus].get_tag() {
HeapCellValueTag::CStr => {
atom_as_cstr_cell!(pstr_atom)
}
HeapCellValueTag::PStr => {
pstr_loc_as_cell!(focus)
}
_ => {
unreachable!()
}
};
machine_st.pdl.push(target_cell);
machine_st.pdl.push(source_cell);
} else {
let h_len = machine_st.heap.len();
machine_st.heap.push(pstr_offset_as_cell!(focus));
machine_st.heap.push(fixnum_as_cell!(
Fixnum::build_with(n as i64)
));
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
machine_st.pdl.push(source_cell);
}
return true;
}
(HeapCellValueTag::PStrOffset, pstr_loc) => {
let n0 = cell_as_fixnum!(machine_st.heap[focus+1])
.get_num() as usize;
if pstr_loc < machine_st.heap.len() - 2 {
machine_st.heap.pop();
machine_st.heap.pop();
}
if n == n0 {
machine_st.pdl.push(pstr_loc_as_cell!(focus));
machine_st.pdl.push(source_cell);
} else {
let h_len = machine_st.heap.len();
machine_st.heap.push(pstr_offset_as_cell!(pstr_loc));
machine_st.heap.push(fixnum_as_cell!(
Fixnum::build_with(n as i64)
));
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
machine_st.pdl.push(source_cell);
}
return true;
}
_ => {
}
);
if focus < machine_st.heap.len() - 2 {
machine_st.heap.pop();
machine_st.heap.pop();
}
machine_st.pdl.push(machine_st.heap[focus]);
machine_st.pdl.push(source_cell);
return true;
}
}
false
}
match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) { match compare_pstr_prefixes(&mut pstr_iter1, &mut pstr_iter2) {
PStrCmpResult::Ordered(Ordering::Equal) => {} PStrCmpResult::Ordered(Ordering::Equal) => {}
PStrCmpResult::Ordered(Ordering::Less) => { PStrCmpResult::Ordered(Ordering::Less) => {
@@ -204,7 +296,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
let mut focus = pstr_iter2.focus; let mut focus = pstr_iter2.focus;
'outer: loop { 'outer: {
while let Some(c) = chars_iter.peek() { while let Some(c) = chars_iter.peek() {
read_heap_cell!(focus, read_heap_cell!(focus,
(HeapCellValueTag::Lis, l) => { (HeapCellValueTag::Lis, l) => {
@@ -229,89 +321,13 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
break 'outer; break 'outer;
} }
} }
(HeapCellValueTag::CStr | HeapCellValueTag::PStrLoc) => {
unify_sequence(machine_st, chars_iter.item.unwrap(), focus);
return;
}
(HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => { (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
match chars_iter.item.unwrap() { if unify_sequence(machine_st, chars_iter.item.unwrap(), heap_loc_as_cell!(h)) {
PStrIteratee::Char(focus, _) => { return;
machine_st.pdl.push(machine_st.heap[focus]);
machine_st.pdl.push(heap_loc_as_cell!(h));
}
PStrIteratee::PStrSegment(focus, _, n) => {
read_heap_cell!(machine_st.heap[focus],
(HeapCellValueTag::CStr | HeapCellValueTag::PStr, pstr_atom) => {
if focus < machine_st.heap.len() - 2 {
machine_st.heap.pop();
machine_st.heap.pop();
}
if n == 0 {
let target_cell = match machine_st.heap[focus].get_tag() {
HeapCellValueTag::CStr => {
atom_as_cstr_cell!(pstr_atom)
}
HeapCellValueTag::PStr => {
pstr_loc_as_cell!(focus)
}
_ => {
unreachable!()
}
};
machine_st.pdl.push(target_cell);
machine_st.pdl.push(heap_loc_as_cell!(h));
} else {
let h_len = machine_st.heap.len();
machine_st.heap.push(pstr_offset_as_cell!(focus));
machine_st.heap.push(fixnum_as_cell!(
Fixnum::build_with(n as i64)
));
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
machine_st.pdl.push(heap_loc_as_cell!(h));
}
return;
}
(HeapCellValueTag::PStrOffset, pstr_loc) => {
let n0 = cell_as_fixnum!(machine_st.heap[focus+1])
.get_num() as usize;
if pstr_loc < machine_st.heap.len() - 2 {
machine_st.heap.pop();
machine_st.heap.pop();
}
if n == n0 {
machine_st.pdl.push(pstr_loc_as_cell!(focus));
machine_st.pdl.push(heap_loc_as_cell!(h));
} else {
let h_len = machine_st.heap.len();
machine_st.heap.push(pstr_offset_as_cell!(pstr_loc));
machine_st.heap.push(fixnum_as_cell!(
Fixnum::build_with(n as i64)
));
machine_st.pdl.push(pstr_loc_as_cell!(h_len));
machine_st.pdl.push(heap_loc_as_cell!(h));
}
return;
}
_ => {
}
);
if focus < machine_st.heap.len() - 2 {
machine_st.heap.pop();
machine_st.heap.pop();
}
machine_st.pdl.push(machine_st.heap[focus]);
machine_st.pdl.push(heap_loc_as_cell!(h));
return;
}
} }
break 'outer; break 'outer;
@@ -329,8 +345,6 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
machine_st.pdl.push(focus); machine_st.pdl.push(focus);
machine_st.pdl.push(chars_iter.iter.focus); machine_st.pdl.push(chars_iter.iter.focus);
break;
} }
} }
PStrCmpResult::Unordered => { PStrCmpResult::Unordered => {
@@ -609,10 +623,8 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
} }
} }
(HeapCellValueTag::Lis, l1) => { (HeapCellValueTag::Lis, l1) => {
if d2.is_ref() { if d2.is_ref() && tabu_list.contains(&(d1, d2)) {
if tabu_list.contains(&(d1, d2)) { continue;
continue;
}
} }
Self::unify_list(self, l1, d2); Self::unify_list(self, l1, d2);
@@ -720,7 +732,11 @@ fn bind_with_occurs_check<U: Unifier>(unifier: &mut U, r: Ref, value: HeapCellVa
if !value.is_constant() { if !value.is_constant() {
let machine_st: &mut MachineState = unifier.deref_mut(); let machine_st: &mut MachineState = unifier.deref_mut();
for cell in stackful_preorder_iter::<NonListElider>(&mut machine_st.heap, &mut machine_st.stack, value) { for cell in stackful_preorder_iter::<NonListElider>(
&mut machine_st.heap,
&mut machine_st.stack,
value,
) {
let cell = unmark_cell_bits!(cell); let cell = unmark_cell_bits!(cell);
if let Some(inner_r) = cell.as_var() { if let Some(inner_r) = cell.as_var() {
@@ -738,7 +754,7 @@ fn bind_with_occurs_check<U: Unifier>(unifier: &mut U, r: Ref, value: HeapCellVa
U::bind(unifier, r, value); U::bind(unifier, r, value);
} }
return occurs_triggered; occurs_triggered
} }
#[derive(Deref, DerefMut)] #[derive(Deref, DerefMut)]

View File

@@ -17,8 +17,8 @@ use crate::parser::dashu::{Integer, Rational};
use fxhash::FxBuildHasher; use fxhash::FxBuildHasher;
use indexmap::IndexMap; use indexmap::IndexMap;
use modular_bitfield::error::OutOfBounds; use scryer_modular_bitfield::error::OutOfBounds;
use modular_bitfield::prelude::*; use scryer_modular_bitfield::prelude::*;
pub type Specifier = u32; pub type Specifier = u32;
@@ -247,11 +247,7 @@ impl GenContext {
#[inline] #[inline]
pub fn is_last(self) -> bool { pub fn is_last(self) -> bool {
if let GenContext::Last(_) = self { matches!(self, GenContext::Last(_))
true
} else {
false
}
} }
} }
@@ -303,24 +299,16 @@ impl OpDesc {
// name and fixity -> operator type and precedence. // name and fixity -> operator type and precedence.
pub type OpDir = IndexMap<(Atom, Fixity), OpDesc, FxBuildHasher>; pub type OpDir = IndexMap<(Atom, Fixity), OpDesc, FxBuildHasher>;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Default, Clone, Copy)]
pub struct MachineFlags { pub struct MachineFlags {
pub double_quotes: DoubleQuotes, pub double_quotes: DoubleQuotes,
pub unknown: Unknown, pub unknown: Unknown,
} }
impl Default for MachineFlags { #[derive(Debug, Default, Clone, Copy, PartialEq)]
fn default() -> Self {
MachineFlags {
double_quotes: DoubleQuotes::default(),
unknown: Unknown::default(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DoubleQuotes { pub enum DoubleQuotes {
Atom, Atom,
#[default]
Chars, Chars,
Codes, Codes,
} }
@@ -339,14 +327,9 @@ impl DoubleQuotes {
} }
} }
impl Default for DoubleQuotes { #[derive(Debug, Clone, Copy, Default)]
fn default() -> Self {
DoubleQuotes::Chars
}
}
#[derive(Debug, Clone, Copy)]
pub enum Unknown { pub enum Unknown {
#[default]
Error, Error,
Fail, Fail,
Warn, Warn,
@@ -366,13 +349,6 @@ impl Unknown {
} }
} }
impl Default for Unknown {
#[inline]
fn default() -> Self {
Unknown::Error
}
}
pub fn default_op_dir() -> OpDir { pub fn default_op_dir() -> OpDir {
let mut op_dir = OpDir::with_hasher(FxBuildHasher::default()); let mut op_dir = OpDir::with_hasher(FxBuildHasher::default());
@@ -441,6 +417,9 @@ impl ParserError {
ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => { ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => {
atom!("unexpected_end_of_file") atom!("unexpected_end_of_file")
} }
ParserError::IO(e) if e.kind() == ErrorKind::InvalidData => {
atom!("invalid_data")
}
ParserError::IO(_) => atom!("input_output_error"), ParserError::IO(_) => atom!("input_output_error"),
ParserError::LexicalError(_) => atom!("lexical_error"), ParserError::LexicalError(_) => atom!("lexical_error"),
ParserError::MissingQuote(..) => atom!("missing_quote"), ParserError::MissingQuote(..) => atom!("missing_quote"),
@@ -505,7 +484,7 @@ impl<'a, 'b> CompositeOpDir<'a, 'b> {
#[inline] #[inline]
pub(crate) fn get(&self, name: Atom, fixity: Fixity) -> Option<OpDesc> { pub(crate) fn get(&self, name: Atom, fixity: Fixity) -> Option<OpDesc> {
let entry = if let Some(ref primary_op_dir) = &self.primary_op_dir { let entry = if let Some(primary_op_dir) = &self.primary_op_dir {
primary_op_dir.get(&(name, fixity)) primary_op_dir.get(&(name, fixity))
} else { } else {
None None
@@ -558,7 +537,7 @@ impl Fixnum {
#[inline] #[inline]
pub fn get_tag(&self) -> HeapCellValueTag { pub fn get_tag(&self) -> HeapCellValueTag {
use modular_bitfield::Specifier; use scryer_modular_bitfield::Specifier;
HeapCellValueTag::from_bytes(self.tag()).unwrap() HeapCellValueTag::from_bytes(self.tag()).unwrap()
} }
@@ -567,7 +546,7 @@ impl Fixnum {
const UPPER_BOUND: i64 = (1 << 55) - 1; const UPPER_BOUND: i64 = (1 << 55) - 1;
const LOWER_BOUND: i64 = -(1 << 55); const LOWER_BOUND: i64 = -(1 << 55);
if LOWER_BOUND <= num && num <= UPPER_BOUND { if (LOWER_BOUND..=UPPER_BOUND).contains(&num) {
Ok(Fixnum::new() Ok(Fixnum::new()
.with_m(false) .with_m(false)
.with_f(false) .with_f(false)
@@ -582,7 +561,7 @@ impl Fixnum {
pub fn get_num(self) -> i64 { pub fn get_num(self) -> i64 {
let n = self.num() as i64; let n = self.num() as i64;
let (n, overflowed) = (n << 8).overflowing_shr(8); let (n, overflowed) = (n << 8).overflowing_shr(8);
debug_assert_eq!(overflowed, false); debug_assert!(!overflowed);
n n
} }
} }
@@ -730,11 +709,12 @@ impl Var {
#[inline(always)] #[inline(always)]
pub fn as_str(&self) -> Option<&str> { pub fn as_str(&self) -> Option<&str> {
match self { match self {
Var::Named(value) => Some(&value), Var::Named(value) => Some(value),
_ => None, _ => None,
} }
} }
#[allow(clippy::inherent_to_string)]
#[inline(always)] #[inline(always)]
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
match self { match self {
@@ -800,10 +780,8 @@ impl Term {
#[inline] #[inline]
pub fn source_arity(terms: &[Term]) -> usize { pub fn source_arity(terms: &[Term]) -> usize {
if let Some(last_arg) = terms.last() { if let Some(Term::Literal(_, Literal::CodeIndex(_))) = terms.last() {
if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { return terms.len() - 1;
return terms.len() - 1;
}
} }
terms.len() terms.len()
@@ -811,10 +789,8 @@ pub fn source_arity(terms: &[Term]) -> usize {
pub(crate) fn unfold_by_str_once(term: &mut Term, s: Atom) -> Option<(Term, Term)> { pub(crate) fn unfold_by_str_once(term: &mut Term, s: Atom) -> Option<(Term, Term)> {
if let Term::Clause(_, ref name, ref mut subterms) = term { if let Term::Clause(_, ref name, ref mut subterms) = term {
if let Some(last_arg) = subterms.last() { if let Some(Term::Literal(_, Literal::CodeIndex(_))) = subterms.last() {
if let Term::Literal(_, Literal::CodeIndex(_)) = last_arg { subterms.pop();
subterms.pop();
}
} }
if name == &s && subterms.len() == 2 { if name == &s && subterms.len() == 2 {

View File

@@ -131,15 +131,9 @@ impl<R: Read> CharReader<R> {
pub fn peek_byte(&mut self) -> Option<io::Result<u8>> { pub fn peek_byte(&mut self) -> Option<io::Result<u8>> {
match self.refresh_buffer() { match self.refresh_buffer() {
Ok(_buf) => {} Ok(_buf) => _buf.first().cloned().map(Ok),
Err(e) => return Some(Err(e)), Err(e) => Some(Err(e)),
} }
return if let Some(b) = self.buf.get(0).cloned() {
Some(Ok(b))
} else {
None
};
} }
} }
@@ -150,6 +144,35 @@ impl<R: Read> CharRead for CharReader<R> {
Err(e) => return Some(Err(e)), Err(e) => return Some(Err(e)),
} }
let bad_bytes_error = |buf: &[u8]| {
// If we have 4 bytes that still don't make up
// a valid code point, then we have garbage.
// We have bad data in the buffer. Remove
// leading bytes until either the buffer is
// empty, or we have a valid code point.
let mut split_point = 1;
let mut badbytes = vec![];
loop {
let (bad, rest) = buf.split_at(split_point);
if rest.is_empty() || str::from_utf8(rest).is_ok() {
badbytes.extend_from_slice(bad);
break;
}
split_point += 1;
}
// Raise the error. If we still have data in
// the buffer, it will be returned on the next
// loop.
io::Error::new(io::ErrorKind::InvalidData, BadUtf8Error { bytes: badbytes })
};
loop { loop {
let buf = &self.buf[self.pos..]; let buf = &self.buf[self.pos..];
@@ -165,77 +188,51 @@ impl<R: Read> CharRead for CharReader<R> {
}; };
if buf.len() - e.valid_up_to() >= 4 { if buf.len() - e.valid_up_to() >= 4 {
// If we have 4 bytes that still don't make up return Some(Err(bad_bytes_error(buf)));
// a valid code point, then we have garbage. } else if self.pos >= self.buf.len() {
return None;
} else if self.buf.len() - self.pos >= 4 && self.pos < e.valid_up_to() {
return match str::from_utf8(&self.buf[self.pos..e.valid_up_to()]) {
Ok(s) => {
let mut chars = s.chars();
let c = chars.next().unwrap();
// We have bad data in the buffer. Remove Some(Ok(c))
// leading bytes until either the buffer is
// empty, or we have a valid code point.
let mut split_point = 1;
let mut badbytes = vec![];
loop {
let (bad, rest) = buf.split_at(split_point);
if rest.is_empty() || str::from_utf8(rest).is_ok() {
badbytes.extend_from_slice(bad);
break;
} }
Err(e) => {
let badbytes = self.buf[self.pos..e.valid_up_to()].to_vec();
split_point += 1; Some(Err(io::Error::new(
io::ErrorKind::InvalidData,
BadUtf8Error { bytes: badbytes },
)))
}
};
} else {
let buf_len = self.buf.len();
for (c, idx) in (self.pos..buf_len).enumerate() {
self.buf[c] = self.buf[idx];
} }
// Raise the error. If we still have data in self.buf.truncate(buf_len - self.pos);
// the buffer, it will be returned on the next
// loop.
return Some(Err(io::Error::new( let buf_len = self.buf.len();
io::ErrorKind::InvalidData, self.pos = 0;
BadUtf8Error { bytes: badbytes },
)));
} else {
if self.pos >= self.buf.len() {
return None;
} else if self.buf.len() - self.pos >= 4 {
return match str::from_utf8(&self.buf[self.pos..e.valid_up_to()]) {
Ok(s) => {
let mut chars = s.chars();
let c = chars.next().unwrap();
Some(Ok(c)) if buf_len >= 4 {
} continue;
Err(e) => { }
let badbytes = self.buf[self.pos..e.valid_up_to()].to_vec();
Some(Err(io::Error::new( let mut word = [0u8; 4];
io::ErrorKind::InvalidData, let word_slice = &mut word[buf_len..4];
BadUtf8Error { bytes: badbytes },
)))
}
};
} else {
let buf_len = self.buf.len();
for (c, idx) in (self.pos..buf_len).enumerate() { match self.inner.read(word_slice) {
self.buf[c] = self.buf[idx]; Err(e) => return Some(Err(e)),
Ok(0) => return Some(Err(bad_bytes_error(&self.buf))),
Ok(nread) => {
self.buf.extend_from_slice(&word_slice[0..nread]);
} }
self.buf.truncate(buf_len - self.pos);
let buf_len = self.buf.len();
let mut word = [0u8; 4];
let word_slice = &mut word[buf_len..4];
match self.inner.read(word_slice) {
Err(e) => return Some(Err(e)),
Ok(nread) => {
self.buf.extend_from_slice(&word_slice[0..nread]);
}
}
self.pos = 0;
} }
} }
} else { } else {
@@ -383,6 +380,7 @@ mod tests {
use std::io::Cursor; use std::io::Cursor;
#[test] #[test]
#[cfg_attr(miri, ignore = "slow and not very relevant")]
fn plain_string() { fn plain_string() {
let mut read_string = CharReader::new(Cursor::new("a string")); let mut read_string = CharReader::new(Cursor::new("a string"));
@@ -395,6 +393,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "slow and not very relevant")]
fn greek_string() { fn greek_string() {
let mut read_string = CharReader::new(Cursor::new("λέξη")); let mut read_string = CharReader::new(Cursor::new("λέξη"));
@@ -407,6 +406,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "slow and not very relevant")]
fn russian_string() { fn russian_string() {
let mut read_string = CharReader::new(Cursor::new("слово")); let mut read_string = CharReader::new(Cursor::new("слово"));
@@ -419,6 +419,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "slow and not very relevant")]
fn greek_lorem_ipsum() { fn greek_lorem_ipsum() {
let lorem_ipsum = "Λορεμ ιπσθμ δολορ σιτ αμετ, οφφενδιτ let lorem_ipsum = "Λορεμ ιπσθμ δολορ σιτ αμετ, οφφενδιτ
εφφιcιενδι σιτ ει, ηαρθμ λεγερε αερενδθμ ιθσ νε. Ηασ νο εροσ εφφιcιενδι σιτ ει, ηαρθμ λεγερε αερενδθμ ιθσ νε. Ηασ νο εροσ
@@ -490,6 +491,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "slow and not very relevant")]
fn armenian_lorem_ipsum() { fn armenian_lorem_ipsum() {
let lorem_ipsum = "լոռեմ իպսում դոլոռ սիթ ամեթ, նովում գռաեծո let lorem_ipsum = "լոռեմ իպսում դոլոռ սիթ ամեթ, նովում գռաեծո
սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում
@@ -563,6 +565,7 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "slow and not very relevant")]
fn russian_lorem_ipsum() { fn russian_lorem_ipsum() {
let lorem_ipsum = "Лорем ипсум долор сит амет, атяуи дицам еи let lorem_ipsum = "Лорем ипсум долор сит амет, атяуи дицам еи
сит, ид сеа фацилис елаборарет. Меа еу яуас алияуид, те яуи сит, ид сеа фацилис елаборарет. Меа еу яуас алияуид, те яуи

View File

@@ -48,11 +48,7 @@ pub enum Token {
impl Token { impl Token {
#[inline] #[inline]
pub(super) fn is_end(&self) -> bool { pub(super) fn is_end(&self) -> bool {
if let Token::End = self { matches!(self, Token::End)
true
} else {
false
}
} }
} }
@@ -604,10 +600,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
break; break;
} }
} }
} else if cut_char!(c) { } else if cut_char!(c) || semicolon_char!(c) {
self.skip_char(c);
token.push(c);
} else if semicolon_char!(c) {
self.skip_char(c); self.skip_char(c);
token.push(c); token.push(c);
} else if single_quote_char!(c) { } else if single_quote_char!(c) {
@@ -690,7 +683,8 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if self.reader.peek_char().is_none() { if self.reader.peek_char().is_none() {
self.return_char('.'); self.return_char('.');
i64::from_str_radix(&token, 10) token
.parse::<i64>()
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))) .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| { .or_else(|_| {
token token
@@ -720,12 +714,12 @@ impl<'a, R: CharRead> Lexer<'a, R> {
token.push(c); token.push(c);
let c = match self.lookahead_char() { let c = match self.lookahead_char() {
Err(_) => return Ok(self.vacate_with_float(token)?), Err(_) => return self.vacate_with_float(token),
Ok(c) => c, Ok(c) => c,
}; };
if !sign_char!(c) && !decimal_digit_char!(c) { if !sign_char!(c) && !decimal_digit_char!(c) {
return Ok(self.vacate_with_float(token)?); return self.vacate_with_float(token);
} }
if sign_char!(c) { if sign_char!(c) {
@@ -735,14 +729,14 @@ impl<'a, R: CharRead> Lexer<'a, R> {
let c = match self.lookahead_char() { let c = match self.lookahead_char() {
Err(_) => { Err(_) => {
self.return_char(token.pop().unwrap()); self.return_char(token.pop().unwrap());
return Ok(self.vacate_with_float(token)?); return self.vacate_with_float(token);
} }
Ok(c) => c, Ok(c) => c,
}; };
if !decimal_digit_char!(c) { if !decimal_digit_char!(c) {
self.return_char(token.pop().unwrap()); self.return_char(token.pop().unwrap());
return Ok(self.vacate_with_float(token)?); return self.vacate_with_float(token);
} }
} }
@@ -769,7 +763,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
self.machine_st.arena self.machine_st.arena
)))) ))))
} else { } else {
return Ok(self.vacate_with_float(token)?); return self.vacate_with_float(token);
} }
} else { } else {
let n = parse_lossy::<f64, _>(token.as_bytes())?; let n = parse_lossy::<f64, _>(token.as_bytes())?;
@@ -781,7 +775,147 @@ impl<'a, R: CharRead> Lexer<'a, R> {
} else { } else {
self.return_char('.'); self.return_char('.');
i64::from_str_radix(&token, 10) token
.parse::<i64>()
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
}
} else if token.starts_with('0') && token.len() == 1 {
if c == 'x' {
self.hexadecimal_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e {
token
.parse::<i64>()
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
} else {
Err(e)
}
})
} else if c == 'o' {
self.octal_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e {
token
.parse::<i64>()
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
} else {
Err(e)
}
})
} else if c == 'b' {
self.binary_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e {
token
.parse::<i64>()
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
} else {
Err(e)
}
})
} else if single_quote_char!(c) {
self.skip_char(c);
let c = self.lookahead_char()?;
if backslash_char!(c) {
self.skip_char(c);
let c = self.lookahead_char()?;
if new_line_char!(c) {
self.skip_char(c);
self.return_char('\'');
return Ok(Token::Literal(Literal::Fixnum(Fixnum::build_with(0))));
} else {
self.return_char('\\');
}
}
self.get_single_quoted_char()
.map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64))))
.or_else(|err| {
match err {
ParserError::UnexpectedChar('\'', ..) => {}
err => return Err(err),
}
self.return_char(c);
token
.parse::<i64>()
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
})
} else {
token
.parse::<i64>()
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))) .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| { .or_else(|_| {
token token
@@ -796,155 +930,20 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}) })
} }
} else { } else {
if token.starts_with('0') && token.len() == 1 { token
if c == 'x' { .parse::<i64>()
self.hexadecimal_constant(c).or_else(|e| { .map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
if let ParserError::ParseBigInt(..) = e { .or_else(|_| {
i64::from_str_radix(&token, 10) token
.map(|n| { .parse::<Integer>()
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)) .map(|n| {
}) Token::Literal(Literal::Integer(arena_alloc!(
.or_else(|_| { n,
token &mut self.machine_st.arena
.parse::<Integer>() )))
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
} else {
Err(e)
}
})
} else if c == 'o' {
self.octal_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e {
i64::from_str_radix(&token, 10)
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
} else {
Err(e)
}
})
} else if c == 'b' {
self.binary_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e {
i64::from_str_radix(&token, 10)
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
} else {
Err(e)
}
})
} else if single_quote_char!(c) {
self.skip_char(c);
let c = self.lookahead_char()?;
if backslash_char!(c) {
self.skip_char(c);
let c = self.lookahead_char()?;
if new_line_char!(c) {
self.skip_char(c);
self.return_char('\'');
return Ok(Token::Literal(Literal::Fixnum(Fixnum::build_with(0))));
} else {
self.return_char('\\');
}
}
self.get_single_quoted_char()
.map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64))))
.or_else(|err| {
match err {
ParserError::UnexpectedChar('\'', ..) => {}
err => return Err(err),
}
self.return_char(c);
i64::from_str_radix(&token, 10)
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
}) })
} else { .map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
i64::from_str_radix(&token, 10) })
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
}
} else {
i64::from_str_radix(&token, 10)
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
}
} }
} }
@@ -953,6 +952,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
c: Option<char>, c: Option<char>,
layout_info: &mut LayoutInfo, layout_info: &mut LayoutInfo,
) -> Result<(), ParserError> { ) -> Result<(), ParserError> {
#[allow(clippy::redundant_guards)]
match c { match c {
Some(c) if layout_char!(c) => { Some(c) if layout_char!(c) => {
self.skip_char(c); self.skip_char(c);

View File

@@ -77,7 +77,7 @@ macro_rules! cut_char {
#[macro_export] #[macro_export]
macro_rules! decimal_digit_char { macro_rules! decimal_digit_char {
($c: expr) => { ($c: expr) => {
('0'..='9').contains(&$c) $c.is_ascii_digit()
}; };
} }
@@ -125,7 +125,7 @@ macro_rules! graphic_token_char {
#[macro_export] #[macro_export]
macro_rules! hexadecimal_digit_char { macro_rules! hexadecimal_digit_char {
($c: expr) => { ($c: expr) => {
('0'..='9').contains(&$c) || ('A'..='F').contains(&$c) || ('a'..='f').contains(&$c) $c.is_ascii_digit() || ('A'..='F').contains(&$c) || ('a'..='f').contains(&$c)
}; };
} }

View File

@@ -11,4 +11,5 @@ pub mod ast;
#[macro_use] #[macro_use]
pub mod macros; pub mod macros;
pub mod lexer; pub mod lexer;
#[allow(clippy::module_inception)]
pub mod parser; pub mod parser;

View File

@@ -97,7 +97,12 @@ pub(crate) fn as_partial_string(
string.push(*c); string.push(*c);
} }
_ => { _ => {
return Err(Term::Cons(Cell::default(), Box::new(head), orig_tail)); tail = Term::Cons(
Cell::default(),
Box::new((**prev).clone()),
Box::new((**succ).clone()),
);
break;
} }
} }
@@ -464,7 +469,12 @@ impl<'a, R: CharRead> Parser<'a, R> {
Token::End => TokenType::End, Token::End => TokenType::End,
}; };
self.stack.push(TokenDesc { tt, priority, spec, unfold_bounds: 0, }); self.stack.push(TokenDesc {
tt,
priority,
spec,
unfold_bounds: 0,
});
} }
fn reduce_op(&mut self, priority: usize) { fn reduce_op(&mut self, priority: usize) {
@@ -472,10 +482,9 @@ impl<'a, R: CharRead> Parser<'a, R> {
if let Some(desc1) = self.stack.pop() { if let Some(desc1) = self.stack.pop() {
if let Some(desc2) = self.stack.pop() { if let Some(desc2) = self.stack.pop() {
if let Some(desc3) = self.stack.pop() { if let Some(desc3) = self.stack.pop() {
if is_xfx!(desc2.spec) && affirm_xfx(priority, desc2, desc3, desc1) { if is_xfx!(desc2.spec) && affirm_xfx(priority, desc2, desc3, desc1)
self.push_binary_op(desc2, LTERM); || is_yfx!(desc2.spec) && affirm_yfx(priority, desc2, desc3, desc1)
continue; {
} else if is_yfx!(desc2.spec) && affirm_yfx(priority, desc2, desc3, desc1) {
self.push_binary_op(desc2, LTERM); self.push_binary_op(desc2, LTERM);
continue; continue;
} else if is_xfy!(desc2.spec) && affirm_xfy(priority, desc2, desc3, desc1) { } else if is_xfy!(desc2.spec) && affirm_xfy(priority, desc2, desc3, desc1) {
@@ -555,10 +564,12 @@ impl<'a, R: CharRead> Parser<'a, R> {
if self.stack.len() > 2 * arity { if self.stack.len() > 2 * arity {
let idx = self.stack.len() - 2 * arity - 1; let idx = self.stack.len() - 2 * arity - 1;
if is_infix!(self.stack[idx].spec) && idx > 0 { if is_infix!(self.stack[idx].spec)
if !is_op!(self.stack[idx - 1].spec) && !self.stack[idx - 1].tt.is_sep() { && idx > 0
return false; && !is_op!(self.stack[idx - 1].spec)
} && !self.stack[idx - 1].tt.is_sep()
{
return false;
} }
} else { } else {
return false; return false;
@@ -571,59 +582,57 @@ impl<'a, R: CharRead> Parser<'a, R> {
let stack_len = self.stack.len() - 2 * arity - 1; let stack_len = self.stack.len() - 2 * arity - 1;
let idx = self.terms.len() - arity; let idx = self.terms.len() - arity;
if TokenType::Term == self.stack[stack_len].tt { if TokenType::Term == self.stack[stack_len].tt
if atomize_term(&self.lexer.machine_st.atom_tbl, &self.terms[idx - 1]).is_some() { && atomize_term(&self.lexer.machine_st.atom_tbl, &self.terms[idx - 1]).is_some()
self.stack.truncate(stack_len + 1); {
self.stack.truncate(stack_len + 1);
let mut subterms: Vec<_> = self.terms.drain(idx..).collect(); let mut subterms: Vec<_> = self.terms.drain(idx..).collect();
if let Some(name) = self if let Some(name) = self
.terms .terms
.pop() .pop()
.and_then(|t| atomize_term(&self.lexer.machine_st.atom_tbl, &t)) .and_then(|t| atomize_term(&self.lexer.machine_st.atom_tbl, &t))
{ {
// reduce the '.' functor to a cons cell if it applies. // reduce the '.' functor to a cons cell if it applies.
if name == atom!(".") && subterms.len() == 2 { if name == atom!(".") && subterms.len() == 2 {
let tail = subterms.pop().unwrap(); let tail = subterms.pop().unwrap();
let head = subterms.pop().unwrap(); let head = subterms.pop().unwrap();
self.terms.push(match as_partial_string(head, tail) { self.terms.push(match as_partial_string(head, tail) {
Ok((string_buf, Some(tail))) => { Ok((string_buf, Some(tail))) => {
Term::PartialString(Cell::default(), string_buf, tail) Term::PartialString(Cell::default(), string_buf, tail)
}
Ok((string_buf, None)) => {
let atom = AtomTable::build_with(
&self.lexer.machine_st.atom_tbl,
&string_buf,
);
Term::CompleteString(Cell::default(), atom)
}
Err(term) => term,
});
} else {
self.terms
.push(Term::Clause(Cell::default(), name, subterms));
}
if let Some(&mut TokenDesc {
ref mut tt,
ref mut priority,
ref mut spec,
ref mut unfold_bounds,
}) = self.stack.last_mut()
{
if *spec == BTERM {
return false;
} }
Ok((string_buf, None)) => {
let atom =
AtomTable::build_with(&self.lexer.machine_st.atom_tbl, &string_buf);
Term::CompleteString(Cell::default(), atom)
}
Err(term) => term,
});
} else {
self.terms
.push(Term::Clause(Cell::default(), name, subterms));
}
*tt = TokenType::Term; if let Some(&mut TokenDesc {
*priority = 0; ref mut tt,
*spec = TERM; ref mut priority,
*unfold_bounds = 0; ref mut spec,
ref mut unfold_bounds,
}) = self.stack.last_mut()
{
if *spec == BTERM {
return false;
} }
return true; *tt = TokenType::Term;
*priority = 0;
*spec = TERM;
*unfold_bounds = 0;
} }
return true;
} }
} }
@@ -642,34 +651,31 @@ impl<'a, R: CharRead> Parser<'a, R> {
/* '|' is a head-tail separator here, not /* '|' is a head-tail separator here, not
* an operator, so expand the * an operator, so expand the
* terms it compacted out again. */ * terms it compacted out again. */
match (term.name(), term.arity()) { if let (Some(atom!(",")), 2) = (term.name(), term.arity()) {
(Some(name), 2) if name == atom!(",") => { let terms = if op_desc.unfold_bounds == 0 {
let terms = if op_desc.unfold_bounds == 0 { unfold_by_str(term, atom!(","))
unfold_by_str(term, atom!(",")) } else {
} else { let mut terms = vec![];
let mut terms = vec![];
while let Some((fst, snd)) = unfold_by_str_once(&mut term, atom!(",")) { while let Some((fst, snd)) = unfold_by_str_once(&mut term, atom!(",")) {
terms.push(fst); terms.push(fst);
term = snd; term = snd;
op_desc.unfold_bounds -= 2; op_desc.unfold_bounds -= 2;
if op_desc.unfold_bounds == 0 { if op_desc.unfold_bounds == 0 {
break; break;
}
} }
}
terms.push(term); terms.push(term);
terms terms
}; };
let arity = terms.len() - 1; let arity = terms.len() - 1;
self.terms.extend(terms.into_iter()); self.terms.extend(terms);
return arity; return arity;
}
_ => {}
} }
} }
@@ -692,18 +698,15 @@ impl<'a, R: CharRead> Parser<'a, R> {
} else { } else {
return None; return None;
} }
} else { } else if desc.tt == TokenType::HeadTailSeparator {
if desc.tt == TokenType::HeadTailSeparator { if arity == 1 {
if arity == 1 { continue;
continue;
}
return None;
} else if desc.tt == TokenType::OpenList {
return Some(arity);
} else if desc.tt != TokenType::Comma {
return None;
} }
return None;
} else if desc.tt == TokenType::OpenList {
return Some(arity);
} else if desc.tt != TokenType::Comma {
return None;
} }
} }
@@ -882,7 +885,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
.push(Term::Literal(Cell::default(), Literal::Atom(atom))); .push(Term::Literal(Cell::default(), Literal::Atom(atom)));
} }
self.stack[idx].spec = if self.stack[idx].priority > 0 { TERM } else { BTERM }; self.stack[idx].spec = BTERM;
self.stack[idx].tt = TokenType::Term; self.stack[idx].tt = TokenType::Term;
self.stack[idx].priority = 0; self.stack[idx].priority = 0;
@@ -1018,13 +1021,11 @@ impl<'a, R: CharRead> Parser<'a, R> {
Token::Open => self.shift(Token::Open, 1300, DELIMITER), Token::Open => self.shift(Token::Open, 1300, DELIMITER),
Token::OpenCT => self.shift(Token::OpenCT, 1300, DELIMITER), Token::OpenCT => self.shift(Token::OpenCT, 1300, DELIMITER),
Token::Close => { Token::Close => {
if !self.reduce_term() { if !self.reduce_term() && !self.reduce_brackets() {
if !self.reduce_brackets() { return Err(ParserError::IncompleteReduction(
return Err(ParserError::IncompleteReduction( self.lexer.line_num,
self.lexer.line_num, self.lexer.col_num,
self.lexer.col_num, ));
));
}
} }
} }
Token::OpenList => self.shift(Token::OpenList, 1300, DELIMITER), Token::OpenList => self.shift(Token::OpenList, 1300, DELIMITER),

View File

@@ -28,6 +28,7 @@ impl<T: RawBlockTraits> RawBlock<T> {
} }
} }
#[allow(clippy::new_without_default)]
pub fn new() -> Self { pub fn new() -> Self {
let mut block = Self::empty_block(); let mut block = Self::empty_block();
@@ -42,7 +43,7 @@ impl<T: RawBlockTraits> RawBlock<T> {
let layout = alloc::Layout::from_size_align_unchecked(cap, T::align()); let layout = alloc::Layout::from_size_align_unchecked(cap, T::align());
self.base = alloc::alloc(layout) as *const _; self.base = alloc::alloc(layout) as *const _;
self.top = (self.base as usize + cap) as *const _; self.top = self.base.add(cap);
*self.ptr.get_mut() = self.base as *mut _; *self.ptr.get_mut() = self.base as *mut _;
} }
@@ -71,7 +72,7 @@ impl<T: RawBlockTraits> RawBlock<T> {
} else { } else {
let allocated = (*self.ptr.get()) as usize - self.base as usize; let allocated = (*self.ptr.get()) as usize - self.base as usize;
self.base.copy_to(new_block.base.cast_mut(), allocated); self.base.copy_to(new_block.base.cast_mut(), allocated);
*new_block.ptr.get_mut() = new_block.base.offset(allocated as isize).cast_mut(); *new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut();
Some(new_block) Some(new_block)
} }
} }
@@ -97,7 +98,7 @@ impl<T: RawBlockTraits> RawBlock<T> {
pub unsafe fn alloc(&self, size: usize) -> *mut u8 { pub unsafe fn alloc(&self, size: usize) -> *mut u8 {
if self.free_space() >= size { if self.free_space() >= size {
let ptr = *self.ptr.get(); let ptr = *self.ptr.get();
*self.ptr.get() = (ptr as usize + size) as *mut _; *self.ptr.get() = ptr.add(size) as *mut _;
ptr ptr
} else { } else {
ptr::null_mut() ptr::null_mut()

View File

@@ -6,7 +6,7 @@ use std::{
ptr::NonNull, ptr::NonNull,
sync::{ sync::{
atomic::{AtomicPtr, AtomicU8}, atomic::{AtomicPtr, AtomicU8},
Arc, Weak, RwLock Arc, RwLock, Weak,
}, },
}; };
@@ -22,7 +22,7 @@ thread_local! {
// odd value means the current thread is about to access the active_epoch of an Rcu // odd value means the current thread is about to access the active_epoch of an Rcu
// a thread has a single epoch counter for all Rcu it accesses, // a thread has a single epoch counter for all Rcu it accesses,
// as a thread can only access one Rcu at a time // as a thread can only access one Rcu at a time
static THREAD_EPOCH_COUNTER: OnceCell<Arc<AtomicU8>> = OnceCell::new(); static THREAD_EPOCH_COUNTER: OnceCell<Arc<AtomicU8>> = const { OnceCell::new() };
} }
pub struct Rcu<T> { pub struct Rcu<T> {
@@ -52,7 +52,7 @@ impl<T> Rcu<T> {
// register the current threads epoch counter on init // register the current threads epoch counter on init
EPOCH_COUNTERS EPOCH_COUNTERS
.write() .write()
.unwrap() .unwrap()
.push(Arc::downgrade(&epoch_counter)); .push(Arc::downgrade(&epoch_counter));
epoch_counter epoch_counter
}); });

View File

@@ -31,8 +31,8 @@ use std::sync::Arc;
type SubtermDeque = VecDeque<(usize, usize)>; type SubtermDeque = VecDeque<(usize, usize)>;
pub(crate) fn devour_whitespace<'a, R: CharRead>( pub(crate) fn devour_whitespace<R: CharRead>(
parser: &mut Parser<'a, R>, parser: &mut Parser<'_, R>,
) -> Result<bool, ParserError> { ) -> Result<bool, ParserError> {
match parser.lexer.scan_for_layout() { match parser.lexer.scan_for_layout() {
Err(e) if e.is_unexpected_eof() => Ok(true), Err(e) if e.is_unexpected_eof() => Ok(true),
@@ -86,7 +86,7 @@ impl MachineState {
static mut PROMPT: bool = false; static mut PROMPT: bool = false;
#[cfg(feature = "repl")] #[cfg(feature = "repl")]
const HISTORY_FILE: &'static str = ".scryer_history"; const HISTORY_FILE: &str = ".scryer_history";
pub(crate) fn set_prompt(value: bool) { pub(crate) fn set_prompt(value: bool) {
unsafe { unsafe {
@@ -130,14 +130,14 @@ impl ReadlineStream {
if let Some(mut path) = dirs_next::home_dir() { if let Some(mut path) = dirs_next::home_dir() {
path.push(HISTORY_FILE); path.push(HISTORY_FILE);
if path.exists() && rl.load_history(&path).is_err() { if path.exists() && rl.load_history(&path).is_err() {
println!("Warning: loading history failed"); println!("% Warning: loading history failed");
} }
} }
ReadlineStream { ReadlineStream {
rl, rl,
pending_input: CharReader::new(Cursor::new(pending_input.to_owned())), pending_input: CharReader::new(Cursor::new(pending_input.to_owned())),
add_history: add_history, add_history,
} }
} }
@@ -145,7 +145,7 @@ impl ReadlineStream {
{ {
ReadlineStream { ReadlineStream {
pending_input: CharReader::new(Cursor::new(pending_input.to_owned())), pending_input: CharReader::new(Cursor::new(pending_input.to_owned())),
add_history: add_history, add_history,
} }
} }
} }
@@ -187,7 +187,7 @@ impl ReadlineStream {
PROMPT = false; PROMPT = false;
} }
if self.pending_input.get_ref().get_ref().chars().last() != Some('\n') { if !self.pending_input.get_ref().get_ref().ends_with('\n') {
*self.pending_input.get_mut().get_mut() += "\n"; *self.pending_input.get_mut().get_mut() += "\n";
} }
} }
@@ -213,10 +213,10 @@ impl ReadlineStream {
path.push(HISTORY_FILE); path.push(HISTORY_FILE);
if path.exists() { if path.exists() {
if self.rl.append_history(&path).is_err() { if self.rl.append_history(&path).is_err() {
println!("Warning: couldn't append history (existing file)"); println!("% Warning: couldn't append history (existing file)");
} }
} else if self.rl.save_history(&path).is_err() { } else if self.rl.save_history(&path).is_err() {
println!("Warning: couldn't save history (new file)"); println!("% Warning: couldn't save history (new file)");
} }
} }
} }
@@ -292,9 +292,9 @@ impl CharRead for ReadlineStream {
} }
#[inline] #[inline]
pub(crate) fn write_term_to_heap<'a, 'b>( pub(crate) fn write_term_to_heap(
term: &'a Term, term: &Term,
heap: &'b mut Heap, heap: &mut Heap,
atom_tbl: &AtomTable, atom_tbl: &AtomTable,
) -> Result<TermWriteResult, CompilationError> { ) -> Result<TermWriteResult, CompilationError> {
let term_writer = TermWriter::new(heap, atom_tbl); let term_writer = TermWriter::new(heap, atom_tbl);
@@ -347,7 +347,7 @@ impl<'a, 'b> TermWriter<'a, 'b> {
match term { match term {
&TermRef::Cons(..) => list_loc_as_cell!(h), &TermRef::Cons(..) => list_loc_as_cell!(h),
&TermRef::AnonVar(_) | &TermRef::Var(..) => heap_loc_as_cell!(h), &TermRef::AnonVar(_) | &TermRef::Var(..) => heap_loc_as_cell!(h),
&TermRef::CompleteString(_, _, ref src) => { TermRef::CompleteString(_, _, src) => {
if src.as_str().is_empty() { if src.as_str().is_empty() {
empty_list_as_cell!() empty_list_as_cell!()
} else if self.heap[h].get_tag() == HeapCellValueTag::CStr { } else if self.heap[h].get_tag() == HeapCellValueTag::CStr {
@@ -358,7 +358,7 @@ impl<'a, 'b> TermWriter<'a, 'b> {
} }
&TermRef::PartialString(..) => pstr_loc_as_cell!(h), &TermRef::PartialString(..) => pstr_loc_as_cell!(h),
&TermRef::Literal(_, _, literal) => HeapCellValue::from(*literal), &TermRef::Literal(_, _, literal) => HeapCellValue::from(*literal),
&TermRef::Clause(_, _, _, subterms) if subterms.len() == 0 => heap_loc_as_cell!(h), &TermRef::Clause(_, _, _, subterms) if subterms.is_empty() => heap_loc_as_cell!(h),
&TermRef::Clause(..) => str_loc_as_cell!(h), &TermRef::Clause(..) => str_loc_as_cell!(h),
} }
} }
@@ -390,7 +390,7 @@ impl<'a, 'b> TermWriter<'a, 'b> {
return Err(CompilationError::ExceededMaxArity); return Err(CompilationError::ExceededMaxArity);
} }
self.heap.push(if subterms.len() == 0 { self.heap.push(if subterms.is_empty() {
heap_loc_as_cell!(heap_loc + 1) heap_loc_as_cell!(heap_loc + 1)
} else { } else {
str_loc_as_cell!(heap_loc + 1) str_loc_as_cell!(heap_loc + 1)
@@ -438,11 +438,11 @@ impl<'a, 'b> TermWriter<'a, 'b> {
continue; continue;
} }
&TermRef::CompleteString(_, _, ref src) => { TermRef::CompleteString(_, _, src) => {
let src = src.as_str().to_owned(); let src = src.as_str().to_owned();
put_complete_string(self.heap, &src, self.atom_tbl); put_complete_string(self.heap, &src, self.atom_tbl);
} }
&TermRef::PartialString(lvl, _, ref src, _) => { &TermRef::PartialString(lvl, _, src, _) => {
if let Level::Root = lvl { if let Level::Root = lvl {
// Var tags can't refer directly to partial strings, // Var tags can't refer directly to partial strings,
// so a PStrLoc cell must be pushed. // so a PStrLoc cell must be pushed.
@@ -458,7 +458,7 @@ impl<'a, 'b> TermWriter<'a, 'b> {
continue; continue;
} }
} }
&TermRef::Var(.., ref var) => { TermRef::Var(.., var) => {
if let Some((arity, site_h)) = self.queue.pop_front() { if let Some((arity, site_h)) = self.queue.pop_front() {
let var_key = VarKey::VarPtr(var.clone()); let var_key = VarKey::VarPtr(var.clone());

View File

@@ -64,10 +64,7 @@ impl<'a> CompilationTarget<'a> for FactInstruction {
} }
fn is_void_instr(instr: &Instruction) -> bool { fn is_void_instr(instr: &Instruction) -> bool {
match instr { matches!(instr, &Instruction::UnifyVoid(_))
&Instruction::UnifyVoid(_) => true,
_ => false,
}
} }
fn to_pstr(lvl: Level, string: Atom, r: RegType, has_tail: bool) -> Instruction { fn to_pstr(lvl: Level, string: Atom, r: RegType, has_tail: bool) -> Instruction {
@@ -75,9 +72,8 @@ impl<'a> CompilationTarget<'a> for FactInstruction {
} }
fn incr_void_instr(instr: &mut Instruction) { fn incr_void_instr(instr: &mut Instruction) {
match instr { if let &mut Instruction::UnifyVoid(ref mut incr) = instr {
&mut Instruction::UnifyVoid(ref mut incr) => *incr += 1, *incr += 1
_ => {}
} }
} }
@@ -146,16 +142,12 @@ impl<'a> CompilationTarget<'a> for QueryInstruction {
} }
fn is_void_instr(instr: &Instruction) -> bool { fn is_void_instr(instr: &Instruction) -> bool {
match instr { matches!(instr, &Instruction::SetVoid(_))
&Instruction::SetVoid(_) => true,
_ => false,
}
} }
fn incr_void_instr(instr: &mut Instruction) { fn incr_void_instr(instr: &mut Instruction) {
match instr { if let &mut Instruction::SetVoid(ref mut incr) = instr {
&mut Instruction::SetVoid(ref mut incr) => *incr += 1, *incr += 1
_ => {}
} }
} }

View File

@@ -1,10 +1,10 @@
/**/ /**/
:- use_module(library(format)).
:- use_module(library(dcgs)). :- use_module(library(dcgs)).
:- use_module(library(format)).
:- use_module(library(lists)). :- use_module(library(lists)).
:- use_module(library(debug)). :- use_module(library(debug)).
:- use_module(library(atts)). :- use_module(library(iso_ext)).
:- use_module(library(dif)). :- use_module(library(dif)).
% Tests from https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dif % Tests from https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dif
@@ -207,6 +207,13 @@ test("scryer-prolog#2056",(
\+ E=[] \+ E=[]
)). )).
% https://github.com/mthom/scryer-prolog/issues/2175
test("scryer-prolog#2175",(
dif(A,B),
A=_C*[],
A=[]*D*B,D=[]
)).
main :- main :-
findall(test(Name, Goal), test(Name, Goal), Tests), findall(test(Name, Goal), test(Name, Goal), Tests),
run_tests(Tests, Failed), run_tests(Tests, Failed),

145
src/tests/when.pl Normal file
View File

@@ -0,0 +1,145 @@
/**/
:- use_module(library(iso_ext)).
:- use_module(library(format)).
:- use_module(library(dcgs)).
:- use_module(library(lists)).
:- use_module(library(debug)).
:- use_module(library(when)).
test("condition true before ground/1",(
A = 1,
when(ground(A), Run = true),
Run == true
)).
test("condition true before nonvar/1",(
A = a(_),
when(nonvar(A), Run = true),
Run == true
)).
test("condition true before ','/2",(
A = 1,
B = a(_),
when((ground(A), nonvar(B)), Run = true),
Run == true
)).
test("condition true before (;)/2",(
A = 1,
when((ground(A) ; nonvar(_)), Run1 = true),
Run1 == true,
B = a(_),
when((ground(_) ; nonvar(B)), Run2 = true),
Run2 == true
)).
test("condition true after ground/1",(
when(ground(A), Run = true),
var(Run),
A = 1,
Run == true
)).
test("condition true after nonvar/1",(
when(nonvar(A), Run = true),
var(Run),
A = a(_),
Run == true
)).
test("condition true after ','/2",(
when((ground(A), nonvar(B)), Run = true),
var(Run),
A = 1,
var(Run),
B = a(_),
Run == true
)).
test("condition true after (;)/2",(
when((ground(A) ; nonvar(_)), Run1 = true),
var(Run1),
A = 1,
Run1 == true,
when((ground(_) ; nonvar(B)), Run2 = true),
var(Run2),
B = a(_),
Run2 == true
)).
test("multiple when/2 on same variable",(
when(nonvar(A), Run1 = true),
when(ground(A), Run2 = true),
var(Run1), var(Run2),
A = a(B),
Run1 == true, var(Run2),
B = 1,
Run2 == true
)).
main :-
findall(test(Name, Goal), test(Name, Goal), Tests),
run_tests(Tests, Failed),
show_failed(Failed),
halt.
main_quiet :-
findall(test(Name, Goal), test(Name, Goal), Tests),
run_tests_quiet(Tests, Failed),
( Failed = [] ->
format("All tests passed", [])
; format("Some tests failed", [])
),
halt.
portray_failed_([]) --> [].
portray_failed_([F|Fs]) -->
"\"", F, "\"", "\n", portray_failed_(Fs).
portray_failed([]) --> [].
portray_failed([F|Fs]) -->
"\n", "Failed tests:", "\n", portray_failed_([F|Fs]).
show_failed(Failed) :-
phrase(portray_failed(Failed), F),
format("~s", [F]).
run_tests([], []).
run_tests([test(Name, Goal)|Tests], Failed) :-
format("Running test \"~s\"~n", [Name]),
( call(Goal) ->
Failed = Failed1
; format("Failed test \"~s\"~n", [Name]),
Failed = [Name|Failed1]
),
run_tests(Tests, Failed1).
run_tests_quiet([], []).
run_tests_quiet([test(Name, Goal)|Tests], Failed) :-
( call(Goal) ->
Failed = Failed1
; Failed = [Name|Failed1]
),
run_tests_quiet(Tests, Failed1).
assert_p(A, B) :-
phrase(portray_clause_(A), Portrayed),
phrase((B, ".\n"), Portrayed).
call_residual_goals(Goal, ResidualGoals) :-
call_residue_vars(Goal, Vars),
variables_residual_goals(Vars, ResidualGoals).
variables_residual_goals(Vars, Goals) :-
phrase(variables_residual_goals(Vars), Goals).
variables_residual_goals([]) --> [].
variables_residual_goals([Var|Vars]) -->
dif_:attribute_goals(Var),
variables_residual_goals(Vars).

View File

@@ -1,7 +1,5 @@
:- module('$toplevel', [argv/1, :- module('$toplevel', []).
copy_term/3]).
:- use_module(library(atts), [call_residue_vars/2]).
:- use_module(library(charsio)). :- use_module(library(charsio)).
:- use_module(library(error)). :- use_module(library(error)).
:- use_module(library(files)). :- use_module(library(files)).
@@ -9,11 +7,13 @@
:- use_module(library(lambda)). :- use_module(library(lambda)).
:- use_module(library(lists)). :- use_module(library(lists)).
:- use_module(library(si)). :- use_module(library(si)).
:- use_module(library(os)).
:- use_module(library('$project_atts')). :- use_module(library('$project_atts')).
:- use_module(library('$atts')). :- use_module(library('$atts')).
:- dynamic(disabled_init_file/0). :- dynamic(disabled_init_file/0).
:- dynamic(started/0).
load_scryerrc :- load_scryerrc :-
( '$home_directory'(HomeDir) -> ( '$home_directory'(HomeDir) ->
@@ -26,43 +26,50 @@ load_scryerrc :-
; true ; true
). ).
:- dynamic(argv/1). '$repl' :-
asserta('$toplevel':started),
'$repl'([_|Args0]) :- raw_argv(Args0),
\+ argv(_), ( append(Args1, ["--"|_], Args0) ->
( append(Args1, ["--"|Args2], Args0) ->
asserta('$toplevel':argv(Args2)),
Args = Args1 Args = Args1
; asserta('$toplevel':argv([])), ; Args = Args0
Args = Args0
), ),
delegate_task(Args, []), ( Args = [_|TaskArgs] ->
(\+ disabled_init_file -> load_scryerrc ; true), delegate_task(TaskArgs, [])
repl.
'$repl'(_) :-
( \+ argv(_) -> asserta('$toplevel':argv([]))
; true ; true
), ),
load_scryerrc, (\+ disabled_init_file -> load_scryerrc ; true),
repl. repl.
args_consults_goals([], [], []).
args_consults_goals([Arg|Args], Consults, Goals) :-
arg_consults_goals(Arg, Args, Consults, Goals).
arg_consults_goals(c(Mod), Args, [c(Mod)|Consults], Goals) :-
args_consults_goals(Args, Consults, Goals).
arg_consults_goals(g(Goal), Args, Consults, [g(Goal)|Goals]) :-
args_consults_goals(Args, Consults, Goals).
delegate_task([], []). delegate_task([], []).
delegate_task([], Goals0) :- delegate_task([], Goals0) :-
reverse(Goals0, Goals),
(\+ disabled_init_file -> load_scryerrc ; true), (\+ disabled_init_file -> load_scryerrc ; true),
reverse(Goals0, Goals1),
args_consults_goals(Goals1, Consults, Goals),
run_goals(Consults),
run_goals(Goals), run_goals(Goals),
repl. repl.
delegate_task([Arg0|Args], Goals0) :- delegate_task([Arg0|Args], Goals0) :-
( member(Arg0, ["-h", "--help"]) -> print_help ( ( member(Arg0, ["-h", "--help"]) -> print_help
; member(Arg0, ["-v", "--version"]) -> print_version ; member(Arg0, ["-v", "--version"]) -> print_version
; member(Arg0, ["-g", "--goal"]) -> gather_goal(g, Args, Goals0) ; member(Arg0, ["-g", "--goal"]) -> gather_goal(g, Args, Goals0)
; member(Arg0, ["-f"]) -> disable_init_file ; member(Arg0, ["-f"]) -> disable_init_file
; member(Arg0, ["--no-add-history"]) -> ignore_machine_arg ; member(Arg0, ["--no-add-history"]) -> ignore_machine_arg
),
!,
delegate_task(Args, Goals0)
; atom_chars(Mod, Arg0), ; atom_chars(Mod, Arg0),
catch(consult(Mod), E, print_exception(E)) delegate_task(Args, [c(Mod)|Goals0])
), ).
delegate_task(Args, Goals0).
print_help :- print_help :-
write('Usage: scryer-prolog [OPTIONS] [FILES] [-- ARGUMENTS]'), write('Usage: scryer-prolog [OPTIONS] [FILES] [-- ARGUMENTS]'),
@@ -102,6 +109,7 @@ ignore_machine_arg.
arg_type(g). arg_type(g).
arg_type(t). arg_type(t).
arg_type(c(_)).
arg_type(g(_)). arg_type(g(_)).
arg_type(t(_)). arg_type(t(_)).
@@ -134,10 +142,18 @@ run_goals([g(Gs0)|Goals]) :- !,
write_term(Exception, [double_quotes(DQ)]), nl % halt? write_term(Exception, [double_quotes(DQ)]), nl % halt?
) )
) -> true ) -> true
; write('Warning: initialization failed for: '), ; write('% Warning: initialization failed for: '),
write_term(Goal, [variable_names(VNs),double_quotes(DQ)]), nl write_term(Goal, [variable_names(VNs),double_quotes(DQ)]), nl
), ),
run_goals(Goals). run_goals(Goals).
run_goals([c(Mod)|Goals]) :- !,
( catch(consult(Mod), E, print_exception(E)) ->
true
; write('% Warning: initialization failed for: '),
double_quotes_option(DQ),
write_term(consult(Mod), [double_quotes(DQ)]), nl
),
run_goals(Goals).
run_goals([Goal|_]) :- run_goals([Goal|_]) :-
loader:write_error(error(domain_error(arg_type, Goal), run_goals/1)), loader:write_error(error(domain_error(arg_type, Goal), run_goals/1)),
nl, nl,
@@ -191,7 +207,7 @@ submit_query_and_print_results_(Term, VarList) :-
bb_put('$report_all', false), bb_put('$report_all', false),
bb_put('$report_n_more', 0), bb_put('$report_n_more', 0),
expand_goal(Term, user, Term0), expand_goal(Term, user, Term0),
atts:call_residue_vars(user:Term0, AttrVars), call_residue_vars(user:Term0, AttrVars),
write_eqs_and_read_input(B, VarList, AttrVars), write_eqs_and_read_input(B, VarList, AttrVars),
!. !.
submit_query_and_print_results_(_, _) :- submit_query_and_print_results_(_, _) :-
@@ -314,7 +330,11 @@ write_eqs_and_read_input(B, VarList, AttrVars) :-
% one layer of depth added for (=/2) functor % one layer of depth added for (=/2) functor
'$term_variables_under_max_depth'(OrigVars, 22, Vars0), '$term_variables_under_max_depth'(OrigVars, 22, Vars0),
'$project_atts':project_attributes(Vars0, AttrVars), '$project_atts':project_attributes(Vars0, AttrVars),
copy_term(AttrVars, AttrVars, AttrGoals), % Need to copy all the visible Vars here so that they appear
% properly in AttrGoals, even the non-attributed. Need to also
% copy all the attributed variables here so that anonymous
% attributed variables also appear properly in AttrGoals.
copy_term([Vars0, AttrVars], [Vars0, AttrVars], AttrGoals),
term_variables(AttrGoals, AttrGoalVars), term_variables(AttrGoals, AttrGoalVars),
append([Vars0, AttrGoalVars, AttrVars], Vars), append([Vars0, AttrGoalVars, AttrVars], Vars),
charsio:extend_var_list(Vars, VarList, NewVarList, fabricated), charsio:extend_var_list(Vars, VarList, NewVarList, fabricated),
@@ -452,4 +472,3 @@ print_exception_with_check(E) :-
% is expected to be printed instead. % is expected to be printed instead.
; print_exception(E) ; print_exception(E)
). ).

View File

@@ -194,6 +194,7 @@ pub enum TrailRef {
BlackboardOffset(Atom, HeapCellValue), // key atom, key value BlackboardOffset(Atom, HeapCellValue), // key atom, key value
} }
#[allow(clippy::enum_variant_names)]
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[bits = 6] #[bits = 6]
pub(crate) enum TrailEntryTag { pub(crate) enum TrailEntryTag {
@@ -331,7 +332,7 @@ impl From<ConsPtr> for HeapCellValue {
} }
} }
impl<'a> From<(Number, &mut Arena)> for HeapCellValue { impl From<(Number, &mut Arena)> for HeapCellValue {
#[inline(always)] #[inline(always)]
fn from((n, arena): (Number, &mut Arena)) -> HeapCellValue { fn from((n, arena): (Number, &mut Arena)) -> HeapCellValue {
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
@@ -393,16 +394,16 @@ impl HeapCellValue {
#[inline] #[inline]
pub fn is_ref(self) -> bool { pub fn is_ref(self) -> bool {
match self.get_tag() { matches!(
self.get_tag(),
HeapCellValueTag::Str HeapCellValueTag::Str
| HeapCellValueTag::Lis | HeapCellValueTag::Lis
| HeapCellValueTag::Var | HeapCellValueTag::Var
| HeapCellValueTag::StackVar | HeapCellValueTag::StackVar
| HeapCellValueTag::AttrVar | HeapCellValueTag::AttrVar
| HeapCellValueTag::PStrLoc | HeapCellValueTag::PStrLoc
| HeapCellValueTag::PStrOffset => true, | HeapCellValueTag::PStrOffset
_ => false, )
}
} }
#[inline] #[inline]
@@ -491,7 +492,7 @@ impl HeapCellValue {
#[inline] #[inline]
pub fn get_value(self) -> u64 { pub fn get_value(self) -> u64 {
self.val() as u64 self.val()
} }
#[inline] #[inline]
@@ -739,10 +740,7 @@ impl UntypedArenaPtr {
#[inline] #[inline]
pub fn payload_offset(self) -> *const u8 { pub fn payload_offset(self) -> *const u8 {
unsafe { unsafe { self.get_ptr().add(mem::size_of::<ArenaHeader>()) }
self.get_ptr()
.offset(mem::size_of::<ArenaHeader>() as isize)
}
} }
#[inline] #[inline]
@@ -806,7 +804,7 @@ impl Sub<i64> for HeapCellValue {
| tag @ HeapCellValueTag::PStrLoc | tag @ HeapCellValueTag::PStrLoc
| tag @ HeapCellValueTag::Var | tag @ HeapCellValueTag::Var
| tag @ HeapCellValueTag::AttrVar => { | tag @ HeapCellValueTag::AttrVar => {
HeapCellValue::build_with(tag, self.get_value() + rhs.abs() as u64) HeapCellValue::build_with(tag, self.get_value() + rhs.unsigned_abs())
} }
_ => self, _ => self,
} }

View File

@@ -74,10 +74,7 @@ impl PermVarAllocation {
#[inline] #[inline]
pub(crate) fn pending(&self) -> bool { pub(crate) fn pending(&self) -> bool {
match self { matches!(self, &PermVarAllocation::Pending)
&PermVarAllocation::Pending => true,
_ => false,
}
} }
} }
@@ -96,9 +93,9 @@ pub enum VarAlloc {
impl VarAlloc { impl VarAlloc {
#[inline] #[inline]
pub(crate) fn as_reg_type(&self) -> RegType { pub(crate) fn as_reg_type(&self) -> RegType {
match self { match *self {
&VarAlloc::Temp { temp_reg, .. } => RegType::Temp(temp_reg), VarAlloc::Temp { temp_reg, .. } => RegType::Temp(temp_reg),
&VarAlloc::Perm(r, _) => RegType::Perm(r), VarAlloc::Perm(r, _) => RegType::Perm(r),
} }
} }
@@ -129,7 +126,7 @@ impl TempVarData {
} }
} }
return false; false
} }
pub(crate) fn populate_conflict_set(&mut self) { pub(crate) fn populate_conflict_set(&mut self) {
@@ -201,16 +198,13 @@ impl VariableRecords {
IndexMap::new(); IndexMap::new();
for (var_gen_index, record) in self.0.iter_mut().enumerate() { for (var_gen_index, record) in self.0.iter_mut().enumerate() {
match &mut record.allocation { if let VarAlloc::Temp { temp_var_data, .. } = &mut record.allocation {
VarAlloc::Temp { temp_var_data, .. } => { let use_set = std::mem::replace(
let use_set = std::mem::replace( &mut temp_var_data.use_set,
&mut temp_var_data.use_set, IndexSet::with_hasher(FxBuildHasher::default()),
IndexSet::with_hasher(FxBuildHasher::default()), );
);
use_sets.insert(var_gen_index, use_set); use_sets.insert(var_gen_index, use_set);
}
_ => {}
} }
} }
@@ -219,19 +213,18 @@ impl VariableRecords {
for &(term_loc, reg) in &use_set { for &(term_loc, reg) in &use_set {
if let GenContext::Last(cn_u) = term_loc { if let GenContext::Last(cn_u) = term_loc {
for (var_gen_index, record) in self.0.iter_mut().enumerate() { for (var_gen_index, record) in self.0.iter_mut().enumerate() {
match &mut record.allocation { if let VarAlloc::Temp {
VarAlloc::Temp { term_loc,
term_loc, temp_var_data,
temp_var_data, ..
.. } = &mut record.allocation
} => { {
if cn_u == term_loc.chunk_num() && u != var_gen_index { if cn_u == term_loc.chunk_num()
if !temp_var_data.uses_reg(reg) { && u != var_gen_index
temp_var_data.no_use_set.insert(reg); && !temp_var_data.uses_reg(reg)
} {
} temp_var_data.no_use_set.insert(reg);
} }
_ => {}
} }
} }
} }

View File

@@ -783,10 +783,10 @@ test_217_181_290_317 :-
( op(1105,xfy,'|'), ( op(1105,xfy,'|'),
read_from_chars("(a-->b,c|d).", T0), read_from_chars("(a-->b,c|d).", T0),
writeq_term_to_chars(T0, C0), writeq_term_to_chars(T0, C0),
C0 == "a-->b,c | d", C0 == "a-->b,c|d",
read_from_chars("[(a|b)].", T1), read_from_chars("[(a|b)].", T1),
writeq_term_to_chars(T1, C1), writeq_term_to_chars(T1, C1),
C1 == "[(a | b)]", C1 == "[(a|b)]",
read_from_chars("[a,(b,c)|[]].", T2), read_from_chars("[a,(b,c)|[]].", T2),
writeq_term_to_chars(T2, C2), writeq_term_to_chars(T2, C2),
C2 == "[a,(b,c)]" C2 == "[a,(b,c)]"
@@ -1005,6 +1005,18 @@ test_311 :- test_syntax_error("Finis ().", syntax_error(incomplete_reduction)).
test_318 :- writeq_term_to_chars(+((1*2)^3), C), test_318 :- writeq_term_to_chars(+((1*2)^3), C),
C == "+ (1*2)^3". C == "+ (1*2)^3".
test_320 :- writeq_term_to_chars([a|\+2], C),
C == "[a|\\+2]".
test_321 :- test_syntax_error("writeq((a)(b)).", syntax_error(incomplete_reduction)).
test_324 :- writeq_term_to_chars('%', C),
C == "'%'".
test_325 :- test_syntax_error("writeq({[y}]).", syntax_error(incomplete_reduction)).
test_326 :- test_syntax_error("(>)(1,2).", syntax_error(incomplete_reduction)).
run_tests([Test|Tests]) --> run_tests([Test|Tests]) -->
( { call(Test) } -> ( { call(Test) } ->
[] []

View File

@@ -0,0 +1,5 @@
X = 1.
use_module(library(dif)).
X = 1.
dif(X,1).
halt.

View File

@@ -0,0 +1,4 @@
X = 1.
true.
X = 1.
dif:dif(X,1).

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