1
0
mirror of https://github.com/janet-lang/janet synced 2025-11-08 19:43:41 +00:00

Compare commits

..

165 Commits

Author SHA1 Message Date
Calvin Rose
5b9eda5e87 Add root-env
This makes images smaller without needing to make sure
that no references to the root environment occur in the final
image.
2019-12-12 17:25:04 -06:00
Calvin Rose
7c2ae45809 Fix some merge issues.
Make everything compile, and test-install pass.
2019-12-12 17:14:36 -06:00
Calvin Rose
36b2f27873 Merge branch 'master' into threads-3 2019-12-12 17:07:03 -06:00
Calvin Rose
b8e02afd1a Improve error messages in os.c and jpm
In os/* functions, show failed path name. In jpm, indicate
a permission issue if we can't stat the file.
2019-12-12 03:20:20 -06:00
Calvin Rose
0fc36aa5d0 Signal to pending threads more often. 2019-12-12 02:19:56 -06:00
Calvin Rose
38f7e256d0 Port threads code to Windows API
Can run demo in examples/threads.janet
2019-12-10 20:32:41 -05:00
Calvin Rose
4187c972a3 Switch to multiple buffers per mailbox.
Needs less copying.
2019-12-10 13:26:00 -06:00
Calvin Rose
2d5af32660 Refine typedefs. 2019-12-09 20:12:10 -06:00
Calvin Rose
e592b24333 Added some type aliases to janet.h
This should make it clearer if a pointer is really just a
plain pointer, or a pointer with a header.
2019-12-09 20:05:53 -06:00
Calvin Rose
8700a407ce Update janet_getmethod to better match new get api. 2019-12-09 18:45:05 -06:00
Calvin Rose
8ecf359bbe Merge pull request #226 from andrewchambers/abstractget
Abstract type getters can indicate key absence.
2019-12-09 18:39:40 -06:00
Calvin Rose
eb1988a5ae Update CHANGELOG.md 2019-12-09 18:26:58 -06:00
Calvin Rose
5b6dffe93d Version bump. 2019-12-09 18:04:38 -06:00
Calvin Rose
1a6eb52f11 Add protect macro.
A more functional version of try catch.
2019-12-09 17:32:02 -06:00
Andrew Chambers
57ccfb692c Abstract type getters can indicate key absence.
This change to the c api allows abstract types to indicate
to the runtime if a key was absent, or if it meant to return nil.
2019-12-09 16:50:33 +13:00
Calvin Rose
eb1c21b0da Fix some example issue and warnings under -Os. 2019-12-08 12:40:05 -06:00
Calvin Rose
66d82c4513 Add mailbox capacity for back pressure.
(thread/send thread msg &opt timeout) can now timeout. Also
changed thread/self to thread/current for better consistency with
fibers, and all blocking operations will by default timeout after 1
second. I think its bad to make things block forerver by default.
2019-12-08 12:30:30 -06:00
Calvin Rose
c9c4424261 Add thread/self. 2019-12-07 17:54:08 -06:00
Calvin Rose
131733549d Get mailbox API working. 2019-12-07 16:51:00 -06:00
Calvin Rose
ee646dadf2 Merge branch 'master' into threads-3 2019-12-07 12:14:44 -06:00
Calvin Rose
73f5314141 Work on moving to mailbox abstraction.
Should be more efficient in the common case.
2019-12-07 12:14:16 -06:00
Calvin Rose
4c5734c2ee Update CHANGELOG.md 2019-12-07 10:35:40 -06:00
Calvin Rose
546669082f New unmarshal proposal.
Gives more control over unmarshalling
abstract types. This should also
make it possible/easy to write abstract types that cannot
cause unmarshal to segfault.
2019-12-06 22:12:18 -06:00
Calvin Rose
4a0ee5df7d Address #215
Also update docs for module/expand-path.
2019-12-06 19:54:11 -06:00
Calvin Rose
4de6c2ad61 Address #211
Scripts run from the command line will automatically
call a main function if it exists.
2019-12-06 19:14:12 -06:00
Calvin Rose
1fa7e73c58 Address #218
Quote output to :lfags in meta data.
2019-12-06 18:45:29 -06:00
Calvin Rose
0e690b4fa0 Add timeout to thread/receive.
If provided, throws an error if no message is received before
timeout. Perhaps should return nil?.
2019-12-06 09:21:36 -06:00
Calvin Rose
c804ae9f7c Update threads.c to avoid a deadlock. 2019-12-06 01:46:23 -06:00
Calvin Rose
dbcceefc20 Fix bad merge. 2019-12-04 22:41:30 -06:00
Calvin Rose
1a4035b02c Merge branch 'master' into threads-3 2019-12-04 22:39:30 -06:00
Calvin Rose
e908029392 Work on thread/receive doubling as select. 2019-12-04 22:31:01 -06:00
Calvin Rose
fd4220f254 Keep single global pthread_cond_t per thread.
This will allow thread/select to be implemented.
Also add thread/close and close method to threads.
2019-12-04 21:44:53 -06:00
Calvin Rose
de6c3d6d70 Simplify structure JanetThread and JanetChannel.
Remove JanetThreadShared.
2019-12-04 21:04:43 -06:00
Calvin Rose
77cb823719 Update CHANGELOG.md 2019-12-04 20:02:15 -06:00
Calvin Rose
49954c7a30 Remove top-level unquote for comptime macro
True top level unquote currently requires basically double compilation
as it currently stands. Also, implementing such double compilation
looses all source mapping information. This is a compromise
implementation that makes it clear that this works differently than
a true top-level unquote.
2019-12-04 19:53:13 -06:00
Calvin Rose
11a7a7069a Update CHANGELOG.md 2019-12-04 18:46:36 -06:00
Calvin Rose
2487162ccf Add top level unquote and macro envs.
This improves macros that eval their arguments and
makes them easier to write.
2019-12-04 18:39:13 -06:00
Calvin Rose
8ca10f37bd Update CHANGELOG.md 2019-12-04 16:51:34 -06:00
Calvin Rose
4199c42fe2 Add support for nested quasiquotation.
This brings Janet more in line with Scheme,
Common Lisp, and Clojure.
2019-12-04 16:40:53 -06:00
Calvin Rose
f39cf702db Address #212 - don't include janet args in script args. 2019-12-04 08:30:36 -06:00
Calvin Rose
db9e431bf7 Address #213 - disallow non-indexed ds for loop range. 2019-12-04 08:18:54 -06:00
Calvin Rose
328454729e Add nan? 2019-12-03 21:24:22 -06:00
Calvin Rose
73a4c395d2 Address #190
We don't ever invoke ld directly, so ignore --linker on non-windows.
For --compiler and --archiver, default to $CC and $AR. These are
overshadowed by CLI flags or settings in project.janet.
2019-12-03 21:00:59 -06:00
Calvin Rose
70328437f1 Add math/rng-buffer.
Allow math/seedrandom to use buffer as seed.
2019-12-03 20:33:21 -06:00
Calvin Rose
600bed9f6d Merge pull request #209 from andrewchambers/cryptorand2
Add os/cryptorand.
2019-12-03 19:12:32 -06:00
Calvin Rose
55eca44c54 Merge pull request #210 from andrewchambers/unhandled_buffer_get
Handle missing get case.
2019-12-03 19:07:44 -06:00
Andrew Chambers
0ac5b243c7 Add os/cryptorand. 2019-12-04 14:02:37 +13:00
Andrew Chambers
9911c90b1d Handle missing get case. 2019-12-04 13:58:21 +13:00
Calvin Rose
a1f35e21c7 Merge branch 'master' into threads-3 2019-12-03 18:11:32 -06:00
Calvin Rose
9ccdab0bc7 Merge pull request #208 from andrewchambers/explain_why
Explain the logic behind negative slice indices.
2019-12-03 10:42:46 -05:00
Andrew Chambers
a20e956f6d Explain the logic behind negative slice indices. 2019-12-03 22:05:43 +13:00
Calvin Rose
59668133a2 Merge pull request #206 from andrewchambers/unkown
Fix typo.
2019-12-03 03:34:46 -05:00
Andrew Chambers
73db8584e0 Fix typo. 2019-12-03 21:14:00 +13:00
Calvin Rose
cecc7e6b9d Rename 'get' opcode to 'in', add new 'get' opcode.
This makes the names of the opcodes match their implied functionality.
We also rename the C functions to match the opcodes and source level
functionality.
2019-12-02 21:26:28 -06:00
Calvin Rose
3a14aad615 Address issue #205. 2019-12-02 18:34:08 -06:00
Calvin Rose
8368e55151 Merge branch 'master' into threads-3 2019-12-02 17:49:39 -06:00
Calvin Rose
ac85fca8a1 Fix warnings for appveyor. 2019-12-02 09:07:49 -06:00
Calvin Rose
e5fbe5c557 Change printf to add trailing newlines.
Also add prinf and eprinf for old behavior. This
is consistent with the naming of print and prin.
2019-12-02 04:45:03 -06:00
Calvin Rose
474bcd50a1 Add methods to threads. 2019-12-02 04:39:13 -06:00
Calvin Rose
70c8b6838d Use make-image-dict and load-image-dict in thread/new
Rather than messing with janet_core_dictionary, we
instead cache the core enevironment, and pull out the
needed tables from there. This is more flexible, more correct, and
also exposes janet_resolve_core, which can be easily used from the C
API.
2019-12-02 04:15:22 -06:00
Calvin Rose
212479188a Have separate encode and decode dicts for threads
This is more correct and mirrors the way marshal -> unmarshal works.
2019-12-01 21:53:39 -06:00
Calvin Rose
5b1e59b535 Merge branch 'master' of github.com:janet-lang/janet into threads-3 2019-12-01 21:26:22 -06:00
Calvin Rose
779d788efa Merge pull request #204 from andrewchambers/get_permissive
New capi janet_get_permissive
2019-12-01 22:06:44 -05:00
Andrew Chambers
6233d804c8 New capi janet_get_permissive
The janet_get_permissive function implements the core semantics
of the 'get' function. The original janet_get implements the semantics of
the 'in' function and also the OP_GET opcode. This slight oddity is
to avoid a backwards incompatible change.
2019-12-02 15:49:51 +13:00
Calvin Rose
8f31a53276 Add thread example.
Also remove reference to pthread_t in the JanetThread structure.
2019-12-01 20:47:22 -06:00
Calvin Rose
6a763aac95 Work on threads.
Add send and receive.
2019-12-01 20:28:12 -06:00
Calvin Rose
5cd6580c2d Merge branch 'threads-3' of github.com:janet-lang/janet into threads-3 2019-12-01 20:25:57 -06:00
Calvin Rose
81a2af700a Merge pull request #201 from andrewchambers/in_docs
Update documentation for in and get builtins.
2019-12-01 20:49:47 -05:00
Andrew Chambers
8a58be81ba Update documentation for in and get builtins.
Try to clarify documentation and teach users the correct
way to read the 'in' so it is less likely to be confused
with python's usage of the keyword.
2019-12-02 12:35:54 +13:00
Calvin Rose
fc53445d08 Merge pull request #198 from andrewchambers/intprint
Integers convert to plain number strings.
2019-12-01 13:00:09 -05:00
Calvin Rose
db261aabf4 Fix bad integer printing range. 2019-12-01 09:46:20 -05:00
Calvin Rose
36ef1c4749 Print proper integers as integers. 2019-12-01 09:40:34 -05:00
Andrew Chambers
5ae520a2c9 Integers convert to plain number strings.
A user can use (type n) to find the true type, the old behavior did not
seem useful for most uses of the string function.
2019-12-01 23:10:52 +13:00
Calvin Rose
8e31bda8f6 Fix issue #189 2019-11-30 21:54:23 -05:00
Calvin Rose
474aed8cfe Merge branch 'master' of github.com:janet-lang/janet 2019-11-30 21:34:38 -05:00
Calvin Rose
0509376aea Merge pull request #193 from andrewchambers/truthy
Add truthy? to core.
2019-11-30 21:32:14 -05:00
Calvin Rose
570f04ca05 Fix typo. 2019-11-30 21:27:36 -05:00
Andrew Chambers
ded08b6e1e Add truthy? to core. 2019-12-01 14:34:41 +13:00
Calvin Rose
f3c0d9115f Fix calling jpm quickbin binary with no arguments. 2019-11-30 15:11:34 -05:00
Calvin Rose
bf609445c1 Merge pull request #186 from quexxon/fix-array-ensure-documentation
Add missing documentation for array/ensure's growth parameter
2019-11-29 22:39:04 -05:00
Calvin Rose
13ef2bd905 Merge pull request #185 from andrewchambers/afl
Add afl fuzzing helpers.
2019-11-29 22:36:47 -05:00
Calvin Rose
4e4cdb6356 Run formatter. 2019-11-28 23:26:11 -05:00
Calvin Rose
688d297a18 Address Issue #184.
Fix strtod.c with better range checking to prevent DOS.
2019-11-28 23:23:37 -05:00
Will Clardy
9e1c3e0f41 Add missing documentation for array/ensure's growth parameter 2019-11-28 23:16:32 -05:00
Andrew Chambers
4acc63e325 Add afl fuzzing helpers. 2019-11-29 16:43:14 +13:00
Calvin Rose
967a8b5a70 Merge pull request #183 from andrewchambers/environ
Add os/environ.
2019-11-28 21:33:43 -05:00
Calvin Rose
92b7d91697 Merge pull request #182 from andrewchambers/scratch_finalizer
Add an optional finalizer to scratch resources.
2019-11-28 21:07:42 -05:00
Andrew Chambers
07db4c530e Add os/environ. 2019-11-28 19:00:52 +13:00
Andrew Chambers
a3fb2d6e0a Add an optional finalizer to scratch resources.
A finalizer can be attached to scratch allocations efficiently at any point in
it's lifecycle via janet_sfinalizer. Care was taken to keep allocations aligned
with  platform alignment requirements.

A big drawbacks to this approach is the waste of up to 16 bytes per scratch
allocation in the case the scratch memory does not require a finalizer.
2019-11-28 17:32:12 +13:00
Calvin Rose
5b9e37e2cc Merge pull request #181 from andrewchambers/fileno
Add missing fileno method to file, sort method list.
2019-11-27 21:06:30 -05:00
Andrew Chambers
88f28773da Add missing fileno method to file, sort method list. 2019-11-28 14:47:16 +13:00
Calvin Rose
66e6979812 Add thread flags to standalone executables. 2019-11-27 15:06:31 -06:00
Calvin Rose
8a91c52fa2 Change pthreads linking with jpm, make, and meson.
Try to add pthread deps when compiling programs with jpm.
2019-11-27 14:52:20 -06:00
Calvin Rose
e542ba7e4d Fix amalg build. 2019-11-27 12:43:45 -06:00
Calvin Rose
bca0392738 First work on threading.
Posix only, needs to be disabled on windows. Also
the Makefile needs to be configurable, and meson.build
needs to take pthreads into account.
2019-11-26 23:13:53 -06:00
Calvin Rose
74d51ab08b Address issue #180 - string/check-set
Fix the function and add test to further clarify that
implementation is correct. Also fix empty string case.
2019-11-25 20:33:16 -06:00
Calvin Rose
6bc400eb8c Update CHANGELOG.md 2019-11-25 20:11:10 -06:00
Calvin Rose
7df0ec6aed Fix up debug/step and janet_step.
Also allow debugging on all signals, including errors.
This is gated behind (setdyn :debug true) in the repl.
2019-11-25 20:00:13 -06:00
Calvin Rose
a0a980e0ef Update sample debugger.
Add .break and .step.
2019-11-25 18:21:53 -06:00
Calvin Rose
6988fd3cab Add debug/step to single step a fiber.
Very useful for implementing debuggers.
2019-11-25 18:14:34 -06:00
Calvin Rose
c3273e8751 Merge branch 'master' of github.com:janet-lang/janet 2019-11-24 17:54:14 -06:00
Calvin Rose
d37c43716a Lots of work on improving debugging.
doc macro can take no arguments and print out
all bindings. Fix an issues with the vm skipping
over a breakpoint in some situations.

Add examples/debugger.janet for proof of concept
debugger.
2019-11-24 17:45:53 -06:00
Calvin Rose
1bf751367b Merge pull request #177 from andrewchambers/parse_review
Minor fixes for parser.
2019-11-23 14:23:57 -05:00
Andrew Chambers
976dfc7195 Minor fixes for parser
Check length before dereferencing buffer in tokenchar.
Check keywords are valid utf-8.
Fix minor typos.
2019-11-24 08:19:04 +13:00
Calvin Rose
8372d1e499 uint32_t -> uint8_t 2019-11-21 23:31:35 -06:00
Calvin Rose
e65716f6ee Add janet_rng_longseed to janet.h 2019-11-21 23:26:31 -06:00
Calvin Rose
4b24d77b2c Switch back to well tested RNG. 2019-11-21 23:22:21 -06:00
Calvin Rose
02fc4ae27b Allow seeding RNG with a byte sequence. 2019-11-21 22:53:39 -06:00
Calvin Rose
624f5f428e Add a number of math functions.
Most of these functions are wrappers around math.h.
2019-11-17 10:54:44 -06:00
Calvin Rose
5171dfd2a8 Fix docstring. 2019-11-16 20:43:21 -06:00
Calvin Rose
8ff5e49d1f Merge pull request #176 from staab/repl-delete
Add support for delete key in repl
2019-11-16 21:42:38 -05:00
Jon Staab
134163708a Fix formatting 2019-11-16 16:07:15 -08:00
Jon Staab
40e6616df0 Add support for delete key in repl 2019-11-16 16:01:52 -08:00
Calvin Rose
bcd2089f71 Version 1.5.1 2019-11-16 17:17:13 -06:00
Calvin Rose
7553b277db Fix return value of update. 2019-11-16 15:50:21 -06:00
Calvin Rose
d71cf093bb Add /i switch to xcopy in jpm 2019-11-12 02:51:37 -05:00
Calvin Rose
86d21816b6 Fix jpm mendoza install on windows. 2019-11-12 02:45:20 -05:00
Calvin Rose
c9521e093e Fix windows issue with (file/read file :all)
When file was created with file/popen, the current optimization
of using fseek on windows fails due to windows not properly returning
and error code and just returning 0. Windows :(.
2019-11-11 20:05:00 -05:00
Calvin Rose
16f6261b44 Improve randomness of numbers from new rng.
First few numbers are very biased.
2019-11-10 17:44:59 -06:00
Calvin Rose
6b76ac3d18 Fix bug when appending buffer to self.
janet_to_string_b had a bug when printing buffers.
2019-11-10 14:57:09 -06:00
Calvin Rose
5681e02e0f Update deployment and fix changelog. 2019-11-10 11:30:31 -06:00
Calvin Rose
41a22f258e Fix appveyor.yml to build windows installer. 2019-11-10 11:12:28 -06:00
Calvin Rose
0d2844b7c9 Update to 1.5.0 2019-11-10 10:57:18 -06:00
Calvin Rose
719f7ba0c4 Default to UTC for date. 2019-11-09 16:57:21 -06:00
Calvin Rose
44ed2c6b47 Tag artifacts with platform name. 2019-11-09 16:20:52 -05:00
Calvin Rose
c9292ef648 Use /MD on windows.
Just makes things easier. Assume machines have msvcrt.dll
on them. If not, we can add msvcrt.dll to the dist folder and add to
installer.
2019-11-09 16:05:07 -05:00
Calvin Rose
135abff100 Try again with vcvarsall.bat 2019-11-09 13:29:01 -05:00
Calvin Rose
7252db1e63 Try 32 bit and 64 bit builds for windows. 2019-11-09 13:25:57 -05:00
Calvin Rose
05e3fd3cc6 Fix build_win. 2019-11-09 13:22:07 -05:00
Calvin Rose
6f1b03b67e Fix build_win test-install.
On failure, cd back to original directory.
2019-11-09 11:28:40 -05:00
Calvin Rose
dca247f01d Fix MSVC build warnings. 2019-11-09 10:12:40 -06:00
Calvin Rose
63e7ca4623 Fix warning on travis CI with Clang. 2019-11-09 10:10:07 -06:00
Calvin Rose
75d21d9f45 Update CHANGELOG.md 2019-11-09 10:05:29 -06:00
Calvin Rose
8911daaf6c Add --test flag to jpm.
Also test some select packages when testing installation.
This is used in CI to make sure that versions of Janet work with
the most libraries.
2019-11-09 10:03:56 -06:00
Calvin Rose
1f55d40a10 Fix janet_opt* api.
Inverted conditional made behavior incorrect. These
were not used in the core library, so were not tested.
2019-11-09 09:39:14 -06:00
Calvin Rose
6591e7636d Copy janetconf to build for amalg target.
This makes testing the amalg easier.
2019-11-08 20:36:16 -06:00
Calvin Rose
c12eaa926a Make sure $prefix/lib/janet is created
After we removed cook.janet, jpm didn't work well out of the box
with a meson install.
2019-11-08 18:43:53 -06:00
Calvin Rose
0e464ded3d Fix meson.build typo. 2019-11-08 18:26:43 -06:00
Calvin Rose
aee1687215 Add RNG functionality to the math/ module.
The new RNG wraps up state for random number generation, so
one can have many rngs and even marshal and unmarshal them.
Adds math/rng, math/rng-uniform, and math/rng-int.

Also introduce `in` and change semantics for
indexing out of range. This commit enforces stricter
invariants on keys when indexing via a function call
on the data structure, or the new `in` function.

The `get` function is now more lax about keys, and will
not throw an error when a bad key is used for a data structure, instead
returning the default value.
2019-11-08 17:40:04 -06:00
Calvin Rose
58e3e63a89 Add jpm to release bundle. 2019-11-08 11:00:12 -06:00
Calvin Rose
9b605b27bd Address #174 - fix string/trim 2019-11-08 08:47:37 -06:00
Calvin Rose
c5010dffb4 Print error message on bad CLI usage.
This was a small regression when bundling cli-main
into boot.janet.
2019-11-05 12:51:15 -06:00
Calvin Rose
026f26f05f Improve error message in slice functions.
Check the first argument before trying to do range
checks.
2019-11-05 09:41:30 -06:00
Calvin Rose
cf2d3861d6 Make slice a c function.
This will allow future integration into the compiler
for more general destructuring.
2019-11-05 09:29:32 -06:00
Calvin Rose
6ceaf9d28d Add with-vars
This helps for temporarily setting vars in a safe
manner that is guaranteed not to leave vars in a bad state
(assuming that a fiber does not emit debug or use signal and
 is never resumed).
2019-10-31 21:58:43 -05:00
Calvin Rose
25a9804d91 Fix build_win test-install 2019-10-29 20:40:09 -05:00
Calvin Rose
cf19cd5292 Add the quickbin command to jpm.
This is useful for making one off executable scripts
without needing to set up a project.janet file.
2019-10-29 20:33:18 -05:00
Calvin Rose
03824dd9f7 Update CHANGELOG.md 2019-10-29 19:41:48 -05:00
Calvin Rose
280dca3998 Add shell.c to the amalgamation.
This allows easy builds of the full interpreter with no
build system.

  1. Get janet.c, janet.h, janetconf.h, and shell.c in a directory. Edit
     janetconf.h as desired.
  2. gcc shell.c janet.c -lm -ldl -O2 -o janet (on GNU-Linux for example)
  3. ./janet -h (Yay!)
2019-10-29 19:18:44 -05:00
Calvin Rose
46e09e4c71 Fix tools/removecr.janet 2019-10-29 18:56:32 -05:00
Calvin Rose
427b2638e0 Fix startup environment. 2019-10-29 18:47:54 -05:00
Calvin Rose
2541806dc1 Fix suite7 failing when run with no docstrings. 2019-10-29 18:28:41 -05:00
Calvin Rose
0d16b9e1a1 Move init.janet into cli-main in boot.janet
This makes it easier to get the CLI functionality when
embedding Janet, although the main reason is the init script
is now pre-compiled to bytecode when generating the boot image.
2019-10-29 18:16:32 -05:00
Calvin Rose
b2263ed5b5 Update CHANGELOG.md 2019-10-29 17:52:41 -05:00
Calvin Rose
45c2819068 Improve flychecking.
Flychecking will now work correctly with arity checking, and
will better handle imports. Well structured modules should interact
cleanly with the flychecker in a mostly safe manner, but maliciously
crafted modules can execute arbitrary code. As such, the flychecker is
not a good way to validate completely untrusted modules.

We also extend run-context with an :evaluator option to replace
:compile-only. This is more flexible and allows users to create their
own flychecker like functionality.
2019-10-27 16:15:41 -05:00
Calvin Rose
d28925fdab Relax type checking when fuction position is nil
This lets the flychecker work as expected.
2019-10-24 15:17:19 -05:00
Calvin Rose
9097e36ea0 [] should evaluate to ()
This is consistent with most bracket tuples.
2019-10-20 14:06:28 -05:00
Calvin Rose
99ef4c7510 Fix an issue with the removecr script 2019-10-19 19:11:13 -04:00
Calvin Rose
b9e05d06fe Update amalg step. 2019-10-19 18:00:29 -05:00
Calvin Rose
423b6db855 Fix memory leak with some string/ functions.
kmp_init leaked memory when called with an empty string.
2019-10-19 15:14:19 -05:00
Calvin Rose
bb54b940c0 Don't call fwrite with size = 0 2019-10-19 10:51:11 -05:00
Calvin Rose
4149df1fca Update CHANGELOG.md 2019-10-19 10:35:56 -05:00
Calvin Rose
8dd8af742a Add eprintf and make printf a C function.
This allows some more optimizations when printing to
buffers or when output is disabled. It also makes printf
more consistent with print and prin (Same with eprintf).
2019-10-19 10:30:29 -05:00
Calvin Rose
d47804d222 Add prin, eprint, and eprin functions.
The print family of functions now writes output
to an optional buffer instead of a file bound to :out.
This means output can be more easily captured an redirected.
2019-10-19 09:44:27 -05:00
Calvin Rose
8dd322c0be Fix webclient. 2019-10-14 20:55:04 -05:00
67 changed files with 3302 additions and 927 deletions

3
.gitignore vendored
View File

@@ -13,6 +13,9 @@ janet
janet-*.tar.gz
dist
# Kakoune (fzf via fd)
.fdignore
# VSCode
.vscode

View File

@@ -1,6 +1,70 @@
# Changelog
All notable changes to this project will be documented in this file.
### Unreleased
- Allow seeding RNGs with any sequence of bytes. This provides
a wider key space for the RNG. Exposed in C as `janet_rng_longseed`.
- Fix issue in `resume` and similar functions that could cause breakpoints to be skipped.
- Add a number of new math functions.
- Improve debugger experience and capabilities. See examples/debugger.janet
for what an interactive debugger could look like.
- Add `debug/step` (janet\_step in the C API) for single stepping Janet bytecode.
- The built in repl now can enter the debugger on any signal (errors, yields,
user signals, and debug signals). To enable this, type (setdyn :debug true)
in the repl environment.
- When exiting the debugger, the fiber being debugged is resumed with the exit value
of the debug session (the value returned by `(quit return-value)`, or nil if user typed Ctrl-D).
- `(quit)` can take an optional argument that is the return value. If a module
contains `(quit some-value)`, the value of that module returned to `(require "somemod")`
is the return value. This lets module writers completely customize a module without writing
a loader.
- Add nested quasiquotation.
- Add `os/cryptorand`
- Add `prinf` and `eprinf` to be have like `printf` and `eprintf`. The latter two functions
now including a trailing newline, like the other print functions.
- Add nan?
- Add `janet_in` to C API.
- Add `truthy?`
- Add `os/environ`
- Use `(doc)` with no arguments to see available bindings and dynamic bindings.
- `jpm` will use `CC` and `AR` environment variables when compiling programs.
- Add `comptime` macro for compile time evaluation.
- Run `main` functions in scripts if they exist, just like jpm standalone binaries.
- Add `protect` macro.
- Add `root-env` to get the root environment table.
- Change marshalling protocol with regard to abstract types.
- Numerous small bug fixes and usability improvements.
### 1.5.1 - 2019-11-16
- Fix bug when printing buffer to self in some edge cases.
- Fix bug with `jpm` on windows.
- Fix `update` return value.
## 1.5.0 - 2019-11-10
- `os/date` now defaults to UTC.
- Add `--test` flag to jpm to test libraries on installation.
- Add `math/rng`, `math/rng-int`, and `math/rng-uniform`.
- Add `in` function to index in a stricter manner. Conversely, `get` will
now not throw errors on bad keys.
- Indexed types and byte sequences will now error when indexed out of range or
with bad keys.
- Add rng functions to Janet. This also replaces the RNG behind `math/random`
and `math/seedrandom` with a consistent, platform independent RNG.
- Add `with-vars` macro.
- Add the `quickbin` command to jpm.
- Create shell.c when making the amalgamated source. This can be compiled with
janet.c to make the janet interpreter.
- Add `cli-main` function to the core, which invokes Janet's CLI interface.
This basically moves what was init.janet into boot.janet.
- Improve flychecking, and fix flychecking bugs introduced in 1.4.0.
- Add `prin`, `eprint`, `eprintf` and `eprin` functions. The
functions prefix with e print to `(dyn :err stderr)`
- Print family of functions can now also print to buffers
(before, they could only print to files.) Output can also
be completely disabled with `(setdyn :out false)`.
- `printf` is now a c function for optimizations in the case
of printing to buffers.
## 1.4.0 - 2019-10-14
- Add `quit` function to exit from a repl, but not always exit the entire
application.

View File

@@ -28,7 +28,7 @@ INCLUDEDIR?=$(PREFIX)/include
BINDIR?=$(PREFIX)/bin
LIBDIR?=$(PREFIX)/lib
JANET_BUILD?="\"$(shell git log --pretty=format:'%h' -n 1)\""
CLIBS=-lm
CLIBS=-lm -lpthread
JANET_TARGET=build/janet
JANET_LIBRARY=build/libjanet.so
JANET_STATIC_LIBRARY=build/libjanet.a
@@ -106,6 +106,7 @@ JANET_CORE_SOURCES=src/core/abstract.c \
src/core/struct.c \
src/core/symcache.c \
src/core/table.c \
src/core/thread.c \
src/core/tuple.c \
src/core/typedarray.c \
src/core/util.c \
@@ -147,7 +148,7 @@ build/core_image.c: build/janet_boot
##########################################################
JANET_CORE_OBJECTS=$(patsubst src/%.c,build/%.o,$(JANET_CORE_SOURCES)) build/core_image.o
JANET_MAINCLIENT_OBJECTS=$(patsubst src/%.c,build/%.o,$(JANET_MAINCLIENT_SOURCES)) build/init.gen.o
JANET_MAINCLIENT_OBJECTS=$(patsubst src/%.c,build/%.o,$(JANET_MAINCLIENT_SOURCES))
# Compile the core image generated by the bootstrap build
build/core_image.o: build/core_image.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
@@ -204,8 +205,6 @@ emscripten: $(JANET_EMTARGET)
build/xxd: tools/xxd.c
$(CC) $< -o $@
build/init.gen.c: src/mainclient/init.janet build/xxd
build/xxd $< $@ janet_gen_init
build/webinit.gen.c: src/webclient/webinit.janet build/xxd
build/xxd $< $@ janet_gen_webinit
build/boot.gen.c: src/boot/boot.janet build/xxd
@@ -215,15 +214,22 @@ build/boot.gen.c: src/boot/boot.janet build/xxd
##### Amalgamation #####
########################
amalg: build/janet.c build/janet.h build/core_image.c
amalg: build/shell.c build/janet.c build/janet.h build/core_image.c build/janetconf.h
AMALG_SOURCE=$(JANET_LOCAL_HEADERS) $(JANET_CORE_SOURCES) build/core_image.c
build/janet.c: $(AMALG_SOURCE) tools/amalg.janet $(JANET_TARGET)
$(JANET_TARGET) tools/amalg.janet $(AMALG_SOURCE) > $@
AMALG_SHELL_SOURCE=src/mainclient/line.h src/mainclient/line.c src/mainclient/main.c
build/shell.c: $(JANET_TARGET) tools/amalg.janet $(AMALG_SHELL_SOURCE)
$(JANET_TARGET) tools/amalg.janet $(AMALG_SHELL_SOURCE) > $@
build/janet.h: src/include/janet.h
cp $< $@
build/janetconf.h: src/conf/janetconf.h
cp $< $@
###################
##### Testing #####
###################
@@ -243,9 +249,13 @@ valgrind: $(JANET_TARGET)
test: $(JANET_TARGET) $(TEST_PROGRAMS)
for f in test/suite*.janet; do ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
./$(JANET_TARGET) -k auxbin/jpm
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
$(VALGRIND_COMMAND) ./$(JANET_TARGET) -k auxbin/jpm
callgrind: $(JANET_TARGET)
for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done
@@ -259,7 +269,7 @@ dist: build/janet-dist.tar.gz
build/janet-%.tar.gz: $(JANET_TARGET) \
src/include/janet.h src/conf/janetconf.h \
jpm.1 janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
build/doc.html README.md build/janet.c
build/doc.html README.md build/janet.c build/shell.c auxbin/jpm
$(eval JANET_DIST_DIR = "janet-$(shell basename $*)")
mkdir -p build/$(JANET_DIST_DIR)
cp -r $^ build/$(JANET_DIST_DIR)/
@@ -344,7 +354,12 @@ test-install:
&& jpm --verbose build \
&& jpm --verbose test \
&& build/testexec \
&& jpm --verbose --modpath=. install https://github.com/janet-lang/json.git
&& jpm --verbose quickbin testexec.janet build/testexec2 \
&& build/testexec2 \
&& jpm --verbose --testdeps --modpath=. install https://github.com/janet-lang/json.git
cd test/install && jpm --verbose --test --modpath=. install https://github.com/janet-lang/jhydro.git
cd test/install && jpm --verbose --test --modpath=. install https://github.com/janet-lang/path.git
cd test/install && jpm --verbose --test --modpath=. install https://github.com/janet-lang/argparse.git
build/embed_janet.o: build/janet.c $(JANET_HEADERS)
$(CC) $(CFLAGS) -c $< -o $@

View File

@@ -7,6 +7,7 @@ configuration:
- Debug
platform:
- x64
- x86
environment:
matrix:
- arch: Win64
@@ -15,7 +16,7 @@ matrix:
# skip unsupported combinations
init:
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat"
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %platform%
install:
- set JANET_BUILD=%appveyor_repo_commit:~0,7%
@@ -26,10 +27,10 @@ install:
- build_win all
- refreshenv
# We need to reload vcvars after refreshing
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat"
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %platform%
- build_win test-install
- set janet_outname=%appveyor_repo_tag_name%
- if "%janet_outname%"=="" set janet_outname=v1.4.0
- if "%janet_outname%"=="" set janet_outname=v1.6.0
build: off
artifacts:
@@ -42,11 +43,14 @@ artifacts:
- name: janetconf.h
path: dist\janetconf.h
type: File
- name: "janet-$(janet_outname)-windows"
- name: shell.c
path: dist\shell.c
type: File
- name: "janet-$(janet_outname)-windows-%platform%"
path: dist
type: Zip
- path: "janet-$(janet_outname)-windows-installer.exe"
name: "janet-$(janet_outname)-windows-installer.exe"
name: "janet-$(janet_outname)-windows-%platform%-installer.exe"
type: File
deploy:
@@ -54,7 +58,7 @@ deploy:
provider: GitHub
auth_token:
secure: lwEXy09qhj2jSH9s1C/KvCkAUqJSma8phFR+0kbsfUc3rVxpNK5uD3z9Md0SjYRx
artifact: /janet.*/
artifact: /(janet|shell).*/
draft: true
on:
APPVEYOR_REPO_TAG: true

View File

@@ -108,29 +108,39 @@
# Compilation Defaults
#
(def default-compiler (if is-win "cl" "cc"))
(def default-linker (if is-win "link" "cc"))
(def default-archiver (if is-win "lib" "ar"))
(def default-compiler (or (os/getenv "CC") (if is-win "cl.exe" "cc")))
(def default-linker (or (os/getenv "CC") (if is-win "link.exe" "cc")))
(def default-archiver (or (os/getenv "AR") (if is-win "lib.exe" "ar")))
# Detect threads
(def env (fiber/getenv (fiber/current)))
(def threads? (not (not (env 'thread/new))))
(print "threads " threads?)
# Default flags for natives, but not required
(def default-lflags (if is-win ["/nologo"] []))
(def default-cflags
(if is-win
["/nologo"]
["/nologo" "/MD"]
["-std=c99" "-Wall" "-Wextra"]))
# Link to pthreads
(def- thread-flags (if is-win [] (if threads? ["-lpthread"] [])))
# Required flags for dynamic libraries. These
# are used no matter what for dynamic libraries.
(def- dynamic-cflags
(if is-win
[]
["/LD"]
["-fPIC"]))
(def- dynamic-lflags
(if is-win
["/DLL"]
["/DLL" ;thread-flags]
(if is-mac
["-shared" "-undefined" "dynamic_lookup"]
["-shared"])))
["-shared" "-undefined" "dynamic_lookup" ;thread-flags]
["-shared" ;thread-flags])))
(defn- opt
"Get an option, allowing overrides via dynamic bindings AND some
@@ -211,21 +221,37 @@
(defn rm
"Remove a directory and all sub directories."
[path]
(if (= (os/stat path :mode) :directory)
(do
(each subpath (os/dir path)
(rm (string path sep subpath)))
(os/rmdir path))
(os/rm path)))
(try
(if (= (os/stat path :mode) :directory)
(do
(each subpath (os/dir path)
(rm (string path sep subpath)))
(os/rmdir path))
(os/rm path))
([err f] (unless (string/has-prefix? "No such file or directory" err)
(propagate err f)))))
(defn copy
"Copy a file or directory recursively from one location to another."
[src dest]
(print "copying " src " to " dest "...")
(if is-win
(shell "xcopy" src dest "/y" "/s" "/e")
(let [end (last (peg/match path-splitter src))
isdir (= (os/stat src :mode) :directory)]
(shell "xcopy" src (if isdir (string dest "\\" end) dest) "/y" "/s" "/e" "/i"))
(shell "cp" "-rf" src dest)))
(defn mkdir
"Create a directory if it doesn't exist. If it does exist, do nothing.
If we can't create it, give a friendly error. Return true if created, false if
existing. Throw an error if we can't create it."
[dir]
(if (os/mkdir dir)
true
(if (os/stat dir :mode)
false
(error (string "Could not create " dir " - this could be a permission issue.")))))
#
# C Compilation
#
@@ -310,7 +336,7 @@
(defn- link-c
"Link object files together to make a native module."
[opts target & objects]
(def ld (opt opts :linker default-linker))
(def linker (opt opts (if is-win :linker :compiler) default-linker))
(def cflags (getcflags opts))
(def lflags [;(opt opts :lflags default-lflags)
;(if (opts :static) [] dynamic-lflags)])
@@ -318,8 +344,8 @@
(check-cc)
(print "linking " target "...")
(if is-win
(shell ld ;lflags (string "/OUT:" target) ;objects (win-import-library))
(shell ld ;cflags `-o` target ;objects ;lflags))))
(shell linker ;lflags (string "/OUT:" target) ;objects (win-import-library))
(shell linker ;cflags `-o` target ;objects ;lflags))))
(defn- archive-c
"Link object files together to make a static library."
@@ -456,6 +482,10 @@ int main(int argc, const char **argv) {
fprintf(stderr, "invalid bytecode image - expected function.");
return 1;
}
JanetFunction *jfunc = janet_unwrap_function(marsh_out);
/* Check arity */
janet_arity(argc, jfunc->def->min_arity, jfunc->def->max_arity);
/* Collect command line arguments */
JanetArray *args = janet_array(argc);
@@ -464,17 +494,17 @@ int main(int argc, const char **argv) {
}
/* Create enviornment */
JanetTable *runtimeEnv = janet_table(0);
runtimeEnv->proto = env;
janet_table_put(runtimeEnv, janet_ckeywordv("args"), janet_wrap_array(args));
janet_gcroot(janet_wrap_table(runtimeEnv));
temptab = janet_table(0);
temptab = env;
janet_table_put(temptab, janet_ckeywordv("args"), janet_wrap_array(args));
janet_gcroot(janet_wrap_table(temptab));
/* Unlock GC */
janet_gcunlock(handle);
/* Run everything */
JanetFiber *fiber = janet_fiber(janet_unwrap_function(marsh_out), 64, argc, args->data);
fiber->env = runtimeEnv;
JanetFiber *fiber = janet_fiber(jfunc, 64, argc, argc ? args->data : NULL);
fiber->env = temptab;
Janet out;
JanetSignal result = janet_continue(fiber, janet_wrap_nil(), &out);
if (result) {
@@ -491,11 +521,11 @@ int main(int argc, const char **argv) {
# Compile and link final exectable
(do
(def extra-lflags (case (os/which)
:macos ["-ldl" "-lm"]
:windows []
:linux ["-lm" "-ldl" "-lrt"]
:macos ["-ldl" "-lm" ;thread-flags]
:windows [;thread-flags]
:linux ["-lm" "-ldl" "-lrt" ;thread-flags]
#default
["-lm"]))
["-lm" ;thread-flags]))
(def cc (opt opts :compiler default-compiler))
(def lflags [;dep-lflags ;(opt opts :lflags default-lflags) ;extra-lflags])
(def cflags (getcflags opts))
@@ -546,9 +576,7 @@ int main(int argc, const char **argv) {
(def path ((string/split "\n" line) 0))
(def path ((string/split "\r" path) 0))
(print "removing " path)
(try (rm path) ([err]
(unless (= err "No such file or directory")
(error err)))))
(rm path))
(:close f)
(print "removing " manifest)
(rm manifest)
@@ -569,17 +597,17 @@ int main(int argc, const char **argv) {
(defn install-git
"Install a bundle from git. If the bundle is already installed, the bundle
is reinistalled (but not rebuilt if artifacts are cached)."
is reinistalled (but not rebuilt if artifacts are cached)."
[repotab &opt recurse]
(def repo (if (string? repotab) repotab (repotab :repo)))
(def tag (unless (string? repotab) (repotab :tag)))
# prevent infinite recursion (very unlikely, but consider
# prevent infinite recursion (very unlikely, but consider
# 'my-package "my-package" in the package listing)
(when (> (or recurse 0) 100)
(error "too many references resolving package url"))
# Handle short names
(unless (string/find ":" repo)
(def pkgs
(def pkgs
(try (require "pkgs")
([err f]
(install-git (dyn :pkglist default-pkglist))
@@ -591,13 +619,14 @@ int main(int argc, const char **argv) {
(error (string "expected string or table for repository, got " next-repo)))
(break (install-git next-repo (if recurse (inc recurse) 0))))
(def cache (find-cache))
(os/mkdir cache)
(mkdir cache)
(def id (filepath-replace repo))
(def module-dir (string cache sep id))
(var fresh false)
(when (os/mkdir module-dir)
(set fresh true)
(os/execute ["git" "clone" repo module-dir] :p))
(when (mkdir module-dir)
(set fresh true)
(print "cloning repository " repo " to " module-dir)
(os/execute ["git" "clone" repo module-dir] :p))
(def olddir (os/cwd))
(try
(with-dyns [:rules @{}
@@ -626,7 +655,7 @@ int main(int argc, const char **argv) {
(def path (string destdir sep name))
(array/push (dyn :installed-files) path)
(add-body "install"
(os/mkdir destdir)
(mkdir destdir)
(copy src destdir)))
#
@@ -668,7 +697,7 @@ int main(int argc, const char **argv) {
"# Metadata for static library %s\n\n%.20p"
(string name statext)
{:static-entry ename
:lflags (opts :lflags)})))
:lflags ~',(opts :lflags)})))
(add-dep "build" metaname)
(install-rule metaname path)
@@ -767,14 +796,16 @@ int main(int argc, const char **argv) {
(setdyn :manifest-dir manifests)
(setdyn :installed-files installed-files)
(rule "./build" [] (os/mkdir "build"))
(rule "./build" [] (mkdir "build"))
(phony "build" ["./build"])
(phony "manifest" []
(print "generating " manifest "...")
(os/mkdir manifests)
(mkdir manifests)
(spit manifest (string (string/join installed-files "\n") "\n")))
(phony "install" ["uninstall" "build" "manifest"]
(when (dyn :test)
(do-rule "test"))
(print "Installed as '" (meta :name) "'."))
(phony "install-deps" []
@@ -843,19 +874,22 @@ Subcommands are:
or (rule "ouput.file" [deps...] ...).
rules : list rules available with run.
update-pkgs : Update the current package listing from the remote git repository selected.
quickbin entry executable : Create an executable from a janet script with a main function.
Keys are:
--modpath : The directory to install modules to. Defaults to $JANET_MODPATH, $JANET_PATH, or (dyn :syspath)
--headerpath : The directory containing janet headers. Defaults to $JANET_HEADERPATH.
--binpath : The directory to install binaries and scripts. Defaults to $JANET_BINPATH.
--libpath : The directory containing janet C libraries (libjanet.*). Defaults to $JANET_LIBPATH.
--compiler : C compiler to use for natives. Defaults to cc (cl on windows).
--archiver : C compiler to use for static libraries. Defaults to ar (lib on windows).
--linker : C linker to use for linking natives. Defaults to cc (link on windows).
--compiler : C compiler to use for natives. Defaults to $CC or cc (cl.exe on windows).
--archiver : C compiler to use for static libraries. Defaults to $AR ar (lib.exe on windows).
--linker : C linker to use for linking natives. Defaults to link.exe on windows, not used on
other platforms.
--pkglist : URL of git repository for package listing. Defaults to $JANET_PKGLIST or https://github.com/janet-lang/pkgs.git
Flags are:
--verbose : Print shell commands as they are executed.
--test : If passed to jpm install, runs tests before installing. Will run tests recursively on dependencies.
`))
(defn- show-help
@@ -900,6 +934,11 @@ Flags are:
[]
(install-git (dyn :pkglist default-pkglist)))
(defn- quickbin
[input output]
(create-executable @{} input output)
(do-rule output))
(def- subcommands
{"build" build
"clean" clean
@@ -912,7 +951,8 @@ Flags are:
"run" local-rule
"rules" list-rules
"update-pkgs" update-pkgs
"uninstall" uninstall-cmd})
"uninstall" uninstall-cmd
"quickbin" quickbin})
(def- args (tuple/slice (dyn :args) 1))
(def- len (length args))

View File

@@ -19,7 +19,7 @@
@rem Set compile and link options here
@setlocal
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
@set JANET_LINK=link /nologo
@set JANET_LINK_STATIC=lib /nologo
@@ -40,14 +40,10 @@ link /nologo /out:build\xxd.exe build\xxd.obj
@if errorlevel 1 goto :BUILDFAIL
@rem Generate the embedded sources
build\xxd.exe src\mainclient\init.janet build\init.gen.c janet_gen_init
@if errorlevel 1 goto :BUILDFAIL
build\xxd.exe src\boot\boot.janet build\boot.gen.c janet_gen_boot
@if errorlevel 1 goto :BUILDFAIL
@rem Build the generated sources
%JANET_COMPILE% /Fobuild\mainclient\init.gen.obj build\init.gen.c
@if errorlevel 1 goto :BUILDFAIL
%JANET_COMPILE% /Fobuild\boot\boot.gen.obj build\boot.gen.c
@if errorlevel 1 goto :BUILDFAIL
@@ -100,6 +96,10 @@ for %%f in (src\core\*.c) do (
janet.exe tools\amalg.janet src\core\util.h src\core\state.h src\core\gc.h src\core\vector.h src\core\fiber.h src\core\regalloc.h src\core\compile.h src\core\emit.h src\core\symcache.h %amalg_files% build\core_image.c > build\janet.c
janet.exe tools\removecr.janet build\janet.c
@rem Gen shell.c
janet.exe tools\amalg.janet src\mainclient\line.h src\mainclient\line.c src\mainclient\main.c > build\shell.c
janet.exe tools\removecr.janet build\shell.c
echo === Successfully built janet.exe for Windows ===
echo === Run 'build_win test' to run tests. ==
echo === Run 'build_win clean' to delete build artifacts. ===
@@ -141,6 +141,7 @@ janet.exe tools\gendoc.janet > dist\doc.html
janet.exe tools\removecr.janet dist\doc.html
copy build\janet.c dist\janet.c
copy build\shell.c dist\shell.c
copy janet.exe dist\janet.exe
copy LICENSE dist\LICENSE
copy README.md dist\README.md
@@ -171,16 +172,30 @@ exit /b 0
:TESTINSTALL
pushd test\install
call jpm clean
@if errorlevel 1 goto :TESTFAIL
@if errorlevel 1 goto :TESTINSTALLFAIL
call jpm test
@if errorlevel 1 goto :TESTFAIL
@if errorlevel 1 goto :TESTINSTALLFAIL
call jpm --verbose --modpath=. install https://github.com/janet-lang/json.git
@if errorlevel 1 goto :TESTFAIL
@if errorlevel 1 goto :TESTINSTALLFAIL
call build\testexec
@if errorlevel 1 goto :TESTFAIL
@if errorlevel 1 goto :TESTINSTALLFAIL
call jpm --verbose quickbin testexec.janet build\testexec2.exe
@if errorlevel 1 goto :TESTINSTALLFAIL
call build\testexec2.exe
@if errorlevel 1 goto :TESTINSTALLFAIL
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/jhydro.git
@if errorlevel 1 goto :TESTINSTALLFAIL
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/path.git
@if errorlevel 1 goto :TESTINSTALLFAIL
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/argparse.git
@if errorlevel 1 goto :TESTINSTALLFAIL
popd
exit /b 0
:TESTINSTALLFAIL
popd
goto :TESTFAIL
@rem build, test, dist, install. Useful for local dev.
:ALL
call %0 build

11
examples/debug.janet Normal file
View File

@@ -0,0 +1,11 @@
# Load this file and run (myfn) to see the debugger
(defn myfn
[]
(debug)
(for i 0 10 (print i)))
(debug/fbreak myfn 3)
# Enable debugging in repl with
# (setdyn :debug true)

153
examples/debugger.janet Normal file
View File

@@ -0,0 +1,153 @@
###
### A useful debugger library for Janet. Should be used
### inside a debug repl.
###
(defn .fiber
"Get the current fiber being debugged."
[]
(if-let [entry (dyn '_fiber)]
(entry :value)
(dyn :fiber)))
(defn .stack
"Print the current fiber stack"
[]
(print)
(debug/stacktrace (.fiber) "")
(print))
(defn .frame
"Show a stack frame"
[&opt n]
(def stack (debug/stack (.fiber)))
(in stack (or n 0)))
(defn .fn
"Get the current function"
[&opt n]
(in (.frame n) :function))
(defn .slots
"Get an array of slots in a stack frame"
[&opt n]
(in (.frame n) :slots))
(defn .slot
"Get the value of the nth slot."
[&opt nth frame-idx]
(in (.slots frame-idx) (or nth 0)))
(defn .quit
"Resume (dyn :fiber) with the value passed to it after exiting the debugger."
[&opt val]
(setdyn :exit true)
(setdyn :resume-value val)
nil)
(defn .disasm
"Gets the assembly for the current function."
[&opt n]
(def frame (.frame n))
(def func (frame :function))
(disasm func))
(defn .bytecode
"Get the bytecode for the current function."
[&opt n]
((.disasm n) 'bytecode))
(defn .ppasm
"Pretty prints the assembly for the current function"
[&opt n]
(def frame (.frame n))
(def func (frame :function))
(def dasm (disasm func))
(def bytecode (dasm 'bytecode))
(def pc (frame :pc))
(def sourcemap (dasm 'sourcemap))
(var last-loc [-2 -2])
(print "\n function: " (dasm 'name) " [" (in dasm 'source "") "]")
(when-let [constants (dasm 'constants)]
(printf " constants: %.4Q\n" constants))
(printf " slots: %.4Q\n\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) "> " " "))
(printf "\e[33m%.20s\e[0m" (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))
(defn .source
"Show the source code for the function being debugged."
[&opt n]
(def frame (.frame n))
(def s (frame :source))
(def all-source (slurp s))
(print "\n\e[33m" all-source "\e[0m\n"))
(defn .breakall
"Set breakpoints on all instructions in the current function."
[&opt n]
(def fun (.fn n))
(def bytecode (.bytecode n))
(for i 0 (length bytecode)
(debug/fbreak fun i))
(print "Set " (length bytecode) " breakpoints in " fun))
(defn .clearall
"Clear all breakpoints on the current function."
[&opt n]
(def fun (.fn n))
(def bytecode (.bytecode n))
(for i 0 (length bytecode)
(debug/unfbreak fun i))
(print "Cleared " (length bytecode) " breakpoints in " fun))
(defn .break
"Set breakpoint at the current pc."
[]
(def frame (.frame))
(def fun (frame :function))
(def pc (frame :pc))
(debug/fbreak fun pc)
(print "Set breakpoint in " fun " at pc=" pc))
(defn .clear
"Clear the current breakpoint"
[]
(def frame (.frame))
(def fun (frame :function))
(def pc (frame :pc))
(debug/unfbreak fun pc)
(print "Cleared breakpoint in " fun " at pc=" pc))
(defn .next
"Go to the next breakpoint."
[&opt n]
(var res nil)
(for i 0 (or n 1)
(set res (resume (.fiber))))
res)
(defn .nextc
"Go to the next breakpoint, clearing the current breakpoint."
[&opt n]
(.clear)
(.next n))
(defn .step
"Execute the next n instructions."
[&opt n]
(var res nil)
(for i 0 (or n 1)
(set res (debug/step (.fiber))))
res)

11
examples/rtest.janet Normal file
View File

@@ -0,0 +1,11 @@
# How random is the RNG really?
(def counts (seq [_ :range [0 100]] 0))
(for i 0 1000000
(let [x (math/random)
intrange (math/floor (* 100 x))
oldcount (counts intrange)]
(put counts intrange (if oldcount (+ 1 oldcount) 1))))
(pp counts)

View File

@@ -1,7 +1,5 @@
# naive matrix implementation for testing typed array
(defmacro printf [& xs] ['print ['string/format (splice xs)]])
(defn matrix [nrow ncol] {:nrow nrow :ncol ncol :array (tarray/new :float64 (* nrow ncol))})
(defn matrix/row [mat i]
@@ -34,22 +32,21 @@
((matrix/row mat i) j))
(defn matrix/get** [mat i j value]
((matrix/column j) i))
((matrix/column mat j) i))
(defn tarray/print [array]
(def size (tarray/length array))
(def buf @"")
(buffer/format buf "[%2i]" size)
(defn tarray/print [arr]
(def size (tarray/length arr))
(prinf "[%2i]" size)
(for i 0 size
(buffer/format buf " %+6.3f " (array i)))
(print buf))
(prinf " %+6.3f " (arr i)))
(print))
(defn matrix/print [mat]
(def {:nrow nrow :ncol ncol :array tarray} mat)
(printf "matrix %iX%i %p" nrow ncol tarray)
(for i 0 nrow
(tarray/print (matrix/row mat i))))
(tarray/print (matrix/row mat i))))
(def nr 5)
@@ -57,27 +54,20 @@
(def A (matrix nr nc))
(loop (i :range (0 nr) j :range (0 nc))
(matrix/set A i j i))
(matrix/set A i j i))
(matrix/print A)
(loop (i :range (0 nr) j :range (0 nc))
(matrix/set* A i j i))
(matrix/set* A i j i))
(matrix/print A)
(loop (i :range (0 nr) j :range (0 nc))
(matrix/set** A i j i))
(matrix/set** A i j i))
(matrix/print A)
(printf "properties:\n%p" (tarray/properties (A :array)))
(for i 0 nr
(printf "row properties:[%i]\n%p" i (tarray/properties (matrix/row A i))))
(printf "row properties:[%i]\n%p" i (tarray/properties (matrix/row A i))))
(for i 0 nc
(printf "col properties:[%i]\n%p" i (tarray/properties (matrix/column A i))))
(printf "col properties:[%i]\n%p" i (tarray/properties (matrix/column A i))))

70
examples/threads.janet Normal file
View File

@@ -0,0 +1,70 @@
(defn worker-main
"Sends 11 messages back to parent"
[parent]
(def name (thread/receive))
(def interval (thread/receive))
(for i 0 10
(os/sleep interval)
(:send parent (string/format "thread %s wakeup no. %d" name i)))
(:send parent name))
(defn make-worker
[name interval]
(-> (thread/new)
(:send worker-main)
(:send name)
(:send interval)))
(def bob (make-worker "bob" 0.02))
(def joe (make-worker "joe" 0.03))
(def sam (make-worker "sam" 0.05))
# Receive out of order
(for i 0 33
(print (thread/receive)))
#
# Recursive Thread Tree - should pause for a bit, and then print a cool zigzag.
#
(def rng (math/rng (os/cryptorand 16)))
(defn choose [& xs]
(in xs (:int rng (length xs))))
(defn worker-tree
[parent]
(def name (thread/receive))
(def depth (thread/receive))
(if (< depth 5)
(do
(defn subtree []
(-> (thread/new)
(:send worker-tree)
(:send (string name "/" (choose "bob" "marley" "harry" "suki" "anna" "yu")))
(:send (inc depth))))
(let [l (subtree)
r (subtree)
lrep (thread/receive)
rrep (thread/receive)]
(:send parent [name ;lrep ;rrep])))
(do
(:send parent [name]))))
(-> (thread/new) (:send worker-tree) (:send "adam") (:send 0))
(def lines (thread/receive))
(map print lines)
#
# Receive timeout
#
(def slow (make-worker "slow-loras" 0.5))
(for i 0 50
(try
(let [msg (thread/receive 0.1)]
(print "\n" msg))
([err] (prin ".") (:flush stdout))))
(print "\ndone timing, timeouts ending.")
(try (while true (print (thread/receive))) ([err] (print "done")))

View File

@@ -1,5 +1,5 @@
# Version
!define VERSION "1.4.0"
!define VERSION "1.6.0"
!define PRODUCT_VERSION "${VERSION}.0"
VIProductVersion "${PRODUCT_VERSION}"
VIFileVersion "${PRODUCT_VERSION}"

22
jpm.1
View File

@@ -28,6 +28,10 @@ More interesting are the local commands. For more information on jpm usage, see
.BR \-\-verbose
Print detailed messages of what jpm is doing, including compilation commands and other shell commands.
.TP
.BR \-\-test
If passed to jpm install, runs tests before installing. Will run tests recursively on dependencies.
.SH OPTIONS
.TP
@@ -56,23 +60,24 @@ Linking statically might be a better idea, even in that case. Defaults to
$JANET_LIBPATH, or a reasonable default. See JANET_LIBPATH for more.
.TP
.BR \-\-compiler=cc
.BR \-\-compiler=$CC
Sets the compiler used for compiling native modules and standalone executables. Defaults
to cc.
.TP
.BR \-\-linker=ld
Sets the linker used to create native modules and executables.
.BR \-\-linker
Sets the linker used to create native modules and executables. Only used on windows, where
it defaults to link.exe.
.TP
.BR \-\-pkglist=https://github.com/janet-lang/pkgs.git
Sets the git repository for the package listing used to resolve shorthand package names.
.TP
.BR \-\-archiver=ar
.BR \-\-archiver=$AR
Sets the command used for creating static libraries, use for linking into the standalone executable.
Native modules are compiled twice, once a normal native module (shared object), and once as an
archive.
archive. Defaults to ar.
.SH COMMANDS
.TP
@@ -138,6 +143,13 @@ List all rules that can be run via run. This is useful for exploring rules in th
.BR update-pkgs
Update the package listing by installing the 'pkgs' package. Same as jpm install pkgs
.TP
.BR quickbin [\fBentry\fR] [\fBexecutable\fR]
Create a standalone, statically linked executable from a Janet source file that contains a main function.
The main function is the entry point of the program and will receive command line arguments
as function arguments. The entry file can import other modules, including native C modules, and
jpm will attempt to include the dependencies into the generated executable.
.SH ENVIRONMENT
.B JANET_PATH

View File

@@ -20,7 +20,7 @@
project('janet', 'c',
default_options : ['c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.4.0')
version : '1.6.0-dev')
# Global settings
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
@@ -30,6 +30,7 @@ header_path = join_paths(get_option('prefix'), get_option('includedir'), 'janet'
cc = meson.get_compiler('c')
m_dep = cc.find_library('m', required : false)
dl_dep = cc.find_library('dl', required : false)
thread_dep = dependency('threads')
# Link options
if build_machine.system() != 'windows'
@@ -83,7 +84,6 @@ gen = generator(xxd,
output : '@BASENAME@.gen.c',
arguments : ['@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@'])
boot_gen = gen.process('src/boot/boot.janet', extra_args: 'janet_gen_boot')
init_gen = gen.process('src/mainclient/init.janet', extra_args: 'janet_gen_init')
# Order is important here, as some headers
# depend on other headers for the amalg target
@@ -129,6 +129,7 @@ core_src = [
'src/core/struct.c',
'src/core/symcache.c',
'src/core/table.c',
'src/core/thread.c',
'src/core/tuple.c',
'src/core/typedarray.c',
'src/core/util.c',
@@ -156,7 +157,7 @@ mainclient_src = [
janet_boot = executable('janet-boot', core_src, boot_src, boot_gen,
include_directories : incdir,
c_args : '-DJANET_BOOTSTRAP',
dependencies : [m_dep, dl_dep],
dependencies : [m_dep, dl_dep, thread_dep],
native : true)
# Build core image
@@ -167,7 +168,7 @@ core_image = custom_target('core_image',
libjanet = library('janet', core_src, core_image,
include_directories : incdir,
dependencies : [m_dep, dl_dep],
dependencies : [m_dep, dl_dep, thread_dep],
install : true)
# Extra c flags - adding -fvisibility=hidden matches the Makefile and
@@ -185,16 +186,16 @@ else
extra_cross_cflags = []
endif
janet_mainclient = executable('janet', core_src, core_image, init_gen, mainclient_src,
janet_mainclient = executable('janet', core_src, core_image, mainclient_src,
include_directories : incdir,
dependencies : [m_dep, dl_dep],
dependencies : [m_dep, dl_dep, thread_dep],
c_args : extra_native_cflags,
install : true)
if meson.is_cross_build()
janet_nativeclient = executable('janet-native', core_src, core_image, init_gen, mainclient_src,
janet_nativeclient = executable('janet-native', core_src, core_image, mainclient_src,
include_directories : incdir,
dependencies : [m_dep, dl_dep],
dependencies : [m_dep, dl_dep, thread_dep],
c_args : extra_cross_cflags,
native : true)
else
@@ -214,11 +215,17 @@ amalg = custom_target('amalg',
output : ['janet.c'],
capture : true,
command : [janet_nativeclient, '@INPUT@'])
amalg_shell = custom_target('amalg-shell',
input : ['tools/amalg.janet', 'src/mainclient/line.h',
'src/mainclient/line.c', 'src/mainclient/main.c'],
output : ['shell.c'],
capture : true,
command : [janet_nativeclient, '@INPUT@'])
# Amalgamated client
janet_amalgclient = executable('janet-amalg', amalg, init_gen, mainclient_src,
janet_amalgclient = executable('janet-amalg', amalg, amalg_shell,
include_directories : incdir,
dependencies : [m_dep, dl_dep],
dependencies : [m_dep, dl_dep, thread_dep],
build_by_default : false)
# Tests
@@ -251,3 +258,4 @@ janet_binscripts = [
'auxbin/jpm'
]
install_data(sources : janet_binscripts, install_dir : 'bin')
install_data(sources : ['tools/.keep'], install_dir : 'lib/janet')

File diff suppressed because it is too large Load Diff

View File

@@ -27,10 +27,10 @@
#define JANETCONF_H
#define JANET_VERSION_MAJOR 1
#define JANET_VERSION_MINOR 4
#define JANET_VERSION_MINOR 6
#define JANET_VERSION_PATCH 0
#define JANET_VERSION_EXTRA ""
#define JANET_VERSION "1.4.0"
#define JANET_VERSION_EXTRA "-dev"
#define JANET_VERSION "1.6.0-dev"
/* #define JANET_BUILD "local" */

View File

@@ -158,8 +158,8 @@ static Janet cfun_array_ensure(int32_t argc, Janet *argv) {
}
static Janet cfun_array_slice(int32_t argc, Janet *argv) {
JanetRange range = janet_getslice(argc, argv);
JanetView view = janet_getindexed(argv, 0);
JanetRange range = janet_getslice(argc, argv);
JanetArray *array = janet_array(range.end - range.start);
if (array->data)
memcpy(array->data, view.items + range.start, sizeof(Janet) * (range.end - range.start));
@@ -261,11 +261,11 @@ static const JanetReg array_cfuns[] = {
},
{
"array/ensure", cfun_array_ensure,
JDOC("(array/ensure arr capacity)\n\n"
JDOC("(array/ensure arr capacity growth)\n\n"
"Ensures that the memory backing the array is large enough for capacity "
"items. Capacity must be an integer. If the backing capacity is already enough, "
"then this function does nothing. Otherwise, the backing memory will be reallocated "
"so that there is enough space.")
"items at the given rate of growth. Capacity and growth must be integers. "
"If the backing capacity is already enough, then this function does nothing. "
"Otherwise, the backing memory will be reallocated so that there is enough space.")
},
{
"array/slice", cfun_array_slice,
@@ -273,7 +273,8 @@ static const JanetReg array_cfuns[] = {
"Takes a slice of array or tuple from start to end. The range is half open, "
"[start, end). Indexes can also be negative, indicating indexing from the end of the "
"end of the array. By default, start is 0 and end is the length of the array. "
"Returns a new array.")
"Note that index -1 is synonymous with index (length arrtup) to allow a full "
"negative slice range. Returns a new array.")
},
{
"array/concat", cfun_array_concat,

View File

@@ -85,6 +85,7 @@ static const JanetInstructionDef janet_ops[] = {
{"gten", JOP_NUMERIC_GREATER_THAN_EQUAL},
{"gtim", JOP_GREATER_THAN_IMMEDIATE},
{"gtn", JOP_NUMERIC_GREATER_THAN},
{"in", JOP_IN},
{"jmp", JOP_JUMP},
{"jmpif", JOP_JUMP_IF},
{"jmpno", JOP_JUMP_IF_NOT},

View File

@@ -242,8 +242,8 @@ static Janet cfun_buffer_popn(int32_t argc, Janet *argv) {
}
static Janet cfun_buffer_slice(int32_t argc, Janet *argv) {
JanetRange range = janet_getslice(argc, argv);
JanetByteView view = janet_getbytes(argv, 0);
JanetRange range = janet_getslice(argc, argv);
JanetBuffer *buffer = janet_buffer(range.end - range.start);
if (buffer->data)
memcpy(buffer->data, view.bytes + range.start, range.end - range.start);

View File

@@ -80,6 +80,7 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
JINT_SSS, /* JOP_RESUME, */
JINT_SSU, /* JOP_SIGNAL, */
JINT_SSS, /* JOP_PROPAGATE */
JINT_SSS, /* JOP_IN, */
JINT_SSS, /* JOP_GET, */
JINT_SSS, /* JOP_PUT, */
JINT_SSU, /* JOP_GET_INDEX, */
@@ -203,7 +204,7 @@ int32_t janet_verify(JanetFuncDef *def) {
/* Allocate an empty funcdef. This function may have added functionality
* as commonalities between asm and compile arise. */
JanetFuncDef *janet_funcdef_alloc() {
JanetFuncDef *janet_funcdef_alloc(void) {
JanetFuncDef *def = janet_gcalloc(JANET_MEMORY_FUNCDEF, sizeof(JanetFuncDef));
def->environments = NULL;
def->constants = NULL;

View File

@@ -51,19 +51,6 @@ void janet_panicf(const char *format, ...) {
janet_panics(ret);
}
void janet_printf(const char *format, ...) {
va_list args;
JanetBuffer buffer;
int32_t len = 0;
while (format[len]) len++;
janet_buffer_init(&buffer, len);
va_start(args, format);
janet_formatb(&buffer, format, args);
va_end(args);
fwrite(buffer.data, buffer.count, 1, janet_dynfile("out", stdout));
janet_buffer_deinit(&buffer);
}
void janet_panic(const char *message) {
janet_panicv(janet_cstringv(message));
}
@@ -99,20 +86,32 @@ type janet_get##name(const Janet *argv, int32_t n) { \
janet_panic_type(x, n, JANET_TFLAG_##NAME); \
} \
return janet_unwrap_##name(x); \
} \
}
#define DEFINE_OPT(name, NAME, type) \
type janet_opt##name(const Janet *argv, int32_t argc, int32_t n, type dflt) { \
if (argc >= n) return dflt; \
if (n >= argc) return dflt; \
if (janet_checktype(argv[n], JANET_NIL)) return dflt; \
return janet_get##name(argv, n); \
}
Janet janet_getmethod(const uint8_t *method, const JanetMethod *methods) {
#define DEFINE_OPTLEN(name, NAME, type) \
type janet_opt##name(const Janet *argv, int32_t argc, int32_t n, int32_t dflt_len) { \
if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {\
return janet_##name(dflt_len); \
}\
return janet_get##name(argv, n); \
}
int janet_getmethod(const uint8_t *method, const JanetMethod *methods, Janet *out) {
while (methods->name) {
if (!janet_cstrcmp(method, methods->name))
return janet_wrap_cfunction(methods->cfun);
if (!janet_cstrcmp(method, methods->name)) {
*out = janet_wrap_cfunction(methods->cfun);
return 1;
}
methods++;
}
return janet_wrap_nil();
return 0;
}
DEFINE_GETTER(number, NUMBER, double)
@@ -130,6 +129,26 @@ DEFINE_GETTER(cfunction, CFUNCTION, JanetCFunction)
DEFINE_GETTER(boolean, BOOLEAN, int)
DEFINE_GETTER(pointer, POINTER, void *)
DEFINE_OPT(number, NUMBER, double)
DEFINE_OPT(tuple, TUPLE, const Janet *)
DEFINE_OPT(struct, STRUCT, const JanetKV *)
DEFINE_OPT(string, STRING, const uint8_t *)
DEFINE_OPT(keyword, KEYWORD, const uint8_t *)
DEFINE_OPT(symbol, SYMBOL, const uint8_t *)
DEFINE_OPT(fiber, FIBER, JanetFiber *)
DEFINE_OPT(function, FUNCTION, JanetFunction *)
DEFINE_OPT(cfunction, CFUNCTION, JanetCFunction)
DEFINE_OPT(boolean, BOOLEAN, int)
DEFINE_OPT(pointer, POINTER, void *)
DEFINE_OPTLEN(buffer, BUFFER, JanetBuffer *)
DEFINE_OPTLEN(table, TABLE, JanetTable *)
DEFINE_OPTLEN(array, ARRAY, JanetArray *)
#undef DEFINE_GETTER
#undef DEFINE_OPT
#undef DEFINE_OPTLEN
const char *janet_getcstring(const Janet *argv, int32_t n) {
const uint8_t *jstr = janet_getstring(argv, n);
const char *cstr = (const char *)jstr;
@@ -139,10 +158,20 @@ const char *janet_getcstring(const Janet *argv, int32_t n) {
return cstr;
}
int32_t janet_getnat(const Janet *argv, int32_t n) {
Janet x = argv[n];
if (!janet_checkint(x)) goto bad;
int32_t ret = janet_unwrap_integer(x);
if (ret < 0) goto bad;
return ret;
bad:
janet_panicf("bad slot #%d, expected non-negative 32 bit signed integer, got %v", n, x);
}
int32_t janet_getinteger(const Janet *argv, int32_t n) {
Janet x = argv[n];
if (!janet_checkint(x)) {
janet_panicf("bad slot #%d, expected integer, got %v", n, x);
janet_panicf("bad slot #%d, expected 32 bit signed integer, got %v", n, x);
}
return janet_unwrap_integer(x);
}
@@ -150,7 +179,7 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) {
int64_t janet_getinteger64(const Janet *argv, int32_t n) {
Janet x = argv[n];
if (!janet_checkint64(x)) {
janet_panicf("bad slot #%d, expected 64 bit integer, got %v", n, x);
janet_panicf("bad slot #%d, expected 64 bit signed integer, got %v", n, x);
}
return (int64_t) janet_unwrap_number(x);
}
@@ -282,6 +311,12 @@ uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags) {
return ret;
}
int32_t janet_optnat(const Janet *argv, int32_t argc, int32_t n, int32_t dflt) {
if (argc <= n) return dflt;
if (janet_checktype(argv[n], JANET_NIL)) return dflt;
return janet_getnat(argv, n);
}
int32_t janet_optinteger(const Janet *argv, int32_t argc, int32_t n, int32_t dflt) {
if (argc <= n) return dflt;
if (janet_checktype(argv[n], JANET_NIL)) return dflt;

View File

@@ -27,10 +27,6 @@
#include "vector.h"
#endif
static int fixarity0(JanetFopts opts, JanetSlot *args) {
(void) opts;
return janet_v_count(args) == 0;
}
static int fixarity1(JanetFopts opts, JanetSlot *args) {
(void) opts;
return janet_v_count(args) == 1;
@@ -101,8 +97,16 @@ static JanetSlot do_error(JanetFopts opts, JanetSlot *args) {
}
static JanetSlot do_debug(JanetFopts opts, JanetSlot *args) {
(void)args;
janetc_emit(opts.compiler, JOP_SIGNAL | (2 << 24));
return janetc_cslot(janet_wrap_nil());
int32_t len = janet_v_count(args);
JanetSlot t = janetc_gettarget(opts);
janetc_emit_ssu(opts.compiler, JOP_SIGNAL, t,
(len == 1) ? args[0] : janetc_cslot(janet_wrap_nil()),
JANET_SIGNAL_DEBUG,
1);
return t;
}
static JanetSlot do_in(JanetFopts opts, JanetSlot *args) {
return opreduce(opts, args, JOP_IN, janet_wrap_nil());
}
static JanetSlot do_get(JanetFopts opts, JanetSlot *args) {
return opreduce(opts, args, JOP_GET, janet_wrap_nil());
@@ -270,12 +274,12 @@ static JanetSlot do_neq(JanetFopts opts, JanetSlot *args) {
/* Arranged by tag */
static const JanetFunOptimizer optimizers[] = {
{fixarity0, do_debug},
{maxarity1, do_debug},
{fixarity1, do_error},
{minarity2, do_apply},
{maxarity1, do_yield},
{fixarity2, do_resume},
{fixarity2, do_get},
{fixarity2, do_in},
{fixarity3, do_put},
{fixarity1, do_length},
{NULL, do_add},
@@ -301,7 +305,8 @@ static const JanetFunOptimizer optimizers[] = {
{NULL, do_lte},
{NULL, do_eq},
{NULL, do_neq},
{fixarity2, do_propagate}
{fixarity2, do_propagate},
{fixarity2, do_get}
};
const JanetFunOptimizer *janetc_funopt(uint32_t flags) {

View File

@@ -569,15 +569,24 @@ static int macroexpand1(
return 0;
/* Evaluate macro */
JanetFiber *fiberp = NULL;
JanetFunction *macro = janet_unwrap_function(macroval);
int32_t arity = janet_tuple_length(form) - 1;
JanetFiber *fiberp = janet_fiber(macro, 64, arity, form + 1);
if (NULL == fiberp) {
int32_t minar = macro->def->min_arity;
int32_t maxar = macro->def->max_arity;
const uint8_t *es = NULL;
if (minar >= 0 && arity < minar)
es = janet_formatc("macro arity mismatch, expected at least %d, got %d", minar, arity);
if (maxar >= 0 && arity > maxar)
es = janet_formatc("macro arity mismatch, expected at most %d, got %d", maxar, arity);
c->result.macrofiber = NULL;
janetc_error(c, es);
}
/* Set env */
fiberp->env = c->env;
int lock = janet_gclock();
JanetSignal status = janet_pcall(
macro,
janet_tuple_length(form) - 1,
form + 1,
&x,
&fiberp);
JanetSignal status = janet_continue(fiberp, janet_wrap_nil(), &x);
janet_gcunlock(lock);
if (status != JANET_SIGNAL_OK) {
const uint8_t *es = janet_formatc("(macro) %V", x);
@@ -628,7 +637,7 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
const Janet *tup = janet_unwrap_tuple(x);
/* Empty tuple is tuple literal */
if (janet_tuple_length(tup) == 0) {
ret = janetc_cslot(x);
ret = janetc_cslot(janet_wrap_tuple(janet_tuple_n(NULL, 0)));
} else if (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR) { /* [] tuples are not function call */
ret = janetc_tuple(opts, x);
} else {

View File

@@ -34,7 +34,7 @@
#define JANET_FUN_APPLY 3
#define JANET_FUN_YIELD 4
#define JANET_FUN_RESUME 5
#define JANET_FUN_GET 6
#define JANET_FUN_IN 6
#define JANET_FUN_PUT 7
#define JANET_FUN_LENGTH 8
#define JANET_FUN_ADD 9
@@ -61,6 +61,7 @@
#define JANET_FUN_EQ 30
#define JANET_FUN_NEQ 31
#define JANET_FUN_PROP 32
#define JANET_FUN_GET 33
/* Compiler typedefs */
typedef struct JanetCompiler JanetCompiler;

View File

@@ -177,6 +177,13 @@ static Janet janet_core_expand_path(int32_t argc, Janet *argv) {
} else if (strncmp(template + i, ":name:", 6) == 0) {
janet_buffer_push_cstring(out, name);
i += 5;
} else if (strncmp(template + i, ":native:", 8) == 0) {
#ifdef JANET_WINDOWS
janet_buffer_push_cstring(out, ".dll");
#else
janet_buffer_push_cstring(out, ".so");
#endif
i += 7;
} else {
janet_buffer_push_u8(out, (uint8_t) template[i]);
}
@@ -342,6 +349,21 @@ static Janet janet_core_array(int32_t argc, Janet *argv) {
return janet_wrap_array(array);
}
static Janet janet_core_slice(int32_t argc, Janet *argv) {
JanetRange range;
JanetByteView bview;
JanetView iview;
if (janet_bytes_view(argv[0], &bview.bytes, &bview.len)) {
range = janet_getslice(argc, argv);
return janet_stringv(bview.bytes + range.start, range.end - range.start);
} else if (janet_indexed_view(argv[0], &iview.items, &iview.len)) {
range = janet_getslice(argc, argv);
return janet_wrap_tuple(janet_tuple_n(iview.items + range.start, range.end - range.start));
} else {
janet_panic_type(argv[0], 0, JANET_TFLAG_BYTES | JANET_TFLAG_INDEXED);
}
}
static Janet janet_core_table(int32_t argc, Janet *argv) {
int32_t i;
if (argc & 1)
@@ -576,7 +598,7 @@ static const JanetReg corelib_cfuns[] = {
"gcsetinterval", janet_core_gcsetinterval,
JDOC("(gcsetinterval interval)\n\n"
"Set an integer number of bytes to allocate before running garbage collection. "
"Low valuesi for interval will be slower but use less memory. "
"Low values for interval will be slower but use less memory. "
"High values will be faster but use more memory.")
},
{
@@ -653,7 +675,13 @@ static const JanetReg corelib_cfuns[] = {
"Expands a path template as found in module/paths for module/find. "
"This takes in a path (the argument to require) and a template string, template, "
"to expand the path to a path that can be "
"used for importing files.")
"used for importing files. The replacements are as follows:\n\n"
"\t:all:\tthe value of path verbatim\n"
"\t:cur:\tthe current file, or (dyn :current-file)\n"
"\t:dir:\tthe directory containing the current file\n"
"\t:name:\tthe filename component of path, with extenion if given\n"
"\t:native:\tthe extension used to load natives, .so or .dll\n"
"\t:sys:\tthe system path, or (syn :syspath)")
},
{
"int?", janet_core_check_int,
@@ -665,6 +693,11 @@ static const JanetReg corelib_cfuns[] = {
JDOC("(nat? x)\n\n"
"Check if x can be exactly represented as a non-negative 32 bit signed two's complement integer.")
},
{
"slice", janet_core_slice,
JDOC("(slice x &opt start end)\n\n"
"Extract a sub-range of an indexed data strutrue or byte sequence.")
},
{NULL, NULL, NULL}
};
@@ -746,7 +779,7 @@ static void templatize_varop(
SSI(JOP_GET_INDEX, 3, 0, 0), /* accum = args[0] */
SI(JOP_LOAD_INTEGER, 5, 1), /* i = 1 */
/* Main loop */
SSS(JOP_GET, 4, 0, 5), /* operand = args[i] */
SSS(JOP_IN, 4, 0, 5), /* operand = args[i] */
SSS(op, 3, 3, 4), /* accum = accum op operand */
SSI(JOP_ADD_IMMEDIATE, 5, 5, 1), /* i++ */
SSI(JOP_EQUALS, 2, 5, 1), /* jump? = (i == argn) */
@@ -794,7 +827,7 @@ static void templatize_comparator(
SI(JOP_LOAD_INTEGER, 5, 1), /* i = 1 */
/* Main loop */
SSS(JOP_GET, 4, 0, 5), /* next = args[i] */
SSS(JOP_IN, 4, 0, 5), /* next = args[i] */
SSS(op, 2, 3, 4), /* jump? = last compare next */
SI(JOP_JUMP_IF_NOT, 2, 7), /* if not jump? goto fail (return false) */
SSI(JOP_ADD_IMMEDIATE, 5, 5, 1), /* i++ */
@@ -841,7 +874,7 @@ static void make_apply(JanetTable *env) {
SI(JOP_LOAD_INTEGER, 4, 0), /* i = 0 */
/* Main loop */
SSS(JOP_GET, 5, 1, 4), /* x = args[i] */
SSS(JOP_IN, 5, 1, 4), /* x = args[i] */
SSI(JOP_ADD_IMMEDIATE, 4, 4, 1), /* i++ */
SSI(JOP_EQUALS, 3, 4, 2), /* jump? = (i == argn) */
SI(JOP_JUMP_IF, 3, 3), /* if jump? go forward 3 */
@@ -870,7 +903,7 @@ static const uint32_t error_asm[] = {
};
static const uint32_t debug_asm[] = {
JOP_SIGNAL | (2 << 24),
JOP_RETURN_NIL
JOP_RETURN
};
static const uint32_t yield_asm[] = {
JOP_SIGNAL | (3 << 24),
@@ -880,6 +913,14 @@ static const uint32_t resume_asm[] = {
JOP_RESUME | (1 << 24),
JOP_RETURN
};
static const uint32_t in_asm[] = {
JOP_IN | (1 << 24),
JOP_LOAD_NIL | (3 << 8),
JOP_EQUALS | (3 << 8) | (3 << 24),
JOP_JUMP_IF | (3 << 8) | (2 << 16),
JOP_RETURN,
JOP_RETURN | (2 << 8)
};
static const uint32_t get_asm[] = {
JOP_GET | (1 << 24),
JOP_LOAD_NIL | (3 << 8),
@@ -904,13 +945,48 @@ static const uint32_t propagate_asm[] = {
JOP_PROPAGATE | (1 << 24),
JOP_RETURN
};
#endif /* ifndef JANET_NO_BOOTSTRAP */
#endif /* ifdef JANET_BOOTSTRAP */
/*
* Setup Environment
*/
static void janet_load_libs(JanetTable *env) {
janet_core_cfuns(env, NULL, corelib_cfuns);
janet_lib_io(env);
janet_lib_math(env);
janet_lib_array(env);
janet_lib_tuple(env);
janet_lib_buffer(env);
janet_lib_table(env);
janet_lib_fiber(env);
janet_lib_os(env);
janet_lib_parse(env);
janet_lib_compile(env);
janet_lib_debug(env);
janet_lib_string(env);
janet_lib_marsh(env);
#ifdef JANET_PEG
janet_lib_peg(env);
#endif
#ifdef JANET_ASSEMBLER
janet_lib_asm(env);
#endif
#ifdef JANET_TYPED_ARRAY
janet_lib_typed_array(env);
#endif
#ifdef JANET_INT_TYPES
janet_lib_inttypes(env);
#endif
#ifdef JANET_THREADS
janet_lib_thread(env);
#endif
}
#ifdef JANET_BOOTSTRAP
JanetTable *janet_core_env(JanetTable *replacements) {
JanetTable *env = (NULL != replacements) ? replacements : janet_table(0);
janet_core_cfuns(env, NULL, corelib_cfuns);
#ifdef JANET_BOOTSTRAP
janet_quick_asm(env, JANET_FUN_PROP,
"propagate", 2, 2, 2, 2, propagate_asm, sizeof(propagate_asm),
JDOC("(propagate x fiber)\n\n"
@@ -919,17 +995,17 @@ JanetTable *janet_core_env(JanetTable *replacements) {
"fiber is in a state that can be resumed, resuming the current fiber will "
"first resume fiber."));
janet_quick_asm(env, JANET_FUN_DEBUG,
"debug", 0, 0, 0, 1, debug_asm, sizeof(debug_asm),
JDOC("(debug)\n\n"
"debug", 1, 0, 1, 1, debug_asm, sizeof(debug_asm),
JDOC("(debug &opt x)\n\n"
"Throws a debug signal that can be caught by a parent fiber and used to inspect "
"the running state of the current fiber. Returns nil."));
"the running state of the current fiber. Returns the value passed in by resume."));
janet_quick_asm(env, JANET_FUN_ERROR,
"error", 1, 1, 1, 1, error_asm, sizeof(error_asm),
JDOC("(error e)\n\n"
"Throws an error e that can be caught and handled by a parent fiber."));
janet_quick_asm(env, JANET_FUN_YIELD,
"yield", 1, 0, 1, 2, yield_asm, sizeof(yield_asm),
JDOC("(yield x)\n\n"
JDOC("(yield &opt x)\n\n"
"Yield a value to a parent fiber. When a fiber yields, its execution is paused until "
"another thread resumes it. The fiber will then resume, and the last yield call will "
"return the value that was passed to resume."));
@@ -940,15 +1016,20 @@ JanetTable *janet_core_env(JanetTable *replacements) {
"will be returned to the last yield in the case of a pending fiber, or the argument to "
"the dispatch function in the case of a new fiber. Returns either the return result of "
"the fiber's dispatch function, or the value from the next yield call in fiber."));
janet_quick_asm(env, JANET_FUN_IN,
"in", 3, 2, 3, 4, in_asm, sizeof(in_asm),
JDOC("(in ds key &opt dflt)\n\n"
"Get value in ds at key, works on associative data structures. Arrays, tuples, tables, structs, "
"strings, symbols, and buffers are all associative and can be used. Arrays, tuples, strings, buffers, "
"and symbols must use integer keys that are in bounds or an error is raised. Structs and tables can "
"take any value as a key except nil and will return nil or dflt if not found."));
janet_quick_asm(env, JANET_FUN_GET,
"get", 3, 2, 3, 4, get_asm, sizeof(get_asm),
"get", 3, 2, 3, 4, get_asm, sizeof(in_asm),
JDOC("(get ds key &opt dflt)\n\n"
"Get a value from any associative data structure. Arrays, tuples, tables, structs, strings, "
"symbols, and buffers are all associative and can be used with get. Order structures, name "
"arrays, tuples, strings, buffers, and symbols must use integer keys. Structs and tables can "
"take any value as a key except nil and return a value except nil. Byte sequences will return "
"integer representations of bytes as result of a get call. If no values is found, will return "
"dflt or nil if no default is provided."));
"Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
"Similar to in, but will not throw an error if the key is invalid for the data structure "
"unless the data structure is an abstract type. In that case, the abstract type getter may throw "
"an error."));
janet_quick_asm(env, JANET_FUN_PUT,
"put", 3, 3, 3, 3, put_asm, sizeof(put_asm),
JDOC("(put ds key value)\n\n"
@@ -1062,48 +1143,50 @@ JanetTable *janet_core_env(JanetTable *replacements) {
/* Allow references to the environment */
janet_def(env, "_env", janet_wrap_table(env), JDOC("The environment table for the current scope."));
/* Set as gc root */
janet_load_libs(env);
janet_gcroot(janet_wrap_table(env));
#endif
return env;
}
/* Load auxiliary envs */
janet_lib_io(env);
janet_lib_math(env);
janet_lib_array(env);
janet_lib_tuple(env);
janet_lib_buffer(env);
janet_lib_table(env);
janet_lib_fiber(env);
janet_lib_os(env);
janet_lib_parse(env);
janet_lib_compile(env);
janet_lib_debug(env);
janet_lib_string(env);
janet_lib_marsh(env);
#ifdef JANET_PEG
janet_lib_peg(env);
#endif
#ifdef JANET_ASSEMBLER
janet_lib_asm(env);
#endif
#ifdef JANET_TYPED_ARRAY
janet_lib_typed_array(env);
#endif
#ifdef JANET_INT_TYPES
janet_lib_inttypes(env);
#endif
#else
#ifndef JANET_BOOTSTRAP
/* Unmarshal from core image */
JanetTable *janet_core_env(JanetTable *replacements) {
/* Memoize core env, ignoring replacements the second time around. */
if (NULL != janet_vm_core_env) {
return janet_vm_core_env;
}
/* Load core cfunctions (and some built in janet assembly functions) */
JanetTable *dict = janet_table(300);
janet_load_libs(dict);
/* Add replacements */
if (replacements != NULL) {
for (int32_t i = 0; i < replacements->capacity; i++) {
JanetKV kv = replacements->data[i];
if (!janet_checktype(kv.key, JANET_NIL)) {
janet_table_put(dict, kv.key, kv.value);
if (janet_checktype(kv.value, JANET_CFUNCTION)) {
janet_table_put(janet_vm_registry, kv.value, kv.key);
}
}
}
}
/* Unmarshal bytecode */
Janet marsh_out = janet_unmarshal(
janet_core_image,
janet_core_image_size,
0,
env,
dict,
NULL);
/* Memoize */
janet_gcroot(marsh_out);
env = janet_unwrap_table(marsh_out);
#endif
JanetTable *env = janet_unwrap_table(marsh_out);
janet_vm_core_env = env;
return env;
}
#endif

View File

@@ -90,9 +90,6 @@ void janet_debug_find(
if (best_def) {
*def_out = best_def;
*pc_out = besti;
if (best_def->name) {
janet_printf("name: %S\n", best_def->name);
}
} else {
janet_panic("could not find breakpoint");
}
@@ -102,13 +99,12 @@ void janet_debug_find(
* consitency with the top level code it is defined once. */
void janet_stacktrace(JanetFiber *fiber, Janet err) {
int32_t fi;
FILE *out = janet_dynfile("err", stderr);
const char *errstr = (const char *)janet_to_string(err);
JanetFiber **fibers = NULL;
int wrote_error = 0;
int print_color = janet_truthy(janet_dyn("err-color"));
if (print_color) fprintf(out, "\x1b[31m");
if (print_color) janet_eprintf("\x1b[31m");
while (fiber) {
janet_v_push(fibers, fiber);
@@ -127,47 +123,47 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
if (!wrote_error) {
JanetFiberStatus status = janet_fiber_status(fiber);
const char *prefix = status == JANET_STATUS_ERROR ? "" : "status ";
fprintf(out, "%s%s: %s\n",
prefix,
janet_status_names[status],
errstr);
janet_eprintf("%s%s: %s\n",
prefix,
janet_status_names[status],
errstr);
wrote_error = 1;
}
fprintf(out, " in");
janet_eprintf(" in");
if (frame->func) {
def = frame->func->def;
fprintf(out, " %s", def->name ? (const char *)def->name : "<anonymous>");
janet_eprintf(" %s", def->name ? (const char *)def->name : "<anonymous>");
if (def->source) {
fprintf(out, " [%s]", (const char *)def->source);
janet_eprintf(" [%s]", (const char *)def->source);
}
} else {
JanetCFunction cfun = (JanetCFunction)(frame->pc);
if (cfun) {
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
if (!janet_checktype(name, JANET_NIL))
fprintf(out, " %s", (const char *)janet_to_string(name));
janet_eprintf(" %s", (const char *)janet_to_string(name));
else
fprintf(out, " <cfunction>");
janet_eprintf(" <cfunction>");
}
}
if (frame->flags & JANET_STACKFRAME_TAILCALL)
fprintf(out, " (tailcall)");
janet_eprintf(" (tailcall)");
if (frame->func && frame->pc) {
int32_t off = (int32_t)(frame->pc - def->bytecode);
if (def->sourcemap) {
JanetSourceMapping mapping = def->sourcemap[off];
fprintf(out, " on line %d, column %d", mapping.line, mapping.column);
janet_eprintf(" on line %d, column %d", mapping.line, mapping.column);
} else {
fprintf(out, " pc=%d", off);
janet_eprintf(" pc=%d", off);
}
}
fprintf(out, "\n");
janet_eprintf("\n");
}
}
if (print_color) fprintf(out, "\x1b[0m");
if (print_color) janet_eprintf("\x1b[0m");
janet_v_free(fibers);
}
@@ -317,6 +313,14 @@ static Janet cfun_debug_argstack(int32_t argc, Janet *argv) {
return janet_wrap_array(array);
}
static Janet cfun_debug_step(int32_t argc, Janet *argv) {
janet_arity(argc, 1, 2);
JanetFiber *fiber = janet_getfiber(argv, 0);
Janet out = janet_wrap_nil();
janet_step(fiber, argc == 1 ? janet_wrap_nil() : argv[1], &out);
return out;
}
static const JanetReg debug_cfuns[] = {
{
"debug/break", cfun_debug_break,
@@ -385,6 +389,13 @@ static const JanetReg debug_cfuns[] = {
"the fiber handling the error can see which fiber raised the signal. This function should "
"be used mostly for debugging purposes.")
},
{
"debug/step", cfun_debug_step,
JDOC("(debug/step fiber &opt x)\n\n"
"Run a fiber for one virtual instruction of the Janet machine. Can optionally "
"pass in a value that will be passed as the resuming value. Returns the signal value, "
"which will usually be nil, as breakpoints raise nil signals.")
},
{NULL, NULL, NULL}
};

View File

@@ -40,6 +40,13 @@ JANET_THREAD_LOCAL uint32_t janet_vm_root_count;
JANET_THREAD_LOCAL uint32_t janet_vm_root_capacity;
/* Scratch Memory */
#ifdef JANET_64
#define SCRATCH_HDR_SIZE 16 /* smalloc must guarantee 16 byte alignment. */
#elif JANET_32
#define SCRATCH_HDR_SIZE 8 /* smalloc must guarantee 8 byte alignment. */
#else
#error "unknown scratch alignment"
#endif
JANET_THREAD_LOCAL void **janet_scratch_mem;
JANET_THREAD_LOCAL size_t janet_scratch_cap;
JANET_THREAD_LOCAL size_t janet_scratch_len;
@@ -347,10 +354,18 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
return (void *)mem;
}
static void free_one_scratch(void *mem) {
ScratchFinalizer finalize = *(ScratchFinalizer *)mem;
if (finalize)
finalize((char *)mem + SCRATCH_HDR_SIZE);
free(mem);
}
/* Free all allocated scratch memory */
static void janet_free_all_scratch(void) {
for (size_t i = 0; i < janet_scratch_len; i++)
free(janet_scratch_mem[i]);
for (size_t i = 0; i < janet_scratch_len; i++) {
free_one_scratch(janet_scratch_mem[i]);
}
janet_scratch_len = 0;
}
@@ -457,10 +472,11 @@ void janet_gcunlock(int handle) {
/* Scratch memory API */
void *janet_smalloc(size_t size) {
void *mem = malloc(size);
void *mem = malloc(SCRATCH_HDR_SIZE + size);
if (NULL == mem) {
JANET_OUT_OF_MEMORY;
}
*(ScratchFinalizer *)mem = NULL;
if (janet_scratch_len == janet_scratch_cap) {
size_t newcap = 2 * janet_scratch_cap + 2;
void **newmem = (void **) realloc(janet_scratch_mem, newcap * sizeof(void *));
@@ -471,20 +487,21 @@ void *janet_smalloc(size_t size) {
janet_scratch_mem = newmem;
}
janet_scratch_mem[janet_scratch_len++] = mem;
return mem;
return (char *)mem + SCRATCH_HDR_SIZE;
}
void *janet_srealloc(void *mem, size_t size) {
if (NULL == mem) return janet_smalloc(size);
mem = (char *)mem - SCRATCH_HDR_SIZE;
if (janet_scratch_len) {
for (size_t i = janet_scratch_len - 1; ; i--) {
if (janet_scratch_mem[i] == mem) {
void *newmem = realloc(mem, size);
void *newmem = realloc(mem, size + SCRATCH_HDR_SIZE);
if (NULL == newmem) {
JANET_OUT_OF_MEMORY;
}
janet_scratch_mem[i] = newmem;
return newmem;
return (char *)newmem + SCRATCH_HDR_SIZE;
}
if (i == 0) break;
}
@@ -492,13 +509,19 @@ void *janet_srealloc(void *mem, size_t size) {
janet_exit("invalid janet_srealloc");
}
void janet_sfinalizer(void *mem, ScratchFinalizer finalizer) {
mem = (char *)mem - SCRATCH_HDR_SIZE;
*(ScratchFinalizer *)mem = finalizer;
}
void janet_sfree(void *mem) {
if (NULL == mem) return;
mem = (char *)mem - SCRATCH_HDR_SIZE;
if (janet_scratch_len) {
for (size_t i = janet_scratch_len - 1; ; i--) {
if (janet_scratch_mem[i] == mem) {
janet_scratch_mem[i] = janet_scratch_mem[--janet_scratch_len];
free(mem);
free_one_scratch(mem);
return;
}
if (i == 0) break;

View File

@@ -36,26 +36,29 @@
#define MAX_INT_IN_DBL 9007199254740992ULL /* 2^53 */
static Janet it_s64_get(void *p, Janet key);
static Janet it_u64_get(void *p, Janet key);
static int it_s64_get(void *p, Janet key, Janet *out);
static int it_u64_get(void *p, Janet key, Janet *out);
static void int64_marshal(void *p, JanetMarshalContext *ctx) {
janet_marshal_abstract(ctx, p);
janet_marshal_int64(ctx, *((int64_t *)p));
}
static void int64_unmarshal(void *p, JanetMarshalContext *ctx) {
*((int64_t *)p) = janet_unmarshal_int64(ctx);
static void *int64_unmarshal(JanetMarshalContext *ctx) {
int64_t *p = janet_unmarshal_abstract(ctx, sizeof(int64_t));
p[0] = janet_unmarshal_int64(ctx);
return p;
}
static void it_s64_tostring(void *p, JanetBuffer *buffer) {
char str[32];
sprintf(str, "<core/s64 %" PRId64 ">", *((int64_t *)p));
sprintf(str, "%" PRId64, *((int64_t *)p));
janet_buffer_push_cstring(buffer, str);
}
static void it_u64_tostring(void *p, JanetBuffer *buffer) {
char str[32];
sprintf(str, "<core/u64 %" PRIu64 ">", *((uint64_t *)p));
sprintf(str, "%" PRIu64, *((uint64_t *)p));
janet_buffer_push_cstring(buffer, str);
}
@@ -348,18 +351,18 @@ static JanetMethod it_u64_methods[] = {
{NULL, NULL}
};
static Janet it_s64_get(void *p, Janet key) {
static int it_s64_get(void *p, Janet key, Janet *out) {
(void) p;
if (!janet_checktype(key, JANET_KEYWORD))
janet_panicf("expected keyword, got %v", key);
return janet_getmethod(janet_unwrap_keyword(key), it_s64_methods);
return 0;
return janet_getmethod(janet_unwrap_keyword(key), it_s64_methods, out);
}
static Janet it_u64_get(void *p, Janet key) {
static int it_u64_get(void *p, Janet key, Janet *out) {
(void) p;
if (!janet_checktype(key, JANET_KEYWORD))
janet_panicf("expected keyword, got %v", key);
return janet_getmethod(janet_unwrap_keyword(key), it_u64_methods);
return 0;
return janet_getmethod(janet_unwrap_keyword(key), it_u64_methods, out);
}
static const JanetReg it_cfuns[] = {

View File

@@ -53,7 +53,7 @@ struct IOFile {
};
static int cfun_io_gc(void *p, size_t len);
static Janet io_file_get(void *p, Janet);
static int io_file_get(void *p, Janet key, Janet *out);
JanetAbstractType cfun_io_filetype = {
"core/file",
@@ -221,27 +221,11 @@ static Janet cfun_io_fread(int32_t argc, Janet *argv) {
if (janet_checktype(argv[1], JANET_KEYWORD)) {
const uint8_t *sym = janet_unwrap_keyword(argv[1]);
if (!janet_cstrcmp(sym, "all")) {
/* Read whole file */
int status = fseek(iof->file, 0, SEEK_SET);
if (status) {
/* backwards fseek did not work (stream like popen) */
int32_t sizeBefore;
do {
sizeBefore = buffer->count;
read_chunk(iof, buffer, 1024);
} while (sizeBefore < buffer->count);
} else {
fseek(iof->file, 0, SEEK_END);
long fsize = ftell(iof->file);
if (fsize < 0) {
janet_panicf("could not get file size of %v", argv[0]);
}
if (fsize > (INT32_MAX)) {
janet_panic("file to large to read into buffer");
}
fseek(iof->file, 0, SEEK_SET);
read_chunk(iof, buffer, (int32_t) fsize);
}
int32_t sizeBefore;
do {
sizeBefore = buffer->count;
read_chunk(iof, buffer, 4096);
} while (sizeBefore < buffer->count);
/* Never return nil for :all */
return janet_wrap_buffer(buffer);
} else if (!janet_cstrcmp(sym, "line")) {
@@ -361,18 +345,19 @@ static Janet cfun_io_fseek(int32_t argc, Janet *argv) {
static JanetMethod io_file_methods[] = {
{"close", cfun_io_fclose},
{"read", cfun_io_fread},
{"write", cfun_io_fwrite},
{"fileno", cfun_io_fileno},
{"flush", cfun_io_fflush},
{"read", cfun_io_fread},
{"seek", cfun_io_fseek},
{"write", cfun_io_fwrite},
{NULL, NULL}
};
static Janet io_file_get(void *p, Janet key) {
static int io_file_get(void *p, Janet key, Janet *out) {
(void) p;
if (!janet_checktype(key, JANET_KEYWORD))
janet_panicf("expected keyword, got %v", key);
return janet_getmethod(janet_unwrap_keyword(key), io_file_methods);
return 0;
return janet_getmethod(janet_unwrap_keyword(key), io_file_methods, out);
}
FILE *janet_dynfile(const char *name, FILE *def) {
@@ -384,27 +369,215 @@ FILE *janet_dynfile(const char *name, FILE *def) {
return iofile->file;
}
static Janet cfun_io_print(int32_t argc, Janet *argv) {
FILE *f = janet_dynfile("out", stdout);
for (int32_t i = 0; i < argc; ++i) {
int32_t j, len;
const uint8_t *vstr = janet_to_string(argv[i]);
len = janet_string_length(vstr);
for (j = 0; j < len; ++j) {
putc(vstr[j], f);
static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
int newline, const char *name, FILE *dflt_file) {
FILE *f;
Janet x = janet_dyn(name);
switch (janet_type(x)) {
default:
/* Other values simply do nothing */
return janet_wrap_nil();
case JANET_BUFFER: {
/* Special case buffer */
JanetBuffer *buf = janet_unwrap_buffer(x);
for (int32_t i = 0; i < argc; ++i) {
janet_to_string_b(buf, argv[i]);
}
if (newline)
janet_buffer_push_u8(buf, '\n');
return janet_wrap_nil();
}
case JANET_NIL:
f = dflt_file;
break;
case JANET_ABSTRACT: {
void *abstract = janet_unwrap_abstract(x);
if (janet_abstract_type(abstract) != &cfun_io_filetype)
return janet_wrap_nil();
IOFile *iofile = abstract;
f = iofile->file;
break;
}
}
putc('\n', f);
for (int32_t i = 0; i < argc; ++i) {
int32_t len;
const uint8_t *vstr;
if (janet_checktype(argv[i], JANET_BUFFER)) {
JanetBuffer *b = janet_unwrap_buffer(argv[i]);
vstr = b->data;
len = b->count;
} else {
vstr = janet_to_string(argv[i]);
len = janet_string_length(vstr);
}
if (len) {
if (1 != fwrite(vstr, len, 1, f)) {
janet_panicf("could not print %d bytes to (dyn :%s)", len, name);
}
}
}
if (newline)
putc('\n', f);
return janet_wrap_nil();
}
static Janet cfun_io_print(int32_t argc, Janet *argv) {
return cfun_io_print_impl(argc, argv, 1, "out", stdout);
}
static Janet cfun_io_prin(int32_t argc, Janet *argv) {
return cfun_io_print_impl(argc, argv, 0, "out", stdout);
}
static Janet cfun_io_eprint(int32_t argc, Janet *argv) {
return cfun_io_print_impl(argc, argv, 1, "err", stderr);
}
static Janet cfun_io_eprin(int32_t argc, Janet *argv) {
return cfun_io_print_impl(argc, argv, 0, "err", stderr);
}
static Janet cfun_io_printf_impl(int32_t argc, Janet *argv, int newline,
const char *name, FILE *dflt_file) {
FILE *f;
janet_arity(argc, 1, -1);
const char *fmt = janet_getcstring(argv, 0);
Janet x = janet_dyn(name);
switch (janet_type(x)) {
default:
/* Other values simply do nothing */
return janet_wrap_nil();
case JANET_BUFFER: {
/* Special case buffer */
JanetBuffer *buf = janet_unwrap_buffer(x);
janet_buffer_format(buf, fmt, 0, argc, argv);
if (newline) janet_buffer_push_u8(buf, '\n');
return janet_wrap_nil();
}
case JANET_NIL:
f = dflt_file;
break;
case JANET_ABSTRACT: {
void *abstract = janet_unwrap_abstract(x);
if (janet_abstract_type(abstract) != &cfun_io_filetype)
return janet_wrap_nil();
IOFile *iofile = abstract;
f = iofile->file;
break;
}
}
JanetBuffer *buf = janet_buffer(10);
janet_buffer_format(buf, fmt, 0, argc, argv);
if (newline) janet_buffer_push_u8(buf, '\n');
if (buf->count) {
if (1 != fwrite(buf->data, buf->count, 1, f)) {
janet_panicf("could not print %d bytes to file", buf->count, name);
}
}
/* Clear buffer to make things easier for GC */
buf->count = 0;
buf->capacity = 0;
free(buf->data);
buf->data = NULL;
return janet_wrap_nil();
}
static Janet cfun_io_printf(int32_t argc, Janet *argv) {
return cfun_io_printf_impl(argc, argv, 1, "out", stdout);
}
static Janet cfun_io_prinf(int32_t argc, Janet *argv) {
return cfun_io_printf_impl(argc, argv, 0, "out", stdout);
}
static Janet cfun_io_eprintf(int32_t argc, Janet *argv) {
return cfun_io_printf_impl(argc, argv, 1, "err", stderr);
}
static Janet cfun_io_eprinf(int32_t argc, Janet *argv) {
return cfun_io_printf_impl(argc, argv, 0, "err", stderr);
}
void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...) {
va_list args;
va_start(args, format);
Janet x = janet_dyn(name);
JanetType xtype = janet_type(x);
switch (xtype) {
default:
/* Other values simply do nothing */
break;
case JANET_NIL:
case JANET_ABSTRACT: {
FILE *f = dflt_file;
JanetBuffer buffer;
int32_t len = 0;
while (format[len]) len++;
janet_buffer_init(&buffer, len);
janet_formatb(&buffer, format, args);
if (xtype == JANET_ABSTRACT) {
void *abstract = janet_unwrap_abstract(x);
if (janet_abstract_type(abstract) != &cfun_io_filetype)
break;
IOFile *iofile = abstract;
f = iofile->file;
}
fwrite(buffer.data, buffer.count, 1, f);
janet_buffer_deinit(&buffer);
break;
}
case JANET_BUFFER:
janet_formatb(janet_unwrap_buffer(x), format, args);
break;
}
va_end(args);
return;
}
static const JanetReg io_cfuns[] = {
{
"print", cfun_io_print,
JDOC("(print & xs)\n\n"
"Print values to the console (standard out). Value are converted "
"to strings if they are not already. After printing all values, a "
"newline character is printed. Returns nil.")
"newline character is printed. Use the value of (dyn :out stdout) to determine "
"what to push characters to. Expects (dyn :out stdout) to be either a core/file or "
"a buffer. Returns nil.")
},
{
"prin", cfun_io_prin,
JDOC("(prin & xs)\n\n"
"Same as print, but does not add trailing newline.")
},
{
"printf", cfun_io_printf,
JDOC("(printf fmt & xs)\n\n"
"Prints output formatted as if with (string/format fmt ;xs) to (dyn :out stdout) with a trailing newline.")
},
{
"prinf", cfun_io_prinf,
JDOC("(prinf fmt & xs)\n\n"
"Like printf but with no trailing newline.")
},
{
"eprin", cfun_io_eprin,
JDOC("(eprin & xs)\n\n"
"Same as prin, but uses (dyn :err stderr) instead of (dyn :out stdout).")
},
{
"eprint", cfun_io_eprint,
JDOC("(eprint & xs)\n\n"
"Same as print, but uses (dyn :err stderr) instead of (dyn :out stdout).")
},
{
"eprintf", cfun_io_eprintf,
JDOC("(eprintf fmt & xs)\n\n"
"Prints output formatted as if with (string/format fmt ;xs) to (dyn :err stderr) with a trailing newline.")
},
{
"eprinf", cfun_io_eprinf,
JDOC("(eprinf fmt & xs)\n\n"
"Like eprintf but with no trailing newline.")
},
{
"file/open", cfun_io_fopen,

View File

@@ -338,6 +338,13 @@ void janet_marshal_janet(JanetMarshalContext *ctx, Janet x) {
marshal_one(st, x, ctx->flags + 1);
}
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++));
}
#define MARK_SEEN() \
janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++))
@@ -345,11 +352,9 @@ static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
void *abstract = janet_unwrap_abstract(x);
const JanetAbstractType *at = janet_abstract_type(abstract);
if (at->marshal) {
JanetMarshalContext context = {st, NULL, flags, NULL};
pushbyte(st, LB_ABSTRACT);
marshal_one(st, janet_csymbolv(at->name), flags + 1);
push64(st, (uint64_t) janet_abstract_size(abstract));
MARK_SEEN();
JanetMarshalContext context = {st, NULL, flags, NULL, at};
at->marshal(abstract, &context);
} else {
janet_panicf("try to marshal unregistered abstract type, cannot marshal %p", x);
@@ -983,6 +988,11 @@ static const uint8_t *unmarshal_one_fiber(
return data;
}
void janet_unmarshal_ensure(JanetMarshalContext *ctx, size_t size) {
UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
MARSH_EOS(st, ctx->data + size);
}
int32_t janet_unmarshal_int(JanetMarshalContext *ctx) {
UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
return readint(st, &(ctx->data));
@@ -1017,17 +1027,28 @@ Janet janet_unmarshal_janet(JanetMarshalContext *ctx) {
return ret;
}
void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
if (ctx->at == NULL) {
janet_panicf("janet_unmarshal_abstract called more than once");
}
void *p = janet_abstract(ctx->at, size);
janet_v_push(st->lookup, janet_wrap_abstract(p));
ctx->at = NULL;
return p;
}
static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *data, Janet *out, int flags) {
Janet key;
data = unmarshal_one(st, data, &key, flags + 1);
const JanetAbstractType *at = janet_get_abstract_type(key);
if (at == NULL) return NULL;
if (at->unmarshal) {
void *p = janet_abstract(at, (size_t) read64(st, &data));
*out = janet_wrap_abstract(p);
JanetMarshalContext context = {NULL, st, flags, data};
janet_v_push(st->lookup, *out);
at->unmarshal(p, &context);
JanetMarshalContext context = {NULL, st, flags, data, at};
*out = janet_wrap_abstract(at->unmarshal(&context));
if (context.at != NULL) {
janet_panicf("janet_unmarshal_abstract not called");
}
return context.data;
}
return NULL;

View File

@@ -27,19 +27,198 @@
#include "util.h"
#endif
static JANET_THREAD_LOCAL JanetRNG janet_vm_rng = {0, 0, 0, 0, 0};
static int janet_rng_get(void *p, Janet key, Janet *out);
static void janet_rng_marshal(void *p, JanetMarshalContext *ctx) {
JanetRNG *rng = (JanetRNG *)p;
janet_marshal_abstract(ctx, p);
janet_marshal_int(ctx, (int32_t) rng->a);
janet_marshal_int(ctx, (int32_t) rng->b);
janet_marshal_int(ctx, (int32_t) rng->c);
janet_marshal_int(ctx, (int32_t) rng->d);
janet_marshal_int(ctx, (int32_t) rng->counter);
}
static void *janet_rng_unmarshal(JanetMarshalContext *ctx) {
JanetRNG *rng = janet_unmarshal_abstract(ctx, sizeof(JanetRNG));
rng->a = (uint32_t) janet_unmarshal_int(ctx);
rng->b = (uint32_t) janet_unmarshal_int(ctx);
rng->c = (uint32_t) janet_unmarshal_int(ctx);
rng->d = (uint32_t) janet_unmarshal_int(ctx);
rng->counter = (uint32_t) janet_unmarshal_int(ctx);
return rng;
}
static JanetAbstractType JanetRNG_type = {
"core/rng",
NULL,
NULL,
janet_rng_get,
NULL,
janet_rng_marshal,
janet_rng_unmarshal,
NULL
};
JanetRNG *janet_default_rng(void) {
return &janet_vm_rng;
}
void janet_rng_seed(JanetRNG *rng, uint32_t seed) {
rng->a = seed;
rng->b = 0x97654321u;
rng->c = 123871873u;
rng->d = 0xf23f56c8u;
rng->counter = 0u;
/* First several numbers aren't that random. */
for (int i = 0; i < 16; i++) janet_rng_u32(rng);
}
void janet_rng_longseed(JanetRNG *rng, const uint8_t *bytes, int32_t len) {
uint8_t state[16] = {0};
for (int32_t i = 0; i < len; i++)
state[i & 0xF] ^= bytes[i];
rng->a = state[0] + (state[1] << 8) + (state[2] << 16) + (state[3] << 24);
rng->b = state[4] + (state[5] << 8) + (state[6] << 16) + (state[7] << 24);
rng->c = state[8] + (state[9] << 8) + (state[10] << 16) + (state[11] << 24);
rng->d = state[12] + (state[13] << 8) + (state[14] << 16) + (state[15] << 24);
rng->counter = 0u;
/* a, b, c, d can't all be 0 */
if (rng->a == 0) rng->a = 1u;
for (int i = 0; i < 16; i++) janet_rng_u32(rng);
}
uint32_t janet_rng_u32(JanetRNG *rng) {
/* Algorithm "xorwow" from p. 5 of Marsaglia, "Xorshift RNGs" */
uint32_t t = rng->d;
uint32_t const s = rng->a;
rng->d = rng->c;
rng->c = rng->b;
rng->b = s;
t ^= t >> 2;
t ^= t << 1;
t ^= s ^ (s << 4);
rng->a = t;
rng->counter += 362437;
return t + rng->counter;
}
double janet_rng_double(JanetRNG *rng) {
uint32_t hi = janet_rng_u32(rng);
uint32_t lo = janet_rng_u32(rng);
uint64_t big = (uint64_t)(lo) | (((uint64_t) hi) << 32);
return ldexp((double)(big >> (64 - 52)), -52);
}
static Janet cfun_rng_make(int32_t argc, Janet *argv) {
janet_arity(argc, 0, 1);
JanetRNG *rng = janet_abstract(&JanetRNG_type, sizeof(JanetRNG));
if (argc == 1) {
if (janet_checkint(argv[0])) {
uint32_t seed = (uint32_t)(janet_getinteger(argv, 0));
janet_rng_seed(rng, seed);
} else {
JanetByteView bytes = janet_getbytes(argv, 0);
janet_rng_longseed(rng, bytes.bytes, bytes.len);
}
} else {
janet_rng_seed(rng, 0);
}
return janet_wrap_abstract(rng);
}
static Janet cfun_rng_uniform(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
JanetRNG *rng = janet_getabstract(argv, 0, &JanetRNG_type);
return janet_wrap_number(janet_rng_double(rng));
}
static Janet cfun_rng_int(int32_t argc, Janet *argv) {
janet_arity(argc, 1, 2);
JanetRNG *rng = janet_getabstract(argv, 0, &JanetRNG_type);
if (argc == 1) {
uint32_t word = janet_rng_u32(rng) >> 1;
return janet_wrap_integer(word);
} else {
int32_t max = janet_optnat(argv, argc, 1, INT32_MAX);
if (max == 0) return janet_wrap_number(0.0);
uint32_t modulo = (uint32_t) max;
uint32_t maxgen = INT32_MAX;
uint32_t maxword = maxgen - (maxgen % modulo);
uint32_t word;
do {
word = janet_rng_u32(rng) >> 1;
} while (word > maxword);
return janet_wrap_integer(word % modulo);
}
}
static void rng_get_4bytes(JanetRNG *rng, uint8_t *buf) {
uint32_t word = janet_rng_u32(rng);
buf[0] = word & 0xFF;
buf[1] = (word >> 8) & 0xFF;
buf[2] = (word >> 16) & 0xFF;
buf[3] = (word >> 24) & 0xFF;
}
static Janet cfun_rng_buffer(int32_t argc, Janet *argv) {
janet_arity(argc, 2, 3);
JanetRNG *rng = janet_getabstract(argv, 0, &JanetRNG_type);
int32_t n = janet_getnat(argv, 1);
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, n);
/* Split into first part (that is divisible by 4), and rest */
int32_t first_part = n & ~3;
int32_t second_part = n - first_part;
/* Get first part in chunks of 4 bytes */
janet_buffer_extra(buffer, n);
uint8_t *buf = buffer->data + buffer->count;
for (int32_t i = 0; i < first_part; i += 4) rng_get_4bytes(rng, buf + i);
buffer->count += first_part;
/* Get remaining 0 - 3 bytes */
if (second_part) {
uint8_t wordbuf[4] = {0};
rng_get_4bytes(rng, wordbuf);
janet_buffer_push_bytes(buffer, wordbuf, second_part);
}
return janet_wrap_buffer(buffer);
}
static const JanetMethod rng_methods[] = {
{"uniform", cfun_rng_uniform},
{"int", cfun_rng_int},
{"buffer", cfun_rng_buffer},
{NULL, NULL}
};
static int janet_rng_get(void *p, Janet key, Janet *out) {
(void) p;
if (!janet_checktype(key, JANET_KEYWORD)) return 0;
return janet_getmethod(janet_unwrap_keyword(key), rng_methods, out);
}
/* Get a random number */
static Janet janet_rand(int32_t argc, Janet *argv) {
(void) argv;
janet_fixarity(argc, 0);
double r = (rand() % RAND_MAX) / ((double) RAND_MAX);
return janet_wrap_number(r);
return janet_wrap_number(janet_rng_double(&janet_vm_rng));
}
/* Seed the random number generator */
static Janet janet_srand(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
int32_t x = janet_getinteger(argv, 0);
srand((unsigned) x);
if (janet_checkint(argv[0])) {
uint32_t seed = (uint32_t)(janet_getinteger(argv, 0));
janet_rng_seed(&janet_vm_rng, seed);
} else {
JanetByteView bytes = janet_getbytes(argv, 0);
janet_rng_longseed(&janet_vm_rng, bytes.bytes, bytes.len);
}
return janet_wrap_nil();
}
@@ -62,17 +241,26 @@ JANET_DEFINE_MATHOP(asin, asin)
JANET_DEFINE_MATHOP(atan, atan)
JANET_DEFINE_MATHOP(cos, cos)
JANET_DEFINE_MATHOP(cosh, cosh)
JANET_DEFINE_MATHOP(acosh, acosh)
JANET_DEFINE_MATHOP(sin, sin)
JANET_DEFINE_MATHOP(sinh, sinh)
JANET_DEFINE_MATHOP(asinh, asinh)
JANET_DEFINE_MATHOP(tan, tan)
JANET_DEFINE_MATHOP(tanh, tanh)
JANET_DEFINE_MATHOP(atanh, atanh)
JANET_DEFINE_MATHOP(exp, exp)
JANET_DEFINE_MATHOP(exp2, exp2)
JANET_DEFINE_MATHOP(expm1, expm1)
JANET_DEFINE_MATHOP(log, log)
JANET_DEFINE_MATHOP(log10, log10)
JANET_DEFINE_MATHOP(log2, log2)
JANET_DEFINE_MATHOP(sqrt, sqrt)
JANET_DEFINE_MATHOP(cbrt, cbrt)
JANET_DEFINE_MATHOP(ceil, ceil)
JANET_DEFINE_MATHOP(fabs, fabs)
JANET_DEFINE_MATHOP(floor, floor)
JANET_DEFINE_MATHOP(trunc, trunc)
JANET_DEFINE_MATHOP(round, round)
#define JANET_DEFINE_MATH2OP(name, fop)\
static Janet janet_##name(int32_t argc, Janet *argv) {\
@@ -84,6 +272,7 @@ static Janet janet_##name(int32_t argc, Janet *argv) {\
JANET_DEFINE_MATH2OP(atan2, atan2)
JANET_DEFINE_MATH2OP(pow, pow)
JANET_DEFINE_MATH2OP(hypot, hypot)
static Janet janet_not(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
@@ -108,8 +297,8 @@ static const JanetReg math_cfuns[] = {
{
"math/seedrandom", janet_srand,
JDOC("(math/seedrandom seed)\n\n"
"Set the seed for the random number generator. 'seed' should be an "
"an integer.")
"Set the seed for the random number generator. seed should be "
"an integer or a buffer.")
},
{
"math/cos", janet_cos,
@@ -156,11 +345,21 @@ static const JanetReg math_cfuns[] = {
JDOC("(math/log10 x)\n\n"
"Returns log base 10 of x.")
},
{
"math/log2", janet_log2,
JDOC("(math/log2 x)\n\n"
"Returns log base 2 of x.")
},
{
"math/sqrt", janet_sqrt,
JDOC("(math/sqrt x)\n\n"
"Returns the square root of x.")
},
{
"math/cbrt", janet_cbrt,
JDOC("(math/cbrt x)\n\n"
"Returns the cube root of x.")
},
{
"math/floor", janet_floor,
JDOC("(math/floor x)\n\n"
@@ -196,17 +395,82 @@ static const JanetReg math_cfuns[] = {
JDOC("(math/tanh x)\n\n"
"Return the hyperbolic tangent of x.")
},
{
"math/atanh", janet_atanh,
JDOC("(math/atanh x)\n\n"
"Return the hyperbolic arctangent of x.")
},
{
"math/asinh", janet_asinh,
JDOC("(math/asinh x)\n\n"
"Return the hyperbolic arcsine of x.")
},
{
"math/acosh", janet_acosh,
JDOC("(math/acosh x)\n\n"
"Return the hyperbolic arccosine of x.")
},
{
"math/atan2", janet_atan2,
JDOC("(math/atan2 y x)\n\n"
"Return the arctangent of y/x. Works even when x is 0.")
},
{
"math/rng", cfun_rng_make,
JDOC("(math/rng &opt seed)\n\n"
"Creates a Psuedo-Random number generator, with an optional seed. "
"The seed should be an unsigned 32 bit integer. "
"Do not use this for cryptography. Returns a core/rng abstract type.")
},
{
"math/rng-uniform", cfun_rng_uniform,
JDOC("(math/rng-seed rng seed)\n\n"
"Extract a random number in the range [0, 1) from the RNG.")
},
{
"math/rng-int", cfun_rng_int,
JDOC("(math/rng-int rng &opt max)\n\n"
"Extract a random random integer in the range [0, max] from the RNG. If "
"no max is given, the default is 2^31 - 1.")
},
{
"math/rng-buffer", cfun_rng_buffer,
JDOC("(math/rng-buffer rng n &opt buf)\n\n"
"Get n random bytes and put them in a buffer. Creates a new buffer if no buffer is "
"provided, otherwise appends to the given buffer. Returns the buffer.")
},
{
"math/hypot", janet_hypot,
JDOC("(math/hypot a b)\n\n"
"Returns the c from the equation c^2 = a^2 + b^2")
},
{
"math/exp2", janet_exp2,
JDOC("(math/exp2 x)\n\n"
"Returns 2 to the power of x.")
},
{
"math/expm1", janet_expm1,
JDOC("(math/expm1 x)\n\n"
"Returns e to the power of x minus 1.")
},
{
"math/trunc", janet_trunc,
JDOC("(math/trunc x)\n\n"
"Returns the integer between x and 0 nearest to x.")
},
{
"math/round", janet_round,
JDOC("(math/round x)\n\n"
"Returns the integer nearest to x.")
},
{NULL, NULL, NULL}
};
/* Module entry point */
void janet_lib_math(JanetTable *env) {
janet_core_cfuns(env, NULL, math_cfuns);
janet_register_abstract_type(&JanetRNG_type);
#ifdef JANET_BOOTSTRAP
janet_def(env, "math/pi", janet_wrap_number(3.1415926535897931),
JDOC("The value pi."));
@@ -214,5 +478,7 @@ void janet_lib_math(JanetTable *env) {
JDOC("The base of the natural log."));
janet_def(env, "math/inf", janet_wrap_number(INFINITY),
JDOC("The number representing positive infinity"));
janet_def(env, "math/-inf", janet_wrap_number(-INFINITY),
JDOC("The number representing negative infinity"));
#endif
}

View File

@@ -25,8 +25,6 @@
#include "util.h"
#endif
#include <stdlib.h>
#ifndef JANET_REDUCED_OS
#include <time.h>
@@ -36,6 +34,8 @@
#include <string.h>
#include <sys/stat.h>
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
#ifdef JANET_WINDOWS
#include <windows.h>
#include <direct.h>
@@ -372,6 +372,30 @@ static Janet os_shell(int32_t argc, Janet *argv) {
: janet_wrap_boolean(stat);
}
static Janet os_environ(int32_t argc, Janet *argv) {
(void) argv;
janet_fixarity(argc, 0);
int32_t nenv = 0;
char **env = environ;
while (*env++)
nenv += 1;
JanetTable *t = janet_table(nenv);
for (int32_t i = 0; i < nenv; i++) {
char *e = environ[i];
char *eq = strchr(e, '=');
if (!eq) janet_panic("no '=' in environ");
char *v = eq + 1;
int32_t full_len = (int32_t) strlen(e);
int32_t val_len = (int32_t) strlen(v);
janet_table_put(
t,
janet_stringv((const uint8_t *)e, full_len - val_len - 1),
janet_stringv((const uint8_t *)v, val_len)
);
}
return janet_wrap_table(t);
}
static Janet os_getenv(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
const char *cstr = janet_getcstring(argv, 0);
@@ -409,9 +433,10 @@ static Janet os_time(int32_t argc, Janet *argv) {
/* Clock shims */
#ifdef JANET_WINDOWS
static int gettime(struct timespec *spec) {
int64_t wintime = 0LL;
GetSystemTimeAsFileTime((FILETIME *)&wintime);
/* Windows epoch is January 1, 1601 apparently*/
FILETIME ftime;
GetSystemTimeAsFileTime(&ftime);
int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
/* Windows epoch is January 1, 1601 apparently */
wintime -= 116444736000000000LL;
spec->tv_sec = wintime / 10000000LL;
/* Resolution is 100 nanoseconds. */
@@ -449,12 +474,13 @@ static Janet os_sleep(int32_t argc, Janet *argv) {
#ifdef JANET_WINDOWS
Sleep((DWORD)(delay * 1000));
#else
int rc;
struct timespec ts;
ts.tv_sec = (time_t) delay;
ts.tv_nsec = (delay <= UINT32_MAX)
? (long)((delay - ((uint32_t)delay)) * 1000000000)
: 0;
nanosleep(&ts, NULL);
RETRY_EINTR(rc, nanosleep(&ts, &ts));
#endif
return janet_wrap_nil();
}
@@ -473,23 +499,96 @@ static Janet os_cwd(int32_t argc, Janet *argv) {
return janet_cstringv(ptr);
}
static Janet os_cryptorand(int32_t argc, Janet *argv) {
JanetBuffer *buffer;
const char *genericerr = "unable to get sufficient random data";
janet_arity(argc, 1, 2);
int32_t offset;
int32_t n = janet_getinteger(argv, 0);
if (n < 0) janet_panic("expected positive integer");
if (argc == 2) {
buffer = janet_getbuffer(argv, 1);
offset = buffer->count;
} else {
offset = 0;
buffer = janet_buffer(n);
}
/* We could optimize here by adding setcount_uninit */
janet_buffer_setcount(buffer, offset + n);
#ifdef JANET_WINDOWS
for (int32_t i = offset; i < buffer->count; i += sizeof(unsigned int)) {
unsigned int v;
if (rand_s(&v))
janet_panic(genericerr);
for (int32_t j = 0; (j < sizeof(unsigned int)) && (i + j < buffer->count); j++) {
buffer->data[i + j] = v & 0xff;
v = v >> 8;
}
}
#elif defined(__linux__) || defined(__APPLE__)
/* We should be able to call getrandom on linux, but it doesn't seem
to be uniformly supported on linux distros. Macos may support
arc4random_buf, but it needs investigation.
In both cases, use this fallback path for now... */
int rc;
int randfd;
RETRY_EINTR(randfd, open("/dev/urandom", O_RDONLY));
if (randfd < 0)
janet_panic(genericerr);
while (n > 0) {
ssize_t nread;
RETRY_EINTR(nread, read(randfd, buffer->data + offset, n));
if (nread <= 0) {
RETRY_EINTR(rc, close(randfd));
janet_panic(genericerr);
}
offset += nread;
n -= nread;
}
RETRY_EINTR(rc, close(randfd));
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
(void) errmsg;
arc4random_buf(buffer->data + offset, n);
#else
janet_panic("cryptorand currently unsupported on this platform");
#endif
return janet_wrap_buffer(buffer);
}
static Janet os_date(int32_t argc, Janet *argv) {
janet_arity(argc, 0, 1);
janet_arity(argc, 0, 2);
(void) argv;
time_t t;
struct tm t_infos;
struct tm *t_info;
struct tm *t_info = NULL;
if (argc) {
t = (time_t) janet_getinteger64(argv, 0);
int64_t integer = janet_getinteger64(argv, 0);
if (integer < 0)
janet_panicf("expected non-negative 64 bit signed integer, got %v", argv[0]);
t = (time_t) integer;
} else {
time(&t);
}
if (argc >= 2 && janet_truthy(argv[2])) {
/* local time */
#ifdef JANET_WINDOWS
localtime_s(&t_infos, &t);
t_info = &t_infos;
localtime_s(&t_infos, &t);
t_info = &t_infos;
#else
t_info = localtime_r(&t, &t_infos);
tzset();
t_info = localtime_r(&t, &t_infos);
#endif
} else {
/* utc time */
#ifdef JANET_WINDOWS
gmtime_s(&t_infos, &t);
t_info = &t_infos;
#else
t_info = gmtime_r(&t, &t_infos);
#endif
}
JanetKV *st = janet_struct_begin(9);
janet_struct_put(st, janet_ckeywordv("seconds"), janet_wrap_number(t_info->tm_sec));
janet_struct_put(st, janet_ckeywordv("minutes"), janet_wrap_number(t_info->tm_min));
@@ -514,7 +613,7 @@ static Janet os_link(int32_t argc, Janet *argv) {
const char *oldpath = janet_getcstring(argv, 0);
const char *newpath = janet_getcstring(argv, 1);
int res = ((argc == 3 && janet_getboolean(argv, 2)) ? symlink : link)(oldpath, newpath);
if (res == -1) janet_panic(strerror(errno));
if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath);
return janet_wrap_integer(res);
#endif
}
@@ -538,7 +637,7 @@ static Janet os_rmdir(int32_t argc, Janet *argv) {
#else
int res = rmdir(path);
#endif
if (res == -1) janet_panic(strerror(errno));
if (-1 == res) janet_panicf("%s: %s", strerror(errno), path);
return janet_wrap_nil();
}
@@ -550,7 +649,7 @@ static Janet os_cd(int32_t argc, Janet *argv) {
#else
int res = chdir(path);
#endif
if (res == -1) janet_panic(strerror(errno));
if (-1 == res) janet_panicf("%s: %s", strerror(errno), path);
return janet_wrap_nil();
}
@@ -578,7 +677,7 @@ static Janet os_remove(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
const char *path = janet_getcstring(argv, 0);
int status = remove(path);
if (-1 == status) janet_panic(strerror(errno));
if (-1 == status) janet_panicf("%s: %s", strerror(errno), path);
return janet_wrap_nil();
}
@@ -816,6 +915,11 @@ static const JanetReg os_cfuns[] = {
"\t:netbsd\n"
"\t:posix - A POSIX compatible system (default)")
},
{
"os/environ", os_environ,
JDOC("(os/environ)\n\n"
"Get a copy of the os environment table.")
},
{
"os/getenv", os_getenv,
JDOC("(os/getenv variable)\n\n"
@@ -937,11 +1041,18 @@ static const JanetReg os_cfuns[] = {
JDOC("(os/cwd)\n\n"
"Returns the current working directory.")
},
{
"os/cryptorand", os_cryptorand,
JDOC("(os/cryptorand n &opt buf)\n\n"
"Get or append n bytes of good quality random data provided by the os. Returns a new buffer or buf.")
},
{
"os/date", os_date,
JDOC("(os/date &opt time)\n\n"
JDOC("(os/date &opt time local)\n\n"
"Returns the given time as a date struct, or the current time if no time is given. "
"Returns a struct with following key values. Note that all numbers are 0-indexed.\n\n"
"Returns a struct with following key values. Note that all numbers are 0-indexed. "
"Date is given in UTC unless local is truthy, in which case the date is formated for "
"the local timezone.\n\n"
"\t:seconds - number of seconds [0-61]\n"
"\t:minutes - number of minutes [0-59]\n"
"\t:hours - number of hours [0-23]\n"

View File

@@ -38,7 +38,7 @@ static int is_whitespace(uint8_t c) {
/* Code generated by tools/symcharsgen.c.
* The table contains 256 bits, where each bit is 1
* if the corresponding ascci code is a symbol char, and 0
* if the corresponding ascii code is a symbol char, and 0
* if not. The upper characters are also considered symbol
* chars and are then checked for utf-8 compliance. */
static const uint32_t symchars[8] = {
@@ -233,7 +233,7 @@ static int escapeh(JanetParser *p, JanetParseState *state, uint8_t c) {
p->error = "invalid hex digit in hex escape";
return 1;
}
state->argn = (state->argn << 4) + digit;;
state->argn = (state->argn << 4) + digit;
state->counter--;
if (!state->counter) {
push_buf(p, (state->argn & 0xFF));
@@ -329,6 +329,12 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
int start_dig = p->buf[0] >= '0' && p->buf[0] <= '9';
int start_num = start_dig || p->buf[0] == '-' || p->buf[0] == '+' || p->buf[0] == '.';
if (p->buf[0] == ':') {
/* Don't do full utf-8 check unless we have seen non ascii characters. */
int valid = (!state->argn) || valid_utf8(p->buf + 1, blen - 1);
if (!valid) {
p->error = "invalid utf-8 in keyword";
return 0;
}
ret = janet_keywordv(p->buf + 1, blen - 1);
} else if (start_num && !janet_scan_number(p->buf, blen, &numval)) {
ret = janet_wrap_number(numval);
@@ -338,7 +344,7 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
ret = janet_wrap_false();
} else if (!check_str_const("true", p->buf, blen)) {
ret = janet_wrap_true();
} else if (p->buf) {
} else {
if (start_dig) {
p->error = "symbol literal cannot start with a digit";
return 0;
@@ -351,9 +357,6 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
}
ret = janet_symbolv(p->buf, blen);
}
} else {
p->error = "empty symbol invalid";
return 0;
}
p->bufcount = 0;
popstate(p, ret);
@@ -727,7 +730,7 @@ static int parsergc(void *p, size_t size) {
return 0;
}
static Janet parserget(void *p, Janet key);
static int parserget(void *p, Janet key, Janet *out);
static JanetAbstractType janet_parse_parsertype = {
"core/parser",
@@ -1052,10 +1055,10 @@ static const JanetMethod parser_methods[] = {
{NULL, NULL}
};
static Janet parserget(void *p, Janet key) {
static int parserget(void *p, Janet key, Janet *out) {
(void) p;
if (!janet_checktype(key, JANET_KEYWORD)) janet_panicf("expected keyword method");
return janet_getmethod(janet_unwrap_keyword(key), parser_methods);
if (!janet_checktype(key, JANET_KEYWORD)) return 0;
return janet_getmethod(janet_unwrap_keyword(key), parser_methods, out);
}
static const JanetReg parse_cfuns[] = {

View File

@@ -888,7 +888,7 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
for (; i > 0 && janet_checktype(peg, JANET_KEYWORD); --i) {
peg = janet_table_get_ex(grammar, peg, &grammar);
if (!grammar || janet_checktype(peg, JANET_NIL))
peg_panic(b, "unkown rule");
peg_panic(b, "unknown rule");
b->form = peg;
b->grammar = grammar;
}
@@ -1017,6 +1017,7 @@ static void peg_marshal(void *p, JanetMarshalContext *ctx) {
Peg *peg = (Peg *)p;
janet_marshal_size(ctx, peg->bytecode_len);
janet_marshal_int(ctx, (int32_t)peg->num_constants);
janet_marshal_abstract(ctx, p);
for (size_t i = 0; i < peg->bytecode_len; i++)
janet_marshal_int(ctx, (int32_t) peg->bytecode[i]);
for (uint32_t j = 0; j < peg->num_constants; j++)
@@ -1030,25 +1031,28 @@ static size_t size_padded(size_t offset, size_t size) {
return x - (x % size);
}
static void peg_unmarshal(void *p, JanetMarshalContext *ctx) {
char *mem = p;
Peg *peg = (Peg *)p;
peg->bytecode_len = janet_unmarshal_size(ctx);
peg->num_constants = (uint32_t) janet_unmarshal_int(ctx);
static void *peg_unmarshal(JanetMarshalContext *ctx) {
size_t bytecode_len = janet_unmarshal_size(ctx);
uint32_t num_constants = (uint32_t) janet_unmarshal_int(ctx);
/* Calculate offsets. Should match those in make_peg */
size_t bytecode_start = size_padded(sizeof(Peg), sizeof(uint32_t));
size_t bytecode_size = peg->bytecode_len * sizeof(uint32_t);
size_t bytecode_size = bytecode_len * sizeof(uint32_t);
size_t constants_start = size_padded(bytecode_start + bytecode_size, sizeof(Janet));
size_t total_size = constants_start + sizeof(Janet) * num_constants;
/* DOS prevention? I.E. we could read bytecode and constants before
* hand so we don't allocated a ton of memory on bad, short input */
/* Allocate PEG */
char *mem = janet_unmarshal_abstract(ctx, total_size);
Peg *peg = (Peg *)mem;
uint32_t *bytecode = (uint32_t *)(mem + bytecode_start);
Janet *constants = (Janet *)(mem + constants_start);
peg->bytecode = NULL;
peg->constants = NULL;
/* Ensure not too large */
if (constants_start + sizeof(Janet) * peg->num_constants > janet_abstract_size(p)) {
janet_panic("size mismatch");
}
peg->bytecode_len = bytecode_len;
peg->num_constants = num_constants;
for (size_t i = 0; i < peg->bytecode_len; i++)
bytecode[i] = (uint32_t) janet_unmarshal_int(ctx);
@@ -1176,7 +1180,7 @@ static void peg_unmarshal(void *p, JanetMarshalContext *ctx) {
peg->bytecode = bytecode;
peg->constants = constants;
free(op_flags);
return;
return peg;
bad:
free(op_flags);

View File

@@ -37,7 +37,12 @@
static void number_to_string_b(JanetBuffer *buffer, double x) {
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%g", x);
/* Use int32_t range for valid integers because that is the
* range most integer-expecting functions in the C api use. */
const char *fmt = (x == floor(x) &&
x <= ((double) INT32_MAX) &&
x >= ((double) INT32_MIN)) ? "%.0f" : "%g";
int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, fmt, x);
buffer->count += count;
}
@@ -253,11 +258,13 @@ void janet_to_string_b(JanetBuffer *buffer, Janet x) {
default:
janet_description_b(buffer, x);
break;
case JANET_BUFFER:
janet_buffer_push_bytes(buffer,
janet_unwrap_buffer(x)->data,
janet_unwrap_buffer(x)->count);
case JANET_BUFFER: {
JanetBuffer *to = janet_unwrap_buffer(x);
/* Prevent resizing buffer while appending */
if (buffer == to) janet_buffer_extra(buffer, to->count);
janet_buffer_push_bytes(buffer, to->data, to->count);
break;
}
case JANET_STRING:
case JANET_SYMBOL:
case JANET_KEYWORD:

View File

@@ -28,7 +28,6 @@
/* Run a string */
int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out) {
JanetParser parser;
FILE *errf = janet_dynfile("err", stderr);
int errflags = 0, done = 0;
int32_t index = 0;
Janet ret = janet_wrap_nil();
@@ -56,8 +55,13 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
done = 1;
}
} else {
fprintf(errf, "compile error in %s: %s\n", sourcePath,
(const char *)cres.error);
if (cres.macrofiber) {
janet_eprintf("compile error in %s: ", sourcePath);
janet_stacktrace(cres.macrofiber, janet_wrap_string(cres.error));
} else {
janet_eprintf("compile error in %s: %s\n", sourcePath,
(const char *)cres.error);
}
errflags |= 0x02;
done = 1;
}
@@ -70,8 +74,8 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
break;
case JANET_PARSE_ERROR:
errflags |= 0x04;
fprintf(errf, "parse error in %s: %s\n",
sourcePath, janet_parser_error(&parser));
janet_eprintf("parse error in %s: %s\n",
sourcePath, janet_parser_error(&parser));
done = 1;
break;
case JANET_PARSE_PENDING:

View File

@@ -55,7 +55,11 @@ static JanetSlot qq_slots(JanetFopts opts, JanetSlot *slots, int makeop) {
return target;
}
static JanetSlot quasiquote(JanetFopts opts, Janet x) {
static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
if (depth == 0) {
janetc_cerror(opts.compiler, "quasiquote too deeply nested");
return janetc_cslot(janet_wrap_nil());
}
JanetSlot *slots = NULL;
switch (janet_type(x)) {
default:
@@ -66,11 +70,18 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x) {
len = janet_tuple_length(tup);
if (len > 1 && janet_checktype(tup[0], JANET_SYMBOL)) {
const uint8_t *head = janet_unwrap_symbol(tup[0]);
if (!janet_cstrcmp(head, "unquote"))
return janetc_value(janetc_fopts_default(opts.compiler), tup[1]);
if (!janet_cstrcmp(head, "unquote")) {
if (level == 0) {
return janetc_value(janetc_fopts_default(opts.compiler), tup[1]);
} else {
level--;
}
} else if (!janet_cstrcmp(head, "quasiquote")) {
level++;
}
}
for (i = 0; i < len; i++)
janet_v_push(slots, quasiquote(opts, tup[i]));
janet_v_push(slots, quasiquote(opts, tup[i], depth - 1, level));
return qq_slots(opts, slots, (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR)
? JOP_MAKE_BRACKET_TUPLE
: JOP_MAKE_TUPLE);
@@ -79,7 +90,7 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x) {
int32_t i;
JanetArray *array = janet_unwrap_array(x);
for (i = 0; i < array->count; i++)
janet_v_push(slots, quasiquote(opts, array->data[i]));
janet_v_push(slots, quasiquote(opts, array->data[i], depth - 1, level));
return qq_slots(opts, slots, JOP_MAKE_ARRAY);
}
case JANET_TABLE:
@@ -88,8 +99,8 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x) {
int32_t len, cap = 0;
janet_dictionary_view(x, &kvs, &len, &cap);
while ((kv = janet_dictionary_next(kvs, cap, kv))) {
JanetSlot key = quasiquote(opts, kv->key);
JanetSlot value = quasiquote(opts, kv->value);
JanetSlot key = quasiquote(opts, kv->key, depth - 1, level);
JanetSlot value = quasiquote(opts, kv->value, depth - 1, level);
key.flags &= ~JANET_SLOT_SPLICED;
value.flags &= ~JANET_SLOT_SPLICED;
janet_v_push(slots, key);
@@ -106,7 +117,7 @@ static JanetSlot janetc_quasiquote(JanetFopts opts, int32_t argn, const Janet *a
janetc_cerror(opts.compiler, "expected 1 argument");
return janetc_cslot(janet_wrap_nil());
}
return quasiquote(opts, argv[0]);
return quasiquote(opts, argv[0], JANET_RECURSION_GUARD, 0);
}
static JanetSlot janetc_unquote(JanetFopts opts, int32_t argn, const Janet *argv) {
@@ -146,7 +157,7 @@ static int destructure(JanetCompiler *c,
janetc_emit_ssu(c, JOP_GET_INDEX, nextright, right, (uint8_t) i, 1);
} else {
JanetSlot k = janetc_cslot(janet_wrap_integer(i));
janetc_emit_sss(c, JOP_GET, nextright, right, k, 1);
janetc_emit_sss(c, JOP_IN, nextright, right, k, 1);
}
if (destructure(c, subval, nextright, leaf, attr))
janetc_freeslot(c, nextright);
@@ -162,7 +173,7 @@ static int destructure(JanetCompiler *c,
if (janet_checktype(kvs[i].key, JANET_NIL)) continue;
JanetSlot nextright = janetc_farslot(c);
JanetSlot k = janetc_value(janetc_fopts_default(c), kvs[i].key);
janetc_emit_sss(c, JOP_GET, nextright, right, k, 1);
janetc_emit_sss(c, JOP_IN, nextright, right, k, 1);
if (destructure(c, kvs[i].value, nextright, leaf, attr))
janetc_freeslot(c, nextright);
}

View File

@@ -32,6 +32,9 @@
* be in it. However, thread local global variables for interpreter
* state should allow easy multi-threading. */
/* Cache the core environment */
extern JANET_THREAD_LOCAL JanetTable *janet_vm_core_env;
/* How many VM stacks have been entered */
extern JANET_THREAD_LOCAL int janet_vm_stackn;
@@ -70,4 +73,10 @@ extern JANET_THREAD_LOCAL void **janet_scratch_mem;
extern JANET_THREAD_LOCAL size_t janet_scratch_cap;
extern JANET_THREAD_LOCAL size_t janet_scratch_len;
/* Setup / teardown */
#ifdef JANET_THREADS
void janet_threads_init(void);
void janet_threads_deinit(void);
#endif
#endif /* JANET_STATE_H_defined */

View File

@@ -104,13 +104,13 @@ static void kmp_init(
struct kmp_state *s,
const uint8_t *text, int32_t textlen,
const uint8_t *pat, int32_t patlen) {
if (patlen == 0) {
janet_panic("expected non-empty pattern");
}
int32_t *lookup = calloc(patlen, sizeof(int32_t));
if (!lookup) {
JANET_OUT_OF_MEMORY;
}
if (patlen == 0) {
janet_panic("expected non-empty pattern");
}
s->lookup = lookup;
s->i = 0;
s->j = 0;
@@ -170,8 +170,8 @@ static int32_t kmp_next(struct kmp_state *state) {
/* CFuns */
static Janet cfun_string_slice(int32_t argc, Janet *argv) {
JanetRange range = janet_getslice(argc, argv);
JanetByteView view = janet_getbytes(argv, 0);
JanetRange range = janet_getslice(argc, argv);
return janet_stringv(view.bytes + range.start, range.end - range.start);
}
@@ -404,7 +404,6 @@ static Janet cfun_string_checkset(int32_t argc, Janet *argv) {
bitset[index] |= mask;
}
/* Check set */
if (str.len == 0) return janet_wrap_false();
for (int32_t i = 0; i < str.len; i++) {
int index = str.bytes[i] >> 5;
uint32_t mask = 1 << (str.bytes[i] & 0x1F);
@@ -500,6 +499,8 @@ static Janet cfun_string_trim(int32_t argc, Janet *argv) {
trim_help_args(argc, argv, &str, &set);
int32_t left_edge = trim_help_leftedge(str, set);
int32_t right_edge = trim_help_rightedge(str, set);
if (right_edge < left_edge)
return janet_stringv(NULL, 0);
return janet_stringv(str.bytes + left_edge, right_edge - left_edge);
}
@@ -524,7 +525,8 @@ static const JanetReg string_cfuns[] = {
"Returns a substring from a byte sequence. The substring is from "
"index start inclusive to index end exclusive. All indexing "
"is from 0. 'start' and 'end' can also be negative to indicate indexing "
"from the end of the string.")
"from the end of the string. Note that index -1 is synonymous with "
"index (length bytes) to allow a full negative slice range. ")
},
{
"string/repeat", cfun_string_repeat,
@@ -611,8 +613,9 @@ static const JanetReg string_cfuns[] = {
{
"string/check-set", cfun_string_checkset,
JDOC("(string/check-set set str)\n\n"
"Checks if any of the bytes in the string set appear in the string str. "
"Returns true if some bytes in set do appear in str, false if no bytes do.")
"Checks that the string str only contains bytes that appear in the string set. "
"Returns true if all bytes in str appear in set, false if some bytes in str do "
"not appear in set.")
},
{
"string/join", cfun_string_join,

View File

@@ -196,7 +196,7 @@ static double bignat_extract(struct BigNat *mant, int32_t exponent2) {
/* Read in a mantissa and exponent of a certain base, and give
* back the double value. Should properly handle 0s, infinities, and
* denormalized numbers. (When the exponent values are too large) */
* denormalized numbers. (When the exponent values are too large or small) */
static double convert(
int negative,
struct BigNat *mant,
@@ -205,11 +205,20 @@ static double convert(
int32_t exponent2 = 0;
/* Short circuit zero and huge numbers */
/* Approximate exponent in base 2 of mant and exponent. This should get us a good estimate of the final size of the
* number, within * 2^32 or so. */
int32_t mant_exp2_approx = mant->n * 32 + 16;
int32_t exp_exp2_approx = (int32_t)(floor(log2(base) * exponent));
int32_t exp2_approx = mant_exp2_approx + exp_exp2_approx;
/* Short circuit zero, huge, and small numbers. We use the exponent range of valid IEEE754 doubles (-1022, 1023)
* with a healthy buffer to allow for inaccuracies in the approximation and denormailzed numbers. */
if (mant->n == 0 && mant->first_digit == 0)
return negative ? -0.0 : 0.0;
if (exponent > 1023)
if (exp2_approx > 1176)
return negative ? -INFINITY : INFINITY;
if (exp2_approx < -1175)
return negative ? -0.0 : 0.0;
/* Final value is X = mant * base ^ exponent * 2 ^ exponent2
* Get exponent to zero while holding X constant. */
@@ -326,7 +335,7 @@ int janet_scan_number(
/* Read exponent */
if (str < end && foundexp) {
int eneg = 0;
int ee = 0;
int32_t ee = 0;
seenadigit = 0;
str++;
if (str >= end) goto error;
@@ -341,10 +350,12 @@ int janet_scan_number(
str++;
seenadigit = 1;
}
while (str < end && ee < (INT32_MAX / 40)) {
while (str < end) {
int digit = digit_lookup[*str & 0x7F];
if (*str > 127 || digit >= base) goto error;
ee = base * ee + digit;
if (ee < (INT32_MAX / 40)) {
ee = base * ee + digit;
}
str++;
seenadigit = 1;
}

650
src/core/thread.c Normal file
View File

@@ -0,0 +1,650 @@
/*
* Copyright (c) 2019 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
* 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.
*/
#ifndef JANET_AMALG
#include <janet.h>
#include "gc.h"
#include "util.h"
#include "state.h"
#endif
#ifdef JANET_THREADS
#ifdef JANET_WINDOWS
#include <windows.h>
#else
#include <setjmp.h>
#include <time.h>
#include <pthread.h>
#endif
/* typedefed in janet.h */
struct JanetMailbox {
/* Synchronization */
#ifdef JANET_WINDOWS
CRITICAL_SECTION lock;
CONDITION_VARIABLE cond;
#else
pthread_mutex_t lock;
pthread_cond_t cond;
#endif
/* Receiving messages - (only by owner thread) */
JanetTable *decode;
/* Setup procedure - requires a parent mailbox
* to receive thunk from */
JanetMailbox *parent;
/* Memory management - reference counting */
int refCount;
int closed;
/* Store messages */
uint16_t messageCapacity;
uint16_t messageCount;
uint16_t messageFirst;
uint16_t messageNext;
/* Buffers to store messages. These buffers are manually allocated, so
* are not owned by any thread's GC. */
JanetBuffer messages[];
};
static JANET_THREAD_LOCAL JanetMailbox *janet_vm_mailbox = NULL;
static JANET_THREAD_LOCAL JanetThread *janet_vm_thread_current = NULL;
static JanetMailbox *janet_mailbox_create(JanetMailbox *parent, int refCount, uint16_t capacity) {
JanetMailbox *mailbox = malloc(sizeof(JanetMailbox) + sizeof(JanetBuffer) * capacity);
if (NULL == mailbox) {
JANET_OUT_OF_MEMORY;
}
#ifdef JANET_WINDOWS
InitializeCriticalSection(&mailbox->lock);
InitializeConditionVariable(&mailbox->cond);
#else
pthread_mutex_init(&mailbox->lock, NULL);
pthread_cond_init(&mailbox->cond, NULL);
#endif
mailbox->refCount = refCount;
mailbox->closed = 0;
mailbox->parent = parent;
mailbox->messageCount = 0;
mailbox->messageCapacity = capacity;
mailbox->messageFirst = 0;
mailbox->messageNext = 0;
for (uint16_t i = 0; i < capacity; i++) {
janet_buffer_init(mailbox->messages + i, 0);
}
return mailbox;
}
static void janet_mailbox_destroy(JanetMailbox *mailbox) {
#ifdef JANET_WINDOWS
DeleteCriticalSection(&mailbox->lock);
#else
pthread_mutex_destroy(&mailbox->lock);
pthread_cond_destroy(&mailbox->cond);
#endif
for (uint16_t i = 0; i < mailbox->messageCapacity; i++) {
janet_buffer_deinit(mailbox->messages + i);
}
free(mailbox);
}
static void janet_mailbox_lock(JanetMailbox *mailbox) {
#ifdef JANET_WINDOWS
EnterCriticalSection(&mailbox->lock);
#else
pthread_mutex_lock(&mailbox->lock);
#endif
}
static void janet_mailbox_unlock(JanetMailbox *mailbox) {
#ifdef JANET_WINDOWS
LeaveCriticalSection(&mailbox->lock);
#else
pthread_mutex_unlock(&mailbox->lock);
#endif
}
/* Assumes you have the mailbox lock already */
static void janet_mailbox_ref_with_lock(JanetMailbox *mailbox, int delta) {
mailbox->refCount += delta;
if (mailbox->refCount <= 0) {
janet_mailbox_unlock(mailbox);
janet_mailbox_destroy(mailbox);
} else {
janet_mailbox_unlock(mailbox);
}
}
static void janet_mailbox_ref(JanetMailbox *mailbox, int delta) {
janet_mailbox_lock(mailbox);
janet_mailbox_ref_with_lock(mailbox, delta);
}
static void janet_close_thread(JanetThread *thread) {
if (thread->mailbox) {
janet_mailbox_ref(thread->mailbox, -1);
thread->mailbox = NULL;
}
}
static int thread_gc(void *p, size_t size) {
(void) size;
JanetThread *thread = (JanetThread *)p;
janet_close_thread(thread);
return 0;
}
static int thread_mark(void *p, size_t size) {
(void) size;
JanetThread *thread = (JanetThread *)p;
if (thread->encode) {
janet_mark(janet_wrap_table(thread->encode));
}
return 0;
}
/* Abstract waiting for timeout across windows/posix */
typedef struct {
int timedwait;
int nowait;
#ifdef JANET_WINDOWS
DWORD interval;
DWORD ticksLeft;
#else
struct timespec ts;
#endif
} JanetWaiter;
static void janet_waiter_init(JanetWaiter *waiter, double sec) {
waiter->timedwait = 0;
waiter->nowait = 0;
if (sec == 0.0 || isnan(sec)) {
waiter->nowait = 1;
return;
}
waiter->timedwait = sec > 0.0;
/* Set maximum wait time to 30 days */
if (sec > (60.0 * 60.0 * 24.0 * 30.0)) {
sec = 60.0 * 60.0 * 24.0 * 30.0;
}
#ifdef JANET_WINDOWS
if (waiter->timedwait) {
waiter->ticksLeft = waiter->interval = (DWORD) floor(1000.0 * sec);
}
#else
if (waiter->timedwait) {
/* N seconds -> timespec of (now + sec) */
struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
time_t tvsec = (time_t) floor(sec);
long tvnsec = (long) floor(1000000000.0 * (sec - ((double) tvsec)));
tvsec += now.tv_sec;
tvnsec += now.tv_nsec;
if (tvnsec >= 1000000000L) {
tvnsec -= 1000000000L;
tvsec += 1;
}
waiter->ts.tv_sec = tvsec;
waiter->ts.tv_nsec = tvnsec;
}
#endif
}
static int janet_waiter_wait(JanetWaiter *wait, JanetMailbox *mailbox) {
if (wait->nowait) return 1;
#ifdef JANET_WINDOWS
if (wait->timedwait) {
if (wait->ticksLeft == 0) return 1;
DWORD startTime = GetTickCount();
int status = !SleepConditionVariableCS(&mailbox->cond, &mailbox->lock, wait->ticksLeft);
DWORD dTick = GetTickCount() - startTime;
/* Be careful about underflow */
wait->ticksLeft = dTick > wait->ticksLeft ? 0 : dTick;
return status;
} else {
SleepConditionVariableCS(&mailbox->cond, &mailbox->lock, INFINITE);
return 0;
}
#else
if (wait->timedwait) {
return pthread_cond_timedwait(&mailbox->cond, &mailbox->lock, &wait->ts);
} else {
pthread_cond_wait(&mailbox->cond, &mailbox->lock);
return 0;
}
#endif
}
static void janet_mailbox_wakeup(JanetMailbox *mailbox) {
#ifdef JANET_WINDOWS
WakeConditionVariable(&mailbox->cond);
#else
pthread_cond_signal(&mailbox->cond);
#endif
}
static int mailbox_at_capacity(JanetMailbox *mailbox) {
return mailbox->messageCount >= mailbox->messageCapacity;
}
/* Returns 1 if could not send (encode error or timeout), 2 for mailbox closed, and
* 0 otherwise. Will not panic. */
int janet_thread_send(JanetThread *thread, Janet msg, double timeout) {
/* Ensure mailbox is not closed. */
JanetMailbox *mailbox = thread->mailbox;
if (NULL == mailbox) return 2;
janet_mailbox_lock(mailbox);
if (mailbox->closed) {
janet_mailbox_ref_with_lock(mailbox, -1);
thread->mailbox = NULL;
return 2;
}
/* Back pressure */
if (mailbox_at_capacity(mailbox)) {
JanetWaiter wait;
janet_waiter_init(&wait, timeout);
if (wait.nowait) {
janet_mailbox_unlock(mailbox);
return 1;
}
/* Retry loop, as there can be multiple writers */
while (mailbox_at_capacity(mailbox)) {
if (janet_waiter_wait(&wait, mailbox)) {
janet_mailbox_unlock(mailbox);
janet_mailbox_wakeup(mailbox);
return 1;
}
}
}
/* Hack to capture all panics from marshalling. This works because
* we know janet_marshal won't mess with other essential global state. */
jmp_buf buf;
jmp_buf *old_buf = janet_vm_jmp_buf;
janet_vm_jmp_buf = &buf;
int32_t oldmcount = mailbox->messageCount;
int ret = 0;
if (setjmp(buf)) {
ret = 1;
mailbox->messageCount = oldmcount;
} else {
JanetBuffer *msgbuf = mailbox->messages + mailbox->messageNext;
msgbuf->count = 0;
/* Start panic zone */
janet_marshal(msgbuf, msg, thread->encode, 0);
/* End panic zone */
mailbox->messageNext = (mailbox->messageNext + 1) % mailbox->messageCapacity;
mailbox->messageCount++;
}
/* Cleanup */
janet_vm_jmp_buf = old_buf;
janet_mailbox_unlock(mailbox);
/* Potentially wake up a blocked thread */
janet_mailbox_wakeup(mailbox);
return ret;
}
/* Returns 0 on successful message. Returns 1 if timedout */
int janet_thread_receive(Janet *msg_out, double timeout) {
JanetMailbox *mailbox = janet_vm_mailbox;
janet_mailbox_lock(mailbox);
/* For timeouts */
JanetWaiter wait;
janet_waiter_init(&wait, timeout);
for (;;) {
/* Check for messages waiting for us */
if (mailbox->messageCount > 0) {
/* Hack to capture all panics from marshalling. This works because
* we know janet_marshal won't mess with other essential global state. */
jmp_buf buf;
jmp_buf *old_buf = janet_vm_jmp_buf;
janet_vm_jmp_buf = &buf;
/* Handle errors */
if (setjmp(buf)) {
/* Cleanup jmp_buf, keep lock */
janet_vm_jmp_buf = old_buf;
} else {
JanetBuffer *msgbuf = mailbox->messages + mailbox->messageFirst;
mailbox->messageCount--;
mailbox->messageFirst = (mailbox->messageFirst + 1) % mailbox->messageCapacity;
/* Read from beginning of channel */
const uint8_t *nextItem = NULL;
Janet item = janet_unmarshal(
msgbuf->data, msgbuf->count,
0, mailbox->decode, &nextItem);
*msg_out = item;
/* Cleanup */
janet_vm_jmp_buf = old_buf;
janet_mailbox_unlock(mailbox);
/* Potentially wake up pending threads */
janet_mailbox_wakeup(mailbox);
return 0;
}
}
if (wait.nowait || mailbox->refCount <= 1) {
janet_mailbox_unlock(mailbox);
return 1;
}
/* Wait for next message */
if (janet_waiter_wait(&wait, mailbox)) {
janet_mailbox_unlock(mailbox);
return 1;
}
}
}
static int janet_thread_getter(void *p, Janet key, Janet *out);
static JanetAbstractType Thread_AT = {
"core/thread",
thread_gc,
thread_mark,
janet_thread_getter,
NULL,
NULL,
NULL,
NULL
};
static JanetThread *janet_make_thread(JanetMailbox *mailbox, JanetTable *encode) {
JanetThread *thread = janet_abstract(&Thread_AT, sizeof(JanetThread));
thread->mailbox = mailbox;
thread->encode = encode;
return thread;
}
JanetThread *janet_getthread(const Janet *argv, int32_t n) {
return (JanetThread *) janet_getabstract(argv, n, &Thread_AT);
}
static JanetTable *janet_get_core_table(const char *name) {
JanetTable *env = janet_core_env(NULL);
Janet out = janet_wrap_nil();
JanetBindingType bt = janet_resolve(env, janet_csymbol(name), &out);
if (bt == JANET_BINDING_NONE) return NULL;
if (!janet_checktype(out, JANET_TABLE)) return NULL;
return janet_unwrap_table(out);
}
/* Runs in new thread */
static int thread_worker(JanetMailbox *mailbox) {
JanetFiber *fiber = NULL;
Janet out;
/* Use the mailbox we were given */
janet_vm_mailbox = mailbox;
/* Init VM */
janet_init();
/* Get dictionaries for default encode/decode */
JanetTable *encode = janet_get_core_table("make-image-dict");
mailbox->decode = janet_get_core_table("load-image-dict");
/* Create parent thread */
JanetThread *parent = janet_make_thread(mailbox->parent, encode);
janet_mailbox_ref(mailbox->parent, -1);
mailbox->parent = NULL; /* only used to create the thread */
Janet parentv = janet_wrap_abstract(parent);
/* Unmarshal the function */
Janet funcv;
int status = janet_thread_receive(&funcv, -1.0);
if (status) goto error;
if (!janet_checktype(funcv, JANET_FUNCTION)) goto error;
JanetFunction *func = janet_unwrap_function(funcv);
/* Arity check */
if (func->def->min_arity > 1 || func->def->max_arity < 1) {
goto error;
}
/* Call function */
Janet argv[1] = { parentv };
fiber = janet_fiber(func, 64, 1, argv);
JanetSignal sig = janet_continue(fiber, janet_wrap_nil(), &out);
if (sig != JANET_SIGNAL_OK) {
janet_eprintf("in thread %v: ", janet_wrap_abstract(janet_make_thread(mailbox, encode)));
janet_stacktrace(fiber, out);
}
/* Normal exit */
janet_deinit();
return 0;
/* Fail to set something up */
error:
janet_eprintf("\nthread failed to start\n");
janet_deinit();
return 1;
}
#ifdef JANET_WINDOWS
static DWORD janet_create_thread_wrapper(void *param) {
thread_worker((JanetMailbox *)param);
return 0;
}
static int janet_thread_start_child(JanetThread *thread) {
HANDLE handle = CreateThread(NULL, 0, janet_create_thread_wrapper, thread->mailbox, 0, NULL);
int ret = NULL == handle;
/* Does not kill thread, simply detatches */
if (!ret) CloseHandle(handle);
return ret;
}
#else
static void *janet_pthread_wrapper(void *param) {
thread_worker((JanetMailbox *)param);
return NULL;
}
static int janet_thread_start_child(JanetThread *thread) {
pthread_t handle;
int error = pthread_create(&handle, NULL, janet_pthread_wrapper, thread->mailbox);
if (error) {
return 1;
} else {
pthread_detach(handle);
return 0;
}
}
#endif
/*
* Setup/Teardown
*/
void janet_threads_init(void) {
if (NULL == janet_vm_mailbox) {
janet_vm_mailbox = janet_mailbox_create(NULL, 1, 10);
}
}
void janet_threads_deinit(void) {
janet_mailbox_lock(janet_vm_mailbox);
janet_vm_mailbox->closed = 1;
janet_mailbox_ref_with_lock(janet_vm_mailbox, -1);
janet_vm_mailbox = NULL;
janet_vm_thread_current = NULL;
}
/*
* Cfuns
*/
static Janet cfun_thread_current(int32_t argc, Janet *argv) {
(void) argv;
janet_fixarity(argc, 0);
if (NULL == janet_vm_thread_current) {
janet_vm_thread_current = janet_make_thread(janet_vm_mailbox, janet_get_core_table("make-image-dict"));
janet_mailbox_ref(janet_vm_mailbox, 1);
janet_gcroot(janet_wrap_abstract(janet_vm_thread_current));
}
return janet_wrap_abstract(janet_vm_thread_current);
}
static Janet cfun_thread_new(int32_t argc, Janet *argv) {
janet_arity(argc, 0, 1);
int32_t cap = janet_optinteger(argv, argc, 0, 10);
if (cap < 1 || cap > UINT16_MAX) {
janet_panicf("bad slot #1, expected integer in range [1, 65535], got %d", cap);
}
JanetTable *encode = janet_get_core_table("make-image-dict");
JanetMailbox *mailbox = janet_mailbox_create(janet_vm_mailbox, 2, (uint16_t) cap);
/* one for created thread, one for ->parent reference in new mailbox */
janet_mailbox_ref(janet_vm_mailbox, 2);
JanetThread *thread = janet_make_thread(mailbox, encode);
if (janet_thread_start_child(thread)) {
janet_mailbox_ref(mailbox, -1); /* mailbox reference */
janet_mailbox_ref(janet_vm_mailbox, -1); /* ->parent reference */
janet_panic("could not start thread");
}
return janet_wrap_abstract(thread);
}
static Janet cfun_thread_send(int32_t argc, Janet *argv) {
janet_arity(argc, 2, 3);
JanetThread *thread = janet_getthread(argv, 0);
int status = janet_thread_send(thread, argv[1], janet_optnumber(argv, argc, 2, 1.0));
switch (status) {
default:
break;
case 1:
janet_panicf("failed to send message %v", argv[1]);
case 2:
janet_panic("thread mailbox is closed");
}
return argv[0];
}
static Janet cfun_thread_receive(int32_t argc, Janet *argv) {
janet_arity(argc, 0, 1);
double wait = janet_optnumber(argv, argc, 0, 1.0);
Janet out;
int status = janet_thread_receive(&out, wait);
switch (status) {
default:
break;
case 1:
janet_panicf("timeout after %f seconds", wait);
}
return out;
}
static Janet cfun_thread_close(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
JanetThread *thread = janet_getthread(argv, 0);
janet_close_thread(thread);
return janet_wrap_nil();
}
static const JanetMethod janet_thread_methods[] = {
{"send", cfun_thread_send},
{"close", cfun_thread_close},
{NULL, NULL}
};
static int janet_thread_getter(void *p, Janet key, Janet *out) {
(void) p;
if (!janet_checktype(key, JANET_KEYWORD)) return 0;
return janet_getmethod(janet_unwrap_keyword(key), janet_thread_methods, out);
}
static const JanetReg threadlib_cfuns[] = {
{
"thread/current", cfun_thread_current,
JDOC("(thread/current)\n\n"
"Get the current running thread.")
},
{
"thread/new", cfun_thread_new,
JDOC("(thread/new &opt capacity)\n\n"
"Start a new thread. The thread will wait for a message containing the function used to start the thread, which should be passed to the thread "
"via thread/send. If capacity is provided, that is how many messages can be stored in the thread's mailbox before blocking senders. "
"The capacity must be between 1 and 65535 inclusive, and defaults to 10. "
"Returns a handle to the new thread.")
},
{
"thread/send", cfun_thread_send,
JDOC("(thread/send thread msg)\n\n"
"Send a message to the thread. This will never block and returns thread immediately. "
"Will throw an error if there is a problem sending the message.")
},
{
"thread/receive", cfun_thread_receive,
JDOC("(thread/receive &opt timeout)\n\n"
"Get a message sent to this thread. If timeout is provided, an error will be thrown after the timeout has elapsed but "
"no messages are received.")
},
{
"thread/close", cfun_thread_close,
JDOC("(thread/close thread)\n\n"
"Close a thread, unblocking it and ending communication with it. Note that closing "
"a thread is idempotent and does not cancel the thread's operation. Returns nil.")
},
{NULL, NULL, NULL}
};
/* Module entry point */
void janet_lib_thread(JanetTable *env) {
janet_core_cfuns(env, NULL, threadlib_cfuns);
janet_register_abstract_type(&Thread_AT);
}
#endif

View File

@@ -100,8 +100,8 @@ static Janet cfun_tuple_brackets(int32_t argc, Janet *argv) {
}
static Janet cfun_tuple_slice(int32_t argc, Janet *argv) {
JanetRange range = janet_getslice(argc, argv);
JanetView view = janet_getindexed(argv, 0);
JanetRange range = janet_getslice(argc, argv);
return janet_wrap_tuple(janet_tuple_n(view.items + range.start, range.end - range.start));
}
@@ -143,7 +143,10 @@ static const JanetReg tuple_cfuns[] = {
JDOC("(tuple/slice arrtup [,start=0 [,end=(length arrtup)]])\n\n"
"Take a sub sequence of an array or tuple from index start "
"inclusive to index end exclusive. If start or end are not provided, "
"they default to 0 and the length of arrtup respectively."
"they default to 0 and the length of arrtup respectively. "
"'start' and 'end' can also be negative to indicate indexing "
"from the end of the input. Note that index -1 is synonymous with "
"index '(length arrtup)' to allow a full negative slice range. "
"Returns the new tuple.")
},
{

View File

@@ -94,17 +94,20 @@ static int ta_buffer_gc(void *p, size_t s) {
static void ta_buffer_marshal(void *p, JanetMarshalContext *ctx) {
JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p;
janet_marshal_abstract(ctx, p);
janet_marshal_size(ctx, buf->size);
janet_marshal_int(ctx, buf->flags);
janet_marshal_bytes(ctx, buf->data, buf->size);
}
static void ta_buffer_unmarshal(void *p, JanetMarshalContext *ctx) {
JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p;
static void *ta_buffer_unmarshal(JanetMarshalContext *ctx) {
JanetTArrayBuffer *buf = janet_unmarshal_abstract(ctx, sizeof(JanetTArrayBuffer));
size_t size = janet_unmarshal_size(ctx);
int32_t flags = janet_unmarshal_int(ctx);
ta_buffer_init(buf, size);
buf->flags = janet_unmarshal_int(ctx);
buf->flags = flags;
janet_unmarshal_bytes(ctx, buf->data, size);
return buf;
}
static const JanetAbstractType ta_buffer_type = {
@@ -128,6 +131,7 @@ static int ta_mark(void *p, size_t s) {
static void ta_view_marshal(void *p, JanetMarshalContext *ctx) {
JanetTArrayView *view = (JanetTArrayView *)p;
size_t offset = (view->buffer->data - view->as.u8);
janet_marshal_abstract(ctx, p);
janet_marshal_size(ctx, view->size);
janet_marshal_size(ctx, view->stride);
janet_marshal_int(ctx, view->type);
@@ -135,11 +139,11 @@ static void ta_view_marshal(void *p, JanetMarshalContext *ctx) {
janet_marshal_janet(ctx, janet_wrap_abstract(view->buffer));
}
static void ta_view_unmarshal(void *p, JanetMarshalContext *ctx) {
JanetTArrayView *view = (JanetTArrayView *)p;
static void *ta_view_unmarshal(JanetMarshalContext *ctx) {
size_t offset;
int32_t atype;
Janet buffer;
JanetTArrayView *view = janet_unmarshal_abstract(ctx, sizeof(JanetTArrayView));
view->size = janet_unmarshal_size(ctx);
view->stride = janet_unmarshal_size(ctx);
atype = janet_unmarshal_int(ctx);
@@ -157,54 +161,55 @@ static void ta_view_unmarshal(void *p, JanetMarshalContext *ctx) {
if (view->buffer->size < buf_need_size)
janet_panic("bad typed array offset in marshalled data");
view->as.u8 = view->buffer->data + offset;
return view;
}
static JanetMethod tarray_view_methods[6];
static Janet ta_getter(void *p, Janet key) {
Janet value;
static int ta_getter(void *p, Janet key, Janet *out) {
size_t index, i;
JanetTArrayView *array = p;
if (janet_checktype(key, JANET_KEYWORD))
return janet_getmethod(janet_unwrap_keyword(key), tarray_view_methods);
if (janet_checktype(key, JANET_KEYWORD)) {
return janet_getmethod(janet_unwrap_keyword(key), tarray_view_methods, out);
}
if (!janet_checksize(key)) janet_panic("expected size as key");
index = (size_t) janet_unwrap_number(key);
i = index * array->stride;
if (index >= array->size) {
value = janet_wrap_nil();
return 0;
} else {
switch (array->type) {
case JANET_TARRAY_TYPE_U8:
value = janet_wrap_number(array->as.u8[i]);
*out = janet_wrap_number(array->as.u8[i]);
break;
case JANET_TARRAY_TYPE_S8:
value = janet_wrap_number(array->as.s8[i]);
*out = janet_wrap_number(array->as.s8[i]);
break;
case JANET_TARRAY_TYPE_U16:
value = janet_wrap_number(array->as.u16[i]);
*out = janet_wrap_number(array->as.u16[i]);
break;
case JANET_TARRAY_TYPE_S16:
value = janet_wrap_number(array->as.s16[i]);
*out = janet_wrap_number(array->as.s16[i]);
break;
case JANET_TARRAY_TYPE_U32:
value = janet_wrap_number(array->as.u32[i]);
*out = janet_wrap_number(array->as.u32[i]);
break;
case JANET_TARRAY_TYPE_S32:
value = janet_wrap_number(array->as.s32[i]);
*out = janet_wrap_number(array->as.s32[i]);
break;
#ifdef JANET_INT_TYPES
case JANET_TARRAY_TYPE_U64:
value = janet_wrap_u64(array->as.u64[i]);
*out = janet_wrap_u64(array->as.u64[i]);
break;
case JANET_TARRAY_TYPE_S64:
value = janet_wrap_s64(array->as.s64[i]);
*out = janet_wrap_s64(array->as.s64[i]);
break;
#endif
case JANET_TARRAY_TYPE_F32:
value = janet_wrap_number_safe(array->as.f32[i]);
*out = janet_wrap_number_safe(array->as.f32[i]);
break;
case JANET_TARRAY_TYPE_F64:
value = janet_wrap_number_safe(array->as.f64[i]);
*out = janet_wrap_number_safe(array->as.f64[i]);
break;
default:
janet_panicf("cannot get from typed array of type %s",
@@ -212,7 +217,7 @@ static Janet ta_getter(void *p, Janet key) {
break;
}
}
return value;
return 1;
}
static void ta_setter(void *p, Janet key, Janet value) {
@@ -445,7 +450,8 @@ static Janet cfun_typed_array_slice(int32_t argc, Janet *argv) {
JanetArray *array = janet_array(range.end - range.start);
if (array->data) {
for (int32_t i = range.start; i < range.end; i++) {
array->data[i - range.start] = ta_getter(src, janet_wrap_number(i));
if (!ta_getter(src, janet_wrap_number(i), &array->data[i - range.start]))
array->data[i - range.start] = janet_wrap_nil();
}
}
array->count = range.end - range.start;

View File

@@ -331,18 +331,9 @@ const JanetAbstractType *janet_get_abstract_type(Janet key) {
void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p) {
(void) p;
Janet key = janet_csymbolv(name);
Janet value;
/* During init, allow replacing core library cfunctions with values from
* the env. */
Janet check = janet_table_get(env, key);
if (janet_checktype(check, JANET_NIL)) {
value = x;
} else {
value = check;
}
janet_table_put(env, key, value);
if (janet_checktype(value, JANET_CFUNCTION)) {
janet_table_put(janet_vm_registry, value, key);
janet_table_put(env, key, x);
if (janet_checktype(x, JANET_CFUNCTION)) {
janet_table_put(janet_vm_registry, x, key);
}
}
@@ -379,6 +370,14 @@ JanetBindingType janet_resolve(JanetTable *env, const uint8_t *sym, Janet *out)
return JANET_BINDING_DEF;
}
/* Resolve a symbol in the core environment. */
Janet janet_resolve_core(const char *name) {
JanetTable *env = janet_core_env(NULL);
Janet out = janet_wrap_nil();
janet_resolve(env, janet_csymbol(name), &out);
return out;
}
/* Read both tuples and arrays as c pointers + int32_t length. Return 1 if the
* view can be constructed, 0 if an invalid type. */
int janet_indexed_view(Janet seq, const Janet **data, int32_t *len) {

View File

@@ -120,5 +120,8 @@ void janet_lib_typed_array(JanetTable *env);
#ifdef JANET_INT_TYPES
void janet_lib_inttypes(JanetTable *env);
#endif
#ifdef JANET_THREADS
void janet_lib_thread(JanetTable *env);
#endif
#endif

View File

@@ -145,8 +145,18 @@ int janet_compare(Janet x, Janet y) {
return (janet_type(x) < janet_type(y)) ? -1 : 1;
}
static int32_t getter_checkint(Janet key, int32_t max) {
if (!janet_checkint(key)) goto bad;
int32_t ret = janet_unwrap_integer(key);
if (ret < 0) goto bad;
if (ret >= max) goto bad;
return ret;
bad:
janet_panicf("expected integer key in range [0, %d), got %v", max, key);
}
/* Gets a value and returns. Can panic. */
Janet janet_get(Janet ds, Janet key) {
Janet janet_in(Janet ds, Janet key) {
Janet value;
switch (janet_type(ds)) {
default:
@@ -160,62 +170,35 @@ Janet janet_get(Janet ds, Janet key) {
break;
case JANET_ARRAY: {
JanetArray *array = janet_unwrap_array(ds);
int32_t index;
if (!janet_checkint(key))
janet_panic("expected integer key");
index = janet_unwrap_integer(key);
if (index < 0 || index >= array->count) {
value = janet_wrap_nil();
} else {
value = array->data[index];
}
int32_t index = getter_checkint(key, array->count);
value = array->data[index];
break;
}
case JANET_TUPLE: {
const Janet *tuple = janet_unwrap_tuple(ds);
int32_t index;
if (!janet_checkint(key))
janet_panic("expected integer key");
index = janet_unwrap_integer(key);
if (index < 0 || index >= janet_tuple_length(tuple)) {
value = janet_wrap_nil();
} else {
value = tuple[index];
}
int32_t len = janet_tuple_length(tuple);
value = tuple[getter_checkint(key, len)];
break;
}
case JANET_BUFFER: {
JanetBuffer *buffer = janet_unwrap_buffer(ds);
int32_t index;
if (!janet_checkint(key))
janet_panic("expected integer key");
index = janet_unwrap_integer(key);
if (index < 0 || index >= buffer->count) {
value = janet_wrap_nil();
} else {
value = janet_wrap_integer(buffer->data[index]);
}
int32_t index = getter_checkint(key, buffer->count);
value = janet_wrap_integer(buffer->data[index]);
break;
}
case JANET_STRING:
case JANET_SYMBOL:
case JANET_KEYWORD: {
const uint8_t *str = janet_unwrap_string(ds);
int32_t index;
if (!janet_checkint(key))
janet_panic("expected integer key");
index = janet_unwrap_integer(key);
if (index < 0 || index >= janet_string_length(str)) {
value = janet_wrap_nil();
} else {
value = janet_wrap_integer(str[index]);
}
int32_t index = getter_checkint(key, janet_string_length(str));
value = janet_wrap_integer(str[index]);
break;
}
case JANET_ABSTRACT: {
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
if (type->get) {
value = (type->get)(janet_unwrap_abstract(ds), key);
if (!(type->get)(janet_unwrap_abstract(ds), key, &value))
janet_panicf("key %v not found in %v ", key, ds);
} else {
janet_panicf("no getter for %v ", ds);
}
@@ -225,6 +208,60 @@ Janet janet_get(Janet ds, Janet key) {
return value;
}
Janet janet_get(Janet ds, Janet key) {
JanetType t = janet_type(ds);
switch (t) {
default:
return janet_wrap_nil();
case JANET_STRING:
case JANET_SYMBOL:
case JANET_KEYWORD: {
if (!janet_checkint(key)) return janet_wrap_nil();
int32_t index = janet_unwrap_integer(key);
if (index < 0) return janet_wrap_nil();
const uint8_t *str = janet_unwrap_string(ds);
if (index >= janet_string_length(str)) return janet_wrap_nil();
return janet_wrap_integer(str[index]);
}
case JANET_ABSTRACT: {
Janet value;
void *abst = janet_unwrap_abstract(ds);
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(abst);
if (!type->get) return janet_wrap_nil();
if ((type->get)(abst, key, &value))
return value;
return janet_wrap_nil();
}
case JANET_ARRAY:
case JANET_TUPLE:
case JANET_BUFFER: {
if (!janet_checkint(key)) return janet_wrap_nil();
int32_t index = janet_unwrap_integer(key);
if (index < 0) return janet_wrap_nil();
if (t == JANET_ARRAY) {
JanetArray *a = janet_unwrap_array(ds);
if (index >= a->count) return janet_wrap_nil();
return a->data[index];
} else if (t == JANET_BUFFER) {
JanetBuffer *b = janet_unwrap_buffer(ds);
if (index >= b->count) return janet_wrap_nil();
return janet_wrap_integer(b->data[index]);
} else {
const Janet *t = janet_unwrap_tuple(ds);
if (index >= janet_tuple_length(t)) return janet_wrap_nil();
return t[index];
}
}
case JANET_TABLE: {
return janet_table_get(janet_unwrap_table(ds), key);
}
case JANET_STRUCT: {
const JanetKV *st = janet_unwrap_struct(ds);
return janet_struct_get(st, key);
}
}
}
Janet janet_getindex(Janet ds, int32_t index) {
Janet value;
if (index < 0) janet_panic("expected non-negative index");
@@ -271,7 +308,8 @@ Janet janet_getindex(Janet ds, int32_t index) {
case JANET_ABSTRACT: {
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
if (type->get) {
value = (type->get)(janet_unwrap_abstract(ds), janet_wrap_integer(index));
if (!(type->get)(janet_unwrap_abstract(ds), janet_wrap_integer(index), &value))
value = janet_wrap_nil();
} else {
janet_panicf("no getter for %v ", ds);
}
@@ -356,7 +394,7 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
janet_buffer_ensure(buffer, index + 1, 2);
buffer->count = index + 1;
}
buffer->data[index] = janet_unwrap_integer(value);
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
break;
}
case JANET_TABLE: {
@@ -382,11 +420,8 @@ void janet_put(Janet ds, Janet key, Janet value) {
janet_panicf("expected %T, got %v",
JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds);
case JANET_ARRAY: {
int32_t index;
JanetArray *array = janet_unwrap_array(ds);
if (!janet_checkint(key)) janet_panicf("expected integer key, got %v", key);
index = janet_unwrap_integer(key);
if (index < 0 || index == INT32_MAX) janet_panicf("bad integer key, got %v", key);
int32_t index = getter_checkint(key, INT32_MAX - 1);
if (index >= array->count) {
janet_array_setcount(array, index + 1);
}
@@ -394,11 +429,8 @@ void janet_put(Janet ds, Janet key, Janet value) {
break;
}
case JANET_BUFFER: {
int32_t index;
JanetBuffer *buffer = janet_unwrap_buffer(ds);
if (!janet_checkint(key)) janet_panicf("expected integer key, got %v", key);
index = janet_unwrap_integer(key);
if (index < 0 || index == INT32_MAX) janet_panicf("bad integer key, got %v", key);
int32_t index = getter_checkint(key, INT32_MAX - 1);
if (!janet_checkint(value))
janet_panicf("can only put integers in buffers, got %v", value);
if (index >= buffer->count) {

View File

@@ -30,6 +30,7 @@
#endif
/* VM state */
JANET_THREAD_LOCAL JanetTable *janet_vm_core_env;
JANET_THREAD_LOCAL JanetTable *janet_vm_registry;
JANET_THREAD_LOCAL int janet_vm_stackn = 0;
JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber = NULL;
@@ -203,7 +204,7 @@ static Janet call_nonfn(JanetFiber *fiber, Janet callee) {
key = callee;
}
fiber->stacktop = fiber->stackstart;
return janet_get(ds, key);
return janet_in(ds, key);
}
/* Get a callable from a keyword method name and check ensure that it is valid. */
@@ -274,6 +275,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
&&label_JOP_RESUME,
&&label_JOP_SIGNAL,
&&label_JOP_PROPAGATE,
&&label_JOP_IN,
&&label_JOP_GET,
&&label_JOP_PUT,
&&label_JOP_GET_INDEX,
@@ -475,7 +477,6 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
&&label_unknown_op,
&&label_unknown_op,
&&label_unknown_op,
&&label_unknown_op,
&&label_unknown_op
};
#endif
@@ -490,21 +491,20 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
* waiting to be resumed. In those cases, use input and increment pc. We
* DO NOT use input when resuming a fiber that has been interrupted at a
* breakpoint. */
uint8_t first_opcode;
if (status != JANET_STATUS_NEW &&
((*pc & 0xFF) == JOP_SIGNAL ||
(*pc & 0xFF) == JOP_PROPAGATE ||
(*pc & 0xFF) == JOP_RESUME)) {
stack[A] = in;
pc++;
first_opcode = *pc & 0xFF;
} else if (status == JANET_STATUS_DEBUG) {
first_opcode = *pc & 0x7F;
} else {
first_opcode = *pc & 0xFF;
}
/* The first opcode to execute. If the first opcode has
* the breakpoint bit set and we were in the debug state, skip
* that first breakpoint. */
uint8_t first_opcode = (status == JANET_STATUS_DEBUG)
? (*pc & 0x7F)
: (*pc & 0xFF);
/* Main interpreter loop. Semantically is a switch on
* (*pc & 0xFF) inside of an infinite loop. */
VM_START();
@@ -894,7 +894,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
JanetFiber *f = janet_unwrap_fiber(fv);
JanetFiberStatus sub_status = janet_fiber_status(f);
if (sub_status > JANET_STATUS_USER9) {
vm_throw("cannot propagate from new or alive fiber");
vm_commit();
janet_panicf("cannot propagate from fiber with status :%s",
janet_status_names[sub_status]);
}
janet_vm_fiber->child = f;
vm_return((int) sub_status, stack[B]);
@@ -910,6 +912,11 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
janet_putindex(stack[A], C, stack[B]);
vm_checkgc_pcnext();
VM_OP(JOP_IN)
vm_commit();
stack[A] = janet_in(stack[B], stack[C]);
vm_pcnext();
VM_OP(JOP_GET)
vm_commit();
stack[A] = janet_get(stack[B], stack[C]);
@@ -949,8 +956,10 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
VM_OP(JOP_MAKE_TABLE) {
int32_t count = fiber->stacktop - fiber->stackstart;
Janet *mem = fiber->data + fiber->stackstart;
if (count & 1)
vm_throw("expected even number of arguments to table constructor");
if (count & 1) {
vm_commit();
janet_panicf("expected even number of arguments to table constructor, got %d", count);
}
JanetTable *table = janet_table(count / 2);
for (int32_t i = 0; i < count; i += 2)
janet_table_put(table, mem[i], mem[i + 1]);
@@ -962,8 +971,10 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
VM_OP(JOP_MAKE_STRUCT) {
int32_t count = fiber->stacktop - fiber->stackstart;
Janet *mem = fiber->data + fiber->stackstart;
if (count & 1)
vm_throw("expected even number of arguments to struct constructor");
if (count & 1) {
vm_commit();
janet_panicf("expected even number of arguments to struct constructor, got %d", count);
}
JanetKV *st = janet_struct_begin(count / 2);
for (int32_t i = 0; i < count; i += 2)
janet_struct_put(st, mem[i], mem[i + 1]);
@@ -999,6 +1010,68 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
VM_END()
}
/*
* Execute a single instruction in the fiber. Does this by inspecting
* the fiber, setting a breakpoint at the next instruction, executing, and
* reseting breakpoints to how they were prior. Yes, it's a bit hacky.
*/
JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out) {
/* No finished or currently alive fibers. */
JanetFiberStatus status = janet_fiber_status(fiber);
if (status == JANET_STATUS_ALIVE ||
status == JANET_STATUS_DEAD ||
status == JANET_STATUS_ERROR) {
janet_panicf("cannot step fiber with status :%s", janet_status_names[status]);
}
/* Get PC for setting breakpoints */
uint32_t *pc = janet_stack_frame(fiber->data + fiber->frame)->pc;
/* Check current opcode (sans debug flag). This tells us where the next or next two candidate
* instructions will be. Usually it's the next instruction in memory,
* but for branching instructions it is also the target of the branch. */
uint32_t *nexta = NULL, *nextb = NULL, olda = 0, oldb = 0;
/* Set temporary breakpoints */
switch (*pc & 0x7F) {
default:
nexta = pc + 1;
break;
/* These we just ignore for now. Supporting them means
* we could step into and out of functions (including JOP_CALL). */
case JOP_RETURN_NIL:
case JOP_RETURN:
case JOP_ERROR:
case JOP_TAILCALL:
break;
case JOP_JUMP:
nexta = pc + DS;
break;
case JOP_JUMP_IF:
case JOP_JUMP_IF_NOT:
nexta = pc + 1;
nextb = pc + ES;
break;
}
if (nexta) {
olda = *nexta;
*nexta |= 0x80;
}
if (nextb) {
oldb = *nextb;
*nextb |= 0x80;
}
/* Go */
JanetSignal signal = janet_continue(fiber, in, out);
/* Restore */
if (nexta) *nexta = olda;
if (nextb) *nextb = oldb;
return signal;
}
Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
/* Check entry conditions */
if (!janet_vm_fiber)
@@ -1045,7 +1118,9 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
if (old_status == JANET_STATUS_ALIVE ||
old_status == JANET_STATUS_DEAD ||
old_status == JANET_STATUS_ERROR) {
*out = janet_cstringv("cannot resume alive, dead, or errored fiber");
const uint8_t *str = janet_formatc("cannot resume fiber with status :%s",
janet_status_names[old_status]);
*out = janet_wrap_string(str);
return JANET_SIGNAL_ERROR;
}
@@ -1126,9 +1201,8 @@ Janet janet_mcall(const char *name, int32_t argc, Janet *argv) {
if (janet_checktype(argv[0], JANET_ABSTRACT)) {
void *abst = janet_unwrap_abstract(argv[0]);
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(abst);
if (!type->get)
if (!type->get || !(type->get)(abst, janet_ckeywordv(name), &method))
janet_panicf("abstract value %v does not implement :%s", argv[0], name);
method = (type->get)(abst, janet_ckeywordv(name));
} else if (janet_checktype(argv[0], JANET_TABLE)) {
JanetTable *table = janet_unwrap_table(argv[0]);
method = janet_table_get(table, janet_ckeywordv(name));
@@ -1171,6 +1245,14 @@ int janet_init(void) {
/* Initialize registry */
janet_vm_registry = janet_table(0);
janet_gcroot(janet_wrap_table(janet_vm_registry));
/* Core env */
janet_vm_core_env = NULL;
/* Seed RNG */
janet_rng_seed(janet_default_rng(), 0);
/* Threads */
#ifdef JANET_THREADS
janet_threads_init();
#endif
return 0;
}
@@ -1183,4 +1265,8 @@ void janet_deinit(void) {
janet_vm_root_count = 0;
janet_vm_root_capacity = 0;
janet_vm_registry = NULL;
janet_vm_core_env = NULL;
#ifdef JANET_THREADS
janet_threads_deinit();
#endif
}

View File

@@ -143,6 +143,11 @@ extern "C" {
#define JANET_INT_TYPES
#endif
/* Enable or disable threads */
#ifndef JANET_NO_THREADS
#define JANET_THREADS
#endif
/* How to export symbols */
#ifndef JANET_API
#ifdef JANET_WINDOWS
@@ -228,9 +233,13 @@ typedef struct {
/***** START SECTION TYPES *****/
#ifdef JANET_WINDOWS
// Must be defined before including stdlib.h
#define _CRT_RAND_S
#endif
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <setjmp.h>
#include <stddef.h>
@@ -316,8 +325,17 @@ typedef struct JanetView JanetView;
typedef struct JanetByteView JanetByteView;
typedef struct JanetDictView JanetDictView;
typedef struct JanetRange JanetRange;
typedef struct JanetRNG JanetRNG;
typedef Janet(*JanetCFunction)(int32_t argc, Janet *argv);
/* String and other aliased pointer types */
typedef const uint8_t *JanetString;
typedef const uint8_t *JanetSymbol;
typedef const uint8_t *JanetKeyword;
typedef const Janet *JanetTuple;
typedef const JanetKV *JanetStruct;
typedef void *JanetAbstract;
/* Basic types for all Janet Values */
typedef enum JanetType {
JANET_NUMBER,
@@ -812,8 +830,8 @@ struct JanetFuncDef {
/* Various debug information */
JanetSourceMapping *sourcemap;
const uint8_t *source;
const uint8_t *name;
JanetString source;
JanetString name;
int32_t flags;
int32_t slotcount; /* The amount of stack space required for the function */
@@ -881,6 +899,7 @@ typedef struct {
void *u_state;
int flags;
const uint8_t *data;
const JanetAbstractType *at;
} JanetMarshalContext;
/* Defines an abstract type */
@@ -888,10 +907,10 @@ struct JanetAbstractType {
const char *name;
int (*gc)(void *data, size_t len);
int (*gcmark)(void *data, size_t len);
Janet(*get)(void *data, Janet key);
int (*get)(void *data, Janet key, Janet *out);
void (*put)(void *data, Janet key, Janet value);
void (*marshal)(void *p, JanetMarshalContext *ctx);
void (*unmarshal)(void *p, JanetMarshalContext *ctx);
void *(*unmarshal)(JanetMarshalContext *ctx);
void (*tostring)(void *p, JanetBuffer *buffer);
};
@@ -927,6 +946,22 @@ struct JanetRange {
int32_t end;
};
struct JanetRNG {
uint32_t a, b, c, d;
uint32_t counter;
};
/* Thread types */
#ifdef JANET_THREADS
typedef struct JanetThread JanetThread;
typedef struct JanetMailbox JanetMailbox;
struct JanetThread {
JanetMailbox *mailbox;
JanetTable *encode;
};
#endif
/***** END SECTION TYPES *****/
/***** START SECTION OPCODES *****/
@@ -1015,6 +1050,7 @@ enum JanetOpCode {
JOP_RESUME,
JOP_SIGNAL,
JOP_PROPAGATE,
JOP_IN,
JOP_GET,
JOP_PUT,
JOP_GET_INDEX,
@@ -1062,7 +1098,7 @@ enum JanetAssembleStatus {
};
struct JanetAssembleResult {
JanetFuncDef *funcdef;
const uint8_t *error;
JanetString error;
enum JanetAssembleStatus status;
};
JANET_API JanetAssembleResult janet_asm(Janet source, int flags);
@@ -1078,12 +1114,12 @@ enum JanetCompileStatus {
};
struct JanetCompileResult {
JanetFuncDef *funcdef;
const uint8_t *error;
JanetString error;
JanetFiber *macrofiber;
JanetSourceMapping error_mapping;
enum JanetCompileStatus status;
};
JANET_API JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *where);
JANET_API JanetCompileResult janet_compile(Janet source, JanetTable *env, JanetString where);
/* Get the default environment for janet */
JANET_API JanetTable *janet_core_env(JanetTable *replacements);
@@ -1101,7 +1137,13 @@ JANET_API void janet_debug_break(JanetFuncDef *def, int32_t pc);
JANET_API void janet_debug_unbreak(JanetFuncDef *def, int32_t pc);
JANET_API void janet_debug_find(
JanetFuncDef **def_out, int32_t *pc_out,
const uint8_t *source, int32_t line, int32_t column);
JanetString source, int32_t line, int32_t column);
/* RNG */
JANET_API JanetRNG *janet_default_rng(void);
JANET_API void janet_rng_seed(JanetRNG *rng, uint32_t seed);
JANET_API void janet_rng_longseed(JanetRNG *rng, const uint8_t *bytes, int32_t len);
JANET_API uint32_t janet_rng_u32(JanetRNG *rng);
/* Array functions */
JANET_API JanetArray *janet_array(int32_t capacity);
@@ -1120,7 +1162,7 @@ JANET_API void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_
JANET_API void janet_buffer_setcount(JanetBuffer *buffer, int32_t count);
JANET_API void janet_buffer_extra(JanetBuffer *buffer, int32_t n);
JANET_API void janet_buffer_push_bytes(JanetBuffer *buffer, const uint8_t *string, int32_t len);
JANET_API void janet_buffer_push_string(JanetBuffer *buffer, const uint8_t *string);
JANET_API void janet_buffer_push_string(JanetBuffer *buffer, JanetString string);
JANET_API void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring);
JANET_API void janet_buffer_push_u8(JanetBuffer *buffer, uint8_t x);
JANET_API void janet_buffer_push_u16(JanetBuffer *buffer, uint16_t x);
@@ -1138,35 +1180,35 @@ JANET_API void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x);
#define janet_tuple_sm_column(t) (janet_tuple_head(t)->sm_column)
#define janet_tuple_flag(t) (janet_tuple_head(t)->gc.flags)
JANET_API Janet *janet_tuple_begin(int32_t length);
JANET_API const Janet *janet_tuple_end(Janet *tuple);
JANET_API const Janet *janet_tuple_n(const Janet *values, int32_t n);
JANET_API int janet_tuple_equal(const Janet *lhs, const Janet *rhs);
JANET_API int janet_tuple_compare(const Janet *lhs, const Janet *rhs);
JANET_API JanetTuple janet_tuple_end(Janet *tuple);
JANET_API JanetTuple janet_tuple_n(const Janet *values, int32_t n);
JANET_API int janet_tuple_equal(JanetTuple lhs, JanetTuple rhs);
JANET_API int janet_tuple_compare(JanetTuple lhs, JanetTuple rhs);
/* String/Symbol functions */
#define janet_string_head(s) ((JanetStringHead *)((char *)s - offsetof(JanetStringHead, data)))
#define janet_string_length(s) (janet_string_head(s)->length)
#define janet_string_hash(s) (janet_string_head(s)->hash)
JANET_API uint8_t *janet_string_begin(int32_t length);
JANET_API const uint8_t *janet_string_end(uint8_t *str);
JANET_API const uint8_t *janet_string(const uint8_t *buf, int32_t len);
JANET_API const uint8_t *janet_cstring(const char *cstring);
JANET_API int janet_string_compare(const uint8_t *lhs, const uint8_t *rhs);
JANET_API int janet_string_equal(const uint8_t *lhs, const uint8_t *rhs);
JANET_API int janet_string_equalconst(const uint8_t *lhs, const uint8_t *rhs, int32_t rlen, int32_t rhash);
JANET_API const uint8_t *janet_description(Janet x);
JANET_API const uint8_t *janet_to_string(Janet x);
JANET_API JanetString janet_string_end(uint8_t *str);
JANET_API JanetString janet_string(const uint8_t *buf, int32_t len);
JANET_API JanetString janet_cstring(const char *cstring);
JANET_API int janet_string_compare(JanetString lhs, JanetString rhs);
JANET_API int janet_string_equal(JanetString lhs, JanetString rhs);
JANET_API int janet_string_equalconst(JanetString lhs, const uint8_t *rhs, int32_t rlen, int32_t rhash);
JANET_API JanetString janet_description(Janet x);
JANET_API JanetString janet_to_string(Janet x);
JANET_API void janet_to_string_b(JanetBuffer *buffer, Janet x);
JANET_API void janet_description_b(JanetBuffer *buffer, Janet x);
#define janet_cstringv(cstr) janet_wrap_string(janet_cstring(cstr))
#define janet_stringv(str, len) janet_wrap_string(janet_string((str), (len)))
JANET_API const uint8_t *janet_formatc(const char *format, ...);
JANET_API JanetString janet_formatc(const char *format, ...);
JANET_API void janet_formatb(JanetBuffer *bufp, const char *format, va_list args);
/* Symbol functions */
JANET_API const uint8_t *janet_symbol(const uint8_t *str, int32_t len);
JANET_API const uint8_t *janet_csymbol(const char *str);
JANET_API const uint8_t *janet_symbol_gen(void);
JANET_API JanetSymbol janet_symbol(const uint8_t *str, int32_t len);
JANET_API JanetSymbol janet_csymbol(const char *str);
JANET_API JanetSymbol janet_symbol_gen(void);
#define janet_symbolv(str, len) janet_wrap_symbol(janet_symbol((str), (len)))
#define janet_csymbolv(cstr) janet_wrap_symbol(janet_csymbol(cstr))
@@ -1183,12 +1225,12 @@ JANET_API const uint8_t *janet_symbol_gen(void);
#define janet_struct_hash(t) (janet_struct_head(t)->hash)
JANET_API JanetKV *janet_struct_begin(int32_t count);
JANET_API void janet_struct_put(JanetKV *st, Janet key, Janet value);
JANET_API const JanetKV *janet_struct_end(JanetKV *st);
JANET_API Janet janet_struct_get(const JanetKV *st, Janet key);
JANET_API JanetTable *janet_struct_to_table(const JanetKV *st);
JANET_API int janet_struct_equal(const JanetKV *lhs, const JanetKV *rhs);
JANET_API int janet_struct_compare(const JanetKV *lhs, const JanetKV *rhs);
JANET_API const JanetKV *janet_struct_find(const JanetKV *st, Janet key);
JANET_API JanetStruct janet_struct_end(JanetKV *st);
JANET_API Janet janet_struct_get(JanetStruct st, Janet key);
JANET_API JanetTable *janet_struct_to_table(JanetStruct st);
JANET_API int janet_struct_equal(JanetStruct lhs, JanetStruct rhs);
JANET_API int janet_struct_compare(JanetStruct lhs, JanetStruct rhs);
JANET_API const JanetKV *janet_struct_find(JanetStruct st, Janet key);
/* Table functions */
JANET_API JanetTable *janet_table(int32_t capacity);
@@ -1199,9 +1241,9 @@ JANET_API Janet janet_table_get_ex(JanetTable *t, Janet key, JanetTable **which)
JANET_API Janet janet_table_rawget(JanetTable *t, Janet key);
JANET_API Janet janet_table_remove(JanetTable *t, Janet key);
JANET_API void janet_table_put(JanetTable *t, Janet key, Janet value);
JANET_API const JanetKV *janet_table_to_struct(JanetTable *t);
JANET_API JanetStruct janet_table_to_struct(JanetTable *t);
JANET_API void janet_table_merge_table(JanetTable *table, JanetTable *other);
JANET_API void janet_table_merge_struct(JanetTable *table, const JanetKV *other);
JANET_API void janet_table_merge_struct(JanetTable *table, JanetStruct other);
JANET_API JanetKV *janet_table_find(JanetTable *t, Janet key);
JANET_API JanetTable *janet_table_clone(JanetTable *table);
@@ -1223,13 +1265,13 @@ JANET_API const JanetKV *janet_dictionary_next(const JanetKV *kvs, int32_t cap,
#define janet_abstract_type(u) (janet_abstract_head(u)->type)
#define janet_abstract_size(u) (janet_abstract_head(u)->size)
JANET_API void *janet_abstract_begin(const JanetAbstractType *type, size_t size);
JANET_API void *janet_abstract_end(void *);
JANET_API void *janet_abstract(const JanetAbstractType *type, size_t size); /* begin and end in one call */
JANET_API JanetAbstract janet_abstract_end(void *abstractTemplate);
JANET_API JanetAbstract janet_abstract(const JanetAbstractType *type, size_t size); /* begin and end in one call */
/* Native */
typedef void (*JanetModule)(JanetTable *);
typedef JanetBuildConfig(*JanetModconf)(void);
JANET_API JanetModule janet_native(const char *name, const uint8_t **error);
JANET_API JanetModule janet_native(const char *name, JanetString *error);
/* Marshaling */
JANET_API void janet_marshal(
@@ -1271,14 +1313,14 @@ JANET_API JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, J
JANET_API int janet_equals(Janet x, Janet y);
JANET_API int32_t janet_hash(Janet x);
JANET_API int janet_compare(Janet x, Janet y);
JANET_API int janet_cstrcmp(const uint8_t *str, const char *other);
JANET_API int janet_cstrcmp(JanetString str, const char *other);
JANET_API Janet janet_in(Janet ds, Janet key);
JANET_API Janet janet_get(Janet ds, Janet key);
JANET_API Janet janet_getindex(Janet ds, int32_t index);
JANET_API int32_t janet_length(Janet x);
JANET_API Janet janet_lengthv(Janet x);
JANET_API void janet_put(Janet ds, Janet key, Janet value);
JANET_API void janet_putindex(Janet ds, int32_t index, Janet value);
JANET_API uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags);
#define janet_flag_at(F, I) ((F) & ((1ULL) << (I)))
JANET_API Janet janet_wrap_number_safe(double x);
@@ -1287,13 +1329,16 @@ JANET_API int janet_init(void);
JANET_API void janet_deinit(void);
JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out);
JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f);
JANET_API JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out);
JANET_API Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv);
JANET_API Janet janet_mcall(const char *name, int32_t argc, Janet *argv);
JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err);
/* Scratch Memory API */
typedef void (*ScratchFinalizer)(void *);
JANET_API void *janet_smalloc(size_t size);
JANET_API void *janet_srealloc(void *mem, size_t size);
JANET_API void janet_sfinalizer(void *mem, ScratchFinalizer finalizer);
JANET_API void janet_sfree(void *mem);
/* C Library helpers */
@@ -1306,9 +1351,12 @@ typedef enum {
JANET_API void janet_def(JanetTable *env, const char *name, Janet val, const char *documentation);
JANET_API void janet_var(JanetTable *env, const char *name, Janet val, const char *documentation);
JANET_API void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns);
JANET_API JanetBindingType janet_resolve(JanetTable *env, const uint8_t *sym, Janet *out);
JANET_API JanetBindingType janet_resolve(JanetTable *env, JanetSymbol sym, Janet *out);
JANET_API void janet_register(const char *name, JanetCFunction cfun);
/* Get values from the core environment. */
JANET_API Janet janet_resolve_core(const char *name);
/* New C API */
/* Allow setting entry name for static libraries */
@@ -1324,25 +1372,27 @@ JANET_API void janet_register(const char *name, JanetCFunction cfun);
JANET_NO_RETURN JANET_API void janet_panicv(Janet message);
JANET_NO_RETURN JANET_API void janet_panic(const char *message);
JANET_NO_RETURN JANET_API void janet_panics(const uint8_t *message);
JANET_NO_RETURN JANET_API void janet_panics(JanetString message);
JANET_NO_RETURN JANET_API void janet_panicf(const char *format, ...);
JANET_API void janet_printf(const char *format, ...);
JANET_API void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...);
#define janet_printf(...) janet_dynprintf("out", stdout, __VA_ARGS__)
#define janet_eprintf(...) janet_dynprintf("err", stderr, __VA_ARGS__)
JANET_NO_RETURN JANET_API void janet_panic_type(Janet x, int32_t n, int expected);
JANET_NO_RETURN JANET_API void janet_panic_abstract(Janet x, int32_t n, const JanetAbstractType *at);
JANET_API void janet_arity(int32_t arity, int32_t min, int32_t max);
JANET_API void janet_fixarity(int32_t arity, int32_t fix);
JANET_API Janet janet_getmethod(const uint8_t *method, const JanetMethod *methods);
JANET_API int janet_getmethod(JanetKeyword method, const JanetMethod *methods, Janet *out);
JANET_API double janet_getnumber(const Janet *argv, int32_t n);
JANET_API JanetArray *janet_getarray(const Janet *argv, int32_t n);
JANET_API const Janet *janet_gettuple(const Janet *argv, int32_t n);
JANET_API JanetTuple janet_gettuple(const Janet *argv, int32_t n);
JANET_API JanetTable *janet_gettable(const Janet *argv, int32_t n);
JANET_API const JanetKV *janet_getstruct(const Janet *argv, int32_t n);
JANET_API const uint8_t *janet_getstring(const Janet *argv, int32_t n);
JANET_API JanetStruct janet_getstruct(const Janet *argv, int32_t n);
JANET_API JanetString janet_getstring(const Janet *argv, int32_t n);
JANET_API const char *janet_getcstring(const Janet *argv, int32_t n);
JANET_API const uint8_t *janet_getsymbol(const Janet *argv, int32_t n);
JANET_API const uint8_t *janet_getkeyword(const Janet *argv, int32_t n);
JANET_API JanetSymbol janet_getsymbol(const Janet *argv, int32_t n);
JANET_API JanetKeyword janet_getkeyword(const Janet *argv, int32_t n);
JANET_API JanetBuffer *janet_getbuffer(const Janet *argv, int32_t n);
JANET_API JanetFiber *janet_getfiber(const Janet *argv, int32_t n);
JANET_API JanetFunction *janet_getfunction(const Janet *argv, int32_t n);
@@ -1350,6 +1400,7 @@ JANET_API JanetCFunction janet_getcfunction(const Janet *argv, int32_t n);
JANET_API int janet_getboolean(const Janet *argv, int32_t n);
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 size_t janet_getsize(const Janet *argv, int32_t n);
@@ -1360,27 +1411,31 @@ JANET_API void *janet_getabstract(const Janet *argv, int32_t n, const JanetAbstr
JANET_API JanetRange janet_getslice(int32_t argc, const Janet *argv);
JANET_API int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const char *which);
JANET_API int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which);
JANET_API uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags);
/* Optionals */
JANET_API double janet_optnumber(const Janet *argv, int32_t argc, int32_t n, double dflt);
JANET_API JanetArray *janet_optarray(const Janet *argv, int32_t argc, int32_t n, JanetArray *dflt);
JANET_API const Janet *janet_opttuple(const Janet *argv, int32_t argc, int32_t n, const Janet *dflt);
JANET_API JanetTable *janet_opttable(const Janet *argv, int32_t argc, int32_t n, JanetTable *dflt);
JANET_API const JanetKV *janet_optstruct(const Janet *argv, int32_t argc, int32_t n, const JanetKV *dflt);
JANET_API const uint8_t *janet_optstring(const Janet *argv, int32_t argc, int32_t n, const uint8_t *dflt);
JANET_API JanetTuple janet_opttuple(const Janet *argv, int32_t argc, int32_t n, JanetTuple dflt);
JANET_API JanetStruct janet_optstruct(const Janet *argv, int32_t argc, int32_t n, JanetStruct dflt);
JANET_API JanetString janet_optstring(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
JANET_API const char *janet_optcstring(const Janet *argv, int32_t argc, int32_t n, const char *dflt);
JANET_API const uint8_t *janet_optsymbol(const Janet *argv, int32_t argc, int32_t n, const uint8_t *dflt);
JANET_API const uint8_t *janet_optkeyword(const Janet *argv, int32_t argc, int32_t n, const uint8_t *dflt);
JANET_API JanetBuffer *janet_optbuffer(const Janet *argv, int32_t argc, int32_t n, JanetBuffer *dflt);
JANET_API JanetSymbol janet_optsymbol(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
JANET_API JanetKeyword janet_optkeyword(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
JANET_API JanetFiber *janet_optfiber(const Janet *argv, int32_t argc, int32_t n, JanetFiber *dflt);
JANET_API JanetFunction *janet_optfunction(const Janet *argv, int32_t argc, int32_t n, JanetFunction *dflt);
JANET_API JanetCFunction janet_optcfunction(const Janet *argv, int32_t argc, int32_t n, JanetCFunction dflt);
JANET_API int janet_optboolean(const Janet *argv, int32_t argc, int32_t n, int dflt);
JANET_API void *janet_optpointer(const Janet *argv, int32_t argc, int32_t n, void *dflt);
JANET_API int32_t janet_optnat(const Janet *argv, int32_t argc, int32_t n, int32_t dflt);
JANET_API int32_t janet_optinteger(const Janet *argv, int32_t argc, int32_t n, int32_t dflt);
JANET_API int64_t janet_optinteger64(const Janet *argv, int32_t argc, int32_t n, int64_t dflt);
JANET_API size_t janet_optsize(const Janet *argv, int32_t argc, int32_t n, size_t dflt);
JANET_API void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetAbstractType *at, void *dflt);
JANET_API JanetAbstract janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetAbstractType *at, JanetAbstract dflt);
/* Mutable optional types specify a size default, and construct a new value if none is provided */
JANET_API JanetBuffer *janet_optbuffer(const Janet *argv, int32_t argc, int32_t n, int32_t dflt_len);
JANET_API JanetTable *janet_opttable(const Janet *argv, int32_t argc, int32_t n, int32_t dflt_len);
JANET_API JanetArray *janet_optarray(const Janet *argv, int32_t argc, int32_t n, int32_t dflt_len);
JANET_API Janet janet_dyn(const char *name);
JANET_API void janet_setdyn(const char *name, Janet value);
@@ -1395,13 +1450,16 @@ JANET_API void janet_marshal_int64(JanetMarshalContext *ctx, int64_t value);
JANET_API void janet_marshal_byte(JanetMarshalContext *ctx, uint8_t value);
JANET_API void janet_marshal_bytes(JanetMarshalContext *ctx, const uint8_t *bytes, size_t len);
JANET_API void janet_marshal_janet(JanetMarshalContext *ctx, Janet x);
JANET_API void janet_marshal_abstract(JanetMarshalContext *ctx, JanetAbstract abstract);
JANET_API void janet_unmarshal_ensure(JanetMarshalContext *ctx, size_t size);
JANET_API size_t janet_unmarshal_size(JanetMarshalContext *ctx);
JANET_API int32_t janet_unmarshal_int(JanetMarshalContext *ctx);
JANET_API int64_t janet_unmarshal_int64(JanetMarshalContext *ctx);
JANET_API uint8_t janet_unmarshal_byte(JanetMarshalContext *ctx);
JANET_API void janet_unmarshal_bytes(JanetMarshalContext *ctx, uint8_t *dest, size_t len);
JANET_API Janet janet_unmarshal_janet(JanetMarshalContext *ctx);
JANET_API JanetAbstract janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size);
JANET_API void janet_register_abstract_type(const JanetAbstractType *at);
JANET_API const JanetAbstractType *janet_get_abstract_type(Janet key);

View File

@@ -1,96 +0,0 @@
# Copyright 2017-2019 (C) Calvin Rose
(do
(var *should-repl* false)
(var *no-file* true)
(var *quiet* false)
(var *raw-stdin* false)
(var *handleopts* true)
(var *exit-on-error* true)
(var *colorize* true)
(var *compile-only* false)
(if-let [jp (os/getenv "JANET_PATH")] (setdyn :syspath jp))
(if-let [jp (os/getenv "JANET_HEADERPATH")] (setdyn :headerpath jp))
(def args (dyn :args))
# Flag handlers
(def handlers :private
{"h" (fn [&]
(print "usage: " (get args 0) " [options] script args...")
(print
`Options are:
-h : Show this help
-v : Print the version string
-s : Use raw stdin instead of getline like functionality
-e code : Execute a string of janet
-r : Enter the repl after running all scripts
-p : Keep on executing if there is a top level error (persistent)
-q : Hide prompt, logo, and repl output (quiet)
-k : Compile scripts but do not execute
-m syspath : Set system path for loading global modules
-c source output : Compile janet source code into an image
-n : Disable ANSI color output in the repl
-l path : Execute code in a file before running the main script
-- : Stop handling options`)
(os/exit 0)
1)
"v" (fn [&] (print janet/version "-" janet/build) (os/exit 0) 1)
"s" (fn [&] (set *raw-stdin* true) (set *should-repl* true) 1)
"r" (fn [&] (set *should-repl* true) 1)
"p" (fn [&] (set *exit-on-error* false) 1)
"q" (fn [&] (set *quiet* true) 1)
"k" (fn [&] (set *compile-only* true) (set *exit-on-error* false) 1)
"n" (fn [&] (set *colorize* false) 1)
"m" (fn [i &] (setdyn :syspath (get args (+ i 1))) 2)
"c" (fn [i &]
(def e (dofile (get args (+ i 1))))
(spit (get args (+ i 2)) (make-image e))
(set *no-file* false)
3)
"-" (fn [&] (set *handleopts* false) 1)
"l" (fn [i &]
(import* (get args (+ i 1))
:prefix "" :exit *exit-on-error*)
2)
"e" (fn [i &]
(set *no-file* false)
(eval-string (get args (+ i 1)))
2)})
(defn- dohandler [n i &]
(def h (get handlers n))
(if h (h i) (do (print "unknown flag -" n) ((get handlers "h")))))
# Process arguments
(var i 0)
(def lenargs (length args))
(while (< i lenargs)
(def arg (get args i))
(if (and *handleopts* (= "-" (string/slice arg 0 1)))
(+= i (dohandler (string/slice arg 1 2) i))
(do
(set *no-file* false)
(dofile arg :prefix "" :exit *exit-on-error* :compile-only *compile-only*)
(set i lenargs))))
(when (and (not *compile-only*) (or *should-repl* *no-file*))
(if-not *quiet*
(print "Janet " janet/version "-" janet/build " Copyright (C) 2017-2019 Calvin Rose"))
(defn noprompt [_] "")
(defn getprompt [p]
(def [line] (parser/where p))
(string "janet:" line ":" (parser/state p :delimiters) "> "))
(def prompter (if *quiet* noprompt getprompt))
(defn getstdin [prompt buf]
(file/write stdout prompt)
(file/flush stdout)
(file/read stdin :line buf))
(def getter (if *raw-stdin* getstdin getline))
(defn getchunk [buf p]
(getter (prompter p) buf))
(def onsig (if *quiet* (fn [x &] x) nil))
(setdyn :pretty-format (if *colorize* "%.20Q" "%.20q"))
(setdyn :err-color (if *colorize* true))
(repl getchunk onsig)))

View File

@@ -20,7 +20,9 @@
* IN THE SOFTWARE.
*/
#ifndef JANET_AMALG
#include "line.h"
#endif
/* Common */
Janet janet_line_getter(int32_t argc, Janet *argv) {
@@ -87,18 +89,18 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c
/* static state */
#define JANET_LINE_MAX 1024
#define JANET_HISTORY_MAX 100
static int gbl_israwmode = 0;
static const char *gbl_prompt = "> ";
static int gbl_plen = 2;
static char gbl_buf[JANET_LINE_MAX];
static int gbl_len = 0;
static int gbl_pos = 0;
static int gbl_cols = 80;
static char *gbl_history[JANET_HISTORY_MAX];
static int gbl_history_count = 0;
static int gbl_historyi = 0;
static int gbl_sigint_flag = 0;
static struct termios gbl_termios_start;
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 int gbl_sigint_flag = 0;
static JANET_THREAD_LOCAL struct termios gbl_termios_start;
/* Unsupported terminal list from linenoise */
static const char *badterms[] = {
@@ -124,7 +126,6 @@ static int rawmode() {
if (tcgetattr(STDIN_FILENO, &gbl_termios_start) == -1) goto fatal;
t = gbl_termios_start;
t.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
t.c_oflag &= ~(OPOST);
t.c_cflag |= (CS8);
t.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
t.c_cc[VMIN] = 1;
@@ -312,6 +313,14 @@ static void kbackspace() {
}
}
static void kdelete() {
if (gbl_pos != gbl_len) {
memmove(gbl_buf + gbl_pos, gbl_buf + gbl_pos + 1, gbl_len - gbl_pos);
gbl_buf[--gbl_len] = '\0';
refresh();
}
}
static int line() {
gbl_cols = getcols();
gbl_plen = 0;
@@ -384,6 +393,9 @@ static int line() {
if (read(STDIN_FILENO, seq + 2, 1) == -1) break;
if (seq[2] == '~') {
switch (seq[1]) {
case '3': /* delete */
kdelete();
break;
default:
break;
}
@@ -477,6 +489,7 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
}
return;
}
fflush(stdin);
norawmode();
fputc('\n', out);
janet_buffer_ensure(buffer, gbl_len + 1, 2);

View File

@@ -23,7 +23,9 @@
#ifndef JANET_LINE_H_defined
#define JANET_LINE_H_defined
#ifndef JANET_AMALG
#include <janet.h>
#endif
void janet_line_init();
void janet_line_deinit();

View File

@@ -20,8 +20,10 @@
* IN THE SOFTWARE.
*/
#ifndef JANET_AMALG
#include <janet.h>
#include "line.h"
#endif
#ifdef _WIN32
#include <windows.h>
@@ -31,9 +33,6 @@
#endif
#endif
extern const unsigned char *janet_gen_init;
extern int32_t janet_gen_init_size;
int main(int argc, char **argv) {
int i, status;
JanetArray *args;
@@ -47,25 +46,6 @@ int main(int argc, char **argv) {
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
SetConsoleOutputCP(65001);
/* Add directory containing janet.exe as DLL search path for
dynamic modules on windows. This is needed because dynamic modules reference
janet.exe for symbols. Otherwise, janet.exe would have to be in the current directory
to load natives correctly. */
#ifndef JANET_NO_DYNAMIC_MODULES
{
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_USER_DIRS);
HMODULE hModule = GetModuleHandleW(NULL);
wchar_t path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
size_t i = wcsnlen(path, MAX_PATH);
while (i > 0 && path[i] != '\\')
path[i--] = '\0';
if (i) AddDllDirectory(path);
GetCurrentDirectoryW(MAX_PATH, path);
AddDllDirectory(path);
}
#endif
#endif
/* Set up VM */
@@ -83,13 +63,20 @@ int main(int argc, char **argv) {
args = janet_array(argc);
for (i = 1; i < argc; i++)
janet_array_push(args, janet_cstringv(argv[i]));
janet_table_put(env, janet_ckeywordv("args"), janet_wrap_array(args));
/* Save current executable path to (dyn :executable) */
janet_table_put(env, janet_ckeywordv("executable"), janet_cstringv(argv[0]));
/* Run startup script */
status = janet_dobytes(env, janet_gen_init, janet_gen_init_size, "init.janet", NULL);
Janet mainfun, out;
janet_resolve(env, janet_csymbol("cli-main"), &mainfun);
Janet mainargs[1] = { janet_wrap_array(args) };
JanetFiber *fiber = janet_fiber(janet_unwrap_function(mainfun), 64, 1, mainargs);
fiber->env = env;
status = janet_continue(fiber, janet_wrap_nil(), &out);
if (status != JANET_SIGNAL_OK) {
janet_stacktrace(fiber, out);
}
/* Deinitialize vm */
janet_deinit();

View File

@@ -5,7 +5,7 @@
(fiber/new (fn webrepl []
(setdyn :pretty-format "%.20P")
(repl (fn get-line [buf p]
(def offset (parser/where p))
(def [offset] (parser/where p))
(def prompt (string "janet:" offset ":" (parser/state p :delimiters) "> "))
(repl-yield prompt buf)
(yield)

View File

@@ -1,4 +1,9 @@
/build
.cache
.manifest
.manifests
json.*
jhydro.*
circlet.*
argparse.*
sqlite3.*
path.*

View File

@@ -55,6 +55,8 @@
(assert (= (get @{} 1) nil) "get nil from empty table")
(assert (= (get {:boop :bap} :boop) :bap) "get non nil from struct")
(assert (= (get @{:boop :bap} :boop) :bap) "get non nil from table")
(assert (= (get @"\0" 0) 0) "get non nil from buffer")
(assert (= (get @"\0" 1) nil) "get nil from buffer oob")
(assert (put @{} :boop :bap) "can add to empty table")
(assert (put @{1 3} :boop :bap) "can add to non-empty table")
@@ -314,5 +316,8 @@
(assert (= y 1) "regression #137 (5)")
(assert (= z 2) "regression #137 (6)")
(assert (= true ;(map truthy? [0 "" true @{} {} [] '()])) "truthy values")
(assert (= false ;(map truthy? [nil false])) "non-truthy values")
(end-suite)

View File

@@ -65,6 +65,9 @@
(assert (:== (:/ (u64 "0xffff_ffff_ffff_ffff") 8 2) "0xfffffffffffffff") "bigint operations")
(assert (let [a (u64 0xff)] (:== (:+ a a a a) (:* a 2 2))) "bigint operations")
(assert (= (string (i64 -123)) "-123") "i64 prints reasonably")
(assert (= (string (u64 123)) "123") "u64 prints reasonably")
(assert-error
"trap INT64_MIN / -1"
(:/ (int/s64 "-0x8000_0000_0000_0000") -1))
@@ -118,12 +121,23 @@
(assert (deep= (parser/status p) (parser/status p2)) "parser 2")
(assert (deep= (parser/state p) (parser/state p2)) "parser 3")
# Parser errors
(defn parse-error [input]
(def p (parser/new))
(parser/consume p input)
(parser/error p))
# Invalid utf-8 sequences
(assert (not= nil (parse-error @"\xc3\x28")) "reject invalid utf-8 symbol")
(assert (not= nil (parse-error @":\xc3\x28")) "reject invalid utf-8 keyword")
# String check-set
(assert (string/check-set "abc" "a") "string/check-set 1")
(assert (not (string/check-set "abc" "z")) "string/check-set 2")
(assert (string/check-set "abc" "abc") "string/check-set 3")
(assert (not (string/check-set "abc" "")) "string/check-set 4")
(assert (string/check-set "abc" "") "string/check-set 4")
(assert (not (string/check-set "" "aabc")) "string/check-set 5")
(assert (not (string/check-set "abc" "abcdefg")) "string/check-set 6")
# Marshal and unmarshal pegs
(def p (-> "abcd" peg/compile marshal unmarshal))

View File

@@ -128,7 +128,7 @@
# Make sure Carriage Returns don't end up in doc strings.
(assert (not (string/find "\r" (get ((fiber/getenv (fiber/current)) 'cond) :doc))) "no \\r in doc strings")
(assert (not (string/find "\r" (get ((fiber/getenv (fiber/current)) 'cond) :doc ""))) "no \\r in doc strings")
# module/expand-path regression
(with-dyns [:syspath ".janet/.janet"]
@@ -170,4 +170,111 @@
(assert (idx= (take 10 (range 100)) (range 10)) "take 10")
(assert (idx= (drop 10 (range 100)) (range 10 100)) "drop 10")
# Printing to buffers
(def out-buf @"")
(def err-buf @"")
(with-dyns [:out out-buf :err err-buf]
(print "Hello")
(prin "hi")
(eprint "Sup")
(eprin "not much."))
(assert (= (string out-buf) "Hello\nhi") "print and prin to buffer 1")
(assert (= (string err-buf) "Sup\nnot much.") "eprint and eprin to buffer 1")
(assert (= (string '()) (string [])) "empty bracket tuple literal")
# with-vars
(var abc 123)
(assert (= 356 (with-vars [abc 456] (- abc 100))) "with-vars 1")
(assert-error "with-vars 2" (with-vars [abc 456] (error :oops)))
(assert (= abc 123) "with-vars 3")
# Trim empty string
(assert (= "" (string/trim " ")) "string/trim regression")
# RNGs
(defn test-rng
[rng]
(assert (all identity (seq [i :range [0 1000]]
(<= (math/rng-int rng i) i))) "math/rng-int test")
(assert (all identity (seq [i :range [0 1000]]
(def x (math/rng-uniform rng))
(and (>= x 0) (< x 1))))
"math/rng-uniform test"))
(def seedrng (math/rng 123))
(for i 0 75
(test-rng (math/rng (:int seedrng))))
(assert (deep-not= (-> 123 math/rng (:buffer 16))
(-> 456 math/rng (:buffer 16))) "math/rng-buffer 1")
(assert-no-error "math/rng-buffer 2" (math/seedrandom "abcdefg"))
# OS Date test
(assert (deep= {:year-day 0
:minutes 30
:month 0
:dst false
:seconds 0
:year 2014
:month-day 0
:hours 20
:week-day 3}
(os/date 1388608200)) "os/date")
# Appending buffer to self
(with-dyns [:out @""]
(prin "abcd")
(prin (dyn :out))
(prin (dyn :out))
(assert (deep= (dyn :out) @"abcdabcdabcdabcd") "print buffer to self"))
(os/setenv "TESTENV1" "v1")
(os/setenv "TESTENV2" "v2")
(assert (= (os/getenv "TESTENV1") "v1") "getenv works")
(def environ (os/environ))
(assert (= [(environ "TESTENV1") (environ "TESTENV2")] ["v1" "v2"]) "environ works")
# Issue #183 - just parse it :)
1e-4000000000000000000000
# Ensure randomness puts n of pred into our buffer eventually
(defn cryptorand-check
[n pred]
(def max-attempts 10000)
(var attempts 0)
(while (not= attempts max-attempts)
(def cryptobuf (os/cryptorand 10))
(when (= n (count pred cryptobuf))
(break))
(++ attempts))
(not= attempts max-attempts))
(def v (math/rng-int (math/rng (os/time)) 100))
(assert (cryptorand-check 0 |(= $ v)) "cryptorand skips value sometimes")
(assert (cryptorand-check 1 |(= $ v)) "cryptorand has value sometimes")
(do
(def buf (buffer/new-filled 1))
(os/cryptorand 1 buf)
(assert (= (in buf 0) 0) "cryptorand doesn't overwrite buffer")
(assert (= (length buf) 2) "cryptorand appends to buffer"))
# Nested quasiquotation
(def nested ~(a ~(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f))
(assert (deep= nested '(a ~(b ,(+ 1 2) ,(foo 4 d) e) f)) "nested quasiquote")
# Top level unquote
(defn constantly
[]
(comptime (math/random)))
(assert (= (constantly) (constantly)) "comptime 1")
(end-suite)

0
tools/.keep Normal file
View File

14
tools/afl/README.md Normal file
View File

@@ -0,0 +1,14 @@
# AFL Fuzzing scripts
To use these, you need to install afl (of course), and xterm. A tiling window manager helps manage
many concurrent fuzzer instances.
## Fuzz the parser
```
$ sh ./tools/afl/prepare_to_fuzz.sh
export NFUZZ=1
$ sh ./tools/afl/fuzz.sh parser
Ctrl+C when done to close all fuzzer terminals.
$ sh ./tools/afl/aggregate_cases.sh parser
$ ls ./fuzz_out/parser_aggregated/
```

View File

@@ -0,0 +1,13 @@
set -eux
n=0
for tc in $(echo ./fuzz_out/$1/*/hangs/* ./fuzz_out/$1/*/crashes/*)
do
if ! test -e $tc
then
continue
fi
mkdir -p ./fuzz_out/$1_aggregated/
cp "$tc" $(printf "./fuzz_out/$1_aggregated/$1-%04d.test" $n)
n=$((n + 1))
done

36
tools/afl/fuzz.sh Normal file
View File

@@ -0,0 +1,36 @@
set -eux
NFUZZ=${NFUZZ:-1}
children=""
function finish {
for pid in $children
do
set +e
kill -s INT $pid
done
wait
}
trap finish EXIT
test -e ./tools/afl/$1_testcases
test -e ./tools/afl/$1_runner.janet
echo "running fuzz master..."
xterm -e \
"afl-fuzz -i ./tools/afl/$1_testcases -o ./fuzz_out/$1 -M Fuzz$1_0 -- ./build/janet ./tools/afl/$1_runner.janet @@" &
children="$! $children"
echo "waiting for afl to get started before starting secondary fuzzers"
sleep 10
NFUZZ=$((NFUZZ - 1))
for N in $(seq $NFUZZ)
do
xterm -e \
"afl-fuzz -i ./tools/afl/$1_testcases -o ./fuzz_out/$1 -S Fuzz$1_$N -- ./build/janet ./tools/afl/$1_runner.janet @@" &
children="$! $children"
done
echo "waiting for child terminals to exit."
wait

View File

@@ -0,0 +1,4 @@
(def p (parser/new))
(parser/consume p (slurp ((dyn :args) 1)))
(while (parser/has-more p)
(pp (parser/produce p)))

View File

@@ -0,0 +1,15 @@
0
123.653
true
:true
{}
`
hello
`
|()
,()
@{:hello "world"}
@[1 "hello"]
nil
(foo 2 3)
([{} @{:k ([""])}])

View File

@@ -0,0 +1,6 @@
set -eux
export CC=afl-clang
make clean
make -j $(nproc) all
mkdir -p "./fuzz_out"

View File

@@ -9,4 +9,8 @@
# Body
(each path (tuple/slice (dyn :args) 1)
(print "\n/* " path " */\n")
(print (slurp path)))
# maybe will help
(:flush stdout)

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# Format all code with astyle

View File

@@ -2,8 +2,6 @@
# windows may add bad line endings, we can just force removal
# with this script.
(def fname ((dyn :args) 1))
(with [f (file/open fname :rb+)]
(def source (:read f :all))
(def new-source (string/replace-all "\r" "" source))
(:seek f :set 0)
(:write f new-source))
(def source (slurp fname))
(def new-source (string/replace-all "\r" "" source))
(spit fname new-source :wb)