Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/trealla-prolog/trealla
A compact, efficient Prolog interpreter written in plain-old C.
https://github.com/trealla-prolog/trealla
c iso-prolog-standard prolog prolog-implementation prolog-interpreter prolog-programming-language
Last synced: 3 months ago
JSON representation
A compact, efficient Prolog interpreter written in plain-old C.
- Host: GitHub
- URL: https://github.com/trealla-prolog/trealla
- Owner: trealla-prolog
- License: mit
- Created: 2022-08-08T06:03:02.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-10-29T20:55:21.000Z (3 months ago)
- Last Synced: 2024-10-29T22:53:04.764Z (3 months ago)
- Topics: c, iso-prolog-standard, prolog, prolog-implementation, prolog-interpreter, prolog-programming-language
- Language: C
- Homepage:
- Size: 3.87 MB
- Stars: 271
- Watchers: 14
- Forks: 13
- Open Issues: 33
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-ccamel - trealla-prolog/trealla - A compact, efficient Prolog interpreter written in plain-old C. (C)
README
Trealla Prolog
==============A compact, efficient Prolog interpreter with ISO Prolog aspirations.
MIT licensed
Integers & Rationals are unbounded
Atoms are UTF-8 of unlimited length
The default double-quoted representation is *chars* list
Strings & slices are super-efficient (especially with mmap'd files)
REPL with history
Runs on Linux, Android, FreeBSD, macOS, and WebAssembly (WASI) & Go
API for calling from C (or by using WASM from Go & JS)
Foreign function interface (FFI) for calling out to user code
Access SQLITE databases using builtin module (uses FFI)
Concurrency via tasks / linda / futures / engines (generators)
Pre-emptive multi-threading
Blackboarding primitives
...
FFIs for GNU Scientific Library (GSL), SQLite, Raylib
Delimited continuations ##EXPERIMENTAL##
Rational trees ##EXPERIMENTAL##
CLP(Z) ##EXPERIMENTAL##Available from: [https://github.com/trealla-prolog/trealla](https://github.com/trealla-prolog/trealla).
Runs with [Jupyter Notebooks](https://github.com/LogtalkDotOrg/logtalk-jupyter-kernel#readme).
Logo
====![Trealla Logo: Trealla](trealla.png)
Usage
=====tpl [options] [files] [-- args]
where options can be:
-O0, --noopt - no optimization
-f file - load file (*~/.tplrc* not loaded)
-l file - load file (*~/.tplrc* loaded)
file - load file (*~/.tplrc* loaded)
-g goal - query goal (only used once)
--library path - alt to TPL_LIBRARY_PATH env var
-t, --trace - trace
-q, --quiet - quiet mode (no banner)
-v, --version - version
-h, --help - help
-d, --daemonize - daemonize
-w, --watchdog - create watchdog
--consult - consult from STDINFor example:
tpl -g test2,halt samples/sieve
Invocation without any goal presents the REPL.
The default path to the library is relative to the executable location.
The file *~/.tplrc* is consulted on startup unless the *-f* option is present.
When consulting, reconsulting and deconsulting files the *.pl* version
of the filename is always preferred (if not specified) when looking for a
file.A note on UTF-8
===============Trealla uses UTF-8 internally and this works well with modern operating
systems that are already [[1](https://www.utf8everywhere.org/)], or moving to
[[2](https://en.wikipedia.org/wiki/Unicode_in_Microsoft_Windows#UTF-8)],
native UTF-8.It aligns well with standard C as functions like strcmp/memcmp that
require no special handling to respect codepoint order. This also works
seamlessly with the implementation of double-quoted *strings* (ie.
chars-list), DCGs, and mmap'd files. Any code-point specific
requirements, like *get_char*, *get_code*, *sub_atom*, *atom_length*,
*atom_codes*, *atom_chars* & *_upper/*_lower are handled on the fly.UTF-8 atoms do not need to be quoted unless they contain breaking
characters...```console
?- [user].
是. % be: means, approximately, "True".
不是 :- \+ 是. % not be: means, approximately, "False".
true.
?- 是.
true.
?- 不是.
false.
``````console
?- X = 国字.
X = 国字.
?-
```Trealla accepts as a var any atom beginning with an uppercase
character...```console
?- atom_upper(δ,C).
C = Δ.
?- Δ is 123456-123455.
Δ = 1.
?-
```Building
========Written in plain-old C99.
git clone https://github.com/trealla-prolog/trealla.git
cd treallaOn Debian+ systems you may need to install GNU readline, xxd & libffi
sudo apt install libreadline-dev xxd libffi-dev
Then...
make
To build without libffi:
make NOFFI=1
On Debian+ systems you may need to install OpenSSL:
sudo apt install libssl-dev
unless you choose to build without SSL/TLS support:
make NOSSL=1
To build without pre-emptive multi-threading support:
make NOTHREADS=1
To build with the included ISOCLINE sources (default is to use GNU Readline):
make ISOCLINE=1
Older compilers may require:
make NOPEDANTIC=1
to avoid issues with newer flags.
Finally...
make install
to install locally.
Optionally...
make test
and there should be no errors, Further (if valgrind is installed)...
make check # or, more extensively
make leaksShould show no memory out-of-bounds, null-pointer, use after free
or memory leaks (there may be one perhaps spurious error).On *BSD* systems use *gmake* to build and do
pkg install xxd
or
pkg install editors/vim # if necessary
to get the *xxd* utility.
For unbounded arithmetic Trealla uses a modified fork of the
[imath](https://github.com/infradig/imath)
library, which is partially included in the source. Note, unbounded
integers (aka. bigints) are for arithmetic purposes only and will give a
type_error when used in places not expected. The *imath* library has a bug
whereby printing large numbers becomes exponentially slower (100K+ digits)
and will require a switch to *libtomath* at some point to remedy.WebAssembly (WASI)
==================Trealla has support for WebAssembly System Interface (WASI).
For an easy build envrionment, set up
[wasi-sdk](https://github.com/WebAssembly/wasi-sdk).
[Binaryen](https://github.com/WebAssembly/binaryen) is needed for optimization.To build the WebAssembinary binary, set CC to wasi-sdk's clang:
make CC=/opt/wasi-sdk/bin/clang wasm
Setting WASI_CC also works as an alternative to CC.
Cross-compile for Windows x64
=============================To cross-compile on Linux and produce a Windows/x86-64 executable...
sudo apt install mingw-w64
make WIN=1```console
$ file tpl.exe
tpl.exe: PE32+ executable (console) x86-64, for MS Windows
```Some have reported success with a native Windows build using msys2.
Cross-compile for Linux x86
===========================To cross-compile on Linux and produce a Linux/x86-32 executable...
sudo apt install gcc-multilib
sudo apt install libssl-dev:i386 libffi-dev:i386 libreadline-dev:i386
make OPT=-m32```console
$ file tpl
tpl: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=31f643d7a4cfacb0a34e81b7c12c78410493de60, for GNU/Linux 3.2.0, with debug_info, not stripped
```Contributions
=============Contributions are welcome.
Acknowledgements
================This project (in current incarnation) started in March 2020 and it
would not be where it is today without help from these people:- [Xin Wang](https://github.com/dram)
- [Paulo Moura](https://github.com/pmoura)
- [Markus Triska](https://github.com/triska)
- [Jos De Roo](https://github.com/josd)
- [Ulrich Neumerkel](https://github.com/uwn)
- [Guregu](https://github.com/guregu)Strings
=======Double-quoted strings, when *set_prolog_flag(double_quotes,chars)* is set
(which is the default) are stored as packed UTF-8 byte arrays. This is
compact and efficient. Such strings emulate a list representation and
from the programmer point of view are very much indistinguishable from
lists.A good use of such strings is *open(filename,read,Str,[mmap(Ls))*
which gives a memory-mapped view of a file as a string *Ls*. List
operations on files are now essentially zero-overhead! DCG applications
will gain greatly (*phrase_from_file/[2-3]* uses this).Both strings and atoms make use of low-overhead reflist-counted byte slices
where appropriate.Non-standard predicates
=======================help/0
help/1 # help(+functor) or help(+PI)
help/2 # help(+PI,+atom) where *atom* can be *swi* or *tau*module_help/1 # help(+module)
module_help/2 # help(+module,+functor) or help(+module,+PI)
module_help/3 # help(+module,+PI,+atom) where *atom* can bw *swi* or *tau*source_info/2 # source_info(+PI, -list)
module_info/2 # module_info(+atom, -list)module/1 # module(?atom)
modules/1 # modules(-list)load_text/2 # load_text(+atom,+opts)
listing/0
listing/1 # listing(+PI)abolish/2 # abolish(+pi,+list)
pretty/1 # pretty-print version of listing/1
between/3
msort/2 # version of sort/3 with duplicates
samsort/2 # same as msort/2
merge/3
format/[1-3]
portray_clause/[1-2]
predicate_property/2
evaluable_property/2
numbervars/[1,3-4]
e/0
name/2
tab/[1,2]get_unbuffered_code/1 # read a single unbuffered code
get_unbuffered_char/1 # read a single unbuffered characterread_from_atom/2 # read_from_atom(+atom,?term)
read_from_chars/2 # read_from_chars(+chars,?term)
read_term_from_atom/3 # read_term_from_atom(+atom,?term,+optlist)
read_term_from_chars/3 # read_term_from_chars(+chars,?term,+optlist)read_from_chars_/3 # read_from_chars+(?term,+chars,-rest)
read_term_from_chars_/4 # read_term_from_chars+(?term,+optlist,+chars,-rest)write_term_to_atom/3 # write_term_to_atom(?atom,?term,+oplist)
write_canonical_to_atom/3 # write_canonical_to_atom(?atom,?term,+oplist)
term_to_atom/2 # term_to_atom(?atom,?term)setrand/1 # set_seed(+integer) set random number seed
srandom/1 # set_seed(+integer) set random number seed
set_seed/1 # set_seed(+integer) set random number seed
get_seed/1 # get_seed(-integer) get random number seed
rand/1 # rand(-integer) integer [0,RAND_MAX]
random/1 # random(-float) float [0.0,<1.0]
random_between/3 # random_between(+int,+int,-int) integer [arg1,)/2 # soft-cut
call_det/2 # call_det(+call,?boolean)
term_attvars/2 # term_attvars(+term,-Vs)
copy_term_nat/2 # doesn't copy attrs
copy_term/3 # copy_term(+term1,-term2,-Goals)
unifiable/3 # unifiable(+term1,+term2,-Goals)
?=/2 # ?=(+term1,+term2)
term_expansion/2
goal_expansion/2
cyclic_term/1
term_singletons/2
findall/4
sort/4
ignore/1
is_list/1
is_partial_list/1
is_list_or_partial_list/1
is_stream/1
term_hash/2
term_hash/3 # ignores arg2 (options)
time/1
inf/0
nan/0
\uXXXX and \UXXXXXXXX # Unicode escapes for JSON)
gcd/2
uuid/1 # uuid(-string)
load_files/[1,2]
split_string/4 # SWI-compatible
module/1
line_count/2
atom_number/2
repeat/1 # repeat(+integer)
make/0rdiv/2 # evaluable
numerator/1 # evaluable
denominator/1 # evaluable
rational/1with_output_to(chars(Cs), Goal) # SWI-compatible
with_output_to(string(Cs), Goal) # SWI-compatible
with_output_to(atom(Atom), Goal) # SWI-compatiblecall_with_time_limit/2 # SWI-compatible
time_out/3 # SICStus-compatiblebb_b_put/2 # bb_b_put(:atom, +term)
bb_put/2 # bb_put(:atom, +term)
bb_get/2 # bb_get(:atom, ?term)
bb_update/3 # bb_update(:atom, ?term, ?term)
bb_delete/2 # bb_delete(:atom, ?term)posix_strftime/3 # posix_strftime(+format,-text,+tm(NNN,...))
posix_strptime/3 # posix_strptime(+format,+text,-tm(NNN,...))
posix_mktime/2 # posix_mktime(+tm(NNN,...),-seconds)
posix_gmtime/2 # posix_gmtime(+seconds,-tm(NNN,...))
posix_localtime/2 # posix_localtime(+seconds,-tm(NNN,...))
posix_ctime/2 # posix_time(+seconds,-atom)
posix_time/1 # posix_time(-seconds)
posix_getpid/1 # posix_pid(-pid)
posix_getppid/1 # posix_ppid(-pid)
posix_fork/1 # posix_fork(-pid)nb_setval(K,V)
nb_getval(K,V)
nb_delete(K)
nb_current(K,V)b_setval(K,V)
b_getval(K,V)
b_delete(K)call_nth/2
offset/2
limit/2getenv/2
setenv/2
unsetenv/1directory_files/2
delete_file/1
exists_file/1 # also file_exists/1
rename_file/2
copy_file/2
time_file/2
size_file/2
exists_directory/1 # also directory_exists/1
make_directory/1
make_directory_path/1
working_directory/2
chdir/1
absolute_file_name/[2,3] # expand(Bool) & relative_to(file) options
is_absolute_file_name/1
access_file/2
set_stream/2 # only supports alias/1 property
alias/2 # alias(?integer,+atom)recorda/2-3
recordz/2-3
recorded/2-3
instance/2
erase/1string_upper/2
string_lower/2
atom_upper/2
atom_lower/2divmod/4 # SWI-compatible
popcount/1 # function returning number of 1 bits
lsb/1 # function returning the least significant bit of a positive integer (count from zero)
msb/1 # function returning the most significant bit of a positive integer (count from zero)
log10/1 # function returning log10 of arg
now/0 # function returning C-time in secs as integer
now/1 # now (-integer) C-time in secs as integer
get_time/1 # get_time(-Var) elapsed wall time in secs as float
cpu_time/1 # cpu_time(-Var) elapsed CPU time in secs as floatcurrent_key/1
string_concat/3 # string_concat(+string,+string,?string)
string_length/2
sleep/1 # sleep time in secs
split/4 # split(+string,+sep,?left,?right)
shell/1
shell/2
wall_time/1
date_time/6
date_time/7
loadfile/2 # loadfile(+filename,-string)
savefile/2 # savefile(+filename,+string)
getfile/2 # getfile(+filename,-strings)
getfile/3 # getfile(+filename,-strings,+opts)
getline/1 # getline(-string)
getline/2 # getline(+stream,-string)
getline/3 # getline(+stream,-string,+opts)
getlines/1 # getlines(-strings)
getlines/2 # getlines(+stream,-strings)
getlines/3 # getlines(+stream,-strings,+opts)
read_line_to_codes/2 # removes terminator
read_line_to_string/2 # removes terminator
read_file_to_string/3
bread/3 # bread(+stream,?len,-string)
bwrite/2 # bwrite(+stream,+string)
replace/4 # replace(+string,+old,+new,-string)open(stream(Str),...) # with open/4 reopen a stream
open(F,M,S,[mmap(Ls)]) # with open/4 mmap() the file to Lsreset/3 # reset(:goal,?ball,-cont)
shift/1 # shift(+ball)term_variables/3
Note: consult/1 and load_files/2 support lists of files as args. Also
support loading into modules eg. *consult(MOD:FILE-SPEC)*.Use these *POSIX* system calls for interprocess creation and
communication...popen/3 # popen(+cmd,+mode,-stream)
popen/4 # popen(+cmd,+mode,-stream,+opts)For example...
```console
tpl -g "use_module(library(apply)),popen('ps -a',read,S,[]),getlines(S,Ls),close(S),maplist(print,Ls),halt"
PID TTY TIME CMD
2806 tty2 00:00:00 gnome-session-b
31645 pts/0 00:00:00 tpl
31646 pts/0 00:00:00 sh
31647 pts/0 00:00:00 ps
```For general *POSIX* process creation use these *SWI-compatible* calls...
process_create/3 # process_create(+cmd,+args,+opts)
process_wait/2 # process_wait(+pid,+opts)
process_wait/1 # process_wait(+pid)
process_kill/2 # process_kill(+pid,+sigint)
process_kill/1 # process_kill(+pid)For example...
```console
?- process_create('ls',['-l'],[process(Pid)]),process_wait(Pid).
total 2552
4 -rw-rw-r-- 1 andrew andrew 1813 Aug 25 10:18 ATTRIBUTION
4 -rw-rw-r-- 1 andrew andrew 1093 Aug 25 10:18 LICENSE
8 -rw-rw-r-- 1 andrew andrew 7259 Sep 18 18:27 Makefile
24 -rw-rw-r-- 1 andrew andrew 23709 Sep 19 08:56 README.md
4 -rw-rw-r-- 1 andrew andrew 28 Aug 25 10:18 _config.yml
4 drwxrwxr-x 2 andrew andrew 4096 Sep 17 10:41 docs
4 drwxrwxr-x 2 andrew andrew 4096 Sep 18 21:29 library
4 drwxrwxr-x 2 andrew andrew 4096 Sep 3 13:02 samples
4 drwxrwxr-x 6 andrew andrew 4096 Sep 19 09:38 src
4 drwxrwxr-x 5 andrew andrew 4096 Sep 14 20:49 tests
1448 -rwxrwxr-x 1 andrew andrew 1478712 Sep 19 09:38 tpl
8 -rw-rw-r-- 1 andrew andrew 7671 Aug 25 10:18 tpl.c
16 -rw-rw-r-- 1 andrew andrew 13928 Sep 18 18:28 tpl.o
36 -rw-rw-r-- 1 andrew andrew 33862 Aug 25 10:18 trealla.png
Pid = 735602.
?-
```Note: read_term/[2,3] supports the positions(Start,End) and the
line_counts(Start,End) property options to report file information.
This is analogous to stream_property/2 use of position(Pos) and
line_count(Line) options.Note: read_term, write_term & friends support the *json(Boolean)*
option to make more sympathetic support for JSON using the builtin
parsing and printing mechanisms.Definite Clause Grammars
========================Uses Ulrich Neumerkel's standard reference library. DCG rules are
translated automatically as this library is auto-included.:- use_module(library(dcgs)).
Crypto functions
================Hash a plain-text data string to a hexadecimal byte string
representing the cryptographic strength hashed value. The options
are *algorithm(Name)* where *Name* can be *sha256*, *sha384* or
*sha512*, and optionally *hmac(Key)* where *Key* is a list of byte
values. This predicate is only available when compiled with OpenSSL...crypto_data_hash/3 # crypto_data_hash(+data,-hash,+options)
Generate 'N' random bytes.
crypto_n_random_bytes(N, Bs) # crypto_n_random_bytes(+integer, -codes)
Convert a hexadecimal string to a byte-list. At least one arg must be
instantiated...hex_bytes/2 # hex_bytes(?hash,?bytes)
Parsing CSV with builtins
=========================Fast, efficient parsing of CSV files...
parse_csv_line/2 # parse_csv_line(+atom,-list)
parse_csv_line/3 # parse_csv_line(+atom,-compound,+options)
parse_csv_file/2 # parse_csv_file(+filename,+options)Where options can be:
trim(Boolean) # default false, trims leading and trailing whitespace
numbers(Boolean) # default false, converts integers and floats
header(Boolean) # default false, skip first (header) line in file
comments(Boolean) # default false, skip lines beginning with comment character in file
comment(Char) # default '#', set the comment character
strings(Boolean) # default depends on type of input (atom or string)
arity(Integer) # default to not checking arity, otherwise throw domain_error
assert(Boolean) # default false, assertz to database instead (assumed for files, needs a functor)
functor(Atom) # default output is a list, create a structure (mandatory for files and with assert)
quote(Char) # default to double-quote
sep(Char) # default to comma for .csv or unknown files & TAB for .tsv filesExamples...
```console
?- parse_csv_line('123,2.345,3456789',T).
T = ['123','2.345','3456789'].
?- parse_csv_line("123,2.345,3456789",T).
T = ["123","2.345","3456789"].
?- parse_csv_line('123,2.345,3456789',T,[functor(f)]).
T = f('123','2.345','3456789').
?- parse_csv_line('123,2.345,3456789',T,[functor(f),numbers(true)]).
T = f(123,2.345,3456789).
?- parse_csv_line('abc, abc, a b c ',T).
T = [abc,' abc',' a b c '].
?- parse_csv_line('abc, abc, a b c ',T,[trim(true)]).
T = [abc,abc,'a b c'].
?- parse_csv_line('123,2.345,3456789',T,[functor(f),numbers(true),assert(true)]).
true.
?- f(A,B,C).
A = 123, B = 2.345, C = 3456789.
?- time(parse_csv_file('../logtalk3/library/csv/test_files/tickers.csv',[functor(f),quote('\'')])).
% Parsed 35193 lines
% Time elapsed 0.096s, 3 Inferences, 0.000 MLips)
true.
?- f(A,B,C,D,E,F).
A = '1125:HK', B = 'OTCGREY', C = 'Stock', D = 'USD', E = '1999-06-22', F = '2019-10-22'
; A = '6317:TK', B = 'PINK', C = 'Stock', D = 'USD', E = '2018-06-27', F = '2020-03-02'
; A = 'A', B = 'NYSE', C = 'Stock', D = 'USD', E = '1999-11-18', F = '2021-06-25'
; A = 'AA', B = 'NYSE', C = 'Stock', D = 'USD', E = '2016-11-01', F = '2021-06-25'
; A = 'AA-W', B = 'NYSE', C = 'Stock', D = 'USD', E = '2016-10-18', F = '2016-11-08'
; A = 'AAA', B = 'NYSEARCA', C = 'ETF', D = 'USD', E = '2020-09-09', F = '2021-06-25'
;
```Application maps (dictionaries)
===============================Maps use atomic key/value pairs only and are represented as
pseudo-streams:map_create/2 # map_create(-skiplist,+opts)
map_create/1 # map_create(-skiplist)
map_set/3 # map_set(+skiplist,+key,+value)
map_get/3 # map_get(+skiplist,+key,?value)
map_del/2 # map_del(+skiplist,+key)
map_count/2 # map_count(+skiplist,-count)
map_list/2 # map_list(+skiplist,?list)
map_close/1 # map_close(+skiplist)```console
$ tpl
?- map_create(S,[alias(foo)]).
S = <$stream>(4).
?- map_set(foo,1,111), map_set(foo,two,222), map_set(foo,3,333).
true.
?- map_get(foo,3,V).
V = 333.
?- map_del(foo,3).
true.
?- map_list(foo,L).
L = [1=111,two=222].
?- map_close(foo).
true.
```Maps can store virtually unlimited amounts of volatile data in
an efficient indexed manner.Maps don't require syntactic extensions to Prolog as found in
other non-standard systems.A possible future extension would be to load a CSV file directly
in a very efficient manner.HTTP 1.1
========:- use_module(library(http)).
http_get/3 # http_get(Url, Data, Opts)
http_post/4 # http_post(Url, Data, Opts)
http_patch/4 # http_patch(Url, Data, Opts)
http_put/4 # http_put(Url, Data, Opts)
http_delete/3 # http_delete(Url, Data, Opts)
http_server/2 # http_server(Goal,Opts),
http_request/5 # http_request(S, Method, Path, Ver, Hdrs)```console
?- http_get("https://github.com/trealla-prolog/trealla", Data, [status_code(Code)]).
Data = "\n\n\n\n\n\n\n 0 meaning return that
many bytes, = 0 meaning return whatever is there (if non-blocking) or
a var meaning return all bytes until end end of file,Simple regular expressions
==========================This is meant as a place-holder until a proper regex package is included.
sre_compile/2 # sre_compile(+pattern,-reg)
sre_matchp/4 # sre_matchp(+reg,+text,-match,-rest)
sre_substp/4 # sre_substp(+reg,+text,-prefix,-rest)sre_match/4 # sre_match(+pattern,+text,-match,-rest)
sre_match_all/3 # sre_matchall(+pattern,+text,-list)
sre_match_all_pos/3 # sre_matchall_pos(+pattern,+text,-pairs)sre_match_all_in_file/3 # sre_matchall_in_file(+pattern,+filename,-list)
sre_match_all_pos_in_file/3 # sre_matchall_pos_in_file(+pattern,+filename,-pairs)sre_subst/4 # sre_subst(+pattern,+text,-prefix,-rest)
sre_subst_all/4 # sre_subst(+pattern,+text,+subst,-text)sre_subst_all_in_file/4 # sre_subst_in_file(+pattern,+filename,+subst,-text)
```
* Supports:
* ---------
* '.' Dot, matches any character
* '^' Start anchor, matches beginning of string
* '$' End anchor, matches end of string
* '*' Asterisk, match zero or more (greedy)
* '+' Plus, match one or more (greedy)
* '?' Question, match zero or one (non-greedy)
* '[abc]' Character class, match if one of {'a', 'b', 'c'}
* '[^abc]' Inverted class, match if NOT one of {'a', 'b', 'c'}
* '[a-zA-Z]' Character ranges, the character set of the ranges { a-z | A-Z }
* '\s' Whitespace, \t \f \r \n \v and spaces
* '\S' Non-whitespace
* '\w' Alphanumeric, [a-zA-Z0-9_]
* '\W' Non-alphanumeric
* '\d' Digits, [0-9]
* '\D' Non-digits
```For example...
```console
?- sre_compile("d.f", Reg), sre_matchp(Reg, "abcdefghi", M, Rest).
Reg = <$blob>(0x6AC5AAF0), M = "def", Rest = "ghi".?- sre_match("d.f", "abcdefghi", M, Rest).
M = "def", Rest = "ghi".?- sre_match_all("d.f", "xdafydbfzdcf-", L).
L = ["daf","dbf","dcf"].?- sre_match_all_pos("d.f", "xdafydbfzdcf-", L).
L = [1-3,2-3,3-3].?- sre_match_all("d[^c]f", "xdafydbfzdcfxddf-", L).
L = ["daf","dbf","ddf"].?- sre_subst("d.f", "xdafydbfzdcf-", P, L).
P = "x", L = "ydbfzdcf-".?- sre_subst_all("d.f", "xdafydbfzdcf-", "$", L).
L = "x$y$z$-".?- sre_match_all("\\S", "Needle In A Haystack", L).
L = ["N","e","e","d","l","e","I","n","A",...].?- sre_match_all_pos("\\s", "Needle In A Haystack", L).
L = [6-1,9-1,11-1].?- time(sre_match_all_in_file("t\\We",'thesaurus.txt',L)),
length(L,Len),
format("Occurrs: ~w times~n",[Len]),
halt.
Time elapsed 0.0463s
Occurrs: 749 times
```Note: if no match is found the returned *match*, *text* (and *list*) is *[]*
indicating an empty string.Note: if the input *text* arg is a string then the output *text* arg
is a no-copy slice of the string. So if the input is a memory-mapped
file then regex searches can be performed quickly and efficiently over
huge files.Foreign Function Interface (libffi)
===================================Allows the loading of dynamic libraries and calling of foreign functions
written in C from within Prolog...'$dlopen'/3 # '$dlopen(+name, +flag, -handle)
These predicates register a foreign function as a builtin and use a
wrapper to validate arg types at call/runtime...'$register_function'/4 # '$ffi_reg'(+handle,+symbol,+types,+ret_type)
'$register_predicate'/4 # '$ffi_reg'(+handle,+symbol,+types,+ret_type)The allowed types are
*sint8*, *sint16*, *sint32*, *sint64*, *sint* (native *signed int*),
*uint8*, *uint16*, *uint32*, *uint64*, *uint* (native *unsigned int*),
*ushort*, *sshort*, *float*, *double*,
*bool*, (use integer 0/1 to align with C *bool* pseudo-type)
*void* (a return type only),
*cstr* (a char pointer),
and *ptr* (for arbitrary pointers/handles).Assuming the following C-code in *samples/foo.c*:
```c
double foo(double x, int64_t y)
{
return pow(x, (double)y);
}int bar(double x, int64_t y, double *result)
{
*result = pow(x, (double)y);
return 0;
}char *baz(const char *x, const char *y)
{
char *s = malloc(strlen(x) + strlen(y) + 1);
strcpy(s, x);
strcat(s, y);
return s;
}
``````console
$ gcc -fPIC -c foo.c
$ gcc -shared -o libfoo.so foo.o
```Register a builtin function...
```console
?- '$dlopen'('samples/libfoo.so', 0, H),
'$register_function'(H, foo, [double, sint64], double).
H = 94051868794416.
?- R is foo(2.0, 3).
R = 8.0.
?- R is foo(abc,3).
error(type_error(float,abc),foo/2).
```Register a builtin predicate...
```console
?- '$dlopen'('samples/libfoo.so', 0, H),
'$register_predicate'(H, bar, [double, sint64, -double], sint64),
'$register_predicate'(H, baz, [cstr, cstr], cstr),
H = 94051868794416.
?- bar(2.0, 3, X, Return).
X = 8.0, Return = 0.
?- baz('abc', '123', Return).
Return = abc123.
```Note: the foreign function return value is passed as an extra argument
to the predicate call, unless it was specified to be of type *void*.Foreign Module Interface (libffi)
=================================This is a simplified interface to FFIs inspired by Adrián Arroyo Calle
and largely supercedes the implementation given above.foreign_struct(+atom, +list)
use_foreign_module(+atom, +list)For example...
```prolog
:- use_foreign_module('samples/libfoo.so', [
bar([double, sint64, -double], sint64),
baz([cstr, cstr], cstr)
]).
```See the *library/raylib.pl* and *samples/test_raylib.pl* for an example
usage including passing and returning structs by value.See the *library/curl.pl* and *samples/test_curl.pl* for an example
usage downloading a file.This is an example using SQLITE. Given the code in *samples/sqlite3.pl*...
```prolog
:- use_module(library(sqlite3)).run :-
test('samples/sqlite3.db', 'SELECT * FROM company').test(Database, Query) :-
sqlite_flag('SQLITE_OK', SQLITE_OK),
sqlite3_open(Database, Connection, Ret), Ret =:= SQLITE_OK,
bagof(Row, sqlite3_query(Connection, Query, Row, _), Results),
writeq(Results), nl.
```Run...
```console
$ tpl -g run,halt samples/sqlite3.pl
[[1,'Paul',32,'California',20000.0],[2,'Allen',25,'Texas',15000.0],[3,'Teddy',23,'Norway',20000.0],[4,'Mark',25,'Rich-Mond ',65000.0],[5,'David',27,'Texas',85000.0],[6,'Kim',22,'South-Hall',45000.0]]
```Concurrent Tasks ##EXPERIMENTAL##
================Co-operative multi-tasking is available in the form of light-weight
coroutines that run until they yield either explicitly or implicitly
(when waiting on an event of some kind). They are called a `task` here.call_task/[1-n] # concurrent form of call/1-n
tasklist/[2-8] # concurrent form of maplist/1-nAn example:
```prolog
:-use_module(library(http)).geturl(Url) :-
http_get(Url,_Data,[status_code(Code),final_url(Location)]),
format("Job [~w] ~w ==> ~w done~n",[Url,Code,Location]).% Fetch each URL in list sequentially...
test54 :-
L = ['www.google.com','www.bing.com','www.duckduckgo.com'],
maplist(geturl,L),
writeln('Finished').$ tpl samples/test -g "time(test54),halt"
Job [www.google.com] 200 ==> www.google.com done
Job [www.bing.com] 200 ==> www.bing.com done
Job [www.duckduckgo.com] 200 ==> https://duckduckgo.com done
Finished
Time elapsed 0.663 secs% Fetch each URL in list concurrently...
test56 :-
L = ['www.google.com','www.bing.com','www.duckduckgo.com'],
tasklist(geturl,L),
writeln('Finished').$ tpl samples/test -g "time(test56),halt"
Job [www.duckduckgo.com] 200 ==> https://duckduckgo.com done
Job [www.bing.com] 200 ==> www.bing.com done
Job [www.google.com] 200 ==> www.google.com done
Finished
Time elapsed 0.33 secs
```Linda Co-ordination Language ##EXPERIMENTAL##
============================Implements a toy (local-only) version of Linda using tasks. See:
[swi-prolog](https://www.swi-prolog.org/pldoc/man?section=tipc-linda-clients).linda_eval/1 # linda_eval(:goal)
out/1 # out(+tuple)
in/1 # in(?tuple)
rd/1 # rd(?tuple)
in_noblock/1 # in_noblock(?tuple)
rd_noblock/1 # rd_noblock(?tuple)
bagof_in_noblock/3 # bagof_in_noblock(+term,+tuple,?list)
bagof_rd_noblock/3 # bagof_rd_noblock(+term,+tuple,?list)
wait/0
end_wait/0For example:
```prolog
:- use_module(library(linda)).
:- initialization(main).main :-
linda_eval(consumer('A')),
linda_eval(consumer('B')),
linda_eval(producer),
wait,
in(producer), % verify it finished normally
writeq(done), nl,
halt.producer :-
between(1, 10, I),
out({msg:I}),
sleep(0.25),
fail.
producer :-
forall(rd_noblock({msg:_}), sleep(0.001)),
end_wait.consumer(N) :-
in({msg:I}),
write(['consumer',N,'got=',I]), nl,
random(R),
sleep(R),
fail.
``````console
$ tpl samples/test_linda.pl
[consumer,B,got=,1]
[consumer,B,got=,2]
[consumer,B,got=,3]
[consumer,A,got=,4]
[consumer,B,got=,5]
[consumer,A,got=,6]
[consumer,B,got=,7]
[consumer,A,got=,8]
[consumer,A,got=,9]
[consumer,B,got=,10]
done
```Concurrent Futures ##EXPERIMENTAL##
==================Inspired by [Tau-Prolog](http://tau-prolog.org/documentation#concurrent)
concurrent futures. Uses co-operative tasks.future/3 – Make a Future from a Prolog goal.
future_all/2 – Make a Future that resolves to a list of the results of an input list of futures.
future_any/2 – Make a Future that resolves as soon as any of the futures in a list succeeds.
future_cancel/1 – Cancel unfinished future.
future_done/1 – Check if a future finished.
await/2 – Wait for a Future.For example:
```prolog
:- use_module(library(concurrent)).
:- use_module(library(http)).test :-
future(Status1, geturl("www.google.com", Status1), F1),
future(Status2, geturl("www.bing.com", Status2), F2),
future(Status3, geturl("www.duckduckgo.com", Status3), F3),
future_all([F1,F2,F3], F),
await(F, StatusCodes),
C = StatusCodes.
```See `samples/test_concurrent.pl`.
Engines ##EXPERIMENTAL##
=======Inspired by [SWI-Prolog](https://www.swi-prolog.org/pldoc/man?section=engine-predicates)
engines. Uses co-operative tasks.engine_create/[3,4]
engine_next/2
engine_yield/1
engine_post/[2,3]
engine_fetch/1
engine_self/1
is_engine/1
current_engine/1
engine_destroy/1Pre-emptive Multi-threading
===========================Start independent (shared state) Prolog queries as dedicated POSIX
threads and communicate via message queues. Note: the database *is*
shared. These predicates conform to the *ISO Prolog multi-threading
support* standards proposal (ISO/IEC DTR 13211–5:2007), now lapsed.thread_create/3 # thread_create(:callable,-thread,+options)
thread_create/2 # thread_create(:callable,-thread)
thread_signal/2 # thread_signal(+thread,:callable)
thread_join/2 # thread_join(+thread,-term)
thread_cancel/1 # thread_cancel(+thread)
thread_detach/1 # thread_detach(+thread)
thread_self/1 # thread_self(-thread)
thread_exit/1 # thread_exit(+term)
thread_sleep/1 # thread_sleep(+integer)
thread_yield/0 # thread_yield
thread_property/2 # thread_property(+thread,+term)
thread_property/1 # thread_property(+term)Where 'options' can be *alias(+atom)*, *at_exit(:term)* and/or *detached(+boolean)*
(the default is *NOT* detached, ie. joinable).Create a stand-alone message queue...
message_queue_create/2 # message_queue_create(-queue,+options)
message_queue_create/1 # message_queue_create(-queue)
message_queue_destroy/1 # message_queue_destroy(+queue)
message_queue_property/2 # message_queue_property(+queue,+term)
thread_send_message/2 # thread_send_message(+queue,+term)
thread_send_message/1 # thread_send_message(+term)
thread_get_message/2 # thread_get_message(+queue,?term)
thread_get_message/1 # thread_get_message(?term)
thread_peek_message/2 # thread_peek_message(+queue,?term)
thread_peek_message/1 # thread_peek_message(?term)Where 'options' can be *alias(+atom)*.
Create a stand-alone mutex...
mutex_create/2 # mutex_create(-mutex,+options)
mutex_create/1 # mutex_create(-mutex)
mutex_destroy/1 # mutex_destroy(+mutex)
mutex_property/2 # mutex_property(+mutex,+term)
with_mutex/2 # with_mutex(+mutex,:callable)mutex_trylock/1 # mutex_trylock(+mutex)
mutex_lock/1 # mutex_lock(+mutex)
mutex_unlock/1 # mutex_unlock(+mutex)
mutex_unlock_all/0 # mutex_unlock_allWhere 'options' can be *alias(+atom)*. Use of mutexes other than
*with_mutex/2* should generally be avoided.For example...
```console
?- thread_create((format("thread_hello~n",[]),sleep(1),format("thread_done~n",[]),thread_exit(99)), Tid, []), format("joining~n",[]), thread_join(Tid,Status), format("join_done~n",[]).
joining
thread_hello
thread_done
join_done
Tid = 1, Status = exited(99).
?-
```Prolog instances ##EXPERIMENTAL##
================Start independent (no shared state) Prolog instances as dedicated
pre-emptive threads and communicate via message queues. Each thread
has it's own message queue associated with it. Note: the database
is *not* shared. For shared state consider using the blackboard.pl_thread/3 # pl_thread(-thread,+filename,+options)
pl_thread/2 # pl_thread(-thread,+filename)Where 'options' can be (currently just) *alias(+atom)*.
pl_msg_send/2 # pl_msg_send(+thread,+term)
pl_msg_recv/2 # pl_msg_recv(-thread,-term)For example...
```console
$ cat samples/thread_calc.pl
:- initialization(main).% At the moment we only do sqrt here...
main :-
write('Calculator running...'), nl,
repeat,
pl_msg_recv(Tid, Term),
Term = sqrt(X, Y),
Y is sqrt(X),
pl_msg_send(Tid, Term),
fail.$ tpl
?- pl_thread(_, 'samples/thread_calc.pl', [alias(calc)]).
Calculator running...
?- Term = sqrt(2, V),
pl_msg_send(calc, Term),
pl_msg_recv(_, Term).
Term = sqrt(2,1.4142135623731), V = 1.4142135623731.
?-
```Profile
=======Why did I put this here?
```console
$ time tpl -q -g 'main,statistics(profile,_),halt' -f ~/trealla/samples/out.pl 2>out.csv
$ head -1 out.csv >out_sorted.csv && tail -n+2 out.csv | sort -k 3 -t ',' -n -r >> out_sorted.csv
$ cat out_sorted.csv
#functor/arity,match_attempts,matched,tcos
'member_/3',20505037,20036023,19362515
'can_step/5',1149136,288705,189915
'can_move/5',164848,98905,32873
'strength/4',1074794,63382,31691
'minus_one/2',1621942,1621942,0
'make_move/6',1086892,1086892,0
'member_/3',20709531,730369,0
'member/2',673508,673508,0
'occupied_by/4',673316,673316,0
...
```