276 Commits

Author SHA1 Message Date
Mark Thom
4e7d9f9010 update README.md
Some checks failed
CI / style (push) Has been cancelled
CI / read-msrv (push) Has been cancelled
CI / build-test (--no-default-features --features=all-simple-cross, miri, true, ubuntu-22.04, nightly, s390x-unknown-linux-gnu, --no-run --no-default-features --features=all-simple-cross) (push) Has been cancelled
CI / build-test (--no-default-features, ubuntu-22.04, true, nightly, wasm32-unknown-unknown, --no-run --no-default-features, true) (push) Has been cancelled
CI / build-test (miri, true, ubuntu-22.04, nightly, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (true, macos-latest, true, stable, x86_64-apple-darwin) (push) Has been cancelled
CI / build-test (true, ubuntu-22.04, true, stable, x86_64-unknown-linux-gnu) (push) Has been cancelled
CI / build-test (true, windows-latest, true, stable, x86_64-pc-windows-msvc) (push) Has been cancelled
CI / build-test (ubuntu-22.04, ${{ needs.read-msrv.outputs.msrv }}, 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, true, stable, i686-unknown-linux-gnu) (push) Has been cancelled
CI / logtalk-test (push) Has been cancelled
CI / report (push) Has been cancelled
CI / release (push) Has been cancelled
Docker Publish / build (push) Has been cancelled
2026-07-21 23:51:19 -07:00
Mark Thom
d3edffb7fc update README.md 2026-07-21 23:48:56 -07:00
Mark Thom
295a642034 Merge pull request #3372 from triska/global_cardinality
FIXED: Correct global_cardinality/2 propagation with reified constraints.
2026-06-30 17:25:30 -07:00
Mark Thom
ae0999a3bb Merge pull request #3398 from Skgland/fix-3320
fix incorrect missing feature errors
2026-06-30 17:25:02 -07:00
Mark Thom
11fd39bbaf Merge pull request #3275 from abmclin/Issue-3223-migrate-to-Rust-edition-2024
Issue 3223 migrate to rust edition 2024
2026-06-30 17:22:27 -07:00
Skgland
eb06f19c32 fix incorrect missing feature errors
this was accidentally introduced in mthom/scryer-prolog#3320

fixes mthom/scryer-prolog#3397
2026-06-29 20:13:10 +02:00
Alexander McLin
ea88ca552d Issue 3223: reformat via cargo fmt 2026-06-16 21:18:49 -04:00
Alexander McLin
eabbf67e86 Issue 3223: add more unsafe scopes 2026-06-16 21:14:43 -04:00
Alexander McLin
7a636b0394 Issue 3223: trying again repair for the failing wasm32 build
Removed `mut` modifier completely.
2026-06-16 21:05:12 -04:00
Alexander McLin
71ea74a077 Issue 3223: repair for the failing wasm32 build
Removed `ref` modifier due to not being allowed when implicitly borrowing.
2026-06-16 21:05:12 -04:00
Alexander McLin
091af28368 Issue 3223: reformat via cargo fmt 2026-06-16 21:05:12 -04:00
Alexander McLin
fbc5345c9a Issue 3223: more unsafe scope refinements 2026-06-16 21:05:12 -04:00
Alexander McLin
9d49415449 Issue 3223: reformat via cargo fmt 2026-06-16 21:05:12 -04:00
Alexander McLin
83218bf0da Issue 3223: make unsafe scopes tighter 2026-06-16 21:05:12 -04:00
Alexander McLin
fcd6c3f127 Issue 3223: Second phase of migration to Rust Edition
Reformat via `cargo fmt`
2026-06-16 21:05:12 -04:00
Alexander McLin
efbddeaeee Issue 3223: First phase of migration to Rust Edition 2024
Update cargo dependencies
Apply cargo fix --edition
Change cargo.toml edition property to `2024`

^ Conflicts:
^	Cargo.lock
^	src/ffi.rs

^ Conflicts:
^	src/offset_table.rs
^	src/raw_block.rs
2026-06-16 20:37:52 -04:00
Mark Thom
daaab1cb37 Merge pull request #3331 from adri326/rawblock-safety
Prove safety of RawBlock and support multi-threaded usages
2026-06-11 00:38:04 -07:00
Markus Triska
9b73536cdd FIXED: Correct global_cardinality/2 propagation with reified constraints.
This issue was found by @david-sitsky in a Prolog formulation of an
interesting scheduling task:

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

See also the filed issue:

    https://github.com/mthom/scryer-prolog/issues/3369

Thank you a lot!

This issue needs more analysis to find out what exactly happened here,
whether other cases like it exist, and whether it can be addressed in
a better way. I would greatly appreciate any help!
2026-06-10 20:31:20 +02:00
Mark Thom
e12d3f14c1 Merge pull request #3363 from Skgland/adjust-discussion-3359-test
adjust regression test for discussion 3359
2026-06-05 13:55:21 -07:00
Mark Thom
ffb77473b2 Merge pull request #3364 from Skgland/finish-msrv-bump-cleanup
finish msrv bump cleanup
2026-06-05 13:54:49 -07:00
Skgland
49c0cd3733 remove unecessary use of LayzLock
AtomicBool::new const
2026-06-05 20:47:20 +02:00
Skgland
a236d00d7d fix lint warnings 2026-06-05 20:45:18 +02:00
Skgland
bb5a59170a cleanup after msrv bump 2026-06-05 20:44:38 +02:00
Skgland
2b757de73a adjust regression test for discussion 3359
- incorporate fix from 3a2d57db33 (commitcomment-187528793)
- mark as should panic rather than ignored, this ensures that the test will be updated should this get fixed
2026-06-05 20:29:50 +02:00
Mark Thom
03f796e9ff Merge pull request #3299 from danilp-id/interrupt_test
Test: SIGINT should be able to interrupt non-terminating goals on linux
2026-06-04 13:11:08 -07:00
Danil Platonov
d8be99b6ad fix merge 2026-06-04 12:25:54 -07:00
Danil Platonov
2a04025419 Merge branch 'master' into interrupt_test 2026-06-04 12:08:36 -07:00
Mark Thom
bc14e3aedd Merge pull request #3362 from danilp-id/socket_interrupt
Handle interrupts for socket_server_accept/4
2026-06-04 10:56:09 -07:00
Danil Platonov
bcb9dd0860 handle interrupts for tcp server 2026-06-04 02:41:11 -07:00
Mark Thom
18dd939159 Merge pull request #3352 from danilp-id/http_interrupt
Interrupts support for http server + other improvements
2026-06-03 13:36:21 -07:00
Mark Thom
1cbc95bd06 Merge pull request #3360 from Skgland/fix-discussion-3359
Fix discussion 3359
2026-06-03 13:35:56 -07:00
Skgland
d1e8ea4dd5 ignore added test on i686-unknown-linux-gnu
appears to have been broken even before d50d425099
so has a different cause that needs to be investigated
2026-06-03 22:05:20 +02:00
Skgland
068959bd07 fix discussion 3359 2026-06-03 22:05:20 +02:00
Skgland
3a2d57db33 add regression test for discussion 3359 2026-06-03 22:05:20 +02:00
Mark Thom
3ce16656ee Merge pull request #3357 from Skgland/fix-issue3346
ensure ParallelHeapIter pushes its stack in pairs and fix 3346
2026-06-02 22:19:21 -07:00
Danil Platonov
732f3ae10a use Notify instead of Sender 2026-06-02 00:49:00 -07:00
Skgland
a4993284cd fix parallel heap iter 2026-06-01 23:59:37 +02:00
Skgland
8aeb326e5c use a pair for ParallelHeapIter
to ensure it is pushed popped in pairs
2026-06-01 23:58:17 +02:00
Danil Platonov
304cf424ed unused code for unimplemented shutdown on timeout feature 2026-05-30 15:59:16 -07:00
Danil Platonov
3cd40419eb cargo fmt 2026-05-30 15:56:18 -07:00
Danil Platonov
f8f7b3b7e2 allow to stop and start http server at will with SIGINT, stop the server on uncaught throws, display timing info for each request, use setup_call_cleanup in more places to avoid potential leaks, when handler for a path doesn't have a matching case, throw a detailed error instead of silently continuing 2026-05-30 15:47:50 -07:00
Danil Platonov
39c850b4f4 Test that SIGINT interrupts non-terminating goals on unix.
Cleanup: use tokio::test instead of special test helpers

Added pty_exec.py helper script to run binaries with a pseudoterminal attached (bug won't trigger otherwise).

Tested CI to fail on all unixes (ubuntu+macos) with older rustyline, and pass with new one.
2026-05-30 11:45:57 -07:00
Mark Thom
8b6d68a4cd Merge pull request #3347 from drzv/im7-compat2
showterm.el: use new magick command by default, fallback to convert
2026-05-28 16:34:52 -07:00
Mark Thom
149d3932be Merge pull request #3272 from abmclin/windows_improve_notty_stdin_support
Windows improve notty stdin support to resolve issue 3264
2026-05-26 14:01:23 -06:00
drzv
36cbeb3d6c showterm.el: use new magick command by default, fallback to convert 2026-05-26 11:22:17 -04:00
drzv
875ae5a773 showterm.el: revert #3338 2026-05-26 08:10:24 -04:00
Alexander McLin
70d046499a Issue 3264: rebase and update dependencies 2026-05-25 18:24:24 -04:00
Alexander McLin
b6db1cde25 Issue 3264 add rationale comment & #[allow(...)]
Explain why no multibyte UTF-8 encoding support
Disable `bytes()` clippy warning about performance penalty due to unbuffered bytes
2026-05-25 18:13:02 -04:00
Alexander McLin
257209df75 Issue 3264: refactor supported_terminal
Use better code style
2026-05-25 18:13:02 -04:00
Alexander McLin
d5d514ec96 Issue 3264: reformat to conform to style guidelines 2026-05-25 18:13:01 -04:00
Alexander McLin
533594b6b6 Fixes issue 3264 by adding a fallback read mode when reading from non-tty stdin on Windows 2026-05-25 18:13:01 -04:00
Mark Thom
43b6afcdf3 Merge pull request #3342 from Skgland/pdl-pairs
ensure pdl is pushed/popped in pairs and reuse tabu_list allocation by moving it from a local variable into the machine state
2026-05-25 15:10:02 -06:00
Mark Thom
2c23f84808 Merge pull request #3310 from Skgland/process_wait-release
add `release(Bool)` option to `process_wait/3`
2026-05-25 15:08:37 -06:00
Mark Thom
ad13aa3801 Merge pull request #3320 from Skgland/all-pure-rust
add a feature for enabling all features that don't pull in non pure rust dependencies
2026-05-25 14:36:04 -06:00
Mark Thom
79597b63bf Merge pull request #3317 from Skgland/optimize-put_back_char
optimize put_back_char
2026-05-25 14:03:31 -06:00
Skgland
bdba7ba0f5 merge crypto-impure feature into feature crypto-full 2026-05-25 19:07:20 +02:00
Skgland
6c7d5e8278 more spelling fixes 2026-05-25 16:39:37 +02:00
Skgland
61ebcdb56e fix spelling 2026-05-24 23:55:19 +02:00
Skgland
f2195c2362 cleanup
- fix some warnings and mark others as expected
- make use of the msrv bump to 1.93.1 and cleanup compat and workarounds
2026-05-24 23:29:23 +02:00
Skgland
6150ca90d3 check individual features in CI 2026-05-24 23:00:27 +02:00
Skgland
938f588769 make crypto depending on ring optional 2026-05-24 23:00:27 +02:00
Mark Thom
61d94dceae Merge pull request #3290 from abmclin/bump-rustyline-version-18
bump `Rust` version to 1.93.1 and bump `rustyline` to version 18.0.0
2026-05-24 13:27:00 -06:00
Mark Thom
1411417a44 Merge pull request #3338 from drzv/master
Use make-process to call imagemagick in showterm.el
2026-05-24 13:22:33 -06:00
Mark Thom
609514b4b8 Merge pull request #3312 from Skgland/big-endian-miri
run miri for a big-endian target
2026-05-24 13:22:00 -06:00
Skgland
1fd9246b16 configure linker via env var insread of .cargo/config.toml
someone might already have a .cargo/config.toml
2026-05-24 19:08:23 +02:00
Skgland
19974e580b fix missing description
Github documentation states that it is a required field and my linter complained

https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#description
2026-05-24 19:02:23 +02:00
Skgland
550efb8416 add a feature for things that are simple to cross-compile 2026-05-24 10:39:20 +02:00
Skgland
e0c491e9f3 fix miri not being passed the target 2026-05-24 10:39:20 +02:00
Skgland
f16850e5f8 fix ci and setup cross-compilation 2026-05-24 10:39:20 +02:00
Skgland
ca0d4e9aac add miri CI for a big endian target 2026-05-24 10:39:20 +02:00
Skgland
6e50268efb fix spelling again 2026-05-24 10:30:30 +02:00
Skgland
54b3034905 remove out-dated comment and fix spelling/grammar 2026-05-24 10:25:24 +02:00
Mark Thom
cf9e667b50 Merge pull request #3311 from Skgland/cleanup2
some cleanup and fixes
2026-05-23 18:27:50 -06:00
Mark Thom
3f0b471227 Merge pull request #3333 from 0nkery/lazy-lock
replace lazy_static macro with std::sync::LazyLock
2026-05-23 09:51:22 -06:00
Mark Thom
bfca60aa86 Merge pull request #3345 from triska/meetups
link to Scryer Prolog Meetup 2026 page and its announcement
2026-05-23 09:51:06 -06:00
Skgland
6b9a291f4e fix spelling 2026-05-23 11:28:45 +02:00
Markus Triska
777637001f link to Scryer Prolog Meetup 2026 page and its announcement 2026-05-23 10:17:11 +02:00
Mark Thom
9fcd7776a9 Merge pull request #3336 from 0nkery/fix-between-doc
fix var mode in between/3 documentation
2026-05-22 23:04:05 -06:00
Mark Thom
4f166f50a8 Merge pull request #3334 from Skgland/stable-order
use BTree{Map,Set} to ensure a stable order based on the key/entry
2026-05-22 22:56:13 -06:00
Mark Thom
1547839c33 Merge pull request #3328 from Skgland/update-libffi
upgrade libffi dependency
2026-05-22 22:53:49 -06:00
Mark Thom
5a1423d0f0 Merge pull request #3316 from josd/master
Improve CharReader buffering
2026-05-22 22:52:27 -06:00
Mark Thom
3faa838ee2 Merge pull request #3323 from triska/VATmiraal
add link to VATmiraal, https://vatmiraal.be
2026-05-22 22:45:39 -06:00
Skgland
e4792429d3 reuse tabu list 2026-05-17 20:50:37 +02:00
Skgland
d50d425099 ensure pdl is pushed/popped in pairs 2026-05-17 19:48:52 +02:00
drzv
0df5f7e576 Call ImageMagick with make-process. Redirect stderr to separate buffer 2026-05-14 19:10:05 +00:00
Dmitrii Shlagov
b9f1443eb0 fix var mode in between/3 documentation 2026-05-14 16:25:33 +02:00
Skgland
ac18aed5a7 use BTree{Map,Set} to ensure a stable order based on the key/entry even if filesystem enumeration order changes
this should help with reproducibility
2026-05-14 01:02:19 +02:00
Dmitrii Shlagov
2c60e4183f roll back gitignore changes 2026-05-12 17:15:28 +02:00
Dmitry Shlagoff
2bbb6487ec remove redundant closure 2026-05-12 16:12:33 +02:00
Dmitry Shlagoff
095fa1227a replace lazy_static macro with std::sync::LazyLock 2026-05-12 15:40:38 +02:00
Emilie Burgun
f395d554a5 RawBlock: finish proof of safety and defragment after growing
After calling `grow()`, the new head would jump to `old_capacity` rather
than staying to the same offset.

In practice this only loses a few bytes at most.
2026-05-10 22:36:11 +02:00
Emilie Burgun
ad614b684c RawBlock: switch to Cell or AtomicPtr instead of UnsafeCell
This is the first step towards enabling multithreading on AtomTable.

For now RawBlock will default to using Cell, which yields a byte-
equivalent compiled output.

Also adds an `atomic` feature, which, when enabled, will make RawBlock
use an AtomicPtr instead, ensuring that it implements `Sync`.
2026-05-10 22:36:11 +02:00
Emilie Burgun
898b6b2b25 RawBlock: seal base and add Stack::index_dangling_or_frame
Direct accesses to `base` are replaced with dedicated methods with
explicit safety requirements.
2026-05-10 22:36:11 +02:00
Emilie Burgun
3c5818a040 RawBlock: seal most fields, replace top with a capacity field 2026-05-10 22:24:57 +02:00
Skgland
0c313a0849 upgrade libffi dependency 2026-05-08 22:36:41 +02:00
Alexander McLin
7c9075b4ee Bump Cargo.toml rust-version to 1.93.1
MSRV discussion settled on `1.93.1` to align with upcoming Debian stable releases
2026-05-05 09:58:05 -04:00
Markus Triska
4c14cbe39a explanation of product
See posting by @constraintAutomaton at:

    https://github.com/mthom/scryer-prolog/pull/3323#issuecomment-4365798844

Many thanks!
2026-05-03 11:00:38 +02:00
Markus Triska
afe744aa57 add link to VATmiraal, https://vatmiraal.be 2026-05-03 09:52:28 +02:00
Skgland
89dde3f370 un-ignore lorem_ipsum tests as they are no longer slow 2026-04-30 19:54:39 +02:00
Skgland
f8ddd78776 optimize CharReader 2026-04-30 19:46:40 +02:00
Skgland
327a677a7c use a function rather than a closure 2026-04-30 19:02:27 +02:00
Skgland
48a34aa2cc add a test with bad utf-8 2026-04-30 19:01:34 +02:00
Skgland
af54fb2aba optimize put_back_char
Always encode the char directly into the buffer.
Only shift the buffer content if there isn't enough room in the front.
2026-04-26 14:13:55 +02:00
josd
b2dad66270 Improve CharReader buffering 2026-04-26 13:42:41 +02:00
Skgland
eac7ff680c fix ArenaPtr payload offset logic
the old logic would be incorrect if the payload has higher alignment than the ArenaHeader i.e. when there is padding between the ArenaHeader and the Payload
2026-04-25 18:12:32 +02:00
Skgland
b4db85c8c3 don't erase ptr type early when construction a Cons HeapCellValue
rather than passing an address as usize pass the ArenaHeader pointer
similarly don't return a u8 ptr but use a ArenaHeader pointer instead

Don't convert the pointer to a ConsPtr by going through native endian
bytes in between.
We are exploiting the fact that the 3 least significant bytes are zero
for pointer to types of alignment 8 and we expect these to line up with
the f, m, and tag field at the end of the ConsPtr struct, but using
native endiannes for this would only work on big endian systems.
2026-04-25 18:12:31 +02:00
Skgland
6765921666 fix unused import warning on windows 2026-04-25 16:19:58 +02:00
Skgland
ff293a56e6 fix large enum variant size difference warning of PermVarAllocation
by wrapping BranchNumber in an Arc.
PermVarAllocation::Done had size 208 and is now down to 32.
A Box rather than an Arc would be smaller, but it looks like BranchNumber/BranchDesignator are clones a bunch so I expect it to be beneficial to reduce allocations both of the Box itself as well as its content.
2026-04-25 16:19:53 +02:00
Skgland
ab2b652c84 fix linux tests on 1.85
old rust versions didn't print the pid
2026-04-25 15:19:41 +02:00
Skgland
83add732e0 adjust test expectations 2026-04-25 14:43:52 +02:00
Skgland
f8c4679850 add process_wait/3 release(Bool) option
make process_wait/3 release the Process by default
2026-04-25 14:35:20 +02:00
Skgland
3da2b8fba2 add tests 2026-04-25 14:35:07 +02:00
Skgland
f7dc1d72f6 remove unused HeapCellValueView
it is identical to HeapCellValueTag
2026-04-22 22:16:36 +02:00
Mark Thom
8dffd72db5 replace tabs with spaces 2026-04-21 16:22:38 -07:00
Mark Thom
703a6dddd1 dereference compare_term_test args down from stack (#3286, #3295) 2026-04-21 16:02:55 -07:00
Mark Thom
5ea262e1d3 replace tabs with spaces 2026-04-21 15:02:14 -07:00
Mark Thom
010b00f349 take Lis index from rd in copy_var (#3298) 2026-04-21 14:31:53 -07:00
Mark Thom
974722bc1c Merge pull request #3269 from no382001/quads
fix library paths and module qualification in quadtests.pl
2026-04-18 18:20:03 -06:00
Mark Thom
695389bda1 Merge pull request #3293 from danilp-id/http_server_test
http_server test + unignore http_open_hanging
2026-04-18 18:17:18 -06:00
Mark Thom
9cf0d8b484 Merge pull request #3274 from no382001/issue3262
fix read/1 on non-TTY stdin blocking until newline
2026-04-18 18:16:53 -06:00
Mark Thom
381b4f2857 Merge pull request #3291 from Skgland/update-actions
Update actions
2026-04-18 18:16:00 -06:00
Danil Platonov
35aa4d9048 cargo fmt 2026-04-12 19:32:40 -07:00
Danil Platonov
8e2dcd905b pass scryer path through stream instead of env var 2026-04-12 19:29:39 -07:00
Danil Platonov
081f3c0827 http server test, and also unignore http client test 2026-04-11 08:59:01 -07:00
Skgland
8bbb484f94 update pyTooling/Actions/with-post-step@v4.1.0 -> pyTooling/Actions/with-post-step@v7.7.0 2026-04-11 00:06:13 +02:00
Skgland
717c3c2db8 update actions/cache@v4.3.0 -> actions/cache@v5.0.4 2026-04-10 23:59:59 +02:00
Skgland
0aa6abe517 pin 3rd-party actions
following githubs recommendations[^3rd-p-a] to use full-length commit SHA for 3rd-party actions

setup dependabot to help keep dependencies up-to-date

[^3rd-p-a]: https://docs.github.com/en/actions/reference/security/secure-use#using-third-party-actions
2026-04-10 23:52:43 +02:00
Skgland
4a9f690c16 update docker/build-push-action@v6 -> docker/build-push-action@v7 2026-04-10 23:22:38 +02:00
Skgland
e3d020ea74 update docker/metadata-action@v5 -> docker/metadata-action@v6 2026-04-10 23:22:38 +02:00
Skgland
82378c6338 update docker/login-action@v3 -> docker/login-action@v4 2026-04-10 23:22:38 +02:00
Skgland
f456c98c5b update docker/setup-buildx-action@v3 -> docker/setup-buildx-action@v4 2026-04-10 23:22:37 +02:00
Skgland
4db325e4b8 update actions/download-artifact@v4 -> actions/download-artifact@v8 2026-04-10 23:22:37 +02:00
Skgland
8f33eaf7d1 update actions/upload-artifact@v4 -> actions/upload-artifact@v7 2026-04-10 23:22:37 +02:00
Skgland
eba16a2213 update actions/checkout@v4 -> actions/checkout@v6 2026-04-10 23:22:37 +02:00
Mark Thom
87f97c7e5c Merge pull request #3276 from Skgland/cleanup
remove unused file
2026-04-08 22:18:32 -06:00
Alexander McLin
2fec440ba6 bump rustyline to version 18.0.0
Fixes regression discovered on Linux after upgrading `rustyline` to 17.0.2; `SIGINT` not handled as expected, breaking ability to interrupt nontermination in `scryer`.

This was due to a change in `rustyline`'s `SIGINT` behavior since version 16 and corrected in version 18.
2026-04-05 20:43:38 -04:00
Mark Thom
3e2f3ecfdb Merge pull request #3273 from no382001/issue2914
fix panic in current_prolog_flag/2 when arguments are the same variable
2026-04-04 13:06:11 -06:00
Mark Thom
2b6b11e6be Merge pull request #3271 from abmclin/update_rustyline
Resolves issue 2694 by updating rustyline to 17.0.2
2026-04-04 12:28:35 -06:00
Mark Thom
7da2ba2015 Merge pull request #3266 from Skgland/perf-issue-3265
perf improvements for 3265
2026-04-04 12:02:29 -06:00
Skgland
bb9c1cf5a2 remove unused file 2026-04-03 00:16:59 +02:00
no382001
28f8da5ce9 restructure based on suggestion
Co-authored-by: Bennet Bleßmann <3877590+Skgland@users.noreply.github.com>
2026-03-31 21:14:54 +02:00
no382001
c065b52778 fix read/1 on non-TTY stdin blocking until newline #3262 2026-03-31 15:52:27 +02:00
no382001
6a10c6d6a5 fix panic in current_prolog_flag/2 when arguments are the same variable 2026-03-31 12:01:00 +02:00
Alexander McLin
0623e8e7df Resolves issue 2694 by updating rustyline to 17.0.2 2026-03-29 14:49:51 -04:00
Skgland
3c9c7ade8d use checked_add to prevent silent overflow 2026-03-27 22:00:54 +01:00
no382001
13c85c74db fix library paths and module qualification in quadtests.pl 2026-03-27 21:27:15 +01:00
Skgland
5fc21241d6 hint to the compiler/branch-predictor that
we expect p to be in bound

if we are suffiently certain that p can never be oob
unsafe get_unchecked could be used to
eliminate bounds checking all together
which should improve performance further.
2026-03-21 19:01:52 +01:00
Skgland
833c3bd77f run rustfmt 2026-03-21 14:24:13 +01:00
Skgland
153f04b72a adjust interrupt handling
we used to spend ~5.8% of the time on getting the next value of the 0..INSTRUCTIONS_PER_INTERRUPT_POLL iterator

increment on Wrapping<u8> now only takes ~0.58 of the time
2026-03-21 14:20:11 +01:00
Skgland
8aab887388 switch from Integer to u128
- u128 is only 16 bytes instead of 24
- incrementing u128 does not involve heap allocations
- u128 should be sufficent
 it would take more than 2 sextilion years to overflow if we would be incrementing it every tick at 5GHz
   i.e. 2^128-1 / 5 GHz / 60 / 60 / 24 / 356 > 2 sextilion

before this ~3.2% of the execution time of the program in https://github.com/mthom/scryer-prolog/issues/3265#issuecomment-4103176469 was spend in the increment_call_count function, after this change it's down to 0.3%
2026-03-21 13:36:40 +01:00
Mark Thom
79a9b950cb Merge pull request #3243 from danilp-id/master
fix http stream not being closed fully
2026-03-19 23:36:58 -07:00
Mark Thom
fba2854f21 Merge pull request #3257 from triska/include
ISO: Implement the include/1 directive
2026-03-17 23:58:04 -07:00
Markus Triska
1c9fd1501a retain error context (load/1) when using :- D. 2026-03-16 07:36:55 +01:00
Mark Thom
b0b89a0aca Merge pull request #3258 from no382001/fix_load_xml/3
load_xml was returning a single term instead of a list of nodes
2026-03-14 22:46:03 -07:00
no382001
025da8315b load_xml was returning a single term instead of a list of nodes 2026-03-10 12:20:34 +01:00
Mark Thom
8002b9a78b Merge pull request #3234 from fusiongyro/patch-1
Fixed a typo in the documentation for atom_codes/2
2026-03-09 22:09:40 -07:00
Mark Thom
02f86587b5 Merge pull request #3232 from triska/not_si
ADDED: not_si/1
2026-03-09 22:09:09 -07:00
Mark Thom
4d144ea819 Merge pull request #3252 from Skgland/towards-a-crate-without-syn@1
replace custom modular_bitfields fork with update upstream
2026-03-09 22:08:28 -07:00
Mark Thom
02c927bb83 Merge pull request #3241 from Skgland/parse-error-improvements
change `ParserError` type
2026-03-09 22:07:03 -07:00
Mark Thom
4a8388a6e9 Merge pull request #3189 from Skgland/fix-function_casts_as_integer
fix `function_casts_as_integer` warning
2026-03-09 22:06:33 -07:00
Mark Thom
7bf014cdf5 Merge pull request #3260 from thierrymarianne/dealing-with-invalid-utf8-string
handle invalid UTF-8 string
2026-03-09 20:56:04 -07:00
Markus Triska
b41563e976 ENHANCED: support quads in included files
Suggested by @dcnorris, many thanks!

    https://github.com/mthom/scryer-prolog/issues/634#issuecomment-4003451420
2026-03-07 09:37:30 +01:00
Thierry Marianne
ead2fccdbb add documentation
Signed-off-by: Thierry Marianne <thierry@marianne.io>
2026-03-05 09:03:31 +01:00
Thierry Marianne
817c584993 revise indentation of raw string
Signed-off-by: Thierry Marianne <thierry@marianne.io>
2026-03-04 15:06:27 +01:00
Thierry M.
e00112b92f use C-string literal
Co-authored-by: Bennet Bleßmann <3877590+Skgland@users.noreply.github.com>
2026-03-04 15:01:23 +01:00
Thierry Marianne
4bd0ff0fcb cargo fmt fixes
Signed-off-by: Thierry Marianne <thierry@marianne.io>
2026-03-04 14:04:47 +01:00
Thierry Marianne
ed59aa05b0 apply suggestion
Signed-off-by: Thierry Marianne <thierry@marianne.io>
2026-03-04 14:01:17 +01:00
Thierry Marianne
16ed1040d2 move test to tests/scryer/ffi.rs
Signed-off-by: Thierry Marianne <thierry@marianne.io>
2026-03-04 14:01:17 +01:00
Thierry M.
fa21ff4813 use CStr .to_str() method
Co-authored-by: Bennet Bleßmann <3877590+Skgland@users.noreply.github.com>
2026-03-04 14:00:44 +01:00
Thierry M.
891f7fc892 use C-string literal
Co-authored-by: Bennet Bleßmann <3877590+Skgland@users.noreply.github.com>
2026-03-04 13:59:53 +01:00
Thierry Marianne
fae9ca1bb9 fix utf-8 panic
Signed-off-by: Thierry Marianne <thierry@marianne.io>
2026-03-04 12:45:58 +01:00
Thierry Marianne
ac71cff338 add failing test highlighting panic on invalid UTF-8 cstr
Signed-off-by: Thierry Marianne <thierry@marianne.io>
2026-03-04 12:45:48 +01:00
Markus Triska
8f39bee463 ISO: Implement the include/1 directive
Quoting from the standard:

    7.4.2.7 include/1

    If F is an implementation defined ground term designating
    a Prolog text unit, then Prolog text P1 which contains
    a directive include(F) is identical to a Prolog text P2
    obtained by replacing the directive include(F) in P1 by
    the Prolog text denoted by F.

Example:

    :- include("hello.pl").

This addresses #583 and #634.
2026-02-28 14:49:13 +01:00
Skgland
2dc904aa73 run apt update before apt install
https://github.com/orgs/community/discussions/145882#discussioncomment-11447924
2026-02-20 23:00:45 +01:00
Skgland
0ceab41b39 fix warning in CI about using apt in a script 2026-02-20 22:57:44 +01:00
Skgland
2a79d6fc40 run cargo fmt 2026-02-20 22:52:25 +01:00
Skgland
49e49b425d fix clippy lint warning 2026-02-20 22:47:08 +01:00
Skgland
7d0d61d789 remove no longer necessary allows 2026-02-20 22:45:09 +01:00
Skgland
5ef5a3f30a replace custom modular_bitfields fork with update upstream
part of #2468
2026-02-20 22:40:33 +01:00
Danil Platonov
337d9c5583 move drop to impl Drop 2026-01-26 04:57:28 -08:00
Danil Platonov
26523139a8 fix http stream not being closed fully 2026-01-25 03:16:37 -08:00
Skgland
65aadb3b88 reduce visibility to pub(crate) to prevent accidentally exposing 2026-01-24 04:20:13 +01:00
Skgland
dfad71bc6e fix some lint warnings 2026-01-24 04:13:26 +01:00
Skgland
d8c6fa1fe5 don't expose lexical error and provide location 2026-01-24 04:08:41 +01:00
Skgland
18b476af28 make ParserError a struct with an enum kind field 2026-01-24 04:08:40 +01:00
Skgland
bd233fedfd add location to InvalidSingleQuotedCharacter 2026-01-24 04:08:40 +01:00
Skgland
083546442d make match exhaustive 2026-01-24 04:08:40 +01:00
Skgland
3343188756 replace pairs of usize with location struct 2026-01-24 04:08:40 +01:00
Daniel K Lyons
ed36aa7313 Fixed a typo in the documentation for atom_codes/2 2026-01-20 09:54:18 -07:00
Markus Triska
0dc94ecc0b ADDED: not_si/1
For a recent example that would benefit from this, see "Prolog Basics
Explained with Pokémon" by @alexpetros:

    https://unplannedobsolescence.com/blog/prolog-basics-pokemon/

Thank you a lot!
2026-01-18 09:58:31 +01:00
Mark Thom
453a88f03e Merge pull request #3211 from mthom/setof_correction_and_opt
Correct and optimize setof/3 and bagof/3
2026-01-14 22:07:10 -07:00
Mark Thom
c79fd74331 remove unnecessary extra work in findall_with_existential/5 2026-01-14 20:39:48 -08:00
Mark Thom
e446b13376 remove hashbrown crate 2026-01-14 20:39:48 -08:00
Mark Thom
a83f412251 replace sort_without_dedup/2 with keysort/2 2026-01-14 20:39:48 -08:00
Mark Thom
1a8c4f9b03 cargo fmt fixes 2026-01-14 20:39:48 -08:00
Mark Thom
c2e1ded852 remove variant_hashing.rs and related instructions 2026-01-14 20:39:48 -08:00
Mark Thom
70220687f4 find variant terms using just sort/2 and (==)/2 2026-01-14 20:39:48 -08:00
Mark Thom
69a367d1c9 do not retain attributes in solutions of findall (#3020) 2026-01-14 20:39:48 -08:00
Mark Thom
e2bdf59c81 fix cargo fmt 2026-01-14 20:39:48 -08:00
Mark Thom
6284aa3a3f add variant_hash and is_non_variant to fix setof/3, bagof/3 2026-01-14 20:39:48 -08:00
Mark Thom
29cd80510b replace compare_term_test with parallel iterator, add is_not_variant 2026-01-14 20:39:45 -08:00
Mark Thom
9089f9ddb4 use branch numbers to detect branch subsumption 2026-01-14 20:34:06 -08:00
Mark Thom
16dc10ee96 assert rational(3) as true in tests/builtins.pl 2026-01-14 20:34:06 -08:00
Mark Thom
4e8f338870 Merge pull request #3222 from Skgland/reduce-instructions_template
move static parts of instructions template directly into instructions module, fix clippy and typos
2026-01-12 23:22:44 -07:00
Skgland
b14e363d5e run rustfmt to fix formatting 2026-01-10 18:31:40 +01:00
Bennet Bleßmann
87f89545bb restore accidentally removed comment 2026-01-10 18:30:09 +01:00
Skgland
5064760b1e fix spelling 2026-01-10 18:13:59 +01:00
Skgland
eca4262be6 fix clippy lints 2026-01-10 17:52:34 +01:00
Skgland
73c26bed2a extaract the static part of the instructions template directly into the instructions module
This way goto source doesn't end up in a generated file for those parts and they can be edited directly.
I have way too often accidentally edited the generated file.
2026-01-10 17:48:07 +01:00
Mark Thom
172b6a6f8f Merge pull request #3207 from Skgland/quote-fix-unquote-segv
"fix" segv from #3198, #3199, and #3205
2025-12-17 14:11:21 -07:00
Skgland
129cca53ed throw error(resource_error(memory), []) when the list length exceeds usize. 2025-12-10 00:04:24 +01:00
Mark Thom
cd1ed106ed Merge pull request #3177 from triska/use_phrase_to_file
use more recently available phrase_[to|from]_file/3 in example
2025-12-08 19:23:52 -07:00
Skgland
00aba96185 remove overengineered RecursionGuard 2025-12-08 22:27:24 +01:00
Skgland
616f071cd9 fix another integer overflow 2025-12-08 19:59:43 +01:00
Skgland
580572aec6 don't calculate panic message eagerly 2025-12-07 23:06:09 +01:00
Skgland
47e908bf76 handle overflown in Heap::with_cell_capacity 2025-12-07 22:59:00 +01:00
Skgland
11901b5fde handle oob heap index calculation 2025-12-07 21:45:13 +01:00
Skgland
d63b0a192c use checked multiplication in heap_index! macro 2025-12-07 21:31:24 +01:00
Skgland
2fc08dde1e prevent multiply with overflow resulting in odd errors/segv 2025-12-07 21:27:24 +01:00
Skgland
193cfabc66 detect recursive throw_resource_error and panic instead of causing a segmentation fault 2025-12-07 21:08:17 +01:00
Skgland
7c3ffd0e87 deny function_casts_as_integer warnings 2025-12-04 23:35:35 +01:00
Skgland
797a8f8611 fix function_casts_as_integer warning
Comparing addresses of function pointers is brittle.

Functions may be duplicated resulting in function pointers to the same function to compare !=.
Functions may be merged/de-duplicated resulting in function pointers to different function to compare ==.

The later shouldn't be relevant here as the function differ in behavior, but mentioning it for completeness.
2025-12-04 23:35:23 +01:00
Mark Thom
f570e566f8 fix backtrack_on_resource_error around choice instructions 2025-12-03 18:13:00 -08:00
Mark Thom
96a9dfcc1b Merge pull request #3185 from mthom/install_verify_attr_opt
Optimize `verify_attr` by removing the need to scan instructions
2025-12-03 19:10:46 -07:00
Mark Thom
6fdcd17f32 Merge pull request #3164 from Skgland/alloc_errors2
handle machine heap/stack allocation error
2025-12-03 19:10:22 -07:00
Mark Thom
652e824e59 Merge pull request #3147 from jjtolton/error-termination-flag
Add -t flag for custom toplevel (replaces --halt-on-error)
2025-12-02 00:24:46 -07:00
Mark Thom
f595eaee2e Merge pull request #3179 from triska/meetups
update meetups
2025-12-01 23:55:46 -07:00
Mark Thom
9ddeeed683 Merge pull request #3180 from triska/foldl_6
ADDED: foldl/6, addressing #2973
2025-12-01 23:55:34 -07:00
Mark Thom
b287f0aedb corrections to arithmetic expression compilation 2025-11-27 21:03:56 -08:00
Mark Thom
3b019fca1d corrections to dispatch loop 2025-11-27 21:03:22 -08:00
Mark Thom
4bd16b8a2e revise InstallVerifyAttrs to remove need for predicate scanning (#3175) 2025-11-25 23:17:51 -08:00
Mark Thom
f2e044e5b8 remove interms field from MachineState 2025-11-24 22:19:36 -08:00
Markus Triska
1cd55d8131 ADDED: foldl/6, addressing #2973 2025-11-23 11:13:57 +01:00
Markus Triska
14265118f1 update meetups 2025-11-23 10:29:31 +01:00
Markus Triska
b66824ffce use more recently available phrase_[to|from]_file/3 in example 2025-11-23 09:42:14 +01:00
Skgland
6ec8c9064f fix pstr_iter_tests test 2025-11-20 23:08:46 +01:00
Skgland
d4f2f7ba2b remove RESOURCE_ERROR_OFFSET_INIT
With it when using multiple Machine in one process only the first would store the pre-allocated error.
Instead Heap.resource_err_loc is now Option<NonZero<usize>> instead of usize using None for uninitialized.
The cell at index 0 should alredy be used by a runtime reserved interstitial cell that is allocated prior. So requiring the offset to be non zero should be fine.
2025-11-20 21:44:29 +01:00
Skgland
a05dc79505 fix unecessary parens in macro 2025-11-20 21:24:47 +01:00
Skgland
e90c813528 return an AllocError instead of panicing in Heap::with_cell_capacity 2025-11-20 21:13:17 +01:00
Skgland
516848e214 wrap resource_error into an error/2 functor and don't wrap it into a syntax_error functor 2025-11-20 01:22:18 +01:00
Skgland
6063783e9d propagate AllocErrro instead of unwrapping 2025-11-20 00:19:24 +01:00
Skgland
e9e97fe82a handle machine heap/stack allocation error 2025-11-19 22:17:24 +01:00
Skgland
5686b69013 stub a clippy config
with things we need to get rid of replace if we want to eliminate panics due to oom
2025-11-19 19:23:42 +01:00
Skgland
248b05c992 use as_bytes().to_vec() instead of bytes().collect()
copying a slice into a vec should be easier to optimize by the complier than collecting a byte iteration into a vec
2025-11-19 19:23:41 +01:00
Skgland
a283c8bdc2 remove some uncessary allocations/copies 2025-11-19 19:23:41 +01:00
Skgland
6b5ec3fab8 ignore incompatible msrv in compat module 2025-11-19 19:23:41 +01:00
Skgland
a0b4162d7a improve count_to_letter_code
- reserve the complete required length at the beginning to reduce reallocations
- use u8 instead of char so that we can re-use the allocation for the string
2025-11-19 19:23:41 +01:00
Skgland
119761ad10 ignore unused_parens warning
parens are generated by external macro
2025-11-18 21:42:00 +01:00
Skgland
bf8651db29 remove two unecessary clones 2025-11-18 21:29:22 +01:00
J.J. Tolton
11d0215963 Fix -t flag help text to reflect it accepts any goal
The -t flag is not limited to arity 0 predicates - it accepts any
goal including goals with arguments (e.g., -t 'halt(1)'). Updated
the help text to remove the incorrect "(arity 0 predicate)" constraint.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 10:55:49 -05:00
J.J. Tolton
16fd7a8d51 Move -t and -g flags before filenames in tests
Per maintainer feedback, switches must come before files to follow
the convention: switches before files are Scryer-specific, switches
after files are application-specific.
2025-11-09 13:04:59 -05:00
J.J. Tolton
5357ecf8e3 Remove redundant unit test file
The custom_toplevel.pl unit tests were trivial and didn't actually test
the functionality. All real testing is done via comprehensive CLI tests
in tests/scryer/cli/src_tests/custom_toplevel.md
2025-11-09 12:56:47 -05:00
J.J. Tolton
e7c288f8f0 Move g_caused_exception/2 dynamic directive to toplevel.pl
- Add dynamic directive in toplevel.pl with other module-level directives
- Update test files to reference it as '':g_caused_exception/2
- Remove redundant dynamic directives from test files
- All tests passing
2025-11-09 12:44:48 -05:00
J.J. Tolton
95abc4017e Update tests to use format/2 instead of write/1 2025-11-09 12:44:48 -05:00
J.J. Tolton
b91ce76052 Add comprehensive tests for g_caused_exception/2
Following TESTING_GUIDE.md, added tests at layers 2 and 3:

Layer 2 - Prolog Integration Tests (src/tests/custom_toplevel.pl):
- Test that g_caused_exception/2 is not asserted when no exception occurs
- Test that g_caused_exception/2 can be checked from custom toplevel
- Added check_for_exception/0 helper predicate for testing

Layer 3 - CLI Tests (tests/scryer/cli/src_tests/custom_toplevel.md):
- Test g_caused_exception/2 with exception thrown
- Test g_caused_exception/2 with no exception
- Test g_caused_exception/2 with error/2 terms
- Added test helper predicates in fixtures/toplevel_test_helper.pl

All tests pass successfully.

Co-Authored-By: J.J.'s Robot <jjtolton@gmail.com>
2025-11-09 12:44:48 -05:00
J.J. Tolton
617a551a56 Add g_caused_exception/2 for custom toplevel error handling
When a goal throws an exception during initialization (-g flag), the
system now asserts g_caused_exception(Goal, Exception) in the user
module. This allows custom toplevels (-t flag) to check if an error
occurred and handle it appropriately.

Example usage:
  scryer-prolog -g "throw(error)" -t check_error

Where check_error can be:
  :- dynamic(g_caused_exception/2).

  check_error :-
      (   g_caused_exception(_, E) ->
          write('Error: '), write(E), nl, halt(1)
      ;   halt(0)
      ).

This enables scripts to use custom toplevels for sophisticated error
handling and exit code logic.

Addresses: https://github.com/mthom/scryer-prolog/pull/3147#issuecomment-3503875719

Co-Authored-By: J.J.'s Robot <jjtolton@gmail.com>
2025-11-09 12:44:48 -05:00
J.J. Tolton
31247545c3 Fix bug where -t argument was processed as filename
Fixed issue where `scryer-prolog -t halt` would try to load "halt.pl"
as a file instead of just using halt as the custom toplevel.

The bug was caused by an extra clause `delegate_task([], []).` that
would return control to the calling context instead of continuing to
start_toplevel. This caused the argument processing in delegate_task
to continue and treat the already-consumed toplevel argument as a
filename.

Removing this clause ensures that delegate_task([], Goals0) always
proceeds to load initialization files and start the toplevel, fixing
the double-processing bug.

Co-Authored-By: J.J.'s Robot <jjtolton@gmail.com>
2025-11-09 12:44:48 -05:00
J.J. Tolton
15d112485c Add comprehensive tests for -t custom toplevel flag
- Create Prolog integration tests in src/tests/custom_toplevel.pl
- Add CLI test configuration in tests/scryer/cli/src_tests/custom_toplevel_tests.toml
- Tests verify:
  * -t halt terminates after initialization
  * Custom toplevels can be user-defined predicates
  * Toplevel receives control after initialization completes
  * Default behavior is REPL when no -t specified
- All tests pass successfully

Following TESTING_GUIDE.md three-layer testing approach:
- Layer 2: Prolog integration tests with test_framework
- Layer 3: CLI snapshot tests with .toml configuration

Co-Authored-By: J.J.'s Robot <noreply@example.com>
2025-11-09 12:44:48 -05:00
J.J. Tolton
768f7ce9a7 -t custom toplevel option
- Add -t FLAG to specify custom toplevel (arity 0 predicate)
- Default toplevel is 'repl' if -t is not specified
- Using `-t halt` achieves original goal of guaranteed termination
- Custom toplevels enable flexible exit strategies (e.g., server mode)
- Update help text to document -t flag

Examples:
  scryer-prolog -t halt program.pl    # Exits after execution
  scryer-prolog -t my_repl program.pl # Custom REPL
  scryer-prolog program.pl            # Default REPL

Co-Authored-By: J.J.'s Robot <noreply@example.com>
2025-11-09 12:44:37 -05:00
Mark Thom
e4d9692535 Merge pull request #3120 from aarroyoc/master
Delete test page from learning section
2025-10-20 19:46:26 -07:00
Mark Thom
f050e75d81 Merge pull request #3124 from bakaq/mark-partial-string-tail
Fix marking of partial string tail in iteration
2025-10-20 19:46:11 -07:00
Mark Thom
3dcb42af3c Merge pull request #3117 from aarroyoc/update-releases-0100
Update web to show 0.10.0 releases
2025-10-20 19:45:19 -07:00
bakaq
fdcd92db08 Fix marking of partial string tail in iteration 2025-10-14 16:59:50 -03:00
Adrián Arroyo Calle
c82f1ccde1 Delete test page from learning section 2025-10-11 15:24:07 +02:00
Adrián Arroyo Calle
2b85a307ae Update web to show 0.10.0 releases 2025-10-07 22:16:40 +02:00
Mark Thom
d7d5dfe059 Merge pull request #3108 from Skgland/issue-3073
emit a more appropriate error when passing the wrong argument count when constructing an ffi struct with the wrong argument count
2025-10-02 23:43:13 -07:00
Mark Thom
109796122e Merge pull request #3111 from Skgland/ffi-non-fixed-sized-integers
add support for non-fixed-width integers types for ffi
2025-10-02 23:42:59 -07:00
Mark Thom
476c1718f9 Merge pull request #3109 from Skgland/issue-3107
move CI  job release from retired ubuntu-20.04 image to ubuntu-22.04
2025-09-29 20:47:55 -07:00
Skgland
f8b9944f5c update documentation 2025-09-28 19:50:21 +02:00
Skgland
6e7dbfc75e consistently treat bool as i8 2025-09-28 18:29:20 +02:00
Skgland
1add5a9a75 add ffi types for non-fixed-sized integers 2025-09-28 18:17:35 +02:00
Skgland
777e6de9ee fix missing quotes 2025-09-28 13:43:09 +02:00
Skgland
2443af57d0 fixup quad 2025-09-27 23:14:14 +02:00
Skgland
ed03d5b1b4 move CI job release from retired ubuntu-20.04 image to ubuntu-22.04
the new image is the same as all other ubuntu jobs
2025-09-27 22:54:45 +02:00
Skgland
9376bc6369 fix mthom/scryer-prolog#3073 2025-09-27 22:48:05 +02:00
Skgland
2f43c3ff40 add regression test for issue 3073 2025-09-27 22:05:51 +02:00
115 changed files with 9023 additions and 7236 deletions

View File

@@ -1,4 +1,7 @@
name: 'Setup Rust' name: 'Setup Rust'
description: |
Setup the rust toolchain and environment for the selected toolchain
inputs: inputs:
rust-version: rust-version:
required: true required: true
@@ -16,7 +19,7 @@ inputs:
runs: runs:
using: "composite" using: "composite"
steps: steps:
- uses: dtolnay/rust-toolchain@master - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # master
id: toolchain id: toolchain
with: with:
toolchain: ${{ inputs.rust-version }} toolchain: ${{ inputs.rust-version }}
@@ -33,7 +36,15 @@ runs:
echo "CC=clang" >> $GITHUB_ENV echo "CC=clang" >> $GITHUB_ENV
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
- uses: actions/cache@v4 - name: Install s390x cross-compilation toolchain
if: ${{ matrix.target == 's390x-unknown-linux-gnu' }}
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y gcc-s390x-linux-gnu
echo "CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_LINKER=s390x-linux-gnu-gcc" >> $GITHUB_ENV
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with: with:
path: | path: |
~/.cargo/bin/ ~/.cargo/bin/
@@ -46,7 +57,7 @@ runs:
# Remove build artifacts for the current crate, since it will be rebuilt every # Remove build artifacts for the current crate, since it will be rebuilt every
# run anyway, but keep dependency artifacts to cache them. # run anyway, but keep dependency artifacts to cache them.
# Must be placed after actions/cache so its post step runs first. # Must be placed after actions/cache so its post step runs first.
- uses: pyTooling/Actions/with-post-step@v4.1.0 - uses: pyTooling/Actions/with-post-step@41aed3e559cbf69bfdb46abb25d7c535900ed5b6 # v7.7.0
with: with:
main: bash ./.github/actions/setup-rust/cleanup.sh main: bash ./.github/actions/setup-rust/cleanup.sh
post: bash ./.github/actions/setup-rust/cleanup.sh post: bash ./.github/actions/setup-rust/cleanup.sh

View File

@@ -4,7 +4,7 @@ set -e
echo Cleanup workspace build artifacts and extra target output 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 # 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(" ")'` cargo clean --workspace
# remove directories in /target/ that are not named `debug` or `release` # remove directories in /target/ that are not named `debug` or `release`
before=`du -s target | awk '{print $1}'` before=`du -s target | awk '{print $1}'`

52
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
version: 2
updates:
# Enable version updates for cargo
- package-ecosystem: "cargo"
# Look for `Cargo.toml` and `Cargo.lock` files in the `root` directory
directory: "/"
schedule:
interval: "monthly"
cooldown:
default-days: "7"
groups:
cargo-incompatible:
applies-to: version-updates
# TODO: use incompatible update-type once available
# see issue https://github.com/dependabot/dependabot-core/issues/9681
update-types:
- "major"
- "minor" # pre-1.0 dependencies
cargo-compatible:
applies-to: version-updates
# TODO: use compatible update-type once available
# see issue https://github.com/dependabot/dependabot-core/issues/9681
update-types:
- "patch"
ignore:
# ignore all cargo updates for now while dependabot does not respect msrv/rust-version
# see issue https://github.com/dependabot/dependabot-core/issues/5423
- dependency-name: "*"
# Enable version updates for Docker
- package-ecosystem: "docker"
# Look for a `Dockerfile` in the `root` directory
directory: "/"
schedule:
interval: "monthly"
cooldown:
default-days: "7"
# Enable version updates for GitHub Actions
- package-ecosystem: "github-actions"
# Workflow files stored in the default location of `.github/workflows`
# You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.
directory: "/"
schedule:
interval: "monthly"
cooldown:
default-days: "7"
ignore:
# these actions doesn't have proper version tags
- dependency-name: "dtolnay/rust-toolchain"
- dependency-name: "logtalk-actions/setup-logtalk"

12
.github/workflows/check_features.sh vendored Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -e
echo "Checking all feature at once"
cargo check -q --all-targets --all-features "$@"
features=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name = "scryer-prolog") | .features | keys | join(" ")')
for feature in ${features} ; do
echo "Checking feature ${feature} in isolation"
cargo check -q --all-targets --no-default-features --features=${feature} "$@"
done

View File

@@ -19,7 +19,7 @@ jobs:
style: style:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Rust - name: Setup Rust
uses: ./.github/actions/setup-rust uses: ./.github/actions/setup-rust
with: with:
@@ -39,7 +39,7 @@ jobs:
outputs: outputs:
msrv: ${{ steps.read-declared-msrv.outputs.msrv }} msrv: ${{ steps.read-declared-msrv.outputs.msrv }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: read-declared-msrv - id: read-declared-msrv
name: Read msrv from Cargo.toml rust_version field name: Read msrv from Cargo.toml rust_version field
run: echo "msrv=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name = "scryer-prolog") | ."rust_version"')" >> "$GITHUB_OUTPUT" run: echo "msrv=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name = "scryer-prolog") | ."rust_version"')" >> "$GITHUB_OUTPUT"
@@ -53,10 +53,10 @@ jobs:
matrix: matrix:
include: include:
# operating systems # operating systems
- { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc', publish: true } - { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc', publish: true, check-features: true }
- { os: macos-latest, rust-version: stable, target: 'x86_64-apple-darwin', publish: true } - { os: macos-latest, rust-version: stable, target: 'x86_64-apple-darwin', publish: true, check-features: true }
# architectures # architectures
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true } - { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true, check-features: true }
- { os: ubuntu-22.04, rust-version: stable, target: 'i686-unknown-linux-gnu', publish: true } - { os: ubuntu-22.04, rust-version: stable, target: 'i686-unknown-linux-gnu', publish: true }
# FIXME(issue #2138): run wasm tests, failing to run since https://github.com/mthom/scryer-prolog/pull/2137 removed wasm-pack # 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', use_swap: true } - { os: ubuntu-22.04, rust-version: nightly, target: 'wasm32-unknown-unknown', publish: true, args: '--no-default-features' , test-args: '--no-run --no-default-features', use_swap: true }
@@ -64,15 +64,19 @@ jobs:
- { os: ubuntu-22.04, rust-version: "${{ needs.read-msrv.outputs.msrv }}" , target: 'x86_64-unknown-linux-gnu' } - { os: ubuntu-22.04, rust-version: "${{ needs.read-msrv.outputs.msrv }}" , 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' }
- { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu', miri: true, components: "miri" } - { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu', miri: true, components: "miri" }
# run miri for a big-endian target, with all features that are simple to get cross-compiled
- { os: ubuntu-22.04, rust-version: nightly, target: 's390x-unknown-linux-gnu', miri: true, components: "miri", args: '--no-default-features --features=all-simple-cross', test-args: '--no-run --no-default-features --features=all-simple-cross' }
defaults: defaults:
run: run:
shell: bash shell: bash
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actionhippie/swap-space@v1
- uses: actionhippie/swap-space@0cffa893f224708cfb6b011690d8ba819d69c10f # v1.1.0
if: matrix.use_swap if: matrix.use_swap
with: with:
size: 10G size: 10G
- name: Setup Rust - name: Setup Rust
uses: ./.github/actions/setup-rust uses: ./.github/actions/setup-rust
with: with:
@@ -84,12 +88,17 @@ jobs:
# Build and test. # Build and test.
- name: Build library - name: Build library
run: cargo build --all-targets --target ${{ matrix.target }} ${{ matrix.args }} --verbose run: cargo build --all-targets --target ${{ matrix.target }} ${{ matrix.args }} --verbose
- name: Test - name: Test
run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
- name: Check features
if: matrix.check-features
run: bash ./.github/workflows/check_features.sh --target ${{ matrix.target }}
- name: Check miri - name: Check miri
if: matrix.miri if: matrix.miri
run: cargo miri test run: cargo miri test --target ${{ matrix.target }} ${{ matrix.args }}
# 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
@@ -110,7 +119,7 @@ jobs:
- name: Publish release binary artifact - name: Publish release binary artifact
if: matrix.publish if: matrix.publish
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
path: | path: |
target/${{ matrix.target }}/release/scryer-prolog* target/${{ matrix.target }}/release/scryer-prolog*
@@ -123,14 +132,14 @@ jobs:
needs: [build-test] needs: [build-test]
steps: steps:
# Download prebuilt ubuntu binary from build-test job, setup logtalk # Download prebuilt ubuntu binary from build-test job, setup logtalk
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
with: with:
name: scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu name: scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu
- run: | - run: |
chmod +x release/scryer-prolog chmod +x release/scryer-prolog
echo "$PWD/release" >> "$GITHUB_PATH" echo "$PWD/release" >> "$GITHUB_PATH"
- name: Install Logtalk - name: Install Logtalk
uses: logtalk-actions/setup-logtalk@master uses: logtalk-actions/setup-logtalk@4ea002fe3037199afcf1c6c91bf1f57de0f995e6 # master
with: with:
logtalk-version: "3.70.0" logtalk-version: "3.70.0"
logtalk-tool-dependencies: false logtalk-tool-dependencies: false
@@ -147,17 +156,17 @@ jobs:
|| echo "::warning ::logtalk compliance suite failed" || echo "::warning ::logtalk compliance suite failed"
# -u "https://github.com/LogtalkDotOrg/logtalk3/tree/$LOGTALK_GIT_HASH/tests/prolog/" \ # -u "https://github.com/LogtalkDotOrg/logtalk3/tree/$LOGTALK_GIT_HASH/tests/prolog/" \
- name: Publish Logtalk test logs - name: Publish Logtalk test logs
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: logtalk-test-logs name: logtalk-test-logs
path: '${{ env.LOGTALKUSER }}/tests/prolog/logtalk_tester_logs' path: '${{ env.LOGTALKUSER }}/tests/prolog/logtalk_tester_logs'
- name: Publish Logtalk test results artifact - name: Publish Logtalk test results artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: logtalk-test-results name: logtalk-test-results
path: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' path: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml'
- name: Publish Logtalk test summary - name: Publish Logtalk test summary
uses: EnricoMi/publish-unit-test-result-action/composite@master uses: EnricoMi/publish-unit-test-result-action/composite@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2.23.0
with: with:
check_name: Logtalk test summary check_name: Logtalk test summary
files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml' files: '${{ env.LOGTALKUSER }}/tests/prolog/**/*.xml'
@@ -167,7 +176,7 @@ jobs:
report: report:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Rust - name: Setup Rust
uses: ./.github/actions/setup-rust uses: ./.github/actions/setup-rust
with: with:
@@ -180,18 +189,19 @@ jobs:
version=`yq -ptoml -oj -r '.package[] | select(.name == "iai-callgrind") | .version' Cargo.lock` version=`yq -ptoml -oj -r '.package[] | select(.name == "iai-callgrind") | .version' Cargo.lock`
echo installing iai-callgrind "$version" echo installing iai-callgrind "$version"
cargo install iai-callgrind-runner --force --version "$version" cargo install iai-callgrind-runner --force --version "$version"
sudo apt install valgrind -y sudo apt-get update -y
sudo apt-get install valgrind -y
- name: Test and report - name: Test and report
run: | run: |
RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml
- name: Publish cargo test results artifact - name: Publish cargo test results artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: cargo-test-results name: cargo-test-results
path: cargo_test_results.xml path: cargo_test_results.xml
- name: Publish cargo test summary - name: Publish cargo test summary
uses: EnricoMi/publish-unit-test-result-action/composite@master uses: EnricoMi/publish-unit-test-result-action/composite@c950f6fb443cb5af20a377fd0dfaa78838901040 # v2.23.0
with: with:
check_name: Cargo test summary check_name: Cargo test summary
files: cargo_test_results.xml files: cargo_test_results.xml
@@ -205,7 +215,7 @@ jobs:
- run: cargo bench --bench run_criterion -- --profile-time 60 - run: cargo bench --bench run_criterion -- --profile-time 60
- name: Publish benchmark results - name: Publish benchmark results
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: benchmark-results name: benchmark-results
path: | path: |
@@ -215,11 +225,11 @@ jobs:
# Publish binaries when building for a tag # Publish binaries when building for a tag
release: release:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
needs: [build-test] needs: [build-test]
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
steps: steps:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
- name: Zip binaries for release - name: Zip binaries for release
run: | run: |
zip scryer-prolog_macos-latest.zip ./scryer-prolog_macos-latest_x86_64-apple-darwin/scryer-prolog zip scryer-prolog_macos-latest.zip ./scryer-prolog_macos-latest_x86_64-apple-darwin/scryer-prolog
@@ -228,7 +238,7 @@ jobs:
zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest_x86_64-pc-windows-msvc/scryer-prolog.exe zip scryer-prolog_windows-latest.zip ./scryer-prolog_windows-latest_x86_64-pc-windows-msvc/scryer-prolog.exe
zip scryer-prolog_wasm32.zip ./scryer-prolog_ubuntu-22.04_wasm32-unknown-unknown/scryer-prolog.wasm zip scryer-prolog_wasm32.zip ./scryer-prolog_ubuntu-22.04_wasm32-unknown-unknown/scryer-prolog.wasm
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with: with:
files: | files: |
scryer-prolog_macos-latest.zip scryer-prolog_macos-latest.zip

View File

@@ -14,17 +14,17 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Workaround: https://github.com/docker/build-push-action/issues/461 # Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx - name: Setup Docker buildx
# https://github.com/docker/setup-buildx-action # https://github.com/docker/setup-buildx-action
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
# Login against Docker registry # Login against Docker registry
- name: Log into registry - name: Log into registry
# https://github.com/docker/login-action # https://github.com/docker/login-action
uses: docker/login-action@v3 uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -35,7 +35,7 @@ jobs:
- name: Extract Docker metadata - name: Extract Docker metadata
id: meta id: meta
# https://github.com/docker/metadata-action # https://github.com/docker/metadata-action
uses: docker/metadata-action@v5 uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
with: with:
images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/scryer-prolog images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/scryer-prolog
tags: | tags: |
@@ -47,7 +47,7 @@ jobs:
- name: Build and push Docker image - name: Build and push Docker image
id: build-and-push id: build-and-push
# https://github.com/docker/build-push-action # https://github.com/docker/build-push-action
uses: docker/build-push-action@v6 uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
# v4 adds SLSA Provenance attestation which is # v4 adds SLSA Provenance attestation which is
# - unsupported by AWS Lambda # - unsupported by AWS Lambda
# - limited support by Google Cloud Run # - limited support by Google Cloud Run

2
.gitignore vendored
View File

@@ -3,3 +3,5 @@ target/
.direnv/ .direnv/
__pycache__
*.pyc

1576
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
name = "scryer-prolog" name = "scryer-prolog"
version = "0.10.0" version = "0.10.0"
authors = ["Mark Thom <markjordanthom@gmail.com>"] authors = ["Mark Thom <markjordanthom@gmail.com>"]
edition = "2021" edition = "2024"
description = "A modern Prolog implementation written mostly in Rust." description = "A modern Prolog implementation written mostly in Rust."
readme = "README.md" readme = "README.md"
repository = "https://github.com/mthom/scryer-prolog" repository = "https://github.com/mthom/scryer-prolog"
@@ -11,28 +11,39 @@ keywords = ["prolog", "prolog-interpreter", "prolog-system"]
categories = ["command-line-utilities"] categories = ["command-line-utilities"]
build = "build/main.rs" build = "build/main.rs"
# Remember to check CI # Remember to check CI
rust-version = "1.85" rust-version = "1.93.1"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features] [features]
default = ["ffi", "repl", "hostname", "tls", "http", "crypto-full"] default = ["all-simple-cross", "tls", "http"]
# activates all features that depend on no non pure-rust dependencies
#
# currently does not include
# ffi due to libffi
# tls, http due to openssl
# crypto-full due to ring
all-pure = ["repl", "hostname"]
# enables all features that are simple to get working for cross-compliation
# currently all but tls, http as those depend on openssl
all-simple-cross = ["all-pure", "ffi", "crypto-full"]
ffi = ["dep:libffi"] ffi = ["dep:libffi"]
repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"] repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"]
hostname = ["dep:hostname"] hostname = ["dep:hostname"]
tls = ["dep:native-tls"] tls = ["dep:native-tls"]
http = ["dep:warp", "dep:reqwest"] http = ["dep:warp", "dep:reqwest"]
crypto-full = [] # crypto function that require non pure-rust dependencies
crypto-full = ["dep:ring"]
[lints.clippy]
collapsible_match = "allow"
[lints.rust] [lints.rust]
unexpected_cfgs = { level = "deny", check-cfg = [ unexpected_cfgs = "deny"
'cfg(rust_version, values("1.87.0"))', function_casts_as_integer = "deny"
] }
[build-dependencies] [build-dependencies]
indexmap = "2.3.0"
proc-macro2 = "1.0.86" proc-macro2 = "1.0.86"
quote = "1.0.36" quote = "1.0.36"
strum = "0.26" strum = "0.26"
@@ -40,7 +51,6 @@ strum_macros = "0.26"
syn = { version = "2.0.72", features = ['full', 'visit', 'extra-traits'] } syn = { version = "2.0.72", features = ['full', 'visit', 'extra-traits'] }
to-syn-value = "0.1.1" to-syn-value = "0.1.1"
to-syn-value_derive = "0.1.1" to-syn-value_derive = "0.1.1"
version_check = "0.9.5"
walkdir = "2" walkdir = "2"
[dependencies] [dependencies]
@@ -61,17 +71,18 @@ futures = "0.3"
fxhash = "0.2.1" fxhash = "0.2.1"
git-version = "0.3.9" git-version = "0.3.9"
indexmap = "2.3.0" indexmap = "2.3.0"
lazy_static = "1.5.0"
lexical = "7.0.4" lexical = "7.0.4"
libc = "0.2.155" libc = "0.2.155"
libloading = "0.8" libloading = "0.8"
scryer-modular-bitfield = "0.11.4" modular-bitfield = "0.13.1"
num-order = { version = "1.2.0" } num-order = { version = "1.2.0" }
ordered-float = "5.0.0" ordered-float = "5.0.0"
phf = { version = "0.11", features = ["macros"] } phf = { version = "0.11", features = ["macros"] }
puruspe = "0.4.1" puruspe = "0.4.1"
rand = "0.8.5" rand = "0.8.5"
ring = { version = "0.17.8", features = ["wasm32_unknown_unknown_js"] } ring = { version = "0.17.8", features = [
"wasm32_unknown_unknown_js",
], optional = true }
ripemd = "0.1.3" ripemd = "0.1.3"
roxmltree = "0.20.0" roxmltree = "0.20.0"
ryu = "1.0.18" ryu = "1.0.18"
@@ -93,12 +104,12 @@ parking_lot = "0.12.4"
crossterm = { version = "0.28.1", optional = true } crossterm = { version = "0.28.1", optional = true }
ctrlc = { version = "3.4.4", optional = true } ctrlc = { version = "3.4.4", optional = true }
hostname = { version = "0.4.0", optional = true } hostname = { version = "0.4.0", optional = true }
libffi = { version = "4.0.0", optional = true } libffi = { version = "5.1.0", optional = true }
native-tls = { version = "0.2.12", optional = true } native-tls = { version = "0.2.12", optional = true }
# the version requirement of reqwest is kept low for compatibility with old deno versions # the version requirement of reqwest is kept low for compatibility with old deno versions
# that pin reqwest to 0.11.20 # that pin reqwest to 0.11.20
reqwest = { version = "0.11.0", optional = true } reqwest = { version = "0.11.0", optional = true }
rustyline = { version = "14.0.0", optional = true } rustyline = { version = "18.0.0", optional = true }
tokio = { version = "1.39.2", features = ["full"] } tokio = { version = "1.39.2", features = ["full"] }
warp = { version = "0.3.7", features = ["tls"], optional = true } warp = { version = "0.3.7", features = ["tls"], optional = true }

View File

@@ -55,14 +55,13 @@ the builtin Prolog modules and libraries in Scryer, check the documentation site
## Downloads ## Downloads
The latest version of Scryer Prolog is *0.9.4*. And it's already useful for lots of tasks. The latest version of Scryer Prolog is *0.10.0*. And it's already useful for lots of tasks.
| Windows (64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_windows-latest_x86_64-pc-windows-msvc.zip) | | Windows (64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_windows-latest_x86_64-pc-windows-msvc.zip) |
| macOS (Intel) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_macos-11_x86_64-apple-darwin.zip) | | macOS (Intel) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_macos-11_x86_64-apple-darwin.zip) |
| macOS (ARM) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog-macos-arm.zip) | | macOS (ARM) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog--0.10.0.arm64_tahoe.bottle.tar.gz) |
| Linux (Ubuntu 20.04, 64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_ubuntu-20.04_x86_64-unknown-linux-gnu.zip) | | Linux (Ubuntu 22.04, 64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu.zip) |
| Linux (Ubuntu 22.04, 64 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_ubuntu-22.04_x86_64-unknown-linux-gnu.zip) | | Linux (Ubuntu 22.04, 32 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-0100/scryer-prolog_ubuntu-22.04_i686-unknown-linux-gnu.zip) |
| Linux (Ubuntu 22.04, 32 bits) | [Download](https://scryerprologrelease.blob.core.windows.net/release-094/scryer-prolog_ubuntu-22.04_i686-unknown-linux-gnu.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.

View File

@@ -73,9 +73,10 @@ Extend Scryer Prolog to include the following, among other features:
- [ ] Inlining all built-ins and system call instructions. - [ ] Inlining all built-ins and system call instructions.
- [x] Greatly reducing the number of instructions used to compile disjunctives. - [x] Greatly reducing the number of instructions used to compile disjunctives.
- [x] Storing short atoms to heap cells without writing them to the atom table. - [x] Storing short atoms to heap cells without writing them to the atom table.
- [ ] Configurable JIT/on-demand indexing over all arguments
(documented in "[Demand-Driven Indexing of Prolog Clauses](https://user.it.uu.se/~kostis/Papers/iclp07.pdf)"). (_in progress_)
- [ ] A compacting garbage collector satisfying the five properties of - [ ] A compacting garbage collector satisfying the five properties of
"[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_) "[Precise Garbage Collection in Prolog](https://www.complang.tuwien.ac.at/ulrich/papers/PDF/2008-ciclops.pdf)." (_in progress_)
- [ ] Mode declarations.
## Phase 3 ## Phase 3
@@ -103,8 +104,6 @@ Gustafson's book "[The End of Error](http://www.johngustafson.net/unums.html)."
3. Add concurrent tables to manage shared references to atoms and 3. Add concurrent tables to manage shared references to atoms and
strings. strings.
4. Add some form of JIT predicate indexing.
## Installing Scryer Prolog ## Installing Scryer Prolog
### Binaries ### Binaries
@@ -159,9 +158,9 @@ during the installation of the rust toolchain.
#### From Crates.io [![Crates.io Version](https://img.shields.io/crates/v/scryer-prolog)](https://crates.io/crates/scryer-prolog) ![Crates.io MSRV](https://img.shields.io/crates/msrv/scryer-prolog) #### From Crates.io [![Crates.io Version](https://img.shields.io/crates/v/scryer-prolog)](https://crates.io/crates/scryer-prolog) ![Crates.io MSRV](https://img.shields.io/crates/msrv/scryer-prolog)
> [!NOTE] > [!NOTE]
> The lates crates.io release can be significantly behind the version available in the git repository > The latest crates.io release can be significantly behind the version available in the git repository
> The crates.io badge in this sections title is a link to the crates.io page. > The crates.io badge in this sections title is a link to the crates.io page.
> The msrv badge in the section title referece to the minimum rust toolchain version required to compile the latest crates.io release > The msrv badge in the section title references the minimum rust toolchain version required to compile the latest crates.io release
`scryer-prolog` is also release on crates.io and can be installed with `scryer-prolog` is also release on crates.io and can be installed with
@@ -880,6 +879,8 @@ Successful existing applications of Scryer Prolog include:
trial&nbsp;design, described in [*An Executable Specification of trial&nbsp;design, described in [*An Executable Specification of
Oncology Dose-Escalation Protocols with&nbsp;Prolog*](https://arxiv.org/abs/2402.08334) Oncology Dose-Escalation Protocols with&nbsp;Prolog*](https://arxiv.org/abs/2402.08334)
and culminating in&nbsp;[**DEDUCTION**](https://codeberg.org/dcnorris/DEDUCTION) and culminating in&nbsp;[**DEDUCTION**](https://codeberg.org/dcnorris/DEDUCTION)
- the core tax engine for VAT reasoning and compliance of the
Belgian&nbsp;company [VATmiraal](https://vatmiraal.be/)
- semantic reasoning and queries in [AD4M](https://github.com/coasys/ad4m), - semantic reasoning and queries in [AD4M](https://github.com/coasys/ad4m),
an agent-centric distributed application meta-ontology. an agent-centric distributed application meta-ontology.
@@ -900,9 +901,11 @@ in&nbsp;person.
and [discussion](https://github.com/mthom/scryer-prolog/discussions/2160). and [discussion](https://github.com/mthom/scryer-prolog/discussions/2160).
- [Scryer Prolog Meetup 2024](https://www.digitalaustria.gv.at/wissenswertes/events/scryerprologmeetup2024) - [Scryer Prolog Meetup 2024](https://www.digitalaustria.gv.at/wissenswertes/events/scryerprologmeetup2024)
in Vienna, Austria. Its [announcement and discussion](https://github.com/mthom/scryer-prolog/discussions/2377). in Vienna, Austria. Its [announcement and discussion](https://github.com/mthom/scryer-prolog/discussions/2377).
- **Save the date:** The [Scryer Prolog Meetup 2025](https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2025/) - [Scryer Prolog Meetup 2025](https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2025/)
will take place on Nov.&nbsp;13th and 14th&nbsp;2025 in Düsseldorf, Germany. in Düsseldorf, Germany. Its [announcement and discussion](https://github.com/mthom/scryer-prolog/discussions/2948).
Its [announcement](https://github.com/mthom/scryer-prolog/discussions/2948). - **Attend, present and represent:** The [Scryer Prolog Meetup 2026](https://www.digitalaustria.gv.at/wissenswertes/events/scryerprologmeetup2026.html)
will take place on Oct.&nbsp;24th and 25th&nbsp;2026 in Vienna, Austria.
Its [announcement](https://github.com/mthom/scryer-prolog/discussions/3327).
## Support and discussions ## Support and discussions

35
_typos.toml Normal file
View File

@@ -0,0 +1,35 @@
# config for https://github.com/crate-ci/typos
[default]
# example from https://github.com/crate-ci/typos/blob/master/docs/reference.md#example-configurations
extend-ignore-re = [
"(#|//)\\s*spellchecker:ignore-next-line\\n.*"
]
# correct word key to value
# can be used to ignore a typo by adding an entry <typo> = "<typo>"
[default.extend-words]
# correct identifier key to value
# can be used to ignore a typo by adding an entry <typo> = "<typo>"
[default.extend-identifiers]
interm = "interm"
IntermReg = "IntermReg"
[type.rust]
extend-glob = [ "*.rs" ]
[type.rust.extend-identifiers]
consts = "consts" # std::{f32,f64}::consts
[type.prolog]
extend-glob = ["*.pl"]
check-file = false
[type.stdout]
extend-glob = ["*.stdout"]
check-file = false
[files]
extend-exclude = ["lib_integration_test_commands.txt"]

View File

@@ -49,7 +49,7 @@ once.
## Adding benchmarks ## Adding benchmarks
This design is meant to suppoort defining lots of benchmarks. This design is meant to support defining lots of benchmarks.
To add a new benchmark: To add a new benchmark:
@@ -77,7 +77,7 @@ Some tips:
cumbersome to run. cumbersome to run.
* Consider that the library runtime actually parses the text output of the top * 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 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. output small so it doesn't just benchmark the output parsing code.
* DO test the output of the benchmark run, we don't want to count broken * DO test the output of the benchmark run, we don't want to count broken
benchmarks. benchmarks.

View File

@@ -1,5 +1,5 @@
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]

View File

@@ -70,7 +70,7 @@ impl PrologBenchmark {
} }
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub fn setup(&self) -> impl FnMut() -> Vec<LeafAnswer> { pub fn setup(&self) -> impl FnMut() -> Vec<LeafAnswer> + use<> {
let mut machine = self.make_machine(); let mut machine = self.make_machine();
let query = self.query; let query = self.query;
move || { move || {

File diff suppressed because it is too large Load Diff

View File

@@ -4,20 +4,22 @@ mod static_string_indexing;
use instructions_template::generate_instructions_rs; use instructions_template::generate_instructions_rs;
use static_string_indexing::index_static_strings; use static_string_indexing::index_static_strings;
use std::collections::BTreeMap;
use std::env; use std::env;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::MAIN_SEPARATOR_STR;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::path::MAIN_SEPARATOR_STR;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
fn find_prolog_files(path_prefix: &str, current_dir: &Path) -> Vec<(String, PathBuf)> { fn find_prolog_files(path_prefix: &str, current_dir: &Path) -> Vec<(String, PathBuf)> {
let mut libraries = vec![]; // use a BTreeMap to get a stable order independent of fs enumeration order
let mut libraries = BTreeMap::new();
let entries = match current_dir.read_dir() { let entries = match current_dir.read_dir() {
Ok(entries) => entries, Ok(entries) => entries,
Err(_) => return libraries, Err(_) => return vec![],
}; };
for entry in entries.filter_map(Result::ok).map(|e| e.path()) { for entry in entries.filter_map(Result::ok).map(|e| e.path()) {
@@ -34,19 +36,15 @@ fn find_prolog_files(path_prefix: &str, current_dir: &Path) -> Vec<(String, Path
let name = entry.file_stem().unwrap().to_str().unwrap(); let name = entry.file_stem().unwrap().to_str().unwrap();
let lib_name = format!("{path_prefix}{name}"); let lib_name = format!("{path_prefix}{name}");
libraries.push((lib_name, entry)); libraries.insert(lib_name, entry);
} }
} }
} }
libraries libraries.into_iter().collect()
} }
fn main() { fn main() {
if version_check::is_min_version("1.87.0").unwrap_or(false) {
println!(r#"cargo:rustc-cfg=rust_version="1.87.0""#);
}
let has_rustfmt = Command::new("rustfmt") let has_rustfmt = Command::new("rustfmt")
.arg("--version") .arg("--version")
.stdin(Stdio::inherit()) .stdin(Stdio::inherit())

View File

@@ -1,18 +1,18 @@
use std::collections::BTreeSet;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use syn::parse::*; use syn::parse::*;
use syn::visit::*; use syn::visit::*;
use syn::*; use syn::*;
use indexmap::IndexSet;
struct StaticStrVisitor { struct StaticStrVisitor {
static_strs: IndexSet<String>, static_strs: BTreeSet<String>,
} }
impl StaticStrVisitor { impl StaticStrVisitor {
fn new() -> Self { fn new() -> Self {
Self { Self {
static_strs: IndexSet::new(), static_strs: BTreeSet::new(),
} }
} }
} }
@@ -166,9 +166,9 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
visitor.visit_file(&syntax) visitor.visit_file(&syntax)
} }
let mut static_str_keys = vec![]; let static_str_keys: Vec<_> = visitor.static_strs.iter().collect();
let mut static_strs = vec![]; let mut static_strs = Vec::with_capacity(static_str_keys.len());
let mut static_str_indices = vec![]; let mut static_str_indices = Vec::with_capacity(static_str_keys.len());
let indices: Vec<u64> = visitor let indices: Vec<u64> = visitor
.static_strs .static_strs
@@ -176,8 +176,6 @@ pub fn index_static_strings(instruction_rs_path: &std::path::Path) -> TokenStrea
.map(|string| { .map(|string| {
let index = static_string_index(string, static_strs.len()); let index = static_string_index(string, static_strs.len());
static_str_keys.push(string);
if index & 1 == 1 { if index & 1 == 1 {
index index
} else { } else {

26
clippy.toml Normal file
View File

@@ -0,0 +1,26 @@
disallowed-macros = [
# https://rust-lang.github.io/rust-clippy/master/#disallowed_macros
# list of macros that may panic on allocation failure e.g.
# "std::vec",
]
disallowed-methods = [
# https://rust-lang.github.io/rust-clippy/master/#disallowed_method
# list of methods that may panic on allocation failure
# though not including things that can be used correctly by reversing ahead of time (i.e. std::vec::Vec::try_reserve + std::iter::Extend::extend ).
# "std::iter::Iter::collect",
# { path = "std::vec::Vec::with_capacity", replacement = "std::vec::Vec::new + std::vec::Vec::try_reserve" },
# { path = "std::string::String::with_capacity", replacement = "std::string::String::new + std::string::String::try_reserve" },
]
disallowed-types = [
# https://rust-lang.github.io/rust-clippy/master/#disallowed_types
# list of types that can't be used without risking a panic due to allocation failure
# { path = "std::collections::BTreeMap", reason = "unlike Vec and HashMap BTreeMap cannot reserve capacity ahead of time (i.e. try_reserve) making it unusable without risk of oom panic"},
]

View File

@@ -6,8 +6,7 @@ omit(["ops_and_meta_predicates.pl", "tabling"]).
learn_pages_source_folder("learn"). learn_pages_source_folder("learn").
learn_pages_categories(["First steps", "Tutorials"]). learn_pages_categories(["First steps", "Tutorials"]).
learn_pages([ learn_pages([
page("Let's play Brisca", "Tutorials", "lets-play-brisca.dj"), page("Let's play Brisca", "Tutorials", "lets-play-brisca.dj")
page("Test page", "First steps", "test-page.dj")
]). ]).
copy_file("logo/scryer.png", "scryer.png"). copy_file("logo/scryer.png", "scryer.png").
copy_file("learn/Spanish_deck_Fournier.jpg", "learn/Spanish_deck_Fournier.jpg"). copy_file("learn/Spanish_deck_Fournier.jpg", "learn/Spanish_deck_Fournier.jpg").

View File

@@ -23,7 +23,7 @@ The rules for knowing which players takes the round are the following:
First, we need to decide a representation of our cards. Coming from another languages we can think that a good representation might be a class or a struct, with two fields, one for the number and the other for the suite, but Prolog doesn't have objects. We can use a list with two elements. But lists are better when we're dealing with variable length data. We could also use a compound term. This is the right choice if our fields are fixed. First, we need to decide a representation of our cards. Coming from another languages we can think that a good representation might be a class or a struct, with two fields, one for the number and the other for the suite, but Prolog doesn't have objects. We can use a list with two elements. But lists are better when we're dealing with variable length data. We could also use a compound term. This is the right choice if our fields are fixed.
A compound term is defined by an atom, followed by the data itself enclosed by parenthesis and separated by comma. Like this: `card(oros, 4)`. Yes, very similar to predicates. In fact the only difference is how we use them, because they're the same. If we pass a compund term in the first level of a query, or inside a call/N, Prolog will treat it as code instead of data. This is one the the examples of Prolog being a homoiconic language. A compound term is defined by an atom, followed by the data itself enclosed by parenthesis and separated by comma. Like this: `card(oros, 4)`. Yes, very similar to predicates. In fact the only difference is how we use them, because they're the same. If we pass a compound term in the first level of a query, or inside a call/N, Prolog will treat it as code instead of data. This is one the the examples of Prolog being a homoiconic language.
We can go further, Prolog is very flexible and we can define custom operators easily if we want. Those are also compound terms, but with a different syntax. There's an operator already defined that is very useful for us: the dash. We can just join two pieces of data with a dash, and they'll be together in the same structure. This is usually called "pair". We can go further, Prolog is very flexible and we can define custom operators easily if we want. Those are also compound terms, but with a different syntax. There's an operator already defined that is very useful for us: the dash. We can just join two pieces of data with a dash, and they'll be together in the same structure. This is usually called "pair".
@@ -98,7 +98,7 @@ cards_score_(X) -->
cards_score_(X1). cards_score_(X1).
``` ```
In DCGs, to match an item of the sequence, we use brackets. We use braces to introduce normal Prolog code. Calling other DCGs (in this case, the same, as it's a recursive one), it's just calling it again. Notice in this code that we are doing the addition of X0 and X1 when we still don't know the value of X1. This would be an error in the traditional arithmethic system of Prolog, but it's valid with clpz. clpz allows us to have a more declarative arithmethic, at least with integers. In DCGs, to match an item of the sequence, we use brackets. We use braces to introduce normal Prolog code. Calling other DCGs (in this case, the same, as it's a recursive one), it's just calling it again. Notice in this code that we are doing the addition of X0 and X1 when we still don't know the value of X1. This would be an error in the traditional arithmetic system of Prolog, but it's valid with clpz. clpz allows us to have a more declarative arithmetic, at least with integers.
Now we can try this code using `phrase/2` which is needed to jump to a DCG. Now we can try this code using `phrase/2` which is needed to jump to a DCG.
@@ -276,7 +276,7 @@ Let's ask ourselves what is a procedure. It's a sequence. And we have already se
The basic idea however is having _explicit_ states. The predicates that we're going to write will take an state (a view of the world at a certain point) and will give us the next state. The basic idea however is having _explicit_ states. The predicates that we're going to write will take an state (a view of the world at a certain point) and will give us the next state.
Let's define the state first. In a game of Brisca we have players. Each player has the three cards he can choose to put (less if we're running out of cards) and the cards he has got from winning rounds. Aditionally we have a stock, a trump suite and the order to play, which is usually from the player who won the last round and going to the right. We could store the data in a list, with different compound terms: Let's define the state first. In a game of Brisca we have players. Each player has the three cards he can choose to put (less if we're running out of cards) and the cards he has got from winning rounds. Additionally we have a stock, a trump suite and the order to play, which is usually from the player who won the last round and going to the right. We could store the data in a list, with different compound terms:
``` ```
[players([player(Name, PlayableCards, WonCards), player(Name, PlayableCards, WonCards), ...]), stock(Cards), trump(Trump)] [players([player(Name, PlayableCards, WonCards), player(Name, PlayableCards, WonCards), ...]), stock(Cards), trump(Trump)]

View File

@@ -1,3 +0,0 @@
# Test page
This is a page about Scryer Prolog

View File

@@ -1,7 +1,6 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
#[cfg(feature = "http")] #[cfg(feature = "http")]
use crate::http::{HttpListener, HttpResponse}; use crate::http::{HttpListener, HttpResponse};
use crate::machine::heap::AllocError;
use crate::machine::loader::LiveLoadState; use crate::machine::loader::LiveLoadState;
use crate::machine::streams::*; use crate::machine::streams::*;
use crate::offset_table::*; use crate::offset_table::*;
@@ -14,16 +13,16 @@ use ordered_float::OrderedFloat;
use std::fmt; use std::fmt;
use std::fmt::Debug; use std::fmt::Debug;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::io::PipeReader;
use std::io::PipeWriter;
use std::mem; use std::mem;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::net::TcpListener; use std::net::TcpListener;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::process::Child; use std::process::Child;
use std::ptr; use std::ptr;
use std::ptr::addr_of_mut;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::ptr::addr_of_mut;
use crate::machine::streams::{PipeReader, PipeWriter};
macro_rules! arena_alloc { macro_rules! arena_alloc {
($e:expr, $arena:expr) => {{ ($e:expr, $arena:expr) => {{
@@ -33,9 +32,7 @@ macro_rules! arena_alloc {
} }
macro_rules! float_alloc { macro_rules! float_alloc {
($e:expr, $arena:expr) => {{ ($e:expr, $arena:expr) => {{ $arena.f64_tbl.build_with(OrderedFloat($e)) }};
$arena.f64_tbl.build_with(OrderedFloat($e))
}};
} }
pub fn header_offset_from_payload<T: ?Sized + ArenaAllocated>() -> usize pub fn header_offset_from_payload<T: ?Sized + ArenaAllocated>() -> usize
@@ -50,7 +47,7 @@ where
payload_offset - header_offset payload_offset - header_offset
} }
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq)] #[derive(Specifier, Copy, Clone, Debug, PartialEq)]
#[bits = 7] #[bits = 7]
pub enum ArenaHeaderTag { pub enum ArenaHeaderTag {
Integer = 0b10, Integer = 0b10,
@@ -283,10 +280,15 @@ pub trait ArenaAllocated {
where where
Self::Payload: Sized, Self::Payload: Sized,
{ {
unsafe {
TypedArenaPtr(NonNull::new_unchecked( TypedArenaPtr(NonNull::new_unchecked(
ptr.payload_offset().cast_mut().cast::<Self::Payload>(), ptr.get_ptr()
.byte_add(Self::header_offset_from_payload())
.cast_mut()
.cast::<Self::Payload>(),
)) ))
} }
}
#[allow(clippy::missing_safety_doc)] #[allow(clippy::missing_safety_doc)]
fn alloc(arena: &mut Arena, value: Self::Payload) -> TypedArenaPtr<Self> fn alloc(arena: &mut Arena, value: Self::Payload) -> TypedArenaPtr<Self>
@@ -443,7 +445,7 @@ impl<T: ?Sized + ArenaAllocated> TypedAllocSlab<T> {
pub fn to_untyped(self: Box<Self>) -> (TypedArenaPtr<T>, UntypedArenaSlab) { pub fn to_untyped(self: Box<Self>) -> (TypedArenaPtr<T>, UntypedArenaSlab) {
let raw_box = Box::into_raw(self); let raw_box = Box::into_raw(self);
// safety: the pointer from Box::into_raw fullfills addr_of_mut's saftey requirements // safety: the pointer from Box::into_raw fulfills addr_of_mut's safety requirements
let payload_ptr = unsafe { addr_of_mut!((*raw_box).payload) }; let payload_ptr = unsafe { addr_of_mut!((*raw_box).payload) };
( (
@@ -485,12 +487,12 @@ unsafe impl Sync for Arena {}
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
impl Arena { impl Arena {
#[inline] #[inline]
pub fn new() -> Self { pub fn new() -> Result<Self, AllocError> {
Arena { Ok(Arena {
base: None, base: None,
f64_tbl: F64Table::new(), f64_tbl: F64Table::new()?,
code_index_tbl: CodeIndexTable::new(), code_index_tbl: CodeIndexTable::new()?,
} })
} }
} }
@@ -500,7 +502,7 @@ unsafe fn drop_slab_in_place(value: NonNull<AllocSlab>, tag: ArenaHeaderTag) {
<$payload as ArenaAllocated>::dealloc($value.cast::<TypedAllocSlab<$payload>>()) <$payload as ArenaAllocated>::dealloc($value.cast::<TypedAllocSlab<$payload>>())
}; };
} }
unsafe {
match tag { match tag {
ArenaHeaderTag::Integer => { ArenaHeaderTag::Integer => {
drop_typed_slab_in_place!(Integer, value); drop_typed_slab_in_place!(Integer, value);
@@ -579,6 +581,7 @@ unsafe fn drop_slab_in_place(value: NonNull<AllocSlab>, tag: ArenaHeaderTag) {
} }
} }
} }
}
impl Drop for Arena { impl Drop for Arena {
fn drop(&mut self) { fn drop(&mut self) {
@@ -634,16 +637,22 @@ mod tests {
#[test] #[test]
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")]
let const_value = HeapCellValue::from(ConsPtr::build_with( assert_eq!(ConsPtr::NICHE_SHIFT, 0);
std::ptr::without_provenance(0x0000_0431),
ConsPtrMaskTag::Cons, #[cfg(not(target_pointer_width = "32"))]
)); assert_eq!(ConsPtr::NICHE_SHIFT, 3);
#[cfg(target_pointer_width = "32")]
let dummy_ptr: *const ArenaHeader = std::ptr::without_provenance(0x0000_0438);
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
let const_value = HeapCellValue::from(ConsPtr::build_with( let dummy_ptr: *const ArenaHeader = std::ptr::without_provenance(0x0000_5555_ff00_0438);
std::ptr::without_provenance(0x0000_5555_ff00_0431),
ConsPtrMaskTag::Cons, assert!(dummy_ptr.is_aligned());
));
let const_value = HeapCellValue::from_arena_header_ptr(dummy_ptr);
match const_value.to_untyped_arena_ptr() { match const_value.to_untyped_arena_ptr() {
Some(arena_ptr) => { Some(arena_ptr) => {
@@ -658,8 +667,7 @@ mod tests {
} }
let stream = Stream::from_static_string("test", &mut wam.machine_st.arena); let stream = Stream::from_static_string("test", &mut wam.machine_st.arena);
let stream_cell = let stream_cell = HeapCellValue::from_arena_header_ptr(stream.as_ptr());
HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons));
match stream_cell.to_untyped_arena_ptr() { match stream_cell.to_untyped_arena_ptr() {
Some(arena_ptr) => { Some(arena_ptr) => {
@@ -728,7 +736,7 @@ mod tests {
Some(untyped_arena_ptr) => { Some(untyped_arena_ptr) => {
assert_eq!( assert_eq!(
Some(big_rat_ptr.header_ptr()), Some(big_rat_ptr.header_ptr()),
Some(untyped_arena_ptr.into()), Some(untyped_arena_ptr.get_ptr()),
); );
} }
None => { None => {

View File

@@ -1,5 +1,3 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
use crate::allocator::*; use crate::allocator::*;
use crate::arena::*; use crate::arena::*;
use crate::atom_table::*; use crate::atom_table::*;
@@ -22,7 +20,7 @@ use num_order::NumOrd;
use ordered_float::{Float, OrderedFloat}; use ordered_float::{Float, OrderedFloat};
use std::cell::Cell; use std::cell::Cell;
use std::cmp::{max, min, Ordering}; use std::cmp::Ordering;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::f64; use std::f64;
use std::num::FpCategory; use std::num::FpCategory;
@@ -31,21 +29,11 @@ use std::vec::Vec;
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ArithmeticTerm { pub enum ArithmeticTerm {
IntermReg(usize),
Reg(RegType), Reg(RegType),
Interm(usize),
Number(Number), Number(Number),
} }
impl ArithmeticTerm {
pub(crate) fn interm_or(&self, interm: usize) -> usize {
if let &ArithmeticTerm::Interm(interm) = self {
interm
} else {
interm
}
}
}
impl Default for ArithmeticTerm { impl Default for ArithmeticTerm {
fn default() -> Self { fn default() -> Self {
ArithmeticTerm::Number(Number::default()) ArithmeticTerm::Number(Number::default())
@@ -57,7 +45,7 @@ pub(crate) struct ArithInstructionIterator<'a> {
state_stack: Vec<TermIterState<'a>>, state_stack: Vec<TermIterState<'a>>,
} }
pub(crate) type ArithCont = (CodeDeque, Option<ArithmeticTerm>); pub(crate) type ArithCont = (CodeDeque, ArithmeticTerm);
impl<'a> ArithInstructionIterator<'a> { impl<'a> ArithInstructionIterator<'a> {
fn push_subterm(&mut self, lvl: Level, term: &'a Term) { fn push_subterm(&mut self, lvl: Level, term: &'a Term) {
@@ -76,7 +64,7 @@ impl<'a> ArithInstructionIterator<'a> {
return Err(ArithmeticError::NonEvaluableFunctor( return Err(ArithmeticError::NonEvaluableFunctor(
Literal::Atom(atom!(".")), Literal::Atom(atom!(".")),
2, 2,
)) ));
} }
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Shallow, cell, var_ptr.clone()), Term::Var(cell, var_ptr) => TermIterState::Var(Level::Shallow, cell, var_ptr.clone()),
}; };
@@ -90,7 +78,7 @@ impl<'a> ArithInstructionIterator<'a> {
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum ArithTermRef<'a> { pub(crate) enum ArithTermRef<'a> {
Literal(Literal), Literal(Literal),
Op(Atom, usize), // name, arity. Op(Level, &'a Cell<RegType>, Atom, usize), // name, arity.
Var(Level, &'a Cell<VarReg>, VarPtr), Var(Level, &'a Cell<VarReg>, VarPtr),
} }
@@ -105,7 +93,7 @@ impl<'a> Iterator for ArithInstructionIterator<'a> {
let arity = subterms.len(); let arity = subterms.len();
if child_num == arity { if child_num == arity {
return Some(Ok(ArithTermRef::Op(name, arity))); return Some(Ok(ArithTermRef::Op(lvl, cell, name, arity)));
} else { } else {
self.state_stack.push(TermIterState::Clause( self.state_stack.push(TermIterState::Clause(
lvl, lvl,
@@ -139,7 +127,6 @@ impl<'a> Iterator for ArithInstructionIterator<'a> {
pub(crate) struct ArithmeticEvaluator<'a> { pub(crate) struct ArithmeticEvaluator<'a> {
marker: &'a mut DebrayAllocator, marker: &'a mut DebrayAllocator,
interm: Vec<ArithmeticTerm>, interm: Vec<ArithmeticTerm>,
interm_c: usize,
} }
pub(crate) trait ArithmeticTermIter<'a> { pub(crate) trait ArithmeticTermIter<'a> {
@@ -180,11 +167,10 @@ fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), Ari
} }
impl<'a> ArithmeticEvaluator<'a> { impl<'a> ArithmeticEvaluator<'a> {
pub(crate) fn new(marker: &'a mut DebrayAllocator, target_int: usize) -> Self { pub(crate) fn new(marker: &'a mut DebrayAllocator) -> Self {
ArithmeticEvaluator { ArithmeticEvaluator {
marker, marker,
interm: Vec::new(), interm: Vec::new(),
interm_c: target_int,
} }
} }
@@ -252,56 +238,37 @@ impl<'a> ArithmeticEvaluator<'a> {
} }
} }
fn incr_interm(&mut self) -> usize { fn try_add_to_free_list(&mut self, a1: ArithmeticTerm) {
let temp = self.interm_c; if let ArithmeticTerm::IntermReg(t) = a1 {
self.marker.add_reg_to_free_list(RegType::Temp(t));
self.interm.push(ArithmeticTerm::Interm(temp)); }
self.interm_c += 1;
temp
} }
fn instr_from_clause( fn instr_from_clause(
&mut self, &mut self,
name: Atom, name: Atom,
arity: usize, arity: usize,
arg: usize,
) -> Result<Instruction, ArithmeticError> { ) -> Result<Instruction, ArithmeticError> {
match arity { match arity {
1 => { 1 => {
let a1 = self.interm.pop().unwrap(); let a1 = self.interm.pop().unwrap();
let ninterm = if a1.interm_or(0) == 0 { self.interm.push(ArithmeticTerm::IntermReg(arg));
self.incr_interm() self.try_add_to_free_list(a1);
} else {
self.interm.push(a1);
a1.interm_or(0)
};
self.get_unary_instr(name, a1, ninterm) self.get_unary_instr(name, a1, arg)
} }
2 => { 2 => {
let a2 = self.interm.pop().unwrap(); let a2 = self.interm.pop().unwrap();
let a1 = self.interm.pop().unwrap(); let a1 = self.interm.pop().unwrap();
let min_interm = min(a1.interm_or(0), a2.interm_or(0)); self.interm.push(ArithmeticTerm::IntermReg(arg));
let ninterm = if min_interm == 0 { self.try_add_to_free_list(a1);
let max_interm = max(a1.interm_or(0), a2.interm_or(0)); self.try_add_to_free_list(a2);
if max_interm == 0 { self.get_binary_instr(name, a1, a2, arg)
self.incr_interm()
} else {
self.interm.push(ArithmeticTerm::Interm(max_interm));
self.interm_c = max_interm + 1;
max_interm
}
} else {
self.interm.push(ArithmeticTerm::Interm(min_interm));
self.interm_c = min_interm + 1;
min_interm
};
self.get_binary_instr(name, a1, a2, ninterm)
} }
_ => Err(ArithmeticError::NonEvaluableFunctor( _ => Err(ArithmeticError::NonEvaluableFunctor(
Literal::Atom(name), Literal::Atom(name),
@@ -346,13 +313,20 @@ impl<'a> ArithmeticEvaluator<'a> {
self.interm.push(ArithmeticTerm::Reg(r)); self.interm.push(ArithmeticTerm::Reg(r));
} }
ArithTermRef::Op(name, arity) => { ArithTermRef::Op(lvl, cell, name, arity) => {
code.push_back(self.instr_from_clause(name, arity)?); self.marker
.mark_non_var::<QueryInstruction>(lvl, term_loc, cell, &mut code);
if let RegType::Temp(t) = cell.get() {
code.push_back(self.instr_from_clause(name, arity, t)?);
} else {
unreachable!()
}
} }
} }
} }
Ok((code, self.interm.pop())) Ok((code, self.interm.pop().unwrap()))
} }
} }
@@ -387,7 +361,7 @@ pub(crate) fn rnd_i(n: &'_ Number, arena: &mut Arena) -> Result<Number, EvalErro
))) )))
} }
} }
Number::Rational(ref r) => { Number::Rational(r) => {
let floor = r.floor(); let floor = r.floor();
if let Ok(value) = Fixnum::build_with_checked(&floor) { if let Ok(value) = Fixnum::build_with_checked(&floor) {
@@ -410,9 +384,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(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(r) => r.to_f64().value(),
} }
} }
@@ -540,33 +514,23 @@ 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(n2)) => n1.get_num().num_eq(&**n2),
(Number::Integer(ref n1), &Number::Fixnum(n2)) => n1.num_eq(&n2.get_num()), (Number::Integer(n1), &Number::Fixnum(n2)) => n1.num_eq(&n2.get_num()),
(&Number::Fixnum(n1), Number::Rational(ref n2)) => { (&Number::Fixnum(n1), Number::Rational(n2)) => {
Integer::from(n1.get_num()).num_eq(&**n2) Integer::from(n1.get_num()).num_eq(&**n2)
} }
(Number::Rational(ref n1), &Number::Fixnum(n2)) => { (Number::Rational(n1), &Number::Fixnum(n2)) => n1.num_eq(&Integer::from(n2.get_num())),
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(n1), Number::Integer(n2)) => n1.eq(n2),
(Number::Integer(ref n1), Number::Float(n2)) => { (Number::Integer(n1), Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(n2),
OrderedFloat(n1.to_f64().value()).eq(n2) (&Number::Float(n1), Number::Integer(n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())),
} (Number::Integer(n1), Number::Rational(n2)) => n1.num_eq(&**n2),
(&Number::Float(n1), Number::Integer(ref n2)) => { (Number::Rational(n1), Number::Integer(n2)) => n1.num_eq(&**n2),
n1.eq(&OrderedFloat(n2.to_f64().value())) (Number::Rational(n1), &Number::Float(n2)) => OrderedFloat(n1.to_f64().value()).eq(&n2),
} (&Number::Float(n1), Number::Rational(n2)) => n1.eq(&OrderedFloat(n2.to_f64().value())),
(Number::Integer(ref n1), Number::Rational(ref n2)) => n1.num_eq(&**n2),
(Number::Rational(ref n1), Number::Integer(ref n2)) => n1.num_eq(&**n2),
(Number::Rational(ref n1), &Number::Float(n2)) => {
OrderedFloat(n1.to_f64().value()).eq(&n2)
}
(&Number::Float(n1), Number::Rational(ref n2)) => {
n1.eq(&OrderedFloat(n2.to_f64().value()))
}
(&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2), (&Number::Float(f1), &Number::Float(f2)) => f1.eq(&f2),
(Number::Rational(ref r1), Number::Rational(ref r2)) => r1.eq(r2), (Number::Rational(r1), Number::Rational(r2)) => r1.eq(r2),
} }
} }
} }
@@ -633,9 +597,7 @@ impl Ord for Number {
(&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(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)) => {
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less) (*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
} }

View File

@@ -1,5 +1,4 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work use crate::machine::heap::AllocError;
use crate::parser::ast::MAX_ARITY; use crate::parser::ast::MAX_ARITY;
use crate::raw_block::*; use crate::raw_block::*;
use crate::types::*; use crate::types::*;
@@ -15,13 +14,13 @@ use std::sync::Mutex;
use std::sync::RwLock; use std::sync::RwLock;
use std::sync::Weak; use std::sync::Weak;
use arcu::Rcu;
use arcu::atomic::Arcu; use arcu::atomic::Arcu;
use arcu::epoch_counters::GlobalEpochCounterPool; use arcu::epoch_counters::GlobalEpochCounterPool;
use arcu::rcu_ref::RcuRef; use arcu::rcu_ref::RcuRef;
use arcu::Rcu;
use indexmap::IndexSet; use indexmap::IndexSet;
use scryer_modular_bitfield::prelude::*; use modular_bitfield::prelude::*;
#[bitfield] #[bitfield]
#[repr(u64)] #[repr(u64)]
@@ -307,8 +306,7 @@ impl Atom {
AtomTableRef::try_map(atom_table.inner.read(), |buf| unsafe { AtomTableRef::try_map(atom_table.inner.read(), |buf| unsafe {
let ptr = buf let ptr = buf
.block .block
.base .get_unchecked(self.flat_index() as usize - STRINGS.len());
.add(self.flat_index() as usize - STRINGS.len());
// TODO use std::ptr::from_raw_parts instead when feature ptr_metadata is stable rust-lang/rust#81513 // TODO use std::ptr::from_raw_parts instead when feature ptr_metadata is stable rust-lang/rust#81513
let atom_data = &*(std::ptr::slice_from_raw_parts(ptr, 0) as *const AtomData); let atom_data = &*(std::ptr::slice_from_raw_parts(ptr, 0) as *const AtomData);
let len = atom_data.header.len(); let len = atom_data.header.len();
@@ -346,11 +344,7 @@ impl Atom {
let c1 = it.next(); let c1 = it.next();
let c2 = it.next(); let c2 = it.next();
if c2.is_none() { if c2.is_none() { c1 } else { None }
c1
} else {
None
}
} }
#[inline] #[inline]
@@ -390,10 +384,12 @@ impl Atom {
} }
unsafe fn write_to_ptr(string: &str, ptr: *mut u8) { unsafe fn write_to_ptr(string: &str, ptr: *mut u8) {
unsafe {
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.add(mem::size_of::<AtomHeader>()); let str_ptr = ptr.add(mem::size_of::<AtomHeader>());
ptr::copy_nonoverlapping(string.as_ptr(), str_ptr, string.len()); ptr::copy_nonoverlapping(string.as_ptr(), str_ptr, string.len());
} }
}
impl PartialOrd for Atom { impl PartialOrd for Atom {
#[inline] #[inline]
@@ -436,21 +432,21 @@ impl InnerAtomTable {
impl AtomTable { impl AtomTable {
#[inline] #[inline]
pub fn new() -> Arc<Self> { pub fn new() -> Result<Arc<Self>, AllocError> {
let upgraded = global_atom_table().read().unwrap().upgrade(); let upgraded = global_atom_table().read().unwrap().upgrade();
// don't inline upgraded, otherwise temporary will be dropped too late in case of None // don't inline upgraded, otherwise temporary will be dropped too late in case of None
if let Some(atom_table) = upgraded { if let Some(atom_table) = upgraded {
atom_table Ok(atom_table)
} else { } else {
let mut guard = global_atom_table().write().unwrap(); let mut guard = global_atom_table().write().unwrap();
// try to upgrade again in case we lost the race on the write lock // try to upgrade again in case we lost the race on the write lock
if let Some(atom_table) = guard.upgrade() { if let Some(atom_table) = guard.upgrade() {
atom_table Ok(atom_table)
} else { } else {
let atom_table = Arc::new(Self { let atom_table = Arc::new(Self {
inner: Arcu::new( inner: Arcu::new(
InnerAtomTable { InnerAtomTable {
block: RawBlock::new(), block: RawBlock::new()?,
table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool), table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool),
}, },
GlobalEpochCounterPool, GlobalEpochCounterPool,
@@ -458,11 +454,16 @@ impl AtomTable {
update: Mutex::new(()), update: Mutex::new(()),
}); });
*guard = Arc::downgrade(&atom_table); *guard = Arc::downgrade(&atom_table);
atom_table Ok(atom_table)
} }
} }
} }
#[inline]
pub fn retrieve() -> Arc<Self> {
global_atom_table().read().unwrap().upgrade().unwrap()
}
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> { pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
self.inner.read().table.read() self.inner.read().table.read()
} }
@@ -516,12 +517,13 @@ impl AtomTable {
} }
}; };
let ptr_base = block_epoch.block.base.addr(); // SAFETY: `len_ptr` was obtained from `block_epoch.block.alloc()`
let len_offset = block_epoch.block.get_offset(len_ptr);
write_to_ptr(string, len_ptr); write_to_ptr(string, len_ptr);
let atom = AtomCell::new() let atom = AtomCell::new()
.with_name((STRINGS.len() + len_ptr.addr() - ptr_base) as u64) .with_name((STRINGS.len() + len_offset) as u64)
.with_arity(0) .with_arity(0)
.with_f(false) .with_f(false)
.with_m(false) .with_m(false)
@@ -533,7 +535,7 @@ impl AtomTable {
table.insert(atom); table.insert(atom);
block_epoch.table.replace(table); block_epoch.table.replace(table);
// expicit drop to ensure we don't accidentally drop it early // explicit drop to ensure we don't accidentally drop it early
drop(update_guard); drop(update_guard);
return atom; return atom;

View File

@@ -145,7 +145,7 @@ impl BranchCodeStack {
settings.trust_me() settings.trust_me()
}); });
combined_code.extend(code.into_iter()); combined_code.extend(code);
} }
} }
@@ -345,10 +345,10 @@ impl<'a> AddToFreeList<'a, QueryInstruction> for CodeGenerator {
fn structure_cell(term: &Term) -> Option<&Cell<RegType>> { fn structure_cell(term: &Term) -> Option<&Cell<RegType>> {
match term { match term {
&Term::Cons(ref cell, ..) Term::Cons(cell, ..)
| &Term::Clause(ref cell, ..) | Term::Clause(cell, ..)
| Term::PartialString(ref cell, ..) | Term::PartialString(cell, ..)
| Term::CompleteString(ref cell, ..) => Some(cell), | Term::CompleteString(cell, ..) => Some(cell),
_ => None, _ => None,
} }
} }
@@ -401,18 +401,18 @@ impl CodeGenerator {
&Term::AnonVar => { &Term::AnonVar => {
Self::add_or_increment_void_instr::<Target>(target); Self::add_or_increment_void_instr::<Target>(target);
} }
&Term::Cons(ref cell, ..) Term::Cons(cell, ..)
| &Term::Clause(ref cell, ..) | Term::Clause(cell, ..)
| Term::PartialString(ref cell, ..) | Term::PartialString(cell, ..)
| Term::CompleteString(ref cell, ..) => { | Term::CompleteString(cell, ..) => {
self.marker self.marker
.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(_, constant) => {
target.push_back(Target::constant_subterm(*constant)); target.push_back(Target::constant_subterm(*constant));
} }
Term::Var(ref cell, ref var_ptr) => { Term::Var(cell, 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(),
@@ -560,27 +560,19 @@ impl CodeGenerator {
&InlinedClauseType::CompareNumber(mut cmp) => { &InlinedClauseType::CompareNumber(mut cmp) => {
self.marker.reset_arg(2); self.marker.reset_arg(2);
let (mut lcode, at_1) = self.compile_arith_expr(&terms[0], 1, term_loc, 1)?; let (mut lcode, at_1) = self.compile_arith_expr(&terms[0], term_loc, 1)?;
let (mut rcode, at_2) = self.compile_arith_expr(&terms[1], term_loc, 2)?;
if !matches!(terms[0], Term::Var(..)) {
self.marker.advance_arg();
}
let (mut rcode, at_2) = self.compile_arith_expr(&terms[1], 2, term_loc, 2)?;
code.append(&mut lcode); code.append(&mut lcode);
code.append(&mut rcode); code.append(&mut rcode);
let at_1 = at_1.unwrap_or(interm!(1));
let at_2 = at_2.unwrap_or(interm!(2));
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::Atom(..)) => { Term::Literal(_, Literal::Atom(..)) => {
instr!("$succeed") instr!("$succeed")
} }
Term::Var(ref vr, ref name) => { Term::Var(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(
@@ -608,7 +600,7 @@ impl CodeGenerator {
Term::Literal(..) => { Term::Literal(..) => {
instr!("$succeed") instr!("$succeed")
} }
Term::Var(ref vr, ref name) => { Term::Var(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(
@@ -629,7 +621,7 @@ impl CodeGenerator {
| Term::CompleteString(..) => { | Term::CompleteString(..) => {
instr!("$succeed") instr!("$succeed")
} }
Term::Var(ref vr, ref name) => { Term::Var(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(
@@ -647,7 +639,10 @@ impl CodeGenerator {
} }
}, },
InlinedClauseType::IsRational(..) => match terms[0] { InlinedClauseType::IsRational(..) => match terms[0] {
Term::Literal(_, Literal::Rational(_)) => { Term::Literal(
_,
Literal::Rational(_) | Literal::Fixnum(_) | Literal::Integer(_),
) => {
instr!("$succeed") instr!("$succeed")
} }
Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
@@ -687,10 +682,13 @@ impl CodeGenerator {
} }
}, },
InlinedClauseType::IsNumber(..) => match terms[0] { InlinedClauseType::IsNumber(..) => match terms[0] {
Term::Literal(_, Literal::F64(..)) Term::Literal(
| Term::Literal(_, Literal::Rational(_)) _,
| Term::Literal(_, Literal::Integer(_)) Literal::F64(..)
| Term::Literal(_, Literal::Fixnum(_)) => { | Literal::Rational(_)
| Literal::Integer(_)
| Literal::Fixnum(_),
) => {
instr!("$succeed") instr!("$succeed")
} }
Term::Var(ref vr, ref name) => { Term::Var(ref vr, ref name) => {
@@ -735,7 +733,7 @@ impl CodeGenerator {
Term::Literal(_, Literal::Integer(_)) | Term::Literal(_, Literal::Fixnum(_)) => { Term::Literal(_, Literal::Integer(_)) | Term::Literal(_, Literal::Fixnum(_)) => {
instr!("$succeed") instr!("$succeed")
} }
Term::Var(ref vr, name) => { Term::Var(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(
@@ -787,11 +785,10 @@ impl CodeGenerator {
fn compile_arith_expr( fn compile_arith_expr(
&mut self, &mut self,
term: &Term, term: &Term,
target_int: usize,
term_loc: GenContext, term_loc: GenContext,
arg: usize, arg: usize,
) -> Result<ArithCont, ArithmeticError> { ) -> Result<ArithCont, ArithmeticError> {
let mut evaluator = ArithmeticEvaluator::new(&mut self.marker, target_int); let mut evaluator = ArithmeticEvaluator::new(&mut self.marker);
evaluator.compile_is(term, term_loc, arg) evaluator.compile_is(term, term_loc, arg)
} }
@@ -804,7 +801,7 @@ impl CodeGenerator {
) -> Result<(), CompilationError> { ) -> Result<(), CompilationError> {
macro_rules! compile_expr { macro_rules! compile_expr {
($self:expr, $terms:expr, $term_loc:expr, $code:expr) => {{ ($self:expr, $terms:expr, $term_loc:expr, $code:expr) => {{
let (acode, at) = $self.compile_arith_expr($terms, 1, $term_loc, 2)?; let (acode, at) = $self.compile_arith_expr($terms, $term_loc, 2)?;
$code.extend(acode.into_iter()); $code.extend(acode.into_iter());
at at
}}; }};
@@ -831,7 +828,7 @@ impl CodeGenerator {
self.marker self.marker
.mark_anon_var::<QueryInstruction>(Level::Shallow, term_loc, code); .mark_anon_var::<QueryInstruction>(Level::Shallow, term_loc, code);
if let Term::Var(ref vr, ref var) = &terms[1] { if let Term::Var(vr, var) = &terms[1] {
let var_num = var.to_var_num().unwrap(); let var_num = var.to_var_num().unwrap();
// if var is an anonymous variable, insert // if var is an anonymous variable, insert
@@ -859,16 +856,18 @@ impl CodeGenerator {
} }
} }
Term::Literal( Term::Literal(
_, ref cell,
c @ Literal::Integer(_) c @ Literal::Integer(_)
| c @ Literal::F64(..) | c @ Literal::F64(..)
| c @ Literal::Rational(_) | c @ Literal::Rational(_)
| c @ Literal::Fixnum(_), | c @ Literal::Fixnum(_),
) => { ) => {
let v = HeapCellValue::from(c); let v = HeapCellValue::from(c);
code.push_back(instr!("put_constant", Level::Shallow, v, temp_v!(1)));
self.marker.advance_arg(); self.marker
.mark_non_var::<QueryInstruction>(Level::Shallow, term_loc, cell, code);
code.push_back(instr!("put_constant", Level::Shallow, v, temp_v!(1)));
compile_expr!(self, &terms[1], term_loc, code) compile_expr!(self, &terms[1], term_loc, code)
} }
_ => { _ => {
@@ -877,7 +876,6 @@ impl CodeGenerator {
} }
}; };
let at = at.unwrap_or(interm!(1));
self.add_call(code, instr!("is", temp_v!(1), at), call_policy); self.add_call(code, instr!("is", temp_v!(1), at), call_policy);
Ok(()) Ok(())
} }
@@ -996,20 +994,27 @@ impl CodeGenerator {
self.marker.in_tail_position = false; self.marker.in_tail_position = false;
self.marker.reset_contents(); self.marker.reset_contents();
} }
ClauseItem::FirstBranch(num_branches) => { ClauseItem::FirstBranch {
branch_num,
num_branches,
} => {
branch_code_stack.add_new_branch_stack(); branch_code_stack.add_new_branch_stack();
branch_code_stack.add_new_branch(); branch_code_stack.add_new_branch();
self.marker.branch_stack.add_branch_stack(num_branches); self.marker
.branch_stack
.add_branch_stack(branch_num.clone(), num_branches);
self.marker.add_branch(); self.marker.add_branch();
} }
ClauseItem::NextBranch => { ClauseItem::NextBranch { branch_num } => {
branch_code_stack.add_new_branch(); branch_code_stack.add_new_branch();
self.marker.add_branch(); self.marker.add_branch();
self.marker.branch_stack.incr_current_branch(); self.marker
.branch_stack
.incr_current_branch(branch_num.clone());
} }
ClauseItem::BranchEnd(depth) => { ClauseItem::BranchEnd { depth } => {
if !clause_iter.in_tail_position() { if !clause_iter.in_tail_position() {
let subsumed_hits = let subsumed_hits =
branch_code_stack.push_missing_vars(depth, &mut self.marker); branch_code_stack.push_missing_vars(depth, &mut self.marker);
@@ -1231,7 +1236,7 @@ impl CodeGenerator {
} }
self.skeleton.clauses.push_back(clause_index_info); self.skeleton.clauses.push_back(clause_index_info);
code.extend(clause_code.into_iter()); code.extend(clause_code);
} }
let index_code = if clauses_len > 1 || self.settings.is_extensible { let index_code = if clauses_len > 1 || self.settings.is_extensible {
@@ -1308,7 +1313,7 @@ impl CodeGenerator {
} }
} }
code.extend(code_segment.into_iter()); code.extend(code_segment);
} }
Ok(code) Ok(code)

View File

@@ -1,6 +1,6 @@
use crate::allocator::*; use crate::allocator::*;
use crate::codegen::SubsumedBranchHits; use crate::codegen::SubsumedBranchHits;
use crate::forms::{GenContext, Level}; use crate::forms::{BranchNumber, GenContext, Level};
use crate::instructions::*; use crate::instructions::*;
use crate::machine::disjuncts::VarData; use crate::machine::disjuncts::VarData;
use crate::parser::ast::*; use crate::parser::ast::*;
@@ -15,6 +15,7 @@ use indexmap::IndexMap;
use std::cell::Cell; use std::cell::Cell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::Arc;
pub type BranchHits = IndexMap<usize, BitVec, FxBuildHasher>; // key: var_num, value: branch arm occurrences. pub type BranchHits = IndexMap<usize, BitVec, FxBuildHasher>; // key: var_num, value: branch arm occurrences.
@@ -24,24 +25,26 @@ pub struct BranchOccurrences {
pub shallow_safety: BitSet<usize>, // unset means safe, set means unsafe (after the branch merge) pub shallow_safety: BitSet<usize>, // unset means safe, set means unsafe (after the branch merge)
pub deep_safety: BitSet<usize>, pub deep_safety: BitSet<usize>,
pub num_branches: usize, pub num_branches: usize,
pub current_branch: usize, pub current_branch_idx: usize,
pub current_branch_num: Arc<BranchNumber>,
pub subsumed_hits: SubsumedBranchHits, pub subsumed_hits: SubsumedBranchHits,
} }
impl BranchOccurrences { impl BranchOccurrences {
fn new(num_branches: usize) -> Self { fn new(current_branch_num: Arc<BranchNumber>, num_branches: usize) -> Self {
Self { Self {
hits: BranchHits::with_hasher(FxBuildHasher::default()), hits: BranchHits::with_hasher(FxBuildHasher::default()),
shallow_safety: BitSet::default(), shallow_safety: BitSet::default(),
deep_safety: BitSet::default(), deep_safety: BitSet::default(),
num_branches, num_branches,
current_branch: 0, current_branch_idx: 0,
current_branch_num,
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) { pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
debug_assert!(self.current_branch < self.num_branches); debug_assert!(self.current_branch_idx < self.num_branches);
let num_branches = self.num_branches; let num_branches = self.num_branches;
let entry = self let entry = self
@@ -49,7 +52,7 @@ impl BranchOccurrences {
.entry(var_num) .entry(var_num)
.or_insert_with(|| BitVec::repeat(false, num_branches)); .or_insert_with(|| BitVec::repeat(false, num_branches));
entry.set(self.current_branch, true); entry.set(self.current_branch_idx, true);
self.subsumed_hits.insert(var_num); self.subsumed_hits.insert(var_num);
} }
} }
@@ -76,19 +79,6 @@ impl DerefMut for BranchStack {
} }
impl BranchStack { impl BranchStack {
fn branch_subsumes(&self, branch: &BranchDesignator, sub_branch: &BranchDesignator) -> bool {
if branch.branch_stack_num < sub_branch.branch_stack_num {
if branch.branch_stack_num == 0 {
true
} else {
let idx = branch.branch_stack_num - 1;
self[idx].current_branch == branch.branch_num
}
} else {
branch == sub_branch
}
}
fn safety_unneeded_in_branch( fn safety_unneeded_in_branch(
&self, &self,
safety: &VarSafetyStatus, safety: &VarSafetyStatus,
@@ -96,9 +86,9 @@ impl BranchStack {
) -> bool { ) -> bool {
match safety { match safety {
VarSafetyStatus::Needed => false, VarSafetyStatus::Needed => false,
VarSafetyStatus::LocallyUnneeded(planter_branch) => { VarSafetyStatus::LocallyUnneeded(planter_branch) => planter_branch
self.branch_subsumes(planter_branch, branch) .branch_num
} .has_as_subbranch(&branch.branch_num),
VarSafetyStatus::GloballyUnneeded => true, VarSafetyStatus::GloballyUnneeded => true,
} }
} }
@@ -109,27 +99,24 @@ impl BranchStack {
} }
} }
pub(crate) fn add_branch_stack(&mut self, num_branches: usize) { pub(crate) fn add_branch_stack(&mut self, branch_num: Arc<BranchNumber>, num_branches: usize) {
self.push(BranchOccurrences::new(num_branches)); self.push(BranchOccurrences::new(branch_num, num_branches));
} }
pub(crate) fn current_branch_designator(&self) -> BranchDesignator { pub(crate) fn current_branch_designator(&self) -> BranchDesignator {
let branch_stack_num = self.len();
let branch_num = self let branch_num = self
.last() .last()
.map(|occurrences| occurrences.current_branch) .map(|occurrences| occurrences.current_branch_num.clone())
.unwrap_or(0); .unwrap_or_default();
BranchDesignator { BranchDesignator { branch_num }
branch_stack_num,
branch_num,
}
} }
#[inline] #[inline]
pub(crate) fn incr_current_branch(&mut self) { pub(crate) fn incr_current_branch(&mut self, branch_num: Arc<BranchNumber>) {
let branch_occurrences = self.last_mut().unwrap(); let branch_occurrences = self.last_mut().unwrap();
branch_occurrences.current_branch += 1; branch_occurrences.current_branch_idx += 1;
branch_occurrences.current_branch_num = branch_num;
} }
#[inline] #[inline]
@@ -171,7 +158,7 @@ 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(_, allocation) => {
if let PermVarAllocation::Done { if let PermVarAllocation::Done {
shallow_safety, shallow_safety,
deep_safety, deep_safety,
@@ -215,7 +202,7 @@ impl DebrayAllocator {
}, },
); );
let branch_designator = self.branch_stack.current_branch_designator(); let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() { let (deep_safety, shallow_safety) = match self.branch_stack.last_mut() {
Some(latest_branch) => { Some(latest_branch) => {
@@ -235,12 +222,12 @@ impl DebrayAllocator {
VarAlloc::Perm(_, allocation) => { VarAlloc::Perm(_, allocation) => {
let shallow_safety = VarSafetyStatus::needed_if( let shallow_safety = VarSafetyStatus::needed_if(
shallow_safety.contains(var_num), shallow_safety.contains(var_num),
branch_designator, &branch_designator,
); );
let deep_safety = VarSafetyStatus::needed_if( let deep_safety = VarSafetyStatus::needed_if(
deep_safety.contains(var_num), deep_safety.contains(var_num),
branch_designator, &branch_designator,
); );
if running_count < num_occurrences { if running_count < num_occurrences {
@@ -520,7 +507,7 @@ impl DebrayAllocator {
} }
pub(crate) fn mark_safe_var_unconditionally(&mut self, var_num: usize) { pub(crate) fn mark_safe_var_unconditionally(&mut self, var_num: usize) {
let branch_designator = self.branch_stack.current_branch_designator(); let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
match &mut self.var_data.records[var_num].allocation { match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm( VarAlloc::Perm(
@@ -531,11 +518,11 @@ impl DebrayAllocator {
.. ..
}, },
) => { ) => {
*deep_safety = VarSafetyStatus::unneeded(branch_designator); *deep_safety = VarSafetyStatus::unneeded(&branch_designator);
*shallow_safety = VarSafetyStatus::unneeded(branch_designator); *shallow_safety = VarSafetyStatus::unneeded(&branch_designator);
} }
VarAlloc::Temp { safety, .. } => { VarAlloc::Temp { safety, .. } => {
*safety = VarSafetyStatus::unneeded(branch_designator); *safety = VarSafetyStatus::unneeded(&branch_designator);
} }
_ => { _ => {
unreachable!() unreachable!()
@@ -544,7 +531,7 @@ impl DebrayAllocator {
} }
fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) { fn mark_safe_var(&mut self, var_num: usize, lvl: Level, term_loc: GenContext) {
let branch_designator = self.branch_stack.current_branch_designator(); let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
match &mut self.var_data.records[var_num].allocation { match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm( VarAlloc::Perm(
@@ -557,15 +544,14 @@ impl DebrayAllocator {
) => { ) => {
// GetVariable in head chunk is considered safe. // GetVariable in head chunk is considered safe.
if lvl == Level::Deep { if lvl == Level::Deep {
*deep_safety = VarSafetyStatus::unneeded(branch_designator); *deep_safety = VarSafetyStatus::unneeded(&branch_designator);
*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 if let Some(&temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c) { } else if let Some(&temp_var_num) = self.shallow_temp_mappings.get(&self.arg_c) {
match &mut self.var_data.records[temp_var_num].allocation { match &mut self.var_data.records[temp_var_num].allocation {
VarAlloc::Temp { VarAlloc::Temp {
ref mut to_perm_var_num, to_perm_var_num, ..
..
} => { } => {
*to_perm_var_num = Some(var_num); *to_perm_var_num = Some(var_num);
} }
@@ -573,7 +559,7 @@ impl DebrayAllocator {
} }
} }
} }
VarAlloc::Temp { ref mut safety, .. } => { VarAlloc::Temp { safety, .. } => {
*safety = VarSafetyStatus::GloballyUnneeded; *safety = VarSafetyStatus::GloballyUnneeded;
} }
_ => { _ => {
@@ -588,16 +574,10 @@ impl DebrayAllocator {
r: RegType, r: RegType,
arg_c: usize, arg_c: usize,
) -> Instruction { ) -> Instruction {
let branch_designator = self.branch_stack.current_branch_designator(); let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
match &mut self.var_data.records[var_num].allocation { match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm( VarAlloc::Perm(_, PermVarAllocation::Done { shallow_safety, .. }) => {
_,
PermVarAllocation::Done {
ref mut shallow_safety,
..
},
) => {
if !self.in_tail_position if !self.in_tail_position
|| self || self
.branch_stack .branch_stack
@@ -605,7 +585,7 @@ impl DebrayAllocator {
{ {
Target::argument_to_value(r, arg_c) Target::argument_to_value(r, arg_c)
} else { } else {
*shallow_safety = VarSafetyStatus::unneeded(branch_designator); *shallow_safety = VarSafetyStatus::unneeded(&branch_designator);
Target::unsafe_argument_to_value(r, arg_c) Target::unsafe_argument_to_value(r, arg_c)
} }
} }
@@ -624,34 +604,28 @@ impl DebrayAllocator {
var_num: usize, var_num: usize,
r: RegType, r: RegType,
) -> Instruction { ) -> Instruction {
let branch_designator = self.branch_stack.current_branch_designator(); let branch_designator = Arc::new(self.branch_stack.current_branch_designator());
match &mut self.var_data.records[var_num].allocation { match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm( VarAlloc::Perm(_, PermVarAllocation::Done { deep_safety, .. }) => {
_,
PermVarAllocation::Done {
ref mut deep_safety,
..
},
) => {
if self if self
.branch_stack .branch_stack
.safety_unneeded_in_branch(deep_safety, &branch_designator) .safety_unneeded_in_branch(deep_safety, &branch_designator)
{ {
Target::subterm_to_value(r) Target::subterm_to_value(r)
} else { } else {
*deep_safety = VarSafetyStatus::unneeded(branch_designator); *deep_safety = VarSafetyStatus::unneeded(&branch_designator);
Target::unsafe_subterm_to_value(r) Target::unsafe_subterm_to_value(r)
} }
} }
VarAlloc::Temp { ref mut safety, .. } => { VarAlloc::Temp { safety, .. } => {
if self if self
.branch_stack .branch_stack
.safety_unneeded_in_branch(safety, &branch_designator) .safety_unneeded_in_branch(safety, &branch_designator)
{ {
Target::subterm_to_value(r) Target::subterm_to_value(r)
} else { } else {
*safety = VarSafetyStatus::unneeded(branch_designator); *safety = VarSafetyStatus::unneeded(&branch_designator);
Target::unsafe_subterm_to_value(r) Target::unsafe_subterm_to_value(r)
} }
} }
@@ -887,7 +861,7 @@ impl Allocator for DebrayAllocator {
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(_, 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

@@ -1,5 +1,7 @@
:- use_module(library(charsio)). :- use_module(library(charsio)).
:- use_module(library(lists)). :- use_module(library(lists)).
:- use_module(library(pio)).
:- use_module(library(dcgs)).
:- initialization(unit_test). :- initialization(unit_test).
@@ -10,22 +12,12 @@ unit_test :-
Cs = "a£\x2124\". Cs = "a£\x2124\".
write_f :- write_f :-
open('x.txt', write, Stream, [type(binary)]),
F = put_byte(Stream),
chars_utf8bytes("£\x2124\\x2764\\x1F496\\n", Bs), chars_utf8bytes("£\x2124\\x2764\\x1F496\\n", Bs),
maplist(F, Bs), maplist(char_code, Cs, Bs),
close(Stream). phrase_to_file(Cs, "x.txt", [type(binary)]).
get_bytes(Stream, Res) :- get_bytes(Stream, [], Res).
get_bytes(Stream, Acc, Res) :-
get_byte(Stream, B),
(B =:= -1 ->
reverse(Acc, Res)
; get_bytes(Stream, [B|Acc], Res)).
read_f :- read_f :-
open('x.txt', read, Stream, [type(binary)]), phrase_from_file(seq(Cs), "x.txt", [type(binary)]),
get_bytes(Stream, Bs), maplist(char_code, Cs, Bs),
chars_utf8bytes(Cs, Bs), chars_utf8bytes(Chars, Bs),
write(Cs), write(Chars).
close(Stream).

View File

@@ -26,17 +26,16 @@ use crate::machine::machine_errors::DomainErrorType;
use crate::parser::ast::{Fixnum, MightNotFitInFixnum}; use crate::parser::ast::{Fixnum, MightNotFitInFixnum};
use dashu::Integer; use dashu::Integer;
use libffi::middle::{Arg, Cif, CodePtr, Type}; use libffi::middle::{Arg, Cif, CodePtr, Ret, Type};
use libloading::{Library, Symbol}; use libloading::{Library, Symbol};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use std::alloc::{self, Layout}; use std::alloc::{self, Layout};
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::ffi::{c_char, c_void, CStr, CString}; use std::ffi::{CStr, CString, c_char, c_void};
use std::fmt::Debug; use std::fmt::Debug;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::ptr::NonNull; use std::ptr::NonNull;
pub struct FunctionDefinition { pub struct FunctionDefinition {
@@ -55,7 +54,9 @@ pub struct FunctionImpl {
impl FunctionImpl { impl FunctionImpl {
unsafe fn call_void(&self, args: &[Arg], _: &mut Arena) -> Result<Value, FfiError> { unsafe fn call_void(&self, args: &[Arg], _: &mut Arena) -> Result<Value, FfiError> {
self.cif.call::<()>(self.code_ptr, args); unsafe {
self.cif.call_return_into(self.code_ptr, args, Ret::void());
}
Ok(Value::Number(Number::Fixnum(Fixnum::build_with(0)))) Ok(Value::Number(Number::Fixnum(Fixnum::build_with(0))))
} }
@@ -64,7 +65,7 @@ impl FunctionImpl {
Integer: From<T>, Integer: From<T>,
T: Copy + TryInto<i64> + MightNotFitInFixnum, T: Copy + TryInto<i64> + MightNotFitInFixnum,
{ {
let n = self.cif.call::<T>(self.code_ptr, args); let n = unsafe { self.cif.call::<T>(self.code_ptr, args) };
Ok(Value::Number(fixnum!(Number, n, arena))) Ok(Value::Number(fixnum!(Number, n, arena)))
} }
@@ -72,7 +73,7 @@ impl FunctionImpl {
where where
T: Into<f64>, T: Into<f64>,
{ {
let n = self.cif.call::<T>(self.code_ptr, args); let n = unsafe { self.cif.call::<T>(self.code_ptr, args) };
Ok(Value::Number(Number::Float(OrderedFloat(n.into())))) Ok(Value::Number(Number::Float(OrderedFloat(n.into()))))
} }
@@ -114,12 +115,8 @@ impl FunctionImpl {
let alloc = FfiStruct::new(layout, FfiAllocator::Rust)?; let alloc = FfiStruct::new(layout, FfiAllocator::Rust)?;
unsafe { unsafe {
libffi::raw::ffi_call( self.cif
self.cif.as_raw_ptr(), .call_return_into(self.code_ptr, args, alloc.as_ret())
Some(*self.code_ptr.as_safe_fun()),
alloc.ptr.as_ptr(),
args.as_ptr() as *mut *mut c_void,
)
}; };
let struct_val = let struct_val =
@@ -152,7 +149,7 @@ impl FunctionImpl {
FfiType::Ptr => FunctionImpl::call_ptr, FfiType::Ptr => FunctionImpl::call_ptr,
FfiType::CStr => FunctionImpl::call_cstr, FfiType::CStr => FunctionImpl::call_cstr,
FfiType::Struct(name) => { FfiType::Struct(name) => {
return unsafe { self.call_struct(name, args, arena, structs_table) } return unsafe { self.call_struct(name, args, arena, structs_table) };
} }
}; };
unsafe { call_fn(self, args, arena) } unsafe { call_fn(self, args, arena) }
@@ -180,10 +177,17 @@ impl StructImpl {
fn build( fn build(
&self, &self,
name: Atom,
structs_table: &HashMap<Atom, StructImpl>, structs_table: &HashMap<Atom, StructImpl>,
struct_args: &mut [Value], struct_args: &mut [Value],
) -> Result<FfiStruct, FfiError> { ) -> Result<FfiStruct, FfiError> {
let args = ArgValue::build_args(struct_args, &self.fields, structs_table)?; let args = ArgValue::build_args(
name,
ArgCountMismatchKind::Struct,
struct_args,
&self.fields,
structs_table,
)?;
let alloc = FfiStruct::new(self.layout()?, FfiAllocator::Rust)?; let alloc = FfiStruct::new(self.layout()?, FfiAllocator::Rust)?;
@@ -200,7 +204,9 @@ impl StructImpl {
.extend(Layout::new::<T>()) .extend(Layout::new::<T>())
.map_err(|_| FfiError::LayoutError)?; .map_err(|_| FfiError::LayoutError)?;
*layout = new_layout; *layout = new_layout;
unsafe {
ptr.byte_offset(offset as isize).cast::<T>().write(val); ptr.byte_offset(offset as isize).cast::<T>().write(val);
}
Ok(()) Ok(())
} }
@@ -261,7 +267,7 @@ impl StructImpl {
.extend(Layout::new::<T>()) .extend(Layout::new::<T>())
.map_err(|_| FfiError::LayoutError)?; .map_err(|_| FfiError::LayoutError)?;
*layout = new_layout; *layout = new_layout;
let n = std::ptr::read::<T>(ptr.byte_offset(offset as isize).cast()); let n = unsafe { std::ptr::read::<T>(ptr.byte_offset(offset as isize).cast()) };
Ok(n) Ok(n)
} }
@@ -274,7 +280,7 @@ impl StructImpl {
T: Copy + TryInto<i64> + MightNotFitInFixnum, T: Copy + TryInto<i64> + MightNotFitInFixnum,
Integer: From<T>, Integer: From<T>,
{ {
let n = read_primitive::<T>(ptr, layout)?; let n = unsafe { read_primitive::<T>(ptr, layout)? };
Ok(Value::Number(fixnum!(Number, n, arena))) Ok(Value::Number(fixnum!(Number, n, arena)))
} }
@@ -285,7 +291,7 @@ impl StructImpl {
where where
T: Into<f64>, T: Into<f64>,
{ {
let n = read_primitive::<T>(ptr, layout)?; let n = unsafe { read_primitive::<T>(ptr, layout)? };
Ok(Value::Number(Number::Float(OrderedFloat(n.into())))) Ok(Value::Number(Number::Float(OrderedFloat(n.into()))))
} }
@@ -329,7 +335,9 @@ impl StructImpl {
substruct_type.read(field_ptr, *substruct, struct_table, arena)?; substruct_type.read(field_ptr, *substruct, struct_table, arena)?;
Ok(struct_val) Ok(struct_val)
} }
FfiType::Void => return Err(FfiError::VoidArgumentType), FfiType::Void => {
return Err(FfiError::UnsupportedArgumentType(Some(atom!("void"))));
}
}; };
returns.push(val?); returns.push(val?);
} }
@@ -338,48 +346,6 @@ impl StructImpl {
} }
} }
struct PointerArgs<'a, 'val> {
memory: Vec<Arg>,
phantom: PhantomData<&'a mut ArgValue<'val>>,
}
impl<'args, 'val> PointerArgs<'args, 'val> {
fn new(args: &'args [ArgValue<'val>]) -> Self {
let args = args
.iter()
.map(|arg| match arg {
ArgValue::U8(a) => libffi::middle::arg(a),
ArgValue::I8(a) => libffi::middle::arg(a),
ArgValue::U16(a) => libffi::middle::arg(a),
ArgValue::I16(a) => libffi::middle::arg(a),
ArgValue::U32(a) => libffi::middle::arg(a),
ArgValue::I32(a) => libffi::middle::arg(a),
ArgValue::U64(a) => libffi::middle::arg(a),
ArgValue::I64(a) => libffi::middle::arg(a),
ArgValue::F32(a) => libffi::middle::arg(a),
ArgValue::F64(a) => libffi::middle::arg(a),
ArgValue::Ptr(ptr, _) => Arg::new(ptr),
ArgValue::Struct(s) => unsafe {
std::mem::transmute::<*mut c_void, Arg>(s.ptr.as_ptr())
},
})
.collect();
PointerArgs {
memory: args,
phantom: PhantomData,
}
}
}
impl Deref for PointerArgs<'_, '_> {
type Target = [Arg];
fn deref(&self) -> &Self::Target {
&self.memory
}
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum FfiType { enum FfiType {
Void, Void,
@@ -399,9 +365,49 @@ enum FfiType {
Struct(Atom), Struct(Atom),
} }
trait ToFfiType {
const TYPE: FfiType;
}
macro_rules! impl_to_ffi_type {
($($t:ty => $v:ident);*$(;)?) => {
$(
impl ToFfiType for $t {
const TYPE: FfiType = FfiType::$v;
}
)*
};
}
impl_to_ffi_type!(
u8 => U8;
i8 => I8;
u16 => U16;
i16 => I16;
u32 => U32;
i32 => I32;
u64 => U64;
i64 => I64;
f32 => F32;
f64 => F64;
);
impl FfiType { impl FfiType {
fn from_atom(atom: &Atom) -> Self { fn from_atom(atom: &Atom) -> Self {
match atom { match atom {
atom!("char") => <core::ffi::c_char as ToFfiType>::TYPE,
atom!("uchar") => <core::ffi::c_uchar as ToFfiType>::TYPE,
atom!("schar") => <core::ffi::c_schar as ToFfiType>::TYPE,
atom!("short") => <core::ffi::c_short as ToFfiType>::TYPE,
atom!("ushort") => <core::ffi::c_ushort as ToFfiType>::TYPE,
atom!("int") => <core::ffi::c_int as ToFfiType>::TYPE,
atom!("uint") => <core::ffi::c_uint as ToFfiType>::TYPE,
atom!("long") => <core::ffi::c_long as ToFfiType>::TYPE,
atom!("ulong") => <core::ffi::c_ulong as ToFfiType>::TYPE,
atom!("longlong") => <core::ffi::c_longlong as ToFfiType>::TYPE,
atom!("ulonglong") => <core::ffi::c_ulonglong as ToFfiType>::TYPE,
atom!("float") => <core::ffi::c_float as ToFfiType>::TYPE,
atom!("double") => <core::ffi::c_double as ToFfiType>::TYPE,
atom!("sint64") | atom!("i64") => Self::I64, atom!("sint64") | atom!("i64") => Self::I64,
atom!("sint32") | atom!("i32") => Self::I32, atom!("sint32") | atom!("i32") => Self::I32,
atom!("sint16") | atom!("i16") => Self::I16, atom!("sint16") | atom!("i16") => Self::I16,
@@ -490,19 +496,30 @@ impl<'val> ArgValue<'val> {
return Err(FfiError::StructNotFound(*arg_type_name)); return Err(FfiError::StructNotFound(*arg_type_name));
}; };
Ok(Self::Struct(struct_type.build(structs_table, args)?)) Ok(Self::Struct(struct_type.build(
*arg_type_name,
structs_table,
args,
)?))
} }
FfiType::Void => Err(FfiError::VoidArgumentType), FfiType::Void => Err(FfiError::UnsupportedArgumentType(Some(atom!("void")))),
} }
} }
fn build_args( fn build_args(
name: Atom,
kind: ArgCountMismatchKind,
args: &'val mut [Value], args: &'val mut [Value],
types: &[FfiType], types: &[FfiType],
structs_table: &HashMap<Atom, StructImpl>, structs_table: &HashMap<Atom, StructImpl>,
) -> Result<Vec<Self>, FfiError> { ) -> Result<Vec<Self>, FfiError> {
if types.len() != args.len() { if types.len() != args.len() {
return Err(FfiError::ArgCountMismatch); return Err(FfiError::ArgCountMismatch {
name,
kind,
expected: types.len(),
got: args.len(),
});
} }
args.iter_mut() args.iter_mut()
@@ -510,6 +527,28 @@ impl<'val> ArgValue<'val> {
.map(|(arg, arg_type)| ArgValue::new(arg, arg_type, structs_table)) .map(|(arg, arg_type)| ArgValue::new(arg, arg_type, structs_table))
.collect::<Result<Vec<_>, _>>() .collect::<Result<Vec<_>, _>>()
} }
fn as_arg<'res, 'this: 'res>(&'this self) -> Arg<'res>
where
'val: 'res,
{
match self {
ArgValue::U8(a) => libffi::middle::arg(a),
ArgValue::I8(a) => libffi::middle::arg(a),
ArgValue::U16(a) => libffi::middle::arg(a),
ArgValue::I16(a) => libffi::middle::arg(a),
ArgValue::U32(a) => libffi::middle::arg(a),
ArgValue::I32(a) => libffi::middle::arg(a),
ArgValue::U64(a) => libffi::middle::arg(a),
ArgValue::I64(a) => libffi::middle::arg(a),
ArgValue::F32(a) => libffi::middle::arg(a),
ArgValue::F64(a) => libffi::middle::arg(a),
ArgValue::Ptr(ptr, _) => Arg::new(ptr),
ArgValue::Struct(s) => unsafe {
std::mem::transmute::<*mut c_void, Arg>(s.ptr.as_ptr())
},
}
}
} }
struct FfiStruct { struct FfiStruct {
@@ -570,6 +609,10 @@ impl FfiStruct {
allocator, allocator,
}) })
} }
fn as_ret(&self) -> libffi::middle::Ret<'_> {
unsafe { std::mem::transmute::<*mut c_void, Ret>(self.ptr.as_ptr()) }
}
} }
impl Drop for FfiStruct { impl Drop for FfiStruct {
@@ -659,11 +702,17 @@ impl ForeignFunctionTable {
let fn_impl = self let fn_impl = self
.table .table
.get(&fn_name) .get(&fn_name)
.ok_or(FfiError::FunctionNotFound(fn_name))?; .ok_or(FfiError::FunctionNotFound(fn_name, args.len()))?;
let args = ArgValue::build_args(&mut args, &fn_impl.args, &self.structs)?; let args = ArgValue::build_args(
fn_name,
ArgCountMismatchKind::Function,
&mut args,
&fn_impl.args,
&self.structs,
)?;
let args = PointerArgs::new(&args); let args: Vec<_> = args.iter().map(|v| v.as_arg()).collect();
fn_impl.call(&args, arena, &self.structs) fn_impl.call(&args, arena, &self.structs)
} }
@@ -691,9 +740,9 @@ impl ForeignFunctionTable {
} }
match FfiType::from_atom(&kind) { match FfiType::from_atom(&kind) {
FfiType::Void => Err(FfiError::VoidArgumentType), FfiType::Void => Err(FfiError::UnsupportedArgumentType(Some(atom!("void")))),
FfiType::Bool => { FfiType::Bool => {
let val = args.as_int::<u8>()?; let val = args.as_int::<i8>()?;
let init = match val { let init = match val {
0 => false, 0 => false,
1 => true, 1 => true,
@@ -720,7 +769,7 @@ impl ForeignFunctionTable {
let (_, args) = args.as_struct()?; let (_, args) = args.as_struct()?;
let ffi_struct = struct_impl.build(&self.structs, args)?; let ffi_struct = struct_impl.build(kind, &self.structs, args)?;
let ptr = ManuallyDrop::new(ffi_struct).ptr; let ptr = ManuallyDrop::new(ffi_struct).ptr;
@@ -744,7 +793,7 @@ impl ForeignFunctionTable {
T: Copy + TryInto<i64> + MightNotFitInFixnum, T: Copy + TryInto<i64> + MightNotFitInFixnum,
Integer: From<T>, Integer: From<T>,
{ {
let n = ptr.cast::<T>().read(); let n = unsafe { ptr.cast::<T>().read() };
Value::Number(fixnum!(Number, n, arena)) Value::Number(fixnum!(Number, n, arena))
} }
@@ -755,9 +804,9 @@ impl ForeignFunctionTable {
}; };
match FfiType::from_atom(&kind) { match FfiType::from_atom(&kind) {
FfiType::Void => Err(FfiError::VoidArgumentType), FfiType::Void => Err(FfiError::UnsupportedArgumentType(Some(atom!("void")))),
FfiType::Bool | FfiType::U8 => Ok(unsafe { read_int::<u8>(ptr, arena) }), FfiType::U8 => Ok(unsafe { read_int::<u8>(ptr, arena) }),
FfiType::I8 => Ok(unsafe { read_int::<i8>(ptr, arena) }), FfiType::Bool | FfiType::I8 => Ok(unsafe { read_int::<i8>(ptr, arena) }),
FfiType::U16 => Ok(unsafe { read_int::<u16>(ptr, arena) }), FfiType::U16 => Ok(unsafe { read_int::<u16>(ptr, arena) }),
FfiType::I16 => Ok(unsafe { read_int::<i16>(ptr, arena) }), FfiType::I16 => Ok(unsafe { read_int::<i16>(ptr, arena) }),
FfiType::U32 => Ok(unsafe { read_int::<u32>(ptr, arena) }), FfiType::U32 => Ok(unsafe { read_int::<u32>(ptr, arena) }),
@@ -805,7 +854,7 @@ impl ForeignFunctionTable {
}; };
match FfiType::from_atom(&kind) { match FfiType::from_atom(&kind) {
FfiType::Void => return Err(FfiError::VoidArgumentType), FfiType::Void => return Err(FfiError::UnsupportedArgumentType(Some(atom!("void")))),
FfiType::Bool => deallocate_primitive::<bool>(allocator, ptr), FfiType::Bool => deallocate_primitive::<bool>(allocator, ptr),
FfiType::U8 => deallocate_primitive::<u8>(allocator, ptr), FfiType::U8 => deallocate_primitive::<u8>(allocator, ptr),
FfiType::I8 => deallocate_primitive::<i8>(allocator, ptr), FfiType::I8 => deallocate_primitive::<i8>(allocator, ptr),
@@ -879,7 +928,7 @@ impl Value {
fn as_ptr(&mut self) -> Result<*mut c_void, FfiError> { fn as_ptr(&mut self) -> Result<*mut c_void, FfiError> {
match self { match self {
Value::CString(ref mut cstr) => Ok(cstr.as_ptr().cast_mut().cast()), Value::CString(cstr) => Ok(cstr.as_ptr().cast_mut().cast()),
Value::Number(Number::Fixnum(fixnum)) => Ok(std::ptr::with_exposed_provenance_mut( Value::Number(Number::Fixnum(fixnum)) => Ok(std::ptr::with_exposed_provenance_mut(
fixnum.get_num() as usize, fixnum.get_num() as usize,
)), )),
@@ -902,20 +951,37 @@ impl Value {
} }
#[derive(Debug)] #[derive(Debug)]
#[non_exhaustive]
pub enum FfiError { pub enum FfiError {
ValueCast(Atom, Atom), ValueCast(Atom, Atom),
ValueOutOfRange(DomainErrorType, Value), ValueOutOfRange(DomainErrorType, Value),
VoidArgumentType, UnsupportedArgumentType(Option<Atom>),
FunctionNotFound(Atom), FunctionNotFound(Atom, usize),
StructNotFound(Atom), StructNotFound(Atom),
ArgCountMismatch, ArgCountMismatch {
name: Atom, // ffi function or struct
kind: ArgCountMismatchKind,
#[allow(dead_code, reason = "will be used by PR 3173")]
expected: usize,
got: usize,
},
AllocationFailed, AllocationFailed,
// LayoutError should never occour // LayoutError should never occur
LayoutError, LayoutError,
UnsupportedTypedef, UnsupportedTypedef,
UnsupportedAbi, UnsupportedAbi,
CStrFieldType, CStrFieldType,
NullPtr, NullPtr,
#[doc(hidden)]
#[non_exhaustive]
Other,
}
#[derive(Debug)]
pub(crate) enum ArgCountMismatchKind {
Function,
Struct,
} }
impl std::fmt::Display for FfiError { impl std::fmt::Display for FfiError {
@@ -931,6 +997,8 @@ impl From<libffi::low::Error> for FfiError {
match value { match value {
libffi::low::Error::Typedef => FfiError::UnsupportedTypedef, libffi::low::Error::Typedef => FfiError::UnsupportedTypedef,
libffi::low::Error::Abi => FfiError::UnsupportedAbi, libffi::low::Error::Abi => FfiError::UnsupportedAbi,
libffi::low::Error::ArgType => FfiError::UnsupportedArgumentType(None),
_ => FfiError::Other,
} }
} }
} }

View File

@@ -18,11 +18,14 @@ use indexmap::{IndexMap, IndexSet};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use std::cell::Cell; use std::cell::Cell;
use std::cmp::Ordering;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::{AddAssign, Deref, DerefMut}; use std::ops::{AddAssign, Deref, DerefMut};
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc;
pub type PredicateKey = (Atom, usize); // name, arity. pub type PredicateKey = (Atom, usize); // name, arity.
@@ -42,12 +45,6 @@ impl AppendOrPrepend {
} }
} }
#[derive(Debug, Clone, Copy)]
pub enum VarComparison {
Indistinct,
Distinct,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Level { pub enum Level {
Deep, Deep,
@@ -128,10 +125,82 @@ impl ChunkType {
} }
} }
#[derive(Debug, Clone)] //, PartialOrd, PartialEq, Eq, Hash)]
pub(crate) struct BranchNumber {
pub(crate) branch_num: Rational,
pub(crate) delta: Rational,
}
impl Default for BranchNumber {
fn default() -> Self {
Self {
branch_num: Rational::from(0),
delta: Rational::from(1u64 << 31),
}
}
}
impl PartialEq<BranchNumber> for BranchNumber {
#[inline]
fn eq(&self, rhs: &BranchNumber) -> bool {
self.branch_num == rhs.branch_num
}
}
impl Eq for BranchNumber {}
impl Hash for BranchNumber {
#[inline(always)]
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.branch_num.hash(hasher)
}
}
impl PartialOrd<BranchNumber> for BranchNumber {
#[inline]
fn partial_cmp(&self, rhs: &BranchNumber) -> Option<Ordering> {
self.branch_num.partial_cmp(&rhs.branch_num)
}
}
impl BranchNumber {
pub(crate) fn has_as_subbranch(&self, other: &Self) -> bool {
other.delta <= self.delta
&& other.branch_num >= self.branch_num
&& other.branch_num < &self.branch_num + &self.delta
}
pub(crate) fn split(&self) -> BranchNumber {
BranchNumber {
branch_num: self.branch_num.clone() + &self.delta / Rational::from(2),
delta: &self.delta / Rational::from(4),
}
}
pub(crate) fn incr_by_delta(&self) -> BranchNumber {
BranchNumber {
branch_num: self.branch_num.clone() + &self.delta,
delta: self.delta.clone(),
}
}
pub(crate) fn halve_delta(&self) -> BranchNumber {
BranchNumber {
branch_num: self.branch_num.clone(),
delta: &self.delta / Rational::from(2),
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum ChunkedTerms { pub enum ChunkedTerms {
Branch(Vec<VecDeque<ChunkedTerms>>), Branch {
Chunk { terms: VecDeque<QueryTerm> }, branch_nums: Vec<Arc<BranchNumber>>,
arms: Vec<VecDeque<ChunkedTerms>>,
},
Chunk {
terms: VecDeque<QueryTerm>,
},
} }
#[derive(Debug)] #[derive(Debug)]
@@ -165,21 +234,22 @@ impl ChunkedTermVec {
} }
pub fn reserve_branch(&mut self, capacity: usize) { pub fn reserve_branch(&mut self, capacity: usize) {
self.chunk_vec self.chunk_vec.push_back(ChunkedTerms::Branch {
.push_back(ChunkedTerms::Branch(Vec::with_capacity(capacity))); branch_nums: Vec::with_capacity(capacity),
arms: Vec::with_capacity(capacity),
});
} }
#[inline] #[inline]
pub fn add_chunk(&mut self) { pub fn add_chunk(&mut self) {
let chunk = ChunkedTerms::Chunk { self.chunk_vec.push_back(ChunkedTerms::Chunk {
terms: VecDeque::from(vec![]), terms: VecDeque::from(vec![]),
}; });
self.chunk_vec.push_back(chunk);
} }
pub fn push_chunk_term(&mut self, term: QueryTerm) { pub fn push_chunk_term(&mut self, term: QueryTerm) {
match self.chunk_vec.back_mut() { match self.chunk_vec.back_mut() {
Some(ChunkedTerms::Branch(_)) => { Some(ChunkedTerms::Branch { .. }) => {
let chunk = ChunkedTerms::Chunk { let chunk = ChunkedTerms::Chunk {
terms: VecDeque::from(vec![term]), terms: VecDeque::from(vec![term]),
}; };
@@ -319,15 +389,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(term, ..) => term.head.name(),
PredicateClause::Rule(ref rule, ..) => rule.name(), PredicateClause::Rule(rule, ..) => rule.name(),
} }
} }
fn arity(&self) -> usize { fn arity(&self) -> usize {
match self { match self {
PredicateClause::Fact(ref term, ..) => term.head.arity(), PredicateClause::Fact(term, ..) => term.head.arity(),
PredicateClause::Rule(ref rule, ..) => rule.arity(), PredicateClause::Rule(rule, ..) => rule.arity(),
} }
} }
} }
@@ -512,7 +582,7 @@ pub(crate) fn fetch_op_spec_from_existing(
op_desc: Option<OpDesc>, op_desc: Option<OpDesc>,
op_dir: &OpDir, op_dir: &OpDir,
) -> Option<OpDesc> { ) -> Option<OpDesc> {
if let Some(ref op_desc) = &op_desc { if let Some(op_desc) = &op_desc {
if op_desc.arity() != arity { if op_desc.arity() != arity {
/* it's possible to extend operator functors with /* it's possible to extend operator functors with
* additional terms. When that happens, * additional terms. When that happens,
@@ -773,9 +843,9 @@ impl Number {
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.is_positive(), Number::Integer(n) => n.is_positive(),
Number::Float(f) => f.is_sign_positive(), Number::Float(f) => f.is_sign_positive(),
Number::Rational(ref r) => r.is_positive(), Number::Rational(r) => r.is_positive(),
} }
} }
@@ -783,9 +853,9 @@ impl Number {
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.is_negative(), Number::Integer(n) => n.is_negative(),
&Number::Float(OrderedFloat(f)) => f.is_sign_negative() && f != -0f64, &Number::Float(OrderedFloat(f)) => f.is_sign_negative() && f != -0f64,
Number::Rational(ref r) => r.is_negative(), Number::Rational(r) => r.is_negative(),
} }
} }
@@ -793,9 +863,9 @@ impl Number {
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.is_zero(), Number::Integer(n) => n.is_zero(),
&Number::Float(OrderedFloat(f)) => f == 0.0 || f == -0.0, &Number::Float(OrderedFloat(f)) => f == 0.0 || f == -0.0,
Number::Rational(ref r) => r.is_zero(), Number::Rational(r) => r.is_zero(),
} }
} }
@@ -850,9 +920,9 @@ impl OptArgIndexKey {
#[inline] #[inline]
pub(crate) fn set_switch_on_term_loc(&mut self, value: usize) { pub(crate) fn set_switch_on_term_loc(&mut self, value: usize) {
match self { match self {
OptArgIndexKey::Literal(_, ref mut loc, ..) OptArgIndexKey::Literal(_, loc, ..)
| OptArgIndexKey::Structure(_, ref mut loc, ..) | OptArgIndexKey::Structure(_, loc, ..)
| OptArgIndexKey::List(_, ref mut loc) => { | OptArgIndexKey::List(_, loc) => {
*loc = value; *loc = value;
} }
OptArgIndexKey::None => {} OptArgIndexKey::None => {}
@@ -864,9 +934,9 @@ impl AddAssign<usize> for OptArgIndexKey {
#[inline] #[inline]
fn add_assign(&mut self, n: usize) { fn add_assign(&mut self, n: usize) {
match self { match self {
OptArgIndexKey::Literal(_, ref mut o, ..) OptArgIndexKey::Literal(_, o, ..)
| OptArgIndexKey::List(_, ref mut o) | OptArgIndexKey::List(_, o)
| OptArgIndexKey::Structure(_, ref mut o, ..) => { | OptArgIndexKey::Structure(_, o, ..) => {
*o += n; *o += n;
} }
OptArgIndexKey::None => {} OptArgIndexKey::None => {}

View File

@@ -230,9 +230,9 @@ pub(crate) fn variadic_functor(
#[allow(unused_parens)] #[allow(unused_parens)]
mod tests { mod tests {
use super::*; use super::*;
use FunctorElement::*;
use indexmap::indexmap; use indexmap::indexmap;
use std::string::String; use std::string::String;
use FunctorElement::*;
#[test] #[test]
fn basic_terms() { fn basic_terms() {
@@ -551,7 +551,7 @@ mod tests {
#[test] #[test]
fn inlined_atoms() { fn inlined_atoms() {
let atom_table = AtomTable::new(); let atom_table = AtomTable::new().unwrap();
let inlined = AtomTable::build_with(&atom_table, "inline"); let inlined = AtomTable::build_with(&atom_table, "inline");
assert!(inlined.is_inlined()); assert!(inlined.is_inlined());

View File

@@ -1,20 +1,348 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work use crate::arena::Arena;
use crate::forms::Number;
#[cfg(test)] #[cfg(test)]
pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter; pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter;
use crate::atom_table::*; use crate::atom_table::*;
use crate::machine::cycle_detection::*; use crate::machine::cycle_detection::*;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::machine_indices::TermOrderCategory;
use crate::machine::stack::*; use crate::machine::stack::*;
use crate::parser::lexer::MachineState;
use crate::types::*; use crate::types::*;
use core::marker::PhantomData; use core::marker::PhantomData;
use scryer_modular_bitfield::prelude::*; use fxhash::FxBuildHasher;
use indexmap::IndexSet;
use modular_bitfield::prelude::*;
use std::cmp::Ordering;
use std::ops::Deref; use std::ops::Deref;
use std::vec::Vec; use std::vec::Vec;
// iterate through the subterms of a pair of terms
// for as long as they structurally agree, reporting
// the earliest (pre-order) difference before returning
// None's and pairs of variables as the Iterator Item.
pub struct ParallelHeapIter<'a> {
stack: Vec<(HeapCellValue, HeapCellValue)>,
heap: &'a Heap,
arena: &'a Arena,
tabu_list: IndexSet<(usize, usize), FxBuildHasher>,
}
impl<'a> ParallelHeapIter<'a> {
pub fn from(machine_st: &'a MachineState, h1: HeapCellValue, h2: HeapCellValue) -> Self {
Self {
stack: vec![(h1, h2)],
heap: &machine_st.heap,
arena: &machine_st.arena,
tabu_list: IndexSet::with_hasher(FxBuildHasher::new()),
}
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub enum TermPair {
Vars(usize, usize),
Less(HeapCellValue, HeapCellValue),
Greater(HeapCellValue, HeapCellValue),
Unordered(HeapCellValue, HeapCellValue),
}
impl ParallelHeapIter<'_> {
#[inline]
fn parallel_cmp<Cmp: Ord>(
&mut self,
v1: Cmp,
v2: Cmp,
h1: HeapCellValue,
h2: HeapCellValue,
) -> Option<TermPair> {
match v1.cmp(&v2) {
Ordering::Greater => {
self.stack.clear();
Some(TermPair::Greater(h1, h2))
}
Ordering::Less => {
self.stack.clear();
Some(TermPair::Less(h1, h2))
}
Ordering::Equal => None,
}
}
}
macro_rules! some_or_return {
($e:expr) => {
if let Some(x) = $e {
return Some(x);
}
};
}
impl Iterator for ParallelHeapIter<'_> {
type Item = TermPair;
fn next(&mut self) -> Option<Self::Item> {
use crate::offset_table::F64Offset;
while let Some((s1, s2)) = self.stack.pop() {
let s1 = heap_bound_deref(self.heap, s1);
let s2 = heap_bound_deref(self.heap, s2);
let v1 = heap_bound_store(self.heap, s1);
let v2 = heap_bound_store(self.heap, s2);
let order_cat_v1 = v1.order_category(self.heap);
let order_cat_v2 = v2.order_category(self.heap);
some_or_return!(self.parallel_cmp(order_cat_v1, order_cat_v2, v1, v2));
match order_cat_v1 {
Some(TermOrderCategory::Variable) => {
let v1 = v1.get_value() as usize;
let v2 = v2.get_value() as usize;
return Some(TermPair::Vars(v1, v2));
}
Some(TermOrderCategory::FloatingPoint) => {
let v1_offset = cell_as_f64_offset!(v1);
let v2_offset = cell_as_f64_offset!(v2);
let v1_f64 = self.arena.f64_tbl.get_entry(v1_offset);
let v2_f64 = self.arena.f64_tbl.get_entry(v2_offset);
some_or_return!(self.parallel_cmp(v1_f64, v2_f64, v1, v2));
}
Some(TermOrderCategory::Integer) => {
let v1_int = Number::try_from((v1, &self.arena.f64_tbl)).unwrap();
let v2_int = Number::try_from((v2, &self.arena.f64_tbl)).unwrap();
some_or_return!(self.parallel_cmp(v1_int, v2_int, v1, v2));
}
Some(TermOrderCategory::Atom) => {
read_heap_cell!(v1,
(HeapCellValueTag::Atom, (n1, _a1)) => {
read_heap_cell!(v2,
(HeapCellValueTag::Atom, (n2, _a2)) => {
some_or_return!(self.parallel_cmp(n1, n2, v1, v2));
}
(HeapCellValueTag::Str, s) => {
let n2 = cell_as_atom_cell!(self.heap[s])
.get_name();
some_or_return!(self.parallel_cmp(n1, n2, v1, v2));
}
_ => {
unreachable!();
}
)
}
(HeapCellValueTag::Str, s) => {
let n1 = cell_as_atom_cell!(self.heap[s])
.get_name();
read_heap_cell!(v2,
(HeapCellValueTag::Atom, (n2, _a2)) => {
some_or_return!(self.parallel_cmp(n1, n2, v1, v2));
}
(HeapCellValueTag::Str, s) => {
let n2 = cell_as_atom_cell!(self.heap[s])
.get_name();
some_or_return!(self.parallel_cmp(n1, n2, v1, v2));
}
_ => {
unreachable!();
}
)
}
_ => {
unreachable!()
}
)
}
Some(TermOrderCategory::Compound) => {
read_heap_cell!(v1,
(HeapCellValueTag::Lis, l1) => {
read_heap_cell!(v2,
(HeapCellValueTag::PStrLoc, l2) => {
if self.tabu_list.contains(&(l1, l2)) {
continue;
}
self.tabu_list.insert((l1, l2));
// like the action of partial_string_to_stack here but the
// ordering of stack pushes is (crucially for comparison
// correctness) different.
let (c, succ_cell) = self.heap.last_str_char_and_tail(l2);
self.stack.push((heap_loc_as_cell!(l1 + 1), succ_cell));
self.stack.push((heap_loc_as_cell!(l1), char_as_cell!(c)));
}
(HeapCellValueTag::Lis, l2) => {
if self.tabu_list.contains(&(l1, l2)) {
continue;
}
self.tabu_list.insert((l1, l2));
self.stack.push((self.heap[l1 + 1], self.heap[l2 + 1]));
self.stack.push((self.heap[l1], self.heap[l2]));
}
(HeapCellValueTag::Str, s2) => {
if self.tabu_list.contains(&(l1, s2)) {
continue;
}
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
.get_name_and_arity();
some_or_return!(self.parallel_cmp((2, atom!(".")), (a2, n2), v1, v2));
self.tabu_list.insert((l1, s2));
self.stack.push((self.heap[l1 + 1], self.heap[s2 + 2]));
self.stack.push((self.heap[l1], self.heap[s2 + 1]));
}
_ => {
unreachable!();
}
)
}
(HeapCellValueTag::PStrLoc, l1) => {
read_heap_cell!(v2,
(HeapCellValueTag::PStrLoc, l2) => {
if self.tabu_list.contains(&(l1, l2)) {
continue;
}
match self.heap.compare_pstr_segments(l1, l2) {
PStrSegmentCmpResult::Continue(v1, v2) => {
self.tabu_list.insert((l1, l2));
self.stack.push((v1.offset_by(l1), v2.offset_by(l2)));
}
PStrSegmentCmpResult::Less => {
self.stack.clear();
return Some(TermPair::Less(v1, v2));
}
PStrSegmentCmpResult::Greater => {
self.stack.clear();
return Some(TermPair::Greater(v1, v2));
}
}
}
(HeapCellValueTag::Lis, l2) => {
if self.tabu_list.contains(&(l1, l2)) {
continue;
}
self.tabu_list.insert((l1, l2));
let (c, succ_cell) = self.heap.last_str_char_and_tail(l1);
self.stack.push((succ_cell, heap_loc_as_cell!(l2 + 1)));
self.stack.push((char_as_cell!(c), heap_loc_as_cell!(l2)));
}
(HeapCellValueTag::Str, s2) => {
if self.tabu_list.contains(&(l1, s2)) {
continue;
}
self.tabu_list.insert((l1, s2));
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
.get_name_and_arity();
some_or_return!(self.parallel_cmp((2, atom!(".")), (a2, n2), v1, v2));
let (c, succ_cell) = self.heap.last_str_char_and_tail(l1);
self.stack.push((succ_cell, heap_loc_as_cell!(s2+2)));
self.stack.push((char_as_cell!(c), heap_loc_as_cell!(s2+1)));
}
_ => {
unreachable!()
}
);
}
(HeapCellValueTag::Str, s1) => {
read_heap_cell!(v2,
(HeapCellValueTag::Str, s2) => {
if self.tabu_list.contains(&(s1, s2)) {
continue;
}
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
.get_name_and_arity();
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
.get_name_and_arity();
some_or_return!(self.parallel_cmp((a1, n1), (a2, n2), v1, v2));
self.tabu_list.insert((s1, s2));
for idx in (1 .. a1+1).rev() {
self.stack.push((self.heap[s1+idx], self.heap[s2+idx]));
}
}
(HeapCellValueTag::Lis, l2) => {
if self.tabu_list.contains(&(s1, l2)) {
continue;
}
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
.get_name_and_arity();
some_or_return!(self.parallel_cmp((a1, n1), (2, atom!(".")), v1, v2));
self.stack.push((self.heap[s1+1], self.heap[l2]));
self.stack.push((self.heap[s1+2], self.heap[l2+1]));
}
(HeapCellValueTag::PStrLoc, l2) => {
if self.tabu_list.contains(&(s1, l2)) {
continue;
}
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
.get_name_and_arity();
some_or_return!(self.parallel_cmp((a1, n1), (2, atom!(".")), v1, v2));
self.tabu_list.insert((s1, l2));
let (c, succ_cell) = self.heap.last_str_char_and_tail(l2);
self.stack.push((heap_loc_as_cell!(s1+2), succ_cell));
self.stack.push((heap_loc_as_cell!(s1+1), char_as_cell!(c)));
}
_ => {
unreachable!()
}
)
}
_ => {
unreachable!()
}
);
}
None => {
return Some(TermPair::Unordered(v1, v2));
}
}
}
None
}
}
#[inline(always)] #[inline(always)]
pub fn eager_stackful_preorder_iter( pub fn eager_stackful_preorder_iter(
heap: &mut Heap, heap: &mut Heap,
@@ -34,7 +362,7 @@ pub struct EagerStackfulPreOrderHeapIter<'a> {
start_value: HeapCellValue, start_value: HeapCellValue,
iter_stack: Vec<HeapCellValue>, iter_stack: Vec<HeapCellValue>,
mark_phase: bool, mark_phase: bool,
heap: &'a mut Heap, pub heap: &'a mut Heap,
} }
impl<'a> Drop for EagerStackfulPreOrderHeapIter<'a> { impl<'a> Drop for EagerStackfulPreOrderHeapIter<'a> {
@@ -118,8 +446,8 @@ impl<'a> EagerStackfulPreOrderHeapIter<'a> {
(HeapCellValueTag::PStrLoc, h) => { (HeapCellValueTag::PStrLoc, h) => {
let tail_idx = self.heap.scan_slice_to_str(h).tail_idx; let tail_idx = self.heap.scan_slice_to_str(h).tail_idx;
self.heap[tail_idx].set_mark_bit(self.mark_phase);
self.iter_stack.push(self.heap[tail_idx]); self.iter_stack.push(self.heap[tail_idx]);
self.heap[tail_idx].set_mark_bit(self.mark_phase);
} }
_ => { _ => {
} }
@@ -141,7 +469,7 @@ impl<'a> Iterator for EagerStackfulPreOrderHeapIter<'a> {
} }
} }
#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] #[derive(Specifier, Clone, Copy, Debug, PartialEq, Eq)]
#[bits = 2] #[bits = 2]
enum IterStackLocTag { enum IterStackLocTag {
Iterable, Iterable,
@@ -149,7 +477,7 @@ enum IterStackLocTag {
PendingMark, PendingMark,
} }
#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] #[derive(Specifier, Clone, Copy, Debug, PartialEq, Eq)]
#[bits = 1] #[bits = 1]
pub enum HeapOrStackTag { pub enum HeapOrStackTag {
Heap, Heap,

View File

@@ -3,7 +3,7 @@ use crate::atom_table::*;
use crate::parser::ast::*; use crate::parser::ast::*;
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::parser::dashu::{Integer, Rational, ibig};
use crate::forms::*; use crate::forms::*;
use crate::heap_iter::*; use crate::heap_iter::*;
@@ -387,7 +387,7 @@ fn negated_op_needs_bracketing(
op_dir: &OpDir, op_dir: &OpDir,
op: &Option<DirectedOp>, op: &Option<DirectedOp>,
) -> bool { ) -> bool {
if let Some(ref op) = op { if let Some(op) = op {
op.is_negative_sign() op.is_negative_sign()
&& iter.leftmost_leaf_has_property(op_dir, |addr| { && iter.leftmost_leaf_has_property(op_dir, |addr| {
match Number::try_from((addr, f64_tbl)) { match Number::try_from((addr, f64_tbl)) {
@@ -1452,7 +1452,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(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(), "("))
{ {

View File

@@ -1,10 +1,12 @@
use bytes::{buf::Reader, Bytes}; use bytes::{Bytes, buf::Reader};
use std::sync::{Arc, Condvar, Mutex}; use std::sync::{Arc, Condvar, Mutex};
use tokio::sync::Notify;
use warp::http; use warp::http;
pub struct HttpListener { pub struct HttpListener {
pub incoming: std::sync::mpsc::Receiver<HttpRequest>, pub incoming: std::sync::mpsc::Receiver<HttpRequest>,
pub warp_shutdown: Arc<Notify>,
} }
pub struct HttpRequest { pub struct HttpRequest {

View File

@@ -167,7 +167,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
.push(IndexingLine::IndexedChoice(third_level_index)); .push(IndexingLine::IndexedChoice(third_level_index));
match &mut self.indexing_code[self.offset] { match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref mut constants)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => {
constants.insert( constants.insert(
constant, constant,
IndexingCodePtr::Internal(indexing_code_len - self.offset), IndexingCodePtr::Internal(indexing_code_len - self.offset),
@@ -196,7 +196,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
.push(IndexingLine::DynamicIndexedChoice(third_level_index)); .push(IndexingLine::DynamicIndexedChoice(third_level_index));
match &mut self.indexing_code[self.offset] { match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref mut constants)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => {
constants.insert( constants.insert(
constant, constant,
IndexingCodePtr::Internal(indexing_code_len - self.offset), IndexingCodePtr::Internal(indexing_code_len - self.offset),
@@ -210,22 +210,22 @@ impl<'a> IndexingCodeMergingPtr<'a> {
fn extend_indexed_choice(&mut self, index: usize) { fn extend_indexed_choice(&mut self, index: usize) {
match &mut self.indexing_code[self.offset] { match &mut self.indexing_code[self.offset] {
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) IndexingLine::IndexedChoice(indexed_choice_instrs)
if self.append_or_prepend.is_append() => if self.append_or_prepend.is_append() =>
{ {
uncap_choice_seq_with_trust(indexed_choice_instrs.make_contiguous()); uncap_choice_seq_with_trust(indexed_choice_instrs.make_contiguous());
indexed_choice_instrs.push_back(IndexedChoiceInstruction::Trust(index)); indexed_choice_instrs.push_back(IndexedChoiceInstruction::Trust(index));
} }
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => { IndexingLine::IndexedChoice(indexed_choice_instrs) => {
uncap_choice_seq_with_try(indexed_choice_instrs.make_contiguous()); uncap_choice_seq_with_try(indexed_choice_instrs.make_contiguous());
indexed_choice_instrs.push_front(IndexedChoiceInstruction::Try(index)); indexed_choice_instrs.push_front(IndexedChoiceInstruction::Try(index));
} }
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) IndexingLine::DynamicIndexedChoice(indexed_choice_instrs)
if self.append_or_prepend.is_append() => if self.append_or_prepend.is_append() =>
{ {
indexed_choice_instrs.push_back(index); indexed_choice_instrs.push_back(index);
} }
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => { IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
indexed_choice_instrs.push_front(index); indexed_choice_instrs.push_front(index);
} }
_ => { _ => {
@@ -244,7 +244,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
let indexing_code_len = self.indexing_code.len(); let indexing_code_len = self.indexing_code.len();
match &mut self.indexing_code[self.offset] { match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, ..)) => {
match *c { match *c {
IndexingCodePtr::Fail if self.is_dynamic => { IndexingCodePtr::Fail if self.is_dynamic => {
*c = IndexingCodePtr::DynamicExternal(index); *c = IndexingCodePtr::DynamicExternal(index);
@@ -322,7 +322,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
let indexing_code_len = self.indexing_code.len(); let indexing_code_len = self.indexing_code.len();
match &mut self.indexing_code[self.offset] { match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, ..)) => {
match *c { match *c {
IndexingCodePtr::Fail if self.is_dynamic => { IndexingCodePtr::Fail if self.is_dynamic => {
*c = IndexingCodePtr::DynamicExternal(index); *c = IndexingCodePtr::DynamicExternal(index);
@@ -445,7 +445,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
.push(IndexingLine::IndexedChoice(third_level_index)); .push(IndexingLine::IndexedChoice(third_level_index));
match &mut self.indexing_code[self.offset] { match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => {
structures.insert( structures.insert(
key, key,
IndexingCodePtr::Internal(indexing_code_len - self.offset), IndexingCodePtr::Internal(indexing_code_len - self.offset),
@@ -474,7 +474,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
.push(IndexingLine::DynamicIndexedChoice(third_level_index)); .push(IndexingLine::DynamicIndexedChoice(third_level_index));
match &mut self.indexing_code[self.offset] { match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => {
structures.insert( structures.insert(
key, key,
IndexingCodePtr::Internal(indexing_code_len - self.offset), IndexingCodePtr::Internal(indexing_code_len - self.offset),
@@ -491,13 +491,8 @@ impl<'a> IndexingCodeMergingPtr<'a> {
let indexing_code_len = self.indexing_code.len(); let indexing_code_len = self.indexing_code.len();
match &mut self.indexing_code[self.offset] { match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm( IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, s)) => {
_, match *s {
_,
_,
_,
ref mut s,
)) => match *s {
IndexingCodePtr::Fail if self.is_dynamic => { IndexingCodePtr::Fail if self.is_dynamic => {
*s = IndexingCodePtr::DynamicExternal(index); *s = IndexingCodePtr::DynamicExternal(index);
break; break;
@@ -517,7 +512,8 @@ impl<'a> IndexingCodeMergingPtr<'a> {
IndexingCodePtr::Internal(o) => { IndexingCodePtr::Internal(o) => {
self.offset += o; self.offset += o;
} }
}, }
}
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => {
match structures.get(&key).cloned() { match structures.get(&key).cloned() {
None | Some(IndexingCodePtr::Fail) if self.is_dynamic => { None | Some(IndexingCodePtr::Fail) if self.is_dynamic => {
@@ -559,8 +555,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
let indexing_code_len = self.indexing_code.len(); let indexing_code_len = self.indexing_code.len();
match &mut self.indexing_code[self.offset] { match &mut self.indexing_code[self.offset] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, ref mut l, _)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, l, _)) => match *l {
match *l {
IndexingCodePtr::Fail if self.is_dynamic => { IndexingCodePtr::Fail if self.is_dynamic => {
*l = IndexingCodePtr::DynamicExternal(index); *l = IndexingCodePtr::DynamicExternal(index);
} }
@@ -603,8 +598,7 @@ impl<'a> IndexingCodeMergingPtr<'a> {
self.offset += o; self.offset += o;
self.extend_indexed_choice(index); self.extend_indexed_choice(index);
} }
} },
}
_ => { _ => {
unreachable!() unreachable!()
} }
@@ -632,7 +626,7 @@ pub(crate) fn merge_clause_index(
); );
match &opt_arg_index_key { match &opt_arg_index_key {
OptArgIndexKey::Literal(_, index_loc, constant, ref overlapping_constants) => { OptArgIndexKey::Literal(_, index_loc, constant, overlapping_constants) => {
let offset = new_clause_loc - index_loc + 1; let offset = new_clause_loc - index_loc + 1;
merging_ptr.index_constant(HeapCellValue::from(*constant), offset); merging_ptr.index_constant(HeapCellValue::from(*constant), offset);
@@ -676,8 +670,7 @@ pub(crate) fn remove_constant_indices(
let iter = once(&constant).chain(overlapping_constants.iter()); let iter = once(&constant).chain(overlapping_constants.iter());
match &mut indexing_code[index] { match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, ..)) => match *c {
match *c {
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => { IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
*c = IndexingCodePtr::Fail; *c = IndexingCodePtr::Fail;
return; return;
@@ -688,8 +681,7 @@ pub(crate) fn remove_constant_indices(
IndexingCodePtr::Fail => { IndexingCodePtr::Fail => {
return; return;
} }
} },
}
_ => { _ => {
unreachable!() unreachable!()
} }
@@ -700,9 +692,7 @@ pub(crate) fn remove_constant_indices(
for constant in iter.map(|l| HeapCellValue::from(*l)) { for constant in iter.map(|l| HeapCellValue::from(*l)) {
loop { loop {
match &mut indexing_code[index] { match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant( IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants)) => {
ref mut constants,
)) => {
constants_index = index; constants_index = index;
match constants.get(&constant).cloned() { match constants.get(&constant).cloned() {
@@ -720,7 +710,7 @@ pub(crate) fn remove_constant_indices(
} }
} }
} }
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => { IndexingLine::IndexedChoice(indexed_choice_instrs) => {
StaticCodeIndices::remove_instruction_with_offset( StaticCodeIndices::remove_instruction_with_offset(
indexed_choice_instrs, indexed_choice_instrs,
offset, offset,
@@ -734,13 +724,13 @@ pub(crate) fn remove_constant_indices(
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm( IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_, _,
_, _,
ref mut c, c,
.., ..,
)) => { )) => {
*c = ext; *c = ext;
} }
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant( IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
ref mut constants, constants,
)) => { )) => {
constants.insert(constant, ext); constants.insert(constant, ext);
} }
@@ -753,7 +743,7 @@ pub(crate) fn remove_constant_indices(
break; break;
} }
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => { IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
DynamicCodeIndices::remove_instruction_with_offset( DynamicCodeIndices::remove_instruction_with_offset(
indexed_choice_instrs, indexed_choice_instrs,
offset, offset,
@@ -767,13 +757,13 @@ pub(crate) fn remove_constant_indices(
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm( IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(
_, _,
_, _,
ref mut c, c,
.., ..,
)) => { )) => {
*c = ext; *c = ext;
} }
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant( IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(
ref mut constants, constants,
)) => { )) => {
constants.insert(constant, ext); constants.insert(constant, ext);
} }
@@ -794,11 +784,11 @@ pub(crate) fn remove_constant_indices(
} }
match &indexing_code[constants_index] { match &indexing_code[constants_index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(ref constants)) IndexingLine::Indexing(IndexingInstruction::SwitchOnConstant(constants))
if constants.is_empty() => if constants.is_empty() =>
{ {
match &mut indexing_code[0] { match &mut indexing_code[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, ref mut c, ..)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, c, ..)) => {
*c = IndexingCodePtr::Fail; *c = IndexingCodePtr::Fail;
} }
_ => { _ => {
@@ -819,8 +809,7 @@ pub(crate) fn remove_structure_index(
let mut index = 0; let mut index = 0;
match &mut indexing_code[index] { match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, ref mut s)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, s)) => match *s {
match *s {
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => { IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
*s = IndexingCodePtr::Fail; *s = IndexingCodePtr::Fail;
return; return;
@@ -831,8 +820,7 @@ pub(crate) fn remove_structure_index(
IndexingCodePtr::Fail => { IndexingCodePtr::Fail => {
return; return;
} }
} },
}
_ => { _ => {
unreachable!() unreachable!()
} }
@@ -842,7 +830,7 @@ pub(crate) fn remove_structure_index(
loop { loop {
match &mut indexing_code[index] { match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref mut structures)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures)) => {
structures_index = index; structures_index = index;
match structures.get(&(name, arity)).cloned() { match structures.get(&(name, arity)).cloned() {
@@ -859,7 +847,7 @@ pub(crate) fn remove_structure_index(
} }
} }
} }
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => { IndexingLine::IndexedChoice(indexed_choice_instrs) => {
StaticCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset); StaticCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
if indexed_choice_instrs.len() == 1 { if indexed_choice_instrs.len() == 1 {
@@ -872,12 +860,12 @@ pub(crate) fn remove_structure_index(
_, _,
_, _,
_, _,
ref mut s, s,
)) => { )) => {
*s = ext; *s = ext;
} }
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure( IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
ref mut structures, structures,
)) => { )) => {
structures.insert((name, arity), ext); structures.insert((name, arity), ext);
} }
@@ -890,7 +878,7 @@ pub(crate) fn remove_structure_index(
break; break;
} }
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => { IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
DynamicCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset); DynamicCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
if indexed_choice_instrs.len() == 1 { if indexed_choice_instrs.len() == 1 {
@@ -903,12 +891,12 @@ pub(crate) fn remove_structure_index(
_, _,
_, _,
_, _,
ref mut s, s,
)) => { )) => {
*s = ext; *s = ext;
} }
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure( IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(
ref mut structures, structures,
)) => { )) => {
structures.insert((name, arity), ext); structures.insert((name, arity), ext);
} }
@@ -928,17 +916,11 @@ pub(crate) fn remove_structure_index(
} }
match &indexing_code[structures_index] { match &indexing_code[structures_index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(ref structures)) IndexingLine::Indexing(IndexingInstruction::SwitchOnStructure(structures))
if structures.is_empty() => if structures.is_empty() =>
{ {
match &mut indexing_code[0] { match &mut indexing_code[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm( IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, _, s)) => {
_,
_,
_,
_,
ref mut s,
)) => {
*s = IndexingCodePtr::Fail; *s = IndexingCodePtr::Fail;
} }
_ => { _ => {
@@ -954,8 +936,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usiz
let mut index = 0; let mut index = 0;
match &mut indexing_code[index] { match &mut indexing_code[index] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, ref mut l, _)) => { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, _, _, l, _)) => match *l {
match *l {
IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => { IndexingCodePtr::DynamicExternal(_) | IndexingCodePtr::External(_) => {
*l = IndexingCodePtr::Fail; *l = IndexingCodePtr::Fail;
return; return;
@@ -966,15 +947,14 @@ pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usiz
IndexingCodePtr::Fail => { IndexingCodePtr::Fail => {
return; return;
} }
} },
}
_ => { _ => {
unreachable!() unreachable!()
} }
} }
match &mut indexing_code[index] { match &mut indexing_code[index] {
IndexingLine::IndexedChoice(ref mut indexed_choice_instrs) => { IndexingLine::IndexedChoice(indexed_choice_instrs) => {
StaticCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset); StaticCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
if indexed_choice_instrs.len() == 1 { if indexed_choice_instrs.len() == 1 {
@@ -986,7 +966,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usiz
_, _,
_, _,
_, _,
ref mut l, l,
_, _,
)) => { )) => {
*l = ext; *l = ext;
@@ -998,7 +978,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usiz
} }
} }
} }
IndexingLine::DynamicIndexedChoice(ref mut indexed_choice_instrs) => { IndexingLine::DynamicIndexedChoice(indexed_choice_instrs) => {
DynamicCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset); DynamicCodeIndices::remove_instruction_with_offset(indexed_choice_instrs, offset);
if indexed_choice_instrs.len() == 1 { if indexed_choice_instrs.len() == 1 {
@@ -1010,7 +990,7 @@ pub(crate) fn remove_list_index(indexing_code: &mut [IndexingLine], offset: usiz
_, _,
_, _,
_, _,
ref mut l, l,
_, _,
)) => { )) => {
*l = ext; *l = ext;
@@ -1034,7 +1014,7 @@ pub(crate) fn remove_index(
clause_loc: usize, clause_loc: usize,
) { ) {
match opt_arg_index_key { match opt_arg_index_key {
OptArgIndexKey::Literal(_, _, constant, ref overlapping_constants) => { OptArgIndexKey::Literal(_, _, constant, overlapping_constants) => {
remove_constant_indices(*constant, *overlapping_constants, indexing_code, clause_loc); remove_constant_indices(*constant, *overlapping_constants, indexing_code, clause_loc);
} }
OptArgIndexKey::Structure(_, _, name, arity) => { OptArgIndexKey::Structure(_, _, name, arity) => {
@@ -1328,9 +1308,7 @@ impl Indexer for DynamicCodeIndices {
for (key, code) in indices.into_iter() { for (key, code) in indices.into_iter() {
if code.len() > 1 { if code.len() > 1 {
index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1)); index_locs.insert(key, IndexingCodePtr::Internal(prelude.len() + 1));
prelude.push_back(IndexingLine::DynamicIndexedChoice( prelude.push_back(IndexingLine::DynamicIndexedChoice(code));
code.into_iter().collect(),
));
} else if let Some(i) = code.front() { } else if let Some(i) = code.front() {
index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i)); index_locs.insert(key, IndexingCodePtr::DynamicExternal(*i));
} }
@@ -1369,9 +1347,7 @@ impl Indexer for DynamicCodeIndices {
) -> IndexingCodePtr { ) -> IndexingCodePtr {
if lists.len() > 1 { if lists.len() > 1 {
let lists = std::mem::take(lists); let lists = std::mem::take(lists);
prelude.push_back(IndexingLine::DynamicIndexedChoice( prelude.push_back(IndexingLine::DynamicIndexedChoice(lists));
lists.into_iter().collect(),
));
IndexingCodePtr::Internal(1) IndexingCodePtr::Internal(1)
} else { } else {
lists lists
@@ -1529,11 +1505,11 @@ impl<I: Indexer> CodeOffsets<I> {
&mut prelude, &mut prelude,
); );
if let IndexingCodePtr::Internal(ref mut i) = &mut str_loc { if let IndexingCodePtr::Internal(i) = &mut str_loc {
*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;
} }
if let IndexingCodePtr::Internal(ref mut i) = &mut lst_loc { if let IndexingCodePtr::Internal(i) = &mut lst_loc {
*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;
} }
@@ -1548,6 +1524,6 @@ impl<I: Indexer> CodeOffsets<I> {
str_loc, str_loc,
))); )));
prelude.into_iter().collect() prelude.into()
} }
} }

1386
src/instructions.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,7 @@ use std::cell::Cell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::iter::*; use std::iter::*;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
use std::vec::Vec; use std::vec::Vec;
#[allow(clippy::borrowed_box)] #[allow(clippy::borrowed_box)]
@@ -89,7 +90,7 @@ impl<'a> QueryIterator<'a> {
| Term::CompleteString(..) => { | Term::CompleteString(..) => {
return QueryIterator { return QueryIterator {
state_stack: vec![], state_stack: vec![],
} };
} }
Term::Clause(r, name, terms) => TermIterState::Clause(Level::Root, 0, r, *name, terms), Term::Clause(r, name, terms) => TermIterState::Clause(Level::Root, 0, r, *name, terms),
Term::Var(cell, var_ptr) => TermIterState::Var(Level::Root, cell, var_ptr.clone()), Term::Var(cell, var_ptr) => TermIterState::Var(Level::Root, cell, var_ptr.clone()),
@@ -102,11 +103,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(cell, ClauseType::CallN(_), 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(cell, ct, 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));
} }
@@ -142,7 +143,7 @@ impl<'a> Iterator for QueryIterator<'a> {
return match lvl { return match lvl {
Level::Root => None, Level::Root => None,
lvl => Some(TermRef::Clause(lvl, cell, name, child_terms)), lvl => Some(TermRef::Clause(lvl, cell, name, child_terms)),
} };
} }
}; };
} else { } else {
@@ -292,7 +293,7 @@ impl<'a> Iterator for FactIterator<'a> {
return Some(TermRef::CompleteString(lvl, cell, atom)); return Some(TermRef::CompleteString(lvl, cell, atom));
} }
TermIterState::Literal(lvl, cell, constant) => { TermIterState::Literal(lvl, cell, constant) => {
return Some(TermRef::Literal(lvl, cell, constant)) return Some(TermRef::Literal(lvl, cell, constant));
} }
TermIterState::Var(lvl, cell, var_ptr) => { TermIterState::Var(lvl, cell, var_ptr) => {
return Some(TermRef::Var(lvl, cell, var_ptr)); return Some(TermRef::Var(lvl, cell, var_ptr));
@@ -319,15 +320,28 @@ pub(crate) fn breadth_first_iter(
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
enum ClauseIteratorState<'a> { enum ClauseIteratorState<'a> {
RemainingChunks(&'a VecDeque<ChunkedTerms>, usize), RemainingChunks(&'a VecDeque<ChunkedTerms>, usize),
RemainingBranches(&'a Vec<VecDeque<ChunkedTerms>>, usize), RemainingBranches(
&'a Vec<Arc<BranchNumber>>,
&'a Vec<VecDeque<ChunkedTerms>>,
usize,
),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum ClauseItem<'a> { pub(crate) enum ClauseItem<'a> {
FirstBranch(usize), FirstBranch {
NextBranch, branch_num: &'a Arc<BranchNumber>,
BranchEnd(usize), num_branches: usize,
Chunk { terms: &'a VecDeque<QueryTerm> }, },
NextBranch {
branch_num: &'a Arc<BranchNumber>,
},
BranchEnd {
depth: usize,
},
Chunk {
terms: &'a VecDeque<QueryTerm>,
},
} }
#[derive(Debug)] #[derive(Debug)]
@@ -338,8 +352,8 @@ pub(crate) struct ClauseIterator<'a> {
fn state_from_chunked_terms(chunk_vec: &VecDeque<ChunkedTerms>) -> ClauseIteratorState<'_> { 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 { branch_nums, arms }) = chunk_vec.front() {
return ClauseIteratorState::RemainingBranches(branches, 0); return ClauseIteratorState::RemainingBranches(branch_nums, arms, 0);
} }
} }
@@ -370,7 +384,9 @@ impl<'a> ClauseIterator<'a> {
while let Some(state) = self.state_stack.pop() { while let Some(state) = self.state_stack.pop() {
match state { match state {
ClauseIteratorState::RemainingBranches(terms, focus) if terms.len() == focus => { ClauseIteratorState::RemainingBranches(_branch_nums, terms, focus)
if terms.len() == focus =>
{
depth += 1; depth += 1;
} }
_ => { _ => {
@@ -399,11 +415,11 @@ impl<'a> Iterator for ClauseIterator<'a> {
} }
match &chunks[focus] { match &chunks[focus] {
ChunkedTerms::Branch(branches) => { ChunkedTerms::Branch { branch_nums, arms } => {
self.state_stack self.state_stack
.push(ClauseIteratorState::RemainingBranches(branches, 0)); .push(ClauseIteratorState::RemainingBranches(branch_nums, arms, 0));
} }
ChunkedTerms::Chunk { ref terms } => { ChunkedTerms::Chunk { terms } => {
return Some(ClauseItem::Chunk { terms }); return Some(ClauseItem::Chunk { terms });
} }
} }
@@ -411,11 +427,15 @@ impl<'a> Iterator for ClauseIterator<'a> {
ClauseIteratorState::RemainingChunks(chunks, focus) => { ClauseIteratorState::RemainingChunks(chunks, focus) => {
debug_assert_eq!(chunks.len(), focus); debug_assert_eq!(chunks.len(), focus);
} }
ClauseIteratorState::RemainingBranches(branches, focus) ClauseIteratorState::RemainingBranches(branch_nums, branches, focus)
if focus < branches.len() => if focus < branches.len() =>
{ {
self.state_stack self.state_stack
.push(ClauseIteratorState::RemainingBranches(branches, focus + 1)); .push(ClauseIteratorState::RemainingBranches(
branch_nums,
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 {
@@ -425,14 +445,21 @@ impl<'a> Iterator for ClauseIterator<'a> {
self.state_stack.push(state); self.state_stack.push(state);
return if focus == 0 { return if focus == 0 {
Some(ClauseItem::FirstBranch(branches.len())) Some(ClauseItem::FirstBranch {
branch_num: &branch_nums[0],
num_branches: branches.len(),
})
} else { } else {
Some(ClauseItem::NextBranch) Some(ClauseItem::NextBranch {
branch_num: &branch_nums[focus],
})
}; };
} }
ClauseIteratorState::RemainingBranches(branches, focus) => { ClauseIteratorState::RemainingBranches(_branch_nums, branches, focus) => {
debug_assert_eq!(branches.len(), focus); debug_assert_eq!(branches.len(), focus);
return Some(ClauseItem::BranchEnd(self.branch_end_depth())); return Some(ClauseItem::BranchEnd {
depth: self.branch_end_depth(),
});
} }
} }
} }

View File

@@ -29,10 +29,10 @@ pub(crate) mod heap_print;
mod http; mod http;
mod indexing; mod indexing;
mod variable_records; mod variable_records;
#[macro_use] #[macro_use]
pub(crate) mod instructions { pub(crate) mod instructions;
include!(concat!(env!("OUT_DIR"), "/instructions.rs"));
}
mod iterators; mod iterators;
pub(crate) mod machine; pub(crate) mod machine;
mod raw_block; mod raw_block;
@@ -43,9 +43,9 @@ mod targets;
pub(crate) mod types; pub(crate) mod types;
// Re-exports // Re-exports
pub use machine::Machine;
pub use machine::config::*; pub use machine::config::*;
pub use machine::lib_machine::*; pub use machine::lib_machine::*;
pub use machine::Machine;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub mod wasm; pub mod wasm;
@@ -54,6 +54,8 @@ pub mod wasm;
/// The entry point for the Scryer Prolog CLI. /// The entry point for the Scryer Prolog CLI.
pub fn run_binary() -> std::process::ExitCode { pub fn run_binary() -> std::process::ExitCode {
use crate::atom_table::Atom; use crate::atom_table::Atom;
#[cfg(feature = "repl")]
use crate::machine::INTERRUPT; use crate::machine::INTERRUPT;
#[cfg(feature = "repl")] #[cfg(feature = "repl")]

View File

@@ -12,7 +12,7 @@ integers that may also be interesting.
:- use_module(library(lists), [length/2]). :- use_module(library(lists), [length/2]).
:- use_module(library(error)). :- use_module(library(error)).
%% between(+Lower, +Upper, -X). %% between(+Lower, +Upper, ?X).
% %
% Given Lower and Upper are both integer numbers, true iff X is an integer so that _Lower =< X =< Upper_. % Given Lower and Upper are both integer numbers, true iff X is an integer so that _Lower =< X =< Upper_.
% Can be used both to check if X is between Lower and Upper or to generate an integer between % Can be used both to check if X is between Lower and Upper or to generate an integer between

View File

@@ -944,65 +944,6 @@ findall(Template, Goal, Solutions0, Solutions1) :-
builtins:findall_cleanup(LhLength, Error) builtins:findall_cleanup(LhLength, Error)
). ).
:- non_counted_backtracking set_difference/3.
set_difference([X|Xs], [Y|Ys], Zs) :-
X == Y, !, set_difference(Xs, [Y|Ys], Zs).
set_difference([X|Xs], [Y|Ys], [X|Zs]) :-
X @< Y, !, set_difference(Xs, [Y|Ys], Zs).
set_difference([X|Xs], [Y|Ys], Zs) :-
X @> Y, !, set_difference([X|Xs], Ys, Zs).
set_difference([], _, []) :- !.
set_difference(Xs, [], Xs).
% variant/2 checks whether X is a variant of Y per the definition in
% 7.1.6.1 of the ISO standard.
:- non_counted_backtracking variant/4.
variant(X,Y,VPs,VPs0) :-
( var(X) ->
var(Y),
VPs = [X-Y|VPs0]
; var(Y) ->
false
; X =.. [FX | XArgs],
Y =.. [FX | YArgs],
lists:foldl('$call'(builtins:variant), XArgs, YArgs, VPs, VPs0)
).
:- non_counted_backtracking variant/2.
singleton([_]).
variant(X, Y) :-
variant(X,Y, VPs, []),
keysort(VPs, SVPs),
pairs:group_pairs_by_key(SVPs, SVPKs),
pairs:pairs_values(SVPKs, Vals),
lists:maplist('$call'(builtins:term_variables), Vals, Vs),
lists:maplist('$call'(builtins:singleton), Vs),
term_variables(Vs, YVars),
lists:length(SVPKs, N),
lists:length(YVars, N).
:- non_counted_backtracking group_by_variant/4.
group_by_variant([V2-S2 | Pairs], V1-S1, [S2 | Solutions], Pairs0) :-
variant(V1, V2),
!,
V1 = V2,
group_by_variant(Pairs, V2-S2, Solutions, Pairs0).
group_by_variant(Pairs, _, [], Pairs).
:- non_counted_backtracking group_by_variants/2.
group_by_variants([V-S|Pairs], [V-Solution|Solutions]) :-
group_by_variant([V-S|Pairs], V-S, Solution, Pairs0),
group_by_variants(Pairs0, Solutions).
group_by_variants([], []).
:- non_counted_backtracking iterate_variants/3. :- non_counted_backtracking iterate_variants/3.
@@ -1035,9 +976,7 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses)
( Goal1 = _ ^ _ ) -> ( Goal1 = _ ^ _ ) ->
rightmost_power(Goal1, Goal2, ExistentialVars0), rightmost_power(Goal1, Goal2, ExistentialVars0),
term_variables(ExistentialVars0, ExistentialVars), term_variables(ExistentialVars0, ExistentialVars),
sort(Witnesses0, Witnesses1), lists:append(Witnesses0, Witnesses, ExistentialVars),
sort(ExistentialVars, ExistentialVars1),
set_difference(Witnesses1, ExistentialVars1, Witnesses),
expand_goal(M:Goal2, M, Goal3), expand_goal(M:Goal2, M, Goal3),
findall(Witnesses-Template, Goal3, PairedSolutions) findall(Witnesses-Template, Goal3, PairedSolutions)
; Witnesses = Witnesses0, ; Witnesses = Witnesses0,
@@ -1045,6 +984,34 @@ findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses)
). ).
:- non_counted_backtracking split_by_variant/4.
:- non_counted_backtracking split_by_variant/3.
:- non_counted_backtracking unify_variant_variables/2.
split_by_variant([V2-S2 | Pairs], V1-S1, Solutions, Rest) :-
( V1 == V2 ->
Solutions = [S2 | Solutions1],
split_by_variant(Pairs, V1-S1, Solutions1, Rest)
; Solutions = [],
Rest = [V2-S2 | Pairs]
).
split_by_variant([], _, [], []).
split_by_variant([V-S|Pairs], Ws, Solutions) :-
split_by_variant(Pairs, V-S, Solutions0, Rest),
( Rest == [] -> V = Ws, Solutions = [S|Solutions0]
; V = Ws, Solutions = [S|Solutions0]
; split_by_variant(Rest, Ws, Solutions)
).
unify_variant_variables([], _Dict).
unify_variant_variables([V-_S|Pairs], Dict) :-
term_variables(V, VVars),
lists:append(VVars, _, Dict),
unify_variant_variables(Pairs, Dict).
:- meta_predicate(bagof(?, 0, ?)). :- meta_predicate(bagof(?, 0, ?)).
:- non_counted_backtracking bagof/3. :- non_counted_backtracking bagof/3.
@@ -1074,22 +1041,10 @@ bagof(Template, Goal, Solution) :-
term_variables(Goal, GoalVars), term_variables(Goal, GoalVars),
term_variables(TemplateVars+GoalVars, TGVs), term_variables(TemplateVars+GoalVars, TGVs),
lists:append(TemplateVars, Witnesses0, TGVs), lists:append(TemplateVars, Witnesses0, TGVs),
findall_with_existential(Template, Goal, PairedSolutions0, Witnesses0, Witnesses), findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses),
keysort(PairedSolutions0, PairedSolutions), unify_variant_variables(PairedSolutions, _Dict),
group_by_variants(PairedSolutions, GroupedSolutions), keysort(PairedSolutions, PairedSolutions1),
iterate_variants(GroupedSolutions, Witnesses, Solution). split_by_variant(PairedSolutions1, Witnesses, Solution).
:- non_counted_backtracking iterate_variants_and_sort/3.
iterate_variants_and_sort([V-Solution0|GroupSolutions], V, Solution) :-
sort(Solution0, Solution1),
Solution1 = Solution,
( GroupSolutions == [] -> !
; true
).
iterate_variants_and_sort([_|GroupSolutions], Ws, Solution) :-
iterate_variants_and_sort(GroupSolutions, Ws, Solution).
:- meta_predicate(setof(?, 0, ?)). :- meta_predicate(setof(?, 0, ?)).
@@ -1112,10 +1067,10 @@ setof(Template, Goal, Solution) :-
term_variables(Goal, GoalVars), term_variables(Goal, GoalVars),
term_variables(TemplateVars+GoalVars, TGVs), term_variables(TemplateVars+GoalVars, TGVs),
lists:append(TemplateVars, Witnesses0, TGVs), lists:append(TemplateVars, Witnesses0, TGVs),
findall_with_existential(Template, Goal, PairedSolutions0, Witnesses0, Witnesses), findall_with_existential(Template, Goal, PairedSolutions, Witnesses0, Witnesses),
'$keysort_with_constant_var_ordering'(PairedSolutions0, PairedSolutions), % see 7.2.1 unify_variant_variables(PairedSolutions, _Dict),
group_by_variants(PairedSolutions, GroupedSolutions), sort(PairedSolutions, PairedSolutions1),
iterate_variants_and_sort(GroupedSolutions, Witnesses, Solution). split_by_variant(PairedSolutions1, Witnesses, Solution).
% Clause retrieval and information. % Clause retrieval and information.
@@ -1551,7 +1506,7 @@ atom_chars(Atom, List) :-
%% atom_codes(?Atom, ?Codes). %% atom_codes(?Atom, ?Codes).
% %
% Relates an atom with a string in codes representation. It can be used to convert % Relates an atom with a string in codes representation. It can be used to convert
% between atoms and strings. However, codes is not the default representation of double quoutes % between atoms and strings. However, codes is not the default representation of double quoted
% strings in Scryer Prolog. Examples: % strings in Scryer Prolog. Examples:
% %
% ``` % ```

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-2024 Markus Triska Copyright (C): 2016-2026 Markus Triska
This library provides CLP(): This library provides CLP():
@@ -6844,6 +6844,22 @@ gcc_global(Vs, KNs) -->
% the propagator of tuples_in/2). Hence: We need this only if % the propagator of tuples_in/2). Hence: We need this only if
% an example shows it, ideally found by a systematic search % an example shows it, ideally found by a systematic search
% that can be used to test the implementation. % that can be used to test the implementation.
%
% UPDATE: A case that requires gcc_check//1 was recently found,
% see: https://github.com/mthom/scryer-prolog/issues/3369.
%
% We need to study carefully what exactly happens in this case.
% The example involves reified constraints, which post constraints
% in their propagators. The example also involves multiple queues,
% because not all reified constraints use variables_same_queue/1
% (for instance (#==>)/2 does not), and variables_same_queue/1 would
% in fact need changes to handle cases where variables no longer
% have a clpz attribute attached. Such cases arise for variables
% involved in entailed reified constraints.
gcc_check(KNs), % see comment above.
do_queue, % is this needed? this (also) needs more analysis.
{ with_local_attributes(Vs, { 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),

View File

@@ -11,34 +11,75 @@ The main predicate is `use_foreign_module/2`. It takes a library name (which dep
operating system could be a `.so`, `.dylib` or `.dll` file). and a list of functions. Each operating system could be a `.so`, `.dylib` or `.dll` file). and a list of functions. Each
function is defined by its name, a list of the type of the arguments, and the return argument. function is defined by its name, a list of the type of the arguments, and the return argument.
Types available are: `sint8`/`i8`, `uint8`/`u8`, `sint16`/`i16`, `uint16`/`u16`, `sint32`/`i32`, `uint32`/`u32`, `sint64`/`i64`, For each function in the list a predicate of the same name is generated in the ffi module which
`uint64`/`u64`, `f32`, `f64`, `cstr`, `void`, `bool`, `ptr` and custom structs, which can be defined can then be used to call the native code.
with `foreign_struct/2`. The predicates arguments are the input arguments of the foreign function and depending on the return type an extra argument for the return value.
Functions with return type `void` or `bool` don't have this extra argument.
After that, each function on the lists maps to a predicate created in the ffi module which Predicates for functions with return type `void` always succeed.
are used to call the native code. Predicates for functions with retun type `bool` succeed iff the return value is 1.
The predicate takes the functor name after the function name. Then, the arguments are the input
arguments followed by a return argument. However, functions with return type `void` or `bool`
don't have that return argument. Predicates with `void` always succeed and `bool` predicates depend
on the return value on the native side.
``` ```
ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN, -ReturnArg). % for all return types except void and bool ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN, -ReturnArg). % for all return types except void and bool
ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN). % for void and bool ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN). % for void and bool
``` ```
## Notes regarding cstr ## Available types are
### Basic C Types
[C Types Reference](https://en.cppreference.com/w/c/language/types.html)
- `void`,
- `char`, `uchar`, `schar`
- `short`, `ushort`
- `int`, `uint`
- `long`, `ulong`,
- `longlong`, `ulonglong`,
- `float`, `double`
### Fixed Width Integer Types
[C Fixed With Integer Types Reference](https://en.cppreference.com/w/c/types/integer.html)
- `sint8`/`i8`, `uint8`/`u8`,
- `sint16`/`i16`, `uint16`/`u16`,
- `sint32`/`i32`, `uint32`/`u32`,
- `sint64`/`i64`, `uint64`/`u64`,
### Fixed Width Floating-Point Types
[C++ Fixed Width Floating-Point Types Reference](https://en.cppreference.com/w/cpp/types/floating-point.html)
- `f32`, `f64`
### Other Types
- `cstr`,
- `ptr`,
- `bool` and,
- custom structs, which can be defined with `foreign_struct/2`.
### Notes regarding bool
- Not necessarily compatible with the fundamental C type bool.
- Same as `i8` but only values 0 and 1 are valid values.
### Notes regarding cstr
- When using `cstr` as an argument type the string will be deallocated once the function returns. - When using `cstr` as an argument type the string will be deallocated once the function returns.
- When using `cstr` as a return type the string will be copied and won't be deallocated. - When using `cstr` as a return type the string will be copied and won't be deallocated.
- When an ffi function returns bytes that are not a valid utf8-string the bytes will be turned into a list of `codes` (integers)
instead of a string (list of `chars`). Note: passing a list of `codes` is not accepted in argument position.
- In argument position you can also pass a pointer directly instead of a string,
e.g. to pass a null-pointer one can provide the integer 0 as the argument.
- In return position a null-pointer will be returned as the integer 0
## Example ## Example
For example, let's see how to define a function from the [raylib](https://www.raylib.com/) library. For example, let's see how to define a function from the [raylib](https://www.raylib.com/) library.
``` ```
?- use_foreign_module("./libraylib.so", ['InitWindow'([sint32, sint32, cstr], void)]). ?- use_foreign_module("./libraylib.so", ['InitWindow'([int, int, cstr], void)]).
``` ```
This creates a `'InitWindow'` predicate under the ffi module. Now, we can call it: This creates a `'InitWindow'` predicate under the ffi module. Now, we can call it:
@@ -143,7 +184,7 @@ allocate(Allocator, Type, Args, Ptr) :-
% %
% Read a value of Type from the pointer Ptr and unify the read value with Value % Read a value of Type from the pointer Ptr and unify the read value with Value
% %
% For type cstr take read a nul-terminated utf-8 string starting at Ptr. % For type cstr read a nul-terminated utf-8 string starting at Ptr.
% %
read_ptr(Type, Ptr, Value) :- read_ptr(Type, Ptr, Value) :-
must_be(atom, Type), must_be(atom, Type),
@@ -163,7 +204,7 @@ deallocate(Allocator, Type, Ptr) :-
%% array_type(+ElemType, +Len, -ArrayType) %% array_type(+ElemType, +Len, -ArrayType)
% %
% unify the ffi type for an array of lenth Len with element type ElemType with ArrayType % unify the ffi type for an array of length Len with element type ElemType with ArrayType
% %
array_type(ElemType, Len, ArrayType) :- array_type(ElemType, Len, ArrayType) :-
(Len =< 0 -> domain_error(greater_than_zero, Len, array_type/3); true), (Len =< 0 -> domain_error(greater_than_zero, Len, array_type/3); true),
@@ -183,7 +224,7 @@ array_type(ElemType, Len, ArrayType) :-
% Allocate the Locals, evaluate the Goal and deallocate the Locals. % Allocate the Locals, evaluate the Goal and deallocate the Locals.
% The Locals will also be cleandup when Goal fails or throws an error. % The Locals will also be cleandup when Goal fails or throws an error.
% %
% Locals is a list of local variable definitions let(-Ptr, +Type, +Args). % Locals is a list of local variable definitions `let(-Ptr, +Type, +Args)`.
% Ptr will be unified with the pointer to the local of Type initialized with Args. % Ptr will be unified with the pointer to the local of Type initialized with Args.
% %
with_locals(Locals, Goal) :- with_locals(Locals, Goal) :-

View File

@@ -112,12 +112,29 @@ module_qualification(M, H0, H) :-
H0 =.. [Method, Path, Goal], H0 =.. [Method, Path, Goal],
H =.. [Method, Path, M:Goal]. H =.. [Method, Path, M:Goal].
http_listen__(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit) :-
'$http_listen'(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit).
http_listen_stop_(HttpListener) :-
'$http_listen_stop'(HttpListener).
http_accept_(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle) :-
'$http_accept'(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle).
http_answer_(ResponseHandle, Code, Headers, ResponseStream) :-
'$http_answer'(ResponseHandle, Code, Headers, ResponseStream).
http_listen_(Port, Handlers, Options) :- http_listen_(Port, Handlers, Options) :-
parse_options(Options, TLSKey, TLSCert, ContentLengthLimit), parse_options(Options, TLSKey, TLSCert, ContentLengthLimit),
phrase(format_("0.0.0.0:~d", [Port]), Addr), phrase(format_("0.0.0.0:~d", [Port]), Addr),
'$http_listen'(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit),!, setup_call_cleanup(
format("Listening at ~s\n", [Addr]), (
http_loop(HttpListener, Handlers). http_listen__(Addr, HttpListener, TLSKey, TLSCert, ContentLengthLimit),
format("Listening at http://~s\n", [Addr])
),
http_loop(HttpListener, Handlers),
http_listen_stop_(HttpListener)
).
parse_options(Options, TLSKey, TLSCert, ContentLengthLimit) :- parse_options(Options, TLSKey, TLSCert, ContentLengthLimit) :-
member_option_default(tls_key, Options, "", TLSKey), member_option_default(tls_key, Options, "", TLSKey),
@@ -132,12 +149,12 @@ member_option_default(Key, List, Default, Default) :-
X =.. [Key, _], X =.. [Key, _],
\+ member(X, List). \+ member(X, List).
http_loop(HttpListener, Handlers) :- http_loop(HttpListener, Handlers) :-
'$http_accept'(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle), time((
http_accept_(HttpListener, RequestMethod, RequestPath, RequestHeaders, RequestQuery, RequestStream, ResponseHandle),
current_time(Time), current_time(Time),
phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString), phrase(format_time("%Y-%m-%d (%H:%M:%S)", Time), TimeString),
format("~s ~w ~s\n", [TimeString, RequestMethod, RequestPath]), format("~s ~w ~s", [TimeString, RequestMethod, RequestPath]),
maplist(map_header_kv, RequestHeaders, RequestHeadersKV), maplist(map_header_kv, RequestHeaders, RequestHeadersKV),
phrase(parse_queries(RequestQueries), RequestQuery), phrase(parse_queries(RequestQueries), RequestQuery),
( (
@@ -145,23 +162,42 @@ http_loop(HttpListener, Handlers) :-
( (
HttpRequest = http_request(RequestHeadersKV, stream(RequestStream), RequestQueries), HttpRequest = http_request(RequestHeadersKV, stream(RequestStream), RequestQueries),
HttpResponse = http_response(_, _, _), HttpResponse = http_response(_, _, _),
catch(
(call(Handler, HttpRequest, HttpResponse) -> (call(Handler, HttpRequest, HttpResponse) ->
send_response(ResponseHandle, HttpResponse) send_response(ResponseHandle, HttpResponse)
; ( ;
'$http_answer'(ResponseHandle, 500, [], ResponseStream), setup_call_cleanup(
call_cleanup(format(ResponseStream, "Internal Server Error", []), close(ResponseStream))) http_answer_(ResponseHandle, 500, [], ResponseStream),
) format(ResponseStream, "Internal Server Error", []),
) close(ResponseStream)
; (
'$http_answer'(ResponseHandle, 404, [], ResponseStream),
call_cleanup(format(ResponseStream, "Not Found", []), close(ResponseStream)))
), ),
throw(handler_not_available(Handler, RequestMethod, RequestPath, RequestQuery, RequestHeaders))
),
HandlerError,
(
setup_call_cleanup(
http_answer_(ResponseHandle, 500, [], ResponseStream),
format(ResponseStream, "Internal Server Error", []),
close(ResponseStream)
),
throw(HandlerError)
)
)
)
;
setup_call_cleanup(
http_answer_(ResponseHandle, 404, [], ResponseStream),
format(ResponseStream, "Not Found", []),
close(ResponseStream)
)
)
)),
http_loop(HttpListener, Handlers). http_loop(HttpListener, Handlers).
send_response(ResponseHandle, http_response(StatusCode0, text(ResponseText), ResponseHeaders0)) :- send_response(ResponseHandle, http_response(StatusCode0, text(ResponseText), ResponseHeaders0)) :-
default(StatusCode0, 200, StatusCode), default(StatusCode0, 200, StatusCode),
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0), maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
'$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream0), http_answer_(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream0),
open(stream(ResponseStream0), write, ResponseStream, [type(text)]), open(stream(ResponseStream0), write, ResponseStream, [type(text)]),
catch( catch(
call_cleanup(format(ResponseStream, "~s", [ResponseText]),close(ResponseStream)), call_cleanup(format(ResponseStream, "~s", [ResponseText]),close(ResponseStream)),
@@ -172,7 +208,7 @@ send_response(ResponseHandle, http_response(StatusCode0, text(ResponseText), Res
send_response(ResponseHandle, http_response(StatusCode0, bytes(ResponseBytes), ResponseHeaders0)) :- send_response(ResponseHandle, http_response(StatusCode0, bytes(ResponseBytes), ResponseHeaders0)) :-
default(StatusCode0, 200, StatusCode), default(StatusCode0, 200, StatusCode),
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0), maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
'$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream), http_answer_(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
catch( catch(
call_cleanup(format(ResponseStream, "~s", [ResponseBytes]),close(ResponseStream)), call_cleanup(format(ResponseStream, "~s", [ResponseBytes]),close(ResponseStream)),
error(existence_error(stream, _), _), error(existence_error(stream, _), _),
@@ -182,7 +218,7 @@ send_response(ResponseHandle, http_response(StatusCode0, bytes(ResponseBytes), R
send_response(ResponseHandle, http_response(StatusCode0, file(Filename), ResponseHeaders0)) :- send_response(ResponseHandle, http_response(StatusCode0, file(Filename), ResponseHeaders0)) :-
default(StatusCode0, 200, StatusCode), default(StatusCode0, 200, StatusCode),
maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0), maplist(map_header_kv_2, ResponseHeaders, ResponseHeaders0),
'$http_answer'(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream), http_answer_(ResponseHandle, StatusCode, ResponseHeaders, ResponseStream),
catch( catch(
call_cleanup( call_cleanup(
setup_call_cleanup( setup_call_cleanup(

View File

@@ -2,7 +2,7 @@
List manipulation predicates List manipulation predicates
*/ */
:- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5, :- module(lists, [member/2, select/3, append/2, append/3, foldl/4, foldl/5, foldl/6,
memberchk/2, reverse/2, length/2, maplist/2, memberchk/2, reverse/2, length/2, maplist/2,
maplist/3, maplist/4, maplist/5, maplist/6, maplist/3, maplist/4, maplist/5, maplist/6,
maplist/7, maplist/8, maplist/9, same_length/2, nth0/3, nth0/4, nth1/3, nth1/4, maplist/7, maplist/8, maplist/9, same_length/2, nth0/3, nth0/4, nth1/3, nth1/4,
@@ -314,6 +314,16 @@ foldl(G_4, [X|Xs], [Y|Ys], A0, A) :-
call(G_4, X, Y, A0, A1), call(G_4, X, Y, A0, A1),
foldl(G_4, Xs, Ys, A1, A). foldl(G_4, Xs, Ys, A1, A).
%% foldl(+Goal, ?Ls0, ?Ls1, ?Ls2, +A0, ?A).
%
% Like `foldl/4`, with 2 additional lists.
foldl(_, [], [], [], A, A).
foldl(G_5, [X|Xs], [Y|Ys], [Z|Zs], A0, A) :-
call(G_5, X, Y, Z, A0, A1),
foldl(G_5, Xs, Ys, Zs, A1, A).
%% transpose(?Ls, ?Ts). %% transpose(?Ls, ?Ts).
% %
% If Ls is a list of lists, Ts contains the transposition % If Ls is a list of lists, Ts contains the transposition

View File

@@ -1,5 +1,7 @@
% Efforts toward literate tests with quads % Efforts toward literate tests with quads
:- module(quadtests, [check_module_quads/2]).
:- use_module(library(iso_ext)). :- use_module(library(iso_ext)).
:- use_module(library(pio)). :- use_module(library(pio)).
:- use_module(library(lists)). :- use_module(library(lists)).
@@ -10,9 +12,8 @@
:- use_module(library(lambda)). :- use_module(library(lambda)).
:- use_module(library(error)). :- use_module(library(error)).
:- use_module(library(time)). :- use_module(library(time)).
:- use_module(library('numerics/testutils')).
:- use_module(testutils). :- use_module(library('numerics/special_functions')).
:- use_module(special_functions).
portray_term(Stream) :- portray_term(Stream) :-
read_term(Stream, Term, []), read_term(Stream, Term, []),
@@ -39,7 +40,7 @@ check_module_quads(Module, Quads) :-
zip(Qs, ADs, Quads), zip(Qs, ADs, Quads),
length(Qs, NQ), length(Qs, NQ),
format("% Checking ~d quads ..~n", [NQ]), format("% Checking ~d quads ..~n", [NQ]),
maplist(check_qu_ad, Qs, ADs). maplist(check_qu_ad(Module), Qs, ADs).
read_quads(Module, Quads) :- read_quads(Module, Quads) :-
module_terms(Module, Terms), module_terms(Module, Terms),
@@ -70,6 +71,7 @@ term_type(Term-_, Type) :-
( Term = (?- _) -> Type = query ( Term = (?- _) -> Type = query
; Term = (_,_) -> Type = answer_description ; Term = (_,_) -> Type = answer_description
; Term = (_;_) -> Type = answer_description ; Term = (_;_) -> Type = answer_description
; Term = (_ = _) -> Type = answer_description
; Term == true -> Type = answer_description ; Term == true -> Type = answer_description
; Term == false -> Type = answer_description ; Term == false -> Type = answer_description
; Type = clause ; Type = clause
@@ -114,22 +116,22 @@ zip([], [], []).
Xs = [1,2,3], Ys = [4,5,6]. Xs = [1,2,3], Ys = [4,5,6].
% 3. Demonstrate checking 1 quad, the top two elements of a QAs list. % 3. Demonstrate checking 1 quad, the top two elements of a QAs list.
check_qu_ad(Q-QVN, A-AVN) :- check_qu_ad(Module, Q-QVN, A-AVN) :-
Q = ?-(G), Q = ?-(G),
phrase(portray_clause_(Q), LitQ), % NB: LitQ terminates w/ newline phrase(portray_clause_(Q), LitQ), % NB: LitQ terminates w/ newline
format("% CHECKING.. ",[]), format("% CHECKING.. ",[]),
( A == true -> call(G) ( A == true -> call(Module:G)
; A == false -> ( call(G) -> false ; A == false -> ( call(Module:G) -> false
; true ; true
) )
; phrase(unconj(A), As) -> ; phrase(unconj(A), As) ->
( length(As, N), ( length(As, N),
n_answers(N, A, AVN, ADs), n_answers(N, A, AVN, ADs),
n_answers(N, G, QVN, Answers), n_answers(N, Module:G, QVN, Answers),
maplist(contains, ADs, Answers) maplist(contains, ADs, Answers)
) )
; % Otherwise, we have the ',' case of a solitary answer ; % Otherwise, we have the ',' case of a solitary answer
call(G), call(Module:G),
call(A), call(A),
QVN == AVN QVN == AVN
), ),
@@ -141,7 +143,7 @@ contains(AD, Answer) :- append(Answer, _, AD).
?- contains(['Xs'=[C],'L'=1,'_A'=C,'_B'=D], ['Xs'=[A],'L'=1]). ?- contains(['Xs'=[C],'L'=1,'_A'=C,'_B'=D], ['Xs'=[A],'L'=1]).
C = A. C = A.
?- check_qu_ad((?-length(_F,_G))-['Xs'=_F,'L'=_G],(_H=[],_I=0;_H=[_J],_I=1;_H=[_J,_K],_I=2;...)-['Xs'=_H,'L'=_I,'_A'=_J,'_B'=_K]). ?- check_qu_ad(quadtests, (?-length(_F,_G))-['Xs'=_F,'L'=_G],(_H=[],_I=0;_H=[_J],_I=1;_H=[_J,_K],_I=2;...)-['Xs'=_H,'L'=_I,'_A'=_J,'_B'=_K]).
% CHECKING.. (?-length(A,B)). % CHECKING.. (?-length(A,B)).
_F = [_A,_B], _G = 2, _H = [_J,_K], _I = 2. _F = [_A,_B], _G = 2, _H = [_J,_K], _I = 2.
@@ -185,3 +187,4 @@ n_answers_(N, G, VN, ADs) :-
?- n_answers(3, length(Xs, L), ('Xs'=Xs,'Len'=L), ADs). ?- n_answers(3, length(Xs, L), ('Xs'=Xs,'Len'=L), ADs).
Xs = [_A,_B], L = 2, ADs = [('Xs'=[],'Len'=0),('Xs'=[_C],'Len'=1),('Xs'=[_D,_E],'Len'=2)]. Xs = [_A,_B], L = 2, ADs = [('Xs'=[],'Len'=0),('Xs'=[_C],'Len'=1),('Xs'=[_D,_E],'Len'=2)].

View File

@@ -95,10 +95,12 @@ process_wait(Process, Status) :- call_with_error_context(process_wait(Process, S
% `Options` is a a list of the following options % `Options` is a a list of the following options
% %
% * timeout(Timeout) supported values for `Timeout` are 0 or `infinite` % * timeout(Timeout) supported values for `Timeout` are 0 or `infinite`
% * release(Bool) supported values for `Bool` are `true` or `false`
% %
% Each options may be specified at most once, when an option is not specified the following defaults apply: % Each options may be specified at most once, when an option is not specified the following defaults apply:
% %
% - timeout(infinite) % - timeout(infinite)
% - release(true)
% %
process_wait(Process, Status, Options) :- call_with_error_context(process_wait_(Process, Status, Options), predicate-process_wait/3). process_wait(Process, Status, Options) :- call_with_error_context(process_wait_(Process, Status, Options), predicate-process_wait/3).
@@ -106,17 +108,29 @@ process_wait_(Process, Status, Options) :-
valid_process(Process), valid_process(Process),
check_options( check_options(
[ [
option([timeout], valid_timeout, timeout(infinite), timeout(Timeout)) option([timeout], valid_timeout, timeout(infinite), timeout(Timeout)),
option([release], valid_release, release(true), release(Release))
], ],
Options, Options,
process_wait_option process_wait_option
), ),
'$process_wait'(Process, Exit, Timeout), '$process_wait'(Process, Exit, Timeout),
((true = Release) -> '$process_release'(Process) ; true),
Exit = Status. Exit = Status.
valid_timeout(timeout(infinite)). valid_timeout(timeout(infinite)).
valid_timeout(timeout(0)). valid_timeout(timeout(0)).
valid_release(release(Arg)) :-
( var(Arg) -> instantiation_error([])
; valid_bool(Arg) -> true
; domain_error(boolean, Arg, [])
).
valid_bool(true).
valid_bool(false).
%% process_kill(+Process). %% process_kill(+Process).
% %
@@ -141,8 +155,7 @@ process_release(Process) :- call_with_error_context(process_release_(Process), p
process_release_(Process) :- process_release_(Process) :-
valid_process(Process), valid_process(Process),
process_wait(Process, _), process_wait(Process, _).
'$process_release'(Process).
must_be_known_options(Valid, Options, Domain) :- call_with_error_context(must_be_known_options_(Valid, [], Options, Domain),predicate-must_be_known_options/3). must_be_known_options(Valid, Options, Domain) :- call_with_error_context(must_be_known_options_(Valid, [], Options, Domain),predicate-must_be_known_options/3).

View File

@@ -40,6 +40,7 @@
term_si/1, term_si/1,
chars_si/1, chars_si/1,
dif_si/2, dif_si/2,
not_si/1,
when_si/2]). when_si/2]).
:- use_module(library(lists)). :- use_module(library(lists)).
@@ -100,6 +101,17 @@ dif_si(X, Y) :-
; throw(error(instantiation_error,dif_si/2)) ; throw(error(instantiation_error,dif_si/2))
). ).
%% not_si(+Goal).
%
% True if Goal is not provable. Instantiation error if Goal is not
% ground.
:- meta_predicate(not_si(0)).
not_si(Goal) :-
term_si(Goal),
\+ Goal.
:- meta_predicate(when_si(+, 0)). :- meta_predicate(when_si(+, 0)).
%% when_si(Condition, Goal). %% when_si(Condition, Goal).

View File

@@ -1,4 +1,3 @@
use std::collections::BTreeSet;
use std::env; use std::env;
#[derive(Debug)] #[derive(Debug)]
@@ -8,9 +7,8 @@ pub struct MachineArgs {
impl MachineArgs { impl MachineArgs {
pub fn new() -> Self { pub fn new() -> Self {
let args: BTreeSet<String> = env::args().collect();
Self { Self {
add_history: !args.contains("--no-add-history"), add_history: env::args().all(|arg| arg != "--no-add-history"),
} }
} }
} }

View File

@@ -1,6 +1,6 @@
use dashu::base::{Abs, Gcd, Signed, UnsignedAbs}; use dashu::base::{Abs, Gcd, Signed, UnsignedAbs};
use dashu::integer::fast_div::ConstDivisor;
use dashu::integer::IBig; use dashu::integer::IBig;
use dashu::integer::fast_div::ConstDivisor;
use divrem::*; use divrem::*;
use num_order::NumOrd; use num_order::NumOrd;
@@ -21,7 +21,6 @@ use ordered_float::{Float, OrderedFloat};
use std::cmp; use std::cmp;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::f64; use std::f64;
use std::mem;
macro_rules! try_numeric_result { macro_rules! try_numeric_result {
($e: expr, $stub_gen: expr) => { ($e: expr, $stub_gen: expr) => {
@@ -1120,9 +1119,11 @@ pub(crate) fn bitwise_complement(n1: Number, arena: &mut Arena) -> Result<Number
impl MachineState { impl MachineState {
#[inline] #[inline]
pub fn get_number(&mut self, at: &ArithmeticTerm) -> Result<Number, MachineStub> { pub fn get_number(&mut self, at: &ArithmeticTerm) -> Result<Number, MachineStub> {
match at { let value = match at {
&ArithmeticTerm::Reg(r) => { &ArithmeticTerm::Reg(r) => self.store(self.deref(self[r])),
let value = self.store(self.deref(self[r])); &ArithmeticTerm::IntermReg(i) => self.registers[i],
ArithmeticTerm::Number(n) => return Ok(*n),
};
match Number::try_from((value, &self.arena.f64_tbl)) { match Number::try_from((value, &self.arena.f64_tbl)) {
Ok(n) => Ok(n), Ok(n) => Ok(n),
@@ -1132,13 +1133,6 @@ impl MachineState {
} }
} }
} }
&ArithmeticTerm::Interm(i) => Ok(mem::replace(
&mut self.interms[i - 1],
Number::Fixnum(Fixnum::build_with(0)),
)),
ArithmeticTerm::Number(n) => Ok(*n),
}
}
pub fn get_rational( pub fn get_rational(
&mut self, &mut self,
@@ -1158,6 +1152,7 @@ impl MachineState {
term_loc: usize, term_loc: usize,
) -> Result<Number, MachineStub> { ) -> Result<Number, MachineStub> {
let stub_gen = || functor_stub(atom!("is"), 2); let stub_gen = || functor_stub(atom!("is"), 2);
let mut interms = vec![];
let mut iter = let mut iter =
stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, term_loc); stackful_post_order_iter::<NonListElider>(&mut self.heap, &mut self.stack, term_loc);
@@ -1194,38 +1189,38 @@ impl MachineState {
read_heap_cell!(value, read_heap_cell!(value,
(HeapCellValueTag::Atom, (name, arity)) => { (HeapCellValueTag::Atom, (name, arity)) => {
if arity == 2 { if arity == 2 {
let a2 = self.interms.pop().unwrap(); let a2 = interms.pop().unwrap();
let a1 = self.interms.pop().unwrap(); let a1 = interms.pop().unwrap();
match name { match name {
atom!("+") => self.interms.push(drop_iter_on_err!( atom!("+") => interms.push(drop_iter_on_err!(
self, self,
iter, iter,
try_numeric_result!(add(a1, a2, &mut self.arena), stub_gen) try_numeric_result!(add(a1, a2, &mut self.arena), stub_gen)
)), )),
atom!("-") => self.interms.push(drop_iter_on_err!( atom!("-") => interms.push(drop_iter_on_err!(
self, self,
iter, iter,
try_numeric_result!(sub(a1, a2, &mut self.arena), stub_gen) try_numeric_result!(sub(a1, a2, &mut self.arena), stub_gen)
)), )),
atom!("*") => self.interms.push(drop_iter_on_err!( atom!("*") => interms.push(drop_iter_on_err!(
self, self,
iter, iter,
try_numeric_result!(mul(a1, a2, &mut self.arena), stub_gen) try_numeric_result!(mul(a1, a2, &mut self.arena), stub_gen)
)), )),
atom!("/") => self.interms.push( atom!("/") => interms.push(
drop_iter_on_err!(self, iter, div(a1, a2)) drop_iter_on_err!(self, iter, div(a1, a2))
), ),
atom!("**") => self.interms.push( atom!("**") => interms.push(
drop_iter_on_err!(self, iter, pow(a1, a2, atom!("is"))) drop_iter_on_err!(self, iter, pow(a1, a2, atom!("is")))
), ),
atom!("^") => self.interms.push( atom!("^") => interms.push(
drop_iter_on_err!(self, iter, int_pow(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, int_pow(a1, a2, &mut self.arena))
), ),
atom!("max") => self.interms.push( atom!("max") => interms.push(
drop_iter_on_err!(self, iter, max(a1, a2)) drop_iter_on_err!(self, iter, max(a1, a2))
), ),
atom!("min") => self.interms.push( atom!("min") => interms.push(
drop_iter_on_err!(self, iter, min(a1, a2)) drop_iter_on_err!(self, iter, min(a1, a2))
), ),
atom!("rdiv") => { atom!("rdiv") => {
@@ -1246,39 +1241,39 @@ impl MachineState {
&mut self.arena &mut self.arena
); );
self.interms.push(Number::Rational(result)); interms.push(Number::Rational(result));
} }
atom!("//") => self.interms.push( atom!("//") => interms.push(
drop_iter_on_err!(self, iter, idiv(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, idiv(a1, a2, &mut self.arena))
), ),
atom!("div") => self.interms.push( atom!("div") => interms.push(
drop_iter_on_err!(self, iter, int_floor_div(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, int_floor_div(a1, a2, &mut self.arena))
), ),
atom!(">>") => self.interms.push( atom!(">>") => interms.push(
drop_iter_on_err!(self, iter, shr(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, shr(a1, a2, &mut self.arena))
), ),
atom!("<<") => self.interms.push( atom!("<<") => interms.push(
drop_iter_on_err!(self, iter, shl(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, shl(a1, a2, &mut self.arena))
), ),
atom!("/\\") => self.interms.push( atom!("/\\") => interms.push(
drop_iter_on_err!(self, iter, and(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, and(a1, a2, &mut self.arena))
), ),
atom!("\\/") => self.interms.push( atom!("\\/") => interms.push(
drop_iter_on_err!(self, iter, or(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, or(a1, a2, &mut self.arena))
), ),
atom!("xor") => self.interms.push( atom!("xor") => interms.push(
drop_iter_on_err!(self, iter, xor(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, xor(a1, a2, &mut self.arena))
), ),
atom!("mod") => self.interms.push( atom!("mod") => interms.push(
drop_iter_on_err!(self, iter, modulus(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, modulus(a1, a2, &mut self.arena))
), ),
atom!("rem") => self.interms.push( atom!("rem") => interms.push(
drop_iter_on_err!(self, iter, remainder(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, remainder(a1, a2, &mut self.arena))
), ),
atom!("atan2") => self.interms.push(Number::Float(OrderedFloat( atom!("atan2") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, atan2(a1, a2)) drop_iter_on_err!(self, iter, atan2(a1, a2))
))), ))),
atom!("gcd") => self.interms.push( atom!("gcd") => interms.push(
drop_iter_on_err!(self, iter, gcd(a1, a2, &mut self.arena)) drop_iter_on_err!(self, iter, gcd(a1, a2, &mut self.arena))
), ),
_ => { _ => {
@@ -1294,56 +1289,56 @@ impl MachineState {
continue; continue;
} else if arity == 1 { } else if arity == 1 {
let a1 = self.interms.pop().unwrap(); let a1 = interms.pop().unwrap();
match name { match name {
atom!("-") => self.interms.push(neg(a1, &mut self.arena)), atom!("-") => interms.push(neg(a1, &mut self.arena)),
atom!("+") => self.interms.push(a1), atom!("+") => interms.push(a1),
atom!("cos") => self.interms.push(Number::Float(OrderedFloat( atom!("cos") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, cos(a1)) drop_iter_on_err!(self, iter, cos(a1))
))), ))),
atom!("sin") => self.interms.push(Number::Float(OrderedFloat( atom!("sin") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, sin(a1)) drop_iter_on_err!(self, iter, sin(a1))
))), ))),
atom!("tan") => self.interms.push(Number::Float(OrderedFloat( atom!("tan") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, tan(a1)) drop_iter_on_err!(self, iter, tan(a1))
))), ))),
atom!("float_fractional_part") => self.interms.push(Number::Float(OrderedFloat( atom!("float_fractional_part") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, float_fractional_part(a1)) drop_iter_on_err!(self, iter, float_fractional_part(a1))
))), ))),
atom!("float_integer_part") => self.interms.push(Number::Float(OrderedFloat( atom!("float_integer_part") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, float_integer_part(a1)) drop_iter_on_err!(self, iter, float_integer_part(a1))
))), ))),
atom!("sqrt") => self.interms.push(Number::Float(OrderedFloat( atom!("sqrt") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, sqrt(a1)) drop_iter_on_err!(self, iter, sqrt(a1))
))), ))),
atom!("log") => self.interms.push(Number::Float(OrderedFloat( atom!("log") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, log(a1)) drop_iter_on_err!(self, iter, log(a1))
))), ))),
atom!("exp") => self.interms.push(Number::Float(OrderedFloat( atom!("exp") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, exp(a1)) drop_iter_on_err!(self, iter, exp(a1))
))), ))),
atom!("acos") => self.interms.push(Number::Float(OrderedFloat( atom!("acos") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, acos(a1)) drop_iter_on_err!(self, iter, acos(a1))
))), ))),
atom!("asin") => self.interms.push(Number::Float(OrderedFloat( atom!("asin") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, asin(a1)) drop_iter_on_err!(self, iter, asin(a1))
))), ))),
atom!("atan") => self.interms.push(Number::Float(OrderedFloat( atom!("atan") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, atan(a1)) drop_iter_on_err!(self, iter, atan(a1))
))), ))),
atom!("abs") => self.interms.push(abs(a1, &mut self.arena)), atom!("abs") => interms.push(abs(a1, &mut self.arena)),
atom!("float") => self.interms.push(Number::Float(OrderedFloat( atom!("float") => interms.push(Number::Float(OrderedFloat(
drop_iter_on_err!(self, iter, float(a1)) drop_iter_on_err!(self, iter, float(a1))
))), ))),
atom!("truncate") => self.interms.push(truncate(a1, &mut self.arena)), atom!("truncate") => interms.push(truncate(a1, &mut self.arena)),
atom!("round") => self.interms.push(drop_iter_on_err!(self, iter, round(a1, &mut self.arena))), atom!("round") => interms.push(drop_iter_on_err!(self, iter, round(a1, &mut self.arena))),
atom!("ceiling") => self.interms.push(ceiling(a1, &mut self.arena)), atom!("ceiling") => interms.push(ceiling(a1, &mut self.arena)),
atom!("floor") => self.interms.push(floor(a1, &mut self.arena)), atom!("floor") => interms.push(floor(a1, &mut self.arena)),
atom!("\\") => self.interms.push( atom!("\\") => interms.push(
drop_iter_on_err!(self, iter, bitwise_complement(a1, &mut self.arena)) drop_iter_on_err!(self, iter, bitwise_complement(a1, &mut self.arena))
), ),
atom!("sign") => self.interms.push(a1.sign()), atom!("sign") => interms.push(a1.sign()),
_ => { _ => {
let evaluable_stub = functor_stub(name, 1); let evaluable_stub = functor_stub(name, 1);
std::mem::drop(iter); std::mem::drop(iter);
@@ -1362,15 +1357,15 @@ impl MachineState {
} else if arity == 0 { } else if arity == 0 {
match name { match name {
atom!("pi") => { atom!("pi") => {
self.interms.push(Number::Float(OrderedFloat(f64::consts::PI))); interms.push(Number::Float(OrderedFloat(f64::consts::PI)));
continue; continue;
} }
atom!("e") => { atom!("e") => {
self.interms.push(Number::Float(OrderedFloat(f64::consts::E))); interms.push(Number::Float(OrderedFloat(f64::consts::E)));
continue; continue;
} }
atom!("epsilon") => { atom!("epsilon") => {
self.interms.push(Number::Float(OrderedFloat(f64::EPSILON))); interms.push(Number::Float(OrderedFloat(f64::EPSILON)));
continue; continue;
} }
_ => { _ => {
@@ -1386,19 +1381,19 @@ impl MachineState {
return Err(self.error_form(evaluable_error, stub)); return Err(self.error_form(evaluable_error, stub));
} }
(HeapCellValueTag::Fixnum, n) => { (HeapCellValueTag::Fixnum, n) => {
self.interms.push(Number::Fixnum(n)); interms.push(Number::Fixnum(n));
} }
(HeapCellValueTag::F64Offset, offset) => { (HeapCellValueTag::F64Offset, offset) => {
let fl = self.arena.f64_tbl.get_entry(offset); let fl = self.arena.f64_tbl.get_entry(offset);
self.interms.push(Number::Float(fl)); interms.push(Number::Float(fl));
} }
(HeapCellValueTag::Cons, ptr) => { (HeapCellValueTag::Cons, ptr) => {
match_untyped_arena_ptr!(ptr, match_untyped_arena_ptr!(ptr,
(ArenaHeaderTag::Integer, n) => { (ArenaHeaderTag::Integer, n) => {
self.interms.push(Number::Integer(n)); interms.push(Number::Integer(n));
} }
(ArenaHeaderTag::Rational, r) => { (ArenaHeaderTag::Rational, r) => {
self.interms.push(Number::Rational(r)); interms.push(Number::Rational(r));
} }
_ => { _ => {
std::mem::drop(iter); std::mem::drop(iter);
@@ -1429,7 +1424,7 @@ impl MachineState {
) )
} }
Ok(self.interms.pop().unwrap()) Ok(interms.pop().unwrap())
} }
} }

View File

@@ -15,7 +15,6 @@ pub(super) struct AttrVarInitializer {
pub(super) bindings: Bindings, pub(super) bindings: Bindings,
pub(super) p: usize, pub(super) p: usize,
pub(super) cp: usize, pub(super) cp: usize,
// pub(super) instigating_p: usize,
pub(super) verify_attrs_loc: usize, pub(super) verify_attrs_loc: usize,
} }
@@ -41,27 +40,29 @@ impl MachineState {
pub(super) fn push_attr_var_binding(&mut self, h: usize, addr: HeapCellValue) { pub(super) fn push_attr_var_binding(&mut self, h: usize, addr: HeapCellValue) {
if self.attr_var_init.bindings.is_empty() { if self.attr_var_init.bindings.is_empty() {
// save self.p and self.cp and ensure that the next // save self.p and self.cp and ensure that the next
// instruction is InstallVerifyAttrInterrupt. // instruction is RunVerifyAttrInterrupt.
self.attr_var_init.p = self.p; self.attr_var_init.p = self.p;
self.attr_var_init.cp = self.cp; self.attr_var_init.cp = self.cp;
self.p = INSTALL_VERIFY_ATTR_INTERRUPT - 1; self.p = VERIFY_ATTR_INTERRUPT_LOC - 1;
self.cp = INSTALL_VERIFY_ATTR_INTERRUPT; self.cp = VERIFY_ATTR_INTERRUPT_LOC;
} }
debug_assert_eq!(self.heap[h].get_tag(), HeapCellValueTag::AttrVar); debug_assert_eq!(self.heap[h].get_tag(), HeapCellValueTag::AttrVar);
self.attr_var_init.bindings.push((h, addr)); self.attr_var_init.bindings.push((h, addr));
} }
fn populate_var_and_value_lists(&mut self) -> Result<(HeapCellValue, HeapCellValue), usize> { fn populate_var_and_value_lists(
&mut self,
) -> Result<(HeapCellValue, HeapCellValue), AllocError> {
let size = self.attr_var_init.bindings.len(); let size = self.attr_var_init.bindings.len();
let iter = self let iter = self
.attr_var_init .attr_var_init
.bindings .bindings
.iter() .iter()
.map(|(ref h, _)| attr_var_as_cell!(*h)); .map(|(h, _)| attr_var_as_cell!(*h));
let var_list_addr = sized_iter_to_heap_list(&mut self.heap, size, iter)?; let var_list_addr = sized_iter_to_heap_list(&mut self.heap, size, iter)?;
let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v); let iter = self.attr_var_init.bindings.drain(0..).map(|(_, ref v)| *v);
@@ -70,7 +71,7 @@ impl MachineState {
Ok((var_list_addr, value_list_addr)) Ok((var_list_addr, value_list_addr))
} }
fn verify_attributes(&mut self) -> Result<(), usize> { fn verify_attributes(&mut self) -> Result<(), AllocError> {
for (h, _) in &self.attr_var_init.bindings { for (h, _) in &self.attr_var_init.bindings {
self.heap[*h] = attr_var_as_cell!(*h); self.heap[*h] = attr_var_as_cell!(*h);
} }
@@ -102,21 +103,24 @@ impl MachineState {
.collect() .collect()
}; };
attr_vars.sort_unstable_by(|a1, a2| { attr_vars
compare_term_test!(self, *a1, *a2).unwrap_or(Ordering::Less) .sort_unstable_by(|a1, a2| self.compare_term_test(*a1, *a2).unwrap_or(Ordering::Less));
});
attr_vars.dedup(); attr_vars.dedup();
attr_vars attr_vars
} }
pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) -> Result<(), usize> { pub(super) fn verify_attr_interrupt(
self.allocate(arity + 3); &mut self,
p: usize,
arity: usize,
) -> Result<(), AllocError> {
self.allocate(arity + 3)?;
let e = self.e; let e = self.e;
let and_frame = self.stack.index_and_frame_mut(e); let and_frame = self.stack.index_and_frame_mut(e);
for i in 1..arity + 1 { for i in 1..=arity {
and_frame[i] = self.registers[i]; and_frame[i] = self.registers[i];
} }

View File

@@ -80,19 +80,19 @@ fn derelictize_try_me_else(
) -> Option<usize> { ) -> Option<usize> {
match &mut code[index] { match &mut code[index] {
Instruction::DynamicElse(_, _, NextOrFail::Next(0)) => None, Instruction::DynamicElse(_, _, NextOrFail::Next(0)) => None,
Instruction::DynamicElse(_, _, NextOrFail::Next(ref mut o)) => { Instruction::DynamicElse(_, _, NextOrFail::Next(o)) => {
retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(index, *o)); retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(index, *o));
Some(mem::replace(o, 0)) Some(mem::replace(o, 0))
} }
Instruction::DynamicInternalElse(_, _, NextOrFail::Next(0)) => None, Instruction::DynamicInternalElse(_, _, NextOrFail::Next(0)) => None,
Instruction::DynamicInternalElse(_, _, NextOrFail::Next(ref mut o)) => { Instruction::DynamicInternalElse(_, _, NextOrFail::Next(o)) => {
retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(index, *o)); retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(index, *o));
Some(mem::replace(o, 0)) Some(mem::replace(o, 0))
} }
Instruction::DynamicElse(_, _, NextOrFail::Fail(_)) Instruction::DynamicElse(_, _, NextOrFail::Fail(_))
| Instruction::DynamicInternalElse(_, _, NextOrFail::Fail(_)) => None, | Instruction::DynamicInternalElse(_, _, NextOrFail::Fail(_)) => None,
Instruction::TryMeElse(0) => None, Instruction::TryMeElse(0) => None,
Instruction::TryMeElse(ref mut o) => { Instruction::TryMeElse(o) => {
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(index, *o)); retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(index, *o));
Some(mem::replace(o, 0)) Some(mem::replace(o, 0))
} }
@@ -275,7 +275,7 @@ fn merge_indexed_subsequences(
.unwrap(), .unwrap(),
); );
if let Instruction::TryMeElse(ref mut o) = &mut code[inner_try_me_else_loc] { if let Instruction::TryMeElse(o) = &mut code[inner_try_me_else_loc] {
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse( retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(
inner_try_me_else_loc, inner_try_me_else_loc,
*o, *o,
@@ -324,7 +324,7 @@ fn merge_indexed_subsequences(
); );
} }
None => { None => {
if let Instruction::TryMeElse(ref mut o) = &mut code[outer_threaded_choice_instr_loc] { if let Instruction::TryMeElse(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));
@@ -472,7 +472,7 @@ fn set_switch_var_offset(
let target_indexing_line = code[index_loc].to_indexing_line_mut().unwrap(); let target_indexing_line = code[index_loc].to_indexing_line_mut().unwrap();
let old_v = match &mut target_indexing_line[0] { let old_v = match &mut target_indexing_line[0] {
IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, ref mut v, ..)) => match *v { IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, ..)) => match *v {
IndexingCodePtr::DynamicExternal(_) => { IndexingCodePtr::DynamicExternal(_) => {
mem::replace(v, IndexingCodePtr::DynamicExternal(offset)) mem::replace(v, IndexingCodePtr::DynamicExternal(offset))
} }
@@ -497,7 +497,7 @@ fn internalize_choice_instr_at(
match &mut code[instr_loc] { match &mut code[instr_loc] {
Instruction::DynamicElse(_, _, NextOrFail::Fail(_)) Instruction::DynamicElse(_, _, NextOrFail::Fail(_))
| Instruction::DynamicInternalElse(_, _, NextOrFail::Fail(_)) => {} | Instruction::DynamicInternalElse(_, _, NextOrFail::Fail(_)) => {}
Instruction::DynamicElse(_, _, ref mut o @ NextOrFail::Next(0)) => { Instruction::DynamicElse(_, _, o @ NextOrFail::Next(0)) => {
retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(instr_loc, 0)); retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(instr_loc, 0));
*o = NextOrFail::Fail(0); *o = NextOrFail::Fail(0);
} }
@@ -516,7 +516,7 @@ fn internalize_choice_instr_at(
} }
} }
} }
Instruction::DynamicInternalElse(_, _, ref mut o @ NextOrFail::Next(0)) => { Instruction::DynamicInternalElse(_, _, o @ NextOrFail::Next(0)) => {
retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(instr_loc, 0)); retraction_info.push_record(RetractionRecord::ReplacedDynamicElseOffset(instr_loc, 0));
*o = NextOrFail::Fail(0); *o = NextOrFail::Fail(0);
} }
@@ -564,16 +564,14 @@ fn thread_choice_instr_at_to(
) { ) {
loop { loop {
match &mut code[instr_loc] { match &mut code[instr_loc] {
Instruction::TryMeElse(ref mut o) | Instruction::RetryMeElse(ref mut o) Instruction::TryMeElse(o) | Instruction::RetryMeElse(o) if target_loc >= instr_loc => {
if target_loc >= instr_loc =>
{
retraction_info.push_record(RetractionRecord::ReplacedChoiceOffset(instr_loc, *o)); retraction_info.push_record(RetractionRecord::ReplacedChoiceOffset(instr_loc, *o));
*o = target_loc - instr_loc; *o = target_loc - instr_loc;
return; return;
} }
Instruction::DynamicElse(_, _, NextOrFail::Next(ref mut o)) Instruction::DynamicElse(_, _, NextOrFail::Next(o))
| Instruction::DynamicInternalElse(_, _, NextOrFail::Next(ref mut o)) | Instruction::DynamicInternalElse(_, _, NextOrFail::Next(o))
if target_loc >= instr_loc => if target_loc >= instr_loc =>
{ {
retraction_info retraction_info
@@ -588,7 +586,7 @@ fn thread_choice_instr_at_to(
Instruction::TryMeElse(o) | Instruction::RetryMeElse(o) => { Instruction::TryMeElse(o) | Instruction::RetryMeElse(o) => {
instr_loc += *o; instr_loc += *o;
} }
Instruction::RevJmpBy(ref mut o) if instr_loc >= target_loc => { Instruction::RevJmpBy(o) if instr_loc >= target_loc => {
retraction_info.push_record(RetractionRecord::ModifiedRevJmpBy(instr_loc, *o)); retraction_info.push_record(RetractionRecord::ModifiedRevJmpBy(instr_loc, *o));
*o = instr_loc - target_loc; *o = instr_loc - target_loc;
@@ -631,7 +629,7 @@ fn thread_choice_instr_at_to(
Instruction::DynamicInternalElse(_, _, NextOrFail::Fail(o)) if *o > 0 => { Instruction::DynamicInternalElse(_, _, NextOrFail::Fail(o)) if *o > 0 => {
instr_loc += *o; instr_loc += *o;
} }
Instruction::TrustMe(ref mut o) if target_loc >= instr_loc => { Instruction::TrustMe(o) if target_loc >= instr_loc => {
retraction_info.push_record( retraction_info.push_record(
RetractionRecord::AppendedTrustMe(instr_loc, *o, false), RetractionRecord::AppendedTrustMe(instr_loc, *o, false),
//choice_instr.is_default()), //choice_instr.is_default()),
@@ -657,7 +655,7 @@ fn remove_non_leading_clause(
retraction_info: &mut RetractionInfo, retraction_info: &mut RetractionInfo,
) -> Option<IndexPtr> { ) -> Option<IndexPtr> {
match &mut code[non_indexed_choice_instr_loc] { match &mut code[non_indexed_choice_instr_loc] {
Instruction::RetryMeElse(ref mut o) => { Instruction::RetryMeElse(o) => {
let o = *o; let o = *o;
thread_choice_instr_at_to( thread_choice_instr_at_to(
@@ -680,7 +678,7 @@ fn remove_non_leading_clause(
None None
} }
Instruction::TryMeElse(ref mut o) => { Instruction::TryMeElse(o) => {
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse( retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(
preceding_choice_instr_loc, preceding_choice_instr_loc,
*o, *o,
@@ -730,7 +728,7 @@ fn remove_leading_unindexed_clause(
retraction_info: &mut RetractionInfo, retraction_info: &mut RetractionInfo,
) -> Option<IndexPtr> { ) -> Option<IndexPtr> {
match &mut code[non_indexed_choice_instr_loc] { match &mut code[non_indexed_choice_instr_loc] {
Instruction::TryMeElse(ref mut o) => { Instruction::TryMeElse(o) => {
if *o > 0 { if *o > 0 {
retraction_info.push_record(RetractionRecord::ModifiedTryMeElse( retraction_info.push_record(RetractionRecord::ModifiedTryMeElse(
non_indexed_choice_instr_loc, non_indexed_choice_instr_loc,
@@ -935,10 +933,10 @@ fn prepend_compiled_clause(
let prepend_queue_len = prepend_queue.len(); let prepend_queue_len = prepend_queue.len();
match &mut prepend_queue[1] { match &mut prepend_queue[1] {
Instruction::TryMeElse(ref mut o) if *o == 0 => { Instruction::TryMeElse(o) if *o == 0 => {
*o = prepend_queue_len - 2; *o = prepend_queue_len - 2;
} }
Instruction::DynamicInternalElse(_, _, ref mut o @ NextOrFail::Next(0)) => { Instruction::DynamicInternalElse(_, _, o @ NextOrFail::Next(0)) => {
*o = NextOrFail::Fail(prepend_queue_len - 2); *o = NextOrFail::Fail(prepend_queue_len - 2);
} }
_ => { _ => {
@@ -1632,8 +1630,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
}; };
match &mut self.wam_prelude.code[clause_loc] { match &mut self.wam_prelude.code[clause_loc] {
Instruction::DynamicElse(_, ref mut d, _) Instruction::DynamicElse(_, d, _) | Instruction::DynamicInternalElse(_, d, _) => {
| Instruction::DynamicInternalElse(_, ref mut d, _) => {
*d = Death::Finite(LS::machine_st(&mut self.payload).global_clock); *d = Death::Finite(LS::machine_st(&mut self.payload).global_clock);
} }
_ => unreachable!(), _ => unreachable!(),

View File

@@ -1,14 +1,14 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::io::Write; use std::io::Write;
use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender, channel};
use rand::{rngs::StdRng, SeedableRng}; use rand::{SeedableRng, rngs::StdRng};
use crate::Machine; use crate::Machine;
use super::{ use super::{
bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module, Arena, Atom, Arena, Atom, Callback, CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState,
Callback, CompilationTarget, IndexStore, ListingSource, MachineArgs, MachineState, Stream, Stream, bootstrapping_compile, current_dir, import_builtin_impls, libraries, load_module,
}; };
#[derive(Default)] #[derive(Default)]

View File

@@ -7,14 +7,14 @@ use crate::machine::heap::*;
use crate::machine::stack::*; use crate::machine::stack::*;
use crate::types::*; use crate::types::*;
use scryer_modular_bitfield::specifiers::*; use modular_bitfield::specifiers::*;
use scryer_modular_bitfield::*; use modular_bitfield::*;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::mem; use std::mem;
use std::ops::{IndexMut, Range}; use std::ops::{IndexMut, Range};
#[derive(BitfieldSpecifier, Copy, Clone, Debug)] #[derive(Specifier, Copy, Clone, Debug)]
#[bits = 6] #[bits = 6]
enum TrailRefTag { enum TrailRefTag {
HeapCell = 0b001011, HeapCell = 0b001011,
@@ -81,16 +81,16 @@ pub trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
fn threshold(&self) -> usize; fn threshold(&self) -> usize;
// returns the tail location of the pstr on success // returns the tail location of the pstr on success
fn as_slice_from<'a>(&'a self, from: usize) -> Box<dyn Iterator<Item = u8> + 'a>; fn as_slice_from<'a>(&'a self, from: usize) -> Box<dyn Iterator<Item = u8> + 'a>;
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize>; fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError>;
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize>; fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError>;
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize>; fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError>;
} }
pub(crate) fn copy_term<T: CopierTarget>( pub(crate) fn copy_term<T: CopierTarget>(
target: T, target: T,
addr: HeapCellValue, addr: HeapCellValue,
attr_var_policy: AttrVarPolicy, attr_var_policy: AttrVarPolicy,
) -> Result<usize, usize> { ) -> Result<usize, AllocError> {
let mut copy_term_state = CopyTermState::new(target, attr_var_policy); let mut copy_term_state = CopyTermState::new(target, attr_var_policy);
let old_threshold = copy_term_state.target.threshold(); let old_threshold = copy_term_state.target.threshold();
@@ -147,7 +147,7 @@ impl<T: CopierTarget> CopyTermState<T> {
self.trail.push((TrailRef::heap_cell(addr), trail_item)); self.trail.push((TrailRef::heap_cell(addr), trail_item));
} }
fn copy_list(&mut self, addr: usize) -> Result<(), usize> { fn copy_list(&mut self, addr: usize) -> Result<(), AllocError> {
for offset in 0..2 { for offset in 0..2 {
read_heap_cell!(self.target[addr + offset], read_heap_cell!(self.target[addr + offset],
(HeapCellValueTag::Lis, h) => { (HeapCellValueTag::Lis, h) => {
@@ -192,7 +192,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) Ok(())
} }
fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), usize> { fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), AllocError> {
match self.pstr_loc_locs.range_mut(..=pstr_loc).next_back() { match self.pstr_loc_locs.range_mut(..=pstr_loc).next_back() {
Some(( Some((
_prev_pstr_loc, _prev_pstr_loc,
@@ -247,7 +247,8 @@ impl<T: CopierTarget> CopyTermState<T> {
self.trail self.trail
.push((TrailRef::pstr_loc(pstr_loc_idx), old_cell)); .push((TrailRef::pstr_loc(pstr_loc_idx), old_cell));
let old_tail_idx = if (pstr_loc + offset + 1) % Heap::heap_cell_alignment() == 0 { let old_tail_idx =
if (pstr_loc + offset + 1).is_multiple_of(Heap::heap_cell_alignment()) {
cell_index!(pstr_loc + offset) + 2 cell_index!(pstr_loc + offset) + 2
} else { } else {
cell_index!(pstr_loc + offset) + 1 cell_index!(pstr_loc + offset) + 1
@@ -279,7 +280,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) Ok(())
} }
fn copy_attr_var_lists(&mut self) -> Result<(), usize> { fn copy_attr_var_lists(&mut self) -> Result<(), AllocError> {
while !self.attr_var_list_locs.is_empty() { while !self.attr_var_list_locs.is_empty() {
let mut list_loc_vec = std::mem::take(&mut self.attr_var_list_locs); let mut list_loc_vec = std::mem::take(&mut self.attr_var_list_locs);
@@ -298,13 +299,13 @@ impl<T: CopierTarget> CopyTermState<T> {
* structure which is ensured by this function and not at all by * structure which is ensured by this function and not at all by
* the vanilla copier. * the vanilla copier.
*/ */
fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) -> Result<(), usize> { fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) -> Result<(), AllocError> {
while let HeapCellValueTag::Lis = list_addr.get_tag() { while let HeapCellValueTag::Lis = list_addr.get_tag() {
let threshold = self.target.threshold(); let threshold = self.target.threshold();
let heap_loc = list_addr.get_value() as usize; let heap_loc = list_addr.get_value() as usize;
let str_loc = self.target[heap_loc].get_value() as usize; let str_loc = self.target[heap_loc].get_value() as usize;
let str_cell = self.target[str_loc]; let str_cell = self.target[str_loc];
let mut writer = self.target.reserve(3).unwrap(); let mut writer = self.target.reserve(3)?;
writer.write_with(|section| { writer.write_with(|section| {
section.push_cell(heap_loc_as_cell!(threshold + 2)); section.push_cell(heap_loc_as_cell!(threshold + 2));
@@ -328,7 +329,11 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) Ok(())
} }
fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) -> Result<(), usize> { fn reinstantiate_var(
&mut self,
addr: HeapCellValue,
frontier: usize,
) -> Result<(), AllocError> {
read_heap_cell!(addr, read_heap_cell!(addr,
(HeapCellValueTag::Var, h) => { (HeapCellValueTag::Var, h) => {
self.target[frontier] = heap_loc_as_cell!(frontier); self.target[frontier] = heap_loc_as_cell!(frontier);
@@ -379,8 +384,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) Ok(())
} }
fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), usize> { fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), AllocError> {
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);
@@ -393,6 +397,7 @@ impl<T: CopierTarget> CopyTermState<T> {
} }
} }
(HeapCellValueTag::Lis, h) => { (HeapCellValueTag::Lis, h) => {
let index = rd.get_value() as usize;
if h >= self.old_h && self.target[index].get_mark_bit() { if h >= self.old_h && self.target[index].get_mark_bit() {
*self.value_at_scan() = heap_loc_as_cell!( *self.value_at_scan() = heap_loc_as_cell!(
if ra.get_forwarding_bit() { if ra.get_forwarding_bit() {
@@ -419,7 +424,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) Ok(())
} }
fn copy_structure(&mut self, addr: usize) -> Result<(), usize> { fn copy_structure(&mut self, addr: usize) -> Result<(), AllocError> {
read_heap_cell!(self.target[addr], read_heap_cell!(self.target[addr],
(HeapCellValueTag::Atom, (_name, arity)) => { (HeapCellValueTag::Atom, (_name, arity)) => {
let threshold = self.target.threshold(); let threshold = self.target.threshold();
@@ -429,7 +434,7 @@ impl<T: CopierTarget> CopyTermState<T> {
let str_cell = if get_structure_index(index_cell).is_some() { let str_cell = if get_structure_index(index_cell).is_some() {
// copy the index pointer trailing this // copy the index pointer trailing this
// inlined or expanded goal. // inlined or expanded goal.
let mut writer = self.target.reserve(1).unwrap(); let mut writer = self.target.reserve(1)?;
writer.write_with(|section| { writer.write_with(|section| {
section.push_cell(index_cell); section.push_cell(index_cell);
@@ -462,7 +467,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) Ok(())
} }
fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), usize> { fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), AllocError> {
self.scan = self.target.threshold(); self.scan = self.target.threshold();
let mut writer = self.target.reserve(1)?; let mut writer = self.target.reserve(1)?;
@@ -501,7 +506,7 @@ impl<T: CopierTarget> CopyTermState<T> {
Ok(()) Ok(())
} }
fn copy_pstrs(&mut self) -> Result<(), usize> { fn copy_pstrs(&mut self) -> Result<(), AllocError> {
while let Some((least_pstr_loc, pstr_data)) = self.pstr_loc_locs.pop_first() { while let Some((least_pstr_loc, pstr_data)) = self.pstr_loc_locs.pop_first() {
let threshold = heap_index!(self.target.threshold()); let threshold = heap_index!(self.target.threshold());

View File

@@ -6,78 +6,16 @@ use crate::machine::loader::*;
use crate::machine::machine_errors::CompilationError; use crate::machine::machine_errors::CompilationError;
use crate::machine::preprocessor::*; use crate::machine::preprocessor::*;
use crate::parser::ast::*; use crate::parser::ast::*;
use crate::parser::dashu::Rational;
use crate::variable_records::*; use crate::variable_records::*;
use dashu::Integer; use dashu::Integer;
use indexmap::{IndexMap, IndexSet}; use indexmap::{IndexMap, IndexSet};
use std::cell::Cell; use std::cell::Cell;
use std::cmp::Ordering;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::hash::{Hash, Hasher}; use std::hash::Hash;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::Arc;
#[derive(Debug, Clone)] //, PartialOrd, PartialEq, Eq, Hash)]
pub struct BranchNumber {
branch_num: Rational,
delta: Rational,
}
impl Default for BranchNumber {
fn default() -> Self {
Self {
branch_num: Rational::from(1u64 << 63),
delta: Rational::from(1),
}
}
}
impl PartialEq<BranchNumber> for BranchNumber {
#[inline]
fn eq(&self, rhs: &BranchNumber) -> bool {
self.branch_num == rhs.branch_num
}
}
impl Eq for BranchNumber {}
impl Hash for BranchNumber {
#[inline(always)]
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.branch_num.hash(hasher)
}
}
impl PartialOrd<BranchNumber> for BranchNumber {
#[inline]
fn partial_cmp(&self, rhs: &BranchNumber) -> Option<Ordering> {
self.branch_num.partial_cmp(&rhs.branch_num)
}
}
impl BranchNumber {
fn split(&self) -> BranchNumber {
BranchNumber {
branch_num: self.branch_num.clone() + &self.delta / Rational::from(2),
delta: &self.delta / Rational::from(4),
}
}
fn incr_by_delta(&self) -> BranchNumber {
BranchNumber {
branch_num: self.branch_num.clone() + &self.delta,
delta: self.delta.clone(),
}
}
fn halve_delta(&self) -> BranchNumber {
BranchNumber {
branch_num: self.branch_num.clone(),
delta: &self.delta / Rational::from(2),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VarInfo { pub struct VarInfo {
@@ -97,12 +35,12 @@ pub struct ChunkInfo {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct BranchInfo { pub struct BranchInfo {
branch_num: BranchNumber, branch_num: Arc<BranchNumber>,
chunks: Vec<ChunkInfo>, chunks: Vec<ChunkInfo>,
} }
impl BranchInfo { impl BranchInfo {
fn new(branch_num: BranchNumber) -> Self { fn new(branch_num: Arc<BranchNumber>) -> Self {
Self { Self {
branch_num, branch_num,
chunks: vec![], chunks: vec![],
@@ -131,7 +69,7 @@ impl DerefMut for BranchMap {
} }
} }
type RootSet = IndexSet<BranchNumber>; type RootSet = IndexSet<Arc<BranchNumber>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClassifyInfo { pub struct ClassifyInfo {
@@ -140,12 +78,13 @@ pub struct ClassifyInfo {
} }
enum TraversalState { enum TraversalState {
// construct a QueryTerm::Branch with number of disjuncts, reset // pop the latest branch number from the root set and use it to construct a QueryTerm::Branch
// the chunk type to that of the chunk preceding the disjunct and the chunk_num. // with number of disjuncts, reset the chunk type to that of the chunk preceding the disjunct
// and the chunk_num.
BuildDisjunct(usize), BuildDisjunct(usize),
BuildFinalDisjunct(usize),
// add the last disjunct to a QueryTerm::Branch, continuing from // add the last disjunct to a QueryTerm::Branch, continuing from
// where it leaves off. // where it leaves off.
BuildFinalDisjunct(usize),
Fail, Fail,
GetCutPoint { var_num: usize, prev_b: bool }, GetCutPoint { var_num: usize, prev_b: bool },
Cut { var_num: usize, is_global: bool }, Cut { var_num: usize, is_global: bool },
@@ -154,15 +93,14 @@ enum TraversalState {
Term(Term), Term(Term),
OverrideGlobalCutVar(usize), OverrideGlobalCutVar(usize),
ResetGlobalCutVarOverride(Option<usize>), ResetGlobalCutVarOverride(Option<usize>),
RemoveBranchNum, // pop the current_branch_num and from the root set. AddBranchNum(Arc<BranchNumber>), // set current_branch_num, add it to the root set
AddBranchNum(BranchNumber), // set current_branch_num, add it to the root set RepBranchNum(Arc<BranchNumber>), // replace current_branch_num and the latest in the root set
RepBranchNum(BranchNumber), // replace current_branch_num and the latest in the root set
} }
#[derive(Debug)] #[derive(Debug)]
pub struct VariableClassifier { pub struct VariableClassifier {
call_policy: CallPolicy, call_policy: CallPolicy,
current_branch_num: BranchNumber, current_branch_num: Arc<BranchNumber>,
current_chunk_num: usize, current_chunk_num: usize,
current_chunk_type: ChunkType, current_chunk_type: ChunkType,
branch_map: BranchMap, branch_map: BranchMap,
@@ -199,7 +137,7 @@ impl VarData {
VarAlloc::Perm(0, PermVarAllocation::Pending); VarAlloc::Perm(0, PermVarAllocation::Pending);
match build_stack.front_mut() { match build_stack.front_mut() {
Some(ChunkedTerms::Branch(_)) => { Some(ChunkedTerms::Branch { .. }) => {
build_stack.push_front(ChunkedTerms::Chunk { build_stack.push_front(ChunkedTerms::Chunk {
terms: VecDeque::from(vec![term]), terms: VecDeque::from(vec![term]),
}); });
@@ -219,24 +157,33 @@ pub type ClassifyFactResult = (Term, VarData);
pub type ClassifyRuleResult = (Term, ChunkedTermVec, VarData); pub type ClassifyRuleResult = (Term, ChunkedTermVec, VarData);
fn merge_branch_seq(branches: impl Iterator<Item = BranchInfo>) -> BranchInfo { fn merge_branch_seq(branches: impl Iterator<Item = BranchInfo>) -> BranchInfo {
let mut branch_info = BranchInfo::new(BranchNumber::default()); let mut branch_info = BranchInfo::new(Arc::new(BranchNumber::default()));
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.append(&mut branch.chunks); branch_info.chunks.append(&mut branch.chunks);
} }
branch_info.branch_num.delta = branch_info.branch_num.delta * Integer::from(2); let new_delta = branch_info.branch_num.delta.clone() * Integer::from(2);
branch_info.branch_num.branch_num -= &branch_info.branch_num.delta;
branch_info.branch_num = Arc::new(BranchNumber {
branch_num: branch_info.branch_num.branch_num.clone() - &new_delta,
delta: new_delta,
});
branch_info branch_info
} }
fn flatten_into_disjunct(build_stack: &mut ChunkedTermVec, preceding_len: usize) { fn flatten_into_disjunct(
build_stack: &mut ChunkedTermVec,
branch_num: Arc<BranchNumber>,
preceding_len: usize,
) {
let branch_vec = build_stack.drain(preceding_len + 1..).collect(); let branch_vec = build_stack.drain(preceding_len + 1..).collect();
if let ChunkedTerms::Branch(ref mut disjuncts) = &mut build_stack[preceding_len] { if let ChunkedTerms::Branch { branch_nums, arms } = &mut build_stack[preceding_len] {
disjuncts.push(branch_vec); branch_nums.push(branch_num);
arms.push(branch_vec);
} else { } else {
unreachable!(); unreachable!();
} }
@@ -246,7 +193,7 @@ impl VariableClassifier {
pub fn new(call_policy: CallPolicy) -> Self { pub fn new(call_policy: CallPolicy) -> Self {
Self { Self {
call_policy, call_policy,
current_branch_num: BranchNumber::default(), current_branch_num: Arc::new(BranchNumber::default()),
current_chunk_num: 0, current_chunk_num: 0,
current_chunk_type: ChunkType::Head, current_chunk_type: ChunkType::Head,
branch_map: BranchMap(BranchMapInt::new()), branch_map: BranchMap(BranchMapInt::new()),
@@ -477,9 +424,6 @@ impl VariableClassifier {
self.root_set.insert(branch_num.clone()); self.root_set.insert(branch_num.clone());
self.current_branch_num = branch_num; self.current_branch_num = branch_num;
} }
TraversalState::RemoveBranchNum => {
self.root_set.pop();
}
TraversalState::RepBranchNum(branch_num) => { TraversalState::RepBranchNum(branch_num) => {
self.root_set.pop(); self.root_set.pop();
self.root_set.insert(branch_num.clone()); self.root_set.insert(branch_num.clone());
@@ -488,14 +432,10 @@ impl VariableClassifier {
TraversalState::ResetCallPolicy(call_policy) => { TraversalState::ResetCallPolicy(call_policy) => {
self.call_policy = call_policy; self.call_policy = call_policy;
} }
TraversalState::BuildDisjunct(preceding_len) => { TraversalState::BuildDisjunct(preceding_len)
flatten_into_disjunct(&mut build_stack, preceding_len); | TraversalState::BuildFinalDisjunct(preceding_len) => {
let branch_num = self.root_set.pop().unwrap();
self.current_chunk_type = ChunkType::Mid; flatten_into_disjunct(&mut build_stack, branch_num, preceding_len);
self.current_chunk_num += 1;
}
TraversalState::BuildFinalDisjunct(preceding_len) => {
flatten_into_disjunct(&mut build_stack, preceding_len);
self.current_chunk_type = ChunkType::Mid; self.current_chunk_type = ChunkType::Mid;
self.current_chunk_num += 1; self.current_chunk_num += 1;
@@ -607,9 +547,9 @@ impl VariableClassifier {
let tail = terms.pop().unwrap(); let tail = terms.pop().unwrap();
let head = terms.pop().unwrap(); let head = terms.pop().unwrap();
let first_branch_num = self.current_branch_num.split(); let first_branch_num = Arc::new(self.current_branch_num.split());
let branches: Vec<_> = std::iter::once(head) let branches: Vec<_> = std::iter::once(head)
.chain(unfold_by_str(tail, atom!(";")).into_iter()) .chain(unfold_by_str(tail, atom!(";")))
.collect(); .collect();
let mut branch_numbers = vec![first_branch_num]; let mut branch_numbers = vec![first_branch_num];
@@ -618,25 +558,24 @@ impl VariableClassifier {
let succ_branch_number = branch_numbers[idx - 1].incr_by_delta(); let succ_branch_number = branch_numbers[idx - 1].incr_by_delta();
branch_numbers.push(if idx + 1 < branches.len() { branch_numbers.push(if idx + 1 < branches.len() {
succ_branch_number.split() Arc::new(succ_branch_number.split())
} else { } else {
succ_branch_number Arc::new(succ_branch_number)
}); });
} }
let build_stack_len = build_stack.len(); let build_stack_len = build_stack.len();
build_stack.reserve_branch(branches.len()); build_stack.reserve_branch(branches.len());
state_stack.push(TraversalState::RepBranchNum( state_stack.push(TraversalState::RepBranchNum(Arc::new(
self.current_branch_num.halve_delta(), self.current_branch_num.halve_delta(),
)); )));
let iter = branches.into_iter().zip(branch_numbers.into_iter()); let iter = branches.into_iter().zip(branch_numbers);
let final_disjunct_loc = state_stack.len(); let final_disjunct_loc = state_stack.len();
for (term, branch_num) in iter.rev() { for (term, branch_num) in iter.rev() {
state_stack.push(TraversalState::BuildDisjunct(build_stack_len)); state_stack.push(TraversalState::BuildDisjunct(build_stack_len));
state_stack.push(TraversalState::RemoveBranchNum);
state_stack.push(TraversalState::Term(term)); state_stack.push(TraversalState::Term(term));
state_stack.push(TraversalState::AddBranchNum(branch_num)); state_stack.push(TraversalState::AddBranchNum(branch_num));
} }
@@ -655,20 +594,15 @@ impl VariableClassifier {
let then_term = terms.pop().unwrap(); let then_term = terms.pop().unwrap();
let if_term = terms.pop().unwrap(); let if_term = terms.pop().unwrap();
let prev_b = if matches!( let prev_b =
state_stack.last(), if let Some(&TraversalState::BuildDisjunct(preceding_len)) =
Some(TraversalState::RemoveBranchNum) state_stack.last()
) { {
// check if the second-to-last element // check if the second-to-last element
// is a regular BuildDisjunct, as we // is a regular BuildDisjunct, as we
// don't want to add GetPrevLevel in // don't want to add GetPrevLevel in
// case of a TrustMe. // case of a TrustMe.
match state_stack.iter().rev().nth(1) {
Some(&TraversalState::BuildDisjunct(preceding_len)) => {
preceding_len + 1 == build_stack.len() preceding_len + 1 == build_stack.len()
}
_ => false,
}
} else { } else {
false false
}; };
@@ -690,14 +624,21 @@ impl VariableClassifier {
let not_term = terms.pop().unwrap(); let not_term = terms.pop().unwrap();
let build_stack_len = build_stack.len(); let build_stack_len = build_stack.len();
let first_branch_num = Arc::new(self.current_branch_num.split());
let second_branch_num = Arc::new(first_branch_num.incr_by_delta());
build_stack.reserve_branch(2); build_stack.reserve_branch(2);
state_stack.push(TraversalState::RepBranchNum(Arc::new(
self.current_branch_num.halve_delta(),
)));
state_stack.push(TraversalState::BuildFinalDisjunct(build_stack_len)); state_stack.push(TraversalState::BuildFinalDisjunct(build_stack_len));
state_stack.push(TraversalState::Term(Term::Clause( state_stack.push(TraversalState::Term(Term::Clause(
Cell::default(), Cell::default(),
atom!("$succeed"), atom!("$succeed"),
vec![], vec![],
))); )));
state_stack.push(TraversalState::AddBranchNum(second_branch_num));
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));
@@ -710,6 +651,7 @@ impl VariableClassifier {
var_num: self.var_num, var_num: self.var_num,
prev_b: false, prev_b: false,
}); });
state_stack.push(TraversalState::AddBranchNum(first_branch_num));
self.current_chunk_type = ChunkType::Mid; self.current_chunk_type = ChunkType::Mid;
self.current_chunk_num += 1; self.current_chunk_num += 1;

File diff suppressed because it is too large Load Diff

View File

@@ -690,8 +690,7 @@ mod tests {
// term is: [a, <stream ptr>] // term is: [a, <stream ptr>]
let stream = Stream::from_static_string("test", &mut wam.machine_st.arena); let stream = Stream::from_static_string("test", &mut wam.machine_st.arena);
let stream_cell = let stream_cell = HeapCellValue::from_arena_header_ptr(stream.as_ptr());
HeapCellValue::from(ConsPtr::build_with(stream.as_ptr(), ConsPtrMaskTag::Cons));
let mut writer = wam.machine_st.heap.reserve(16).unwrap(); let mut writer = wam.machine_st.heap.reserve(16).unwrap();

View File

@@ -5,16 +5,25 @@ use crate::types::*;
use std::alloc; use std::alloc;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::num::NonZero;
use std::ops::{Bound, Index, IndexMut, Range, RangeBounds}; use std::ops::{Bound, Index, IndexMut, Range, RangeBounds};
use std::ptr; use std::ptr;
use std::sync::Once;
const ALIGN: usize = Heap::heap_cell_alignment(); const ALIGN: usize = Heap::heap_cell_alignment();
#[derive(Debug, Clone)]
pub struct AllocError;
impl AllocError {
pub(crate) fn resource_error_offset(&self, heap: &mut Heap) -> usize {
heap.resource_error_offset()
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Heap { pub struct Heap {
inner: InnerHeap, inner: InnerHeap,
resource_err_loc: usize, resource_err_loc: Option<NonZero<usize>>,
} }
impl Drop for Heap { impl Drop for Heap {
@@ -63,12 +72,15 @@ impl InnerHeap {
"Allocation too large. We should probably GC (TODO)" "Allocation too large. We should probably GC (TODO)"
); );
let new_ptr = if self.byte_cap == 0 { let new_ptr = unsafe {
if self.byte_cap == 0 {
alloc::alloc(new_layout) alloc::alloc(new_layout)
} else { } else {
let old_layout = let old_layout =
alloc::Layout::from_size_align(self.byte_cap, size_of::<HeapCellValue>()).unwrap(); alloc::Layout::from_size_align(self.byte_cap, size_of::<HeapCellValue>())
.unwrap();
alloc::realloc(self.ptr, old_layout, new_layout.size()) alloc::realloc(self.ptr, old_layout, new_layout.size())
}
}; };
if !new_ptr.is_null() { if !new_ptr.is_null() {
@@ -85,8 +97,6 @@ impl InnerHeap {
unsafe impl Send for Heap {} unsafe impl Send for Heap {}
unsafe impl Sync for Heap {} unsafe impl Sync for Heap {}
static RESOURCE_ERROR_OFFSET_INIT: Once = Once::new();
#[derive(Debug)] #[derive(Debug)]
pub struct HeapStringScan<'a> { pub struct HeapStringScan<'a> {
pub string: &'a str, pub string: &'a str,
@@ -99,7 +109,8 @@ unsafe fn scan_slice_to_str(heap_slice: &[u8]) -> HeapStringScan<'_> {
.iter() .iter()
.position(|b| *b == 0u8) .position(|b| *b == 0u8)
.unwrap_or(heap_slice.len()); .unwrap_or(heap_slice.len());
let zero_byte_addr = heap_slice.as_ptr().add(string_len);
let zero_byte_addr = unsafe { heap_slice.as_ptr().add(string_len) };
let sentinel_len = pstr_sentinel_length(zero_byte_addr.addr()); let sentinel_len = pstr_sentinel_length(zero_byte_addr.addr());
let tail_idx = cell_index!( let tail_idx = cell_index!(
@@ -110,7 +121,7 @@ unsafe fn scan_slice_to_str(heap_slice: &[u8]) -> HeapStringScan<'_> {
let str_slice = &heap_slice[..string_len]; let str_slice = &heap_slice[..string_len];
HeapStringScan { HeapStringScan {
string: std::str::from_utf8_unchecked(str_slice), string: unsafe { std::str::from_utf8_unchecked(str_slice) },
tail_idx, tail_idx,
} }
} }
@@ -132,7 +143,7 @@ unsafe fn scan_slice_to_str_from_start(heap_slice: &[u8]) -> HeapStringScan<'_>
let str_slice = &heap_slice[..string_len]; let str_slice = &heap_slice[..string_len];
HeapStringScan { HeapStringScan {
string: std::str::from_utf8_unchecked(str_slice), string: unsafe { std::str::from_utf8_unchecked(str_slice) },
tail_idx, tail_idx,
} }
} }
@@ -268,19 +279,22 @@ impl ReservedHeapSection {
src.as_ptr(), src.as_ptr(),
self.heap_ptr.add(heap_index!(self.heap_cell_len)), self.heap_ptr.add(heap_index!(self.heap_cell_len)),
str_byte_len, str_byte_len,
); )
};
let zero_region_idx = heap_index!(self.heap_cell_len) + str_byte_len; let zero_region_idx = heap_index!(self.heap_cell_len) + str_byte_len;
let align_offset = pstr_sentinel_length(zero_region_idx); let align_offset = pstr_sentinel_length(zero_region_idx);
ptr::write_bytes(self.heap_ptr.add(zero_region_idx), 0u8, align_offset); unsafe { ptr::write_bytes(self.heap_ptr.add(zero_region_idx), 0u8, align_offset) };
cells_written = if align_offset == 1 { cells_written = if align_offset == 1 {
unsafe {
ptr::write_bytes( ptr::write_bytes(
self.heap_ptr.add(zero_region_idx + 1), self.heap_ptr.add(zero_region_idx + 1),
0u8, 0u8,
size_of::<HeapCellValue>(), size_of::<HeapCellValue>(),
); )
};
// ensure there are at least two bytes in the boundary // ensure there are at least two bytes in the boundary
// buffer separating the string data from the tail // buffer separating the string data from the tail
@@ -291,7 +305,6 @@ impl ReservedHeapSection {
}; };
self.heap_cell_len += cells_written; self.heap_cell_len += cells_written;
}
cells_written cells_written
} }
@@ -438,16 +451,12 @@ impl Index<usize> for ReservedHeapSection {
/// Computes the number of bytes required to pad a string of length `chunk_len` /// Computes the number of bytes required to pad a string of length `chunk_len`
/// with zeroes, such that `chunk_len + pstr_sentinel_length(chunk_len)` is a /// with zeroes, such that `chunk_len + pstr_sentinel_length(chunk_len)` is a
/// multiple of `Heap::heap_cell_alignement()`. /// multiple of `Heap::heap_cell_alignment()`.
fn pstr_sentinel_length(chunk_len: usize) -> usize { fn pstr_sentinel_length(chunk_len: usize) -> usize {
let res = chunk_len.next_multiple_of(ALIGN) - chunk_len; let res = chunk_len.next_multiple_of(ALIGN) - chunk_len;
// No bytes available in last chunk // No bytes available in last chunk
if res == 0 { if res == 0 { ALIGN } else { res }
ALIGN
} else {
res
}
} }
#[must_use] #[must_use]
@@ -563,14 +572,14 @@ impl Heap {
byte_len: 0, byte_len: 0,
byte_cap: 0, byte_cap: 0,
}, },
resource_err_loc: 0, resource_err_loc: None,
} }
} }
// takes a heap index, returns a cell index // takes a heap index, returns a cell index
#[inline] #[inline]
pub const fn pstr_tail_idx(pstr_zero_byte_loc: usize) -> usize { pub const fn pstr_tail_idx(pstr_zero_byte_loc: usize) -> usize {
if (pstr_zero_byte_loc + 1) % Heap::heap_cell_alignment() == 0 { if (pstr_zero_byte_loc + 1).is_multiple_of(Heap::heap_cell_alignment()) {
cell_index!(pstr_zero_byte_loc) + 2 cell_index!(pstr_zero_byte_loc) + 2
} else { } else {
cell_index!(pstr_zero_byte_loc) + 1 cell_index!(pstr_zero_byte_loc) + 1
@@ -579,18 +588,20 @@ impl Heap {
#[inline(always)] #[inline(always)]
unsafe fn grow(&mut self) -> bool { unsafe fn grow(&mut self) -> bool {
self.inner.grow() unsafe { self.inner.grow() }
} }
#[inline] #[inline]
fn resource_error_offset(&self) -> usize { fn resource_error_offset(&self) -> usize {
self.resource_err_loc self.resource_err_loc
.expect("`error(resource_error(memory), [])` should be stored at the start of the heap")
.get()
} }
pub(crate) fn with_cell_capacity(cap: usize) -> Result<Self, usize> { pub(crate) fn with_cell_capacity(cap: usize) -> Result<Self, AllocError> {
let ptr = unsafe { let ptr = unsafe {
let layout = alloc::Layout::from_size_align( let layout = alloc::Layout::from_size_align(
cap * size_of::<HeapCellValue>(), heap_index_checked!(cap).ok_or(AllocError)?,
size_of::<HeapCellValue>(), size_of::<HeapCellValue>(),
) )
.unwrap(); .unwrap();
@@ -598,7 +609,7 @@ impl Heap {
}; };
if ptr.is_null() { if ptr.is_null() {
panic!("could not allocate {} bytes for heap!", heap_index!(cap)) Err(AllocError)
} else { } else {
Ok(Self { Ok(Self {
inner: InnerHeap { inner: InnerHeap {
@@ -607,26 +618,24 @@ impl Heap {
byte_cap: heap_index!(cap), byte_cap: heap_index!(cap),
}, },
// pstr_vec: bitvec![], // pstr_vec: bitvec![],
resource_err_loc: 0, resource_err_loc: None,
}) })
} }
} }
pub fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> { pub fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
let section; let section;
let len = heap_index!(num_cells); let len = heap_index_checked!(num_cells).ok_or(AllocError)?;
loop { loop {
unsafe {
if self.free_space() >= len { if self.free_space() >= len {
section = ReservedHeapSection { section = ReservedHeapSection {
heap_ptr: self.inner.ptr, heap_ptr: self.inner.ptr,
heap_cell_len: self.cell_len(), heap_cell_len: self.cell_len(),
}; };
break; break;
} else if !self.grow() { } else if unsafe { !self.grow() } {
return Err(self.resource_error_offset()); return Err(AllocError);
}
} }
} }
@@ -649,7 +658,7 @@ impl Heap {
} }
} }
pub(crate) fn append(&mut self, other_heap: &impl SizedHeap) -> Result<(), usize> { pub(crate) fn append(&mut self, other_heap: &impl SizedHeap) -> Result<(), AllocError> {
let other_len = heap_index!(other_heap.cell_len()); let other_len = heap_index!(other_heap.cell_len());
loop { loop {
@@ -665,7 +674,7 @@ impl Heap {
self.inner.byte_len += heap_index!(other_heap.cell_len()); self.inner.byte_len += heap_index!(other_heap.cell_len());
break; break;
} else if unsafe { !self.grow() } { } else if unsafe { !self.grow() } {
return Err(self.resource_error_offset()); return Err(AllocError);
} }
} }
@@ -678,26 +687,26 @@ impl Heap {
} }
pub(crate) fn clear(&mut self) { pub(crate) fn clear(&mut self) {
unsafe { *self = Heap::new();
let layout =
alloc::Layout::from_size_align(self.inner.byte_cap, size_of::<HeapCellValue>())
.unwrap();
alloc::dealloc(self.inner.ptr, layout);
}
self.inner.ptr = ptr::null_mut();
self.inner.byte_len = 0;
self.inner.byte_cap = 0;
} }
pub(crate) fn store_resource_error(&mut self) { pub(crate) fn store_resource_error(&mut self) {
RESOURCE_ERROR_OFFSET_INIT.call_once(move || { if self.resource_err_loc.is_none() {
let stub = functor!(atom!("resource_error"), [atom_as_cell((atom!("memory")))]); let stub = functor!(
self.resource_err_loc = cell_index!(self.inner.byte_len); atom!("error"),
[
functor((atom!("resource_error")), [atom_as_cell((atom!("memory")))]),
atom_as_cell((atom!("[]")))
]
);
self.resource_err_loc = Some(NonZero::new(cell_index!(self.inner.byte_len)).expect(
"index 0 should already be taken by an interstitial cell reserved by the runtime",
));
let mut writer = Heap::functor_writer(stub); let mut writer = Heap::functor_writer(stub);
writer(self).unwrap(); writer(self).unwrap();
}); }
} }
#[inline] #[inline]
@@ -742,10 +751,10 @@ impl Heap {
// either succeed & return nothing or fail & return an offset into // either succeed & return nothing or fail & return an offset into
// the heap to a pre-allocated resource error // the heap to a pre-allocated resource error
pub(crate) fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), usize> { pub(crate) fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), AllocError> {
unsafe { unsafe {
if self.inner.byte_len == self.inner.byte_cap && !self.grow() { if self.inner.byte_len == self.inner.byte_cap && !self.grow() {
return Err(self.resource_error_offset()); return Err(AllocError);
} }
// SAFETY: // SAFETY:
@@ -779,7 +788,7 @@ impl Heap {
Range { start, end } Range { start, end }
} }
pub fn allocate_pstr(&mut self, src: &str) -> Result<HeapCellValue, usize> { pub fn allocate_pstr(&mut self, src: &str) -> Result<HeapCellValue, AllocError> {
let size_in_heap = Self::compute_pstr_size(src); let size_in_heap = Self::compute_pstr_size(src);
let mut writer = self.reserve(size_in_heap)?; let mut writer = self.reserve(size_in_heap)?;
let HeapSectionWriteResult { result, .. } = let HeapSectionWriteResult { result, .. } =
@@ -794,7 +803,7 @@ impl Heap {
// note that allocate_cstr emits a tail cell to the string (completing it with the empty list) // note that allocate_cstr emits a tail cell to the string (completing it with the empty list)
// unlike any version of allocate_pstr. // unlike any version of allocate_pstr.
pub fn allocate_cstr(&mut self, src: &str) -> Result<HeapCellValue, usize> { pub fn allocate_cstr(&mut self, src: &str) -> Result<HeapCellValue, AllocError> {
let size_in_heap = Self::compute_pstr_size(src); let size_in_heap = Self::compute_pstr_size(src);
let mut writer = self.reserve(size_in_heap + 1)?; let mut writer = self.reserve(size_in_heap + 1)?;
let HeapSectionWriteResult { result, .. } = let HeapSectionWriteResult { result, .. } =
@@ -849,15 +858,15 @@ impl Heap {
// copies only the string, not its tail. returns the cell index of // copies only the string, not its tail. returns the cell index of
// the tail location // the tail location
pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result<usize, usize> { pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
let HeapStringScan { string, tail_idx } = self.scan_slice_to_str(pstr_loc); let HeapStringScan { string, tail_idx } = self.scan_slice_to_str(pstr_loc);
let s_len = string.len(); let s_len = string.len();
let align_offset = pstr_sentinel_length(s_len); let align_offset = pstr_sentinel_length(s_len);
let copy_size = s_len + align_offset; let copy_size = s_len + align_offset;
unsafe {
loop { loop {
unsafe {
if self.free_space() >= copy_size { if self.free_space() >= copy_size {
let slice = let slice =
std::slice::from_raw_parts_mut(self.inner.ptr, self.inner.byte_len + s_len); std::slice::from_raw_parts_mut(self.inner.ptr, self.inner.byte_len + s_len);
@@ -884,7 +893,7 @@ impl Heap {
break; break;
} else if !self.grow() { } else if !self.grow() {
return Err(self.resource_error_offset()); return Err(AllocError);
} }
} }
} }
@@ -893,7 +902,10 @@ impl Heap {
} }
// src is a cell-indexed range. // src is a cell-indexed range.
pub(crate) fn copy_slice_to_end<R: RangeBounds<usize>>(&mut self, src: R) -> Result<(), usize> { pub(crate) fn copy_slice_to_end<R: RangeBounds<usize>>(
&mut self,
src: R,
) -> Result<(), AllocError> {
let range = self.slice_range(src); let range = self.slice_range(src);
let len = range.end - range.start; let len = range.end - range.start;
@@ -911,7 +923,7 @@ impl Heap {
break; break;
} else if !self.grow() { } else if !self.grow() {
return Err(self.resource_error_offset()); return Err(AllocError);
} }
} }
} }
@@ -970,7 +982,7 @@ impl Heap {
pub(crate) fn functor_writer( pub(crate) fn functor_writer(
functor: Vec<FunctorElement>, functor: Vec<FunctorElement>,
) -> impl FnMut(&mut Heap) -> Result<HeapCellValue, usize> { ) -> impl FnMut(&mut Heap) -> Result<HeapCellValue, AllocError> {
let size = Heap::compute_functor_byte_size(&functor); let size = Heap::compute_functor_byte_size(&functor);
let mut functor_writer = ReservedHeapSection::functor_writer(functor); let mut functor_writer = ReservedHeapSection::functor_writer(functor);
@@ -1133,10 +1145,12 @@ pub fn sized_iter_to_heap_list<SrcT: Into<HeapCellValue>>(
heap: &mut Heap, heap: &mut Heap,
size: usize, size: usize,
values: impl Iterator<Item = SrcT>, values: impl Iterator<Item = SrcT>,
) -> Result<HeapCellValue, usize> { ) -> Result<HeapCellValue, AllocError> {
if size > 0 { if size > 0 {
let h = heap.cell_len(); let h = heap.cell_len();
let mut writer = heap.reserve(1 + 2 * size)?; // not using checked_add for 1 + as the result of multiplying by 2 will be even and the largest representable usize is odd,
// so the addition cannot overflow
let mut writer = heap.reserve(1 + size.checked_mul(2).ok_or(AllocError)?)?;
writer.write_with(|section| { writer.write_with(|section| {
for (idx, value) in values.enumerate() { for (idx, value) in values.enumerate() {

View File

@@ -3,22 +3,23 @@ use std::collections::BTreeMap;
use std::rc::Rc; use std::rc::Rc;
use crate::atom_table; use crate::atom_table;
use crate::heap_iter::{stackful_post_order_iter, NonListElider}; use crate::heap_iter::{NonListElider, stackful_post_order_iter};
use crate::machine::heap::AllocError;
use crate::machine::machine_indices::VarKey; use crate::machine::machine_indices::VarKey;
use crate::machine::mock_wam::CompositeOpDir; use crate::machine::mock_wam::CompositeOpDir;
use crate::machine::{ use crate::machine::{
ArenaHeaderTag, Fixnum, Number, BREAK_FROM_DISPATCH_LOOP_LOC, LIB_QUERY_SUCCESS, ArenaHeaderTag, BREAK_FROM_DISPATCH_LOOP_LOC, Fixnum, LIB_QUERY_SUCCESS, Number,
}; };
use crate::offset_table::*; use crate::offset_table::*;
use crate::parser::ast::{Var, VarPtr}; 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, TermWriteResult}; use crate::read::{TermWriteResult, write_term_to_heap};
use crate::types::UntypedArenaPtr; use crate::types::UntypedArenaPtr;
use dashu::{Integer, Rational}; use dashu::{Integer, Rational};
use indexmap::IndexMap; use indexmap::IndexMap;
use super::{streams::Stream, Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine}; use super::{Atom, AtomCell, HeapCellValue, HeapCellValueTag, Machine, streams::Stream};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@@ -128,9 +129,11 @@ impl Term {
pub fn try_conjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> { pub fn try_conjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> {
let mut iter = value.into_iter(); let mut iter = value.into_iter();
iter.next().map(|first| { iter.next().map(|first| {
Term::try_conjunction(iter) if let Some(rest) = Term::try_conjunction(iter) {
.map(|rest| Term::compound(",", [first.clone(), rest])) Term::compound(",", [first, rest])
.unwrap_or(first) } else {
first
}
}) })
} }
@@ -143,9 +146,11 @@ impl Term {
pub fn try_disjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> { pub fn try_disjunction(value: impl IntoIterator<Item = Term>) -> Option<Self> {
let mut iter = value.into_iter(); let mut iter = value.into_iter();
iter.next().map(|first| { iter.next().map(|first| {
Term::try_disjunction(iter) if let Some(rest) = Term::try_disjunction(iter) {
.map(|rest| Term::compound(";", [first.clone(), rest])) Term::compound(";", [first, rest])
.unwrap_or(first) } else {
first
}
}) })
} }
} }
@@ -155,9 +160,14 @@ impl Term {
fn count_to_letter_code(mut count: usize) -> String { fn count_to_letter_code(mut count: usize) -> String {
let mut letters = Vec::new(); let mut letters = Vec::new();
// +2 rather than +1 to account for the _ at the end
let length = count.checked_ilog(26).unwrap_or(0) as usize + 2;
letters.reserve(length);
loop { loop {
let letter_idx = (count % 26) as u32; let letter_idx = (count % 26) as u8;
letters.push(char::from_u32('A' as u32 + letter_idx).unwrap()); letters.push(b'A' + letter_idx);
count /= 26; count /= 26;
if count == 0 { if count == 0 {
@@ -165,7 +175,15 @@ fn count_to_letter_code(mut count: usize) -> String {
} }
} }
letters.into_iter().chain("_".chars()).rev().collect() letters.push(b'_');
debug_assert_eq!(length, letters.len());
letters.reverse();
// Safety: we only push ascii chars A-Z and _
// an ascii only byte sequence is always valid utf-8
unsafe { String::from_utf8_unchecked(letters) }
} }
impl Term { impl Term {
@@ -207,14 +225,14 @@ impl Term {
let list = match tail { let list = match tail {
Term::Atom(atom) if atom == "[]" => match head { Term::Atom(atom) if atom == "[]" => match head {
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => { Term::Atom(ref a) if a.chars().count() == 1 => {
// Handle lists of char as strings // Handle lists of char as strings
Term::String(a.to_string()) Term::String(a.to_string())
} }
_ => Term::List(vec![head]), _ => Term::List(vec![head]),
}, },
Term::List(elems) if elems.is_empty() => match head { Term::List(elems) if elems.is_empty() => match head {
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => { Term::Atom(ref a) if a.chars().count() == 1 => {
// Handle lists of char as strings // Handle lists of char as strings
Term::String(a.to_string()) Term::String(a.to_string())
}, },
@@ -225,7 +243,7 @@ impl Term {
Term::List(elems) Term::List(elems)
}, },
Term::String(mut elems) => match head { Term::String(mut elems) => match head {
Term::Atom(ref a) if a.chars().collect::<Vec<_>>().len() == 1 => { Term::Atom(ref a) if a.chars().count() == 1 => {
// Handle lists of char as strings // Handle lists of char as strings
elems.insert(0, a.chars().next().unwrap()); elems.insert(0, a.chars().next().unwrap());
Term::String(elems) Term::String(elems)
@@ -397,7 +415,7 @@ pub struct QueryState<'a> {
impl Drop for QueryState<'_> { impl Drop for QueryState<'_> {
fn drop(&mut self) { fn drop(&mut self) {
// FIXME: This may be wrong if the iterator is not fully consumend, but from testing it // FIXME: This may be wrong if the iterator is not fully consumed, but from testing it
// seems fine. Is this really ok? // seems fine. Is this really ok?
self.machine.trust_me(); self.machine.trust_me();
} }
@@ -428,14 +446,15 @@ impl Iterator for QueryState<'_> {
// contained in self.machine_st.ball. // contained in self.machine_st.ball.
let h = machine.machine_st.heap.cell_len(); let h = machine.machine_st.heap.cell_len();
if let Err(resource_err_loc) = machine if let Err(err) = machine
.machine_st .machine_st
.heap .heap
.append(&machine.machine_st.ball.stub) .append(&machine.machine_st.ball.stub)
{ {
let resource_error_offset = err.resource_error_offset(&mut machine.machine_st.heap);
return Some(Err(Term::from_heapcell( return Some(Err(Term::from_heapcell(
machine, machine,
machine.machine_st.heap[resource_err_loc], machine.machine_st.heap[resource_error_offset],
&mut IndexMap::new(), &mut IndexMap::new(),
))); )));
} }
@@ -536,11 +555,11 @@ impl Machine {
self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2)); self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2));
} }
pub(crate) fn allocate_stub_choice_point(&mut self) { pub(crate) fn allocate_stub_choice_point(&mut self) -> Result<(), AllocError> {
// 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. // 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(stub_b); 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;
@@ -558,6 +577,8 @@ impl Machine {
self.machine_st.b = stub_b; self.machine_st.b = stub_b;
self.machine_st.hb = self.machine_st.heap.cell_len(); self.machine_st.hb = self.machine_st.heap.cell_len();
self.machine_st.block = stub_b; self.machine_st.block = stub_b;
Ok(())
} }
/// Runs a query. /// Runs a query.
@@ -571,7 +592,8 @@ impl Machine {
.read_term(&op_dir, Tokens::Default) .read_term(&op_dir, Tokens::Default)
.expect("Failed to parse query"); .expect("Failed to parse query");
self.allocate_stub_choice_point(); self.allocate_stub_choice_point()
.expect("failed to 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) let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap)
@@ -619,3 +641,11 @@ impl Machine {
} }
} }
} }
#[test]
fn test_count_to_letter_code() {
for idx in 0..1000 {
// ensure the debug assert doesn't trigger
count_to_letter_code(idx);
}
}

View File

@@ -3,7 +3,7 @@ use crate::MachineBuilder;
#[test] #[test]
#[cfg_attr(miri, ignore = "it takes too long to run")] #[cfg_attr(miri, ignore = "it takes too long to run")]
fn programatic_query() { fn programmatic_query() {
let mut machine = MachineBuilder::default().build(); let mut machine = MachineBuilder::default().build();
machine.load_module_string( machine.load_module_string(

View File

@@ -34,7 +34,7 @@ pub(super) fn set_code_index<'a, LS: LoadState<'a>>(
RetractionRecord::ReplacedUserPredicate(key, replaced) RetractionRecord::ReplacedUserPredicate(key, replaced)
} }
} }
CompilationTarget::Module(ref module_name) => { CompilationTarget::Module(module_name) => {
if IndexPtrTag::Undefined == code_idx_ptr.tag() { if IndexPtrTag::Undefined == code_idx_ptr.tag() {
*code_idx_ptr = code_ptr; *code_idx_ptr = code_ptr;
RetractionRecord::AddedModulePredicate(*module_name, key) RetractionRecord::AddedModulePredicate(*module_name, key)
@@ -98,7 +98,7 @@ pub(super) fn add_op_decl(
CompilationTarget::User => { CompilationTarget::User => {
retraction_info.push_record(RetractionRecord::ReplacedUserOp(*op_decl, op_desc)); retraction_info.push_record(RetractionRecord::ReplacedUserOp(*op_decl, op_desc));
} }
CompilationTarget::Module(ref module_name) => { CompilationTarget::Module(module_name) => {
retraction_info.push_record(RetractionRecord::ReplacedModuleOp( retraction_info.push_record(RetractionRecord::ReplacedModuleOp(
*module_name, *module_name,
*op_decl, *op_decl,
@@ -110,7 +110,7 @@ pub(super) fn add_op_decl(
CompilationTarget::User => { CompilationTarget::User => {
retraction_info.push_record(RetractionRecord::AddedUserOp(*op_decl)); retraction_info.push_record(RetractionRecord::AddedUserOp(*op_decl));
} }
CompilationTarget::Module(ref module_name) => { CompilationTarget::Module(module_name) => {
retraction_info retraction_info
.push_record(RetractionRecord::AddedModuleOp(*module_name, *op_decl)); .push_record(RetractionRecord::AddedModuleOp(*module_name, *op_decl));
} }
@@ -167,7 +167,7 @@ pub(super) fn import_module_exports<'a, LS: LoadState<'a>>(
)); ));
} }
} }
ModuleExport::OpDecl(ref op_decl) => { ModuleExport::OpDecl(op_decl) => {
add_op_decl( add_op_decl(
&mut payload.retraction_info, &mut payload.retraction_info,
compilation_target, compilation_target,
@@ -221,7 +221,7 @@ fn import_module_exports_into_module<'a, LS: LoadState<'a>>(
)); ));
} }
} }
ModuleExport::OpDecl(ref op_decl) => { ModuleExport::OpDecl(op_decl) => {
add_op_decl_as_module_export::<LS>(payload, op_dir, wam_op_dir, op_decl); add_op_decl_as_module_export::<LS>(payload, op_dir, wam_op_dir, op_decl);
} }
} }
@@ -276,7 +276,7 @@ fn import_qualified_module_exports<'a, LS: LoadState<'a>>(
)); ));
} }
} }
ModuleExport::OpDecl(ref op_decl) => { ModuleExport::OpDecl(op_decl) => {
add_op_decl( add_op_decl(
&mut payload.retraction_info, &mut payload.retraction_info,
compilation_target, compilation_target,
@@ -336,7 +336,7 @@ fn import_qualified_module_exports_into_module<'a, LS: LoadState<'a>>(
)); ));
} }
} }
ModuleExport::OpDecl(ref op_decl) => { ModuleExport::OpDecl(op_decl) => {
add_op_decl_as_module_export::<LS>(payload, op_dir, wam_op_dir, op_decl); add_op_decl_as_module_export::<LS>(payload, op_dir, wam_op_dir, op_decl);
} }
} }
@@ -545,7 +545,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
) { ) {
for export in removed_module.module_decl.exports.iter() { for export in removed_module.module_decl.exports.iter() {
match export { match export {
ModuleExport::PredicateKey(ref key) => { ModuleExport::PredicateKey(key) => {
if let (Some(module_code_idx), Some(target_code_idx)) = ( if let (Some(module_code_idx), Some(target_code_idx)) = (
removed_module.code_dir.get(key).cloned(), removed_module.code_dir.get(key).cloned(),
code_dir.get_mut(key).cloned(), code_dir.get_mut(key).cloned(),
@@ -1050,7 +1050,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
); );
for export in &module.module_decl.exports { for export in &module.module_decl.exports {
if let ModuleExport::OpDecl(ref op_decl) = export { if let ModuleExport::OpDecl(op_decl) = export {
add_op_decl_as_module_export::<LS>( add_op_decl_as_module_export::<LS>(
&mut self.payload, &mut self.payload,
&mut module.op_dir, &mut module.op_dir,
@@ -1084,7 +1084,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
&mut self.wam_prelude.indices.meta_predicates, &mut self.wam_prelude.indices.meta_predicates,
)?; )?;
} }
CompilationTarget::Module(ref defining_module_name) => { CompilationTarget::Module(defining_module_name) => {
match self match self
.wam_prelude .wam_prelude
.indices .indices
@@ -1137,7 +1137,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
&exports, &exports,
&mut self.wam_prelude, &mut self.wam_prelude,
), ),
CompilationTarget::Module(ref defining_module_name) => { CompilationTarget::Module(defining_module_name) => {
match self match self
.wam_prelude .wam_prelude
.indices .indices

View File

@@ -160,7 +160,7 @@ impl fmt::Display for CompilationTarget {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
CompilationTarget::User => write!(f, "user"), CompilationTarget::User => write!(f, "user"),
CompilationTarget::Module(ref module_name) => write!(f, "{}", module_name.as_str()), CompilationTarget::Module(module_name) => write!(f, "{}", module_name.as_str()),
} }
} }
} }
@@ -819,11 +819,8 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
if let Instruction::IndexingCode(ref mut indexing_code) = if let Instruction::IndexingCode(ref mut indexing_code) =
self.wam_prelude.code[index_loc] self.wam_prelude.code[index_loc]
{ {
if let IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm( if let IndexingLine::Indexing(IndexingInstruction::SwitchOnTerm(_, v, ..)) =
_, &mut indexing_code[0]
ref mut v,
..,
)) = &mut indexing_code[0]
{ {
*v = old_v; *v = old_v;
} }
@@ -1110,7 +1107,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
} }
} }
} }
CompilationTarget::Module(ref module_name) => { CompilationTarget::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) => match module.extensible_predicates.get_mut(&key) { Some(ref mut module) => match module.extensible_predicates.get_mut(&key) {
Some(ref mut skeleton) => { Some(ref mut skeleton) => {
@@ -1364,14 +1361,12 @@ impl<'a> MachinePreludeView<'a> {
) -> CompositeOpDir<'_, '_> { ) -> CompositeOpDir<'_, '_> {
match compilation_target { match compilation_target {
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(module_name) => match self.indices.modules.get(module_name) {
match self.indices.modules.get(module_name) {
Some(module) => CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir)), Some(module) => CompositeOpDir::new(&self.indices.op_dir, Some(&module.op_dir)),
None => { None => {
unreachable!() unreachable!()
} }
} },
}
} }
} }
} }
@@ -1504,9 +1499,10 @@ impl Machine {
} }
pub(crate) fn load_compiled_library(&mut self) -> CallResult { pub(crate) fn load_compiled_library(&mut self) -> CallResult {
let library = cell_as_atom!(self let library = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
if let Some(module) = self.indices.modules.get(&library) { if let Some(module) = self.indices.modules.get(&library) {
if let ListingSource::DynamicallyGenerated = module.listing_src { if let ListingSource::DynamicallyGenerated = module.listing_src {
@@ -1537,9 +1533,10 @@ impl Machine {
} }
pub(crate) fn declare_module(&mut self) -> CallResult { pub(crate) fn declare_module(&mut self) -> CallResult {
let module_name = cell_as_atom!(self let module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
let mut loader = self.loader_from_heap_evacuable(temp_v!(3)); let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
@@ -1649,9 +1646,10 @@ impl Machine {
} }
pub(crate) fn add_goal_expansion_clause(&mut self) -> CallResult { pub(crate) fn add_goal_expansion_clause(&mut self) -> CallResult {
let target_module_name = cell_as_atom!(self let target_module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
let mut loader = self.loader_from_heap_evacuable(temp_v!(3)); let mut loader = self.loader_from_heap_evacuable(temp_v!(3));
@@ -1744,9 +1742,10 @@ impl Machine {
&mut self, &mut self,
r: RegType, r: RegType,
) -> Loader<'_, LiveLoadAndMachineState<'_>> { ) -> Loader<'_, LiveLoadAndMachineState<'_>> {
let mut load_state = cell_as_load_state_payload!(self let mut load_state = cell_as_load_state_payload!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st[r]))); .store(self.machine_st.deref(self.machine_st[r]))
);
load_state.set_tag(ArenaHeaderTag::LiveLoadState); load_state.set_tag(ArenaHeaderTag::LiveLoadState);
@@ -1842,9 +1841,7 @@ impl Machine {
let err = self.machine_st.permission_error( let err = self.machine_st.permission_error(
Permission::Modify, Permission::Modify,
atom!("static_procedure"), atom!("static_procedure"),
functor_stub(atom!(":"), 2) functor_stub(atom!(":"), 2),
.into_iter()
.collect::<MachineStub>(),
); );
self.machine_st self.machine_st
@@ -2100,9 +2097,10 @@ impl Machine {
} }
pub(crate) fn abolish_clause(&mut self) -> CallResult { pub(crate) fn abolish_clause(&mut self) -> CallResult {
let module_name = cell_as_atom!(self let module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self let key = self
.machine_st .machine_st
@@ -2216,9 +2214,10 @@ impl Machine {
_ => unreachable!(), _ => unreachable!(),
}; };
let module_name = cell_as_atom!(self let module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[4]))); .store(self.machine_st.deref(self.machine_st.registers[4]))
);
let compilation_target = match module_name { let compilation_target = match module_name {
atom!("user") => CompilationTarget::User, atom!("user") => CompilationTarget::User,
@@ -2271,9 +2270,10 @@ impl Machine {
} }
pub(crate) fn is_consistent_with_term_queue(&mut self) -> CallResult { pub(crate) fn is_consistent_with_term_queue(&mut self) -> CallResult {
let module_name = cell_as_atom!(self let module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self let key = self
.machine_st .machine_st
@@ -2311,9 +2311,10 @@ impl Machine {
} }
pub(crate) fn remove_module_exports(&mut self) -> CallResult { pub(crate) fn remove_module_exports(&mut self) -> CallResult {
let module_name = cell_as_atom!(self let module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
let mut loader = self.loader_from_heap_evacuable(temp_v!(2)); let mut loader = self.loader_from_heap_evacuable(temp_v!(2));
@@ -2339,9 +2340,10 @@ impl Machine {
} }
pub(crate) fn meta_predicate_property(&mut self) { pub(crate) fn meta_predicate_property(&mut self) {
let module_name = cell_as_atom!(self let module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
let (predicate_name, arity) = self let (predicate_name, arity) = self
.machine_st .machine_st
@@ -2361,8 +2363,8 @@ impl Machine {
let mut writer = match self.machine_st.heap.reserve(3 + meta_specs.len()) { let mut writer = match self.machine_st.heap.reserve(3 + meta_specs.len()) {
Ok(writer) => writer, Ok(writer) => writer,
Err(err_loc) => { Err(err) => {
self.machine_st.throw_resource_error(err_loc); self.machine_st.throw_resource_error(err);
return; return;
} }
}; };
@@ -2376,7 +2378,7 @@ impl Machine {
MetaSpec::Plus => atom_as_cell!(atom!("-")), MetaSpec::Plus => atom_as_cell!(atom!("-")),
MetaSpec::Either => atom_as_cell!(atom!("?")), MetaSpec::Either => atom_as_cell!(atom!("?")),
MetaSpec::Colon => atom_as_cell!(atom!(":")), MetaSpec::Colon => atom_as_cell!(atom!(":")),
MetaSpec::RequiresExpansionWithArgument(ref arg_num) => { MetaSpec::RequiresExpansionWithArgument(arg_num) => {
fixnum_as_cell!(/* FIXME this is not safe */ unsafe { fixnum_as_cell!(/* FIXME this is not safe */ unsafe {
Fixnum::build_with_unchecked(*arg_num as i64) Fixnum::build_with_unchecked(*arg_num as i64)
}) })
@@ -2403,9 +2405,10 @@ impl Machine {
} }
pub(crate) fn dynamic_property(&mut self) { pub(crate) fn dynamic_property(&mut self) {
let module_name = cell_as_atom!(self let module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self let key = self
.machine_st .machine_st
@@ -2430,9 +2433,10 @@ impl Machine {
} }
pub(crate) fn multifile_property(&mut self) { pub(crate) fn multifile_property(&mut self) {
let module_name = cell_as_atom!(self let module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self let key = self
.machine_st .machine_st
@@ -2457,9 +2461,10 @@ impl Machine {
} }
pub(crate) fn discontiguous_property(&mut self) { pub(crate) fn discontiguous_property(&mut self) {
let module_name = cell_as_atom!(self let module_name = cell_as_atom!(
.machine_st self.machine_st
.store(self.machine_st.deref(self.machine_st.registers[1]))); .store(self.machine_st.deref(self.machine_st.registers[1]))
);
let key = self let key = self
.machine_st .machine_st

View File

@@ -6,6 +6,7 @@ use crate::parser::ast::*;
use crate::ffi::{self, FfiError}; use crate::ffi::{self, FfiError};
use crate::forms::*; use crate::forms::*;
use crate::functor_macro::*; use crate::functor_macro::*;
use crate::machine::heap::AllocError;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::loader::CompilationTarget; use crate::machine::loader::CompilationTarget;
use crate::machine::machine_state::*; use crate::machine::machine_state::*;
@@ -19,7 +20,7 @@ pub type MachineStubGen = Box<dyn Fn(&mut MachineState) -> MachineStub>;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct MachineError { pub(crate) struct MachineError {
stub: MachineStub, stub: MachineStub,
location: Option<(usize, usize)>, // line_num, col_num location: Option<Location>,
} }
// from 7.12.2 b) of 13211-1:1995 // from 7.12.2 b) of 13211-1:1995
@@ -75,7 +76,6 @@ impl ValidType {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) enum ResourceError { pub(crate) enum ResourceError {
FiniteMemory(HeapCellValue),
OutOfFiles, OutOfFiles,
} }
@@ -329,12 +329,6 @@ impl MachineState {
pub(super) fn resource_error(err: ResourceError) -> MachineError { pub(super) fn resource_error(err: ResourceError) -> MachineError {
let stub = match err { let stub = match err {
ResourceError::FiniteMemory(size_requested) => {
functor!(
atom!("resource_error"),
[atom_as_cell((atom!("finite_memory"))), cell(size_requested)]
)
}
ResourceError::OutOfFiles => { ResourceError::OutOfFiles => {
functor!( functor!(
atom!("resource_error"), atom!("resource_error"),
@@ -447,10 +441,26 @@ impl MachineState {
location: None, location: None,
} }
} }
ExistenceError::FfiFunction(atom) => { ExistenceError::FfiFunction(name, arity) => {
let culprit = functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]);
let stub = functor!( let stub = functor!(
atom!("existence_error"), atom!("existence_error"),
[atom_as_cell((atom!("ffi_function"))), atom_as_cell(atom)] [atom_as_cell((atom!("ffi_function"))), functor(culprit)]
);
MachineError {
stub,
location: None,
}
}
ExistenceError::FfiStructConstructor(name, arity) => {
let culprit = functor!(atom!("/"), [atom_as_cell(name), fixnum(arity)]);
let stub = functor!(
atom!("existence_error"),
[
atom_as_cell((atom!("ffi_struct_constructor"))),
functor(culprit)
]
); );
MachineError { MachineError {
@@ -629,6 +639,19 @@ impl MachineState {
return self.directive_error(err); return self.directive_error(err);
} }
if let CompilationError::FiniteMemoryInHeap(err) = err {
// err.resource_error_offset() should be the address of the error/2 functor in the pre-allocated term error(resource_error(memory), [])
let err_loc = err.resource_error_offset(&mut self.heap);
let stub = vec![FunctorElement::AbsoluteCell(
// err_loc + 1 should be the functors first argument which should be a str cell pointing at the resource_error/1 functor
self.heap[err_loc + 1],
)];
return MachineError {
stub,
location: None,
};
}
let location = err.line_and_col_num(); let location = err.line_and_col_num();
let stub = err.as_functor(); let stub = err.as_functor();
@@ -688,13 +711,25 @@ impl MachineState {
} }
} }
FfiError::ValueOutOfRange(domain, culprit) => self.domain_error(domain, culprit), FfiError::ValueOutOfRange(domain, culprit) => self.domain_error(domain, culprit),
FfiError::FunctionNotFound(name) => { FfiError::FunctionNotFound(name, arity) => {
self.existence_error(ExistenceError::FfiFunction(name)) self.existence_error(ExistenceError::FfiFunction(name, arity))
} }
FfiError::StructNotFound(name) => { FfiError::StructNotFound(name) => {
self.existence_error(ExistenceError::FfiStructType(name)) self.existence_error(ExistenceError::FfiStructType(name))
} }
FfiError::ArgCountMismatch => self.unreachable_error(), FfiError::ArgCountMismatch {
name,
kind,
expected: _,
got,
} => match kind {
ffi::ArgCountMismatchKind::Function => {
self.existence_error(ExistenceError::FfiFunction(name, got))
}
ffi::ArgCountMismatchKind::Struct => {
self.existence_error(ExistenceError::FfiStructConstructor(name, got))
}
},
FfiError::AllocationFailed => MachineError { FfiError::AllocationFailed => MachineError {
stub: functor!(atom!("resource_error"), [atom_as_cell((atom!("heap")))]), stub: functor!(atom!("resource_error"), [atom_as_cell((atom!("heap")))]),
location: None, location: None,
@@ -702,10 +737,13 @@ impl MachineState {
FfiError::LayoutError => self.representation_error(RepFlag::FfiLayout), FfiError::LayoutError => self.representation_error(RepFlag::FfiLayout),
FfiError::UnsupportedTypedef => self.representation_error(RepFlag::FfiLayout), FfiError::UnsupportedTypedef => self.representation_error(RepFlag::FfiLayout),
FfiError::UnsupportedAbi => self.representation_error(RepFlag::FfiAbi), FfiError::UnsupportedAbi => self.representation_error(RepFlag::FfiAbi),
FfiError::VoidArgumentType => self.domain_error( FfiError::UnsupportedArgumentType(None) => self.domain_error(
DomainErrorType::FfiArgumentType, DomainErrorType::FfiArgumentType,
atom_as_cell!(atom!("void")), atom_as_cell!(atom!("unknown")),
), ),
FfiError::UnsupportedArgumentType(Some(kind)) => {
self.domain_error(DomainErrorType::FfiArgumentType, atom_as_cell!(kind))
}
FfiError::CStrFieldType => self.domain_error( FfiError::CStrFieldType => self.domain_error(
DomainErrorType::NonCStrFfiArgumentType, DomainErrorType::NonCStrFfiArgumentType,
atom_as_cell!(atom!("cstr")), atom_as_cell!(atom!("cstr")),
@@ -714,18 +752,20 @@ impl MachineState {
DomainErrorType::NonNullPtr, DomainErrorType::NonNullPtr,
fixnum_as_cell!(Fixnum::build_with(0)), fixnum_as_cell!(Fixnum::build_with(0)),
), ),
FfiError::Other => self.unreachable_error(),
} }
} }
pub(super) fn error_form(&mut self, err: MachineError, src: MachineStub) -> MachineStub { pub(super) fn error_form(&mut self, err: MachineError, src: MachineStub) -> MachineStub {
if let Some((line_num, _col_num)) = err.location { if let Some(location) = err.location {
let line = location.line();
functor!( functor!(
atom!("error"), atom!("error"),
[ [
functor((err.stub)), functor((err.stub)),
functor( functor(
(atom!(":")), (atom!(":")),
[functor(src), number(line_num, (&mut self.arena))] [functor(src), number(line, (&mut self.arena))]
) )
] ]
) )
@@ -735,10 +775,19 @@ impl MachineState {
} }
// throw an error pre-allocated in the heap // throw an error pre-allocated in the heap
pub(super) fn throw_resource_error(&mut self, err_loc: usize) { pub(super) fn throw_resource_error(&mut self, err: AllocError) {
self.registers[1] = str_loc_as_cell!(err_loc); if self.throwing_resource_error {
panic!(
"attempted to throw `error(resource_error(memory), [])` while attempting to throw `error(resource_error(memory), [])`"
);
}
self.throwing_resource_error = true;
self.registers[1] = str_loc_as_cell!(err.resource_error_offset(&mut self.heap));
self.set_ball(); self.set_ball();
self.unwind_stack(); self.unwind_stack();
self.throwing_resource_error = false;
} }
pub(super) fn throw_exception(&mut self, err: MachineStub) { pub(super) fn throw_exception(&mut self, err: MachineStub) {
@@ -749,8 +798,8 @@ impl MachineState {
self.registers[1] = match writer(&mut self.heap) { self.registers[1] = match writer(&mut self.heap) {
Ok(loc) => loc, Ok(loc) => loc,
Err(resource_err_loc) => { Err(err) => {
self.throw_resource_error(resource_err_loc); self.throw_resource_error(err);
return; return;
} }
}; };
@@ -774,7 +823,13 @@ pub enum CompilationError {
InvalidRuleHead, InvalidRuleHead,
InvalidUseModuleDecl, InvalidUseModuleDecl,
InvalidModuleResolution(Atom), InvalidModuleResolution(Atom),
FiniteMemoryInHeap(usize), FiniteMemoryInHeap(AllocError),
}
impl From<AllocError> for CompilationError {
fn from(value: AllocError) -> Self {
Self::FiniteMemoryInHeap(value)
}
} }
#[derive(Debug)] #[derive(Debug)]
@@ -805,9 +860,9 @@ 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<Location> {
match self { match self {
CompilationError::ParserError(err) => err.line_and_col_num(), CompilationError::ParserError(err) => err.location(),
_ => None, _ => None,
} }
} }
@@ -847,11 +902,11 @@ impl CompilationError {
CompilationError::InvalidUseModuleDecl => { CompilationError::InvalidUseModuleDecl => {
functor!(atom!("invalid_use_module_declaration")) functor!(atom!("invalid_use_module_declaration"))
} }
CompilationError::ParserError(ref err) => { CompilationError::ParserError(err) => {
functor!(err.as_atom()) functor!(err.as_atom())
} }
CompilationError::FiniteMemoryInHeap(h) => { CompilationError::FiniteMemoryInHeap(_) => {
vec![FunctorElement::AbsoluteCell(str_loc_as_cell!(*h))] functor!(atom!("resource_error"))
} }
} }
} }
@@ -884,6 +939,7 @@ impl Permission {
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[cfg_attr(not(feature = "ffi"), expect(dead_code))]
pub(crate) enum DomainErrorType { pub(crate) enum DomainErrorType {
IOMode, IOMode,
NotLessThanZero, NotLessThanZero,
@@ -934,6 +990,7 @@ impl DomainErrorType {
// from 7.12.2 f) of 13211-1:1995 // from 7.12.2 f) of 13211-1:1995
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
#[cfg_attr(not(feature = "ffi"), expect(dead_code))]
pub(crate) enum RepFlag { pub(crate) enum RepFlag {
Character, Character,
CharacterCode, CharacterCode,
@@ -1125,6 +1182,7 @@ impl MachineState {
} }
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(not(feature = "ffi"), expect(dead_code))]
pub enum ExistenceError { pub enum ExistenceError {
Module(Atom), Module(Atom),
ModuleSource(ModuleSource), ModuleSource(ModuleSource),
@@ -1137,7 +1195,8 @@ pub enum ExistenceError {
SourceSink(HeapCellValue), SourceSink(HeapCellValue),
Stream(HeapCellValue), Stream(HeapCellValue),
Process(HeapCellValue), Process(HeapCellValue),
FfiFunction(Atom), FfiFunction(Atom, usize),
FfiStructConstructor(Atom, usize),
FfiStructType(Atom), FfiStructType(Atom),
} }

View File

@@ -1,20 +1,18 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
use crate::parser::ast::*; use crate::parser::ast::*;
use crate::atom_table::*; use crate::atom_table::*;
use crate::forms::*; use crate::forms::*;
use crate::machine::ClauseType;
use crate::machine::MachineStubGen;
use crate::machine::loader::*; use crate::machine::loader::*;
use crate::machine::machine_state::*; use crate::machine::machine_state::*;
use crate::machine::streams::{Stream, StreamOptions}; use crate::machine::streams::{Stream, StreamOptions};
use crate::machine::ClauseType;
use crate::machine::MachineStubGen;
use crate::offset_table::*; use crate::offset_table::*;
use fxhash::FxBuildHasher; use fxhash::FxBuildHasher;
use indexmap::{IndexMap, IndexSet}; use indexmap::{IndexMap, IndexSet};
use scryer_modular_bitfield::specifiers::*; use modular_bitfield::specifiers::*;
use scryer_modular_bitfield::{bitfield, BitfieldSpecifier}; use modular_bitfield::{Specifier, bitfield};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeSet; use std::collections::BTreeSet;
@@ -65,7 +63,7 @@ impl PartialOrd<Ref> for HeapCellValue {
} }
} }
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq)] #[derive(Specifier, Copy, Clone, Debug, PartialEq)]
#[bits = 7] #[bits = 7]
pub enum IndexPtrTag { pub enum IndexPtrTag {
DynamicUndefined = 0b1000101, // a predicate, declared as dynamic, whose location in code is as yet undefined. DynamicUndefined = 0b1000101, // a predicate, declared as dynamic, whose location in code is as yet undefined.
@@ -294,7 +292,7 @@ impl IndexStore {
) -> Option<&mut PredicateSkeleton> { ) -> Option<&mut PredicateSkeleton> {
match compilation_target { match compilation_target {
CompilationTarget::User => self.extensible_predicates.get_mut(key), CompilationTarget::User => self.extensible_predicates.get_mut(key),
CompilationTarget::Module(ref module_name) => { CompilationTarget::Module(module_name) => {
if let Some(module) = self.modules.get_mut(module_name) { if let Some(module) = self.modules.get_mut(module_name) {
module.extensible_predicates.get_mut(key) module.extensible_predicates.get_mut(key)
} else { } else {
@@ -311,7 +309,7 @@ impl IndexStore {
) -> Option<&PredicateSkeleton> { ) -> Option<&PredicateSkeleton> {
match compilation_target { match compilation_target {
CompilationTarget::User => self.extensible_predicates.get(key), CompilationTarget::User => self.extensible_predicates.get(key),
CompilationTarget::Module(ref module_name) => { CompilationTarget::Module(module_name) => {
if let Some(module) = self.modules.get(module_name) { if let Some(module) = self.modules.get(module_name) {
module.extensible_predicates.get(key) module.extensible_predicates.get(key)
} else { } else {
@@ -382,7 +380,7 @@ impl IndexStore {
) -> Option<PredicateSkeleton> { ) -> Option<PredicateSkeleton> {
match compilation_target { match compilation_target {
CompilationTarget::User => self.extensible_predicates.swap_remove(key), CompilationTarget::User => self.extensible_predicates.swap_remove(key),
CompilationTarget::Module(ref module_name) => { CompilationTarget::Module(module_name) => {
if let Some(module) = self.modules.get_mut(module_name) { if let Some(module) = self.modules.get_mut(module_name) {
module.extensible_predicates.swap_remove(key) module.extensible_predicates.swap_remove(key)
} else { } else {
@@ -415,7 +413,7 @@ impl IndexStore {
) -> Option<&Vec<MetaSpec>> { ) -> Option<&Vec<MetaSpec>> {
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(module_name) => match self.modules.get(module_name) {
Some(module) => module Some(module) => module
.meta_predicates .meta_predicates
.get(&(name, arity)) .get(&(name, arity))

View File

@@ -3,21 +3,22 @@ use crate::atom_table::*;
use crate::forms::*; use crate::forms::*;
use crate::heap_iter::*; use crate::heap_iter::*;
use crate::heap_print::*; use crate::heap_print::*;
use crate::machine::Machine;
use crate::machine::attributed_variables::*; use crate::machine::attributed_variables::*;
use crate::machine::copier::*; use crate::machine::copier::*;
use crate::machine::heap::AllocError;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::machine_errors::*; use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*; use crate::machine::machine_indices::*;
use crate::machine::stack::*; use crate::machine::stack::*;
use crate::machine::streams::*; use crate::machine::streams::*;
use crate::machine::Machine;
use crate::parser::ast::*; use crate::parser::ast::*;
use crate::read::TermWriteResult; use crate::read::TermWriteResult;
use crate::types::*; use crate::types::*;
use crate::parser::dashu::Integer; use fxhash::FxBuildHasher;
use indexmap::IndexMap; use indexmap::IndexMap;
use indexmap::IndexSet;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
@@ -56,11 +57,68 @@ pub enum OnEOF {
Return, Return,
Continue, Continue,
} }
pub(crate) trait OccursCheckImpl {
fn flag_value(&self) -> Atom;
fn unify(&self, state: &mut MachineState);
fn bind(&self, state: &mut MachineState, r: Ref, h: HeapCellValue);
}
/// Not subject to occurs-check
pub(crate) struct Nsto;
impl OccursCheckImpl for Nsto {
fn flag_value(&self) -> Atom {
atom!("false")
}
fn unify(&self, state: &mut MachineState) {
state.unify();
}
fn bind(&self, state: &mut MachineState, r: Ref, h: HeapCellValue) {
state.bind(r, h);
}
}
/// Subject to occurs-check
pub(crate) struct Sto;
impl OccursCheckImpl for Sto {
fn flag_value(&self) -> Atom {
atom!("true")
}
fn unify(&self, state: &mut MachineState) {
state.unify_with_occurs_check();
}
fn bind(&self, state: &mut MachineState, r: Ref, h: HeapCellValue) {
state.bind_with_occurs_check_wrapper(r, h);
}
}
/// Subject to occurs-check -> error
pub(crate) struct StoError;
impl OccursCheckImpl for StoError {
fn flag_value(&self) -> Atom {
atom!("error")
}
fn unify(&self, state: &mut MachineState) {
state.unify_with_occurs_check_with_error();
}
fn bind(&self, state: &mut MachineState, r: Ref, h: HeapCellValue) {
state.bind_with_occurs_check_with_error_wrapper(r, h);
}
}
pub struct MachineState { pub struct MachineState {
pub atom_tbl: Arc<AtomTable>, pub atom_tbl: Arc<AtomTable>,
pub arena: Arena, pub arena: Arena,
pub(super) pdl: Vec<HeapCellValue>, pub(super) pdl: Vec<(HeapCellValue, HeapCellValue)>,
pub(super) unify_tabu_list: IndexSet<(HeapCellValue, HeapCellValue), FxBuildHasher>,
pub(super) s: HeapPtr, pub(super) s: HeapPtr,
pub(super) s_offset: usize, pub(super) s_offset: usize,
pub(super) p: usize, pub(super) p: usize,
@@ -73,6 +131,7 @@ pub struct MachineState {
pub(super) cp: usize, pub(super) cp: usize,
pub(super) attr_var_init: AttrVarInitializer, pub(super) attr_var_init: AttrVarInitializer,
pub(super) fail: bool, pub(super) fail: bool,
pub throwing_resource_error: bool,
pub heap: Heap, pub heap: Heap,
pub(super) mode: MachineMode, pub(super) mode: MachineMode,
pub(crate) stack: Stack, pub(crate) stack: Stack,
@@ -85,7 +144,6 @@ pub struct MachineState {
pub(super) ball: Ball, pub(super) ball: Ball,
pub(super) ball_stack: Vec<Ball>, // save current ball before jumping via, e.g., verify_attr interrupt. pub(super) ball_stack: Vec<Ball>, // save current ball before jumping via, e.g., verify_attr interrupt.
pub(super) lifted_heap: Heap, pub(super) lifted_heap: Heap,
pub(super) interms: Vec<Number>, // intermediate numbers.
// locations of cleaners, cut points, the previous scc_block. for setup_call_cleanup/3. // locations of cleaners, cut points, the previous scc_block. for setup_call_cleanup/3.
pub(super) cont_pts: Vec<(HeapCellValue, usize, usize)>, pub(super) cont_pts: Vec<(HeapCellValue, usize, usize)>,
pub(super) cwil: CWIL, pub(super) cwil: CWIL,
@@ -93,8 +151,7 @@ pub struct MachineState {
pub(crate) cc: usize, pub(crate) cc: usize,
pub(crate) global_clock: usize, pub(crate) global_clock: usize,
pub(crate) dynamic_mode: FirstOrNext, pub(crate) dynamic_mode: FirstOrNext,
pub(crate) unify_fn: fn(&mut MachineState), pub(crate) occurs_check: &'static dyn OccursCheckImpl,
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,
} }
@@ -124,33 +181,12 @@ impl fmt::Debug for MachineState {
.field("ball", &self.ball) .field("ball", &self.ball)
.field("ball_stack", &self.ball_stack) .field("ball_stack", &self.ball_stack)
.field("lifted_heap", &self.lifted_heap) .field("lifted_heap", &self.lifted_heap)
.field("interms", &self.interms)
.field("flags", &self.flags) .field("flags", &self.flags)
.field("cc", &self.cc) .field("cc", &self.cc)
.field("global_clock", &self.global_clock) .field("global_clock", &self.global_clock)
.field("dynamic_mode", &self.dynamic_mode) .field("dynamic_mode", &self.dynamic_mode)
.field( .field("unify_fn", &&*self.occurs_check.flag_value().as_str())
"unify_fn", .field("bind_fn", &&*self.occurs_check.flag_value().as_str())
if self.unify_fn as usize == MachineState::unify as usize {
&"MachineState::unify"
} else if self.unify_fn as usize == MachineState::unify_with_occurs_check as usize {
&"MachineState::unify_with_occurs_check"
} else {
&"MachineState::unify_with_occurs_check_with_error"
},
)
.field(
"bind_fn",
if self.bind_fn as usize == MachineState::bind as usize {
&"MachineState::bind"
} else if self.bind_fn as usize
== MachineState::bind_with_occurs_check_wrapper as usize
{
&"MachineState::bind_with_occurs_check"
} else {
&"MachineState::bind_with_occurs_check_with_error_wrapper"
},
)
.finish() .finish()
} }
} }
@@ -192,7 +228,7 @@ fn push_var_eq_functors<'a>(
size: usize, size: usize,
iter: impl Iterator<Item = (&'a VarKey, &'a HeapCellValue)>, iter: impl Iterator<Item = (&'a VarKey, &'a HeapCellValue)>,
atom_tbl: &AtomTable, atom_tbl: &AtomTable,
) -> Result<HeapCellValue, usize> { ) -> Result<HeapCellValue, AllocError> {
let src_h = heap.cell_len(); let src_h = heap.cell_len();
let true_size = if size > 0 { let true_size = if size > 0 {
@@ -257,7 +293,7 @@ impl Ball {
self.stub.clear(); self.stub.clear();
} }
pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result<usize, usize> { pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result<usize, AllocError> {
let h = dest.cell_len(); let h = dest.cell_len();
let diff = self.boundary as i64 - h as i64; let diff = self.boundary as i64 - h as i64;
@@ -346,17 +382,17 @@ impl<'a> CopierTarget for CopyTerm<'a> {
} }
#[inline(always)] #[inline(always)]
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> { fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
self.state.heap.copy_pstr_within(pstr_loc) self.state.heap.copy_pstr_within(pstr_loc)
} }
#[inline(always)] #[inline(always)]
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> { fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
self.state.heap.reserve(num_cells) self.state.heap.reserve(num_cells)
} }
#[inline(always)] #[inline(always)]
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> { fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError> {
self.state.heap.copy_slice_to_end(bounds) self.state.heap.copy_slice_to_end(bounds)
} }
} }
@@ -455,7 +491,7 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
self.stack self.stack
} }
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> { fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
debug_assert!(pstr_loc < self.heap.byte_len()); debug_assert!(pstr_loc < self.heap.byte_len());
let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(pstr_loc); let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(pstr_loc);
@@ -477,11 +513,11 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
} }
#[inline] #[inline]
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> { fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
self.stub.reserve(num_cells) self.stub.reserve(num_cells)
} }
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> { fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError> {
let len = bounds.end - bounds.start; let len = bounds.end - bounds.start;
let mut stub_writer = self.stub.reserve(len)?; let mut stub_writer = self.stub.reserve(len)?;
@@ -515,7 +551,7 @@ impl MachineState {
return true; return true;
} }
self.cwil.global_count += 1; self.cwil.global_count = self.cwil.global_count.strict_add(1);
if let Some(&(ref limit, block)) = self.cwil.limits.last() { if let Some(&(ref limit, block)) = self.cwil.limits.last() {
if self.cwil.local_count == *limit { if self.cwil.local_count == *limit {
@@ -617,7 +653,7 @@ impl MachineState {
mut var_list: Vec<(VarKey, HeapCellValue, usize)>, mut var_list: Vec<(VarKey, HeapCellValue, usize)>,
singleton_heap_list: HeapCellValue, singleton_heap_list: HeapCellValue,
) -> CallResult { ) -> CallResult {
var_list.sort_by(|(_, _, idx_1), (_, _, idx_2)| idx_1.cmp(idx_2)); var_list.sort_by_key(|(_, _, idx_1)| *idx_1);
let singleton_addr = self.registers[3]; let singleton_addr = self.registers[3];
unify_fn!(*self, singleton_heap_list, singleton_addr); unify_fn!(*self, singleton_heap_list, singleton_addr);
@@ -1077,47 +1113,47 @@ impl MachineState {
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct CWIL { pub(crate) struct CWIL {
local_count: Integer, local_count: u128,
pub(crate) global_count: Integer, pub(crate) global_count: u128,
limits: Vec<(Integer, usize)>, limits: Vec<(u128, usize)>,
pub(crate) inference_limit_exceeded: bool, pub(crate) inference_limit_exceeded: bool,
} }
impl CWIL { impl CWIL {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
CWIL { CWIL {
local_count: Integer::from(0), local_count: 0,
global_count: Integer::from(0), global_count: 0,
limits: vec![], limits: vec![],
inference_limit_exceeded: false, inference_limit_exceeded: false,
} }
} }
pub(crate) fn add_limit(&mut self, mut limit: Integer, block: usize) -> &Integer { pub(crate) fn add_limit(&mut self, mut limit: u128, block: usize) -> u128 {
limit += &self.local_count; limit = limit.strict_add(self.local_count);
match self.limits.last() { match self.limits.last() {
Some((ref inner_limit, _)) if *inner_limit <= limit => {} Some((inner_limit, _)) if *inner_limit <= limit => {}
_ => self.limits.push((limit, block)), _ => self.limits.push((limit, block)),
} }
&self.local_count self.local_count
} }
#[inline(always)] #[inline(always)]
pub(crate) fn remove_limit(&mut self, block: usize) -> &Integer { pub(crate) fn remove_limit(&mut self, block: usize) -> u128 {
if let Some((_, bl)) = self.limits.last() { if let Some((_, bl)) = self.limits.last() {
if bl == &block { if bl == &block {
self.limits.pop(); self.limits.pop();
} }
} }
&self.local_count self.local_count
} }
#[inline(always)] #[inline(always)]
pub(crate) fn reset(&mut self) { pub(crate) fn reset(&mut self) {
self.local_count = Integer::from(0); self.local_count = 0;
self.limits.clear(); self.limits.clear();
self.inference_limit_exceeded = false; self.inference_limit_exceeded = false;
} }

View File

@@ -1,12 +1,15 @@
use fxhash::FxBuildHasher;
use indexmap::IndexSet;
use crate::arena::*; use crate::arena::*;
use crate::atom_table::*; use crate::atom_table::*;
use crate::forms::*; use crate::forms::*;
use crate::heap_iter::*; use crate::heap_iter::*;
use crate::machine::attributed_variables::*; use crate::machine::attributed_variables::*;
use crate::machine::copier::*; use crate::machine::copier::*;
use crate::machine::heap::AllocError;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::machine_errors::*; use crate::machine::machine_errors::*;
use crate::machine::machine_indices::*;
use crate::machine::machine_state::*; use crate::machine::machine_state::*;
use crate::machine::partial_string::*; use crate::machine::partial_string::*;
use crate::machine::stack::*; use crate::machine::stack::*;
@@ -16,8 +19,6 @@ use crate::parser::ast::*;
use crate::parser::dashu::{Integer, Rational}; use crate::parser::dashu::{Integer, Rational};
use crate::types::*; use crate::types::*;
use indexmap::IndexSet;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::convert::TryFrom; use std::convert::TryFrom;
@@ -30,9 +31,10 @@ impl MachineState {
heap.store_resource_error(); heap.store_resource_error();
MachineState { MachineState {
arena: Arena::new(), arena: Arena::new().unwrap(),
atom_tbl: AtomTable::new(), atom_tbl: AtomTable::new().unwrap(),
pdl: Vec::with_capacity(1024), pdl: Vec::with_capacity(1024),
unify_tabu_list: IndexSet::with_hasher(FxBuildHasher::default()),
s: HeapPtr::default(), s: HeapPtr::default(),
s_offset: 0, s_offset: 0,
p: 0, p: 0,
@@ -47,7 +49,7 @@ impl MachineState {
fail: false, fail: false,
heap, heap,
mode: MachineMode::Write, mode: MachineMode::Write,
stack: Stack::new(), stack: Stack::new().unwrap(),
registers: [heap_loc_as_cell!(0); MAX_ARITY + 1], // self.registers[0] is never used. registers: [heap_loc_as_cell!(0); MAX_ARITY + 1], // self.registers[0] is never used.
trail: vec![], trail: vec![],
tr: 0, tr: 0,
@@ -57,16 +59,15 @@ impl MachineState {
ball: Ball::new(), ball: Ball::new(),
ball_stack: vec![], ball_stack: vec![],
lifted_heap: Heap::new(), lifted_heap: Heap::new(),
interms: vec![Number::default(); 256],
cont_pts: Vec::with_capacity(256), cont_pts: Vec::with_capacity(256),
cwil: CWIL::new(), cwil: CWIL::new(),
flags: MachineFlags::default(), flags: MachineFlags::default(),
cc: 0, cc: 0,
global_clock: 0, global_clock: 0,
dynamic_mode: FirstOrNext::First, dynamic_mode: FirstOrNext::First,
unify_fn: MachineState::unify, occurs_check: &Nsto,
bind_fn: MachineState::bind,
run_cleaners_fn: |_| false, run_cleaners_fn: |_| false,
throwing_resource_error: false,
} }
} }
@@ -174,8 +175,8 @@ impl MachineState {
} }
} }
pub fn allocate(&mut self, num_cells: usize) { pub fn allocate(&mut self, num_cells: usize) -> Result<(), AllocError> {
let e = self.stack.allocate_and_frame(num_cells); let e = self.stack.allocate_and_frame(num_cells)?;
let and_frame = self.stack.index_and_frame_mut(e); let and_frame = self.stack.index_and_frame_mut(e);
and_frame.prelude.e = self.e; and_frame.prelude.e = self.e;
@@ -183,6 +184,8 @@ impl MachineState {
self.e = e; self.e = e;
self.p += 1; self.p += 1;
Ok(())
} }
pub fn bind(&mut self, r1: Ref, a2: HeapCellValue) { pub fn bind(&mut self, r1: Ref, a2: HeapCellValue) {
@@ -377,347 +380,6 @@ impl MachineState {
} }
} }
pub fn compare_term_test(&mut self, var_comparison: VarComparison) -> Option<Ordering> {
let mut tabu_list = IndexSet::new();
while let Some(s1) = self.pdl.pop() {
let s1 = self.deref(s1);
let s2 = self.pdl.pop().unwrap();
let s2 = self.deref(s2);
if s1 == s2 {
continue;
}
let v1 = self.store(s1);
let v2 = self.store(s2);
let order_cat_v1 = v1.order_category(&self.heap);
let order_cat_v2 = v2.order_category(&self.heap);
if order_cat_v1 != order_cat_v2 {
self.pdl.clear();
return Some(order_cat_v1.cmp(&order_cat_v2));
}
match order_cat_v1 {
Some(TermOrderCategory::Variable) => {
if let VarComparison::Distinct = var_comparison {
let v1 = v1.as_var().unwrap();
let v2 = v2.as_var().unwrap();
if v1 != v2 {
self.pdl.clear();
return Some(v1.cmp(&v2));
}
}
}
Some(TermOrderCategory::FloatingPoint) => {
let v1 = cell_as_f64_offset!(v1);
let v2 = cell_as_f64_offset!(v2);
let v1 = self.arena.f64_tbl.get_entry(v1);
let v2 = self.arena.f64_tbl.get_entry(v2);
if v1 != v2 {
self.pdl.clear();
return Some(v1.cmp(&v2));
}
}
Some(TermOrderCategory::Integer) => {
let v1 = Number::try_from((v1, &self.arena.f64_tbl)).unwrap();
let v2 = Number::try_from((v2, &self.arena.f64_tbl)).unwrap();
if v1 != v2 {
self.pdl.clear();
return Some(v1.cmp(&v2));
}
}
Some(TermOrderCategory::Atom) => {
read_heap_cell!(v1,
(HeapCellValueTag::Atom, (n1, _a1)) => {
read_heap_cell!(v2,
(HeapCellValueTag::Atom, (n2, _a2)) => {
if n1 != n2 {
self.pdl.clear();
return Some(n1.cmp(&n2));
}
}
(HeapCellValueTag::Str, s) => {
let n2 = cell_as_atom_cell!(self.heap[s])
.get_name();
if n1 != n2 {
self.pdl.clear();
return Some(n1.cmp(&n2));
}
}
_ => {
unreachable!();
}
)
}
(HeapCellValueTag::Str, s) => {
let n1 = cell_as_atom_cell!(self.heap[s])
.get_name();
read_heap_cell!(v2,
(HeapCellValueTag::Atom, (n2, _a2)) => {
if n1 != n2 {
self.pdl.clear();
return Some(n1.cmp(&n2));
}
}
(HeapCellValueTag::Str, s) => {
let n2 = cell_as_atom_cell!(self.heap[s])
.get_name();
if n1 != n2 {
self.pdl.clear();
return Some(n1.cmp(&n2));
}
}
_ => {
unreachable!();
}
)
}
_ => {
unreachable!()
}
)
}
Some(TermOrderCategory::Compound) => {
read_heap_cell!(v1,
(HeapCellValueTag::Lis, l1) => {
read_heap_cell!(v2,
(HeapCellValueTag::PStrLoc, l2) => {
if tabu_list.contains(&(l1, l2)) {
continue;
}
tabu_list.insert((l1, l2));
// like the action of
// partial_string_to_pdl here but
// the ordering of PDL pushes is
// (crucially for comparison
// correctness) different.
let (c, succ_cell) = self.heap.last_str_char_and_tail(l2);
self.pdl.push(succ_cell);
self.pdl.push(heap_loc_as_cell!(l1 + 1));
self.pdl.push(char_as_cell!(c));
self.pdl.push(heap_loc_as_cell!(l1));
}
(HeapCellValueTag::Lis, l2) => {
if tabu_list.contains(&(l1, l2)) {
continue;
}
tabu_list.insert((l1, l2));
self.pdl.push(self.heap[l2 + 1]);
self.pdl.push(self.heap[l1 + 1]);
self.pdl.push(self.heap[l2]);
self.pdl.push(self.heap[l1]);
}
(HeapCellValueTag::Str, s2) => {
if tabu_list.contains(&(l1, s2)) {
continue;
}
let (name, arity) = cell_as_atom_cell!(self.heap[s2])
.get_name_and_arity();
match (2, atom!(".")).cmp(&(arity, name)) {
Ordering::Equal => {
tabu_list.insert((l1, s2));
self.pdl.push(self.heap[s2 + 2]);
self.pdl.push(self.heap[l1 + 1]);
self.pdl.push(self.heap[s2 + 1]);
self.pdl.push(self.heap[l1]);
}
ordering => {
self.pdl.clear();
return Some(ordering);
}
}
}
_ => {
unreachable!();
}
)
}
(HeapCellValueTag::PStrLoc, l1) => {
read_heap_cell!(v2,
(HeapCellValueTag::PStrLoc, l2) => {
if tabu_list.contains(&(l1, l2)) {
continue;
}
tabu_list.insert((l1, l2));
match self.heap.compare_pstr_segments(l1, l2) {
PStrSegmentCmpResult::Continue(v1, v2) => {
self.pdl.push(v1.offset_by(l1));
self.pdl.push(v2.offset_by(l2));
}
PStrSegmentCmpResult::Less => {
self.pdl.clear();
return Some(Ordering::Less);
}
PStrSegmentCmpResult::Greater => {
self.pdl.clear();
return Some(Ordering::Greater);
}
}
}
(HeapCellValueTag::Lis, l2) => {
if tabu_list.contains(&(l1, l2)) {
continue;
}
tabu_list.insert((l1, l2));
let (c, succ_cell) = self.heap.last_str_char_and_tail(l1);
self.pdl.push(succ_cell);
self.pdl.push(heap_loc_as_cell!(l2 + 1));
self.pdl.push(char_as_cell!(c));
self.pdl.push(heap_loc_as_cell!(l2));
}
(HeapCellValueTag::Str, s2) => {
if tabu_list.contains(&(l1, s2)) {
continue;
}
tabu_list.insert((l1, s2));
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
.get_name_and_arity();
match (2, atom!(".")).cmp(&(a2,n2)) {
Ordering::Equal => {
let (c, succ_cell) = self.heap.last_str_char_and_tail(l1);
self.pdl.push(heap_loc_as_cell!(s2+2));
self.pdl.push(succ_cell);
self.pdl.push(heap_loc_as_cell!(s2+1));
self.pdl.push(char_as_cell!(c));
}
ordering => {
self.pdl.clear();
return Some(ordering);
}
}
}
_ => {
unreachable!()
}
);
}
(HeapCellValueTag::Str, s1) => {
read_heap_cell!(v2,
(HeapCellValueTag::Str, s2) => {
if tabu_list.contains(&(s1, s2)) {
continue;
}
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
.get_name_and_arity();
let (n2, a2) = cell_as_atom_cell!(self.heap[s2])
.get_name_and_arity();
match (a1,n1).cmp(&(a2, n2)) {
Ordering::Equal => {
tabu_list.insert((s1, s2));
for idx in (1 .. a1+1).rev() {
self.pdl.push(self.heap[s2+idx]);
self.pdl.push(self.heap[s1+idx]);
}
}
ordering => {
self.pdl.clear();
return Some(ordering);
}
}
}
(HeapCellValueTag::Lis, l2) => {
if tabu_list.contains(&(s1, l2)) {
continue;
}
tabu_list.insert((s1, l2));
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
.get_name_and_arity();
match (a1,n1).cmp(&(2, atom!("."))) {
Ordering::Equal => {
self.pdl.push(self.heap[l2]);
self.pdl.push(self.heap[s1+1]);
self.pdl.push(self.heap[l2+1]);
self.pdl.push(self.heap[s1+2]);
}
ordering => {
self.pdl.clear();
return Some(ordering);
}
}
}
(HeapCellValueTag::PStrLoc, l2) => {
let (n1, a1) = cell_as_atom_cell!(self.heap[s1])
.get_name_and_arity();
match (a1,n1).cmp(&(2, atom!("."))) {
Ordering::Equal => {
let (c, succ_cell) = self.heap.last_str_char_and_tail(l2);
self.pdl.push(succ_cell);
self.pdl.push(heap_loc_as_cell!(s1+2));
self.pdl.push(char_as_cell!(c));
self.pdl.push(heap_loc_as_cell!(s1+1));
}
ordering => {
self.pdl.clear();
return Some(ordering);
}
}
}
_ => {
unreachable!()
}
)
}
_ => {
unreachable!()
}
);
}
None => {
if v1 != v2 {
self.pdl.clear();
return None;
}
}
}
}
Some(Ordering::Equal)
}
pub(crate) fn setup_call_n_init_goal_info( pub(crate) fn setup_call_n_init_goal_info(
&mut self, &mut self,
goal: HeapCellValue, goal: HeapCellValue,
@@ -888,16 +550,37 @@ impl MachineState {
} }
// returns true on failure, false on success. // returns true on failure, false on success.
pub fn eq_test(&mut self, h1: HeapCellValue, h2: HeapCellValue) -> bool { pub fn eq_test(&self, h1: HeapCellValue, h2: HeapCellValue) -> bool {
if h1 == h2 { if h1 == h2 {
return false; return false;
} }
compare_term_test!(self, h1, h2) self.compare_term_test(h1, h2)
.map(|o| o != Ordering::Equal) .map(|o| !o.is_eq())
.unwrap_or(true) .unwrap_or(true)
} }
pub fn compare_term_test(&self, h1: HeapCellValue, h2: HeapCellValue) -> Option<Ordering> {
// all derefs downstack from this function are heap bound so
// dereference stack vars pointing into the heap while possible
let h1 = self.store(h1);
let h2 = self.store(h2);
for term_pair in ParallelHeapIter::from(self, h1, h2) {
match term_pair {
TermPair::Vars(v1_offset, v2_offset) if v1_offset != v2_offset => {
return Some(v1_offset.cmp(&v2_offset));
}
TermPair::Less(..) => return Some(Ordering::Less),
TermPair::Greater(..) => return Some(Ordering::Greater),
TermPair::Unordered(cell_1, cell_2) if cell_1 != cell_2 => return None,
_ => {}
}
}
Some(Ordering::Equal)
}
#[inline(always)] #[inline(always)]
fn try_functor_compound_case(&mut self, name: Atom, arity: usize) { fn try_functor_compound_case(&mut self, name: Atom, arity: usize) {
self.try_functor_unify_components(atom_as_cell!(name), arity); self.try_functor_unify_components(atom_as_cell!(name), arity);
@@ -922,7 +605,7 @@ impl MachineState {
name: Atom, name: Atom,
arity: usize, arity: usize,
r: Ref, r: Ref,
) -> Result<(), usize> { ) -> Result<(), AllocError> {
let h = self.heap.cell_len(); let h = self.heap.cell_len();
let mut writer = self.heap.reserve(arity + 1)?; let mut writer = self.heap.reserve(arity + 1)?;
@@ -949,7 +632,7 @@ impl MachineState {
} }
}; };
(self.bind_fn)(self, r, f_a); self.occurs_check.bind(self, r, f_a);
Ok(()) Ok(())
} }
@@ -1259,7 +942,10 @@ impl MachineState {
} }
// see 8.4.4.3 of Draft Technical Corrigendum 2 for an error guide. // see 8.4.4.3 of Draft Technical Corrigendum 2 for an error guide.
pub fn project_onto_key(&mut self, value: HeapCellValue) -> Result<HeapCellValue, MachineStub> { pub fn key_val_pair(
&mut self,
value: HeapCellValue,
) -> Result<(HeapCellValue, HeapCellValue), MachineStub> {
let stub_gen = || functor_stub(atom!("keysort"), 2); let stub_gen = || functor_stub(atom!("keysort"), 2);
let store_v = self.store(self.deref(value)); let store_v = self.store(self.deref(value));
@@ -1273,7 +959,7 @@ impl MachineState {
let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity(); let (name, arity) = cell_as_atom_cell!(self.heap[s]).get_name_and_arity();
if name == atom!("-") && arity == 2 { if name == atom!("-") && arity == 2 {
Ok(heap_loc_as_cell!(s + 1)) Ok((heap_loc_as_cell!(s+1), heap_loc_as_cell!(s+2)))
} else { } else {
let err = self.type_error(ValidType::Pair, self.heap[s]); let err = self.type_error(ValidType::Pair, self.heap[s]);
Err(self.error_form(err, stub_gen())) Err(self.error_form(err, stub_gen()))

View File

@@ -168,7 +168,7 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
} }
#[inline(always)] #[inline(always)]
fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, usize> { fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result<usize, AllocError> {
self.wam.machine_st.heap.copy_pstr_within(pstr_loc) self.wam.machine_st.heap.copy_pstr_within(pstr_loc)
} }
@@ -178,12 +178,12 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
} }
#[inline(always)] #[inline(always)]
fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, usize> { fn reserve(&mut self, num_cells: usize) -> Result<HeapWriter<'_>, AllocError> {
self.wam.machine_st.heap.reserve(num_cells) self.wam.machine_st.heap.reserve(num_cells)
} }
#[inline(always)] #[inline(always)]
fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), usize> { fn copy_slice_to_end(&mut self, bounds: Range<usize>) -> Result<(), AllocError> {
self.wam.machine_st.heap.copy_slice_to_end(bounds) self.wam.machine_st.heap.copy_slice_to_end(bounds)
} }
} }
@@ -581,56 +581,44 @@ mod tests {
}); });
assert_eq!( assert_eq!(
compare_term_test!(wam, wam.heap[0], wam.heap[1]), wam.compare_term_test(wam.heap[0], wam.heap[1]),
Some(Ordering::Less) Some(Ordering::Less)
); );
assert_eq!( assert_eq!(
compare_term_test!(wam, wam.heap[1], wam.heap[0]), wam.compare_term_test(wam.heap[1], wam.heap[0]),
Some(Ordering::Greater) Some(Ordering::Greater)
); );
assert_eq!( assert_eq!(
compare_term_test!(wam, wam.heap[0], wam.heap[0]), wam.compare_term_test(wam.heap[0], wam.heap[0]),
Some(Ordering::Equal) Some(Ordering::Equal)
); );
assert_eq!( assert_eq!(
compare_term_test!(wam, wam.heap[1], wam.heap[1]), wam.compare_term_test(wam.heap[1], wam.heap[1]),
Some(Ordering::Equal) Some(Ordering::Equal)
); );
let cstr_cell = wam.heap.allocate_cstr("string").unwrap(); let cstr_cell = wam.heap.allocate_cstr("string").unwrap();
assert_eq!( assert_eq!(
compare_term_test!(wam, atom_as_cell!(atom!("atom")), cstr_cell), wam.compare_term_test(atom_as_cell!(atom!("atom")), cstr_cell),
Some(Ordering::Less) Some(Ordering::Less)
); );
assert_eq!( assert_eq!(
compare_term_test!( wam.compare_term_test(atom_as_cell!(atom!("atom")), atom_as_cell!(atom!("atom"))),
wam,
atom_as_cell!(atom!("atom")),
atom_as_cell!(atom!("atom"))
),
Some(Ordering::Equal) Some(Ordering::Equal)
); );
assert_eq!( assert_eq!(
compare_term_test!( wam.compare_term_test(atom_as_cell!(atom!("atom")), atom_as_cell!(atom!("aaa"))),
wam,
atom_as_cell!(atom!("atom")),
atom_as_cell!(atom!("aaa"))
),
Some(Ordering::Greater) Some(Ordering::Greater)
); );
assert_eq!( assert_eq!(
compare_term_test!( wam.compare_term_test(fixnum_as_cell!(Fixnum::build_with(6)), heap_loc_as_cell!(1)),
wam,
fixnum_as_cell!(Fixnum::build_with(6)),
heap_loc_as_cell!(1)
),
Some(Ordering::Greater) Some(Ordering::Greater)
); );
@@ -644,12 +632,12 @@ mod tests {
}); });
assert_eq!( assert_eq!(
compare_term_test!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(0)), wam.compare_term_test(str_loc_as_cell!(0), str_loc_as_cell!(0)),
Some(Ordering::Equal) Some(Ordering::Equal)
); );
assert_eq!( assert_eq!(
compare_term_test!(wam, heap_loc_as_cell!(0), atom_as_cell!(atom!("a"))), wam.compare_term_test(str_loc_as_cell!(0), atom_as_cell!(atom!("a"))),
Some(Ordering::Greater) Some(Ordering::Greater)
); );
@@ -676,23 +664,22 @@ mod tests {
}); });
assert_eq!( assert_eq!(
compare_term_test!(wam, heap_loc_as_cell!(7), heap_loc_as_cell!(7)), wam.compare_term_test(heap_loc_as_cell!(7), heap_loc_as_cell!(7)),
Some(Ordering::Equal) Some(Ordering::Equal)
); );
assert_eq!( assert_eq!(
compare_term_test!(wam, heap_loc_as_cell!(0), heap_loc_as_cell!(7)), wam.compare_term_test(heap_loc_as_cell!(0), heap_loc_as_cell!(7)),
Some(Ordering::Greater) Some(Ordering::Greater)
); );
assert_eq!( assert_eq!(
compare_term_test!(wam, empty_list_as_cell!(), heap_loc_as_cell!(7)), wam.compare_term_test(empty_list_as_cell!(), heap_loc_as_cell!(7)),
Some(Ordering::Less) Some(Ordering::Less)
); );
assert_eq!( assert_eq!(
compare_term_test!( wam.compare_term_test(
wam,
empty_list_as_cell!(), empty_list_as_cell!(),
fixnum_as_cell!(Fixnum::build_with(1)) fixnum_as_cell!(Fixnum::build_with(1))
), ),
@@ -702,29 +689,29 @@ mod tests {
let cstr_cell = wam.heap.allocate_cstr("string").unwrap(); let cstr_cell = wam.heap.allocate_cstr("string").unwrap();
assert_eq!( assert_eq!(
compare_term_test!(wam, empty_list_as_cell!(), cstr_cell), wam.compare_term_test(empty_list_as_cell!(), cstr_cell),
Some(Ordering::Less) Some(Ordering::Less)
); );
assert_eq!( assert_eq!(
compare_term_test!(wam, empty_list_as_cell!(), atom_as_cell!(atom!("atom"))), wam.compare_term_test(empty_list_as_cell!(), atom_as_cell!(atom!("atom"))),
Some(Ordering::Less) Some(Ordering::Less)
); );
assert_eq!( assert_eq!(
compare_term_test!(wam, atom_as_cell!(atom!("atom")), empty_list_as_cell!()), wam.compare_term_test(atom_as_cell!(atom!("atom")), empty_list_as_cell!()),
Some(Ordering::Greater) Some(Ordering::Greater)
); );
let one_p_one = HeapCellValue::from(float_alloc!(1.1, &mut wam.arena)); let one_p_one = HeapCellValue::from(float_alloc!(1.1, &mut wam.arena));
assert_eq!( assert_eq!(
compare_term_test!(wam, one_p_one, fixnum_as_cell!(Fixnum::build_with(1))), wam.compare_term_test(one_p_one, fixnum_as_cell!(Fixnum::build_with(1))),
Some(Ordering::Less) Some(Ordering::Less)
); );
assert_eq!( assert_eq!(
compare_term_test!(wam, fixnum_as_cell!(Fixnum::build_with(1)), one_p_one), wam.compare_term_test(fixnum_as_cell!(Fixnum::build_with(1)), one_p_one),
Some(Ordering::Greater) Some(Ordering::Greater)
); );
} }

View File

@@ -38,6 +38,7 @@ use crate::instructions::*;
use crate::machine::args::*; use crate::machine::args::*;
use crate::machine::compile::*; use crate::machine::compile::*;
use crate::machine::copier::*; use crate::machine::copier::*;
use crate::machine::heap::AllocError;
use crate::machine::heap::*; use crate::machine::heap::*;
use crate::machine::loader::*; use crate::machine::loader::*;
use crate::machine::machine_errors::*; use crate::machine::machine_errors::*;
@@ -51,7 +52,6 @@ use crate::parser::dashu::{Integer, Rational};
use crate::types::*; use crate::types::*;
use indexmap::IndexMap; use indexmap::IndexMap;
use lazy_static::lazy_static;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use rand::rngs::StdRng; use rand::rngs::StdRng;
@@ -60,12 +60,11 @@ use std::cmp::Ordering;
use std::env; use std::env;
use std::io::Read; use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::atomic::AtomicBool; use std::process::ExitCode;
use std::sync::OnceLock; use std::sync::OnceLock;
use std::sync::atomic::AtomicBool;
lazy_static! { pub static INTERRUPT: AtomicBool = AtomicBool::new(false);
pub static ref INTERRUPT: AtomicBool = AtomicBool::new(false);
}
/// An instance of Scryer Prolog. /// An instance of Scryer Prolog.
/// ///
@@ -141,9 +140,8 @@ mod libraries {
} }
pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0; pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0;
pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1; pub static VERIFY_ATTR_INTERRUPT_LOC: usize = 1;
pub static VERIFY_ATTR_INTERRUPT_LOC: usize = 2; pub static LIB_QUERY_SUCCESS: usize = 2;
pub static LIB_QUERY_SUCCESS: usize = 3;
pub struct MachinePreludeView<'a> { pub struct MachinePreludeView<'a> {
pub indices: &'a mut IndexStore, pub indices: &'a mut IndexStore,
@@ -233,12 +231,7 @@ impl Machine {
/// Gets the current inference count. /// Gets the current inference count.
pub fn get_inference_count(&mut self) -> u64 { pub fn get_inference_count(&mut self) -> u64 {
self.machine_st self.machine_st.cwil.global_count.try_into().unwrap()
.cwil
.global_count
.clone()
.try_into()
.unwrap()
} }
/// Runs the predicate `key` in `module_name` until completion. /// Runs the predicate `key` in `module_name` until completion.
@@ -261,7 +254,9 @@ impl Machine {
let p = index_ptr.local().unwrap(); let p = index_ptr.local().unwrap();
// Leave a halting choice point to backtrack to in case the predicate fails or throws. // Leave a halting choice point to backtrack to in case the predicate fails or throws.
self.allocate_stub_choice_point(); if self.allocate_stub_choice_point().is_err() {
return ExitCode::FAILURE;
}
self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC;
self.machine_st.p = p; self.machine_st.p = p;
@@ -414,12 +409,11 @@ 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() + 3;
self.code.extend(vec![ self.code.extend(vec![
Instruction::BreakFromDispatchLoop, Instruction::BreakFromDispatchLoop,
Instruction::InstallVerifyAttr, Instruction::RunVerifyAttr,
Instruction::VerifyAttrInterrupt(0),
Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS Instruction::BreakFromDispatchLoop, // the location of LIB_QUERY_SUCCESS
Instruction::ExecuteTermGreaterThan, Instruction::ExecuteTermGreaterThan,
Instruction::ExecuteTermLessThan, Instruction::ExecuteTermLessThan,
@@ -729,10 +723,10 @@ impl Machine {
} }
#[inline(always)] #[inline(always)]
pub(super) fn try_me_else(&mut self, offset: usize) { pub(super) fn try_me_else(&mut self, offset: usize) -> Result<(), AllocError> {
if let Some(offset) = self.next_applicable_clause(offset) { if let Some(offset) = self.next_applicable_clause(offset) {
let n = self.machine_st.num_of_args; let n = self.machine_st.num_of_args;
let b = self.machine_st.stack.allocate_or_frame(n); let b = self.machine_st.stack.allocate_or_frame(n)?;
let or_frame = self.machine_st.stack.index_or_frame_mut(b); let or_frame = self.machine_st.stack.index_or_frame_mut(b);
or_frame.prelude.num_cells = n; or_frame.prelude.num_cells = n;
@@ -758,13 +752,15 @@ impl Machine {
} }
self.machine_st.p += 1; self.machine_st.p += 1;
Ok(())
} }
#[inline(always)] #[inline(always)]
pub(super) fn indexed_try(&mut self, offset: usize) { pub(super) fn indexed_try(&mut self, offset: usize) -> Result<(), AllocError> {
if let Some(iip_offset) = self.next_inner_applicable_clause() { if let Some(iip_offset) = self.next_inner_applicable_clause() {
let n = self.machine_st.num_of_args; let n = self.machine_st.num_of_args;
let b = self.machine_st.stack.allocate_or_frame(n); let b = self.machine_st.stack.allocate_or_frame(n)?;
let or_frame = self.machine_st.stack.index_or_frame_mut(b); let or_frame = self.machine_st.stack.index_or_frame_mut(b);
or_frame.prelude.num_cells = n; or_frame.prelude.num_cells = n;
@@ -793,6 +789,7 @@ impl Machine {
} }
self.machine_st.p += offset; self.machine_st.p += offset;
Ok(())
} }
#[inline(always)] #[inline(always)]
@@ -1227,7 +1224,7 @@ impl Machine {
let key = Atom::from(h as u64); let key = Atom::from(h as u64);
match self.indices.global_variables.get_mut(&key) { match self.indices.global_variables.get_mut(&key) {
Some((_, ref mut loc)) => *loc = None, Some((_, loc)) => *loc = None,
None => unreachable!(), None => unreachable!(),
} }
} }
@@ -1236,7 +1233,7 @@ impl Machine {
let value_cell = HeapCellValue::from(u64::from(self.machine_st.trail[i + 1])); let value_cell = HeapCellValue::from(u64::from(self.machine_st.trail[i + 1]));
match self.indices.global_variables.get_mut(&key) { match self.indices.global_variables.get_mut(&key) {
Some((_, ref mut loc)) => *loc = Some(value_cell), Some((_, loc)) => *loc = Some(value_cell),
None => unreachable!(), None => unreachable!(),
} }
} }

View File

@@ -5,13 +5,42 @@ use crate::machine::machine_errors::CycleSearchResult;
use crate::machine::system_calls::BrentAlgState; use crate::machine::system_calls::BrentAlgState;
use crate::types::*; use crate::types::*;
trait StepperImpl {
fn step<'a>(&self, iter: &mut HeapPStrIter<'a>) -> Option<PStrIteratee>;
fn is_cyclic(&self) -> bool;
}
struct PreCycleDiscoverStepper;
impl StepperImpl for PreCycleDiscoverStepper {
fn step<'a>(&self, iter: &mut HeapPStrIter<'a>) -> Option<PStrIteratee> {
iter.pre_cycle_discovery_stepper()
}
fn is_cyclic(&self) -> bool {
false
}
}
struct PostCycleDiscoverStepper;
impl StepperImpl for PostCycleDiscoverStepper {
fn step<'a>(&self, iter: &mut HeapPStrIter<'a>) -> Option<PStrIteratee> {
iter.post_cycle_discovery_stepper()
}
fn is_cyclic(&self) -> bool {
true
}
}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct HeapPStrIter<'a> { pub struct HeapPStrIter<'a> {
pub heap: &'a Heap, pub heap: &'a Heap,
// pub focus: HeapCellValue, // pub focus: HeapCellValue,
orig_focus: usize, orig_focus: usize,
brent_st: BrentAlgState, brent_st: BrentAlgState,
stepper: fn(&mut HeapPStrIter<'a>) -> Option<PStrIteratee>, stepper: &'static dyn StepperImpl,
} }
struct PStrIterStep { struct PStrIterStep {
@@ -33,7 +62,7 @@ impl<'a> HeapPStrIter<'a> {
heap, heap,
orig_focus, orig_focus,
brent_st: BrentAlgState::new(orig_focus), brent_st: BrentAlgState::new(orig_focus),
stepper: HeapPStrIter::pre_cycle_discovery_stepper, stepper: &PreCycleDiscoverStepper,
} }
} }
@@ -161,7 +190,7 @@ impl<'a> HeapPStrIter<'a> {
debug_assert!(matches!(cycle_result, CycleSearchResult::Cyclic { .. })); debug_assert!(matches!(cycle_result, CycleSearchResult::Cyclic { .. }));
self.walk_hare_to_cycle_end(); self.walk_hare_to_cycle_end();
self.stepper = HeapPStrIter::post_cycle_discovery_stepper; self.stepper = &PostCycleDiscoverStepper;
} }
None => { None => {
// self.focus = self.heap[next_hare]; // self.focus = self.heap[next_hare];
@@ -194,7 +223,7 @@ impl<'a> HeapPStrIter<'a> {
} }
pub(crate) fn is_cyclic(&self) -> bool { pub(crate) fn is_cyclic(&self) -> bool {
self.stepper as usize == Self::post_cycle_discovery_stepper as usize self.stepper.is_cyclic()
} }
} }
@@ -203,7 +232,7 @@ impl<'a> Iterator for HeapPStrIter<'a> {
#[inline(always)] #[inline(always)]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
(self.stepper)(self) self.stepper.step(self)
} }
} }
@@ -217,6 +246,8 @@ mod test {
fn pstr_iter_tests() { fn pstr_iter_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
let init_len = wam.machine_st.heap.cell_len();
let pstr_cell = wam.machine_st.heap.allocate_pstr("abc ").unwrap(); let pstr_cell = wam.machine_st.heap.allocate_pstr("abc ").unwrap();
wam.machine_st wam.machine_st
.heap .heap
@@ -233,7 +264,7 @@ mod test {
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(1), slice_loc: heap_index!(init_len),
slice_len: "abc ".len() slice_len: "abc ".len()
}), }),
); );
@@ -241,9 +272,9 @@ mod test {
assert!(!iter.is_cyclic()); assert!(!iter.is_cyclic());
} }
assert_eq!(wam.machine_st.heap[2], empty_list_as_cell!()); assert_eq!(wam.machine_st.heap[init_len + 1], empty_list_as_cell!());
wam.machine_st.heap[2] = pstr_loc_as_cell!(heap_index!(3)); wam.machine_st.heap[init_len + 1] = pstr_loc_as_cell!(heap_index!(init_len + 2));
wam.machine_st.heap.allocate_pstr("def").unwrap(); wam.machine_st.heap.allocate_pstr("def").unwrap();
let h = wam.machine_st.heap.cell_len(); let h = wam.machine_st.heap.cell_len();
@@ -256,14 +287,14 @@ mod test {
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(1), slice_loc: heap_index!(init_len),
slice_len: "abc ".len() slice_len: "abc ".len()
}) })
); );
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(3), slice_loc: heap_index!(init_len + 2),
slice_len: "def".len(), slice_len: "def".len(),
}) })
); );
@@ -282,14 +313,14 @@ mod test {
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(1), slice_loc: heap_index!(init_len),
slice_len: "abc ".len() slice_len: "abc ".len()
}) })
); );
assert_eq!( assert_eq!(
iter.next(), iter.next(),
Some(PStrIteratee::PStrSlice { Some(PStrIteratee::PStrSlice {
slice_loc: heap_index!(3), slice_loc: heap_index!(init_len + 2),
slice_len: "def".len(), slice_len: "def".len(),
}) })
); );
@@ -298,7 +329,7 @@ mod test {
assert!(!iter.is_cyclic()); assert!(!iter.is_cyclic());
} }
wam.machine_st.heap[h] = pstr_loc_as_cell!(heap_index!(3)); wam.machine_st.heap[h] = pstr_loc_as_cell!(heap_index!(init_len + 2));
{ {
let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0); let mut iter = HeapPStrIter::new(&wam.machine_st.heap, 0);

View File

@@ -37,7 +37,7 @@ fn setup_op_decl(mut terms: Vec<Term>) -> Result<OpDecl, CompilationError> {
other => { other => {
return Err(CompilationError::InvalidDirective( return Err(CompilationError::InvalidDirective(
DirectiveError::InvalidOpDeclSpecDomain(other), DirectiveError::InvalidOpDeclSpecDomain(other),
)) ));
} }
}; };
@@ -82,7 +82,7 @@ fn setup_op_decl(mut terms: Vec<Term>) -> Result<OpDecl, CompilationError> {
fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, CompilationError> { fn setup_predicate_indicator(term: &mut Term) -> Result<PredicateKey, CompilationError> {
match term { match term {
Term::Clause(_, slash, ref mut terms) Term::Clause(_, slash, terms)
if (*slash == atom!("/") || *slash == atom!("//")) && terms.len() == 2 => if (*slash == atom!("/") || *slash == atom!("//")) && terms.len() == 2 =>
{ {
let arity = terms.pop().unwrap(); let arity = terms.pop().unwrap();

View File

@@ -1,111 +0,0 @@
use core::marker::PhantomData;
use std::alloc;
use std::mem;
use std::ptr;
pub(crate) trait RawBlockTraits {
fn init_size() -> usize;
fn align() -> usize;
#[inline]
fn base_offset(base: *const u8) -> *const u8 {
base
}
}
#[derive(Debug)]
pub(crate) struct RawBlock<T: RawBlockTraits> {
pub(crate) size: usize,
pub(crate) base: *const u8,
pub(crate) top: *const u8,
_marker: PhantomData<T>,
}
impl<T: RawBlockTraits> RawBlock<T> {
pub(crate) fn new() -> Self {
let mut block = RawBlock {
size: 0,
base: ptr::null(),
top: ptr::null(),
_marker: PhantomData,
};
unsafe {
block.grow();
}
block
}
unsafe fn init_at_size(&mut self, cap: usize) {
let layout = alloc::Layout::from_size_align_unchecked(cap, T::align());
self.base = alloc::alloc(layout) as *const _;
self.size = cap;
self.top = T::base_offset(self.base);
}
pub(super) unsafe fn grow(&mut self) {
if self.size == 0 {
self.init_at_size(T::init_size());
} else {
let layout = alloc::Layout::from_size_align_unchecked(T::init_size(), T::align());
let top_dist = self.top as usize - self.base as usize;
self.base = alloc::realloc(self.base as *mut _, layout, self.size * 2) as *const _;
self.top = (self.base as usize + top_dist) as *const _;
self.size *= 2;
}
}
fn empty_block() -> Self {
RawBlock {
size: 0,
base: ptr::null(),
top: ptr::null(),
_marker: PhantomData,
}
}
#[inline]
pub(crate) fn take(&mut self) -> Self {
mem::replace(self, Self::empty_block())
}
#[inline]
fn free_space(&self) -> usize {
debug_assert!(
self.top >= self.base,
"self.top = {:?} < {:?} = self.base",
self.top,
self.base
);
self.size - (self.top as usize - self.base as usize)
}
#[inline]
pub(crate) unsafe fn new_block(&mut self, size: usize) -> *const u8 {
loop {
if self.free_space() >= size {
return (self.top as usize + size) as *const _;
} else {
self.grow();
}
}
}
pub(crate) fn deallocate(&mut self) {
unsafe {
let layout = alloc::Layout::from_size_align_unchecked(self.size, T::align());
alloc::dealloc(self.base as *mut u8, layout);
self.top = ptr::null();
self.base = ptr::null();
self.size = 0;
}
}
}

View File

@@ -1,5 +1,7 @@
use core::marker::PhantomData; use core::marker::PhantomData;
use std::ptr::NonNull;
use crate::machine::heap::AllocError;
use crate::raw_block::*; use crate::raw_block::*;
use crate::types::*; use crate::types::*;
@@ -87,14 +89,20 @@ impl Index<usize> for Stack {
#[inline] #[inline]
fn index(&self, index: usize) -> &Self::Output { fn index(&self, index: usize) -> &Self::Output {
unsafe { &*self.buf.base.add(index).cast() } unsafe {
let ptr = self.buf.get_unchecked(index);
&*ptr.cast::<HeapCellValue>()
}
} }
} }
impl IndexMut<usize> for Stack { impl IndexMut<usize> for Stack {
#[inline] #[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output { fn index_mut(&mut self, index: usize) -> &mut Self::Output {
unsafe { &mut *self.buf.base.add(index).cast_mut().cast() } unsafe {
let ptr = self.buf.get_unchecked(index);
&mut *ptr.cast_mut().cast::<HeapCellValue>()
}
} }
} }
@@ -159,41 +167,39 @@ impl OrFrame {
} }
impl Stack { impl Stack {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Result<Self, AllocError> {
Stack { Ok(Stack {
buf: RawBlock::new(), buf: RawBlock::new()?,
_marker: PhantomData, _marker: PhantomData,
} })
} }
#[inline(always)] #[inline(always)]
unsafe fn alloc(&mut self, frame_size: usize) -> *mut u8 { unsafe fn alloc(&mut self, frame_size: usize) -> Result<NonNull<u8>, AllocError> {
loop { loop {
unsafe {
let ptr = self.buf.alloc(frame_size); let ptr = self.buf.alloc(frame_size);
if let Some(ptr) = NonNull::new(ptr) {
if ptr.is_null() { return Ok(ptr);
if !self.buf.grow() {
panic!("growing the stack failed")
} }
} else { self.buf.grow()?;
return ptr;
} }
} }
} }
pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> usize { pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> Result<usize, AllocError> {
let frame_size = AndFrame::size_of(num_cells); let frame_size = AndFrame::size_of(num_cells);
unsafe { unsafe {
let e = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); let e = self.buf.used_bytes();
let new_ptr = self.alloc(frame_size); let new_ptr = self.alloc(frame_size)?;
let mut offset = prelude_size::<AndFramePrelude>(); let mut offset = prelude_size::<AndFramePrelude>();
for idx in 0..num_cells { for idx in 0..num_cells {
let cell_ptr = new_ptr.add(offset) as *mut HeapCellValue; let cell_ptr = new_ptr.add(offset).cast::<HeapCellValue>();
ptr::write(cell_ptr, stack_loc_as_cell!(AndFrame, e, idx + 1)); ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(AndFrame, e, idx + 1));
// Because in the Index and IndexMut inplementations we need to get this from // Because in the Index and IndexMut implementations we need to get this from
// exposed provenance, we need to expose the provenance here, even though we don't // exposed provenance, we need to expose the provenance here, even though we don't
// actually use the value for anything. This is a reminder that `expose_provenance` // actually use the value for anything. This is a reminder that `expose_provenance`
// isn't just a cast from a pointer to an integer but has actual side effects. // isn't just a cast from a pointer to an integer but has actual side effects.
@@ -205,27 +211,27 @@ impl Stack {
let and_frame = self.index_and_frame_mut(e); let and_frame = self.index_and_frame_mut(e);
and_frame.prelude.num_cells = num_cells; and_frame.prelude.num_cells = num_cells;
e Ok(e)
} }
} }
pub(crate) fn top(&self) -> usize { pub(crate) fn top(&self) -> usize {
unsafe { (*self.buf.ptr.get()).addr() - self.buf.base.addr() } self.buf.used_bytes()
} }
pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> usize { pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> Result<usize, AllocError> {
let frame_size = OrFrame::size_of(num_cells); let frame_size = OrFrame::size_of(num_cells);
unsafe { unsafe {
let b = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); let b = self.buf.used_bytes();
let new_ptr = self.alloc(frame_size); let new_ptr = self.alloc(frame_size)?;
let mut offset = prelude_size::<OrFramePrelude>(); let mut offset = prelude_size::<OrFramePrelude>();
for idx in 0..num_cells { for idx in 0..num_cells {
let cell_ptr = new_ptr.byte_add(offset) as *mut HeapCellValue; let cell_ptr = new_ptr.byte_add(offset).cast::<HeapCellValue>();
ptr::write(cell_ptr, stack_loc_as_cell!(OrFrame, b, idx)); ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(OrFrame, b, idx));
// Because in the Index and IndexMut inplementations we need to get this from // Because in the Index and IndexMut implementations we need to get this from
// exposed provenance, we need to expose the provenance here, even though we don't // exposed provenance, we need to expose the provenance here, even though we don't
// actually use the value for anything. This is a reminder that `expose_provenance` // actually use the value for anything. This is a reminder that `expose_provenance`
// isn't just a cast from a pointer to an integer but has actual side effects. // isn't just a cast from a pointer to an integer but has actual side effects.
@@ -237,41 +243,61 @@ impl Stack {
let or_frame = self.index_or_frame_mut(b); let or_frame = self.index_or_frame_mut(b);
or_frame.prelude.num_cells = num_cells; or_frame.prelude.num_cells = num_cells;
b Ok(b)
} }
} }
fn get_raw(&self, index: usize) -> *const u8 {
debug_assert!(index < self.buf.used_bytes());
unsafe { self.buf.get_unchecked(index) }
}
#[inline(always)] #[inline(always)]
pub(crate) fn index_and_frame(&self, e: usize) -> &AndFrame { pub(crate) fn index_and_frame(&self, e: usize) -> &AndFrame {
unsafe { &*self.buf.base.add(e).cast() } let ptr = self.get_raw(e);
unsafe { &*ptr.cast::<AndFrame>() }
} }
#[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 { let ptr = self.get_raw(e);
// This is doing alignment wrong
let ptr = self.buf.base.add(e); unsafe { &mut *ptr.cast_mut().cast::<AndFrame>() }
&mut *(ptr as *mut AndFrame)
}
} }
#[inline(always)] #[inline(always)]
pub(crate) fn index_or_frame(&self, b: usize) -> &OrFrame { pub(crate) fn index_or_frame(&self, b: usize) -> &OrFrame {
unsafe { &*self.buf.base.add(b).cast() } let ptr = self.get_raw(b);
unsafe { &*ptr.cast::<OrFrame>() }
} }
#[inline(always)] #[inline(always)]
pub(crate) fn index_or_frame_mut(&mut self, b: usize) -> &mut OrFrame { pub(crate) fn index_or_frame_mut(&mut self, b: usize) -> &mut OrFrame {
unsafe { &mut *self.buf.base.add(b).cast_mut().cast() } let ptr = self.get_raw(b);
unsafe { &mut *ptr.cast_mut().cast::<OrFrame>() }
}
/// # Safety
///
/// The stack must contain a valid OrFrame at [`self.top()`](Self::top),
/// which can only be achieved by allocating it in the first place and later truncating the stack.
///
/// No allocation must have been done since the last call to [`truncate()`](Self::truncate).
#[inline(always)]
pub(crate) unsafe fn index_dangling_or_frame(&self) -> &OrFrame {
unsafe {
let ptr = self.buf.get_unchecked(self.top());
&*ptr.cast::<OrFrame>()
}
} }
#[inline(always)] #[inline(always)]
pub(crate) fn truncate(&mut self, b: usize) { pub(crate) fn truncate(&mut self, b: usize) {
let base = unsafe { self.buf.base.add(b) }; self.buf.shift_back(b);
if base < (*self.buf.ptr.get_mut()) {
*self.buf.ptr.get_mut() = base.cast_mut();
}
} }
} }
@@ -285,7 +311,7 @@ mod tests {
fn stack_tests() { fn stack_tests() {
let mut wam = MockWAM::new(); let mut wam = MockWAM::new();
let e = wam.machine_st.stack.allocate_and_frame(10); // create an AND frame! let e = wam.machine_st.stack.allocate_and_frame(10).unwrap(); // create an AND frame!
let and_frame = wam.machine_st.stack.index_and_frame_mut(e); let and_frame = wam.machine_st.stack.index_and_frame_mut(e);
assert_eq!( assert_eq!(
@@ -303,7 +329,7 @@ mod tests {
assert_eq!(and_frame[5], empty_list_as_cell!()); assert_eq!(and_frame[5], empty_list_as_cell!());
let b = wam.machine_st.stack.allocate_or_frame(5); let b = wam.machine_st.stack.allocate_or_frame(5).unwrap();
let or_frame = wam.machine_st.stack.index_or_frame_mut(b); let or_frame = wam.machine_st.stack.index_or_frame_mut(b);
@@ -311,7 +337,7 @@ mod tests {
assert_eq!(or_frame[idx], stack_loc_as_cell!(OrFrame, b, idx)); assert_eq!(or_frame[idx], stack_loc_as_cell!(OrFrame, b, idx));
} }
let next_e = wam.machine_st.stack.allocate_and_frame(9); // create an AND frame! let next_e = wam.machine_st.stack.allocate_and_frame(9).unwrap(); // create an AND frame!
let and_frame = wam.machine_st.stack.index_and_frame_mut(next_e); let and_frame = wam.machine_st.stack.index_and_frame_mut(next_e);
for idx in 0..9 { for idx in 0..9 {

View File

@@ -12,10 +12,10 @@ use crate::machine::machine_indices::*;
use crate::machine::machine_state::*; use crate::machine::machine_state::*;
use crate::types::*; use crate::types::*;
pub use scryer_modular_bitfield::prelude::*; pub use modular_bitfield::prelude::*;
#[cfg(feature = "http")] #[cfg(feature = "http")]
use bytes::{buf::Reader as BufReader, Buf, Bytes}; use bytes::{Buf, Bytes, buf::Reader as BufReader};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
@@ -23,6 +23,8 @@ 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::PipeReader;
use std::io::PipeWriter;
use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write}; use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::net::{Shutdown, TcpStream}; use std::net::{Shutdown, TcpStream};
@@ -38,10 +40,7 @@ use native_tls::TlsStream;
#[cfg(feature = "http")] #[cfg(feature = "http")]
use warp::hyper; use warp::hyper;
mod compat; #[derive(Debug, Specifier, Clone, Copy, PartialEq, Eq, Hash)]
pub use compat::*;
#[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)]
#[bits = 1] #[bits = 1]
pub enum StreamType { pub enum StreamType {
Binary, Binary,
@@ -74,7 +73,7 @@ impl StreamType {
} }
} }
#[derive(Debug, BitfieldSpecifier, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Specifier, Clone, Copy, PartialEq, Eq, Hash)]
#[bits = 2] #[bits = 2]
pub enum EOFAction { pub enum EOFAction {
EOFCode, EOFCode,
@@ -82,7 +81,7 @@ pub enum EOFAction {
Reset, Reset,
} }
#[derive(Debug, BitfieldSpecifier, Copy, Clone, PartialEq)] #[derive(Debug, Specifier, Copy, Clone, PartialEq)]
#[bits = 2] #[bits = 2]
pub(crate) enum AtEndOfStream { pub(crate) enum AtEndOfStream {
Not, Not,
@@ -328,10 +327,7 @@ impl Write for HttpWriteStream {
} }
#[cfg(feature = "http")] #[cfg(feature = "http")]
impl HttpWriteStream { impl Drop for HttpWriteStream {
// TODO why is this suddenly dead code and should it be used somewhere?
// Should this be impl Drop for HttpWriteStream?
#[allow(dead_code)]
fn drop(&mut self) { fn drop(&mut self) {
let headers = unsafe { std::mem::ManuallyDrop::take(&mut self.headers) }; let headers = unsafe { std::mem::ManuallyDrop::take(&mut self.headers) };
let buffer = unsafe { std::mem::ManuallyDrop::take(&mut self.buffer) }; let buffer = unsafe { std::mem::ManuallyDrop::take(&mut self.buffer) };
@@ -660,6 +656,18 @@ impl Stream {
#[inline] #[inline]
pub fn stdin(arena: &mut Arena, add_history: bool) -> Stream { pub fn stdin(arena: &mut Arena, add_history: bool) -> Stream {
#[cfg(unix)]
use std::io::IsTerminal;
#[cfg(unix)]
if !std::io::stdin().is_terminal() {
use std::os::unix::io::{FromRawFd, RawFd};
// dup fd 0 so the File can be owned (and closed later) without
// closing the real stdin.
let fd = unsafe { libc::dup(0 as RawFd) };
let file = unsafe { File::from_raw_fd(fd) };
return Stream::from_file_as_input(atom!("user_input"), file, arena);
}
Stream::Readline(arena_alloc!( Stream::Readline(arena_alloc!(
StreamLayout::new(ReadlineStream::new("", add_history)), StreamLayout::new(ReadlineStream::new("", add_history)),
arena arena
@@ -742,49 +750,49 @@ impl Stream {
pub fn options(&self) -> &StreamOptions { pub fn options(&self) -> &StreamOptions {
match self { match self {
Stream::Byte(ref ptr) => &ptr.options, Stream::Byte(ptr) => &ptr.options,
Stream::InputFile(ref ptr) => &ptr.options, Stream::InputFile(ptr) => &ptr.options,
Stream::OutputFile(ref ptr) => &ptr.options, Stream::OutputFile(ptr) => &ptr.options,
Stream::StaticString(ref ptr) => &ptr.options, Stream::StaticString(ptr) => &ptr.options,
Stream::NamedTcp(ref ptr) => &ptr.options, Stream::NamedTcp(ptr) => &ptr.options,
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
Stream::NamedTls(ref ptr) => &ptr.options, Stream::NamedTls(ptr) => &ptr.options,
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpRead(ref ptr) => &ptr.options, Stream::HttpRead(ptr) => &ptr.options,
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpWrite(ref ptr) => &ptr.options, Stream::HttpWrite(ptr) => &ptr.options,
Stream::Null(ref options) => options, Stream::Null(options) => options,
Stream::Readline(ref ptr) => &ptr.options, Stream::Readline(ptr) => &ptr.options,
Stream::StandardOutput(ref ptr) => &ptr.options, Stream::StandardOutput(ptr) => &ptr.options,
Stream::StandardError(ref ptr) => &ptr.options, Stream::StandardError(ptr) => &ptr.options,
Stream::Callback(ref ptr) => &ptr.options, Stream::Callback(ptr) => &ptr.options,
Stream::InputChannel(ref ptr) => &ptr.options, Stream::InputChannel(ptr) => &ptr.options,
Stream::PipeReader(ref ptr) => &ptr.options, Stream::PipeReader(ptr) => &ptr.options,
Stream::PipeWriter(ref ptr) => &ptr.options, Stream::PipeWriter(ptr) => &ptr.options,
} }
} }
pub(super) fn options_mut(&mut self) -> &mut StreamOptions { pub(super) fn options_mut(&mut self) -> &mut StreamOptions {
match self { match self {
Stream::Byte(ref mut ptr) => &mut ptr.options, Stream::Byte(ptr) => &mut ptr.options,
Stream::InputFile(ref mut ptr) => &mut ptr.options, Stream::InputFile(ptr) => &mut ptr.options,
Stream::OutputFile(ref mut ptr) => &mut ptr.options, Stream::OutputFile(ptr) => &mut ptr.options,
Stream::StaticString(ref mut ptr) => &mut ptr.options, Stream::StaticString(ptr) => &mut ptr.options,
Stream::NamedTcp(ref mut ptr) => &mut ptr.options, Stream::NamedTcp(ptr) => &mut ptr.options,
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
Stream::NamedTls(ref mut ptr) => &mut ptr.options, Stream::NamedTls(ptr) => &mut ptr.options,
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpRead(ref mut ptr) => &mut ptr.options, Stream::HttpRead(ptr) => &mut ptr.options,
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpWrite(ref mut ptr) => &mut ptr.options, Stream::HttpWrite(ptr) => &mut ptr.options,
Stream::Null(ref mut options) => options, Stream::Null(options) => options,
Stream::Readline(ref mut ptr) => &mut ptr.options, Stream::Readline(ptr) => &mut ptr.options,
Stream::StandardOutput(ref mut ptr) => &mut ptr.options, Stream::StandardOutput(ptr) => &mut ptr.options,
Stream::StandardError(ref mut ptr) => &mut ptr.options, Stream::StandardError(ptr) => &mut ptr.options,
Stream::Callback(ref mut ptr) => &mut ptr.options, Stream::Callback(ptr) => &mut ptr.options,
Stream::InputChannel(ref mut ptr) => &mut ptr.options, Stream::InputChannel(ptr) => &mut ptr.options,
Stream::PipeReader(ref mut ptr) => &mut ptr.options, Stream::PipeReader(ptr) => &mut ptr.options,
Stream::PipeWriter(ref mut ptr) => &mut ptr.options, Stream::PipeWriter(ptr) => &mut ptr.options,
} }
} }
@@ -952,17 +960,17 @@ impl CharRead for Stream {
fn consume(&mut self, nread: usize) { fn consume(&mut self, nread: usize) {
match self { match self {
Stream::InputFile(ref mut file) => file.consume(nread), Stream::InputFile(file) => file.consume(nread),
Stream::NamedTcp(ref mut tcp_stream) => tcp_stream.consume(nread), Stream::NamedTcp(tcp_stream) => tcp_stream.consume(nread),
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
Stream::NamedTls(ref mut tls_stream) => tls_stream.consume(nread), Stream::NamedTls(tls_stream) => tls_stream.consume(nread),
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpRead(ref mut http_stream) => http_stream.consume(nread), Stream::HttpRead(http_stream) => http_stream.consume(nread),
Stream::Readline(ref mut rl_stream) => rl_stream.consume(nread), Stream::Readline(rl_stream) => rl_stream.consume(nread),
Stream::StaticString(ref mut src) => src.consume(nread), Stream::StaticString(src) => src.consume(nread),
Stream::Byte(ref mut cursor) => cursor.consume(nread), Stream::Byte(cursor) => cursor.consume(nread),
Stream::InputChannel(ref mut cursor) => cursor.consume(nread), Stream::InputChannel(cursor) => cursor.consume(nread),
Stream::PipeReader(ref mut cursor) => cursor.consume(nread), Stream::PipeReader(cursor) => cursor.consume(nread),
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpWrite(_) => {} Stream::HttpWrite(_) => {}
Stream::OutputFile(_) Stream::OutputFile(_)
@@ -1011,17 +1019,17 @@ impl Read for Stream {
impl Write for Stream { impl Write for Stream {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
match self { match self {
Stream::OutputFile(ref mut file) => file.write(buf), Stream::OutputFile(file) => file.write(buf),
Stream::NamedTcp(ref mut tcp_stream) => tcp_stream.get_mut().write(buf), Stream::NamedTcp(tcp_stream) => tcp_stream.get_mut().write(buf),
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
Stream::NamedTls(ref mut tls_stream) => tls_stream.get_mut().write(buf), Stream::NamedTls(tls_stream) => tls_stream.get_mut().write(buf),
Stream::Byte(ref mut cursor) => cursor.get_mut().write(buf), Stream::Byte(cursor) => cursor.get_mut().write(buf),
Stream::Callback(ref mut callback_stream) => callback_stream.get_mut().write(buf), Stream::Callback(callback_stream) => callback_stream.get_mut().write(buf),
Stream::StandardOutput(stream) => stream.write(buf), Stream::StandardOutput(stream) => stream.write(buf),
Stream::StandardError(stream) => stream.write(buf), Stream::StandardError(stream) => stream.write(buf),
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpWrite(ref mut stream) => stream.get_mut().write(buf), Stream::HttpWrite(stream) => stream.get_mut().write(buf),
Stream::PipeWriter(ref mut stream) => stream.get_mut().write(buf), Stream::PipeWriter(stream) => stream.get_mut().write(buf),
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpRead(_) => Err(std::io::Error::new( Stream::HttpRead(_) => Err(std::io::Error::new(
ErrorKind::PermissionDenied, ErrorKind::PermissionDenied,
@@ -1041,17 +1049,17 @@ impl Write for Stream {
fn flush(&mut self) -> std::io::Result<()> { fn flush(&mut self) -> std::io::Result<()> {
match self { match self {
Stream::OutputFile(ref mut file) => file.stream.flush(), Stream::OutputFile(file) => file.stream.flush(),
Stream::NamedTcp(ref mut tcp_stream) => tcp_stream.stream.get_mut().flush(), Stream::NamedTcp(tcp_stream) => tcp_stream.stream.get_mut().flush(),
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
Stream::NamedTls(ref mut tls_stream) => tls_stream.stream.get_mut().flush(), Stream::NamedTls(tls_stream) => tls_stream.stream.get_mut().flush(),
Stream::Byte(ref mut cursor) => cursor.stream.get_mut().flush(), Stream::Byte(cursor) => cursor.stream.get_mut().flush(),
Stream::Callback(ref mut callback_stream) => callback_stream.stream.get_mut().flush(), Stream::Callback(callback_stream) => callback_stream.stream.get_mut().flush(),
Stream::StandardError(stream) => stream.stream.flush(), Stream::StandardError(stream) => stream.stream.flush(),
Stream::StandardOutput(stream) => stream.stream.flush(), Stream::StandardOutput(stream) => stream.stream.flush(),
Stream::PipeWriter(ref mut stream) => stream.stream.get_mut().flush(), Stream::PipeWriter(stream) => stream.stream.get_mut().flush(),
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpWrite(ref mut stream) => stream.stream.get_mut().flush(), Stream::HttpWrite(stream) => stream.stream.get_mut().flush(),
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpRead(_) => Err(std::io::Error::new( Stream::HttpRead(_) => Err(std::io::Error::new(
ErrorKind::PermissionDenied, ErrorKind::PermissionDenied,
@@ -1522,58 +1530,58 @@ impl Stream {
#[inline] #[inline]
pub(crate) fn close(&mut self) -> Result<(), std::io::Error> { pub(crate) fn close(&mut self) -> Result<(), std::io::Error> {
match self { match self {
Stream::NamedTcp(ref mut tcp_stream) => { Stream::NamedTcp(tcp_stream) => {
tcp_stream.inner_mut().tcp_stream.shutdown(Shutdown::Both) tcp_stream.inner_mut().tcp_stream.shutdown(Shutdown::Both)
} }
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
Stream::NamedTls(ref mut tls_stream) => tls_stream.inner_mut().tls_stream.shutdown(), Stream::NamedTls(tls_stream) => tls_stream.inner_mut().tls_stream.shutdown(),
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpRead(ref mut http_stream) => { Stream::HttpRead(http_stream) => {
http_stream.drop_payload(); http_stream.drop_payload();
Ok(()) Ok(())
} }
#[cfg(feature = "http")] #[cfg(feature = "http")]
Stream::HttpWrite(mut http_stream) => { &mut Stream::HttpWrite(mut http_stream) => {
http_stream.drop_payload(); http_stream.drop_payload();
Ok(()) Ok(())
} }
Stream::InputFile(mut file_stream) => { &mut Stream::InputFile(mut file_stream) => {
// close the stream by dropping the inner File. // close the stream by dropping the inner File.
file_stream.drop_payload(); file_stream.drop_payload();
Ok(()) Ok(())
} }
Stream::OutputFile(mut file_stream) => { &mut Stream::OutputFile(mut file_stream) => {
// close the stream by dropping the inner File. // close the stream by dropping the inner File.
file_stream.drop_payload(); file_stream.drop_payload();
Ok(()) Ok(())
} }
Stream::Byte(mut stream) => { &mut Stream::Byte(mut stream) => {
stream.drop_payload(); stream.drop_payload();
Ok(()) Ok(())
} }
Stream::Callback(mut stream) => { &mut Stream::Callback(mut stream) => {
stream.drop_payload(); stream.drop_payload();
Ok(()) Ok(())
} }
Stream::InputChannel(mut stream) => { &mut Stream::InputChannel(mut stream) => {
stream.drop_payload(); stream.drop_payload();
Ok(()) Ok(())
} }
Stream::StaticString(mut stream) => { &mut Stream::StaticString(mut stream) => {
stream.drop_payload(); stream.drop_payload();
Ok(()) Ok(())
} }
Stream::PipeReader(mut stream) => { &mut Stream::PipeReader(mut stream) => {
stream.drop_payload(); stream.drop_payload();
Ok(()) Ok(())
} }
Stream::PipeWriter(mut stream) => { &mut Stream::PipeWriter(mut stream) => {
stream.drop_payload(); stream.drop_payload();
Ok(()) Ok(())
} }
@@ -1636,11 +1644,11 @@ impl Stream {
self.set_past_end_of_stream(false); self.set_past_end_of_stream(false);
match self { match self {
Stream::Byte(ref mut cursor) => { Stream::Byte(cursor) => {
cursor.stream.get_mut().0.set_position(0); cursor.stream.get_mut().0.set_position(0);
true true
} }
Stream::InputFile(ref mut file_stream) => { Stream::InputFile(file_stream) => {
file_stream file_stream
.stream .stream
.get_mut() .get_mut()
@@ -1649,11 +1657,11 @@ impl Stream {
.unwrap(); .unwrap();
true true
} }
Stream::Readline(ref mut readline_stream) => { Stream::Readline(readline_stream) => {
readline_stream.reset(); readline_stream.reset();
true true
} }
Stream::InputChannel(ref mut input_channel_stream) => { Stream::InputChannel(input_channel_stream) => {
input_channel_stream.stream.get_mut().inner.set_position(0); input_channel_stream.stream.get_mut().inner.set_position(0);
true true
} }
@@ -1664,7 +1672,7 @@ impl Stream {
#[inline] #[inline]
pub(crate) fn peek_byte(&mut self) -> std::io::Result<u8> { pub(crate) fn peek_byte(&mut self) -> std::io::Result<u8> {
match self { match self {
Stream::Byte(ref mut cursor) => { Stream::Byte(cursor) => {
let mut b = [0u8; 1]; let mut b = [0u8; 1];
let pos = cursor.stream.get_mut().0.position(); let pos = cursor.stream.get_mut().0.position();
@@ -1676,15 +1684,15 @@ impl Stream {
_ => Err(std::io::Error::new(ErrorKind::UnexpectedEof, "end of file")), _ => Err(std::io::Error::new(ErrorKind::UnexpectedEof, "end of file")),
} }
} }
Stream::InputFile(ref mut file) => match file.peek_byte() { Stream::InputFile(file) => match file.peek_byte() {
Some(result) => Ok(result?), Some(result) => Ok(result?),
_ => Err(std::io::Error::new( _ => Err(std::io::Error::new(
ErrorKind::UnexpectedEof, ErrorKind::UnexpectedEof,
StreamError::PeekByteFailed, StreamError::PeekByteFailed,
)), )),
}, },
Stream::Readline(ref mut stream) => stream.stream.peek_byte(), Stream::Readline(stream) => stream.stream.peek_byte(),
Stream::NamedTcp(ref mut stream) => { Stream::NamedTcp(stream) => {
let mut b = [0u8; 1]; let mut b = [0u8; 1];
stream.stream.get_mut().tcp_stream.peek(&mut b)?; stream.stream.get_mut().tcp_stream.peek(&mut b)?;
Ok(b[0]) Ok(b[0])
@@ -1950,7 +1958,7 @@ impl MachineState {
) -> Result<Stream, ParserError> { ) -> Result<Stream, ParserError> {
match stream.peek_char() { match stream.peek_char() {
None => Ok(stream), // empty stream is handled gracefully by Lexer::eof None => Ok(stream), // empty stream is handled gracefully by Lexer::eof
Some(Err(e)) => Err(ParserError::IO(e)), Some(Err(e)) => Err(ParserError::from(e)),
Some(Ok(c)) => { Some(Ok(c)) => {
if c == '\u{feff}' { if c == '\u{feff}' {
// skip UTF-8 BOM // skip UTF-8 BOM
@@ -2187,8 +2195,8 @@ mod test {
use crate::*; use crate::*;
use std::{cell::RefCell, io::Read, io::Write, rc::Rc}; use std::{cell::RefCell, io::Read, io::Write, rc::Rc};
use crate::machine::config::*;
use crate::LeafAnswer; use crate::LeafAnswer;
use crate::machine::config::*;
use super::{Stream, StreamOptions}; use super::{Stream, StreamOptions};

View File

@@ -1,70 +0,0 @@
#[cfg(rust_version = "1.87.0")]
pub use ge_1_87_0::{PipeReader, PipeWriter};
#[cfg(not(rust_version = "1.87.0"))]
pub use lt_1_87_0::{PipeReader, PipeWriter};
#[cfg(not(rust_version = "1.87.0"))]
pub(crate) use lt_1_87_0::PipeReaderInner;
#[cfg(not(rust_version = "1.87.0"))]
mod lt_1_87_0 {
use std::process::{ChildStderr, ChildStdout};
pub type PipeWriter = std::process::ChildStdin;
#[derive(Debug)]
pub struct PipeReader(pub(crate) PipeReaderInner);
#[derive(Debug)]
pub(crate) enum PipeReaderInner {
Stdout(ChildStdout),
Stderr(ChildStderr),
}
impl std::io::Read for PipeReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match &mut self.0 {
PipeReaderInner::Stdout(child_stdout) => child_stdout.read(buf),
PipeReaderInner::Stderr(child_stderr) => child_stderr.read(buf),
}
}
fn read_vectored(
&mut self,
bufs: &mut [std::io::IoSliceMut<'_>],
) -> std::io::Result<usize> {
match &mut self.0 {
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_vectored(bufs),
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_vectored(bufs),
}
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
match &mut self.0 {
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_to_end(buf),
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_to_end(buf),
}
}
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
match &mut self.0 {
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_to_string(buf),
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_to_string(buf),
}
}
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
match &mut self.0 {
PipeReaderInner::Stdout(child_stdout) => child_stdout.read_exact(buf),
PipeReaderInner::Stderr(child_stderr) => child_stderr.read_exact(buf),
}
}
}
}
#[cfg(rust_version = "1.87.0")]
mod ge_1_87_0 {
pub type PipeReader = std::io::PipeReader;
pub type PipeWriter = std::io::PipeWriter;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
use crate::machine::Number;
use crate::Machine; use crate::Machine;
use crate::machine::Number;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use puruspe::beta::*; use puruspe::beta::*;
use puruspe::error::*; use puruspe::error::*;

View File

@@ -1,6 +1,6 @@
use crate::arena::*; use crate::arena::*;
use crate::forms::*; use crate::forms::*;
use crate::heap_iter::{stackful_preorder_iter, NonListElider}; use crate::heap_iter::{NonListElider, stackful_preorder_iter};
use crate::machine::machine_state::*; use crate::machine::machine_state::*;
use crate::machine::*; use crate::machine::*;
use crate::offset_table::*; use crate::offset_table::*;
@@ -9,19 +9,14 @@ use crate::types::*;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use derive_more::*; use derive_more::*;
use fxhash::FxBuildHasher;
use indexmap::IndexSet;
use num_order::NumOrd; use num_order::NumOrd;
impl MachineState { impl MachineState {
pub(crate) fn partial_string_to_pdl(&mut self, pstr_loc: usize, l: usize) { pub(crate) fn partial_string_to_pdl(&mut self, pstr_loc: usize, l: usize) {
let (c, succ_cell) = self.heap.last_str_char_and_tail(pstr_loc); let (c, succ_cell) = self.heap.last_str_char_and_tail(pstr_loc);
self.pdl.push(heap_loc_as_cell!(l + 1)); self.pdl.push((succ_cell, heap_loc_as_cell!(l + 1)));
self.pdl.push(succ_cell); self.pdl.push((char_as_cell!(c), heap_loc_as_cell!(l)));
self.pdl.push(heap_loc_as_cell!(l));
self.pdl.push(char_as_cell!(c));
} }
} }
@@ -37,8 +32,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
if n1 == n2 && a1 == a2 { if n1 == n2 && a1 == a2 {
for idx in (0..a1).rev() { for idx in (0..a1).rev() {
self.pdl.push(heap_loc_as_cell!(s2+1+idx)); self.pdl.push((heap_loc_as_cell!(s1+1+idx), heap_loc_as_cell!(s2+1+idx)));
self.pdl.push(heap_loc_as_cell!(s1+1+idx));
} }
} else { } else {
self.fail = true; self.fail = true;
@@ -47,8 +41,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
(HeapCellValueTag::Lis, l2) => { (HeapCellValueTag::Lis, l2) => {
if a1 == 2 && n1 == atom!(".") { if a1 == 2 && n1 == atom!(".") {
for idx in (0..2).rev() { for idx in (0..2).rev() {
self.pdl.push(heap_loc_as_cell!(l2+1+idx)); self.pdl.push((heap_loc_as_cell!(s1+1+idx), heap_loc_as_cell!(l2+1+idx)));
self.pdl.push(heap_loc_as_cell!(s1+1+idx));
} }
} else { } else {
self.fail = true; self.fail = true;
@@ -76,8 +69,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
read_heap_cell!(value, read_heap_cell!(value,
(HeapCellValueTag::Lis, l2) => { (HeapCellValueTag::Lis, l2) => {
for idx in (0..2).rev() { for idx in (0..2).rev() {
self.pdl.push(heap_loc_as_cell!(l2 + idx)); self.pdl.push((heap_loc_as_cell!(l1 + idx), heap_loc_as_cell!(l2 + idx)));
self.pdl.push(heap_loc_as_cell!(l1 + idx));
} }
} }
(HeapCellValueTag::Str, s2) => { (HeapCellValueTag::Str, s2) => {
@@ -86,8 +78,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
if a2 == 2 && n2 == atom!(".") { if a2 == 2 && n2 == atom!(".") {
for idx in (0..2).rev() { for idx in (0..2).rev() {
self.pdl.push(heap_loc_as_cell!(s2+1+idx)); self.pdl.push((heap_loc_as_cell!(l1+idx), heap_loc_as_cell!(s2+1+idx)));
self.pdl.push(heap_loc_as_cell!(l1+idx));
} }
} else { } else {
self.fail = true; self.fail = true;
@@ -136,8 +127,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
(HeapCellValueTag::PStrLoc, other_pstr_loc) => { (HeapCellValueTag::PStrLoc, other_pstr_loc) => {
match machine_st.heap.compare_pstr_segments(pstr_loc, other_pstr_loc) { match machine_st.heap.compare_pstr_segments(pstr_loc, other_pstr_loc) {
PStrSegmentCmpResult::Continue(v1, v2) => { PStrSegmentCmpResult::Continue(v1, v2) => {
machine_st.pdl.push(v1.offset_by(pstr_loc)); machine_st.pdl.push((v2.offset_by(other_pstr_loc), v1.offset_by(pstr_loc)));
machine_st.pdl.push(v2.offset_by(other_pstr_loc));
} }
_ => { _ => {
machine_st.fail = true; machine_st.fail = true;
@@ -355,13 +345,15 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
} }
fn unify_internal(&mut self) { fn unify_internal(&mut self) {
let mut tabu_list = IndexSet::with_hasher(FxBuildHasher::default()); debug_assert!(self.unify_tabu_list.is_empty());
while let Some((s1, s2)) = self.pdl.pop() {
if self.fail {
// FIXME(msrv) FIXME(edition2024) use let chain to move this to check this in the while condition
break;
}
while !(self.pdl.is_empty() || self.fail) {
let s1 = self.pdl.pop().unwrap();
let s1 = (self.deref() as &MachineState).deref(s1); let s1 = (self.deref() as &MachineState).deref(s1);
let s2 = self.pdl.pop().unwrap();
let s2 = (self.deref() as &MachineState).deref(s2); let s2 = (self.deref() as &MachineState).deref(s2);
if s1 != s2 { if s1 != s2 {
@@ -383,7 +375,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
Self::unify_atom(self, name, d2); Self::unify_atom(self, name, d2);
} }
(HeapCellValueTag::Str, s1) => { (HeapCellValueTag::Str, s1) => {
if tabu_list.contains(&(d1, d2)) { if self.unify_tabu_list.contains(&(d1, d2)) {
continue; continue;
} }
@@ -391,11 +383,11 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
if !self.fail { if !self.fail {
let d2 = self.store(d2); let d2 = self.store(d2);
tabu_list.insert((d1, d2)); self.unify_tabu_list.insert((d1, d2));
} }
} }
(HeapCellValueTag::Lis, l1) => { (HeapCellValueTag::Lis, l1) => {
if d2.is_ref() && tabu_list.contains(&(d1, d2)) { if d2.is_ref() && self.unify_tabu_list.contains(&(d1, d2)) {
continue; continue;
} }
@@ -403,7 +395,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
if !self.fail { if !self.fail {
let d2 = self.store(d2); let d2 = self.store(d2);
tabu_list.insert((d1, d2)); self.unify_tabu_list.insert((d1, d2));
} }
} }
(HeapCellValueTag::PStrLoc, l) => { (HeapCellValueTag::PStrLoc, l) => {
@@ -411,7 +403,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
(HeapCellValueTag::PStrLoc | (HeapCellValueTag::PStrLoc |
HeapCellValueTag::Lis | HeapCellValueTag::Lis |
HeapCellValueTag::Str) => { HeapCellValueTag::Str) => {
if tabu_list.contains(&(d1, d2)) { if self.unify_tabu_list.contains(&(d1, d2)) {
continue; continue;
} }
} }
@@ -429,7 +421,7 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
if !self.fail && !d2.is_constant() { if !self.fail && !d2.is_constant() {
let d2 = self.store(d2); let d2 = self.store(d2);
tabu_list.insert((d1, d2)); self.unify_tabu_list.insert((d1, d2));
} }
} }
(HeapCellValueTag::F64Offset, f1) => { (HeapCellValueTag::F64Offset, f1) => {
@@ -450,6 +442,8 @@ pub(crate) trait Unifier: DerefMut<Target = MachineState> {
); );
} }
} }
self.unify_tabu_list.clear();
} }
fn bind(&mut self, r: Ref, value: HeapCellValue); fn bind(&mut self, r: Ref, value: HeapCellValue);

View File

@@ -138,14 +138,7 @@ macro_rules! typed_arena_ptr_as_cell {
} }
macro_rules! raw_ptr_as_cell { macro_rules! raw_ptr_as_cell {
($ptr:expr) => {{ ($ptr:expr) => {{ HeapCellValue::from_arena_header_ptr($ptr) }};
// Cell is 64-bit, but raw ptr is 32-bit in 32-bit systems
let ptr: *const _ = $ptr;
// This needs to expose provenance because it needs to be turned back into a pointer
// in contexts where there is no available provenance locally. For example, in
// `ConsPtr::as_ptr`.
HeapCellValue::from_ptr_addr(ptr.expose_provenance())
}};
} }
macro_rules! untyped_arena_ptr_as_cell { macro_rules! untyped_arena_ptr_as_cell {
@@ -369,12 +362,6 @@ macro_rules! compare_number_instr {
}}; }};
} }
macro_rules! interm {
($n: expr) => {
ArithmeticTerm::Interm($n)
};
}
macro_rules! ar_reg { macro_rules! ar_reg {
($r: expr) => { ($r: expr) => {
ArithmeticTerm::Reg($r) ArithmeticTerm::Reg($r)
@@ -412,47 +399,32 @@ macro_rules! index_store {
} }
macro_rules! unify { macro_rules! unify {
($machine_st:expr, $($value:expr),*) => {{ ($machine_st:expr, $($v1:expr, $v2:expr),*) => {{
$($machine_st.pdl.push($value);)* $($machine_st.pdl.push(($v2, $v1));)*
$machine_st.unify() $machine_st.unify()
}}; }};
} }
macro_rules! unify_fn { macro_rules! unify_fn {
($machine_st:expr, $($value:expr),*) => {{ ($machine_st:expr, $($v1:expr, $v2: expr),*) => {{
$($machine_st.pdl.push($value);)* $($machine_st.pdl.push(($v2, $v1));)*
($machine_st.unify_fn)(&mut $machine_st) $machine_st.occurs_check.unify(&mut $machine_st)
}}; }};
} }
macro_rules! unify_with_occurs_check { macro_rules! unify_with_occurs_check {
($machine_st:expr, $($value:expr),*) => {{ ($machine_st:expr, $($v1:expr, $v2:expr),*) => {{
$($machine_st.pdl.push($value);)* $($machine_st.pdl.push(($v2, $v1));)*
$machine_st.unify_with_occurs_check() $machine_st.unify_with_occurs_check()
}}; }};
} }
macro_rules! compare_term_test {
($machine_st:expr, $e1:expr, $e2:expr) => {{
$machine_st.pdl.push($e2);
$machine_st.pdl.push($e1);
$machine_st.compare_term_test(VarComparison::Distinct)
}};
($machine_st:expr, $e1:expr, $e2:expr, $var_comparison:expr) => {{
$machine_st.pdl.push($e2);
$machine_st.pdl.push($e1);
$machine_st.compare_term_test($var_comparison)
}};
}
macro_rules! step_or_resource_error { macro_rules! step_or_resource_error {
($machine_st:expr, $val:expr) => {{ ($machine_st:expr, $val:expr) => {{
match $val { match $val {
Ok(r) => r, Ok(r) => r,
Err(err_loc) => { Err(err) => {
$machine_st.throw_resource_error(err_loc); $machine_st.throw_resource_error(err);
return; return;
} }
} }
@@ -460,8 +432,8 @@ macro_rules! step_or_resource_error {
($machine_st:expr, $val:expr, $fail:block) => {{ ($machine_st:expr, $val:expr, $fail:block) => {{
match $val { match $val {
Ok(r) => r, Ok(r) => r,
Err(err_loc) => { Err(err) => {
$machine_st.throw_resource_error(err_loc); $machine_st.throw_resource_error(err);
$fail $fail
} }
} }
@@ -476,14 +448,29 @@ macro_rules! resource_error_call_result {
}; };
} }
macro_rules! heap_index { macro_rules! heap_index_checked {
($idx:expr) => { ($idx:expr) => {
($idx) * std::mem::size_of::<HeapCellValue>() std::mem::size_of::<HeapCellValue>().checked_mul($idx)
}; };
} }
pub(crate) use heap_index_checked;
macro_rules! heap_index {
($idx:expr) => {{
let idx = $idx;
$crate::macros::heap_index_checked!(idx).unwrap_or_else(|| {
panic!(
"overflow while calculating heap index {idx} * {} > {}",
std::mem::size_of::<HeapCellValue>(),
usize::MAX,
)
})
}};
}
macro_rules! cell_index { macro_rules! cell_index {
($idx:expr) => { ($idx:expr) => {
(($idx) / std::mem::size_of::<HeapCellValue>()) ($idx) / std::mem::size_of::<HeapCellValue>()
}; };
} }

View File

@@ -2,17 +2,18 @@ use std::cell::UnsafeCell;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, mem, ptr}; use std::{fmt, mem, ptr};
use arcu::Rcu;
use arcu::atomic::Arcu; use arcu::atomic::Arcu;
use arcu::epoch_counters::GlobalEpochCounterPool; use arcu::epoch_counters::GlobalEpochCounterPool;
use arcu::rcu_ref::RcuRef; use arcu::rcu_ref::RcuRef;
use arcu::Rcu;
use fxhash::FxBuildHasher; use fxhash::FxBuildHasher;
use indexmap::IndexMap; use indexmap::IndexMap;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use crate::machine::heap::AllocError;
use crate::machine::machine_indices::IndexPtr; use crate::machine::machine_indices::IndexPtr;
use crate::raw_block::RawBlock;
use crate::raw_block::RawBlockTraits; use crate::raw_block::RawBlockTraits;
use crate::raw_block::{RawBlock, RawBlockConcurrent};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
@@ -58,8 +59,10 @@ impl<T: RawBlockTraits> From<Arc<ConcurrentOffsetTable<T>>> for OffsetTableImpl<
impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> { impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> {
#[inline(always)] #[inline(always)]
pub fn new() -> Self { pub fn new() -> Result<Self, AllocError> {
Self(InnerOffsetTableImpl::Serial(SerialOffsetTable::new())) Ok(Self(
InnerOffsetTableImpl::Serial(SerialOffsetTable::new()?),
))
} }
#[must_use = "the returned concurrent table must be absorbed into the owned OffsetTable"] #[must_use = "the returned concurrent table must be absorbed into the owned OffsetTable"]
@@ -90,8 +93,9 @@ impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> {
// this shouldn't be able to fail // this shouldn't be able to fail
let raw_block = let raw_block =
Arc::try_unwrap(table.block.replace(RawBlock::empty_block())).unwrap(); Arc::try_unwrap(table.block.replace(RawBlock::empty_block())).unwrap();
self.0 = self.0 = InnerOffsetTableImpl::Serial(SerialOffsetTable {
InnerOffsetTableImpl::Serial(SerialOffsetTable { block: raw_block }); block: raw_block.into(),
});
Ok(()) Ok(())
} }
Err(table_arc) => { Err(table_arc) => {
@@ -116,7 +120,7 @@ impl<T: fmt::Debug + RawBlockTraits> OffsetTableImpl<T> {
impl<T: fmt::Debug + RawBlockTraits> Default for OffsetTableImpl<T> { impl<T: fmt::Debug + RawBlockTraits> Default for OffsetTableImpl<T> {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new().unwrap()
} }
} }
@@ -127,7 +131,7 @@ struct SerialOffsetTable<T: RawBlockTraits> {
#[derive(Debug)] #[derive(Debug)]
pub struct ConcurrentOffsetTable<T: RawBlockTraits> { pub struct ConcurrentOffsetTable<T: RawBlockTraits> {
block: Arcu<RawBlock<T>, GlobalEpochCounterPool>, block: Arcu<RawBlock<T, RawBlockConcurrent>, GlobalEpochCounterPool>,
growth_lock: RwLock<()>, growth_lock: RwLock<()>,
offset_locks: RwLock<Vec<RwLock<()>>>, offset_locks: RwLock<Vec<RwLock<()>>>,
} }
@@ -201,13 +205,14 @@ impl OffsetTable<IndexPtr> for OffsetTableImpl<IndexPtr> {
impl<T: RawBlockTraits> SerialOffsetTable<T> { impl<T: RawBlockTraits> SerialOffsetTable<T> {
#[inline] #[inline]
fn new() -> Self { fn new() -> Result<Self, AllocError> {
Self { Ok(Self {
block: RawBlock::new(), block: RawBlock::new()?,
} })
} }
unsafe fn build_with(&mut self, value: T) -> usize { unsafe fn build_with(&mut self, value: T) -> usize {
unsafe {
let mut ptr; let mut ptr;
loop { loop {
@@ -222,19 +227,22 @@ impl<T: RawBlockTraits> SerialOffsetTable<T> {
} }
ptr::write(ptr as *mut T, value); ptr::write(ptr as *mut T, value);
ptr.addr() - self.block.base.addr() // SAFETY: `ptr` was obtained from `self.block.alloc()`
self.block.get_offset(ptr)
}
} }
#[inline] #[inline]
unsafe fn lookup(&self, offset: usize) -> &T { unsafe fn lookup(&self, offset: usize) -> &T {
&*self.block.base.add(offset).cast::<T>() unsafe { &*self.block.get_unchecked(offset).cast::<T>() }
} }
#[inline] #[inline]
unsafe fn lookup_mut(&mut self, offset: usize) -> &mut T { unsafe fn lookup_mut(&mut self, offset: usize) -> &mut T {
&mut *self.block.base.add(offset).cast::<T>().cast_mut() unsafe { &mut *self.block.get_unchecked(offset).cast::<T>().cast_mut() }
} }
#[allow(clippy::wrong_self_convention)]
fn to_concurrent(&mut self) -> ConcurrentOffsetTable<T> fn to_concurrent(&mut self) -> ConcurrentOffsetTable<T>
where where
T: fmt::Debug, T: fmt::Debug,
@@ -244,8 +252,9 @@ impl<T: RawBlockTraits> SerialOffsetTable<T> {
}; };
let serial_tbl = mem::replace(self, empty_serial_tbl); let serial_tbl = mem::replace(self, empty_serial_tbl);
let num_tbl_entries = serial_tbl.block.size() / size_of::<T>(); let num_tbl_entries = serial_tbl.block.used_bytes() / size_of::<T>();
let block = Arcu::new(serial_tbl.block, GlobalEpochCounterPool); let raw_block: RawBlock<T, RawBlockConcurrent> = serial_tbl.block.into();
let block = Arcu::new(raw_block, GlobalEpochCounterPool);
let offset_locks: Vec<RwLock<()>> = (0..num_tbl_entries).map(|_| RwLock::new(())).collect(); let offset_locks: Vec<RwLock<()>> = (0..num_tbl_entries).map(|_| RwLock::new(())).collect();
@@ -279,7 +288,7 @@ impl<T: RawBlockTraits> ConcurrentOffsetTable<T> {
} }
} }
let new_tbl_sz = block_epoch.size() / size_of::<T>(); let new_tbl_sz = block_epoch.used_bytes() / size_of::<T>();
let mut offset_locks = self.offset_locks.write(); let mut offset_locks = self.offset_locks.write();
offset_locks.resize_with(new_tbl_sz, || RwLock::new(())); offset_locks.resize_with(new_tbl_sz, || RwLock::new(()));
@@ -288,7 +297,8 @@ impl<T: RawBlockTraits> ConcurrentOffsetTable<T> {
ptr::write(ptr as *mut T, value); ptr::write(ptr as *mut T, value);
} }
let value = ptr.addr() - block_epoch.base.addr(); // SAFETY: `ptr` was obtained from `block_epoch.alloc()`
let value = unsafe { block_epoch.get_offset(ptr) };
// AtomTable would have to update the index table at this point // AtomTable would have to update the index table at this point
// explicit drop to ensure we don't accidentally drop it early // explicit drop to ensure we don't accidentally drop it early
@@ -303,7 +313,7 @@ impl<T: RawBlockTraits> ConcurrentOffsetTable<T> {
let inner_offset_lock = outer_offset_lock[offset / size_of::<T>()].read(); let inner_offset_lock = outer_offset_lock[offset / size_of::<T>()].read();
let rcu_ref = RcuRef::try_map(self.block.read(), |raw_block| unsafe { let rcu_ref = RcuRef::try_map(self.block.read(), |raw_block| unsafe {
raw_block.base.add(offset).cast::<T>().as_ref() raw_block.get_unchecked(offset).cast::<T>().as_ref()
}) })
.expect("offset valid"); .expect("offset valid");
@@ -322,8 +332,7 @@ impl<T: RawBlockTraits> ConcurrentOffsetTable<T> {
let rcu_ref = RcuRef::try_map(self.block.read(), |raw_block| unsafe { let rcu_ref = RcuRef::try_map(self.block.read(), |raw_block| unsafe {
raw_block raw_block
.base .get_unchecked(offset)
.add(offset)
.cast_mut() .cast_mut()
.cast::<UnsafeCell<T>>() .cast::<UnsafeCell<T>>()
.as_ref() .as_ref()
@@ -374,11 +383,11 @@ pub enum F64Table {
} }
impl F64Table { impl F64Table {
pub fn new() -> Self { pub fn new() -> Result<Self, AllocError> {
Self::Serial(SerialF64Table { Ok(Self::Serial(SerialF64Table {
indirection_tbl: IndexMap::with_hasher(FxBuildHasher::new()), indirection_tbl: IndexMap::with_hasher(FxBuildHasher::new()),
offset_tbl: SerialOffsetTable::new(), offset_tbl: SerialOffsetTable::new()?,
}) }))
} }
pub fn build_with(&mut self, value: OrderedFloat<f64>) -> F64Offset { pub fn build_with(&mut self, value: OrderedFloat<f64>) -> F64Offset {
@@ -398,6 +407,8 @@ impl F64Table {
// which breaks the invariant indirection_tbl is meant to enforce. // which breaks the invariant indirection_tbl is meant to enforce.
// Since this branch is never invoked, it does no harm, but that // Since this branch is never invoked, it does no harm, but that
// that will eventually change. // that will eventually change.
//
// Note: may be indirectly fixed by the use of the new RawBlockConcurrency trait.
{ {
let indirection_tbl = concurrent_tbl.indirection_tbl.lock(); let indirection_tbl = concurrent_tbl.indirection_tbl.lock();
@@ -468,7 +479,9 @@ impl F64Table {
.unwrap(); .unwrap();
*self = Self::Serial(SerialF64Table { *self = Self::Serial(SerialF64Table {
indirection_tbl: indirection_tbl.into_inner(), indirection_tbl: indirection_tbl.into_inner(),
offset_tbl: SerialOffsetTable { block: raw_block }, offset_tbl: SerialOffsetTable {
block: raw_block.into(),
},
}); });
Ok(()) Ok(())

View File

@@ -1,9 +1,6 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
use crate::arena::*; use crate::arena::*;
use crate::atom_table::*; use crate::atom_table::*;
use crate::offset_table::*; use crate::offset_table::*;
use crate::parser::char_reader::*;
use crate::types::HeapCellValueTag; use crate::types::HeapCellValueTag;
use std::cell::{Cell, Ref, RefCell, RefMut}; use std::cell::{Cell, Ref, RefCell, RefMut};
@@ -22,9 +19,9 @@ use dashu::Integer;
use dashu::Rational; use dashu::Rational;
use fxhash::FxBuildHasher; use fxhash::FxBuildHasher;
use indexmap::IndexMap; use indexmap::IndexMap;
use modular_bitfield::error::OutOfBounds;
use modular_bitfield::prelude::*;
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use scryer_modular_bitfield::error::OutOfBounds;
use scryer_modular_bitfield::prelude::*;
pub type Specifier = u32; pub type Specifier = u32;
@@ -249,6 +246,7 @@ pub enum GInteger {
impl GInteger { impl GInteger {
#[inline] #[inline]
#[allow(clippy::wrong_self_convention)]
pub fn to_literal(self) -> Literal { pub fn to_literal(self) -> Literal {
match self { match self {
GInteger::Integer(integer) => Literal::Integer(integer), GInteger::Integer(integer) => Literal::Integer(integer),
@@ -359,16 +357,12 @@ impl OpDesc {
#[inline] #[inline]
pub fn get_spec(self) -> OpDeclSpec { pub fn get_spec(self) -> OpDeclSpec {
OpDeclSpec::try_from(self.spec()).expect("OpDecl always contains a valud OpDeclSpec") OpDeclSpec::try_from(self.spec()).expect("OpDecl always contains a valid OpDeclSpec")
} }
#[inline] #[inline]
pub fn arity(self) -> usize { pub fn arity(self) -> usize {
if !self.get_spec().is_infix() { if !self.get_spec().is_infix() { 1 } else { 2 }
1
} else {
2
}
} }
} }
@@ -424,72 +418,92 @@ pub enum ArithmeticError {
UninstantiatedVar, UninstantiatedVar,
} }
#[derive(Debug, Clone)]
pub struct Location {
pub(super) line: usize,
pub(super) column: usize,
}
impl Location {
// beginning of file
pub(crate) const BOF: Self = Self { line: 0, column: 0 };
pub fn line(&self) -> usize {
self.line
}
pub fn column(&self) -> usize {
self.column
}
}
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
pub enum ParserError { pub struct ParserError {
BackQuotedString(usize, usize), pub(crate) location: Option<Location>,
pub(crate) kind: ParserErrorKind,
}
#[allow(dead_code)]
#[derive(Debug)]
#[non_exhaustive]
pub(crate) enum ParserErrorKind {
BackQuotedString,
IO(IOError), IO(IOError),
IncompleteReduction(usize, usize), IncompleteReduction,
InfiniteFloat(usize, usize), InfiniteFloat,
InvalidSingleQuotedCharacter(char), InvalidSingleQuotedCharacter(char),
LexicalError(lexical::Error), ParseFloat,
MissingQuote(usize, usize), MissingQuote,
NonPrologChar(usize, usize), NonPrologChar,
ParseBigInt(usize, usize), ParseBigInt,
UnexpectedChar(char, usize, usize), UnexpectedChar(char),
// UnexpectedEOF, // UnexpectedEOF,
Utf8Error(usize, usize), Utf8Error,
} }
impl ParserError { impl ParserError {
pub fn line_and_col_num(&self) -> Option<(usize, usize)> { pub(crate) fn location(&self) -> Option<Location> {
match self { self.location.as_ref().cloned()
&ParserError::BackQuotedString(line_num, col_num)
| &ParserError::IncompleteReduction(line_num, col_num)
| &ParserError::InfiniteFloat(line_num, col_num)
| &ParserError::MissingQuote(line_num, col_num)
| &ParserError::NonPrologChar(line_num, col_num)
| &ParserError::ParseBigInt(line_num, col_num)
| &ParserError::UnexpectedChar(_, line_num, col_num)
| &ParserError::Utf8Error(line_num, col_num) => Some((line_num, col_num)),
_ => None,
}
} }
pub fn as_atom(&self) -> Atom { pub(crate) fn as_atom(&self) -> Atom {
match self { match &self.kind {
ParserError::BackQuotedString(..) => atom!("back_quoted_string"), ParserErrorKind::BackQuotedString => atom!("back_quoted_string"),
ParserError::IncompleteReduction(..) => atom!("incomplete_reduction"), ParserErrorKind::IncompleteReduction => atom!("incomplete_reduction"),
ParserError::InvalidSingleQuotedCharacter(..) => { ParserErrorKind::InvalidSingleQuotedCharacter(..) => {
atom!("invalid_single_quoted_character") atom!("invalid_single_quoted_character")
} }
ParserError::InfiniteFloat(..) => { ParserErrorKind::InfiniteFloat => {
atom!("infinite_float") atom!("infinite_float")
} }
ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => { ParserErrorKind::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 => { ParserErrorKind::IO(e) if e.kind() == ErrorKind::InvalidData => {
atom!("invalid_data") atom!("invalid_data")
} }
ParserError::IO(_) => atom!("input_output_error"), ParserErrorKind::IO(_) => atom!("input_output_error"),
ParserError::LexicalError(_) => atom!("lexical_error"), ParserErrorKind::ParseFloat => atom!("parse_float"),
ParserError::MissingQuote(..) => atom!("missing_quote"), ParserErrorKind::MissingQuote => atom!("missing_quote"),
ParserError::NonPrologChar(..) => atom!("non_prolog_character"), ParserErrorKind::NonPrologChar => atom!("non_prolog_character"),
ParserError::ParseBigInt(..) => atom!("cannot_parse_big_int"), ParserErrorKind::ParseBigInt => atom!("cannot_parse_big_int"),
ParserError::UnexpectedChar(..) => atom!("unexpected_char"), ParserErrorKind::UnexpectedChar(..) => atom!("unexpected_char"),
ParserError::Utf8Error(..) => atom!("utf8_conversion_error"), ParserErrorKind::Utf8Error => atom!("utf8_conversion_error"),
} }
} }
#[inline] #[inline]
pub fn unexpected_eof() -> Self { pub(crate) fn unexpected_eof() -> Self {
ParserError::IO(std::io::Error::from(ErrorKind::UnexpectedEof)) ParserError {
location: None,
kind: ParserErrorKind::IO(std::io::Error::from(ErrorKind::UnexpectedEof)),
}
} }
#[inline] #[inline]
pub fn is_unexpected_eof(&self) -> bool { pub(crate) fn is_unexpected_eof(&self) -> bool {
if let ParserError::IO(e) = self { if let ParserErrorKind::IO(e) = &self.kind {
e.kind() == ErrorKind::UnexpectedEof e.kind() == ErrorKind::UnexpectedEof
} else { } else {
false false
@@ -497,24 +511,11 @@ impl ParserError {
} }
} }
impl From<lexical::Error> for ParserError {
fn from(e: lexical::Error) -> ParserError {
ParserError::LexicalError(e)
}
}
impl From<IOError> for ParserError { impl From<IOError> for ParserError {
fn from(e: IOError) -> ParserError { fn from(e: IOError) -> ParserError {
ParserError::IO(e) ParserError {
} location: None,
} kind: ParserErrorKind::IO(e),
impl From<&IOError> for ParserError {
fn from(error: &IOError) -> ParserError {
if error.get_ref().filter(|e| e.is::<BadUtf8Error>()).is_some() {
ParserError::Utf8Error(0, 0)
} else {
ParserError::IO(error.kind().into())
} }
} }
} }
@@ -603,6 +604,7 @@ mod private {
impl<T: FitsInFixnumSeal> MightNotFitInFixnumSeal for T {} impl<T: FitsInFixnumSeal> MightNotFitInFixnumSeal for T {}
impl MightNotFitInFixnumSeal for i64 {} impl MightNotFitInFixnumSeal for i64 {}
impl MightNotFitInFixnumSeal for u64 {} impl MightNotFitInFixnumSeal for u64 {}
impl MightNotFitInFixnumSeal for u128 {}
impl MightNotFitInFixnumSeal for &Integer {} impl MightNotFitInFixnumSeal for &Integer {}
impl MightNotFitInFixnumSeal for Integer {} impl MightNotFitInFixnumSeal for Integer {}
impl MightNotFitInFixnumSeal for usize {} impl MightNotFitInFixnumSeal for usize {}
@@ -669,7 +671,7 @@ impl Fixnum {
#[inline] #[inline]
pub fn get_tag(&self) -> HeapCellValueTag { pub fn get_tag(&self) -> HeapCellValueTag {
use scryer_modular_bitfield::Specifier; use modular_bitfield::Specifier;
HeapCellValueTag::from_bytes(self.tag()).unwrap() HeapCellValueTag::from_bytes(self.tag()).unwrap()
} }
@@ -871,7 +873,7 @@ pub enum Term {
impl Term { impl Term {
pub fn first_arg(&self) -> Option<&Term> { pub fn first_arg(&self) -> Option<&Term> {
match self { match self {
Term::Clause(_, _, ref terms) => terms.first(), Term::Clause(_, _, terms) => terms.first(),
_ => None, _ => None,
} }
} }
@@ -886,14 +888,14 @@ impl Term {
pub fn arity(&self) -> usize { pub fn arity(&self) -> usize {
match self { match self {
Term::Clause(_, _, ref child_terms, ..) => child_terms.len(), Term::Clause(_, _, child_terms, ..) => child_terms.len(),
_ => 0, _ => 0,
} }
} }
} }
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 &mut Term::Clause(_, ref name, ref mut subterms) = term {
if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = subterms.last() { if let Some(Term::Literal(_, Literal::CodeIndexOffset(_))) = subterms.last() {
subterms.pop(); subterms.pop();
} }

View File

@@ -102,19 +102,27 @@ impl<R> CharReader<R> {
} }
impl<R: Read> CharReader<R> { impl<R: Read> CharReader<R> {
pub fn read_chunk(&mut self) -> io::Result<usize> {
let mut chunk = [0u8; 8 * 1024];
let nread = self.inner.read(&mut chunk)?;
self.buf.extend_from_slice(&chunk[..nread]);
Ok(nread)
}
pub fn refresh_buffer(&mut self) -> io::Result<&[u8]> { pub fn refresh_buffer(&mut self) -> io::Result<&[u8]> {
// If we've reached the end of our internal buffer then we need to fetch // If we've reached the end of our internal buffer then we need to fetch
// some more data from the underlying reader. // some more data from the underlying reader.
// Branch using `>=` instead of the more correct `==` // Branch using `>=` instead of the more correct `==`
// to tell the compiler that the pos..cap slice is always valid. // to tell the compiler that the pos..cap slice is always valid.
if self.pos >= self.buf.len() { if self.pos >= self.buf.len() {
self.buf.clear(); // make some space in buf
if self.buf.len() > 4 {
// keep 4 bytes so that put_back_char can put back at least one char
self.buf.drain(4..);
}
self.pos = self.buf.len();
let mut word = [0u8; std::mem::size_of::<char>()]; self.read_chunk()?;
let nread = self.inner.read(&mut word)?;
self.buf.extend_from_slice(&word[..nread]);
self.pos = 0;
} }
Ok(&self.buf[self.pos..]) Ok(&self.buf[self.pos..])
@@ -135,7 +143,7 @@ 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]| { fn bad_bytes_error(buf: &[u8]) -> std::io::Error {
// If we have 4 bytes that still don't make up // If we have 4 bytes that still don't make up
// a valid code point, then we have garbage. // a valid code point, then we have garbage.
@@ -143,110 +151,95 @@ impl<R: Read> CharRead for CharReader<R> {
// leading bytes until either the buffer is // leading bytes until either the buffer is
// empty, or we have a valid code point. // empty, or we have a valid code point.
let mut split_point = 1; // note we might have a sequence of invalid bytes followed by valid bytes followed by invalid bytes
let mut badbytes = vec![];
loop { let err = str::from_utf8(buf).expect_err("the start of buf should be invalid utf-8");
let (bad, rest) = buf.split_at(split_point); assert_eq!(err.valid_up_to(), 0, "the error should be a prefix");
if rest.is_empty() || str::from_utf8(rest).is_ok() { let invalid_prefix = err.error_len().expect("we should have at least 4 bytes");
badbytes.extend_from_slice(bad);
break; let bad_bytes = buf[..invalid_prefix].to_vec();
io::Error::new(
io::ErrorKind::InvalidData,
BadUtf8Error { bytes: bad_bytes },
)
} }
split_point += 1; // while we haven't consumed all bytes from the buffer
} while self.pos < self.buf.len() {
// buf must be non-empty
// 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 {
let buf = &self.buf[self.pos..]; let buf = &self.buf[self.pos..];
if !buf.is_empty() { // we need at most 4 bytes for a char so don't decode the whole buffer
let e = match str::from_utf8(buf) { // as it can be quite large and we are going to discard the remaining chars anyway
// if there is a valid prefix
let prefix = if buf.len() > 4 { &buf[..4] } else { buf };
let e = match str::from_utf8(prefix) {
Ok(s) => { Ok(s) => {
let mut chars = s.chars(); let mut chars = s.chars();
let c = chars.next().unwrap(); let c = chars.next().expect(
"a non-empty buffer that is valid utf-8 contains at least one character",
);
return Some(Ok(c)); return Some(Ok(c));
} }
Err(e) => e, Err(e) => e,
}; };
if buf.len() - e.valid_up_to() >= 4 { if e.valid_up_to() != 0 {
// the valid prefix is non-empty so it is guaranteed that we can decode at least one char
let c = str::from_utf8(&prefix[..e.valid_up_to()])
.expect("prefix is verified valid up to this point")
.chars()
.next()
.expect("the valid prefix was non-empty");
return Some(Ok(c));
}
if e.error_len().is_some() {
return Some(Err(bad_bytes_error(buf))); return Some(Err(bad_bytes_error(buf)));
} 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..self.pos + e.valid_up_to()]) {
Ok(s) => {
let mut chars = s.chars();
let c = chars.next().unwrap();
Some(Ok(c))
}
Err(e) => {
let badbytes = self.buf[self.pos..self.pos + e.valid_up_to()].to_vec();
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];
} }
self.buf.truncate(buf_len - self.pos); // buf is too short to determine if the remaining bytes in buf are a valid char
// i.e. the content of buf is a prefix of a valid utf-8 encoded char
//
// we need to read more data from the underlying stream
// so that we can determine its validity
let buf_len = self.buf.len(); if self.buf.len() > 4 {
self.pos = 0; // keep a prefix of 4 bytes so that we can put back at least one char
self.buf.drain(4..self.pos);
if buf_len >= 4 { self.pos = 4;
continue;
} }
let mut word = [0u8; 4]; match self.read_chunk() {
let word_slice = &mut word[buf_len..4];
match self.inner.read(word_slice) {
Err(e) => return Some(Err(e)), Err(e) => return Some(Err(e)),
Ok(0) => return Some(Err(bad_bytes_error(&self.buf))), Ok(0) => return Some(Err(bad_bytes_error(&self.buf))),
Ok(nread) => { Ok(_) => {
self.buf.extend_from_slice(&word_slice[0..nread]); // successfully filled the buffer with another chunk of data
} }
} }
} }
} else {
return None; None
}
}
} }
#[inline(always)] #[inline(always)]
fn put_back_char(&mut self, c: char) { fn put_back_char(&mut self, c: char) {
let src_len = self.buf.len() - self.pos;
debug_assert!(src_len <= self.buf.capacity());
let c_len = c.len_utf8(); let c_len = c.len_utf8();
let mut shifted_slice = [0u8; 32]; if c_len <= self.pos {
self.pos -= c_len;
shifted_slice[0..src_len].copy_from_slice(&self.buf[self.pos..self.buf.len()]); } else {
self.buf.insert_from_slice(
self.buf.resize(c_len, 0); 0,
self.buf.extend_from_slice(&shifted_slice[0..src_len]); &[0u8; 4/* char::MAX_LEN_UTF8 once msrv reached 1.93 */][..c_len - self.pos],
);
self.pos = 0; self.pos = 0;
}
c.encode_utf8(&mut self.buf[0..c_len]); c.encode_utf8(&mut self.buf[self.pos..]);
} }
#[inline(always)] #[inline(always)]
@@ -381,6 +374,51 @@ mod tests {
assert!(read_string.read_char().is_none()); assert!(read_string.read_char().is_none());
} }
#[test]
fn interspersed_bad_utf8() {
let mut read_string = CharReader::new(Cursor::new(b"a string\xffmore_text\xff"));
for c in "a string".chars() {
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
}
assert_eq!(
read_string.peek_char().unwrap().unwrap_err().kind(),
std::io::ErrorKind::InvalidData
);
let err = read_string
.read_char()
.unwrap()
.unwrap_err()
.downcast::<BadUtf8Error>()
.unwrap();
read_string.consume(err.bytes.len());
for c in "more_text".chars() {
assert_eq!(read_string.peek_char().unwrap().ok(), Some(c));
assert_eq!(read_string.read_char().unwrap().ok(), Some(c));
}
assert_eq!(
read_string.peek_char().unwrap().unwrap_err().kind(),
std::io::ErrorKind::InvalidData
);
let err = read_string
.read_char()
.unwrap()
.unwrap_err()
.downcast::<BadUtf8Error>()
.unwrap();
read_string.consume(err.bytes.len());
assert!(read_string.read_char().is_none());
}
#[test] #[test]
fn greek_string() { fn greek_string() {
let mut read_string = CharReader::new(Cursor::new("λέξη")); let mut read_string = CharReader::new(Cursor::new("λέξη"));
@@ -406,7 +444,6 @@ 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ιενδι σιτ ει, ηαρθμ λεγερε αερενδθμ ιθσ νε. Ηασ νο εροσ
@@ -478,7 +515,6 @@ 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 = "լոռեմ իպսում դոլոռ սիթ ամեթ, նովում գռաեծո
սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում սեա եա, աբհոռռեանթ դիսպութանդո եի քուի. իդ քուոդ ինդոծթում
@@ -552,7 +588,6 @@ 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

@@ -16,7 +16,10 @@ macro_rules! consume_chars_with {
match $e { match $e {
Ok(Some(c)) => $token.push(c), Ok(Some(c)) => $token.push(c),
Ok(None) => continue, Ok(None) => continue,
Err($crate::parser::ast::ParserError::UnexpectedChar(..)) => break, Err($crate::parser::ast::ParserError {
kind: $crate::parser::ast::ParserErrorKind::UnexpectedChar(..),
..
}) => break,
Err(e) => return Err(e), Err(e) => return Err(e),
} }
} }
@@ -82,7 +85,7 @@ macro_rules! try_nt {
Ok(NumberToken::Partial($token)) Ok(NumberToken::Partial($token))
} else { } else {
Err(e) Err(e)
} };
} }
} }
}}; }};
@@ -91,27 +94,45 @@ macro_rules! try_nt {
pub(crate) struct Lexer<'a, R> { pub(crate) struct Lexer<'a, R> {
pub(crate) reader: R, pub(crate) reader: R,
pub(crate) machine_st: &'a mut MachineState, pub(crate) machine_st: &'a mut MachineState,
pub(crate) line_num: usize, pub(crate) location: Location,
pub(crate) col_num: usize,
} }
impl<'a, R: fmt::Debug> fmt::Debug for Lexer<'a, R> { impl<'a, R: fmt::Debug> fmt::Debug for Lexer<'a, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LexerParser") f.debug_struct("LexerParser")
.field("reader", &"&'a mut R") // Hacky solution. .field("reader", &"&'a mut R") // Hacky solution.
.field("line_num", &self.line_num) .field("location", &self.location)
.field("col_num", &self.col_num)
.finish() .finish()
} }
} }
impl<R> Lexer<'_, R> {
pub(crate) fn located_error(&self, kind: ParserErrorKind) -> ParserError {
ParserError {
location: Some(self.location.clone()),
kind,
}
}
pub(crate) fn parse_big_int_error(&self) -> ParserError {
self.located_error(ParserErrorKind::ParseBigInt)
}
pub(crate) fn incomplete_reduction(&self) -> ParserError {
self.located_error(ParserErrorKind::IncompleteReduction)
}
pub(crate) fn unexpected_char(&self, c: char) -> ParserError {
self.located_error(ParserErrorKind::UnexpectedChar(c))
}
}
impl<'a, R: CharRead> Lexer<'a, R> { impl<'a, R: CharRead> Lexer<'a, R> {
pub fn new(src: R, machine_st: &'a mut MachineState) -> Self { pub fn new(src: R, machine_st: &'a mut MachineState) -> Self {
Self { Self {
reader: src, reader: src,
machine_st, machine_st,
line_num: 0, location: Location::BOF,
col_num: 0,
} }
} }
@@ -138,10 +159,10 @@ impl<'a, R: CharRead> Lexer<'a, R> {
self.reader.consume(c.len_utf8()); self.reader.consume(c.len_utf8());
if new_line_char!(c) { if new_line_char!(c) {
self.line_num += 1; self.location.line += 1;
self.col_num = 0; self.location.column = 0;
} else { } else {
self.col_num += 1; self.location.column += 1;
} }
} }
@@ -200,10 +221,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
match comment_loop() { match comment_loop() {
Err(e) if e.is_unexpected_eof() => { Err(e) if e.is_unexpected_eof() => {
return Err(ParserError::IncompleteReduction( return Err(self.incomplete_reduction());
self.line_num,
self.col_num,
));
} }
Err(e) => { Err(e) => {
return Err(e); return Err(e);
@@ -215,7 +233,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
self.skip_char(c); self.skip_char(c);
Ok(true) Ok(true)
} else { } else {
Err(ParserError::NonPrologChar(self.line_num, self.col_num)) Err(self.located_error(ParserErrorKind::NonPrologChar))
} }
} else { } else {
self.return_char('/'); self.return_char('/');
@@ -232,7 +250,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if !back_quote_char!(c2) { if !back_quote_char!(c2) {
self.return_char(c); self.return_char(c);
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num)) Err(self.unexpected_char(c))
} else { } else {
self.skip_char(c2); self.skip_char(c2);
Ok(c2) Ok(c2)
@@ -257,7 +275,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
Ok(None) Ok(None)
} else { } else {
self.return_char(c); self.return_char(c);
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num)) Err(self.unexpected_char(c))
} }
} else { } else {
self.get_back_quoted_char().map(Some) self.get_back_quoted_char().map(Some)
@@ -279,10 +297,13 @@ impl<'a, R: CharRead> Lexer<'a, R> {
self.skip_char(c); self.skip_char(c);
Ok(token) Ok(token)
} else { } else {
Err(ParserError::MissingQuote(self.line_num, self.col_num)) Err({
let this = &self;
this.located_error(ParserErrorKind::MissingQuote)
})
} }
} else { } else {
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num)) Err(self.unexpected_char(c))
} }
} }
@@ -313,7 +334,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if !single_quote_char!(c2) { if !single_quote_char!(c2) {
self.return_char(c); self.return_char(c);
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num)) Err(self.unexpected_char(c))
} else { } else {
self.skip_char(c2); self.skip_char(c2);
Ok(c2) Ok(c2)
@@ -354,7 +375,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if !double_quote_char!(c2) { if !double_quote_char!(c2) {
self.return_char(c); self.return_char(c);
Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num)) Err(self.unexpected_char(c))
} else { } else {
self.skip_char(c2); self.skip_char(c2);
Ok(c2) Ok(c2)
@@ -378,7 +399,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
't' => '\t', 't' => '\t',
'n' => '\n', 'n' => '\n',
'r' => '\r', 'r' => '\r',
c => return Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num)), c => return Err(self.unexpected_char(c)),
}; };
self.skip_char(c); self.skip_char(c);
@@ -396,10 +417,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if hexadecimal_digit_char!(c) { if hexadecimal_digit_char!(c) {
self.escape_sequence_to_char(|c| hexadecimal_digit_char!(c), 16) self.escape_sequence_to_char(|c| hexadecimal_digit_char!(c), 16)
} else { } else {
Err(ParserError::IncompleteReduction( Err(self.incomplete_reduction())
self.line_num,
self.col_num,
))
} }
} }
@@ -425,17 +443,11 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if backslash_char!(c) { if backslash_char!(c) {
self.skip_char(c); self.skip_char(c);
u32::from_str_radix(&token, radix).map_or_else( u32::from_str_radix(&token, radix).map_or_else(
|_| Err(ParserError::ParseBigInt(self.line_num, self.col_num)), |_| Err(self.parse_big_int_error()),
|n| { |n| char::try_from(n).map_err(|_| self.located_error(ParserErrorKind::Utf8Error)),
char::try_from(n)
.map_err(|_| ParserError::Utf8Error(self.line_num, self.col_num))
},
) )
} else { } else {
Err(ParserError::IncompleteReduction( Err(self.incomplete_reduction())
self.line_num,
self.col_num,
))
} }
} }
@@ -447,7 +459,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
Ok(c) Ok(c)
} else { } else {
if !backslash_char!(c) { if !backslash_char!(c) {
return Err(ParserError::UnexpectedChar(c, self.line_num, self.col_num)); return Err(self.unexpected_char(c));
} }
self.skip_char(c); self.skip_char(c);
@@ -478,7 +490,10 @@ impl<'a, R: CharRead> Lexer<'a, R> {
self.skip_char(c); self.skip_char(c);
Ok(token) Ok(token)
} else { } else {
Err(ParserError::MissingQuote(self.line_num, self.col_num)) Err({
let this = &self;
this.located_error(ParserErrorKind::MissingQuote)
})
} }
} }
@@ -509,7 +524,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
.map(NumberToken::Integer) .map(NumberToken::Integer)
} else { } else {
self.return_char(start); self.return_char(start);
Err(ParserError::ParseBigInt(self.line_num, self.col_num)) Err(self.parse_big_int_error())
} }
} }
@@ -540,7 +555,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
.map(NumberToken::Integer) .map(NumberToken::Integer)
} else { } else {
self.return_char(start); self.return_char(start);
Err(ParserError::ParseBigInt(self.line_num, self.col_num)) Err(self.parse_big_int_error())
} }
} }
@@ -571,7 +586,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
.map(NumberToken::Integer) .map(NumberToken::Integer)
} else { } else {
self.return_char(start); self.return_char(start);
Err(ParserError::ParseBigInt(self.line_num, self.col_num)) Err(self.parse_big_int_error())
} }
} }
@@ -644,11 +659,11 @@ impl<'a, R: CharRead> Lexer<'a, R> {
} }
} }
} else { } else {
return Err(ParserError::InvalidSingleQuotedCharacter(c)); return Err(self.located_error(ParserErrorKind::InvalidSingleQuotedCharacter(c)));
} }
} else { } else {
match self.get_back_quoted_string() { match self.get_back_quoted_string() {
Ok(_) => return Err(ParserError::BackQuotedString(self.line_num, self.col_num)), Ok(_) => return Err(self.located_error(ParserErrorKind::BackQuotedString)),
Err(e) => return Err(e), Err(e) => return Err(e),
} }
} }
@@ -669,7 +684,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
) -> Result<(F64Offset, OrderedFloat<f64>), ParserError> { ) -> Result<(F64Offset, OrderedFloat<f64>), ParserError> {
self.return_char(token.pop().unwrap()); self.return_char(token.pop().unwrap());
let n = parse_float_lossy(&token)?; let n = self.parse_float_lossy(&token)?;
let offset = float_alloc!(n, self.machine_st.arena); let offset = float_alloc!(n, self.machine_st.arena);
Ok((offset, OrderedFloat(n))) Ok((offset, OrderedFloat(n)))
@@ -686,7 +701,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if decimal_digit_char!(c) { if decimal_digit_char!(c) {
Ok(c) Ok(c)
} else { } else {
Err(ParserError::ParseBigInt(self.line_num, self.col_num)) Err(self.parse_big_int_error())
} }
} else { } else {
Ok(c) Ok(c)
@@ -708,7 +723,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
.or_else(|_| { .or_else(|_| {
Integer::from_str_radix(token, radix) Integer::from_str_radix(token, radix)
.map(|n| GInteger::Integer(arena_alloc!(n, &mut self.machine_st.arena))) .map(|n| GInteger::Integer(arena_alloc!(n, &mut self.machine_st.arena)))
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num)) .map_err(|_| self.parse_big_int_error())
}) })
} }
@@ -756,7 +771,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
Err(_) => { Err(_) => {
return self return self
.vacate_with_float(token) .vacate_with_float(token)
.map(|(offset, fl)| NumberToken::Float(offset, fl)) .map(|(offset, fl)| NumberToken::Float(offset, fl));
} }
Ok(c) => c, Ok(c) => c,
}; };
@@ -806,7 +821,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
} }
} }
let n = parse_float_lossy(&token)?; let n = self.parse_float_lossy(&token)?;
let offset = float_alloc!(n, self.machine_st.arena); let offset = float_alloc!(n, self.machine_st.arena);
Ok(NumberToken::Float(offset, OrderedFloat(n))) Ok(NumberToken::Float(offset, OrderedFloat(n)))
@@ -815,7 +830,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
.map(|(offset, fl)| NumberToken::Float(offset, fl)) .map(|(offset, fl)| NumberToken::Float(offset, fl))
} }
} else { } else {
let n = parse_float_lossy(&token)?; let n = self.parse_float_lossy(&token)?;
let offset = float_alloc!(n, self.machine_st.arena); let offset = float_alloc!(n, self.machine_st.arena);
Ok(NumberToken::Float(offset, OrderedFloat(n))) Ok(NumberToken::Float(offset, OrderedFloat(n)))
} }
@@ -826,7 +841,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
} else if token.starts_with('0') && token.len() == 1 { } else if token.starts_with('0') && token.len() == 1 {
if c == 'x' { if c == 'x' {
self.hexadecimal_constant(c).or_else(|e| { self.hexadecimal_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e { if let ParserErrorKind::ParseBigInt = e.kind {
self.parse_integer(&token).map(NumberToken::Integer) self.parse_integer(&token).map(NumberToken::Integer)
} else { } else {
Err(e) Err(e)
@@ -834,7 +849,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}) })
} else if c == 'o' { } else if c == 'o' {
self.octal_constant(c).or_else(|e| { self.octal_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e { if let ParserErrorKind::ParseBigInt = e.kind {
self.parse_integer(&token).map(NumberToken::Integer) self.parse_integer(&token).map(NumberToken::Integer)
} else { } else {
Err(e) Err(e)
@@ -842,7 +857,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}) })
} else if c == 'b' { } else if c == 'b' {
self.binary_constant(c).or_else(|e| { self.binary_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e { if let ParserErrorKind::ParseBigInt = e.kind {
self.parse_integer(&token).map(NumberToken::Integer) self.parse_integer(&token).map(NumberToken::Integer)
} else { } else {
Err(e) Err(e)
@@ -871,9 +886,9 @@ impl<'a, R: CharRead> Lexer<'a, R> {
self.get_single_quoted_char() self.get_single_quoted_char()
.map(|c| NumberToken::Integer(GInteger::Fixnum(Fixnum::build_with(c)))) .map(|c| NumberToken::Integer(GInteger::Fixnum(Fixnum::build_with(c))))
.or_else(|err| { .or_else(|err| {
match err { match &err.kind {
ParserError::UnexpectedChar('\'', ..) => {} ParserErrorKind::UnexpectedChar('\'', ..) => {}
err => return Err(err), _ => return Err(err),
} }
self.return_char(c); self.return_char(c);
@@ -954,7 +969,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
Ok(NumberToken::Partial(token_string)) => match self.parse_integer(&token_string) { Ok(NumberToken::Partial(token_string)) => match self.parse_integer(&token_string) {
Ok(n) => Ok(Token::Literal(n.to_literal())), Ok(n) => Ok(Token::Literal(n.to_literal())),
Err(_) => { Err(_) => {
let n = parse_float_lossy(&token_string)?; let n = self.parse_float_lossy(&token_string)?;
let offset = float_alloc!(n, self.machine_st.arena); let offset = float_alloc!(n, self.machine_st.arena);
Ok(Token::Literal(Literal::F64(offset, OrderedFloat(n)))) Ok(Token::Literal(Literal::F64(offset, OrderedFloat(n))))
} }
@@ -1068,14 +1083,17 @@ impl<'a, R: CharRead> Lexer<'a, R> {
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
}
fn parse_float_lossy(token: &str) -> Result<f64, ParserError> { fn parse_float_lossy(&self, token: &str) -> Result<f64, ParserError> {
const FORMAT: u128 = lexical::format::STANDARD; const FORMAT: u128 = lexical::format::STANDARD;
let options = lexical::ParseFloatOptions::builder() let Ok(options) = lexical::ParseFloatOptions::builder().lossy(true).build() else {
.lossy(true) return Err(self.located_error(ParserErrorKind::ParseFloat));
.build() };
.unwrap();
let n = lexical::parse_with_options::<f64, _, FORMAT>(token.as_bytes(), &options)?; let Ok(n) = lexical::parse_with_options::<f64, _, FORMAT>(token.as_bytes(), &options)
else {
return Err(self.located_error(ParserErrorKind::ParseFloat));
};
Ok(n) Ok(n)
} }
}

View File

@@ -254,10 +254,7 @@ pub fn read_tokens<R: CharRead>(lexer: &mut Lexer<'_, R>) -> Result<Vec<Token>,
} }
} }
Err(e) if e.is_unexpected_eof() && !tokens.is_empty() => { Err(e) if e.is_unexpected_eof() && !tokens.is_empty() => {
return Err(ParserError::IncompleteReduction( return Err(lexer.incomplete_reduction());
lexer.line_num,
lexer.col_num,
));
} }
Err(e) => { Err(e) => {
return Err(e); return Err(e);
@@ -700,12 +697,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
} else { } else {
let term = match self.terms.pop() { let term = match self.terms.pop() {
Some(term) => term, Some(term) => term,
_ => { _ => return Err(self.lexer.incomplete_reduction()),
return Err(ParserError::IncompleteReduction(
self.lexer.line_num,
self.lexer.col_num,
))
}
}; };
if self.stack[idx].priority > 1000 { if self.stack[idx].priority > 1000 {
@@ -718,10 +710,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
}; };
if arity > self.terms.len() { if arity > self.terms.len() {
return Err(ParserError::IncompleteReduction( return Err(self.lexer.incomplete_reduction());
self.lexer.line_num,
self.lexer.col_num,
));
} }
let idx = self.terms.len() - arity; let idx = self.terms.len() - arity;
@@ -789,12 +778,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
let term = match self.terms.pop() { let term = match self.terms.pop() {
Some(term) => term, Some(term) => term,
_ => { _ => return Err(self.lexer.incomplete_reduction()),
return Err(ParserError::IncompleteReduction(
self.lexer.line_num,
self.lexer.col_num,
))
}
}; };
self.terms self.terms
@@ -972,10 +956,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
self.negate_number(n, negate_rat_rc, Literal::Rational) self.negate_number(n, negate_rat_rc, Literal::Rational)
} }
Token::Literal(Literal::F64(_offset, n)) if n.is_infinite() => { Token::Literal(Literal::F64(_offset, n)) if n.is_infinite() => {
return Err(ParserError::InfiniteFloat( return Err(self.lexer.located_error(ParserErrorKind::InfiniteFloat));
self.lexer.line_num,
self.lexer.col_num,
));
} }
Token::Literal(Literal::F64(offset, n)) => { Token::Literal(Literal::F64(offset, n)) => {
self.negate_number((offset, n), negate_f64, |(offset, n)| { self.negate_number((offset, n), negate_f64, |(offset, n)| {
@@ -997,28 +978,19 @@ impl<'a, R: CharRead> Parser<'a, R> {
Token::OpenCT => self.shift(Token::OpenCT, 1300, DELIMITER), Token::OpenCT => self.shift(Token::OpenCT, 1300, DELIMITER),
Token::Close => { Token::Close => {
if !self.reduce_term() && !self.reduce_brackets() { if !self.reduce_term() && !self.reduce_brackets() {
return Err(ParserError::IncompleteReduction( return Err(self.lexer.incomplete_reduction());
self.lexer.line_num,
self.lexer.col_num,
));
} }
} }
Token::OpenList => self.shift(Token::OpenList, 1300, DELIMITER), Token::OpenList => self.shift(Token::OpenList, 1300, DELIMITER),
Token::CloseList => { Token::CloseList => {
if !self.reduce_list()? { if !self.reduce_list()? {
return Err(ParserError::IncompleteReduction( return Err(self.lexer.incomplete_reduction());
self.lexer.line_num,
self.lexer.col_num,
));
} }
} }
Token::OpenCurly => self.shift(Token::OpenCurly, 1300, DELIMITER), Token::OpenCurly => self.shift(Token::OpenCurly, 1300, DELIMITER),
Token::CloseCurly => { Token::CloseCurly => {
if !self.reduce_curly()? { if !self.reduce_curly()? {
return Err(ParserError::IncompleteReduction( return Err(self.lexer.incomplete_reduction());
self.lexer.line_num,
self.lexer.col_num,
));
} }
} }
Token::HeadTailSeparator => { Token::HeadTailSeparator => {
@@ -1051,12 +1023,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
| Some(TokenType::OpenList) | Some(TokenType::OpenList)
| Some(TokenType::OpenCurly) | Some(TokenType::OpenCurly)
| Some(TokenType::HeadTailSeparator) | Some(TokenType::HeadTailSeparator)
| Some(TokenType::Comma) => { | Some(TokenType::Comma) => return Err(self.lexer.incomplete_reduction()),
return Err(ParserError::IncompleteReduction(
self.lexer.line_num,
self.lexer.col_num,
))
}
_ => {} _ => {}
}, },
} }
@@ -1066,12 +1033,12 @@ impl<'a, R: CharRead> Parser<'a, R> {
#[inline] #[inline]
pub fn add_lines_read(&mut self, lines_read: usize) { pub fn add_lines_read(&mut self, lines_read: usize) {
self.lexer.line_num += lines_read; self.lexer.location.line += lines_read;
} }
#[inline] #[inline]
pub fn lines_read(&self) -> usize { pub fn lines_read(&self) -> usize {
self.lexer.line_num self.lexer.location.line
} }
// on success, returns the parsed term and the number of lines read. // on success, returns the parsed term and the number of lines read.
@@ -1092,10 +1059,7 @@ impl<'a, R: CharRead> Parser<'a, R> {
self.reduce_op(1400); self.reduce_op(1400);
if self.terms.len() > 1 || self.stack.len() > 1 { if self.terms.len() > 1 || self.stack.len() > 1 {
return Err(ParserError::IncompleteReduction( return Err(self.lexer.incomplete_reduction());
self.lexer.line_num,
self.lexer.col_num,
));
} }
match self.terms.pop() { match self.terms.pop() {
@@ -1103,16 +1067,10 @@ impl<'a, R: CharRead> Parser<'a, R> {
if self.terms.is_empty() { if self.terms.is_empty() {
Ok(term) Ok(term)
} else { } else {
Err(ParserError::IncompleteReduction( Err(self.lexer.incomplete_reduction())
self.lexer.line_num,
self.lexer.col_num,
))
} }
} }
_ => Err(ParserError::IncompleteReduction( _ => Err(self.lexer.incomplete_reduction()),
self.lexer.line_num,
self.lexer.col_num,
)),
} }
} }
} }

View File

@@ -1,136 +1,417 @@
#![deny(unsafe_op_in_unsafe_fn)]
use core::marker::PhantomData; use core::marker::PhantomData;
use std::alloc; use std::alloc;
use std::cell::UnsafeCell; use std::cell::Cell;
use std::ptr; use std::ptr;
use std::sync::atomic::{AtomicPtr, Ordering};
trait PtrCellTrait: std::fmt::Debug {
fn new(val: *mut u8) -> Self;
fn get(&self) -> *mut u8;
/// Modifies the wrapped value.
fn set(&mut self, val: *mut u8);
/// Performs an atomic compare-and-swap on the wrapped value.
///
/// If the compare succeeded and `cb` returns `Some(new_ptr)`, stored `new_ptr` and returns `Ok(old_ptr).
///
/// If `cb` returns `None`, returns `Err(old_ptr)`.
///
/// May retry multiple times if the comparison fails.
fn try_update(&self, cb: impl Fn(*mut u8) -> Option<*mut u8>) -> Result<*mut u8, *mut u8>;
}
impl PtrCellTrait for Cell<*mut u8> {
fn new(val: *mut u8) -> Self {
Cell::new(val)
}
#[inline(always)]
fn get(&self) -> *mut u8 {
Cell::get(self)
}
#[inline(always)]
fn set(&mut self, val: *mut u8) {
Cell::set(self, val)
}
#[inline(always)]
fn try_update(&self, cb: impl Fn(*mut u8) -> Option<*mut u8>) -> Result<*mut u8, *mut u8> {
let val = Cell::get(self);
if let Some(new_val) = cb(val) {
Cell::set(self, new_val);
Ok(val)
} else {
Err(val)
}
}
}
impl PtrCellTrait for AtomicPtr<u8> {
fn new(val: *mut u8) -> Self {
AtomicPtr::new(val)
}
#[inline(always)]
fn get(&self) -> *mut u8 {
self.load(Ordering::Acquire)
}
#[inline(always)]
fn set(&mut self, val: *mut u8) {
*self.get_mut() = val;
}
#[inline]
fn try_update(&self, cb: impl Fn(*mut u8) -> Option<*mut u8>) -> Result<*mut u8, *mut u8> {
let mut prev = PtrCellTrait::get(self);
while let Some(next) = cb(prev) {
match self.compare_exchange_weak(prev, next, Ordering::Relaxed, Ordering::Acquire) {
x @ Ok(_) => return x,
Err(next_prev) => prev = next_prev,
}
}
Err(prev)
// TODO: replace with the following once 1.95 is the msrv:
// AtomicPtr::try_update(self, Ordering::Relaxed, Ordering::Acquire, cb)
}
}
/// Allows the choice of implementation for the mutable pointer in [`RawBlock`].
/// Can be one of:
/// - [`RawBlockSerial`] (using a [`Cell<*mut u8>`])
/// - [`RawBlockConcurrent`] (using a [`AtomicPtr<u8>`])
pub(crate) trait RawBlockConcurrency {
#[allow(private_bounds)]
type PtrCell: PtrCellTrait;
}
#[derive(Debug, Clone, Copy)]
pub struct RawBlockSerial();
#[derive(Debug, Clone, Copy)]
pub struct RawBlockConcurrent();
impl RawBlockConcurrency for RawBlockSerial {
type PtrCell = Cell<*mut u8>;
}
impl RawBlockConcurrency for RawBlockConcurrent {
type PtrCell = AtomicPtr<u8>;
}
use crate::machine::heap::AllocError;
pub trait RawBlockTraits { pub trait RawBlockTraits {
/// ## Safety
///
/// Must be non-zero.
fn init_size() -> usize; fn init_size() -> usize;
/// ## Safety
///
/// Must be constant.
///
/// Must respect the invariants of [`std::alloc::Layout::from_size_align()`], namely:
/// - must not be zero
/// - must be a power of two
/// - must not overflow `usize`
fn align() -> usize; fn align() -> usize;
} }
/// A block of memory with fast, lock-free appends.
///
/// ## Invariants
///
/// - `base.is_null()` iff `capacity == 0`
/// - if `!base.is_null()`, then `ptr.get()` is in the same allocation as `base`.
#[derive(Debug)] #[derive(Debug)]
pub struct RawBlock<T: RawBlockTraits> { pub struct RawBlock<T: RawBlockTraits, C: RawBlockConcurrency = RawBlockSerial> {
pub base: *const u8, base: *const u8,
pub top: *const u8, capacity: usize,
pub ptr: UnsafeCell<*mut u8>,
ptr: C::PtrCell,
_marker: PhantomData<T>, _marker: PhantomData<T>,
_c_marker: PhantomData<C>,
} }
impl<T: RawBlockTraits> RawBlock<T> { impl<T: RawBlockTraits, C: RawBlockConcurrency> RawBlock<T, C> {
#[inline] #[inline]
pub fn empty_block() -> Self { pub fn empty_block() -> Self {
RawBlock { RawBlock {
base: ptr::null(), base: ptr::null(),
top: ptr::null(), capacity: 0,
ptr: UnsafeCell::new(ptr::null_mut()), ptr: C::PtrCell::new(ptr::null_mut()),
_marker: PhantomData, _marker: PhantomData,
_c_marker: PhantomData,
} }
} }
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
pub fn new() -> Self { pub fn new() -> Result<Self, AllocError> {
let mut block = Self::empty_block(); let mut block = Self::empty_block();
unsafe { unsafe {
block.grow(); block.grow()?;
} }
block Ok(block)
} }
unsafe fn init_at_size(&mut self, cap: usize) { /// ## Safety
///
/// Assumes that the object has not been initialized before (ie. `self.base.is_null()`)
/// and assumes that `cap > 0`.
unsafe fn init_at_size(&mut self, cap: usize) -> Result<(), AllocError> {
debug_assert!(cap > 0);
debug_assert!(self.base.is_null());
// SAFETY:
// - Guaranteed by caller: `cap > 0`
// - Guaranteed by caller: `T::align()` respects the invariants of `Layout::from_size_align`
let new_base = unsafe {
let layout = alloc::Layout::from_size_align_unchecked(cap, T::align()); let layout = alloc::Layout::from_size_align_unchecked(cap, T::align());
let new_base = alloc::alloc(layout).cast_const(); alloc::alloc(layout).cast_const()
};
if new_base.is_null() { if new_base.is_null() {
panic!( return Err(AllocError);
"failed to allocate in init_at_size for {}",
std::any::type_name::<Self>()
);
}
self.base = new_base;
self.top = self.base.add(cap);
*self.ptr.get_mut() = self.base.cast_mut();
} }
pub unsafe fn grow(&mut self) -> bool { self.base = new_base;
self.capacity = cap;
self.ptr.set(self.base.cast_mut());
Ok(())
}
/// ## Safety
///
/// Invalidates all pointers previously obtained by [`RawBlock::get()`] or [`RawBlock::alloc()`].
pub unsafe fn grow(&mut self) -> Result<(), AllocError> {
self.debug_check_invariants();
if self.base.is_null() { if self.base.is_null() {
self.init_at_size(T::init_size()); // SAFETY:
true // - Guaranteed by caller: `T::init_size() > 0`
// - Asserted: `self.base.is_null()`
unsafe { self.init_at_size(T::init_size()) }
} else { } else {
let size = self.size(); let size = self.capacity();
let used_bytes = self.used_bytes();
// SAFETY:
// - Guaranteed by caller: `T::align()` respects the invariants of `Layout::from_size_align`
// - Asserted: `!self.base.is_null()`
// - Invariant: `self.base.is_null()` iff `self.capacity() == 0`
// - Thus `self.capacity() > 0`
let new_base = unsafe {
let layout = alloc::Layout::from_size_align_unchecked(size, T::align()); let layout = alloc::Layout::from_size_align_unchecked(size, T::align());
let new_base = alloc::realloc(self.base.cast_mut(), layout, size * 2).cast_const(); alloc::realloc(self.base.cast_mut(), layout, size * 2).cast_const()
};
if new_base.is_null() { if new_base.is_null() {
false Err(AllocError)
} else { } else {
self.base = new_base; self.base = new_base;
self.top = self.base.add(size * 2); self.capacity = size * 2;
*self.ptr.get_mut() = self.base.add(size).cast_mut(); // SAFETY:
true // - Invariant: `used_bytes < size`
// - Definition: `new_base` has allocation size `2 * size`
let new_ptr = unsafe { self.base.add(used_bytes).cast_mut() };
self.ptr.set(new_ptr);
self.debug_check_invariants();
Ok(())
} }
} }
} }
pub unsafe fn grow_new(&self) -> Option<Self> { pub unsafe fn grow_new(&self) -> Result<Self, AllocError> {
self.debug_check_invariants();
if self.base.is_null() { if self.base.is_null() {
Some(Self::new()) Self::new()
} else { } else {
let mut new_block = Self::empty_block(); let mut new_block = Self::empty_block();
new_block.init_at_size(self.size() * 2); // SAFETY:
if new_block.base.is_null() { // - Asserted: !self.base.is_null()
// allocation failed // - Invariant: self.base.is_null() iff self.capacity == 0
None // - Thus self.capacity > 0
} else { // - Definition: `new_block` was not yet initialized
let allocated = (*self.ptr.get()).addr() - self.base.addr(); unsafe {
self.base.copy_to(new_block.base.cast_mut(), allocated); new_block.init_at_size(self.capacity() * 2)?;
*new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut();
Some(new_block)
} }
let used_bytes = self.used_bytes();
// SAFETY:
// - Definition: `self.base` contains `self.allocated()` bytes
// - Invariant: `self.used_bytes() < self.allocated()`
unsafe {
self.base.copy_to(new_block.base.cast_mut(), used_bytes);
new_block.ptr.set(new_block.base.add(used_bytes).cast_mut());
}
new_block.debug_check_invariants();
Ok(new_block)
}
}
#[inline(always)]
fn debug_check_invariants(&self) {
if cfg!(debug_assertions) {
assert!(
self.ptr.get().cast_const() >= self.base,
"self.ptr = {:?} < {:?} = self.base",
self.ptr.get(),
self.base
);
assert!(self.used_bytes() <= self.capacity());
} }
} }
#[inline] #[inline]
pub fn size(&self) -> usize { pub fn capacity(&self) -> usize {
self.top.addr() - self.base.addr() self.capacity
} }
#[inline(always)] #[inline]
unsafe fn free_space(&self) -> usize { pub fn used_bytes(&self) -> usize {
debug_assert!( // SAFETY:
*self.ptr.get() as *const _ >= self.base, // - Invariant: `ptr` is in the same allocation as `base`
"self.ptr = {:?} < {:?} = self.base", unsafe { self.ptr.get().offset_from(self.base) as usize }
*self.ptr.get(),
self.base
);
self.top.addr() - (*self.ptr.get()).addr()
} }
pub unsafe fn alloc(&self, size: usize) -> *mut u8 { pub unsafe fn alloc(&self, size: usize) -> *mut u8 {
let aligned_size = size.next_multiple_of(size); self.debug_check_invariants();
if self.free_space() >= aligned_size {
let ptr = *self.ptr.get(); let aligned_size = size.next_multiple_of(T::align());
*self.ptr.get() = ptr.add(aligned_size) as *mut _;
ptr match self.ptr.try_update(|ptr| {
// SAFETY:
// - Invariant: `ptr` is in the same allocation as `base`
let free_bytes = unsafe { self.capacity() - ptr.offset_from(self.base) as usize };
if free_bytes >= aligned_size {
Some(unsafe { ptr.add(aligned_size) })
} else { } else {
ptr::null_mut() // Not enough space: don't allocate and return a null pointer
None
} }
}) {
Ok(ptr) => ptr,
Err(_) => ptr::null_mut(),
} }
} }
impl<T: RawBlockTraits> Drop for RawBlock<T> { /// Moves `ptr` back to `new_size`.
///
/// Note that this method does *not* deallocate what was placed in the [`RawBlock`].
/// Pointers to data past `new_size` remain valid until the next call to [`RawBlock::alloc()`].
pub fn shift_back(&mut self, new_size: usize) {
self.debug_check_invariants();
assert!(
new_size <= self.used_bytes(),
"Shrink cannot grow: new_size = {:?} > allocated = {:?}",
new_size,
self.used_bytes()
);
// SAFETY:
// - Asserted: new_size <= self.capacity
// - Definition: self.base := alloc(self.capacity)
let new_ptr = unsafe { self.base.add(new_size) };
debug_assert!(new_ptr as usize <= self.ptr.get() as usize,);
self.ptr.set(new_ptr.cast_mut());
self.debug_check_invariants();
}
/// Returns a pointer at a given `offset` within the block of memory.
///
/// Panics if that range of bytes wasn't allocated yet with [`RawBlock::alloc()`].
pub fn get(&self, offset: usize) -> *const u8 {
assert!(offset < self.used_bytes());
// SAFETY: Asserted.
unsafe { self.get_unchecked(offset) }
}
/// Returns a pointer at a given `offset` within the block of memory.
///
/// ## Safety
///
/// Assumes that `offset < self.capacity()`.
#[inline]
pub unsafe fn get_unchecked(&self, offset: usize) -> *const u8 {
debug_assert!(
offset < self.capacity(),
"offset out of bounds: offset is {:?} but {:?} bytes are available",
offset,
self.used_bytes()
);
// SAFETY: Guaranteed by caller.
unsafe { self.base.add(offset) }
}
/// ## Safety
///
/// `ptr` is a valid pointer be obtained from [`RawBlock::get()`] or [`RawBlock::alloc()`].
#[inline]
pub unsafe fn get_offset(&self, ptr: *const u8) -> usize {
// SAFETY:
// - Guaranteed by caller: `ptr` is still valid
// - Guranteed by caller: `ptr` was obtained from `get()` or `alloc()`
// - get() and alloc() return pointers in the same allocation as `self.base`
// - All functions modifying `self.base` invalidate pointers in their contract
// - Thus `ptr` and `self.base` originate from the same allocation
unsafe { ptr.offset_from(self.base) as usize }
}
}
impl<T: RawBlockTraits, C: RawBlockConcurrency> Drop for RawBlock<T, C> {
fn drop(&mut self) { fn drop(&mut self) {
if !self.base.is_null() { if !self.base.is_null() {
unsafe { unsafe {
let layout = alloc::Layout::from_size_align_unchecked(self.size(), T::align()); let layout = alloc::Layout::from_size_align_unchecked(self.capacity(), T::align());
alloc::dealloc(self.base as *mut _, layout); alloc::dealloc(self.base as *mut _, layout);
} }
}
}
}
self.top = ptr::null(); impl<T: RawBlockTraits> From<RawBlock<T, RawBlockConcurrent>> for RawBlock<T, RawBlockSerial> {
self.base = ptr::null(); fn from(other: RawBlock<T, RawBlockConcurrent>) -> Self {
*self.ptr.get_mut() = ptr::null_mut(); Self {
base: other.base,
capacity: other.capacity,
ptr: PtrCellTrait::new(other.ptr.get()),
_marker: PhantomData,
_c_marker: PhantomData,
}
}
}
impl<T: RawBlockTraits> From<RawBlock<T, RawBlockSerial>> for RawBlock<T, RawBlockConcurrent> {
fn from(other: RawBlock<T, RawBlockSerial>) -> Self {
Self {
base: other.base,
capacity: other.capacity,
ptr: PtrCellTrait::new(other.ptr.get()),
_marker: PhantomData,
_c_marker: PhantomData,
} }
} }
} }

View File

@@ -1,3 +1,8 @@
#[cfg(feature = "repl")]
pub mod fallback_mode;
#[cfg(feature = "repl")]
pub mod user_interaction;
use crate::parser::ast::*; use crate::parser::ast::*;
use crate::parser::lexer::Lexer; use crate::parser::lexer::Lexer;
use crate::parser::parser::*; use crate::parser::parser::*;
@@ -48,12 +53,11 @@ pub(crate) fn error_after_read_term<R>(
parser: &Parser<R>, parser: &Parser<R>,
) -> CompilationError { ) -> CompilationError {
if err.is_unexpected_eof() { if err.is_unexpected_eof() {
let line_num = parser.lexer.line_num; let location = &parser.lexer.location;
let col_num = parser.lexer.col_num;
// rough overlap with errors 8.14.1.3 k) & l) of the ISO standard here // rough overlap with errors 8.14.1.3 k) & l) of the ISO standard here
if !(line_num == prior_num_lines_read && col_num == 0) { if !(location.line() == prior_num_lines_read && location.column() == 0) {
return CompilationError::from(ParserError::IncompleteReduction(line_num, col_num)); return CompilationError::from(parser.lexer.incomplete_reduction());
} }
} }
@@ -98,13 +102,7 @@ pub(crate) fn set_prompt(value: bool) {
#[cfg(feature = "repl")] #[cfg(feature = "repl")]
#[inline] #[inline]
fn get_prompt() -> &'static str { fn get_prompt() -> &'static str {
unsafe { if unsafe { PROMPT } { "?- " } else { "" }
if PROMPT {
"?- "
} else {
""
}
}
} }
thread_local! { thread_local! {
@@ -379,9 +377,7 @@ impl<'a> TermWriter<'a> {
#[inline] #[inline]
fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), CompilationError> { fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), CompilationError> {
self.heap Ok(self.heap.push_cell(cell)?)
.push_cell(cell)
.map_err(CompilationError::FiniteMemoryInHeap)
} }
fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue { fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue {
@@ -475,10 +471,7 @@ impl<'a> TermWriter<'a> {
self.push_stub_addr()?; self.push_stub_addr()?;
} }
let cell = self let cell = self.heap.allocate_cstr(src)?;
.heap
.allocate_cstr(src)
.map_err(CompilationError::FiniteMemoryInHeap)?;
let new_h = self.heap.cell_len(); let new_h = self.heap.cell_len();
self.push_cell(cell)?; self.push_cell(cell)?;
@@ -496,10 +489,7 @@ impl<'a> TermWriter<'a> {
self.push_stub_addr()?; self.push_stub_addr()?;
} }
let cell = self let cell = self.heap.allocate_pstr(src)?;
.heap
.allocate_pstr(src)
.map_err(CompilationError::FiniteMemoryInHeap)?;
let tail_h = self.heap.cell_len(); let tail_h = self.heap.cell_len();
self.push_stub_addr()?; self.push_stub_addr()?;

46
src/read/fallback_mode.rs Normal file
View File

@@ -0,0 +1,46 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers};
use std::io::{Error, ErrorKind, Read};
// Provide graceful degradation limited support mode if reading from stdin that does not
// support terminal features.
//
// Retrieve a single byte from stdin, does not support full Unicode decoding; only convert
// byte to KeyEvent char if it falls within the ASCII subset of UTF-8.
// Does not support passing through Ctrl-C as a KeyEvent.
//
// We are not supporting multibyte UTF-8 encodings because we are only
// interested in single ASCII characters which are 1-byte UTF-8 characters.
// Multibyte non-ASCII characters would be ignored anyway, so it's not worth the effort.
//
// It is expected that `limited_support_read` will only be used when prompting for user
// input during solution enumeration and the available commands are single ASCII characters.
pub(crate) fn limited_support_read() -> std::io::Result<KeyEvent> {
#[allow(
clippy::unbuffered_bytes,
reason = "We are aware of `bytes()` performance pitfall but don't expect it to be relevant here, we are doing single byte user input I/O."
)]
let byte_or_none = std::io::stdin().bytes().next();
match byte_or_none {
Some(byte) => match byte {
Ok(b) => {
if b.is_ascii() {
Ok(map_ascii_to_keyevent(b as char))
} else {
Err(Error::new(ErrorKind::Unsupported, "not supported input"))
}
}
Err(e) => Err(e),
},
None => Err(Error::new(ErrorKind::UnexpectedEof, "EOF")),
}
}
fn map_ascii_to_keyevent(c: char) -> KeyEvent {
KeyEvent {
code: KeyCode::Char(c),
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::empty(),
}
}

View File

@@ -0,0 +1,43 @@
use crossterm::event::{Event, KeyEventKind, read};
pub(crate) use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use crossterm::tty::IsTty;
use crate::read::fallback_mode;
pub(crate) fn get_key() -> KeyEvent {
let key;
if supported_terminal() {
enable_raw_mode().expect("failed to enable raw mode");
loop {
let key_ = read();
if let Ok(Event::Key(key_)) = key_ {
if key_.kind != KeyEventKind::Release {
match key_.code {
KeyCode::Char(_) | KeyCode::Enter | KeyCode::Tab => {
key = key_;
break;
}
_ => (),
}
}
}
}
disable_raw_mode().expect("failed to disable raw mode");
} else {
// stdin is not supported terminal type, fallback to limited support mode.
loop {
let key_ = fallback_mode::limited_support_read();
if let Ok(key_) = key_ {
key = key_;
break;
}
}
}
key
}
fn supported_terminal() -> bool {
// If OS is Windows and stdin is not a tty, it's either a pipe or an unsupported terminal configuration.
cfg!(not(windows)) || std::io::stdin().is_tty()
}

View File

@@ -1,5 +1,5 @@
use rustyline::completion::Completer; use rustyline::completion::Completer;
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter}; use rustyline::highlight::{CmdKind, Highlighter, MatchingBracketHighlighter};
use rustyline::hint::Hinter; use rustyline::hint::Hinter;
use rustyline::validate::Validator; use rustyline::validate::Validator;
use rustyline::{Context, Helper as RlHelper, Result}; use rustyline::{Context, Helper as RlHelper, Result};
@@ -10,14 +10,14 @@ use crate::atom_table::{AtomString, AtomTable, STATIC_ATOMS_MAP};
// TODO: Maybe add validation to the helper // TODO: Maybe add validation to the helper
pub struct Helper { pub struct Helper {
highligher: MatchingBracketHighlighter, highlighter: MatchingBracketHighlighter,
pub atoms: Weak<AtomTable>, pub atoms: Weak<AtomTable>,
} }
impl Helper { impl Helper {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
highligher: MatchingBracketHighlighter::new(), highlighter: MatchingBracketHighlighter::new(),
atoms: Weak::new(), atoms: Weak::new(),
} }
} }
@@ -90,11 +90,11 @@ impl Completer for Helper {
impl Highlighter for Helper { impl Highlighter for Helper {
fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> { fn highlight<'l>(&self, line: &'l str, pos: usize) -> std::borrow::Cow<'l, str> {
self.highligher.highlight(line, pos) self.highlighter.highlight(line, pos)
} }
fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool { fn highlight_char(&self, line: &str, pos: usize, forced: CmdKind) -> bool {
self.highligher.highlight_char(line, pos, forced) self.highlighter.highlight_char(line, pos, forced)
} }
} }

View File

@@ -46,7 +46,7 @@ test_queries_on_builtins :-
\+ float([1,2,_]), \+ float([1,2,_]),
\+ (X is 3 rdiv 4, float(X)), \+ (X is 3 rdiv 4, float(X)),
\+ \+ (X is 3 rdiv 4, rational(X)), \+ \+ (X is 3 rdiv 4, rational(X)),
\+ rational(3), rational(3),
\+ rational(f(_)), \+ rational(f(_)),
\+ rational("sdfa"), \+ rational("sdfa"),
\+ rational(atom), \+ rational(atom),

View File

@@ -16,6 +16,8 @@
:- dynamic(disabled_init_file/0). :- dynamic(disabled_init_file/0).
:- dynamic(started/0). :- dynamic(started/0).
:- dynamic(custom_toplevel/1).
:- dynamic(g_caused_exception/2).
load_scryerrc :- load_scryerrc :-
( '$home_directory'(HomeDir) -> ( '$home_directory'(HomeDir) ->
@@ -53,7 +55,18 @@ start_repl :-
; true ; true
), ),
(\+ disabled_init_file -> load_scryerrc ; true), (\+ disabled_init_file -> load_scryerrc ; true),
repl. start_toplevel.
start_toplevel :-
( custom_toplevel(Goal) ->
catch(user:call(Goal),
Exception,
( print_exception(Exception),
halt(1)
)
)
; repl
).
args_consults_goals([], [], []). args_consults_goals([], [], []).
args_consults_goals([Arg|Args], Consults, Goals) :- args_consults_goals([Arg|Args], Consults, Goals) :-
@@ -64,19 +77,19 @@ arg_consults_goals(c(Mod), Args, [c(Mod)|Consults], Goals) :-
arg_consults_goals(g(Goal), Args, Consults, [g(Goal)|Goals]) :- arg_consults_goals(g(Goal), Args, Consults, [g(Goal)|Goals]) :-
args_consults_goals(Args, Consults, Goals). args_consults_goals(Args, Consults, Goals).
delegate_task([], []).
delegate_task([], Goals0) :- delegate_task([], Goals0) :-
(\+ disabled_init_file -> load_scryerrc ; true), (\+ disabled_init_file -> load_scryerrc ; true),
reverse(Goals0, Goals1), reverse(Goals0, Goals1),
args_consults_goals(Goals1, Consults, Goals), args_consults_goals(Goals1, Consults, Goals),
run_goals(Consults), run_goals(Consults),
run_goals(Goals), run_goals(Goals),
repl. start_toplevel.
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, ["-t"]) -> gather_toplevel(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
), ),
@@ -96,6 +109,8 @@ print_help :-
write('Print version information and exit'), nl, write('Print version information and exit'), nl,
write(' -g, --goal GOAL '), write(' -g, --goal GOAL '),
write('Run the query GOAL'), nl, write('Run the query GOAL'), nl,
write(' -t GOAL '),
write('Use GOAL as custom toplevel'), nl,
write(' -f '), write(' -f '),
write('Fast startup. Do not load initialization file (~/.scryerrc)'), nl, write('Fast startup. Do not load initialization file (~/.scryerrc)'), nl,
write(' --no-add-history '), write(' --no-add-history '),
@@ -117,6 +132,17 @@ gather_goal(Type, Args0, Goals) :-
Gs =.. [Type, Gs1], Gs =.. [Type, Gs1],
delegate_task(Args, [Gs|Goals]). delegate_task(Args, [Gs|Goals]).
gather_toplevel(Args0, Goals0) :-
length(Args0, N),
( N < 1 -> print_help, halt
; true
),
[TopLevel|Args] = Args0,
atom_chars(Goal, TopLevel),
retractall(custom_toplevel(_)),
asserta(custom_toplevel(Goal)),
delegate_task(Args, Goals0).
disable_init_file :- disable_init_file :-
asserta('disabled_init_file'). asserta('disabled_init_file').
@@ -154,7 +180,8 @@ run_goals([g(Gs0)|Goals]) :- !,
Exception, Exception,
( write_term(Goal, [variable_names(VNs),double_quotes(DQ)]), ( write_term(Goal, [variable_names(VNs),double_quotes(DQ)]),
write(' causes: '), write(' causes: '),
write_term(Exception, [double_quotes(DQ)]), nl % halt? write_term(Exception, [double_quotes(DQ)]), nl,
asserta(g_caused_exception(Goal, Exception))
) )
) -> true ) -> true
; write('% Warning: initialization failed for: '), ; write('% Warning: initialization failed for: '),
@@ -193,6 +220,77 @@ expand_op_list([Op | OtherOps], Pri, Spec, [(:- op(Pri, Spec, Op)) | OtherResult
expand_op_list(OtherOps, Pri, Spec, OtherResults). expand_op_list(OtherOps, Pri, Spec, OtherResults).
% Implement the include/1 directive via term expansion.
user:term_expansion((:- Include), Clauses) :-
nonvar(Include),
Include = include(File0),
( si:atom_si(File0) ->
atom_chars(File0, File),
format("% Warning: include/1: atom arguments are deprecated. Use chars for file paths:~n", []),
format("% :- include(\"~s\").~n", [File])
; error:must_be(chars, File0),
File = File0
),
'$toplevel':gather_clauses_from_file(File, Clauses).
gather_clauses_from_file(File, Clauses) :-
( file_exists(File) ->
setup_call_cleanup(open(File, read, Stream),
gather_clauses_(Stream, File, Clauses),
close(Stream))
; format("include/1: ~s does not exist.", [File]),
Clauses = []
).
gather_clauses_(Stream, _, []) :- at_end_of_stream(Stream), !.
gather_clauses_(Stream, File, Clauses) :-
catch((read(Stream, Clause),
Continue = true),
Error,
( Error = error(syntax_error(incomplete_reduction),_),
at_end_of_stream(Stream) ->
true
; format("~s: ~q~n", [File,Error])
)),
( Continue == true ->
( var(Clause) ->
format("~s: variable clause is ignored.~n", [File]),
gather_clauses_(Stream, File, Clauses)
; Clause = (?- _Query) ->
devour_answer_descriptions(Stream, File, Clauses)
; Clauses = [Clause|Rest],
gather_clauses_(Stream, File, Rest)
)
; Clauses = []
).
devour_answer_descriptions(Stream, File, Clauses) :-
catch((read(Stream, Clause),
Continue = true),
Error,
( Error = error(syntax_error(incomplete_reduction),_),
at_end_of_stream(Stream) ->
true
; format("~s: ~q~n", [File,Error])
)),
( Continue == true ->
( var(Clause) ->
format("~s: variable clause is ignored.~n", [File])
; Clause = (?- _Query) ->
devour_answer_descriptions(Stream, File, Clauses)
; loader:answer_description(Clause) ->
devour_answer_descriptions(Stream, File, Clauses)
; Clauses = [Clause|Rest],
gather_clauses_(Stream, File, Rest)
)
; Clauses = []
).
read_and_match :- read_and_match :-
'$read_query_term'(_, Term, _, _, VarList), '$read_query_term'(_, Term, _, _, VarList),
instruction_match(Term, VarList). instruction_match(Term, VarList).

View File

@@ -1,5 +1,3 @@
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
use crate::arena::*; use crate::arena::*;
use crate::atom_table::*; use crate::atom_table::*;
use crate::forms::*; use crate::forms::*;
@@ -18,7 +16,8 @@ use std::ops::{Add, Sub, SubAssign};
use dashu::{Integer, Rational}; use dashu::{Integer, Rational};
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] // Variant tag MUST be odd for all but Cons
#[derive(Specifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)] #[repr(u8)]
#[bits = 6] #[bits = 6]
pub enum HeapCellValueTag { pub enum HeapCellValueTag {
@@ -45,34 +44,7 @@ pub enum HeapCellValueTag {
TrailedBlackboardOffset = 0b110001, TrailedBlackboardOffset = 0b110001,
} }
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Specifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
#[bits = 6]
pub enum HeapCellValueView {
Str = 0b000001,
Lis = 0b000101,
Var = 0b001011,
StackVar = 0b001101,
AttrVar = 0b010001,
PStrLoc = 0b010011,
// constants.
Cons = 0b0,
F64Offset = 0b010101,
Fixnum = 0b011001,
CodeIndexOffset = 0b011011,
Atom = 0b011111,
CutPoint = 0b011101,
// trail elements.
TrailedHeapVar = 0b100001,
TrailedStackVar = 0b100011,
TrailedAttrVar = 0b100101,
TrailedAttrVarListLink = 0b101001,
TrailedAttachedValue = 0b101011,
TrailedBlackboardEntry = 0b101101,
TrailedBlackboardOffset = 0b110001,
}
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[bits = 1] #[bits = 1]
pub enum ConsPtrMaskTag { pub enum ConsPtrMaskTag {
Cons = 0b0, Cons = 0b0,
@@ -90,19 +62,46 @@ pub struct ConsPtr {
} }
impl ConsPtr { impl ConsPtr {
// ConstPtr.ptr has 61 bits if usize on the current arch is larger than that
// use the niche provided by the alignment of ArenaHeader.
// The niece is that the log2(alignment) least significant bits are always 0, so we can shift the
// address down by that many places without losing information.
// That way with an alignment of 8 a 64-bit address fits into 61-bits after shifting it down by 3 places
// and as a result be can store it without loss in ConsPtr.ptr
pub(crate) const NICHE_SHIFT: u32 = if usize::BITS > 61 {
std::mem::align_of::<ArenaHeader>().ilog2()
} else {
0
};
#[inline(always)] #[inline(always)]
pub fn build_with(ptr: *const ArenaHeader, tag: ConsPtrMaskTag) -> Self { pub fn from_ptr(ptr: *const ArenaHeader) -> Self {
Self::build_with(ptr, ConsPtrMaskTag::Cons)
}
#[inline(always)]
fn build_with(ptr: *const ArenaHeader, tag: ConsPtrMaskTag) -> Self {
let mut addr = u64::try_from(ptr.expose_provenance())
.expect("pointer address {ptr:p} should fit into u64");
debug_assert_eq!(addr % std::mem::align_of::<ArenaHeader>() as u64, 0);
addr >>= Self::NICHE_SHIFT;
ConsPtr::new() ConsPtr::new()
.with_ptr(ptr.expose_provenance() as u64) .with_ptr(addr)
.with_f(false) .with_f(false)
.with_m(false) .with_m(false)
.with_tag(tag) .with_tag(tag)
} }
#[inline(always)] #[inline(always)]
pub fn as_ptr(self) -> *mut u8 { pub fn as_ptr(self) -> *const ArenaHeader {
let addr: u64 = self.ptr(); let mut addr: u64 = self.ptr();
std::ptr::with_exposed_provenance_mut(addr as usize)
addr <<= Self::NICHE_SHIFT;
std::ptr::with_exposed_provenance(addr as usize)
} }
#[inline(always)] #[inline(always)]
@@ -111,7 +110,7 @@ impl ConsPtr {
} }
} }
#[derive(BitfieldSpecifier, Copy, Clone, Debug)] #[derive(Specifier, Copy, Clone, Debug)]
#[bits = 6] #[bits = 6]
pub(crate) enum RefTag { pub(crate) enum RefTag {
HeapCell = 0b001011, HeapCell = 0b001011,
@@ -198,7 +197,7 @@ pub enum TrailRef {
} }
#[allow(clippy::enum_variant_names)] // allow the common "Trailed" prefix #[allow(clippy::enum_variant_names)] // allow the common "Trailed" prefix
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Specifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[bits = 6] #[bits = 6]
pub(crate) enum TrailEntryTag { pub(crate) enum TrailEntryTag {
TrailedHeapVar = 0b101111, TrailedHeapVar = 0b101111,
@@ -365,7 +364,7 @@ where
{ {
#[inline] #[inline]
fn from(arena_ptr: TypedArenaPtr<T>) -> HeapCellValue { fn from(arena_ptr: TypedArenaPtr<T>) -> HeapCellValue {
HeapCellValue::from(arena_ptr.header_ptr().expose_provenance() as u64) HeapCellValue::from_arena_header_ptr(arena_ptr.header_ptr())
} }
} }
@@ -386,18 +385,6 @@ impl From<CodeIndexOffset> for HeapCellValue {
} }
} }
impl From<ConsPtr> for HeapCellValue {
#[inline(always)]
fn from(cons_ptr: ConsPtr) -> HeapCellValue {
HeapCellValue::from_bytes(
ConsPtr::from(cons_ptr.as_ptr().expose_provenance() as u64)
.with_tag(ConsPtrMaskTag::Cons)
.with_m(false)
.into_bytes(),
)
}
}
impl 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 {
@@ -587,12 +574,12 @@ impl HeapCellValue {
} }
#[inline] #[inline]
pub fn from_ptr_addr(ptr_bytes: usize) -> Self { pub fn from_arena_header_ptr(ptr: *const ArenaHeader) -> Self {
HeapCellValue::from_bytes((ptr_bytes as u64).to_ne_bytes()) HeapCellValue::from_bytes(ConsPtr::from_ptr(ptr).into_bytes())
} }
pub fn to_ptr_addr(self) -> usize { pub fn to_arena_header_ptr(self) -> *const ArenaHeader {
u64::from_ne_bytes(self.into_bytes()) as usize ConsPtr::from_bytes(self.into_bytes()).as_ptr()
} }
#[inline] #[inline]
@@ -736,21 +723,14 @@ const_assert!(mem::size_of::<UntypedArenaPtr>() == 8);
impl From<*const ArenaHeader> for UntypedArenaPtr { impl From<*const ArenaHeader> for UntypedArenaPtr {
#[inline] #[inline]
fn from(ptr: *const ArenaHeader) -> UntypedArenaPtr { fn from(ptr: *const ArenaHeader) -> UntypedArenaPtr {
UntypedArenaPtr::build_with(ptr.expose_provenance()) UntypedArenaPtr::from_bytes(ConsPtr::from_ptr(ptr).into_bytes())
}
}
impl From<*const IndexPtr> for UntypedArenaPtr {
#[inline]
fn from(ptr: *const IndexPtr) -> UntypedArenaPtr {
UntypedArenaPtr::build_with(ptr.expose_provenance())
} }
} }
impl From<UntypedArenaPtr> for *const ArenaHeader { impl From<UntypedArenaPtr> for *const ArenaHeader {
#[inline] #[inline]
fn from(ptr: UntypedArenaPtr) -> *const ArenaHeader { fn from(ptr: UntypedArenaPtr) -> *const ArenaHeader {
ptr.get_ptr().cast::<ArenaHeader>() ptr.get_ptr()
} }
} }
@@ -761,9 +741,8 @@ impl UntypedArenaPtr {
} }
#[inline] #[inline]
pub fn get_ptr(self) -> *const u8 { pub fn get_ptr(self) -> *const ArenaHeader {
let addr: u64 = self.ptr(); ConsPtr::from_bytes(self.into_bytes()).as_ptr()
std::ptr::with_exposed_provenance(addr as usize)
} }
#[inline] #[inline]
@@ -775,20 +754,15 @@ impl UntypedArenaPtr {
} }
} }
#[inline]
pub fn payload_offset(self) -> *const u8 {
unsafe { self.get_ptr().add(size_of::<ArenaHeader>()) }
}
/// # Safety /// # Safety
/// - this UntypedArenaPtr actuall pointee type is T /// - this UntypedArenaPtr actual pointee type is T
/// - the pointer must be non-null /// - the pointer must be non-null
#[inline] #[inline]
pub unsafe fn as_typed_ptr<T: ?Sized + ArenaAllocated>(self) -> TypedArenaPtr<T> pub unsafe fn as_typed_ptr<T: ?Sized + ArenaAllocated>(self) -> TypedArenaPtr<T>
where where
T::Payload: Sized, T::Payload: Sized,
{ {
T::typed_ptr(self) unsafe { T::typed_ptr(self) }
} }
#[inline] #[inline]

View File

@@ -1,10 +1,13 @@
use crate::forms::GenContext; use crate::forms::{BranchNumber, GenContext};
use crate::parser::ast::*; use crate::parser::ast::*;
use bit_set::*; use bit_set::*;
use fxhash::FxBuildHasher; use fxhash::FxBuildHasher;
use indexmap::{IndexMap, IndexSet}; use indexmap::{IndexMap, IndexSet};
use num_order::NumOrd;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::Arc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TempVarData { pub struct TempVarData {
@@ -13,20 +16,19 @@ pub struct TempVarData {
pub(crate) conflict_set: BitSet<usize>, pub(crate) conflict_set: BitSet<usize>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct BranchDesignator { pub struct BranchDesignator {
pub branch_stack_num: usize, pub branch_num: Arc<BranchNumber>,
pub branch_num: usize,
} }
impl BranchDesignator { impl BranchDesignator {
#[inline] #[inline]
pub fn is_sub_branch(&self) -> bool { pub fn is_sub_branch(&self) -> bool {
self.branch_stack_num > 0 self.branch_num.branch_num.num_gt(&0)
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub enum VarSafetyStatus { pub enum VarSafetyStatus {
Needed, Needed,
// which branch planted the last unsafe guarded instruction? It may still be needed. // which branch planted the last unsafe guarded instruction? It may still be needed.
@@ -35,27 +37,27 @@ pub enum VarSafetyStatus {
} }
impl VarSafetyStatus { impl VarSafetyStatus {
pub(crate) fn unneeded(current_branch: BranchDesignator) -> Self { pub(crate) fn unneeded(current_branch: &BranchDesignator) -> Self {
if current_branch.is_sub_branch() { if current_branch.is_sub_branch() {
VarSafetyStatus::LocallyUnneeded(current_branch) VarSafetyStatus::LocallyUnneeded(current_branch.clone())
} else { } else {
VarSafetyStatus::GloballyUnneeded VarSafetyStatus::GloballyUnneeded
} }
} }
#[inline] #[inline]
pub(crate) fn needed_if(needed: bool, branch_designator: BranchDesignator) -> Self { pub(crate) fn needed_if(needed: bool, branch_designator: &BranchDesignator) -> Self {
if needed { if needed {
VarSafetyStatus::Needed VarSafetyStatus::Needed
} else if branch_designator.branch_stack_num == 0 { } else if branch_designator.branch_num.branch_num.num_eq(&0) {
VarSafetyStatus::GloballyUnneeded VarSafetyStatus::GloballyUnneeded
} else { } else {
VarSafetyStatus::LocallyUnneeded(branch_designator) VarSafetyStatus::LocallyUnneeded(branch_designator.clone())
} }
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub enum PermVarAllocation { pub enum PermVarAllocation {
Done { Done {
shallow_safety: VarSafetyStatus, shallow_safety: VarSafetyStatus,
@@ -103,10 +105,8 @@ impl VarAlloc {
#[inline] #[inline]
pub(crate) fn set_register(&mut self, reg_num: usize) { pub(crate) fn set_register(&mut self, reg_num: usize) {
match self { match self {
VarAlloc::Perm(ref mut p, _) => *p = reg_num, VarAlloc::Perm(p, _) => *p = reg_num,
VarAlloc::Temp { VarAlloc::Temp { temp_reg, .. } => *temp_reg = reg_num,
ref mut temp_reg, ..
} => *temp_reg = reg_num,
}; };
} }
} }

View File

@@ -129,8 +129,7 @@ impl WasmQueryState {
let mut error = None; let mut error = None;
let mut to_drop = false; let mut to_drop = false;
match &mut self.inner { match &mut self.inner {
Some(ref mut inner) => { Some(inner) => inner.with_query_state_mut(|query_state| match query_state.next() {
inner.with_query_state_mut(|query_state| match query_state.next() {
Some(Ok(leaf_answer)) => { Some(Ok(leaf_answer)) => {
js_sys::Reflect::set(&ret, &"value".into(), &leaf_answer.into()).unwrap(); js_sys::Reflect::set(&ret, &"value".into(), &leaf_answer.into()).unwrap();
js_sys::Reflect::set(&ret, &"done".into(), &false.into()).unwrap(); js_sys::Reflect::set(&ret, &"done".into(), &false.into()).unwrap();
@@ -144,8 +143,7 @@ impl WasmQueryState {
js_sys::Reflect::set(&ret, &"done".into(), &true.into()).unwrap(); js_sys::Reflect::set(&ret, &"done".into(), &true.into()).unwrap();
to_drop = true; to_drop = true;
} }
}) }),
}
None => return Err(js_sys::Error::new("This query was already dropped").into()), None => return Err(js_sys::Error::new("This query was already dropped").into()),
} }

View File

@@ -0,0 +1,11 @@
:- use_module(library(dcgs)).
:- use_module(library(tabling)).
:- table expr//0.
expr --> "1".
expr --> expr, "+", expr.
run :- phrase(expr, "1+1+1+1+1").
:- initialization(run).

View File

@@ -0,0 +1,16 @@
:- use_module(library(os)).
:- use_module(library(ffi)).
init :-
read(Body),
term_variables(Body, [LIB]),
Body,
use_foreign_module(LIB, [
'ffi_invalid_utf8_cstr'([], cstr)
]).
test :-
ffi:'ffi_invalid_utf8_cstr'(Str),
write(Str), nl.
:- initialization((init,test)).

View File

@@ -1,5 +1,26 @@
:- module(http_open_hanging, [submit_request/0]). % Server
:- use_module(library(process)).
:- use_module(library(iso_ext)).
:- use_module(library(os)).
prolog_path(Prolog) :-
read(Body),
term_variables(Body, [Prolog]),
Body.
server_start([Process,Out]) :-
prolog_path(Prolog),
process_create(Prolog,
["tests-pl/issue-http_open-hanging_server", "-t", "server"],
[process(Process), stdout(pipe(Out))]).
server_wait_start([_Process, Out]) :-
get_char(Out, _C).
server_stop([Process,_Out]) :-
process_kill(Process).
% Client
:- use_module(library(charsio)). :- use_module(library(charsio)).
:- use_module(library(http/http_open)). :- use_module(library(http/http_open)).
@@ -10,14 +31,21 @@ send_request :-
request_headers([]), request_headers([]),
headers(_) headers(_)
], ],
http_open("https://scryer.pl", _Stream, Options), http_open("http://localhost:8472", _Stream, Options),
write_term('received response with status code':StatusCode, []), nl. write_term('received response with status code':StatusCode, []), nl.
main :- main :-
setup_call_cleanup(
server_start(Server),
(
server_wait_start(Server),
send_request, send_request,
send_request, send_request,
send_request, send_request,
send_request, send_request,
send_request. send_request
),
server_stop(Server)
).
:- initialization(main). :- initialization(main).

View File

@@ -0,0 +1,8 @@
:- use_module(library(process)).
:- use_module(library(http/http_server)).
server :-
http_listen(8472, [get(/, hello)]).
hello(_Req, Res) :-
http_body(Res, text("Ok")).

View File

@@ -0,0 +1,38 @@
% Server
:- use_module(library(process)).
:- use_module(library(charsio)).
:- use_module(library(lists)).
:- use_module(library(time)).
prolog_path(Prolog) :-
read(Body),
term_variables(Body, [Prolog]),
Body.
main :-
prolog_path(Prolog),
CMD = "\
use_module(library(os)), \
pid(PID), \
write(PID), nl, \
asserta((f :- f)), \
catch(f, Err, (write(Err),nl)), \
write(done), nl, \
halt.",
process_create("tests-pl/pty_exec.py", [Prolog, "-g", CMD], [stdout(pipe(O))]),
get_line_to_chars(O, PID0, ""),
append(PID, "\r\n", PID0),
process_create("kill", ["-s", "INT", PID], []),
sleep(5),
% second kill should fail because process should exit after one kill
process_create("kill", ["-s", "INT", PID], [stderr(null), process(PK)]),
process_wait(PK, Status),
status_report(Status, PID), nl.
status_report(exit(1), _PID) :- write(ok).
status_report(exit(0), PID) :-
write(not_dead),
% kill by force to exit cleanly
process_create("kill", ["-9", PID], []).
:- initialization(main).

2
tests-pl/issue2914.pl Normal file
View File

@@ -0,0 +1,2 @@
:- initialization(main).
main :- ( current_prolog_flag(X, X) -> write(true) ; write(false) ).

5
tests-pl/issue3256.pl Normal file
View File

@@ -0,0 +1,5 @@
:- use_module(library(sgml)).
test :- load_xml("<foo><bar>hello</bar></foo>", Es, []), write(Es).
:- initialization(test).

4
tests-pl/issue3262.pl Normal file
View File

@@ -0,0 +1,4 @@
:- initialization(main).
main :-
read(Term),
write(Term).

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