1
0
mirror of https://github.com/janet-lang/janet synced 2025-10-28 06:07:43 +00:00

Compare commits

...

161 Commits

Author SHA1 Message Date
Calvin Rose
894cd0e022 Prepare for 1.25.1 release. 2022-10-29 11:58:29 -05:00
Calvin Rose
db2c63fffc Update CHANGELOG.md 2022-10-24 20:32:02 -05:00
Calvin Rose
60e0f32f1a Fix os/open with :rw permissions on posix. 2022-10-24 19:39:58 -05:00
Calvin Rose
e731996a68 Allow overriding JANETCONF_HEADER in Makefile.
This allows a configuration workflow that is a bit simpler than before
and doesn't requiring applying patches. Instead, add a config.mk to
source dir with JANETCONF_HEADER=myconfig.h and compile as usual.

The patching workflow will of course still work exactly as before.
2022-10-24 09:49:51 -05:00
Calvin Rose
2f69cd4209 Add easier option for adding config.mk in root directory. 2022-10-23 13:11:07 -05:00
Calvin Rose
fd59de25c5 Add memcmp to the core. Useful in binary protocol implementations. 2022-10-18 11:54:07 -05:00
Calvin Rose
af12c3d41a Typo fixes. 2022-10-10 18:38:24 -05:00
Calvin Rose
54b52bbeb5 Prepare for 1.25.0 release. 2022-10-10 18:24:48 -05:00
Calvin Rose
1174c68d9a Update CHANGELOG.md 2022-10-10 18:23:15 -05:00
Calvin Rose
448ea7167f Add CLOEXEC when calling accept on Linux.
Prevents leakage of file descriptors to subprocesses.
The symptom of the above issue is sockets that don't seem to close
until a subprocess completes.
2022-10-10 18:06:31 -05:00
Calvin Rose
6b27008c99 Fix os/date with nil argument. 2022-10-10 15:24:28 -05:00
Calvin Rose
725c785882 Formatting. 2022-10-10 14:24:03 -05:00
Calvin Rose
ab068cff67 Remove WNOWAIT code on linux.
Would cause os/proc-wait to block in some circumstances.
2022-10-10 14:23:17 -05:00
bakpakin
9dc03adfda Fix pass by reference in windows FFI to accomodate stack shift. 2022-09-22 10:58:16 -05:00
bakpakin
49f9e4eddf Fix ifdef in capi.c for janet_getuinteger64 and janet_getinteger64 2022-09-20 15:42:20 -05:00
bakpakin
43c47ac44c Address #1037 - move stack hack after arg writing logic to avoid
clobber.
2022-09-20 15:37:20 -05:00
Calvin Rose
1cebe64664 Add some soft test cases for #1037. 2022-09-20 10:01:12 -05:00
Calvin Rose
f33c381043 Improve sysv64 classify algorithm. 2022-09-20 09:45:17 -05:00
Calvin Rose
3479841c77 Address #1034 - add handling for 8-16 byte structs in FFI. 2022-09-20 09:28:46 -05:00
Calvin Rose
6a899968a9 Allow passing user signals to (signal) as keywords. 2022-09-17 21:18:07 -05:00
Calvin Rose
bb8405a36e Merge pull request #1029 from locriacyber/patch-0
Fix documentation for ev/go, ev/spawn
2022-09-16 07:32:25 -05:00
bakpakin
c7bc711f63 Add windows FFI example test case for void functions with double
argument.
2022-09-15 13:58:54 -05:00
bakpakin
e326071c35 Fix void returns in windows FFI - address #1025 2022-09-15 13:51:11 -05:00
Locria Cyber
ad6a669381 Add doc for ev/go
Document that you can pass a function instead of a fiber to ev/go
2022-09-14 00:17:53 +00:00
Locria Cyber
e4c9dafc9a Fix typo in ev/spawn doc 2022-09-13 23:49:42 +00:00
Calvin Rose
dfc0aefd87 Merge pull request #1028 from autumnull/master
Made peg 'not' and 'if-not' drop their captures on success
2022-09-13 15:20:10 -05:00
Calvin Rose
356b39c6f5 Add test case for #1027 2022-09-12 19:00:59 -05:00
Calvin Rose
8da7bb6b68 Fix peg/replace-all and family - Fix #1027 2022-09-12 18:58:48 -05:00
Autumn!
9341081a4d Made peg 'not' and 'if-not' drop their captures on success 2022-09-12 23:07:56 +01:00
Calvin Rose
324a086eb4 Merge pull request #1023 from ScriptDevil/set-manpath
Set JANET_MANPATH environment variable while installing.
2022-09-10 09:55:01 -05:00
Ashok Gautham
ed595f52c2 Set JANET_MANPATH environment variable while installing.
JPM on windows currently installs its manpage to C:\ directly because this isn't set when installing Janet through the MSI installer
2022-09-09 13:24:36 +05:30
Calvin Rose
64ad0023bb Merge pull request #1022 from autumnull/master
Removed unnecessary backslashes from documentation
2022-09-08 08:52:55 -05:00
Autumn!
fe5f661d15 Removed unnecessary backslashes from documentation 2022-09-08 13:21:17 +01:00
Calvin Rose
ff26e3a8ba Remove end of string check that is now redudant.
The addition of some code to avoid valgrind warnings made this code
redundant.
2022-09-05 20:13:15 -05:00
Calvin Rose
14657a762c Fix peg RULE_SET op code when at tail of string in some cases. 2022-09-05 14:11:03 -05:00
Calvin Rose
4754fa3902 Fix issue #1021 - bad format specifiers in run.c 2022-09-03 14:03:51 -05:00
Calvin Rose
f302f87337 Merge pull request #1019 from Techcable/fix/inttypes-overflow
Signed integer overflow is undefined behavior in C, avoid it in inttypes.c
2022-08-30 23:23:11 -05:00
Calvin Rose
94dbcde292 Merge pull request #1020 from pepe/comment-typo
Fix typo in define comment
2022-08-30 22:57:52 -05:00
Josef Pospíšil
4336a174b1 Fix typo in define comment 2022-08-30 09:21:20 +02:00
Techcable
0adb13ed71 inttypes.c: Avoid signed integer overflow (U.B.)
In C, signed arithmetic overflow is undefined behvior
but unsigned arithmetic overflow is twos complement

Unconditionally switch to unsigned arithmetic internally for +, -, *
This will not affect the result thanks to twos complement awesomeness.

I don't think this will be an issue in these functions,
but it has a history of causing bugs.....
2022-08-29 18:38:51 -07:00
Calvin Rose
03ba1f7021 Update CHANGELOG and version numbers. 2022-08-26 13:15:30 -05:00
Calvin Rose
1f7f20788c Add line loop example for awk or sed like processing. 2022-08-26 12:29:23 -05:00
Calvin Rose
c59dd29190 Add stress test for marshalling to examples. 2022-08-26 12:27:53 -05:00
Calvin Rose
99f63a41a3 Improve pointer hashing to avoid hash collisions. 2022-08-26 12:18:10 -05:00
Calvin Rose
a575f5df36 Add option to marshal values without cycle detection. 2022-08-26 11:20:02 -05:00
Calvin Rose
0817e627ee Prepare for 1.24.1 release. 2022-08-24 13:23:53 -05:00
Calvin Rose
14d90239a7 Merge branch 'master' of github.com:janet-lang/janet 2022-08-24 11:35:37 -05:00
Calvin Rose
f5d11dc656 Address #1014 improve parse errors when bad delimiters are found.
Reuse some existing logic for eof errors.
2022-08-24 11:34:59 -05:00
Calvin Rose
6dcf5bf077 Merge pull request #1012 from Techcable/doc/clarify-flag-E
Clarify the documentation of janet -E flag
2022-08-21 13:45:17 -05:00
Calvin Rose
ac2082e9b3 Allow adding name to short-fns.
When short-fn is used in a macro, it can be useful to
give the function a nicer name then a raw pointer.
2022-08-18 14:33:59 -05:00
Techcable
dbac495bee Clarify the documentation of janet -E flag
This confused me, despite having a fair deal of janet experience.
2022-08-18 12:16:14 -07:00
Calvin Rose
fe5ccb163e Merge branch 'master' of github.com:janet-lang/janet 2022-08-16 12:38:59 -05:00
Calvin Rose
1aea5ee007 Remove stack inversion code for sysv64 FFI. 2022-08-16 12:38:44 -05:00
Calvin Rose
13cd9f8067 Remove stack inversion code for sysv64 FFI. 2022-08-16 12:20:38 -05:00
bakpakin
34496ecaf0 Prepare for 1.24.0 release. 2022-08-14 20:20:09 -05:00
bakpakin
c043b1d949 Add win32 ffi example. 2022-08-14 15:40:09 -05:00
bakpakin
9a6d2a7b32 Fix FFI for reference return values and stack parameter passing. 2022-08-14 15:20:30 -05:00
bakpakin
f8a9efa8e4 Allow binding pre-loaded symbols in windows FFI.
Mimic the posix RTLD_NOW setting for dlopen by iterating
opened DLLs to look for symbols.
2022-08-14 13:26:13 -05:00
Calvin Rose
5b2169e0d1 Fix docstring. 2022-08-02 14:58:32 -05:00
Calvin Rose
2c927ea768 Add testcase for issue #1005 2022-08-02 12:34:24 -05:00
Calvin Rose
f4bbcdcbc8 Get rid of disabled tracing. #1005 2022-08-02 12:19:22 -05:00
Calvin Rose
79c375b1af Address #1005 - Fix janet_call stack clobbering on dirty stack. 2022-08-02 12:13:56 -05:00
Calvin Rose
f443a3b3a1 Remove type_array option to meson_min build 2022-07-26 14:27:22 -05:00
Calvin Rose
684d2d63f4 Emphasize the ldconfig error is expected. 2022-07-20 11:19:09 -05:00
Calvin Rose
1900d8f843 Fix build warnings on Linux GCC version 12.1.0 x64 2022-07-20 08:04:03 -05:00
Calvin Rose
3c2af95d21 Update CHANGELOG.md 2022-07-19 20:05:21 -05:00
Calvin Rose
b35414ea0f Merge branch 'master' of github.com:janet-lang/janet 2022-07-19 20:04:44 -05:00
Calvin Rose
fb5b056f7b Address #1001 - don't process names passed to dlopen. 2022-07-19 20:04:17 -05:00
bakpakin
7248c1dfdb Give up if ln fail. 2022-07-09 19:10:25 -05:00
bakpakin
4c7ea9e893 Merge branch 'master' of github.com:janet-lang/janet 2022-07-09 11:44:20 -05:00
bakpakin
c7801ce277 Address #997 - clang undefined behavior warning. 2022-07-09 11:43:51 -05:00
Calvin Rose
f741a8e3ff Merge pull request #998 from autumnull/master
stop doc-format detecting other modes within code blocks
2022-07-09 11:40:51 -05:00
bakpakin
6a92e8b609 Update CHANGELOG and make tweaks to win32 shell 2022-07-09 11:39:06 -05:00
bakpakin
9da91a8217 Update shell.c to have smart behavior on windows. 2022-07-09 11:23:02 -05:00
bakpakin
69853c8e5c Merge branch 'master' of github.com:janet-lang/janet 2022-07-08 09:49:56 -05:00
Autumn!
1f41b6c138 doc-format no longer detects other modes within code blocks 2022-07-07 14:41:46 +01:00
Calvin Rose
e001efa9fd Fix #996 - linking command works on busybox. 2022-07-04 16:48:07 -05:00
Calvin Rose
435e64d4cf Allow shorthand for setting task-id on new threads with flag.
Avoids the need to wrap function bodies in closures in many cases.
2022-07-03 12:08:21 -05:00
Calvin Rose
f296c8f5fb Merge branch 'master' of github.com:janet-lang/janet 2022-07-02 21:11:55 -05:00
Calvin Rose
8d0e6ed32f Fix function handlers for :out and :err.
They were not properly handled for formatting functions.
2022-07-02 21:11:05 -05:00
Calvin Rose
b6a36afffe Merge pull request #994 from shassard/master
Use relative path for include/janet.h symlink
2022-07-02 12:30:39 -05:00
Stephen Hassard
e422abc269 Use relative path for include/janet.h symlink
When using make to build an rpm, the current symlink is
created with an absolute path to the buildroot as causes
the make install target to fail with:

error: Symlink points to BuildRoot: /usr/include/janet.h -> /home/stephen/rpmbuild/BUILDROOT/janet-1.23.0-3.x86_64/usr/include/janet/janet.h

We can create the link relatively which makes this more
portable, where:

ln -sf -t '/home/stephen/rpmbuild/BUILDROOT/janet-1.23.0-3.x86_64/usr/include' janet.h janet/janet.h

Resulting in the following symlink:

ls -la BUILDROOT/usr/include/janet.h
lrwxrwxrwx. 1 stephen stephen 13 Jul  2 08:17 BUILDROOT/usr/include/janet.h -> janet/janet.h

This symlink can then be properly packaged without path
issues.

Signed-off-by: Stephen Hassard <steve@hassard.net>
2022-07-02 08:52:17 -07:00
Calvin Rose
221d71d07b Merge pull request #993 from pepe/test-tabseq
Add basic test for tabseq
2022-07-02 09:30:59 -05:00
Calvin Rose
9f35f0837e Merge pull request #991 from pepe/master
Trace function to the stderr
2022-07-02 09:29:18 -05:00
Josef Pospíšil
515891b035 Add basic test for tabseq 2022-07-02 07:43:28 +02:00
Josef Pospíšil
94a506876f Trace function to the stderr 2022-07-01 12:23:25 +02:00
Calvin Rose
9bde57854a Add tabseq macro. 2022-06-28 22:51:41 -05:00
Calvin Rose
f456369941 Add support for a dyn :task-id
Adds extra information to default information from supervisor
channels. For threaded channels as supervisors, we don't get
the source fiber so identifying the source of messages was not
possible. This change allows better multithreading with  supervisors.
2022-06-25 18:51:21 -05:00
Calvin Rose
8f0a1ffe5d Github showing old git attributes. 2022-06-20 11:23:21 -05:00
Calvin Rose
e4bafc621a Remove ssize_t usage. 2022-06-20 11:09:41 -05:00
Calvin Rose
cfa39ab3b0 Prepare for 1.23.0 release. 2022-06-20 10:49:25 -05:00
Calvin Rose
47e91bfd89 Fix docstring. 2022-06-19 18:52:37 -05:00
Calvin Rose
eecc388ebd Add support for 0-element arrays in FFI.
Allows for flexible array member construct mapping.
2022-06-19 16:29:55 -05:00
Calvin Rose
0a15a5ee56 Prepare for 1.23.0 release. 2022-06-19 15:07:35 -05:00
Calvin Rose
cfaae47cea Fix trailing :pack-all or :pack in struct. 2022-06-19 13:06:19 -05:00
Calvin Rose
c1a0352592 Fix unset field in JanetFFIType. 2022-06-19 12:58:45 -05:00
Calvin Rose
965f45aa3f Update changelog to say FFI initially only available on non-windows
platforms.
2022-06-19 12:42:44 -05:00
Calvin Rose
6ea27fe836 Error message sounded a bit unsure. 2022-06-19 10:29:42 -05:00
Calvin Rose
0dccc22b38 Improve error messages when using bad metadata
Print metadata value as well as binding name.
2022-06-19 10:28:18 -05:00
Calvin Rose
cbe833962b Remove bad suite0009 test. Close #871
The issue is that there was no synchronization on writes.
The stability of the test relied on the fact that the server
would read in an entire message in one call to ev/read, which
would _almost_ always happen since the messages are so small.
2022-06-19 10:01:10 -05:00
Calvin Rose
b5720f6f10 On suite0009 errors for localname/peername, add info
Tag when the issue in the server or in the client. On windows, sometimes
these seemed to get swapped for strange reason.
2022-06-19 09:31:43 -05:00
Calvin Rose
56b4e0b0ec Update CONTRIBUTING.md 2022-06-19 09:18:59 -05:00
Calvin Rose
e316ccb1e0 Use _tzset() on windows before localtime_s 2022-06-19 08:49:54 -05:00
Calvin Rose
a6f93efd39 Support for array types in ffi. 2022-06-19 08:03:32 -05:00
Calvin Rose
20511cf608 Cast NULL pointer to nil in return in ffi. 2022-06-18 16:53:01 -05:00
Calvin Rose
1a1dd39367 Use __builtin_alloca if no other option. 2022-06-18 13:54:47 -05:00
Calvin Rose
589981bdcb BSD systems put alloca in the stdlib 2022-06-18 12:18:06 -05:00
Calvin Rose
89546776b2 alloca 2022-06-18 12:15:16 -05:00
Calvin Rose
f0d7b3cd12 No alloca.h? 2022-06-18 11:19:14 -05:00
Calvin Rose
e37be627e0 Allow loading current process on windows as well. 2022-06-18 10:31:00 -05:00
Calvin Rose
d803561582 Fix ffi/defbind for non-lazy bindings.
Add testing to bind to symbols in current binary.
2022-06-18 10:14:42 -05:00
Calvin Rose
a1aab4008f Update FFI example. 2022-06-18 10:06:39 -05:00
Calvin Rose
a1172529bf Fix named arguments with optional args. 2022-06-18 09:46:28 -05:00
Calvin Rose
1d905bf07f SRWLock is the size of a void pointer. 2022-06-17 17:49:02 -05:00
Calvin Rose
eed678a14b Include windows.h for windows builds 2022-06-17 17:41:50 -05:00
Calvin Rose
b1bdffbc34 Don't inlcude pthread on windows. 2022-06-17 17:35:58 -05:00
Calvin Rose
cff718f37d Add suite0012 stub with delay test. 2022-06-17 17:27:42 -05:00
Calvin Rose
40e9430278 Move examples to example directory. 2022-06-17 17:24:52 -05:00
Calvin Rose
62fc55fc74 Remove pthread.h from janet.h
Should make janet a bit easier to use. Also changes the
header to not expose the size of native mutexes and rwlocks, except
with janet_os_mutex_size and janet_os_rwlock_size.
2022-06-17 17:13:58 -05:00
Calvin Rose
80729353c8 Add :lazy option for ffi/context for jpm quickbin usage. 2022-06-13 21:26:03 -05:00
Calvin Rose
105ba5e124 Add ffi/context and ffi/defbind helpers.
Wrap the very bare-bones FFI library to be a bit more
useful out of the box.
2022-06-12 18:48:47 -05:00
Calvin Rose
ad1b50d1f5 Update dofile function signature. 2022-06-12 18:03:23 -05:00
Calvin Rose
1905437abe Merge branch 'master' into ffi 2022-06-12 13:59:37 -05:00
Calvin Rose
87fc339c45 Add named arguments with the &named symbol.
Similar to &keys, but more ergonomic.
2022-06-12 13:53:18 -05:00
Calvin Rose
3af7d61d3e Update gtk example. 2022-06-12 12:51:06 -05:00
Calvin Rose
a45ef7a856 Update CHANGELOG to reflect new function renames. 2022-06-12 10:17:25 -05:00
Calvin Rose
299998055d Update meson min build to turn off ffi. 2022-06-12 10:15:36 -05:00
Calvin Rose
c9586d39ed Add a :none calling convention.
The ffi module is useful even when true ffi calls
are not yet implemented. This lets the ffi be enabled
on any architecture, albeit with a degraded feature set
where calling conventions are not implemented.
2022-06-12 10:12:45 -05:00
Calvin Rose
2e9f67f4e4 Change all "native-*" to ffi/. Move new dll loading funcs.
native-close, raw-native and native-lookup have become
ffi/close, ffi/native, and ffi/lookup instead.

The new ffi module will be useful for any architecture even if we don't
support making calls to certain functions. We can simple add a
do-nothing calling convetion that panics on call. ffi/read and ffi/write
are useful in their own right.
2022-06-12 10:02:02 -05:00
Calvin Rose
e318170fea Begin working on windows calling convetion.
Also remove inline assembly for making sysv64 calls.
Instead, use crafted function signatures to set all needed registers.
2022-06-12 09:16:10 -05:00
Calvin Rose
73c4289792 Fix define check. 2022-06-11 21:50:34 -05:00
Calvin Rose
ea45d7ee47 Convert to one big blob of assembly for sysv cc.
Also begin working on win64 calling convention.
2022-06-11 21:43:35 -05:00
Calvin Rose
6d970725e7 Update boot.janet for typos. 2022-06-11 21:19:42 -05:00
Calvin Rose
458c2c6d88 Make calling convention optional for trampoline 2022-06-11 15:47:51 -05:00
Calvin Rose
0cc53a8964 Get a GTK example working. Good proof of concept. 2022-06-11 14:47:35 -05:00
Calvin Rose
0bc96304a9 Add r32 and r64 aliases for real numbers in ffi types. 2022-06-11 09:40:37 -05:00
Calvin Rose
c75b088ff8 Format boot.janet with janet-format. 2022-06-10 19:13:23 -05:00
Calvin Rose
181f0341f5 Add :pack and :pack-all keywords to allow for struct packing.
Syntax may need some work but covers both fully packed structs
as well as packing of individual members.
2022-06-10 18:53:22 -05:00
Calvin Rose
33bb08d53b Merge branch 'master' into ffi 2022-06-10 16:24:55 -05:00
Calvin Rose
6d188f6e44 Improve .ppasm function. 2022-06-10 16:24:40 -05:00
Calvin Rose
c3648331f1 Expose an easy to use debugger function. 2022-06-10 15:39:29 -05:00
Calvin Rose
a5b66029d3 Expose the built-in debugger in more places. 2022-06-10 15:23:15 -05:00
Calvin Rose
49bfe80191 Make sure void return types work as expected. 2022-06-10 12:33:01 -05:00
Calvin Rose
a5def77bfe Add support for struct return values. 2022-06-10 12:25:08 -05:00
Calvin Rose
9ecb5b4791 Add native-read function as inverse to native-write. 2022-06-10 09:38:52 -05:00
Calvin Rose
1cc48a370a Add native-write, which will write structs to buffers.
Useful for testing as well as useful in its own right. Begs
for an inverse, native-read which would convert byte data
to native structs.
2022-06-10 08:46:20 -05:00
Calvin Rose
f1ec8d1e11 Beginning of struct support.
TODO:
- struct return values
- support for unions in signatures
- more testing
- complex types
- packed structs
- writing structs to buffers (useful and we have most of the machinery).
2022-06-09 20:27:56 -05:00
Calvin Rose
55c34cd84f Merge pull request #985 from masukomi/readme_update
added make install & install-jpm-git to Readme
2022-06-09 10:02:58 -05:00
masukomi
aca52d1e36 added make install & install-jpm-git to readme 2022-06-08 21:58:55 -04:00
Calvin Rose
6f90df26a5 Alloca included by default on some OS, but not all.
Do explcitly include alloca on non-msvc compilers.
2022-06-08 10:01:19 -05:00
Calvin Rose
9d9cb378ff Don't include alloca.h, not in MSVC. 2022-06-08 09:59:13 -05:00
Calvin Rose
f92aac14aa Only enable FFI on x86-64, non-windows OSes. 2022-06-08 09:50:31 -05:00
Calvin Rose
3f27d78ab5 Add some FFI testing and more improvements to sysv abi.
Add support for integer return and floating point return variants, as
well as arguments on the stack. Start flushing out struct arguments.

Still needed:
- structs (packed and unpacked)
- complex numbers
- long doubles
- MASM alternative for windows (you can technically use sysv abi on
  windows)
- more calling conventions.
2022-06-08 09:44:49 -05:00
Calvin Rose
282d1ba22f Implement sys v abi on x64 partially. 2022-06-06 18:54:17 -05:00
Calvin Rose
94c19575b1 Fix when clib is not pointer type. 2022-06-06 13:37:07 -05:00
Calvin Rose
e3e485285b Prevent double usage of native objects after closing. 2022-06-06 13:36:03 -05:00
Calvin Rose
986e36720e Update windows builds for raw-natives. 2022-06-06 13:08:12 -05:00
Calvin Rose
74348ab6c2 Fix symbol lookup when symbol isn't found. 2022-06-06 10:53:00 -05:00
Calvin Rose
8d1ad99f42 Add stubs that are precursor to FFI.
FFI may be best implemented as an external library
(libffi has incompatible license to Janet) or as code
that takes void * and wraps then into Janet C functions
given a function signature. Either way, we need to some way
to load symbols from arbitrary dynamic libraries.
2022-06-06 10:49:30 -05:00
Calvin Rose
e69bbff195 Update CHANGELOG.md 2022-06-05 17:40:50 -05:00
bakpakin
e0b7533c39 Add toggle macro. 2022-05-12 15:36:29 -05:00
49 changed files with 3077 additions and 561 deletions

View File

@@ -13,7 +13,7 @@ tasks:
gmake test-install
- meson_min: |
cd janet
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dtyped_array=false -Dreduced_os=true
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
cd build_meson_min
ninja
- meson_prf: |

1
.gitattributes vendored
View File

@@ -1,3 +1,4 @@
*.janet linguist-language=Janet
*.janet text eol=lf
*.c text eol=lf
*.h text eol=lf

5
.gitignore vendored
View File

@@ -68,10 +68,13 @@ tags
vgcore.*
*.out.*
# Wix artifacts
# WiX artifacts
*.msi
*.wixpdb
# Makefile config
/config.mk
# Created by https://www.gitignore.io/api/c
### C ###

View File

@@ -1,7 +1,48 @@
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased - ???
## 1.25.1 - 2022-10-29
- Add `memcmp` function to core library.
- Fix bug in `os/open` with `:rw` permissions not correct on Linux.
- Support config.mk for more easily configuring the Makefile.
## 1.25.0 - 2022-10-10
- Windows FFI fixes.
- Fix PEG `if-not` combinator with captures in the condition
- Fix bug with `os/date` with nil first argument
- Fix bug with `net/accept` on Linux that could leak file descriptors to subprocesses
- Reduce number of hash collisions from pointer hashing
- Add optional parameter to `marshal` to skip cycle checking code
## 1.24.1 - 2022-08-24
- Fix FFI bug on Linux/Posix
- Improve parse error messages for bad delimiters.
- Add optional `name` parameter to the `short-fn` macro.
## 1.24.0 - 2022-08-14
- Add FFI support to 64-bit windows compiled with MSVC
- Don't process shared object names passed to dlopen.
- Add better support for windows console in the default shell.c for auto-completion and
other shell-like input features.
- Improve default error message from `assert`.
- Add the `tabseq` macro for simpler table comprehensions.
- Allow setting `(dyn :task-id)` in fibers to improve context in supervisor messages. Prior to
this change, supervisor messages over threaded channels would be from ambiguous threads/fibers.
## 1.23.0 - 2022-06-20
- Add experimental `ffi/` module for interfacing with dynamic libraries and raw function pointers. Only available
on 64 bit linux, mac, and bsd systems.
- Allow using `&named` in function prototypes for named arguments. This is a more ergonomic
variant of `&keys` that isn't as redundant, more self documenting, and allows extension to
things like default arguments.
- Add `delay` macro for lazy evaluate-and-save thunks.
- Remove pthread.h from janet.h for easier includes.
- Add `debugger` - an easy to use debugger function that just takes a fiber.
- `dofile` will now start a debugger on errors if the environment it is passed has `:debug` set.
- Add `debugger-on-status` function, which can be passed to `run-context` to start a debugger on
abnormal fiber signals.
- Allow running scripts with the `-d` flag to use the built-in debugger on errors and breakpoints.
- Add mutexes (locks) and reader-writer locks to ev module for thread coordination.
- Add `parse-all` as a generalization of the `parse` function.
- Add `os/cpu-count` to get the number of available processors on a machine

View File

@@ -43,7 +43,7 @@ For changes to the VM and Core code, you will probably need to know C. Janet is
a subset of C99 that works with Microsoft Visual C++. This means most of C99 but with the following
omissions.
* No `restrict`
* No `restrict`
* Certain functions in the standard library are not always available
In practice, this means programming for both MSVC on one hand and everything else on the other.
@@ -64,6 +64,23 @@ ensure a consistent code style for C.
All janet code in the project should be formatted similar to the code in core.janet.
The auto formatting from janet.vim will work well.
## Typo Fixing and One-Line changes
Typo fixes are welcome, as are simple one line fixes. Do not open many separate pull requests for each
individual typo fix. This is incredibly annoying to deal with as someone needs to review each PR, run
CI, and merge. Instead, accumulate batches of typo fixes into a single PR. If there are objections to
specific changes, these can be addressed in the review process before the final merge, if the changes
are accepted.
Similarly, low effort and bad faith changes are annoying to developers and such issues may be closed
immediately without response.
## Contributions from Automated Tools
People making changes found or generated by automated tools MUST note this when opening an issue
or creating a pull request. This can help give context to developers if the change/issue is
confusing or nonsensical.
## Suggesting Changes
To suggest changes, open an issue on GitHub. Check GitHub for other issues

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2021 Calvin Rose
# Copyright (c) 2022 Calvin Rose
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
@@ -21,9 +21,10 @@
################################
##### Set global variables #####
################################
sinclude config.mk
PREFIX?=/usr/local
JANETCONF_HEADER?=src/conf/janetconf.h
INCLUDEDIR?=$(PREFIX)/include
BINDIR?=$(PREFIX)/bin
LIBDIR?=$(PREFIX)/lib
@@ -83,7 +84,7 @@ all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h
##### Name Files #####
######################
JANET_HEADERS=src/include/janet.h src/conf/janetconf.h
JANET_HEADERS=src/include/janet.h $(JANETCONF_HEADER)
JANET_LOCAL_HEADERS=src/core/features.h \
src/core/util.h \
@@ -108,6 +109,7 @@ JANET_CORE_SOURCES=src/core/abstract.c \
src/core/debug.c \
src/core/emit.c \
src/core/ev.c \
src/core/ffi.c \
src/core/fiber.c \
src/core/gc.c \
src/core/inttypes.c \
@@ -167,24 +169,24 @@ build/c/janet.c: build/janet_boot src/boot/boot.janet
########################
ifeq ($(UNAME), Darwin)
SONAME=libjanet.1.22.dylib
SONAME=libjanet.1.25.dylib
else
SONAME=libjanet.so.1.22
SONAME=libjanet.so.1.25
endif
build/c/shell.c: src/mainclient/shell.c
cp $< $@
build/janet.h: $(JANET_TARGET) src/include/janet.h src/conf/janetconf.h
./$(JANET_TARGET) tools/patch-header.janet src/include/janet.h src/conf/janetconf.h $@
build/janet.h: $(JANET_TARGET) src/include/janet.h $(JANETCONF_HEADER)
./$(JANET_TARGET) tools/patch-header.janet src/include/janet.h $(JANETCONF_HEADER) $@
build/janetconf.h: src/conf/janetconf.h
build/janetconf.h: $(JANETCONF_HEADER)
cp $< $@
build/janet.o: build/c/janet.c src/conf/janetconf.h src/include/janet.h
build/janet.o: build/c/janet.c $(JANETCONF_HEADER) src/include/janet.h
$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
build/shell.o: build/c/shell.c src/conf/janetconf.h src/include/janet.h
build/shell.o: build/c/shell.c $(JANETCONF_HEADER) src/include/janet.h
$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
$(JANET_TARGET): build/janet.o build/shell.o
@@ -282,7 +284,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
ln -sf '$(DESTDIR)$(INCLUDEDIR)/janet/janet.h' '$(DESTDIR)$(INCLUDEDIR)/janet.h'
ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' || true #fixme bsd
mkdir -p '$(DESTDIR)$(JANET_PATH)'
mkdir -p '$(DESTDIR)$(LIBDIR)'
if test $(UNAME) = Darwin ; then \
@@ -299,7 +301,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
cp janet.1 '$(DESTDIR)$(JANET_MANPATH)'
mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
[ -z '$(DESTDIR)' ] && $(LDCONFIG) || true
[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs"
install-jpm-git: $(JANET_TARGET)
mkdir -p build

View File

@@ -89,6 +89,8 @@ cd somewhere/my/projects/janet
make
make test
make repl
make install
make install-jpm-git
```
Find out more about the available make targets by running `make help`.
@@ -103,6 +105,8 @@ cd somewhere/my/projects/janet
make CC=gcc-x86
make test
make repl
make install
make install-jpm-git
```
### FreeBSD
@@ -116,6 +120,8 @@ cd somewhere/my/projects/janet
gmake
gmake test
gmake repl
gmake install
gmake install-jpm-git
```
### NetBSD

71
examples/ffi/gtk.janet Normal file
View File

@@ -0,0 +1,71 @@
# :lazy true needed for jpm quickbin
# lazily loads library on first function use
# so the `main` function
# can be marshalled.
(ffi/context "/usr/lib/libgtk-3.so" :lazy true)
(ffi/defbind
gtk-application-new :ptr
"Add docstrings as needed."
[title :string flags :uint])
(ffi/defbind
g-signal-connect-data :ulong
[a :ptr b :ptr c :ptr d :ptr e :ptr f :int])
(ffi/defbind
g-application-run :int
[app :ptr argc :int argv :ptr])
(ffi/defbind
gtk-application-window-new :ptr
[a :ptr])
(ffi/defbind
gtk-button-new-with-label :ptr
[a :ptr])
(ffi/defbind
gtk-container-add :void
[a :ptr b :ptr])
(ffi/defbind
gtk-widget-show-all :void
[a :ptr])
(ffi/defbind
gtk-button-set-label :void
[a :ptr b :ptr])
(def cb (delay (ffi/trampoline :default)))
(defn ffi/array
``Convert a janet array to a buffer that can be passed to FFI functions.
For example, to create an array of type `char *` (array of c strings), one
could use `(ffi/array ["hello" "world"] :ptr)`. One needs to be careful that
array elements are not garbage collected though - the GC can't follow references
inside an arbitrary byte buffer.``
[arr ctype &opt buf]
(default buf @"")
(each el arr
(ffi/write ctype el buf))
buf)
(defn on-active
[app]
(def window (gtk-application-window-new app))
(def btn (gtk-button-new-with-label "Click Me!"))
(g-signal-connect-data btn "clicked" (cb)
(fn [btn] (gtk-button-set-label btn "Hello World"))
nil 1)
(gtk-container-add window btn)
(gtk-widget-show-all window))
(defn main
[&]
(def app (gtk-application-new "org.janet-lang.example.HelloApp" 0))
(g-signal-connect-data app "activate" (cb) on-active nil 1)
# manually build an array with ffi/write
# - we are responsible for preventing gc when the arg array is used
(def argv (ffi/array (dyn *args*) :string))
(g-application-run app (length (dyn *args*)) argv))

208
examples/ffi/so.c Normal file
View File

@@ -0,0 +1,208 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#ifdef _WIN32
#define EXPORTER __declspec(dllexport)
#else
#define EXPORTER
#endif
/* Structs */
typedef struct {
int a, b;
float c, d;
} Split;
typedef struct {
float c, d;
int a, b;
} SplitFlip;
typedef struct {
int u, v, w, x, y, z;
} SixInts;
typedef struct {
int a;
int b;
} intint;
typedef struct {
int a;
int b;
int c;
} intintint;
typedef struct {
int64_t a;
int64_t b;
int64_t c;
} big;
/* Functions */
EXPORTER
int int_fn(int a, int b) {
return (a << 2) + b;
}
EXPORTER
double my_fn(int64_t a, int64_t b, const char *x) {
return (double)(a + b) + 0.5 + strlen(x);
}
EXPORTER
double double_fn(double x, double y, double z) {
return (x + y) * z * 3;
}
EXPORTER
double double_many(double x, double y, double z, double w, double a, double b) {
return x + y + z + w + a + b;
}
EXPORTER
double double_lots(
double a,
double b,
double c,
double d,
double e,
double f,
double g,
double h,
double i,
double j) {
return i + j;
}
EXPORTER
double double_lots_2(
double a,
double b,
double c,
double d,
double e,
double f,
double g,
double h,
double i,
double j) {
return a +
10.0 * b +
100.0 * c +
1000.0 * d +
10000.0 * e +
100000.0 * f +
1000000.0 * g +
10000000.0 * h +
100000000.0 * i +
1000000000.0 * j;
}
EXPORTER
double float_fn(float x, float y, float z) {
return (x + y) * z;
}
EXPORTER
int intint_fn(double x, intint ii) {
printf("double: %g\n", x);
return ii.a + ii.b;
}
EXPORTER
int intintint_fn(double x, intintint iii) {
printf("double: %g\n", x);
return iii.a + iii.b + iii.c;
}
EXPORTER
intint return_struct(int i) {
intint ret;
ret.a = i;
ret.b = i * i;
return ret;
}
EXPORTER
big struct_big(int i, double d) {
big ret;
ret.a = i;
ret.b = (int64_t) d;
ret.c = ret.a + ret.b + 1000;
return ret;
}
EXPORTER
void void_fn(void) {
printf("void fn ran\n");
}
EXPORTER
void void_fn_2(double y) {
printf("y = %f\n", y);
}
EXPORTER
void void_ret_fn(int x) {
printf("void fn ran: %d\n", x);
}
EXPORTER
int intintint_fn_2(intintint iii, int i) {
fprintf(stderr, "iii.a = %d, iii.b = %d, iii.c = %d, i = %d\n", iii.a, iii.b, iii.c, i);
return i * (iii.a + iii.b + iii.c);
}
EXPORTER
float split_fn(Split s) {
return s.a * s.c + s.b * s.d;
}
EXPORTER
float split_flip_fn(SplitFlip s) {
return s.a * s.c + s.b * s.d;
}
EXPORTER
Split split_ret_fn(int x, float y) {
Split ret;
ret.a = x;
ret.b = x;
ret.c = y;
ret.d = y;
return ret;
}
EXPORTER
SplitFlip split_flip_ret_fn(int x, float y) {
SplitFlip ret;
ret.a = x;
ret.b = x;
ret.c = y;
ret.d = y;
return ret;
}
EXPORTER
SixInts sixints_fn(void) {
return (SixInts) {
6666, 1111, 2222, 3333, 4444, 5555
};
}
EXPORTER
int sixints_fn_2(int x, SixInts s) {
return x + s.u + s.v + s.w + s.x + s.y + s.z;
}
EXPORTER
int sixints_fn_3(SixInts s, int x) {
return x + s.u + s.v + s.w + s.x + s.y + s.z;
}

134
examples/ffi/test.janet Normal file
View File

@@ -0,0 +1,134 @@
#
# Simple FFI test script that tests against a simple shared object
#
(def is-windows (= :windows (os/which)))
(def ffi/loc (string "examples/ffi/so." (if is-windows "dll" "so")))
(def ffi/source-loc "examples/ffi/so.c")
(if is-windows
(os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px)
(os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px))
(ffi/context ffi/loc)
(def intintint (ffi/struct :int :int :int))
(def big (ffi/struct :s64 :s64 :s64))
(def split (ffi/struct :int :int :float :float))
(def split-flip (ffi/struct :float :float :int :int))
(def six-ints (ffi/struct :int :int :int :int :int :int))
(ffi/defbind int-fn :int [a :int b :int])
(ffi/defbind double-fn :double [a :double b :double c :double])
(ffi/defbind double-many :double
[x :double y :double z :double w :double a :double b :double])
(ffi/defbind double-lots :double
[a :double b :double c :double d :double e :double f :double g :double h :double i :double j :double])
(ffi/defbind float-fn :double
[x :float y :float z :float])
(ffi/defbind intint-fn :int
[x :double ii [:int :int]])
(ffi/defbind return-struct [:int :int]
[i :int])
(ffi/defbind intintint-fn :int
[x :double iii intintint])
(ffi/defbind struct-big big
[i :int d :double])
(ffi/defbind void-fn :void [])
(ffi/defbind double-lots-2 :double
[a :double
b :double
c :double
d :double
e :double
f :double
g :double
h :double
i :double
j :double])
(ffi/defbind void-fn-2 :void [y :double])
(ffi/defbind intintint-fn-2 :int [iii intintint i :int])
(ffi/defbind split-fn :float [s split])
(ffi/defbind split-flip-fn :float [s split-flip])
(ffi/defbind split-ret-fn split [x :int y :float])
(ffi/defbind split-flip-ret-fn split-flip [x :int y :float])
(ffi/defbind sixints-fn six-ints [])
(ffi/defbind sixints-fn-2 :int [x :int s six-ints])
(ffi/defbind sixints-fn-3 :int [s six-ints x :int])
#
# Struct reading and writing
#
(defn check-round-trip
[t value]
(def buf (ffi/write t value))
(def same-value (ffi/read t buf))
(assert (deep= value same-value)
(string/format "round trip %j (got %j)" value same-value)))
(check-round-trip :bool true)
(check-round-trip :bool false)
(check-round-trip :void nil)
(check-round-trip :void nil)
(check-round-trip :s8 10)
(check-round-trip :s8 0)
(check-round-trip :s8 -10)
(check-round-trip :u8 10)
(check-round-trip :u8 0)
(check-round-trip :s16 10)
(check-round-trip :s16 0)
(check-round-trip :s16 -12312)
(check-round-trip :u16 10)
(check-round-trip :u16 0)
(check-round-trip :u32 0)
(check-round-trip :u32 10)
(check-round-trip :u32 0xFFFF7777)
(check-round-trip :s32 0x7FFF7777)
(check-round-trip :s32 0)
(check-round-trip :s32 -1234567)
(def s (ffi/struct :s8 :s8 :s8 :float))
(check-round-trip s [1 3 5 123.5])
(check-round-trip s [-1 -3 -5 -123.5])
#
# Call functions
#
(tracev (sixints-fn))
(tracev (sixints-fn-2 100 [1 2 3 4 5 6]))
(tracev (sixints-fn-3 [1 2 3 4 5 6] 200))
(tracev (split-ret-fn 10 12))
(tracev (split-flip-ret-fn 10 12))
(tracev (split-flip-ret-fn 12 10))
(tracev (intintint-fn-2 [10 20 30] 3))
(tracev (split-fn [5 6 1.2 3.4]))
(tracev (void-fn-2 10.3))
(tracev (double-many 1 2 3 4 5 6))
(tracev (string/format "%.17g" (double-many 1 2 3 4 5 6)))
(tracev (type (double-many 1 2 3 4 5 6)))
(tracev (double-lots-2 0 1 2 3 4 5 6 7 8 9))
(tracev (void-fn))
(tracev (int-fn 10 20))
(tracev (double-fn 1.5 2.5 3.5))
(tracev (double-lots 1 2 3 4 5 6 7 8 9 10))
(tracev (float-fn 8 4 17))
(tracev (intint-fn 123.456 [10 20]))
(tracev (intintint-fn 123.456 [10 20 30]))
(tracev (return-struct 42))
(tracev (double-lots 1 2 3 4 5 6 700 800 9 10))
(tracev (struct-big 11 99.5))
(assert (= [10 10 12 12] (split-ret-fn 10 12)))
(assert (= [12 12 10 10] (split-flip-ret-fn 10 12)))
(assert (= 183 (intintint-fn-2 [10 20 31] 3)))
(assert (= 264 (math/round (* 10 (split-fn [5 6 1.2 3.4])))))
(assert (= 9876543210 (double-lots-2 0 1 2 3 4 5 6 7 8 9)))
(assert (= 60 (int-fn 10 20)))
(assert (= 42 (double-fn 1.5 2.5 3.5)))
(assert (= 21 (math/round (double-many 1 2 3 4 5 6.01))))
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
(assert (= 204 (float-fn 8 4 17)))
(print "Done.")

7
examples/ffi/win32.janet Normal file
View File

@@ -0,0 +1,7 @@
(ffi/context "user32.dll")
(ffi/defbind MessageBoxA :int
[w :ptr text :string cap :string typ :int])
(MessageBoxA nil "Hello, World!" "Test" 0)

2
examples/lineloop.janet Normal file
View File

@@ -0,0 +1,2 @@
(while (not (empty? (def line (getline))))
(prin "line: " line))

View File

@@ -0,0 +1,30 @@
(defn init-db [c]
(def res @{:clients @{}})
(var i 0)
(repeat c
(def n (string "client" i))
(put-in res [:clients n] @{:name n :projects @{}})
(++ i)
(repeat c
(def pn (string "project" i))
(put-in res [:clients n :projects pn] @{:name pn})
(++ i)
(repeat c
(def tn (string "task" i))
(put-in res [:clients n :projects pn :tasks tn] @{:name pn})
(++ i))))
res)
(loop [c :range [30 80 1]]
(var s (os/clock))
(print "Marshal DB with " c " clients, "
(* c c) " projects and "
(* c c c) " tasks. "
"Total " (+ (* c c c) (* c c) c) " tables")
(def buf (marshal (init-db c) @{} @""))
(print "Buffer is " (length buf) " bytes")
(print "Duration " (- (os/clock) s))
(set s (os/clock))
(gccollect)
(print "Collected garbage in " (- (os/clock) s)))

View File

@@ -164,10 +164,15 @@ Execute a string of Janet source. Source code is executed in the order it is enc
arguments are executed before later ones.
.TP
.BR \-E\ code arguments
.BR \-E\ code\ arguments...
Execute a single Janet expression as a Janet short-fn, passing the remaining command line arguments to the expression. This allows
more concise one-liners with command line arguments.
Example: janet -E '(print $0)' 12 is equivalent to '((short-fn (print $0)) 12)', which is in turn equivalent to
`((fn [k] (print k)) 12)`
See docs for the `short-fn` function for more details.
.TP
.BR \-d
Enable debug mode. On all terminating signals as well the debug signal, this will

View File

@@ -20,7 +20,7 @@
project('janet', 'c',
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.22.1')
version : '1.25.1')
# Global settings
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
@@ -76,6 +76,7 @@ conf.set('JANET_SIMPLE_GETLINE', get_option('simple_getline'))
conf.set('JANET_EV_NO_EPOLL', not get_option('epoll'))
conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue'))
conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt'))
conf.set('JANET_NO_FFI', not get_option('ffi'))
if get_option('os_name') != ''
conf.set('JANET_OS_NAME', get_option('os_name'))
endif
@@ -116,6 +117,7 @@ core_src = [
'src/core/debug.c',
'src/core/emit.c',
'src/core/ev.c',
'src/core/ffi.c',
'src/core/fiber.c',
'src/core/gc.c',
'src/core/inttypes.c',

View File

@@ -19,6 +19,7 @@ option('simple_getline', type : 'boolean', value : false)
option('epoll', type : 'boolean', value : false)
option('kqueue', type : 'boolean', value : false)
option('interpreter_interrupt', type : 'boolean', value : false)
option('ffi', type : 'boolean', value : true)
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200)

View File

@@ -1,5 +1,5 @@
# The core janet library
# Copyright 2021 © Calvin Rose
# Copyright 2022 © Calvin Rose
###
###
@@ -45,6 +45,7 @@
(defn defmacro :macro
"Define a macro."
[name & more]
(setdyn name @{}) # override old macro definitions in the case of a recursive macro
(apply defn name :macro more))
(defmacro as-macro
@@ -75,6 +76,11 @@
[name & more]
~(var ,name :private ,;more))
(defmacro toggle
"Set a value to its boolean inverse. Same as `(set value (not value))`."
[value]
~(set ,value (,not ,value)))
(defn defglobal
"Dynamically create a global def."
[name value]
@@ -157,7 +163,7 @@
(def ,v ,x)
(if ,v
,v
(,error ,(if err err "assert failure")))))
(,error ,(if err err (string/format "assert failure in %j" x))))))
(defn errorf
"A combination of `error` and `string/format`. Equivalent to `(error (string/format fmt ;args))`."
@@ -605,13 +611,20 @@
See `loop` for details.``
[head & body]
(def $accum (gensym))
~(do (def ,$accum @[]) (loop ,head (array/push ,$accum (do ,;body))) ,$accum))
~(do (def ,$accum @[]) (loop ,head (,array/push ,$accum (do ,;body))) ,$accum))
(defmacro tabseq
``Similar to `loop`, but accumulates key value pairs into a table.
See `loop` for details.``
[head key-body & value-body]
(def $accum (gensym))
~(do (def ,$accum @{}) (loop ,head (,put ,$accum ,key-body (do ,;value-body))) ,$accum))
(defmacro generate
``Create a generator expression using the `loop` syntax. Returns a fiber
that yields all values inside the loop in order. See `loop` for details.``
[head & body]
~(fiber/new (fn [] (loop ,head (yield (do ,;body)))) :yi))
~(,fiber/new (fn [] (loop ,head (yield (do ,;body)))) :yi))
(defmacro coro
"A wrapper for making fibers that may yield multiple values (coroutine). Same as `(fiber/new (fn [] ;body) :yi)`."
@@ -948,12 +961,12 @@
(def call-buffer @[])
(while true
(forv i 0 ninds
(let [old-key (in iterkeys i)
ii (in inds i)
new-key (next ii old-key)]
(if (= nil new-key)
(do (set done true) (break))
(do (set (iterkeys i) new-key) (array/push call-buffer (in ii new-key))))))
(let [old-key (in iterkeys i)
ii (in inds i)
new-key (next ii old-key)]
(if (= nil new-key)
(do (set done true) (break))
(do (set (iterkeys i) new-key) (array/push call-buffer (in ii new-key))))))
(if done (break))
(array/push res (f ;call-buffer))
(array/clear call-buffer))))
@@ -1591,8 +1604,8 @@
(each x ind
(def y (f x))
(cond
is-new (do (set is-new false) (set category y) (set span @[x]) (array/push ret span))
(= y category) (array/push span x)
is-new (do (set is-new false) (set category y) (set span @[x]) (array/push ret span))
(= y category) (array/push span x)
(do (set category y) (set span @[x]) (array/push ret span))))
ret)
@@ -1736,7 +1749,7 @@
* tuple -- a tuple pattern will match if its first element matches, and the
following elements are treated as predicates and are true.
* `\_` symbol -- the last special case is the `\_` symbol, which is a wildcard
* `_` symbol -- the last special case is the `_` symbol, which is a wildcard
that will match any value without creating a binding.
While a symbol pattern will ordinarily match any value, the pattern `(@ <sym>)`,
@@ -1842,7 +1855,7 @@
(when isarr
(array/push anda (get-length-sym s))
(def pattern-len
(if-let [ rest-idx (find-index (fn [x] (= x '&)) pattern) ]
(if-let [rest-idx (find-index (fn [x] (= x '&)) pattern)]
rest-idx
(length pattern)))
(array/push anda [<= pattern-len (get-length-sym s)]))
@@ -2164,7 +2177,7 @@
|(+ $ $) # use pipe reader macro for terse function literals.
|(+ $&) # variadic functions
```
[arg]
[arg &opt name]
(var max-param-seen -1)
(var vararg false)
(defn saw-special-arg
@@ -2190,8 +2203,9 @@
x))
x))
(def expanded (macex arg on-binding))
(def name-splice (if name [name] []))
(def fn-args (seq [i :range [0 (+ 1 max-param-seen)]] (symbol '$ i)))
~(fn [,;fn-args ,;(if vararg ['& '$&] [])] ,expanded))
~(fn ,;name-splice [,;fn-args ,;(if vararg ['& '$&] [])] ,expanded))
###
###
@@ -2282,9 +2296,9 @@
(def source-code (file/read f :all))
(var index 0)
(repeat (dec line)
(if-not index (break))
(set index (string/find "\n" source-code index))
(if index (++ index)))
(if-not index (break))
(set index (string/find "\n" source-code index))
(if index (++ index)))
(when index
(def line-end (string/find "\n" source-code index))
(eprint " " (string/slice source-code index line-end))
@@ -2586,8 +2600,8 @@
(while (parser/has-more p)
(array/push ret (parser/produce p)))
(if (= :error (parser/status p))
(error (parser/error p))
ret)))
(error (parser/error p))
ret)))
(def load-image-dict
``A table used in combination with `unmarshal` to unmarshal byte sequences created
@@ -2746,19 +2760,64 @@
(get r 0)
v))))
(def debugger-env
"An environment that contains dot prefixed functions for debugging."
@{})
(var- debugger-on-status-var nil)
(defn debugger
"Run a repl-based debugger on a fiber. Optionally pass in a level
to differentiate nested debuggers."
[fiber &opt level]
(default level 1)
(def nextenv (make-env (fiber/getenv fiber)))
(put nextenv :fiber fiber)
(put nextenv :debug-level level)
(put nextenv :signal (fiber/last-value fiber))
(merge-into nextenv debugger-env)
(defn debugger-chunks [buf p]
(def status (:state p :delimiters))
(def c ((:where p) 0))
(def prpt (string "debug[" level "]:" c ":" status "> "))
(getline prpt buf nextenv))
(eprint "entering debug[" level "] - (quit) to exit")
(flush)
(run-context
{:chunks debugger-chunks
:on-status (debugger-on-status-var nextenv (+ 1 level) true)
:env nextenv})
(eprint "exiting debug[" level "]")
(flush)
(nextenv :resume-value))
(defn debugger-on-status
"Create a function that can be passed to `run-context`'s `:on-status`
argument that will drop into a debugger on errors. The debugger will
only start on abnormal signals if the env table has the `:debug` dyn
set to a truthy value."
[env &opt level is-repl]
(default level 1)
(fn [f x]
(def fs (fiber/status f))
(if (= :dead fs)
(when is-repl
(put env '_ @{:value x})
(printf (get env :pretty-format "%q") x)
(flush))
(do
(debug/stacktrace f x "")
(eflush)
(if (get env :debug) (debugger f level))))))
(set debugger-on-status-var debugger-on-status)
(defn dofile
``Evaluate a file, file path, or stream and return the resulting environment. :env, :expander,
:source, :evaluator, :read, and :parser are passed through to the underlying
`run-context` call. If `exit` is true, any top level errors will trigger a
call to `(os/exit 1)` after printing the error.``
[path &keys
{:exit exit
:env env
:source src
:expander expander
:evaluator evaluator
:read read
:parser parser}]
[path &named exit env source expander evaluator read parser]
(def f (case (type path)
:core/file path
:core/stream path
@@ -2766,7 +2825,7 @@
(def path-is-file (= f path))
(default env (make-env))
(def spath (string path))
(put env :source (or src (if-not path-is-file spath path)))
(put env :source (or source (if-not path-is-file spath path)))
(var exit-error nil)
(var exit-fiber nil)
(defn chunks [buf _] (:read f 4096 buf))
@@ -2802,14 +2861,17 @@
(debug/stacktrace f x "")
(eflush)
(os/exit 1))
(put env :exit true)
(set exit-error x)
(set exit-fiber f)))
(if (get env :debug)
((debugger-on-status env) f x)
(do
(put env :exit true)
(set exit-error x)
(set exit-fiber f)))))
:evaluator evaluator
:expander expander
:read read
:parser parser
:source (or src (if path-is-file :<anonymous> spath))}))
:source (or source (if path-is-file :<anonymous> spath))}))
(if-not path-is-file (:close f))
(when exit-error
(if exit-fiber
@@ -2964,7 +3026,7 @@
:italics ["*" "*"]
:bold ["**" "**"]}))
(def modes @{})
(defn toggle [mode]
(defn toggle-mode [mode]
(def active (get modes mode))
(def delims (get delimiters mode))
(put modes mode (not active))
@@ -2972,7 +3034,7 @@
# Parse state
(var cursor 0) # indexes into string for parsing
(var stack @[]) # return value for this block.
(var stack @[]) # return value for this block.
# Traversal helpers
(defn c [] (get str cursor))
@@ -3074,7 +3136,7 @@
(def token @"")
(var token-length 0)
(defn delim [mode]
(def d (toggle mode))
(def d (toggle-mode mode))
(if-not has-color (+= token-length (length d)))
(buffer/push token d))
(defn endtoken []
@@ -3085,44 +3147,48 @@
(def b (get line i))
(cond
(or (= b (chr "\n")) (= b (chr " "))) (endtoken)
(= b (chr `\`)) (do
(++ token-length)
(buffer/push token (get line (++ i))))
(= b (chr "_")) (delim :underline)
(= b (chr "`")) (delim :code)
(= b (chr "*"))
(if (= (chr "*") (get line (+ i 1)))
(do (++ i)
(delim :bold))
(delim :italics))
(not (modes :code)) (cond
(= b (chr `\`)) (do
(++ token-length)
(buffer/push token (get line (++ i))))
(= b (chr "_")) (delim :underline)
(= b (chr "*"))
(if (= (chr "*") (get line (+ i 1)))
(do (++ i)
(delim :bold))
(delim :italics))
(do (++ token-length) (buffer/push token b)))
(do (++ token-length) (buffer/push token b))))
(endtoken)
(tuple/slice tokens))
(set parse-blocks (fn parse-blocks [indent]
(var new-indent indent)
(var p-start nil)
(var p-end nil)
(defn p-line []
(unless p-start
(set p-start cursor))
(skipline)
(set p-end cursor)
(set new-indent (skipwhite)))
(defn finish-p []
(when (and p-start (> p-end p-start))
(push (tokenize-line (getslice p-start p-end)))
(set p-start nil)))
(while (and (c) (>= new-indent indent))
(cond
(nl?) (do (finish-p) (c++) (set new-indent (skipwhite)))
(ul?) (do (finish-p) (set new-indent (parse-list ul? :ul new-indent)))
(ol?) (do (finish-p) (set new-indent (parse-list ol? :ol new-indent)))
(fcb?) (do (finish-p) (set new-indent (parse-fcb new-indent)))
(>= new-indent (+ 4 indent)) (do (finish-p) (set new-indent (parse-icb new-indent)))
(p-line)))
(finish-p)
new-indent))
(set
parse-blocks
(fn parse-blocks [indent]
(var new-indent indent)
(var p-start nil)
(var p-end nil)
(defn p-line []
(unless p-start
(set p-start cursor))
(skipline)
(set p-end cursor)
(set new-indent (skipwhite)))
(defn finish-p []
(when (and p-start (> p-end p-start))
(push (tokenize-line (getslice p-start p-end)))
(set p-start nil)))
(while (and (c) (>= new-indent indent))
(cond
(nl?) (do (finish-p) (c++) (set new-indent (skipwhite)))
(ul?) (do (finish-p) (set new-indent (parse-list ul? :ul new-indent)))
(ol?) (do (finish-p) (set new-indent (parse-list ol? :ol new-indent)))
(fcb?) (do (finish-p) (set new-indent (parse-fcb new-indent)))
(>= new-indent (+ 4 indent)) (do (finish-p) (set new-indent (parse-icb new-indent)))
(p-line)))
(finish-p)
new-indent))
# Handle first line specially for defn, defmacro, etc.
(when (= (chr "(") (in str 0))
@@ -3259,10 +3325,10 @@
(do
(def [fullpath mod-kind] (module/find (string sym)))
(if-let [mod-env (in module/cache fullpath)]
(print-module-entry {:module true
:kind mod-kind
(print-module-entry {:module true
:kind mod-kind
:source-map [fullpath nil nil]
:doc (in mod-env :doc)})
:doc (in mod-env :doc)})
(print "symbol " sym " not found."))))
(print-module-entry x)))
@@ -3362,25 +3428,26 @@
(def pc (frame :pc))
(def sourcemap (in dasm :sourcemap))
(var last-loc [-2 -2])
(print "\n signal: " (.signal))
(print " function: " (dasm :name) " [" (in dasm :source "") "]")
(eprint "\n signal: " (.signal))
(eprint " status: " (fiber/status (.fiber)))
(eprint " function: " (get dasm :name "<anonymous>") " [" (in dasm :source "") "]")
(when-let [constants (dasm :constants)]
(printf " constants: %.4q" constants))
(printf " slots: %.4q\n" (frame :slots))
(eprintf " constants: %.4q" constants))
(eprintf " slots: %.4q\n" (frame :slots))
(def padding (string/repeat " " 20))
(loop [i :range [0 (length bytecode)]
:let [instr (bytecode i)]]
(prin (if (= (tuple/type instr) :brackets) "*" " "))
(prin (if (= i pc) "> " " "))
(prinf "%.20s" (string (string/join (map string instr) " ") padding))
(eprin (if (= (tuple/type instr) :brackets) "*" " "))
(eprin (if (= i pc) "> " " "))
(eprinf "%.20s" (string (string/join (map string instr) " ") padding))
(when sourcemap
(let [[sl sc] (sourcemap i)
loc [sl sc]]
(when (not= loc last-loc)
(set last-loc loc)
(prin " # line " sl ", column " sc))))
(print))
(print))
(eprin " # line " sl ", column " sc))))
(eprint))
(eprint))
(defn .breakall
"Set breakpoints on all instructions in the current function."
@@ -3389,7 +3456,7 @@
(def bytecode (.bytecode n))
(forv i 0 (length bytecode)
(debug/fbreak fun i))
(print "Set " (length bytecode) " breakpoints in " fun))
(eprint "set " (length bytecode) " breakpoints in " fun))
(defn .clearall
"Clear all breakpoints on the current function."
@@ -3398,7 +3465,7 @@
(def bytecode (.bytecode n))
(forv i 0 (length bytecode)
(debug/unfbreak fun i))
(print "Cleared " (length bytecode) " breakpoints in " fun)))
(eprint "cleared " (length bytecode) " breakpoints in " fun)))
(defn .source
"Show the source code for the function being debugged."
@@ -3406,7 +3473,7 @@
(def frame (.frame n))
(def s (frame :source))
(def all-source (slurp s))
(print "\n" all-source "\n"))
(eprint "\n" all-source "\n"))
(defn .break
"Set breakpoint at the current pc."
@@ -3415,7 +3482,7 @@
(def fun (frame :function))
(def pc (frame :pc))
(debug/fbreak fun pc)
(print "Set breakpoint in " fun " at pc=" pc))
(eprint "set breakpoint in " fun " at pc=" pc))
(defn .clear
"Clear the current breakpoint."
@@ -3424,7 +3491,7 @@
(def fun (frame :function))
(def pc (frame :pc))
(debug/unfbreak fun pc)
(print "Cleared breakpoint in " fun " at pc=" pc))
(eprint "cleared breakpoint in " fun " at pc=" pc))
(defn .next
"Go to the next breakpoint."
@@ -3448,10 +3515,6 @@
(set res (debug/step (.fiber))))
res)
(def debugger-env
"An environment that contains dot prefixed functions for debugging."
@{})
(def- debugger-keys (filter (partial string/has-prefix? ".") (keys root-env)))
(each k debugger-keys (put debugger-env k (root-env k)) (put root-env k nil))
@@ -3479,43 +3542,9 @@
":"
(:state p :delimiters) "> ")
buf env)))
(defn make-onsignal
[e level]
(defn enter-debugger
[f x]
(def nextenv (make-env env))
(put nextenv :fiber f)
(put nextenv :debug-level level)
(put nextenv :signal x)
(merge-into nextenv debugger-env)
(defn debugger-chunks [buf p]
(def status (:state p :delimiters))
(def c ((:where p) 0))
(def prpt (string "debug[" level "]:" c ":" status "> "))
(getline prpt buf nextenv))
(print "entering debug[" level "] - (quit) to exit")
(flush)
(repl debugger-chunks (make-onsignal nextenv (+ 1 level)) nextenv)
(print "exiting debug[" level "]")
(flush)
(nextenv :resume-value))
(fn [f x]
(def fs (fiber/status f))
(if (= :dead fs)
(do
(put e '_ @{:value x})
(printf (get e :pretty-format "%q") x)
(flush))
(do
(debug/stacktrace f x "")
(eflush)
(if (e :debug) (enter-debugger f x))))))
(run-context {:env env
:chunks chunks
:on-status (or onsignal (make-onsignal env 1))
:on-status (or onsignal (debugger-on-status env 1 true))
:parser parser
:read read
:source :repl}))
@@ -3539,7 +3568,7 @@
(ev/go (fn _call [&] (f ;args))))
(defmacro ev/spawn
"Run some code in a new fiber. This is shorthand for `(ev/call (fn [] ;body))`."
"Run some code in a new fiber. This is shorthand for `(ev/go (fn [] ;body))`."
[& body]
~(,ev/go (fn _spawn [&] ,;body)))
@@ -3582,8 +3611,8 @@
(def ,chan (,ev/chan))
(def ,res @[])
(,wait-for-fibers ,chan
,(seq [[i body] :pairs bodies]
~(,ev/go (fn [] (put ,res ,i ,body)) nil ,chan)))
,(seq [[i body] :pairs bodies]
~(,ev/go (fn [] (put ,res ,i ,body)) nil ,chan)))
,res))))
(compwhen (dyn 'net/listen)
@@ -3595,6 +3624,75 @@
(ev/call (fn [] (net/accept-loop s handler))))
s))
###
###
### FFI Extra
###
###
(defmacro delay
"Lazily evaluate a series of expressions. Returns a function that
returns the result of the last expression. Will only evaluate the
body once, and then memoizes the result."
[& forms]
(def state (gensym))
(def loaded (gensym))
~((fn []
(var ,state nil)
(var ,loaded nil)
(fn []
(if ,loaded
,state
(do
(set ,loaded true)
(set ,state (do ,;forms))))))))
(compwhen (dyn 'ffi/native)
(defdyn *ffi-context* " Current native library for ffi/bind and other settings")
(defn- default-mangle
[name &]
(string/replace-all "-" "_" name))
(defn ffi/context
"Set the path of the dynamic library to implictly bind, as well
as other global state for ease of creating native bindings."
[&opt native-path &named map-symbols lazy]
(default map-symbols default-mangle)
(def lib (if lazy nil (ffi/native native-path)))
(def lazy-lib (if lazy (delay (ffi/native native-path))))
(setdyn *ffi-context*
@{:native-path native-path
:native lib
:native-lazy lazy-lib
:lazy lazy
:map-symbols map-symbols}))
(defmacro ffi/defbind
"Generate bindings for native functions in a convenient manner."
[name ret-type & body]
(def real-ret-type (eval ret-type))
(def meta (slice body 0 -2))
(def arg-pairs (partition 2 (last body)))
(def formal-args (map 0 arg-pairs))
(def type-args (map 1 arg-pairs))
(def computed-type-args (eval ~[,;type-args]))
(def {:native lib
:lazy lazy
:native-lazy llib
:map-symbols ms} (assert (dyn *ffi-context*) "no ffi context found"))
(def raw-symbol (ms name))
(defn make-sig []
(ffi/signature :default real-ret-type ;computed-type-args))
(defn make-ptr []
(assert (ffi/lookup (if lazy (llib) lib) raw-symbol) (string "failed to find ffi symbol " raw-symbol)))
(if lazy
~(defn ,name ,;meta [,;formal-args]
(,ffi/call (,(delay (make-ptr))) (,(delay (make-sig))) ,;formal-args))
~(defn ,name ,;meta [,;formal-args]
(,ffi/call ,(make-ptr) ,(make-sig) ,;formal-args)))))
###
###
### Flychecking
@@ -3665,7 +3763,7 @@
(try
(dofile path :evaluator flycheck-evaluator ;(kvs kwargs))
([e f]
(debug/stacktrace f e "")))
(debug/stacktrace f e "")))
(table/clear module/cache)
(merge-into module/cache old-modcache)
nil)
@@ -3682,10 +3780,18 @@
(defn- run-main
[env subargs arg]
(if-let [entry (in env 'main)
main (or (get entry :value) (in (get entry :ref) 0))]
(let [thunk (compile [main ;subargs] env arg)]
(if (function? thunk) (thunk) (error (thunk :error))))))
(when-let [entry (in env 'main)
main (or (get entry :value) (in (get entry :ref) 0))]
(def guard (if (get env :debug) :ydt :y))
(defn wrap-main [&]
(main ;subargs))
(def f (fiber/new wrap-main guard))
(fiber/setenv f env)
(var res nil)
(while (fiber/can-resume? f)
(set res (resume f res))
(when (not= :dead (fiber/status f))
((debugger-on-status env) f res)))))
(defdyn *args*
"Dynamic bindings that will contain command line arguments at program start.")
@@ -3781,7 +3887,7 @@
"E" (fn E-switch [i &]
(set no-file false)
(def subargs (array/slice args (+ i 2)))
(def src ~|,(parse (in args (+ i 1))))
(def src ~(short-fn ,(parse (in args (+ i 1))) E-expression))
(def thunk (compile src))
(if (function? thunk)
((thunk) ;subargs)
@@ -3847,8 +3953,8 @@
(file/read stdin :line buf))
(def env (make-env))
(when-let [profile.janet (dyn *profilepath*)]
(def new-env (dofile profile.janet :exit true))
(merge-module env new-env "" false))
(def new-env (dofile profile.janet :exit true))
(merge-module env new-env "" false))
(when debug-flag
(put env *debug* true)
(put env *redef* true))
@@ -3870,10 +3976,6 @@
(do
# Deprecate file/popen
(when-let [v (get root-env 'file/popen)]
(put v :deprecated true))
# Modify root-env to remove private symbols and
# flatten nested tables.
(loop [[k v] :in (pairs root-env)
@@ -3938,6 +4040,7 @@
"src/core/debug.c"
"src/core/emit.c"
"src/core/ev.c"
"src/core/ffi.c"
"src/core/fiber.c"
"src/core/gc.c"
"src/core/inttypes.c"

View File

@@ -4,10 +4,10 @@
#define JANETCONF_H
#define JANET_VERSION_MAJOR 1
#define JANET_VERSION_MINOR 22
#define JANET_VERSION_MINOR 25
#define JANET_VERSION_PATCH 1
#define JANET_VERSION_EXTRA "-dev"
#define JANET_VERSION "1.22.1-dev"
#define JANET_VERSION_EXTRA ""
#define JANET_VERSION "1.25.1"
/* #define JANET_BUILD "local" */

View File

@@ -23,14 +23,16 @@
#ifndef JANET_AMALG
#include "features.h"
#include <janet.h>
#include "util.h"
#include "gc.h"
#include "state.h"
#endif
#ifdef JANET_EV
#ifdef JANET_WINDOWS
#include <windows.h>
#endif
#endif
#endif
/* Create new userdata */
void *janet_abstract_begin(const JanetAbstractType *atype, size_t size) {
@@ -85,6 +87,14 @@ void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size) {
#ifdef JANET_WINDOWS
size_t janet_os_mutex_size(void) {
return sizeof(CRITICAL_SECTION);
}
size_t janet_os_rwlock_size(void) {
return sizeof(void *);
}
static int32_t janet_incref(JanetAbstractHead *ab) {
return InterlockedIncrement(&ab->gc.data.refcount);
}
@@ -137,6 +147,14 @@ void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
#else
size_t janet_os_mutex_size(void) {
return sizeof(pthread_mutex_t);
}
size_t janet_os_rwlock_size(void) {
return sizeof(pthread_rwlock_t);
}
static int32_t janet_incref(JanetAbstractHead *ab) {
return __atomic_add_fetch(&ab->gc.data.refcount, 1, __ATOMIC_RELAXED);
}
@@ -149,44 +167,44 @@ void janet_os_mutex_init(JanetOSMutex *mutex) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(mutex, &attr);
pthread_mutex_init((pthread_mutex_t *) mutex, &attr);
}
void janet_os_mutex_deinit(JanetOSMutex *mutex) {
pthread_mutex_destroy(mutex);
pthread_mutex_destroy((pthread_mutex_t *) mutex);
}
void janet_os_mutex_lock(JanetOSMutex *mutex) {
pthread_mutex_lock(mutex);
pthread_mutex_lock((pthread_mutex_t *) mutex);
}
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
int ret = pthread_mutex_unlock(mutex);
int ret = pthread_mutex_unlock((pthread_mutex_t *) mutex);
if (ret) janet_panic("cannot release lock");
}
void janet_os_rwlock_init(JanetOSRWLock *rwlock) {
pthread_rwlock_init(rwlock, NULL);
pthread_rwlock_init((pthread_rwlock_t *) rwlock, NULL);
}
void janet_os_rwlock_deinit(JanetOSRWLock *rwlock) {
pthread_rwlock_destroy(rwlock);
pthread_rwlock_destroy((pthread_rwlock_t *) rwlock);
}
void janet_os_rwlock_rlock(JanetOSRWLock *rwlock) {
pthread_rwlock_rdlock(rwlock);
pthread_rwlock_rdlock((pthread_rwlock_t *) rwlock);
}
void janet_os_rwlock_wlock(JanetOSRWLock *rwlock) {
pthread_rwlock_wrlock(rwlock);
pthread_rwlock_wrlock((pthread_rwlock_t *) rwlock);
}
void janet_os_rwlock_runlock(JanetOSRWLock *rwlock) {
pthread_rwlock_unlock(rwlock);
pthread_rwlock_unlock((pthread_rwlock_t *) rwlock);
}
void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
pthread_rwlock_unlock(rwlock);
pthread_rwlock_unlock((pthread_rwlock_t *) rwlock);
}
#endif

View File

@@ -553,6 +553,10 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
x = janet_get1(s, janet_ckeywordv("vararg"));
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
/* Check structarg */
x = janet_get1(s, janet_ckeywordv("structarg"));
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
/* Check source */
x = janet_get1(s, janet_ckeywordv("source"));
if (janet_checktype(x, JANET_STRING)) def->source = janet_unwrap_string(x);
@@ -884,6 +888,10 @@ static Janet janet_disasm_vararg(JanetFuncDef *def) {
return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_VARARG);
}
static Janet janet_disasm_structarg(JanetFuncDef *def) {
return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_STRUCTARG);
}
static Janet janet_disasm_constants(JanetFuncDef *def) {
JanetArray *constants = janet_array(def->constants_length);
for (int32_t i = 0; i < def->constants_length; i++) {
@@ -933,6 +941,7 @@ Janet janet_disasm(JanetFuncDef *def) {
janet_table_put(ret, janet_ckeywordv("bytecode"), janet_disasm_bytecode(def));
janet_table_put(ret, janet_ckeywordv("source"), janet_disasm_source(def));
janet_table_put(ret, janet_ckeywordv("vararg"), janet_disasm_vararg(def));
janet_table_put(ret, janet_ckeywordv("structarg"), janet_disasm_structarg(def));
janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def));
janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def));
janet_table_put(ret, janet_ckeywordv("constants"), janet_disasm_constants(def));
@@ -986,6 +995,7 @@ JANET_CORE_FN(cfun_disasm,
if (!janet_cstrcmp(kw, "source")) return janet_disasm_source(f->def);
if (!janet_cstrcmp(kw, "name")) return janet_disasm_name(f->def);
if (!janet_cstrcmp(kw, "vararg")) return janet_disasm_vararg(f->def);
if (!janet_cstrcmp(kw, "structarg")) return janet_disasm_structarg(f->def);
if (!janet_cstrcmp(kw, "slotcount")) return janet_disasm_slotcount(f->def);
if (!janet_cstrcmp(kw, "constants")) return janet_disasm_constants(f->def);
if (!janet_cstrcmp(kw, "sourcemap")) return janet_disasm_sourcemap(f->def);

View File

@@ -260,11 +260,27 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) {
}
int64_t janet_getinteger64(const Janet *argv, int32_t n) {
#ifdef JANET_INT_TYPES
return janet_unwrap_s64(argv[n]);
#else
Janet x = argv[n];
if (!janet_checkint64(x)) {
janet_panicf("bad slot #%d, expected 64 bit signed integer, got %v", n, x);
}
return (int64_t) janet_unwrap_number(x);
#endif
}
uint64_t janet_getuinteger64(const Janet *argv, int32_t n) {
#ifdef JANET_INT_TYPES
return janet_unwrap_u64(argv[n]);
#else
Janet x = argv[n];
if (!janet_checkint64(x)) {
janet_panicf("bad slot #%d, expected 64 bit unsigned integer, got %v", n, x);
}
return (uint64_t) janet_unwrap_number(x);
#endif
}
size_t janet_getsize(const Janet *argv, int32_t n) {

View File

@@ -996,7 +996,7 @@ JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *w
}
/* C Function for compiling */
JANET_CORE_FN(cfun,
JANET_CORE_FN(cfun_compile,
"(compile ast &opt env source lints)",
"Compiles an Abstract Syntax Tree (ast) into a function. "
"Pair the compile function with parsing functionality to implement "
@@ -1043,7 +1043,7 @@ JANET_CORE_FN(cfun,
void janet_lib_compile(JanetTable *env) {
JanetRegExt cfuns[] = {
JANET_CORE_REG("compile", cfun),
JANET_CORE_REG("compile", cfun_compile),
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, cfuns);

View File

@@ -42,51 +42,6 @@ extern size_t janet_core_image_size;
#define JDOC(x) NULL
#endif
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
* with native code. */
#if defined(JANET_NO_DYNAMIC_MODULES)
typedef int Clib;
#define load_clib(name) ((void) name, 0)
#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL)
#define error_clib() "dynamic libraries not supported"
#elif defined(JANET_WINDOWS)
#include <windows.h>
typedef HINSTANCE Clib;
#define load_clib(name) LoadLibrary((name))
#define symbol_clib(lib, sym) GetProcAddress((lib), (sym))
static char error_clib_buf[256];
static char *error_clib(void) {
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
error_clib_buf, sizeof(error_clib_buf), NULL);
error_clib_buf[strlen(error_clib_buf) - 1] = '\0';
return error_clib_buf;
}
#else
#include <dlfcn.h>
typedef void *Clib;
#define load_clib(name) dlopen((name), RTLD_NOW)
#define symbol_clib(lib, sym) dlsym((lib), (sym))
#define error_clib() dlerror()
#endif
static char *get_processed_name(const char *name) {
if (name[0] == '.') return (char *) name;
const char *c;
for (c = name; *c; c++) {
if (*c == '/') return (char *) name;
}
size_t l = (size_t)(c - name);
char *ret = janet_malloc(l + 3);
if (NULL == ret) {
JANET_OUT_OF_MEMORY;
}
ret[0] = '.';
ret[1] = '/';
memcpy(ret + 2, name, l + 1);
return ret;
}
JanetModule janet_native(const char *name, const uint8_t **error) {
char *processed_name = get_processed_name(name);
Clib lib = load_clib(processed_name);
@@ -659,27 +614,39 @@ JANET_CORE_FN(janet_core_signal,
"(signal what x)",
"Raise a signal with payload x. ") {
janet_arity(argc, 1, 2);
int sig;
Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
if (janet_checkint(argv[0])) {
int32_t s = janet_unwrap_integer(argv[0]);
if (s < 0 || s > 9) {
janet_panicf("expected user signal between 0 and 9, got %d", s);
}
sig = JANET_SIGNAL_USER0 + s;
janet_signalv(JANET_SIGNAL_USER0 + s, payload);
} else {
JanetKeyword kw = janet_getkeyword(argv, 0);
if (!janet_cstrcmp(kw, "yield")) {
sig = JANET_SIGNAL_YIELD;
} else if (!janet_cstrcmp(kw, "error")) {
sig = JANET_SIGNAL_ERROR;
} else if (!janet_cstrcmp(kw, "debug")) {
sig = JANET_SIGNAL_DEBUG;
} else {
janet_panicf("unknown signal, expected :yield, :error, or :debug, got %v", argv[0]);
for (unsigned i = 0; i < sizeof(janet_signal_names) / sizeof(char *); i++) {
if (!janet_cstrcmp(kw, janet_signal_names[i])) {
janet_signalv((JanetSignal) i, payload);
}
}
}
Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
janet_signalv(sig, payload);
janet_panicf("unknown signal %v", argv[0]);
}
JANET_CORE_FN(janet_core_memcmp,
"(memcmp a b &opt len offset-a offset-b)",
"Compare memory. Takes to byte sequences `a` and `b`, and "
"return 0 if they have identical contents, a negative integer if a is less than b, "
"and a positive integer if a is greather than b. Optionally take a length and offsets "
"to compare slices of the bytes sequences.") {
janet_arity(argc, 2, 5);
JanetByteView a = janet_getbytes(argv, 0);
JanetByteView b = janet_getbytes(argv, 1);
int32_t len = janet_optnat(argv, argc, 2, a.len < b.len ? a.len : b.len);
int32_t offset_a = janet_optnat(argv, argc, 3, 0);
int32_t offset_b = janet_optnat(argv, argc, 4, 0);
if (offset_a + len > a.len) janet_panicf("invalid offset-a: %d", offset_a);
if (offset_b + len > b.len) janet_panicf("invalid offset-b: %d", offset_b);
return janet_wrap_integer(memcmp(a.bytes + offset_a, b.bytes + offset_b, (size_t) len));
}
#ifdef JANET_BOOTSTRAP
@@ -983,6 +950,7 @@ static void janet_load_libs(JanetTable *env) {
JANET_CORE_REG("nat?", janet_core_check_nat),
JANET_CORE_REG("slice", janet_core_slice),
JANET_CORE_REG("signal", janet_core_signal),
JANET_CORE_REG("memcmp", janet_core_memcmp),
JANET_CORE_REG("getproto", janet_core_getproto),
JANET_REG_END
};
@@ -1016,6 +984,9 @@ static void janet_load_libs(JanetTable *env) {
#ifdef JANET_NET
janet_lib_net(env);
#endif
#ifdef JANET_FFI
janet_lib_ffi(env);
#endif
}
#ifdef JANET_BOOTSTRAP

View File

@@ -79,7 +79,11 @@ typedef struct {
int32_t limit;
int closed;
int is_threaded;
JanetOSMutex lock;
#ifdef JANET_WINDOWS
CRITICAL_SECTION lock;
#else
pthread_mutex_t lock;
#endif
} JanetChannel;
typedef struct {
@@ -531,10 +535,15 @@ static int janet_channel_push(JanetChannel *channel, Janet x, int mode);
static int janet_channel_pop(JanetChannel *channel, Janet *item, int is_choice);
static Janet make_supervisor_event(const char *name, JanetFiber *fiber, int threaded) {
Janet tup[2];
Janet tup[3];
tup[0] = janet_ckeywordv(name);
tup[1] = threaded ? fiber->last_value : janet_wrap_fiber(fiber) ;
return janet_wrap_tuple(janet_tuple_n(tup, 2));
if (fiber->env != NULL) {
tup[2] = janet_table_get(fiber->env, janet_ckeywordv("task-id"));
} else {
tup[2] = janet_wrap_nil();
}
return janet_wrap_tuple(janet_tuple_n(tup, 3));
}
/* Common init code */
@@ -643,7 +652,7 @@ static void janet_chan_init(JanetChannel *chan, int32_t limit, int threaded) {
janet_q_init(&chan->items);
janet_q_init(&chan->read_pending);
janet_q_init(&chan->write_pending);
janet_os_mutex_init(&chan->lock);
janet_os_mutex_init((JanetOSMutex *) &chan->lock);
}
static void janet_chan_deinit(JanetChannel *chan) {
@@ -656,17 +665,17 @@ static void janet_chan_deinit(JanetChannel *chan) {
}
}
janet_q_deinit(&chan->items);
janet_os_mutex_deinit(&chan->lock);
janet_os_mutex_deinit((JanetOSMutex *) &chan->lock);
}
static void janet_chan_lock(JanetChannel *chan) {
if (!janet_chan_is_threaded(chan)) return;
janet_os_mutex_lock(&chan->lock);
janet_os_mutex_lock((JanetOSMutex *) &chan->lock);
}
static void janet_chan_unlock(JanetChannel *chan) {
if (!janet_chan_is_threaded(chan)) return;
janet_os_mutex_unlock(&chan->lock);
janet_os_mutex_unlock((JanetOSMutex *) &chan->lock);
}
/*
@@ -2678,9 +2687,10 @@ error:
/* C functions */
JANET_CORE_FN(cfun_ev_go,
"(ev/go fiber &opt value supervisor)",
"Put a fiber on the event loop to be resumed later. Optionally pass "
"a value to resume with, otherwise resumes with nil. Returns the fiber. "
"(ev/go fiber-or-fun &opt value supervisor)",
"Put a fiber on the event loop to be resumed later. If a function is used, it is wrapped"
"with `fiber/new` first. "
"Optionally pass a value to resume with, otherwise resumes with nil. Returns the fiber. "
"An optional `core/channel` can be provided as a supervisor. When various "
"events occur in the newly scheduled fiber, an event will be pushed to the supervisor. "
"If not provided, the new fiber will inherit the current supervisor.") {
@@ -2715,6 +2725,8 @@ JANET_CORE_FN(cfun_ev_go,
return janet_wrap_fiber(fiber);
}
#define JANET_THREAD_SUPERVISOR_FLAG 0x100
/* For ev/thread - Run an interpreter in the new thread. */
static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
JanetBuffer *buffer = (JanetBuffer *) args.argp;
@@ -2737,7 +2749,7 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
}
/* Get supervsior */
if (flags & 0x8) {
if (flags & JANET_THREAD_SUPERVISOR_FLAG) {
Janet sup =
janet_unmarshal(nextbytes, endbytes - nextbytes,
JANET_MARSHAL_UNSAFE, NULL, &nextbytes);
@@ -2789,6 +2801,10 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
} else {
fiber = janet_unwrap_fiber(fiberv);
}
if (flags & 0x8) {
if (NULL == fiber->env) fiber->env = janet_table(0);
janet_table_put(fiber->env, janet_ckeywordv("task-id"), value);
}
fiber->supervisor_channel = janet_vm.user;
janet_schedule(fiber, value);
janet_loop();
@@ -2833,6 +2849,7 @@ JANET_CORE_FN(cfun_ev_thread,
"If you want to run the thread without waiting for a result, pass the `:n` flag to return nil immediately. "
"Otherwise, returns nil. Available flags:\n\n"
"* `:n` - return immediately\n"
"* `:t` - set the task-id of the new thread to value. The task-id is passed in messages to the supervisor channel.\n"
"* `:a` - don't copy abstract registry to new thread (performance optimization)\n"
"* `:c` - don't copy cfunction registry to new thread (performance optimization)") {
janet_arity(argc, 1, 4);
@@ -2840,10 +2857,10 @@ JANET_CORE_FN(cfun_ev_thread,
if (!janet_checktype(argv[0], JANET_FUNCTION)) janet_getfiber(argv, 0);
uint64_t flags = 0;
if (argc >= 3) {
flags = janet_getflags(argv, 2, "nac");
flags = janet_getflags(argv, 2, "nact");
}
void *supervisor = janet_optabstract(argv, argc, 3, &janet_channel_type, janet_vm.root_fiber->supervisor_channel);
if (NULL != supervisor) flags |= 0x8;
if (NULL != supervisor) flags |= JANET_THREAD_SUPERVISOR_FLAG;
/* Marshal arguments for the new thread. */
JanetBuffer *buffer = janet_malloc(sizeof(JanetBuffer));
@@ -2854,7 +2871,7 @@ JANET_CORE_FN(cfun_ev_thread,
if (!(flags & 0x2)) {
janet_marshal(buffer, janet_wrap_table(janet_vm.abstract_registry), NULL, JANET_MARSHAL_UNSAFE);
}
if (flags & 0x8) {
if (flags & JANET_THREAD_SUPERVISOR_FLAG) {
janet_marshal(buffer, janet_wrap_abstract(supervisor), NULL, JANET_MARSHAL_UNSAFE);
}
if (!(flags & 0x4)) {
@@ -3013,14 +3030,9 @@ JANET_CORE_FN(janet_cfun_stream_write,
janet_await();
}
typedef struct {
JanetOSMutex mutex;
} JanetAbstractMutex;
static int mutexgc(void *p, size_t size) {
JanetAbstractMutex *mutex = (JanetAbstractMutex *) p;
(void) size;
janet_os_mutex_deinit(&mutex->mutex);
janet_os_mutex_deinit(p);
return 0;
}
@@ -3031,43 +3043,38 @@ const JanetAbstractType janet_mutex_type = {
};
JANET_CORE_FN(janet_cfun_mutex,
"(ev/lock)",
"Create a new lock to coordinate threads.") {
"(ev/lock)",
"Create a new lock to coordinate threads.") {
janet_fixarity(argc, 0);
(void) argv;
JanetAbstractMutex *mutex = janet_abstract_threaded(&janet_mutex_type, sizeof(JanetAbstractMutex));
janet_os_mutex_init(&mutex->mutex);
void *mutex = janet_abstract_threaded(&janet_mutex_type, janet_os_mutex_size());
janet_os_mutex_init(mutex);
return janet_wrap_abstract(mutex);
}
JANET_CORE_FN(janet_cfun_mutex_acquire,
"(ev/acquire-lock lock)",
"Acquire a lock such that this operating system thread is the only thread with access to this resource."
" This will block this entire thread until the lock becomes available, and will not yield to other fibers "
"on this system thread.") {
"(ev/acquire-lock lock)",
"Acquire a lock such that this operating system thread is the only thread with access to this resource."
" This will block this entire thread until the lock becomes available, and will not yield to other fibers "
"on this system thread.") {
janet_fixarity(argc, 1);
JanetAbstractMutex *mutex = janet_getabstract(argv, 0, &janet_mutex_type);
janet_os_mutex_lock(&mutex->mutex);
void *mutex = janet_getabstract(argv, 0, &janet_mutex_type);
janet_os_mutex_lock(mutex);
return argv[0];
}
JANET_CORE_FN(janet_cfun_mutex_release,
"(ev/release-lock lock)",
"Release a lock such that other threads may acquire it.") {
"(ev/release-lock lock)",
"Release a lock such that other threads may acquire it.") {
janet_fixarity(argc, 1);
JanetAbstractMutex *mutex = janet_getabstract(argv, 0, &janet_mutex_type);
janet_os_mutex_unlock(&mutex->mutex);
void *mutex = janet_getabstract(argv, 0, &janet_mutex_type);
janet_os_mutex_unlock(mutex);
return argv[0];
}
typedef struct {
JanetOSRWLock rwlock;
} JanetAbstractRWLock;
static int rwlockgc(void *p, size_t size) {
JanetAbstractRWLock *rwlock = (JanetAbstractRWLock *) p;
(void) size;
janet_os_rwlock_deinit(&rwlock->rwlock);
janet_os_rwlock_deinit(p);
return 0;
}
@@ -3078,48 +3085,48 @@ const JanetAbstractType janet_rwlock_type = {
};
JANET_CORE_FN(janet_cfun_rwlock,
"(ev/rwlock)",
"Create a new read-write lock to coordinate threads.") {
"(ev/rwlock)",
"Create a new read-write lock to coordinate threads.") {
janet_fixarity(argc, 0);
(void) argv;
JanetAbstractRWLock *rwlock = janet_abstract_threaded(&janet_rwlock_type, sizeof(JanetAbstractRWLock));
janet_os_rwlock_init(&rwlock->rwlock);
void *rwlock = janet_abstract_threaded(&janet_rwlock_type, janet_os_rwlock_size());
janet_os_rwlock_init(rwlock);
return janet_wrap_abstract(rwlock);
}
JANET_CORE_FN(janet_cfun_rwlock_read_lock,
"(ev/acquire-rlock rwlock)",
"Acquire a read lock an a read-write lock.") {
"(ev/acquire-rlock rwlock)",
"Acquire a read lock an a read-write lock.") {
janet_fixarity(argc, 1);
JanetAbstractRWLock *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
janet_os_rwlock_rlock(&rwlock->rwlock);
void *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
janet_os_rwlock_rlock(rwlock);
return argv[0];
}
JANET_CORE_FN(janet_cfun_rwlock_write_lock,
"(ev/acquire-wlock rwlock)",
"Acquire a write lock on a read-write lock.") {
"(ev/acquire-wlock rwlock)",
"Acquire a write lock on a read-write lock.") {
janet_fixarity(argc, 1);
JanetAbstractRWLock *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
janet_os_rwlock_wlock(&rwlock->rwlock);
void *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
janet_os_rwlock_wlock(rwlock);
return argv[0];
}
JANET_CORE_FN(janet_cfun_rwlock_read_release,
"(ev/release-rlock rwlock)",
"Release a read lock on a read-write lock") {
"(ev/release-rlock rwlock)",
"Release a read lock on a read-write lock") {
janet_fixarity(argc, 1);
JanetAbstractRWLock *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
janet_os_rwlock_runlock(&rwlock->rwlock);
void *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
janet_os_rwlock_runlock(rwlock);
return argv[0];
}
JANET_CORE_FN(janet_cfun_rwlock_write_release,
"(ev/release-wlock rwlock)",
"Release a write lock on a read-write lock") {
"(ev/release-wlock rwlock)",
"Release a write lock on a read-write lock") {
janet_fixarity(argc, 1);
JanetAbstractRWLock *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
janet_os_rwlock_wunlock(&rwlock->rwlock);
void *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
janet_os_rwlock_wunlock(rwlock);
return argv[0];
}

1372
src/core/ffi.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -407,13 +407,26 @@ static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
return janet_wrap_nil();
}
/*
* In C, signed arithmetic overflow is undefined behvior
* but unsigned arithmetic overflow is twos complement
*
* Reference:
* https://en.cppreference.com/w/cpp/language/ub
* http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
*
* This means OPMETHOD & OPMETHODINVERT must always use
* unsigned arithmetic internally, regardless of the true type.
* This will not affect the end result (property of twos complement).
*/
#define OPMETHOD(T, type, name, oper) \
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
janet_arity(argc, 2, -1); \
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
*box = janet_unwrap_##type(argv[0]); \
for (int32_t i = 1; i < argc; i++) \
*box oper##= janet_unwrap_##type(argv[i]); \
/* This avoids undefined behavior. See above for why. */ \
*box = (T) ((uint64_t) (*box)) oper ((uint64_t) janet_unwrap_##type(argv[i])); \
return janet_wrap_abstract(box); \
} \
@@ -422,7 +435,8 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
janet_fixarity(argc, 2); \
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
*box = janet_unwrap_##type(argv[1]); \
*box oper##= janet_unwrap_##type(argv[0]); \
/* This avoids undefined behavior. See above for why. */ \
*box = (T) ((uint64_t) *box) oper ((uint64_t) janet_unwrap_##type(argv[0])); \
return janet_wrap_abstract(box); \
} \

View File

@@ -545,6 +545,16 @@ static Janet cfun_io_printf_impl_x(int32_t argc, Janet *argv, int newline,
if (newline) janet_buffer_push_u8(buf, '\n');
return janet_wrap_nil();
}
case JANET_FUNCTION: {
/* Special case function */
JanetFunction *fun = janet_unwrap_function(x);
JanetBuffer *buf = janet_buffer(0);
janet_buffer_format(buf, fmt, offset, argc, argv);
if (newline) janet_buffer_push_u8(buf, '\n');
Janet args[1] = { janet_wrap_buffer(buf) };
janet_call(fun, 1, args);
return janet_wrap_nil();
}
case JANET_NIL:
f = dflt_file;
if (f == NULL) janet_panic("cannot print to nil");
@@ -684,6 +694,16 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
janet_buffer_deinit(&buffer);
break;
}
case JANET_FUNCTION: {
JanetFunction *fun = janet_unwrap_function(x);
int32_t len = 0;
while (format[len]) len++;
JanetBuffer *buf = janet_buffer(len);
janet_formatbv(buf, format, args);
Janet args[1] = { janet_wrap_buffer(buf) };
janet_call(fun, 1, args);
break;
}
case JANET_BUFFER:
janet_formatbv(janet_unwrap_buffer(x), format, args);
break;

View File

@@ -37,6 +37,7 @@ typedef struct {
JanetFuncEnv **seen_envs;
JanetFuncDef **seen_defs;
int32_t nextid;
int maybe_cycles;
} MarshalState;
/* Lead bytes in marshaling protocol */
@@ -364,13 +365,15 @@ void janet_marshal_janet(JanetMarshalContext *ctx, Janet x) {
void janet_marshal_abstract(JanetMarshalContext *ctx, void *abstract) {
MarshalState *st = (MarshalState *)(ctx->m_state);
janet_table_put(&st->seen,
janet_wrap_abstract(abstract),
janet_wrap_integer(st->nextid++));
if (st->maybe_cycles) {
janet_table_put(&st->seen,
janet_wrap_abstract(abstract),
janet_wrap_integer(st->nextid++));
}
}
#define MARK_SEEN() \
janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++))
do { if (st->maybe_cycles) janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); } while (0)
static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
void *abstract = janet_unwrap_abstract(x);
@@ -428,11 +431,14 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
/* Check reference and registry value */
{
Janet check = janet_table_get(&st->seen, x);
if (janet_checkint(check)) {
pushbyte(st, LB_REFERENCE);
pushint(st, janet_unwrap_integer(check));
return;
Janet check;
if (st->maybe_cycles) {
check = janet_table_get(&st->seen, x);
if (janet_checkint(check)) {
pushbyte(st, LB_REFERENCE);
pushint(st, janet_unwrap_integer(check));
return;
}
}
if (st->rreg) {
check = janet_table_get(st->rreg, x);
@@ -613,6 +619,7 @@ void janet_marshal(
st.seen_defs = NULL;
st.seen_envs = NULL;
st.rreg = rreg;
st.maybe_cycles = !(flags & JANET_MARSHAL_NO_CYCLES);
janet_table_init(&st.seen, 0);
marshal_one(&st, x, flags);
janet_table_deinit(&st.seen);
@@ -1471,16 +1478,17 @@ JANET_CORE_FN(cfun_env_lookup,
}
JANET_CORE_FN(cfun_marshal,
"(marshal x &opt reverse-lookup buffer)",
"(marshal x &opt reverse-lookup buffer no-cycles)",
"Marshal a value into a buffer and return the buffer. The buffer "
"can then later be unmarshalled to reconstruct the initial value. "
"Optionally, one can pass in a reverse lookup table to not marshal "
"aliased values that are found in the table. Then a forward "
"lookup table can be used to recover the original value when "
"unmarshalling.") {
janet_arity(argc, 1, 3);
janet_arity(argc, 1, 4);
JanetBuffer *buffer;
JanetTable *rreg = NULL;
uint32_t flags = 0;
if (argc > 1) {
rreg = janet_gettable(argv, 1);
}
@@ -1489,7 +1497,10 @@ JANET_CORE_FN(cfun_marshal,
} else {
buffer = janet_buffer(10);
}
janet_marshal(buffer, argv[0], rreg, 0);
if (argc > 3 && janet_truthy(argv[3])) {
flags |= JANET_MARSHAL_NO_CYCLES;
}
janet_marshal(buffer, argv[0], rreg, flags);
return janet_wrap_buffer(buffer);
}

View File

@@ -224,7 +224,12 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
janet_schedule(s->fiber, janet_wrap_nil());
return JANET_ASYNC_STATUS_DONE;
case JANET_ASYNC_EVENT_READ: {
#if defined(JANET_LINUX)
JSock connfd = accept4(s->stream->handle, NULL, NULL, SOCK_CLOEXEC);
#else
/* On BSDs, CLOEXEC should be inherited from server socket */
JSock connfd = accept(s->stream->handle, NULL, NULL);
#endif
if (JSOCKVALID(connfd)) {
janet_net_socknoblock(connfd);
JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
@@ -884,7 +889,6 @@ static JanetStream *make_stream(JSock handle, uint32_t flags) {
return janet_stream((JanetHandle) handle, flags | JANET_STREAM_SOCKET, net_stream_methods);
}
void janet_lib_net(JanetTable *env) {
JanetRegExt net_cfuns[] = {
JANET_CORE_REG("net/address", cfun_net_sockaddr),

View File

@@ -470,15 +470,7 @@ static int proc_get_status(JanetProc *proc) {
/* Function that is called in separate thread to wait on a pid */
static JanetEVGenericMessage janet_proc_wait_subr(JanetEVGenericMessage args) {
JanetProc *proc = (JanetProc *) args.argp;
#ifdef WNOWAIT
pid_t result;
int status = 0;
do {
result = waitpid(proc->pid, &status, WNOWAIT);
} while (result == -1 && errno == EINTR);
#else
args.tag = proc_get_status(proc);
#endif
return args;
}
@@ -489,11 +481,7 @@ static void janet_proc_wait_cb(JanetEVGenericMessage args) {
janet_ev_dec_refcount();
JanetProc *proc = (JanetProc *) args.argp;
if (NULL != proc) {
#ifdef WNOWAIT
int status = proc_get_status(proc);
#else
int status = args.tag;
#endif
proc->return_code = (int32_t) status;
proc->flags |= JANET_PROC_WAITED;
proc->flags &= ~JANET_PROC_WAITING;
@@ -1121,8 +1109,8 @@ JANET_CORE_FN(os_spawn,
"Execute a program on the system and return a handle to the process. Otherwise, takes the "
"same arguments as `os/execute`. Does not wait for the process. "
"For each of the :in, :out, and :err keys to the `env` argument, one "
"can also pass in the keyword `:pipe`"
"to get streams for standard IO of the subprocess that can be read from and written to."
"can also pass in the keyword `:pipe` "
"to get streams for standard IO of the subprocess that can be read from and written to. "
"The returned value `proc` has the fields :in, :out, :err, :return-code, and "
"the additional field :pid on unix-like platforms. Use `(os/proc-wait proc)` to rejoin the "
"subprocess or `(os/proc-kill proc)`.") {
@@ -1336,7 +1324,7 @@ JANET_CORE_FN(os_date,
time_t t;
struct tm t_infos;
struct tm *t_info = NULL;
if (argc) {
if (argc && !janet_checktype(argv[0], JANET_NIL)) {
int64_t integer = janet_getinteger64(argv, 0);
t = (time_t) integer;
} else {
@@ -1345,6 +1333,7 @@ JANET_CORE_FN(os_date,
if (argc >= 2 && janet_truthy(argv[1])) {
/* local time */
#ifdef JANET_WINDOWS
_tzset();
localtime_s(&t_infos, &t);
t_info = &t_infos;
#else
@@ -2035,23 +2024,23 @@ JANET_CORE_FN(os_open,
"Allowed flags are as follows:\n\n"
" * :r - open this file for reading\n"
" * :w - open this file for writing\n"
" * :c - create a new file (O_CREATE)\n"
" * :e - fail if the file exists (O_EXCL)\n"
" * :t - shorten an existing file to length 0 (O_TRUNC)\n\n"
" * :c - create a new file (O\\_CREATE)\n"
" * :e - fail if the file exists (O\\_EXCL)\n"
" * :t - shorten an existing file to length 0 (O\\_TRUNC)\n\n"
"Posix-only flags:\n\n"
" * :a - append to a file (O_APPEND)\n"
" * :x - O_SYNC\n"
" * :C - O_NOCTTY\n\n"
" * :a - append to a file (O\\_APPEND)\n"
" * :x - O\\_SYNC\n"
" * :C - O\\_NOCTTY\n\n"
"Windows-only flags:\n\n"
" * :R - share reads (FILE_SHARE_READ)\n"
" * :W - share writes (FILE_SHARE_WRITE)\n"
" * :D - share deletes (FILE_SHARE_DELETE)\n"
" * :H - FILE_ATTRIBUTE_HIDDEN\n"
" * :O - FILE_ATTRIBUTE_READONLY\n"
" * :F - FILE_ATTRIBUTE_OFFLINE\n"
" * :T - FILE_ATTRIBUTE_TEMPORARY\n"
" * :d - FILE_FLAG_DELETE_ON_CLOSE\n"
" * :b - FILE_FLAG_NO_BUFFERING\n") {
" * :R - share reads (FILE\\_SHARE\\_READ)\n"
" * :W - share writes (FILE\\_SHARE\\_WRITE)\n"
" * :D - share deletes (FILE\\_SHARE\\_DELETE)\n"
" * :H - FILE\\_ATTRIBUTE\\_HIDDEN\n"
" * :O - FILE\\_ATTRIBUTE\\_READONLY\n"
" * :F - FILE\\_ATTRIBUTE\\_OFFLINE\n"
" * :T - FILE\\_ATTRIBUTE\\_TEMPORARY\n"
" * :d - FILE\\_FLAG\\_DELETE\\_ON\\_CLOSE\n"
" * :b - FILE\\_FLAG\\_NO\\_BUFFERING\n") {
janet_arity(argc, 1, 3);
const char *path = janet_getcstring(argv, 0);
const uint8_t *opt_flags = janet_optkeyword(argv, argc, 1, (const uint8_t *) "r");
@@ -2148,20 +2137,18 @@ JANET_CORE_FN(os_open,
#ifdef JANET_LINUX
open_flags |= O_CLOEXEC;
#endif
int read_flag = 0;
int write_flag = 0;
for (const uint8_t *c = opt_flags; *c; c++) {
switch (*c) {
default:
break;
case 'r':
open_flags = (open_flags & O_WRONLY)
? ((open_flags & ~O_WRONLY) | O_RDWR)
: (open_flags | O_RDONLY);
read_flag = 1;
stream_flags |= JANET_STREAM_READABLE;
break;
case 'w':
open_flags = (open_flags & O_RDONLY)
? ((open_flags & ~O_RDONLY) | O_RDWR)
: (open_flags | O_WRONLY);
write_flag = 1;
stream_flags |= JANET_STREAM_WRITABLE;
break;
case 'c':
@@ -2185,6 +2172,15 @@ JANET_CORE_FN(os_open,
break;
}
}
/* If both read and write, fix up to O_RDWR */
if (read_flag && !write_flag) {
open_flags |= O_RDONLY;
} else if (write_flag && !read_flag) {
open_flags |= O_WRONLY;
} else {
open_flags = O_RDWR;
}
do {
fd = open(path, open_flags, mode);
} while (fd == -1 && errno == EINTR);

View File

@@ -206,6 +206,37 @@ static void popstate(JanetParser *p, Janet val) {
}
}
static void delim_error(JanetParser *parser, size_t stack_index, char c, const char *msg) {
JanetParseState *s = parser->states + stack_index;
JanetBuffer *buffer = janet_buffer(40);
if (msg) {
janet_buffer_push_cstring(buffer, msg);
}
if (c) {
janet_buffer_push_u8(buffer, c);
}
if (stack_index > 0) {
janet_buffer_push_cstring(buffer, ", ");
if (s->flags & PFLAG_PARENS) {
janet_buffer_push_u8(buffer, '(');
} else if (s->flags & PFLAG_SQRBRACKETS) {
janet_buffer_push_u8(buffer, '[');
} else if (s->flags & PFLAG_CURLYBRACKETS) {
janet_buffer_push_u8(buffer, '{');
} else if (s->flags & PFLAG_STRING) {
janet_buffer_push_u8(buffer, '"');
} else if (s->flags & PFLAG_LONGSTRING) {
int32_t i;
for (i = 0; i < s->argn; i++) {
janet_buffer_push_u8(buffer, '`');
}
}
janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
}
parser->error = (const char *) janet_string(buffer->data, buffer->count);
parser->flag |= JANET_PARSER_GENERATED_ERROR;
}
static int checkescape(uint8_t c) {
switch (c) {
default:
@@ -612,7 +643,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
case '}': {
Janet ds;
if (p->statecount == 1) {
p->error = "unexpected delimiter";
delim_error(p, 0, c, "unexpected closing delimiter ");
return 1;
}
if ((c == ')' && (state->flags & PFLAG_PARENS)) ||
@@ -633,7 +664,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
ds = close_struct(p, state);
}
} else {
p->error = "mismatched delimiter";
delim_error(p, p->statecount - 1, c, "mismatched delimiter ");
return 1;
}
popstate(p, ds);
@@ -684,26 +715,7 @@ void janet_parser_eof(JanetParser *parser) {
size_t oldline = parser->line;
janet_parser_consume(parser, '\n');
if (parser->statecount > 1) {
JanetParseState *s = parser->states + (parser->statecount - 1);
JanetBuffer *buffer = janet_buffer(40);
janet_buffer_push_cstring(buffer, "unexpected end of source, ");
if (s->flags & PFLAG_PARENS) {
janet_buffer_push_u8(buffer, '(');
} else if (s->flags & PFLAG_SQRBRACKETS) {
janet_buffer_push_u8(buffer, '[');
} else if (s->flags & PFLAG_CURLYBRACKETS) {
janet_buffer_push_u8(buffer, '{');
} else if (s->flags & PFLAG_STRING) {
janet_buffer_push_u8(buffer, '"');
} else if (s->flags & PFLAG_LONGSTRING) {
int32_t i;
for (i = 0; i < s->argn; i++) {
janet_buffer_push_u8(buffer, '`');
}
}
janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
parser->error = (const char *) janet_string(buffer->data, buffer->count);
parser->flag |= JANET_PARSER_GENERATED_ERROR;
delim_error(parser, parser->statecount - 1, 0, "unexpected end of source");
}
parser->line = oldline;
parser->column = oldcolumn;

View File

@@ -211,9 +211,10 @@ tail:
}
case RULE_SET: {
if (text >= s->text_end) return NULL;
uint32_t word = rule[1 + (text[0] >> 5)];
uint32_t mask = (uint32_t)1 << (text[0] & 0x1F);
return (text < s->text_end && (word & mask))
return (word & mask)
? text + 1
: NULL;
}
@@ -260,24 +261,46 @@ tail:
goto tail;
}
case RULE_IF:
case RULE_IFNOT: {
case RULE_IF: {
const uint32_t *rule_a = s->bytecode + rule[1];
const uint32_t *rule_b = s->bytecode + rule[2];
down1(s);
const uint8_t *result = peg_rule(s, rule_a, text);
up1(s);
if (rule[0] == RULE_IF ? !result : !!result) return NULL;
if (!result) return NULL;
rule = rule_b;
goto tail;
}
case RULE_IFNOT: {
const uint32_t *rule_a = s->bytecode + rule[1];
const uint32_t *rule_b = s->bytecode + rule[2];
down1(s);
CapState cs = cap_save(s);
const uint8_t *result = peg_rule(s, rule_a, text);
if (!!result) {
up1(s);
return NULL;
} else {
cap_load(s, cs);
up1(s);
rule = rule_b;
goto tail;
}
}
case RULE_NOT: {
const uint32_t *rule_a = s->bytecode + rule[1];
down1(s);
CapState cs = cap_save(s);
const uint8_t *result = peg_rule(s, rule_a, text);
up1(s);
return (result) ? NULL : text;
if (result) {
up1(s);
return NULL;
} else {
cap_load(s, cs);
up1(s);
return text;
}
}
case RULE_THRU:
@@ -1661,7 +1684,9 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
}
static void peg_call_reset(PegCall *c) {
c->s.depth = JANET_RECURSION_GUARD;
c->s.captures->count = 0;
c->s.tagged_captures->count = 0;
c->s.scratch->count = 0;
c->s.tags->count = 0;
}

View File

@@ -762,8 +762,7 @@ static const char *scanformat(
memset(precision, '\0', 3);
while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL)
p++; /* skip flags */
if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS) / sizeof(char))
janet_panic("invalid format (repeated flags)");
if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS)) janet_panic("invalid format (repeated flags)");
if (isdigit((int)(*p)))
width[0] = *p++; /* skip width */
if (isdigit((int)(*p)))
@@ -983,8 +982,9 @@ void janet_buffer_format(
break;
}
case 's': {
const uint8_t *s = janet_getstring(argv, arg);
int32_t l = janet_string_length(s);
JanetByteView bytes = janet_getbytes(argv, arg);
const uint8_t *s = bytes.bytes;
int32_t l = bytes.len;
if (form[2] == '\0')
janet_buffer_push_bytes(b, s, l);
else {

View File

@@ -80,9 +80,9 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
const char *e = janet_parser_error(&parser);
errflags |= 0x04;
ret = janet_cstringv(e);
size_t line = parser.line;
size_t col = parser.column;
janet_eprintf("%s:%lu:%lu: parse error: %s\n", sourcePath, line, col, e);
int32_t line = (int32_t) parser.line;
int32_t col = (int32_t) parser.column;
janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e);
done = 1;
break;
}

View File

@@ -31,7 +31,7 @@
static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv) {
if (argn != 1) {
janetc_cerror(opts.compiler, "expected 1 argument");
janetc_cerror(opts.compiler, "expected 1 argument to quote");
return janetc_cslot(janet_wrap_nil());
}
return janetc_cslot(argv[0]);
@@ -40,7 +40,7 @@ static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv)
static JanetSlot janetc_splice(JanetFopts opts, int32_t argn, const Janet *argv) {
JanetSlot ret;
if (argn != 1) {
janetc_cerror(opts.compiler, "expected 1 argument");
janetc_cerror(opts.compiler, "expected 1 argument to splice");
return janetc_cslot(janet_wrap_nil());
}
ret = janetc_value(opts, argv[0]);
@@ -117,7 +117,7 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
static JanetSlot janetc_quasiquote(JanetFopts opts, int32_t argn, const Janet *argv) {
if (argn != 1) {
janetc_cerror(opts.compiler, "expected 1 argument");
janetc_cerror(opts.compiler, "expected 1 argument to quasiquote");
return janetc_cslot(janet_wrap_nil());
}
return quasiquote(opts, argv[0], JANET_RECURSION_GUARD, 0);
@@ -143,7 +143,7 @@ static int destructure(JanetCompiler *c,
JanetTable *attr) {
switch (janet_type(left)) {
default:
janetc_cerror(c, "unexpected type in destructuring");
janetc_error(c, janet_formatc("unexpected type in destruction, got %v", left));
return 1;
case JANET_SYMBOL:
/* Leaf, assign right to left */
@@ -302,6 +302,9 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv)
static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv) {
int32_t i;
JanetTable *tab = janet_table(2);
const char *binding_name = janet_type(argv[0]) == JANET_SYMBOL
? ((const char *)janet_unwrap_symbol(argv[0]))
: "<multiple bindings>";
for (i = 1; i < argn - 1; i++) {
Janet attr = argv[i];
switch (janet_type(attr)) {
@@ -309,7 +312,7 @@ static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv)
janetc_cerror(c, "unexpected form - did you intend to use defn?");
break;
default:
janetc_cerror(c, "could not add metadata to binding");
janetc_error(c, janet_formatc("cannot add metadata %v to binding %s", attr, binding_name));
break;
case JANET_KEYWORD:
janet_table_put(tab, attr, janet_wrap_true());
@@ -822,6 +825,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
int selfref = 0;
int seenamp = 0;
int seenopt = 0;
int namedargs = 0;
/* Begin function */
c->scope->flags |= JANET_SCOPE_CLOSURE;
@@ -846,6 +850,9 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
/* Keep track of destructured parameters */
JanetSlot *destructed_params = NULL;
JanetSlot *named_params = NULL;
JanetTable *named_table = NULL;
JanetSlot named_slot;
/* Compile function parameters */
params = janet_unwrap_tuple(argv[parami]);
@@ -853,49 +860,75 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
arity = paramcount;
for (i = 0; i < paramcount; i++) {
Janet param = params[i];
if (janet_checktype(param, JANET_SYMBOL)) {
if (namedargs) {
arity--;
if (!janet_checktype(param, JANET_SYMBOL)) {
errmsg = "only named arguments can follow &named";
goto error;
}
Janet key = janet_wrap_keyword(janet_unwrap_symbol(param));
janet_table_put(named_table, key, param);
janet_v_push(named_params, janetc_farslot(c));
} else if (janet_checktype(param, JANET_SYMBOL)) {
/* Check for varargs and unfixed arity */
if (!janet_cstrcmp(janet_unwrap_symbol(param), "&")) {
if (seenamp) {
errmsg = "& in unexpected location";
goto error;
} else if (i == paramcount - 1) {
allow_extra = 1;
const uint8_t *sym = janet_unwrap_symbol(param);
if (sym[0] == '&') {
if (!janet_cstrcmp(sym, "&")) {
if (seenamp) {
errmsg = "& in unexpected location";
goto error;
} else if (i == paramcount - 1) {
allow_extra = 1;
arity--;
} else if (i == paramcount - 2) {
vararg = 1;
arity -= 2;
} else {
errmsg = "& in unexpected location";
goto error;
}
seenamp = 1;
} else if (!janet_cstrcmp(sym, "&opt")) {
if (seenopt) {
errmsg = "only one &opt allowed";
goto error;
} else if (i == paramcount - 1) {
errmsg = "&opt cannot be last item in parameter list";
goto error;
}
min_arity = i;
arity--;
} else if (i == paramcount - 2) {
vararg = 1;
arity -= 2;
} else {
errmsg = "& in unexpected location";
goto error;
}
seenamp = 1;
} else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&opt")) {
if (seenopt) {
errmsg = "only one &opt allowed";
goto error;
} else if (i == paramcount - 1) {
errmsg = "&opt cannot be last item in parameter list";
goto error;
}
min_arity = i;
arity--;
seenopt = 1;
} else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&keys")) {
if (seenamp) {
errmsg = "&keys in unexpected location";
goto error;
} else if (i == paramcount - 2) {
seenopt = 1;
} else if (!janet_cstrcmp(sym, "&keys")) {
if (seenamp) {
errmsg = "&keys in unexpected location";
goto error;
} else if (i == paramcount - 2) {
vararg = 1;
structarg = 1;
arity -= 2;
} else {
errmsg = "&keys in unexpected location";
goto error;
}
seenamp = 1;
} else if (!janet_cstrcmp(sym, "&named")) {
if (seenamp) {
errmsg = "&named in unexpected location";
goto error;
}
vararg = 1;
structarg = 1;
arity -= 2;
arity--;
seenamp = 1;
namedargs = 1;
named_table = janet_table(10);
named_slot = janetc_farslot(c);
} else {
errmsg = "&keys in unexpected location";
goto error;
janetc_nameslot(c, sym, janetc_farslot(c));
}
seenamp = 1;
} else {
janetc_nameslot(c, janet_unwrap_symbol(param), janetc_farslot(c));
janetc_nameslot(c, sym, janetc_farslot(c));
}
} else {
janet_v_push(destructed_params, janetc_farslot(c));
@@ -914,6 +947,14 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
}
janet_v_free(destructed_params);
/* Compile named arguments */
if (namedargs) {
Janet param = janet_wrap_table(named_table);
destructure(c, param, named_slot, defleaf, NULL);
janetc_freeslot(c, named_slot);
janet_v_free(named_params);
}
max_arity = (vararg || allow_extra) ? INT32_MAX : arity;
if (!seenopt) min_arity = arity;

View File

@@ -530,7 +530,7 @@ JANET_CORE_FN(cfun_string_join,
JANET_CORE_FN(cfun_string_format,
"(string/format format & values)",
"Similar to `snprintf`, but specialized for operating with Janet values. Returns "
"Similar to C's `snprintf`, but specialized for operating with Janet values. Returns "
"a new string.") {
janet_arity(argc, 1, -1);
JanetBuffer *buffer = janet_buffer(0);

View File

@@ -36,6 +36,13 @@
#endif
#endif
#ifdef JANET_WINDOWS
#ifdef JANET_DYNAMIC_MODULES
#include <psapi.h>
#pragma comment (lib, "Psapi.lib")
#endif
#endif
#ifdef JANET_APPLE
#include <AvailabilityMacros.h>
#endif
@@ -739,6 +746,13 @@ int janet_checkint64(Janet x) {
return janet_checkint64range(dval);
}
int janet_checkuint64(Janet x) {
if (!janet_checktype(x, JANET_NUMBER))
return 0;
double dval = janet_unwrap_number(x);
return dval >= 0 && dval <= JANET_INTMAX_DOUBLE && dval == (uint64_t) dval;
}
int janet_checksize(Janet x) {
if (!janet_checktype(x, JANET_NUMBER))
return 0;
@@ -877,6 +891,73 @@ int janet_cryptorand(uint8_t *out, size_t n) {
#endif
}
/* Dynamic library loading */
char *get_processed_name(const char *name) {
if (name[0] == '.') return (char *) name;
const char *c;
for (c = name; *c; c++) {
if (*c == '/') return (char *) name;
}
size_t l = (size_t)(c - name);
char *ret = janet_malloc(l + 3);
if (NULL == ret) {
JANET_OUT_OF_MEMORY;
}
ret[0] = '.';
ret[1] = '/';
memcpy(ret + 2, name, l + 1);
return ret;
}
#if defined(JANET_WINDOWS)
static char error_clib_buf[256];
char *error_clib(void) {
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
error_clib_buf, sizeof(error_clib_buf), NULL);
error_clib_buf[strlen(error_clib_buf) - 1] = '\0';
return error_clib_buf;
}
Clib load_clib(const char *name) {
if (name == NULL) {
return GetModuleHandle(NULL);
} else {
return LoadLibrary(name);
}
}
void free_clib(HINSTANCE clib) {
if (clib != GetModuleHandle(NULL)) {
FreeLibrary(clib);
}
}
void *symbol_clib(HINSTANCE clib, const char *sym) {
if (clib != GetModuleHandle(NULL)) {
return GetProcAddress(clib, sym);
} else {
/* Look up symbols from all loaded modules */
HMODULE hMods[1024];
DWORD needed = 0;
if (EnumProcessModules(GetCurrentProcess(), hMods, sizeof(hMods), &needed)) {
needed /= sizeof(HMODULE);
for (DWORD i = 0; i < needed; i++) {
void *address = GetProcAddress(hMods[i], sym);
if (NULL != address) {
return address;
}
}
} else {
janet_panicf("ffi: %s", error_clib());
}
return NULL;
}
}
#endif
/* Alloc function macro fills */
void *(janet_malloc)(size_t size) {

View File

@@ -31,6 +31,14 @@
#include <stdio.h>
#include <errno.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef JANET_EV
#ifndef JANET_WINDOWS
#include <pthread.h>
#endif
#endif
#if !defined(JANET_REDUCED_OS) || !defined(JANET_SINGLE_THREADED)
#include <time.h>
@@ -121,6 +129,31 @@ int janet_gettime(struct timespec *spec);
#define strdup(x) _strdup(x)
#endif
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
* with native code. */
#if defined(JANET_NO_DYNAMIC_MODULES)
typedef int Clib;
#define load_clib(name) ((void) name, 0)
#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL)
#define error_clib() "dynamic libraries not supported"
#define free_clib(c) ((void) (c), 0)
#elif defined(JANET_WINDOWS)
#include <windows.h>
typedef HINSTANCE Clib;
void *symbol_clib(Clib clib, const char *sym);
void free_clib(Clib clib);
Clib load_clib(const char *name);
char *error_clib(void);
#else
#include <dlfcn.h>
typedef void *Clib;
#define load_clib(name) dlopen((name), RTLD_NOW)
#define free_clib(lib) dlclose((lib))
#define symbol_clib(lib, sym) dlsym((lib), (sym))
#define error_clib() dlerror()
#endif
char *get_processed_name(const char *name);
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
/* Initialize builtin libraries */
@@ -159,5 +192,8 @@ void janet_lib_ev(JanetTable *env);
void janet_ev_mark(void);
int janet_make_pipe(JanetHandle handles[2], int mode);
#endif
#ifdef JANET_FFI
void janet_lib_ffi(JanetTable *env);
#endif
#endif

View File

@@ -295,6 +295,15 @@ int janet_equals(Janet x, Janet y) {
return 1;
}
static uint64_t murmur64(uint64_t h) {
h ^= h >> 33;
h *= 0xff51afd7ed558ccdUL;
h ^= h >> 33;
h *= 0xc4ceb9fe1a85ec53UL;
h ^= h >> 33;
return h;
}
/* Computes a hash value for a function */
int32_t janet_hash(Janet x) {
int32_t hash = 0;
@@ -341,14 +350,11 @@ int32_t janet_hash(Janet x) {
default:
if (sizeof(double) == sizeof(void *)) {
/* Assuming 8 byte pointer (8 byte aligned) */
uint64_t i = janet_u64(x);
uint32_t lo = (uint32_t)(i & 0xFFFFFFFF);
uint32_t hi = (uint32_t)(i >> 32);
uint32_t hilo = (hi ^ lo) * 2654435769u;
hash = (int32_t)((hilo << 16) | (hilo >> 16));
uint64_t i = murmur64(janet_u64(x));
hash = (int32_t)(i >> 32);
} else {
/* Assuming 4 byte pointer (or smaller) */
ptrdiff_t diff = ((char *)janet_unwrap_pointer(x) - (char *)0);
uintptr_t diff = (uintptr_t) janet_unwrap_pointer(x);
uint32_t hilo = (uint32_t) diff * 2654435769u;
hash = (int32_t)((hilo << 16) | (hilo >> 16));
}

View File

@@ -220,14 +220,14 @@
/* Trace a function call */
static void vm_do_trace(JanetFunction *func, int32_t argc, const Janet *argv) {
if (func->def->name) {
janet_printf("trace (%S", func->def->name);
janet_eprintf("trace (%S", func->def->name);
} else {
janet_printf("trace (%p", janet_wrap_function(func));
janet_eprintf("trace (%p", janet_wrap_function(func));
}
for (int32_t i = 0; i < argc; i++) {
janet_printf(" %p", argv[i]);
janet_eprintf(" %p", argv[i]);
}
janet_printf(")\n");
janet_eprintf(")\n");
}
/* Invoke a method once we have looked it up */
@@ -1285,6 +1285,12 @@ JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out) {
return signal;
}
static Janet void_cfunction(int32_t argc, Janet *argv) {
(void) argc;
(void) argv;
janet_panic("placeholder");
}
Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
/* Check entry conditions */
if (!janet_vm.fiber)
@@ -1292,9 +1298,17 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
if (janet_vm.stackn >= JANET_RECURSION_GUARD)
janet_panic("C stack recursed too deeply");
/* Dirty stack */
int32_t dirty_stack = janet_vm.fiber->stacktop - janet_vm.fiber->stackstart;
if (dirty_stack) {
janet_fiber_cframe(janet_vm.fiber, void_cfunction);
}
/* Tracing */
if (fun->gc.flags & JANET_FUNCFLAG_TRACE) {
janet_vm.stackn++;
vm_do_trace(fun, argc, argv);
janet_vm.stackn--;
}
/* Push frame */
@@ -1322,6 +1336,10 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
/* Teardown */
janet_vm.stackn = oldn;
janet_gcunlock(handle);
if (dirty_stack) {
janet_fiber_popframe(janet_vm.fiber);
janet_vm.fiber->stacktop += dirty_stack;
}
if (signal != JANET_SIGNAL_OK) {
janet_panicv(*janet_vm.return_reg);

View File

@@ -163,6 +163,14 @@ extern "C" {
#define JANET_DYNAMIC_MODULES
#endif
/* Enable or disable the FFI library. Currently, FFI only enabled on
* x86-64 operating systems. */
#ifndef JANET_NO_FFI
#if !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64))
#define JANET_FFI
#endif
#endif
/* Enable or disable the assembler. Enabled by default. */
#ifndef JANET_NO_ASSEMBLER
#define JANET_ASSEMBLER
@@ -228,7 +236,7 @@ extern "C" {
/* Maximum depth to follow table prototypes before giving up and returning nil. */
#define JANET_MAX_PROTO_DEPTH 200
/* Maximum depth to follow table prototypes before giving up and returning nil. */
/* Prevent macros to expand too deeply and error out. */
#define JANET_MAX_MACRO_EXPAND 200
/* Define default max stack size for stacks before raising a stack overflow error.
@@ -299,22 +307,10 @@ typedef struct {
JANET_CURRENT_CONFIG_BITS })
#endif
/* Feature include for pthreads. Most feature detection code should go in
* features.h instead. */
#ifndef JANET_WINDOWS
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#if _XOPEN_SOURCE < 600
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 600
#endif
#endif
/* What to do when out of memory */
#ifndef JANET_OUT_OF_MEMORY
#include <stdio.h>
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0)
/* Some extra includes if EV is enabled */
#ifdef JANET_EV
typedef struct JanetOSMutex JanetOSMutex;
typedef struct JanetOSRWLock JanetOSRWLock;
#endif
/***** END SECTION CONFIG *****/
@@ -334,27 +330,10 @@ typedef struct {
#include <stddef.h>
#include <stdio.h>
/* Some extra includes if EV is enabled */
#ifdef JANET_EV
#ifdef JANET_WINDOWS
typedef struct JanetDudCriticalSection {
/* Avoid including windows.h here - instead, create a structure of the same size */
/* Needs to be same size as crtical section see WinNT.h for CRITCIAL_SECTION definition */
void *debug_info;
long lock_count;
long recursion_count;
void *owning_thread;
void *lock_semaphore;
unsigned long spin_count;
} JanetOSMutex;
typedef struct JanetDudRWLock {
void *ptr;
} JanetOSRWLock;
#else
#include <pthread.h>
typedef pthread_mutex_t JanetOSMutex;
typedef pthread_rwlock_t JanetOSRWLock;
#endif
/* What to do when out of memory */
#ifndef JANET_OUT_OF_MEMORY
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0)
#endif
#ifdef JANET_BSD
@@ -865,6 +844,7 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
JANET_API int janet_checkint(Janet x);
JANET_API int janet_checkint64(Janet x);
JANET_API int janet_checkuint64(Janet x);
JANET_API int janet_checksize(Janet x);
JANET_API JanetAbstract janet_checkabstract(Janet x, const JanetAbstractType *at);
#define janet_checkintrange(x) ((x) >= INT32_MIN && (x) <= INT32_MAX && (x) == (int32_t)(x))
@@ -1385,6 +1365,8 @@ JANET_API int32_t janet_abstract_incref(void *abst);
JANET_API int32_t janet_abstract_decref(void *abst);
/* Expose some OS sync primitives */
JANET_API size_t janet_os_mutex_size(void);
JANET_API size_t janet_os_rwlock_size(void);
JANET_API void janet_os_mutex_init(JanetOSMutex *mutex);
JANET_API void janet_os_mutex_deinit(JanetOSMutex *mutex);
JANET_API void janet_os_mutex_lock(JanetOSMutex *mutex);
@@ -1689,6 +1671,7 @@ JANET_API JanetModule janet_native(const char *name, JanetString *error);
/* Marshaling */
#define JANET_MARSHAL_UNSAFE 0x20000
#define JANET_MARSHAL_NO_CYCLES 0x40000
JANET_API void janet_marshal(
JanetBuffer *buf,
@@ -1936,6 +1919,7 @@ JANET_API void *janet_getpointer(const Janet *argv, int32_t n);
JANET_API int32_t janet_getnat(const Janet *argv, int32_t n);
JANET_API int32_t janet_getinteger(const Janet *argv, int32_t n);
JANET_API int64_t janet_getinteger64(const Janet *argv, int32_t n);
JANET_API uint64_t janet_getuinteger64(const Janet *argv, int32_t n);
JANET_API size_t janet_getsize(const Janet *argv, int32_t n);
JANET_API JanetView janet_getindexed(const Janet *argv, int32_t n);
JANET_API JanetByteView janet_getbytes(const Janet *argv, int32_t n);

View File

@@ -87,8 +87,30 @@ static void simpleline(JanetBuffer *buffer) {
}
}
/* Windows */
#if defined(JANET_WINDOWS) || defined(JANET_SIMPLE_GETLINE)
/* State */
#ifndef JANET_SIMPLE_GETLINE
/* static state */
#define JANET_LINE_MAX 1024
#define JANET_MATCH_MAX 256
#define JANET_HISTORY_MAX 100
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
static JANET_THREAD_LOCAL int gbl_plen = 2;
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
static JANET_THREAD_LOCAL int gbl_len = 0;
static JANET_THREAD_LOCAL int gbl_pos = 0;
static JANET_THREAD_LOCAL int gbl_cols = 80;
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
static JANET_THREAD_LOCAL int gbl_history_count = 0;
static JANET_THREAD_LOCAL int gbl_historyi = 0;
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
static JANET_THREAD_LOCAL int gbl_match_count = 0;
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
#endif
/* Fallback */
#if defined(JANET_SIMPLE_GETLINE)
void janet_line_init() {
;
@@ -105,6 +127,80 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
simpleline(buffer);
}
/* Rich implementation */
#else
/* Windows */
#ifdef _WIN32
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
static void setup_console_output(void) {
/* Enable color console on windows 10 console and utf8 output and other processing */
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
SetConsoleOutputCP(65001);
}
/* Ansi terminal raw mode */
static int rawmode(void) {
if (gbl_israwmode) return 0;
HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode &= ~ENABLE_LINE_INPUT;
dwMode &= ~ENABLE_INSERT_MODE;
dwMode &= ~ENABLE_ECHO_INPUT;
dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
dwMode &= ~ENABLE_PROCESSED_INPUT;
if (!SetConsoleMode(hOut, dwMode)) return 1;
gbl_israwmode = 1;
return 0;
}
/* Disable raw mode */
static void norawmode(void) {
if (!gbl_israwmode) return;
HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_LINE_INPUT;
dwMode |= ENABLE_INSERT_MODE;
dwMode |= ENABLE_ECHO_INPUT;
dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT;
dwMode |= ENABLE_PROCESSED_INPUT;
SetConsoleMode(hOut, dwMode);
gbl_israwmode = 0;
}
static long write_console(const char *bytes, size_t n) {
DWORD nwritten = 0;
BOOL result = WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), bytes, (DWORD) n, &nwritten, NULL);
if (!result) return -1; /* error */
return (long)nwritten;
}
static long read_console(char *into, size_t n) {
DWORD numread;
BOOL result = ReadConsole(GetStdHandle(STD_INPUT_HANDLE), into, (DWORD) n, &numread, NULL);
if (!result) return -1; /* error */
return (long)numread;
}
static int check_simpleline(JanetBuffer *buffer) {
if (!_isatty(_fileno(stdin)) || rawmode()) {
simpleline(buffer);
return 1;
}
return 0;
}
/* Posix */
#else
@@ -125,24 +221,7 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c
#include <string.h>
#include <signal.h>
/* static state */
#define JANET_LINE_MAX 1024
#define JANET_MATCH_MAX 256
#define JANET_HISTORY_MAX 100
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
static JANET_THREAD_LOCAL int gbl_plen = 2;
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
static JANET_THREAD_LOCAL int gbl_len = 0;
static JANET_THREAD_LOCAL int gbl_pos = 0;
static JANET_THREAD_LOCAL int gbl_cols = 80;
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
static JANET_THREAD_LOCAL int gbl_history_count = 0;
static JANET_THREAD_LOCAL int gbl_historyi = 0;
static JANET_THREAD_LOCAL struct termios gbl_termios_start;
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
static JANET_THREAD_LOCAL int gbl_match_count = 0;
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
/* Unsupported terminal list from linenoise */
static const char *badterms[] = {
@@ -152,15 +231,6 @@ static const char *badterms[] = {
NULL
};
static char *sdup(const char *s) {
size_t len = strlen(s) + 1;
char *mem = janet_malloc(len);
if (!mem) {
return NULL;
}
return memcpy(mem, s, len);
}
/* Ansi terminal raw mode */
static int rawmode(void) {
struct termios t;
@@ -186,13 +256,53 @@ static void norawmode(void) {
gbl_israwmode = 0;
}
static int checktermsupport() {
const char *t = getenv("TERM");
int i;
if (!t) return 1;
for (i = 0; badterms[i]; i++)
if (!strcmp(t, badterms[i])) return 0;
return 1;
}
static long write_console(char *bytes, size_t n) {
return write(STDOUT_FILENO, bytes, n);
}
static long read_console(char *into, size_t n) {
return read(STDIN_FILENO, into, n);
}
static int check_simpleline(JanetBuffer *buffer) {
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
simpleline(buffer);
return 1;
}
if (rawmode()) {
simpleline(buffer);
return 1;
}
return 0;
}
#endif
static char *sdup(const char *s) {
size_t len = strlen(s) + 1;
char *mem = janet_malloc(len);
if (!mem) {
return NULL;
}
return memcpy(mem, s, len);
}
static int curpos(void) {
char buf[32];
int cols, rows;
unsigned int i = 0;
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1;
if (write_console("\x1b[6n", 4) != 4) return -1;
while (i < sizeof(buf) - 1) {
if (read(STDIN_FILENO, buf + i, 1) != 1) break;
if (read_console(buf + i, 1) != 1) break;
if (buf[i] == 'R') break;
i++;
}
@@ -203,18 +313,23 @@ static int curpos(void) {
}
static int getcols(void) {
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
return (int)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
#else
struct winsize ws;
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
int start, cols;
start = curpos();
if (start == -1) goto failed;
if (write(STDOUT_FILENO, "\x1b[999C", 6) != 6) goto failed;
if (write_console("\x1b[999C", 6) != 6) goto failed;
cols = curpos();
if (cols == -1) goto failed;
if (cols > start) {
char seq[32];
snprintf(seq, 32, "\x1b[%dD", cols - start);
if (write(STDOUT_FILENO, seq, strlen(seq)) == -1) {
if (write_console(seq, strlen(seq)) == -1) {
exit(1);
}
}
@@ -224,10 +339,11 @@ static int getcols(void) {
}
failed:
return 80;
#endif
}
static void clear(void) {
if (write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7) <= 0) {
if (write_console("\x1b[H\x1b[2J", 7) <= 0) {
exit(1);
}
}
@@ -259,7 +375,7 @@ static void refresh(void) {
/* Move cursor to original position. */
snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + gbl_plen));
janet_buffer_push_cstring(&b, seq);
if (write(STDOUT_FILENO, b.data, b.count) == -1) {
if (write_console((char *) b.data, b.count) == -1) {
exit(1);
}
janet_buffer_deinit(&b);
@@ -285,7 +401,7 @@ static int insert(char c, int draw) {
if (gbl_plen + gbl_len < gbl_cols) {
/* Avoid a full update of the line in the
* trivial case. */
if (write(STDOUT_FILENO, &c, 1) == -1) return -1;
if (write_console(&c, 1) == -1) return -1;
} else {
refresh();
}
@@ -312,7 +428,7 @@ static void historymove(int delta) {
gbl_historyi = gbl_history_count - 1;
}
strncpy(gbl_buf, gbl_history[gbl_historyi], JANET_LINE_MAX - 1);
gbl_pos = gbl_len = strlen(gbl_buf);
gbl_pos = gbl_len = (int) strlen(gbl_buf);
gbl_buf[gbl_len] = '\0';
refresh();
@@ -527,6 +643,7 @@ static void check_specials(JanetByteView src) {
check_cmatch(src, "unquote");
check_cmatch(src, "var");
check_cmatch(src, "while");
check_cmatch(src, "upscope");
}
static void resolve_format(JanetTable *entry) {
@@ -740,14 +857,14 @@ static int line() {
addhistory();
if (write(STDOUT_FILENO, gbl_prompt, gbl_plen) == -1) return -1;
if (write_console((char *) gbl_prompt, gbl_plen) == -1) return -1;
for (;;) {
char c;
char seq[3];
int rc;
do {
rc = read(STDIN_FILENO, &c, 1);
rc = read_console(&c, 1);
} while (rc < 0 && errno == EINTR);
if (rc <= 0) return -1;
@@ -764,8 +881,13 @@ static int line() {
kleft();
break;
case 3: /* ctrl-c */
clearlines();
norawmode();
#ifdef _WIN32
ExitProcess(1);
#else
kill(getpid(), SIGINT);
#endif
/* fallthrough */
case 17: /* ctrl-q */
gbl_cancel_current_repl_form = 1;
@@ -826,23 +948,25 @@ static int line() {
case 23: /* ctrl-w */
kbackspacew();
break;
#ifndef _WIN32
case 26: /* ctrl-z */
norawmode();
kill(getpid(), SIGSTOP);
rawmode();
refresh();
break;
#endif
case 27: /* escape sequence */
/* Read the next two bytes representing the escape sequence.
* Use two calls to handle slow terminals returning the two
* chars at different times. */
if (read(STDIN_FILENO, seq, 1) == -1) break;
if (read_console(seq, 1) == -1) break;
/* Esc[ = Control Sequence Introducer (CSI) */
if (seq[0] == '[') {
if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
if (read_console(seq + 1, 1) == -1) break;
if (seq[1] >= '0' && seq[1] <= '9') {
/* Extended escape, read additional byte. */
if (read(STDIN_FILENO, seq + 2, 1) == -1) break;
if (read_console(seq + 2, 1) == -1) break;
if (seq[2] == '~') {
switch (seq[1]) {
case '1': /* Home */
@@ -861,7 +985,7 @@ static int line() {
}
}
} else if (seq[0] == 'O') {
if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
if (read_console(seq + 1, 1) == -1) break;
switch (seq[1]) {
default:
break;
@@ -944,28 +1068,12 @@ void janet_line_deinit() {
gbl_historyi = 0;
}
static int checktermsupport() {
const char *t = getenv("TERM");
int i;
if (!t) return 1;
for (i = 0; badterms[i]; i++)
if (!strcmp(t, badterms[i])) return 0;
return 1;
}
void janet_line_get(const char *p, JanetBuffer *buffer) {
gbl_prompt = p;
buffer->count = 0;
gbl_historyi = 0;
if (check_simpleline(buffer)) return;
FILE *out = janet_dynfile("err", stderr);
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
simpleline(buffer);
return;
}
if (rawmode()) {
simpleline(buffer);
return;
}
if (line()) {
norawmode();
fputc('\n', out);
@@ -981,6 +1089,13 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
replacehistory();
}
static void clear_at_exit(void) {
if (!gbl_israwmode) {
clearlines();
norawmode();
}
}
#endif
/*
@@ -993,18 +1108,11 @@ int main(int argc, char **argv) {
JanetTable *env;
#ifdef _WIN32
/* Enable color console on windows 10 console and utf8 output. */
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
SetConsoleOutputCP(65001);
setup_console_output();
#endif
#if !defined(JANET_WINDOWS) && !defined(JANET_SIMPLE_GETLINE)
/* Try and not leave the terminal in a bad state */
atexit(norawmode);
#if !defined(JANET_SIMPLE_GETLINE)
atexit(clear_at_exit);
#endif
#if defined(JANET_PRF)

View File

@@ -164,36 +164,26 @@
(:close s))
(defn check-matching-names [stream]
(def ln (net/localname stream))
(def pn (net/peername stream))
(def [my-ip my-port] ln)
(def [remote-ip remote-port] pn)
(def msg (string my-ip " " my-port " " remote-ip " " remote-port))
(def buf @"")
(ev/gather
(net/write stream msg)
(net/read stream 1024 buf))
(def comparison (string/split " " buf))
(assert (and (= my-ip (get comparison 2))
(= (string my-port) (get comparison 3))
(= remote-ip (get comparison 0))
(= (string remote-port) (get comparison 1)))
(string/format "localname should match peername: msg=%j, buf=%j" msg buf)))
# Test on both server and client
(defn names-handler
[stream]
(defer (:close stream)
(check-matching-names stream)))
# prevent immediate close
(ev/read stream 1)
(def [host port] (net/localname stream))
(assert (= host "127.0.0.1") "localname host server")
(assert (= port 8000) "localname port server")))
# Test localname and peername
(repeat 20
(repeat 10
(with [s (net/server "127.0.0.1" "8000" names-handler)]
(defn test-names []
(repeat 10
(with [conn (net/connect "127.0.0.1" "8000")]
(check-matching-names conn)))
(repeat 20 (test-names)))
(def [host port] (net/peername conn))
(assert (= host "127.0.0.1") "peername host client ")
(assert (= port 8000) "peername port client")
# let server close
(ev/write conn " "))))
(gccollect))
# Create pipe

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2021 Calvin Rose & contributors
# Copyright (c) 2022 Calvin Rose & contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
@@ -80,5 +80,27 @@
"table rawget regression"
(table/new -1))
# Named arguments
(defn named-arguments
[&named bob sally joe]
(+ bob sally joe))
(assert (= 15 (named-arguments :bob 3 :sally 5 :joe 7)) "named arguments 1")
(defn named-opt-arguments
[&opt x &named a b c]
(+ x a b c))
(assert (= 10 (named-opt-arguments 1 :a 2 :b 3 :c 4)) "named arguments 2")
(let [b @""]
(defn dummy [a b c]
(+ a b c))
(trace dummy)
(defn errout [arg]
(buffer/push b arg))
(assert (= 6 (with-dyns [*err* errout] (dummy 1 2 3))) "trace to custom err function")
(assert (deep= @"trace (dummy 1 2 3)\n" b) "trace buffer correct"))
(end-suite)

55
test/suite0012.janet Normal file
View File

@@ -0,0 +1,55 @@
# Copyright (c) 2022 Calvin Rose & contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
(import ./helper :prefix "" :exit true)
(start-suite 12)
(var counter 0)
(def thunk (delay (++ counter)))
(assert (= (thunk) 1) "delay 1")
(assert (= counter 1) "delay 2")
(assert (= (thunk) 1) "delay 3")
(assert (= counter 1) "delay 4")
(def has-ffi (dyn 'ffi/native))
# FFI check
(compwhen has-ffi
(ffi/context))
(compwhen has-ffi
(ffi/defbind memcpy :ptr [dest :ptr src :ptr n :size]))
(compwhen has-ffi
(def buffer1 @"aaaa")
(def buffer2 @"bbbb")
(memcpy buffer1 buffer2 4)
(assert (= (string buffer1) "bbbb") "ffi 1 - memcpy"))
(compwhen has-ffi
(assert (= 8 (ffi/size [:int :char])) "size unpacked struct 1")
(assert (= 5 (ffi/size [:pack :int :char])) "size packed struct 1")
(assert (= 5 (ffi/size [:int :pack-all :char])) "size packed struct 2")
(assert (= 4 (ffi/align [:int :char])) "align 1")
(assert (= 1 (ffi/align [:pack :int :char])) "align 2")
(assert (= 1 (ffi/align [:int :char :pack-all])) "align 3")
(assert (= 26 (ffi/size [:char :pack :int @[:char 21]])) "array struct size"))
(end-suite)

43
test/suite0013.janet Normal file
View File

@@ -0,0 +1,43 @@
# Copyright (c) 2022 Calvin Rose & contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
(import ./helper :prefix "" :exit true)
(start-suite 13)
(assert (deep= (tabseq [i :in (range 3)] i (* 3 i))
@{0 0 1 3 2 6}))
(assert (deep= (tabseq [i :in (range 3)] i)
@{}))
(def- sym-prefix-peg
(peg/compile
~{:symchar (+ (range "\x80\xff" "AZ" "az" "09") (set "!$%&*+-./:<?=>@^_"))
:anchor (drop (cmt ($) ,|(= $ 0)))
:cap (* (+ (> -1 (not :symchar)) :anchor) (* ($) '(some :symchar)))
:recur (+ :cap (> -1 :recur))
:main (> -1 :recur)}))
(assert (deep= (peg/match sym-prefix-peg @"123" 3) @[0 "123"]) "peg lookback")
(assert (deep= (peg/match sym-prefix-peg @"1234" 4) @[0 "1234"]) "peg lookback 2")
(assert (deep= (peg/replace-all '(* (<- 1) 1 (backmatch)) "xxx" "aba cdc efa") @"xxx xxx efa") "peg replace-all 1")
(end-suite)

20
test/suite0014.janet Normal file
View File

@@ -0,0 +1,20 @@
(import ./helper :prefix "" :exit true)
(start-suite 14)
(assert (deep=
(peg/match '(not (* (constant 7) "a")) "hello")
@[]) "peg not")
(assert (deep=
(peg/match '(if-not (* (constant 7) "a") "hello") "hello")
@[]) "peg if-not")
(assert (deep=
(peg/match '(if-not (drop (* (constant 7) "a")) "hello") "hello")
@[]) "peg if-not drop")
(assert (deep=
(peg/match '(if (not (* (constant 7) "a")) "hello") "hello")
@[]) "peg if not")
(end-suite)

View File

@@ -159,6 +159,7 @@
<Condition>ALLUSERS=1</Condition>
<Environment Id="PATH_PERMACHINE" Name="PATH" Value="[BinDir]" Action="set" Permanent="no" System="yes" Part="last"/>
<Environment Id="JANET_BINPATH_PERMACHINE" Name="JANET_BINPATH" Value="[BinDir]" Action="set" Permanent="no" System="yes"/>
<Environment Id="JANET_MANPATH_PERMACHINE" Name="JANET_MANPATH" Value="[DocsDir]" Action="set" Permanent="no" System="yes"/>
<Environment Id="JANET_PATH_PERMACHINE" Name="JANET_PATH" Value="[LibraryDir]" Action="set" Permanent="no" System="yes" />
<Environment Id="JANET_HEADERPATH_PERMACHINE" Name="JANET_HEADERPATH" Value="[CDir]" Action="set" Permanent="no" System="yes"/>
<Environment Id="JANET_LIBPATH_PERMACHINE" Name="JANET_LIBPATH" Value="[CDir]" Action="set" Permanent="no" System="yes"/>
@@ -167,6 +168,7 @@
<Condition>NOT ALLUSERS=1</Condition>
<Environment Id="PATH_PERUSER" Name="PATH" Value="[BinDir]" Action="set" Permanent="no" System="no" Part="last"/>
<Environment Id="JANET_BINPATH_PERUSER" Name="JANET_BINPATH" Value="[BinDir]" Action="set" Permanent="no" System="no"/>
<Environment Id="JANET_MANPATH_PERUSER" Name="JANET_MANPATH" Value="[DocsDir]" Action="set" Permanent="no" System="no"/>
<Environment Id="JANET_PATH_PERUSER" Name="JANET_PATH" Value="[LibraryDir]" Action="set" Permanent="no" System="no" />
<Environment Id="JANET_HEADERPATH_PERUSER" Name="JANET_HEADERPATH" Value="[CDir]" Action="set" Permanent="no" System="no"/>
<Environment Id="JANET_LIBPATH_PERUSER" Name="JANET_LIBPATH" Value="[CDir]" Action="set" Permanent="no" System="no"/>

View File

@@ -17,6 +17,7 @@
"quote"
"quasiquote"
"unquote"
"upscope"
"splice"]
(all-bindings)))
(def allsyms (dyn :allsyms))