mirror of
https://github.com/janet-lang/janet
synced 2025-11-06 18:43:04 +00:00
Compare commits
335 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b9eda5e87 | ||
|
|
7c2ae45809 | ||
|
|
36b2f27873 | ||
|
|
b8e02afd1a | ||
|
|
0fc36aa5d0 | ||
|
|
38f7e256d0 | ||
|
|
4187c972a3 | ||
|
|
2d5af32660 | ||
|
|
e592b24333 | ||
|
|
8700a407ce | ||
|
|
8ecf359bbe | ||
|
|
eb1988a5ae | ||
|
|
5b6dffe93d | ||
|
|
1a6eb52f11 | ||
|
|
57ccfb692c | ||
|
|
eb1c21b0da | ||
|
|
66d82c4513 | ||
|
|
c9c4424261 | ||
|
|
131733549d | ||
|
|
ee646dadf2 | ||
|
|
73f5314141 | ||
|
|
4c5734c2ee | ||
|
|
546669082f | ||
|
|
4a0ee5df7d | ||
|
|
4de6c2ad61 | ||
|
|
1fa7e73c58 | ||
|
|
0e690b4fa0 | ||
|
|
c804ae9f7c | ||
|
|
dbcceefc20 | ||
|
|
1a4035b02c | ||
|
|
e908029392 | ||
|
|
fd4220f254 | ||
|
|
de6c3d6d70 | ||
|
|
77cb823719 | ||
|
|
49954c7a30 | ||
|
|
11a7a7069a | ||
|
|
2487162ccf | ||
|
|
8ca10f37bd | ||
|
|
4199c42fe2 | ||
|
|
f39cf702db | ||
|
|
db9e431bf7 | ||
|
|
328454729e | ||
|
|
73a4c395d2 | ||
|
|
70328437f1 | ||
|
|
600bed9f6d | ||
|
|
55eca44c54 | ||
|
|
0ac5b243c7 | ||
|
|
9911c90b1d | ||
|
|
a1f35e21c7 | ||
|
|
9ccdab0bc7 | ||
|
|
a20e956f6d | ||
|
|
59668133a2 | ||
|
|
73db8584e0 | ||
|
|
cecc7e6b9d | ||
|
|
3a14aad615 | ||
|
|
8368e55151 | ||
|
|
ac85fca8a1 | ||
|
|
e5fbe5c557 | ||
|
|
474bcd50a1 | ||
|
|
70c8b6838d | ||
|
|
212479188a | ||
|
|
5b1e59b535 | ||
|
|
779d788efa | ||
|
|
6233d804c8 | ||
|
|
8f31a53276 | ||
|
|
6a763aac95 | ||
|
|
5cd6580c2d | ||
|
|
81a2af700a | ||
|
|
8a58be81ba | ||
|
|
fc53445d08 | ||
|
|
db261aabf4 | ||
|
|
36ef1c4749 | ||
|
|
5ae520a2c9 | ||
|
|
8e31bda8f6 | ||
|
|
474aed8cfe | ||
|
|
0509376aea | ||
|
|
570f04ca05 | ||
|
|
ded08b6e1e | ||
|
|
f3c0d9115f | ||
|
|
bf609445c1 | ||
|
|
13ef2bd905 | ||
|
|
4e4cdb6356 | ||
|
|
688d297a18 | ||
|
|
9e1c3e0f41 | ||
|
|
4acc63e325 | ||
|
|
967a8b5a70 | ||
|
|
92b7d91697 | ||
|
|
07db4c530e | ||
|
|
a3fb2d6e0a | ||
|
|
5b9e37e2cc | ||
|
|
88f28773da | ||
|
|
66e6979812 | ||
|
|
8a91c52fa2 | ||
|
|
e542ba7e4d | ||
|
|
bca0392738 | ||
|
|
74d51ab08b | ||
|
|
6bc400eb8c | ||
|
|
7df0ec6aed | ||
|
|
a0a980e0ef | ||
|
|
6988fd3cab | ||
|
|
c3273e8751 | ||
|
|
d37c43716a | ||
|
|
1bf751367b | ||
|
|
976dfc7195 | ||
|
|
8372d1e499 | ||
|
|
e65716f6ee | ||
|
|
4b24d77b2c | ||
|
|
02fc4ae27b | ||
|
|
624f5f428e | ||
|
|
5171dfd2a8 | ||
|
|
8ff5e49d1f | ||
|
|
134163708a | ||
|
|
40e6616df0 | ||
|
|
bcd2089f71 | ||
|
|
7553b277db | ||
|
|
d71cf093bb | ||
|
|
86d21816b6 | ||
|
|
c9521e093e | ||
|
|
16f6261b44 | ||
|
|
6b76ac3d18 | ||
|
|
5681e02e0f | ||
|
|
41a22f258e | ||
|
|
0d2844b7c9 | ||
|
|
719f7ba0c4 | ||
|
|
44ed2c6b47 | ||
|
|
c9292ef648 | ||
|
|
135abff100 | ||
|
|
7252db1e63 | ||
|
|
05e3fd3cc6 | ||
|
|
6f1b03b67e | ||
|
|
dca247f01d | ||
|
|
63e7ca4623 | ||
|
|
75d21d9f45 | ||
|
|
8911daaf6c | ||
|
|
1f55d40a10 | ||
|
|
6591e7636d | ||
|
|
c12eaa926a | ||
|
|
0e464ded3d | ||
|
|
aee1687215 | ||
|
|
58e3e63a89 | ||
|
|
9b605b27bd | ||
|
|
c5010dffb4 | ||
|
|
026f26f05f | ||
|
|
cf2d3861d6 | ||
|
|
6ceaf9d28d | ||
|
|
25a9804d91 | ||
|
|
cf19cd5292 | ||
|
|
03824dd9f7 | ||
|
|
280dca3998 | ||
|
|
46e09e4c71 | ||
|
|
427b2638e0 | ||
|
|
2541806dc1 | ||
|
|
0d16b9e1a1 | ||
|
|
b2263ed5b5 | ||
|
|
45c2819068 | ||
|
|
d28925fdab | ||
|
|
9097e36ea0 | ||
|
|
99ef4c7510 | ||
|
|
b9e05d06fe | ||
|
|
423b6db855 | ||
|
|
bb54b940c0 | ||
|
|
4149df1fca | ||
|
|
8dd8af742a | ||
|
|
d47804d222 | ||
|
|
8dd322c0be | ||
|
|
7fd0748c19 | ||
|
|
655d4b3aad | ||
|
|
5f51476526 | ||
|
|
d47b5f8c6a | ||
|
|
a18a251d16 | ||
|
|
8ee54e887f | ||
|
|
088c926196 | ||
|
|
54b66a4199 | ||
|
|
f9d57103f4 | ||
|
|
f780df0aa6 | ||
|
|
fede40f279 | ||
|
|
6ae5a9be60 | ||
|
|
e9f3dc7d5c | ||
|
|
841b58042f | ||
|
|
63e3e02a39 | ||
|
|
944347e828 | ||
|
|
7910a5feef | ||
|
|
2becd196dd | ||
|
|
bcb45157a8 | ||
|
|
70ffe3b6bd | ||
|
|
339dea9390 | ||
|
|
b26a7bb22a | ||
|
|
45dfc7cc96 | ||
|
|
9d020c3ec5 | ||
|
|
8cda06b995 | ||
|
|
a8afc5b81f | ||
|
|
228d045a06 | ||
|
|
c447e7b3a5 | ||
|
|
803c3fc235 | ||
|
|
a032529437 | ||
|
|
7bee204390 | ||
|
|
064a700edd | ||
|
|
7809f89dfc | ||
|
|
940860755c | ||
|
|
1b283c47b4 | ||
|
|
8e427317cd | ||
|
|
908a3b6f5c | ||
|
|
f2ba91899f | ||
|
|
16127fc55c | ||
|
|
97d874f16b | ||
|
|
8aba5e76ae | ||
|
|
0e7144f2dc | ||
|
|
9f48c3e2db | ||
|
|
e6306ea188 | ||
|
|
0e99d8d80f | ||
|
|
de5cd73cd7 | ||
|
|
b585d19519 | ||
|
|
8753d2dcb8 | ||
|
|
39f1d81fd4 | ||
|
|
fcd203c646 | ||
|
|
4ebb749131 | ||
|
|
37a943d9b5 | ||
|
|
2f2b875c2a | ||
|
|
99f147219a | ||
|
|
7a13d24e6f | ||
|
|
8dc91755f7 | ||
|
|
96a3104fe2 | ||
|
|
97f525d069 | ||
|
|
4ad1bdec15 | ||
|
|
530d94a4b9 | ||
|
|
141d3e9588 | ||
|
|
98eaadf2d1 | ||
|
|
54a04b5894 | ||
|
|
8bc8709d0e | ||
|
|
730080e6fd | ||
|
|
d4b49cd622 | ||
|
|
7e0586cb55 | ||
|
|
05695a35c7 | ||
|
|
58ffb9d7a5 | ||
|
|
7eb487d998 | ||
|
|
f903ee8acc | ||
|
|
91cbe2e22c | ||
|
|
c45bad9437 | ||
|
|
4aa6afbf47 | ||
|
|
29054e8072 | ||
|
|
060d11e4c2 | ||
|
|
77870508de | ||
|
|
133ad0d355 | ||
|
|
711fe64a51 | ||
|
|
78b5c94cb0 | ||
|
|
95266bdcf8 | ||
|
|
b78879dc18 | ||
|
|
5d29079393 | ||
|
|
b052a57fc8 | ||
|
|
292be33b9d | ||
|
|
0360942942 | ||
|
|
c35d6d2396 | ||
|
|
1c73d8ce2b | ||
|
|
6a539df480 | ||
|
|
1de09ec149 | ||
|
|
a1f785038d | ||
|
|
5d475848a6 | ||
|
|
2695f2da46 | ||
|
|
3cdbf5753d | ||
|
|
daf92be5bc | ||
|
|
79bbb0ee1c | ||
|
|
826bb1abbe | ||
|
|
81789a6930 | ||
|
|
28fb2403d9 | ||
|
|
1872bd344f | ||
|
|
54170d92db | ||
|
|
ec62e871dd | ||
|
|
4ba912cd57 | ||
|
|
7713674ff6 | ||
|
|
0fce440455 | ||
|
|
ab782d8896 | ||
|
|
c84ddefc53 | ||
|
|
5802155882 | ||
|
|
ee8a68f7b2 | ||
|
|
61bbeebfba | ||
|
|
18da183ef7 | ||
|
|
19c6714f06 | ||
|
|
2193193b12 | ||
|
|
850a2d7f79 | ||
|
|
ca5dce5d9f | ||
|
|
40eff3e4a3 | ||
|
|
d334f070a3 | ||
|
|
44e752d737 | ||
|
|
5c83ebd75d | ||
|
|
02ce3031e9 | ||
|
|
2b295a5459 | ||
|
|
6caf8d3d56 | ||
|
|
b18f1e8127 | ||
|
|
3e67916971 | ||
|
|
21cccc00d7 | ||
|
|
4809867b33 | ||
|
|
8bbe518696 | ||
|
|
17b4dc1fc6 | ||
|
|
cca19e921e | ||
|
|
de50a38bb1 | ||
|
|
c2ef58d880 | ||
|
|
eafcb548ce | ||
|
|
ec32d11b76 | ||
|
|
7e97687c9e | ||
|
|
da5a64131f | ||
|
|
71e5278364 | ||
|
|
d6a1faa380 | ||
|
|
166862ecff | ||
|
|
3c133bd677 | ||
|
|
b0b1024f8a | ||
|
|
cc07ff987d | ||
|
|
efc38b87de | ||
|
|
a3a3e4c0dc | ||
|
|
d46bcd5b8f | ||
|
|
dfe00fee94 | ||
|
|
9118f2ce08 | ||
|
|
a0e98b9aa8 | ||
|
|
0d3986abbb | ||
|
|
529b34d84e | ||
|
|
e0fe8476aa | ||
|
|
0ca0180f27 | ||
|
|
21a355c89f | ||
|
|
e528b86a2a | ||
|
|
2e6ee39506 | ||
|
|
894877a0e3 | ||
|
|
6887dd05f6 | ||
|
|
95dbad6ec1 | ||
|
|
ea88ae1a5b | ||
|
|
e8e4d637ef | ||
|
|
3928136670 | ||
|
|
0dcae6c3d6 | ||
|
|
b639ccdad1 | ||
|
|
affcb5b459 | ||
|
|
70c80d7899 | ||
|
|
fb7914a3c8 | ||
|
|
6099d2a45d | ||
|
|
044fc7c461 | ||
|
|
7c4670c3de | ||
|
|
c1113d61d6 | ||
|
|
2c4366dd71 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,2 +0,0 @@
|
||||
# Use an approximate language for syntax highlighting (clojure is pretty close)
|
||||
*.janet linguist-language=clojure
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -13,6 +13,9 @@ janet
|
||||
janet-*.tar.gz
|
||||
dist
|
||||
|
||||
# Kakoune (fzf via fd)
|
||||
.fdignore
|
||||
|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
@@ -20,6 +23,9 @@ dist
|
||||
.project
|
||||
.cproject
|
||||
|
||||
# Gnome Builder
|
||||
.buildconfig
|
||||
|
||||
# Local directory for testing
|
||||
local
|
||||
|
||||
|
||||
139
CHANGELOG.md
139
CHANGELOG.md
@@ -1,6 +1,145 @@
|
||||
# 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.
|
||||
- Add `update-pkgs` to jpm.
|
||||
- Integrate jpm with https://github.com/janet-lang/pkgs.git. jpm can now
|
||||
install packages based on their short names in the package listing, which
|
||||
can be customized via an env variable.
|
||||
- Add `varfn` macro
|
||||
- Add compile time arity checking when function in function call is known.
|
||||
- Added `slice` to the core library.
|
||||
- The `*/slice` family of functions now can take nil as start or end to get
|
||||
the same behavior as the defaults (0 and -1) for those parameters.
|
||||
- `string/` functions that take a pattern to search for will throw an error
|
||||
when receiving the empty string.
|
||||
- Replace (start:end) style stacktrace source position information with
|
||||
line, column. This should be more readable for humans. Also, range information
|
||||
can be recovered by re-parsing source.
|
||||
|
||||
## 1.3.1 - 2019-09-21
|
||||
- Fix some linking issues when creating executables with native dependencies.
|
||||
- jpm now runs each test script in a new interpreter.
|
||||
- Fix an issue that prevent some valid programs from compiling.
|
||||
- Add `mean` to core.
|
||||
- Abstract types that implement the `:+`, `:-`, `:*`, `:/`, `:>`, `:==`, `:<`,
|
||||
`:<=`, and `:>=` methods will work with the corresponding built-in
|
||||
arithmetic functions. This means built-in integer types can now be used as
|
||||
normal number values in many contexts.
|
||||
- Allow (length x) on typed arrays an other abstract types that implement
|
||||
the :length method.
|
||||
|
||||
## 1.3.0 - 2019-09-05
|
||||
- Add `get-in`, `put-in`, `update-in`, and `freeze` to core.
|
||||
- Add `jpm run rule` and `jpm rules` to jpm to improve utility and discoverability of jpm.
|
||||
- Remove `cook` module and move `path` module to https://github.com/janet-lang/path.git.
|
||||
The functionality in `cook` is now bundled directly in the `jpm` script.
|
||||
- Add `buffer/format` and `string/format` format flags `Q` and `q` to print colored and
|
||||
non-colored single-line values, similar to `P` and `p`.
|
||||
- Change default repl to print long sequences on one line and color stacktraces if color is enabled.
|
||||
- Add `backmatch` pattern for PEGs.
|
||||
- jpm detects if not in a Developer Command prompt on windows for a better error message.
|
||||
- jpm install git submodules in dependencies
|
||||
- Change default fiber stack limit to the maximum value of a 32 bit signed integer.
|
||||
- Some bug fixes with `jpm`
|
||||
- Fix bugs with pegs.
|
||||
- Add `os/arch` to get ISA that janet was compiled for
|
||||
- Add color to stacktraces via `(dyn :err-color)`
|
||||
|
||||
## 1.2.0 - 2019-08-08
|
||||
- Add `take` and `drop` functions that are easier to use compared to the
|
||||
existing slice functions.
|
||||
- Add optional default value to `get`.
|
||||
- Add function literal short-hand via `|` reader macro, which maps to the
|
||||
`short-fn` macro.
|
||||
- Add `int?` and `nat?` functions to the core.
|
||||
- Add `(dyn :executable)` at top level to get what used to be
|
||||
`(process/args 0)`.
|
||||
- Add `:linux` to platforms returned by `(os/which)`.
|
||||
- Update jpm to build standalone executables. Use `declare-executable` for this.
|
||||
- Add `use` macro.
|
||||
- Remove `process/args` in favor of `(dyn :args)`.
|
||||
- Fix bug with Nanbox implementation allowing users to created
|
||||
custom values of any type with typed array and marshal modules, which
|
||||
was unsafe.
|
||||
- Add `janet_wrap_number_safe` to API, for converting numbers to Janets
|
||||
where the number could be any 64 bit, user provided bit pattern. Certain
|
||||
NaN values (which a machine will never generate as a result of a floating
|
||||
point operation) are guarded against and converted to a default NaN value.
|
||||
|
||||
## 1.1.0 - 2019-07-08
|
||||
- Change semantics of `-l` flag to be import rather than dofile.
|
||||
- Fix compiler regression in top level defs with destructuring.
|
||||
- Add `table/clone`.
|
||||
- Improve `jpm` tool with git and dependency capabilities, as well as better
|
||||
module uninstalls.
|
||||
|
||||
## 1.0.0 - 2019-07-01
|
||||
- Add `with` macro for resource handling.
|
||||
- Add `propagate` function so we can "rethrow" signals after they are
|
||||
|
||||
114
Makefile
114
Makefile
@@ -24,32 +24,37 @@
|
||||
|
||||
PREFIX?=/usr/local
|
||||
|
||||
INCLUDEDIR=$(PREFIX)/include
|
||||
BINDIR=$(PREFIX)/bin
|
||||
LIBDIR=$(PREFIX)/lib
|
||||
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
|
||||
JANET_PATH?=$(PREFIX)/lib/janet
|
||||
JANET_PATH?=$(LIBDIR)/janet
|
||||
MANPATH?=$(PREFIX)/share/man/man1/
|
||||
PKG_CONFIG_PATH?=$(PREFIX)/lib/pkgconfig
|
||||
PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig
|
||||
DEBUGGER=gdb
|
||||
|
||||
CFLAGS=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fpic -O2 -fvisibility=hidden \
|
||||
CFLAGS=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fPIC -O2 -fvisibility=hidden \
|
||||
-DJANET_BUILD=$(JANET_BUILD)
|
||||
LDFLAGS=-rdynamic
|
||||
|
||||
# For installation
|
||||
LDCONFIG:=ldconfig "$(LIBDIR)"
|
||||
|
||||
# Check OS
|
||||
UNAME:=$(shell uname -s)
|
||||
ifeq ($(UNAME), Darwin)
|
||||
CLIBS:=$(CLIBS) -ldl
|
||||
LDCONFIG:=
|
||||
else ifeq ($(UNAME), Linux)
|
||||
CLIBS:=$(CLIBS) -lrt -ldl
|
||||
endif
|
||||
# For other unix likes, add flags here!
|
||||
ifeq ($(UNAME),Haiku)
|
||||
ifeq ($(UNAME), Haiku)
|
||||
LDCONFIG:=
|
||||
LDFLAGS=-Wl,--export-dynamic
|
||||
endif
|
||||
|
||||
@@ -101,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 \
|
||||
@@ -135,14 +141,14 @@ build/janet_boot: $(JANET_BOOT_OBJECTS)
|
||||
|
||||
# Now the reason we bootstrap in the first place
|
||||
build/core_image.c: build/janet_boot
|
||||
build/janet_boot $@ JANET_PATH $(JANET_PATH) JANET_HEADERPATH $(INCLUDEDIR)/janet
|
||||
build/janet_boot $@ JANET_PATH '$(JANET_PATH)' JANET_HEADERPATH '$(INCLUDEDIR)/janet'
|
||||
|
||||
##########################################################
|
||||
##### The main interpreter program and shared object #####
|
||||
##########################################################
|
||||
|
||||
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)
|
||||
@@ -199,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
|
||||
@@ -210,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 #####
|
||||
###################
|
||||
@@ -238,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
|
||||
@@ -253,9 +268,12 @@ dist: build/janet-dist.tar.gz
|
||||
|
||||
build/janet-%.tar.gz: $(JANET_TARGET) \
|
||||
src/include/janet.h src/conf/janetconf.h \
|
||||
janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
|
||||
build/doc.html README.md build/janet.c
|
||||
tar -czvf $@ $^
|
||||
jpm.1 janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
|
||||
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)/
|
||||
cd build && tar -czvf ../$@ $(JANET_DIST_DIR)
|
||||
|
||||
#########################
|
||||
##### Documentation #####
|
||||
@@ -272,9 +290,8 @@ build/doc.html: $(JANET_TARGET) tools/gendoc.janet
|
||||
|
||||
SONAME=libjanet.so.1
|
||||
|
||||
.PHONY: $(PKG_CONFIG_PATH)/janet.pc
|
||||
$(PKG_CONFIG_PATH)/janet.pc: $(JANET_TARGET)
|
||||
mkdir -p $(PKG_CONFIG_PATH)
|
||||
.PHONY: build/janet.pc
|
||||
build/janet.pc: $(JANET_TARGET)
|
||||
echo 'prefix=$(PREFIX)' > $@
|
||||
echo 'exec_prefix=$${prefix}' >> $@
|
||||
echo 'includedir=$(INCLUDEDIR)/janet' >> $@
|
||||
@@ -288,31 +305,34 @@ $(PKG_CONFIG_PATH)/janet.pc: $(JANET_TARGET)
|
||||
echo 'Libs: -L$${libdir} -ljanet $(LDFLAGS)' >> $@
|
||||
echo 'Libs.private: $(CLIBS)' >> $@
|
||||
|
||||
install: $(JANET_TARGET) $(PKG_CONFIG_PATH)/janet.pc
|
||||
mkdir -p $(BINDIR)
|
||||
cp $(JANET_TARGET) $(BINDIR)/janet
|
||||
mkdir -p $(INCLUDEDIR)/janet
|
||||
cp -rf $(JANET_HEADERS) $(INCLUDEDIR)/janet
|
||||
mkdir -p $(JANET_PATH)
|
||||
mkdir -p $(LIBDIR)
|
||||
cp $(JANET_LIBRARY) $(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')
|
||||
cp $(JANET_STATIC_LIBRARY) $(LIBDIR)/libjanet.a
|
||||
ln -sf $(SONAME) $(LIBDIR)/libjanet.so
|
||||
install: $(JANET_TARGET) build/janet.pc
|
||||
mkdir -p '$(BINDIR)'
|
||||
cp $(JANET_TARGET) '$(BINDIR)/janet'
|
||||
mkdir -p '$(INCLUDEDIR)/janet'
|
||||
cp -rf $(JANET_HEADERS) '$(INCLUDEDIR)/janet'
|
||||
mkdir -p '$(JANET_PATH)'
|
||||
mkdir -p '$(LIBDIR)'
|
||||
cp $(JANET_LIBRARY) '$(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')'
|
||||
cp $(JANET_STATIC_LIBRARY) '$(LIBDIR)/libjanet.a'
|
||||
ln -sf $(SONAME) '$(LIBDIR)/libjanet.so'
|
||||
ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(LIBDIR)/$(SONAME)
|
||||
cp -rf auxlib/* $(JANET_PATH)
|
||||
cp -rf auxbin/* $(BINDIR)
|
||||
mkdir -p $(MANPATH)
|
||||
cp janet.1 $(MANPATH)
|
||||
-ldconfig $(LIBDIR)
|
||||
cp -rf auxbin/* '$(BINDIR)'
|
||||
mkdir -p '$(MANPATH)'
|
||||
cp janet.1 '$(MANPATH)'
|
||||
cp jpm.1 '$(MANPATH)'
|
||||
mkdir -p '$(PKG_CONFIG_PATH)'
|
||||
cp build/janet.pc '$(PKG_CONFIG_PATH)/janet.pc'
|
||||
-$(LDCONFIG)
|
||||
|
||||
uninstall:
|
||||
-rm $(BINDIR)/janet
|
||||
-rm $(BINDIR)/jpm
|
||||
-rm -rf $(INCLUDEDIR)/janet
|
||||
-rm -rf $(LIBDIR)/libjanet.*
|
||||
-rm $(PKG_CONFIG_PATH)/janet.pc
|
||||
-rm $(MANPATH)/janet.1
|
||||
# -rm -rf $(JANET_PATH)/* - err on the side of correctness here
|
||||
-rm '$(BINDIR)/janet'
|
||||
-rm '$(BINDIR)/jpm'
|
||||
-rm -rf '$(INCLUDEDIR)/janet'
|
||||
-rm -rf '$(LIBDIR)'/libjanet.*
|
||||
-rm '$(PKG_CONFIG_PATH)/janet.pc'
|
||||
-rm '$(MANPATH)/janet.1'
|
||||
-rm '$(MANPATH)/jpm.1'
|
||||
# -rm -rf '$(JANET_PATH)'/* - err on the side of correctness here
|
||||
|
||||
#################
|
||||
##### Other #####
|
||||
@@ -329,7 +349,17 @@ clean:
|
||||
-rm -rf build vgcore.* callgrind.*
|
||||
|
||||
test-install:
|
||||
cd test/install && rm -rf build && jpm build && jpm test
|
||||
cd test/install \
|
||||
&& rm -rf build .cache .manifests \
|
||||
&& jpm --verbose build \
|
||||
&& jpm --verbose test \
|
||||
&& build/testexec \
|
||||
&& 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 $@
|
||||
|
||||
29
README.md
29
README.md
@@ -4,7 +4,6 @@
|
||||
[](https://travis-ci.org/janet-lang/janet)
|
||||
[](https://builds.sr.ht/~bakpakin/janet/.freebsd.yaml?)
|
||||
[](https://builds.sr.ht/~bakpakin/janet/.openbsd.yaml?)
|
||||
<noscript><a href="https://liberapay.com/Janet-Language/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
||||
|
||||
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left">
|
||||
|
||||
@@ -62,7 +61,7 @@ documentation for symbols in the core library. For example,
|
||||
Shows documentation for the doc macro.
|
||||
|
||||
To get a list of all bindings in the default
|
||||
environment, use the `(all-symbols)` function.
|
||||
environment, use the `(all-bindings)` function.
|
||||
|
||||
## Source
|
||||
|
||||
@@ -132,6 +131,32 @@ is maybe more convenient and flexible for integrating into existing pipelines.
|
||||
Meson also provides much better IDE integration than Make or batch files, as well as support
|
||||
for cross compilation.
|
||||
|
||||
For the impatient, building with Meson is as simple as follows. The options provided to
|
||||
`meson setup` below emulate Janet's Makefile.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/janet-lang/janet.git
|
||||
cd janet
|
||||
meson setup build \
|
||||
--buildtype release \
|
||||
--optimization 2 \
|
||||
-Dgit_hash=$(git log --pretty=format:'%h' -n 1)
|
||||
ninja -C build
|
||||
|
||||
# Run the binary
|
||||
build/janet
|
||||
|
||||
# Installation
|
||||
ninja -C build install
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
Janet can be hacked on with pretty much any environment you like, but for IDE
|
||||
lovers, [Gnome Builder](https://wiki.gnome.org/Apps/Builder) is probably the
|
||||
best option, as it has excellent meson integration. It also offers code completion
|
||||
for Janet's C API right out of the box, which is very useful for exploring.
|
||||
|
||||
## Installation
|
||||
|
||||
See [the Introduction](https://janet-lang.org/introduction.html) for more details. If you just want
|
||||
|
||||
47
appveyor.yml
47
appveyor.yml
@@ -1,12 +1,13 @@
|
||||
version: build-{build}
|
||||
clone_folder: c:\projects\janet
|
||||
image:
|
||||
- Visual Studio 2017
|
||||
- Visual Studio 2019
|
||||
configuration:
|
||||
- Release
|
||||
- Debug
|
||||
platform:
|
||||
- x64
|
||||
- x86
|
||||
environment:
|
||||
matrix:
|
||||
- arch: Win64
|
||||
@@ -15,25 +16,41 @@ matrix:
|
||||
|
||||
# skip unsupported combinations
|
||||
init:
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %platform%
|
||||
|
||||
install:
|
||||
- build_win
|
||||
- build_win test
|
||||
- set JANET_BUILD=%appveyor_repo_commit:~0,7%
|
||||
- choco install nsis -y -pre
|
||||
- build_win dist
|
||||
- call "C:\Program Files (x86)\NSIS\makensis.exe" janet-installer.nsi
|
||||
|
||||
# Replace makensis.exe and files with special long string build. This should
|
||||
# prevent issues when setting PATH during installation.
|
||||
- 7z e "tools\nsis-3.04-strlen_8192.zip" -o"C:\Program Files (x86)\NSIS\" -y
|
||||
- build_win all
|
||||
- refreshenv
|
||||
# We need to reload vcvars after refreshing
|
||||
- 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.6.0
|
||||
build: off
|
||||
|
||||
only_commits:
|
||||
files:
|
||||
- appveyor.yml
|
||||
- src/
|
||||
|
||||
artifacts:
|
||||
- path: janet-installer.exe
|
||||
name: janet-windows-installer.exe
|
||||
- name: janet.c
|
||||
path: dist\janet.c
|
||||
type: File
|
||||
- name: janet.h
|
||||
path: dist\janet.h
|
||||
type: File
|
||||
- name: janetconf.h
|
||||
path: dist\janetconf.h
|
||||
type: File
|
||||
- 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-%platform%-installer.exe"
|
||||
type: File
|
||||
|
||||
deploy:
|
||||
@@ -41,7 +58,7 @@ deploy:
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: lwEXy09qhj2jSH9s1C/KvCkAUqJSma8phFR+0kbsfUc3rVxpNK5uD3z9Md0SjYRx
|
||||
artifact: janet-windows
|
||||
artifact: /(janet|shell).*/
|
||||
draft: true
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
|
||||
986
auxbin/jpm
986
auxbin/jpm
File diff suppressed because it is too large
Load Diff
@@ -1,397 +0,0 @@
|
||||
### cook.janet
|
||||
###
|
||||
### Library to help build janet natives and other
|
||||
### build artifacts.
|
||||
###
|
||||
### Copyright 2019 © Calvin Rose
|
||||
|
||||
#
|
||||
# Basic Path Settings
|
||||
#
|
||||
|
||||
# Windows is the OS outlier
|
||||
(def- is-win (= (os/which) :windows))
|
||||
(def- is-mac (= (os/which) :macos))
|
||||
(def- sep (if is-win "\\" "/"))
|
||||
(def- objext (if is-win ".obj" ".o"))
|
||||
(def- modext (if is-win ".dll" ".so"))
|
||||
|
||||
#
|
||||
# Rule Engine
|
||||
#
|
||||
|
||||
(defn- getrules []
|
||||
(def rules (dyn :rules))
|
||||
(if rules rules (setdyn :rules @{})))
|
||||
|
||||
(defn- gettarget [target]
|
||||
(def item ((getrules) target))
|
||||
(unless item (error (string "No rule for target " target)))
|
||||
item)
|
||||
|
||||
(defn- rule-impl
|
||||
[target deps thunk &opt phony]
|
||||
(put (getrules) target @[(array/slice deps) thunk phony]))
|
||||
|
||||
(defmacro rule
|
||||
"Add a rule to the rule graph."
|
||||
[target deps & body]
|
||||
~(,rule-impl ,target ,deps (fn [] nil ,;body)))
|
||||
|
||||
(defmacro phony
|
||||
"Add a phony rule to the rule graph. A phony rule will run every time
|
||||
(it is always considered out of date). Phony rules are good for defining
|
||||
user facing tasks."
|
||||
[target deps & body]
|
||||
~(,rule-impl ,target ,deps (fn [] nil ,;body) true))
|
||||
|
||||
(defn add-dep
|
||||
"Add a dependency to an existing rule. Useful for extending phony
|
||||
rules or extending the dependency graph of existing rules."
|
||||
[target dep]
|
||||
(def [deps] (gettarget target))
|
||||
(array/push deps dep))
|
||||
|
||||
(defn- add-thunk
|
||||
[target more]
|
||||
(def item (gettarget target))
|
||||
(def [_ thunk] item)
|
||||
(put item 1 (fn [] (more) (thunk))))
|
||||
|
||||
(defmacro add-body
|
||||
"Add recipe code to an existing rule. This makes existing rules do more but
|
||||
does not modify the dependency graph."
|
||||
[target & body]
|
||||
~(,add-thunk ,target (fn [] ,;body)))
|
||||
|
||||
(defn- needs-build
|
||||
[dest src]
|
||||
(let [mod-dest (os/stat dest :modified)
|
||||
mod-src (os/stat src :modified)]
|
||||
(< mod-dest mod-src)))
|
||||
|
||||
(defn- needs-build-some
|
||||
[dest sources]
|
||||
(def f (file/open dest))
|
||||
(if (not f) (break true))
|
||||
(file/close f)
|
||||
(some (partial needs-build dest) sources))
|
||||
|
||||
(defn do-rule
|
||||
"Evaluate a given rule."
|
||||
[target]
|
||||
(def item ((getrules) target))
|
||||
(unless item
|
||||
(if (os/stat target :mode)
|
||||
(break target)
|
||||
(error (string "No rule for file " target " found."))))
|
||||
(def [deps thunk phony] item)
|
||||
(def realdeps (seq [dep :in deps :let [x (do-rule dep)] :when x] x))
|
||||
(when (or phony (needs-build-some target realdeps))
|
||||
(thunk))
|
||||
(unless phony target))
|
||||
|
||||
(def- _env (fiber/getenv (fiber/current)))
|
||||
|
||||
(defn import-rules
|
||||
"Import another file that defines more cook rules. This ruleset
|
||||
is merged into the current ruleset."
|
||||
[path]
|
||||
(def env (make-env))
|
||||
(unless (os/stat path :mode)
|
||||
(error (string "cannot open " path)))
|
||||
(loop [k :keys _env :when (symbol? k)]
|
||||
(unless ((_env k) :private) (put env k (_env k))))
|
||||
(def currenv (fiber/getenv (fiber/current)))
|
||||
(loop [k :keys currenv :when (keyword? k)]
|
||||
(put env k (currenv k)))
|
||||
(dofile path :env env)
|
||||
(when-let [rules (env :rules)] (merge-into (getrules) rules)))
|
||||
|
||||
#
|
||||
# Configuration
|
||||
#
|
||||
|
||||
# Installation settings
|
||||
(def JANET_MODPATH (or (os/getenv "JANET_MODPATH") (dyn :syspath)))
|
||||
(def JANET_HEADERPATH (os/getenv "JANET_HEADERPATH"))
|
||||
(def JANET_BINPATH (or (os/getenv "JANET_BINPATH") (unless is-win "/usr/local/bin")))
|
||||
|
||||
# Compilation settings
|
||||
(def- OPTIMIZE (or (os/getenv "OPTIMIZE") 2))
|
||||
(def- COMPILER (or (os/getenv "COMPILER") (if is-win "cl" "cc")))
|
||||
(def- LINKER (or (os/getenv "LINKER") (if is-win "link" COMPILER)))
|
||||
(def- LFLAGS
|
||||
(if-let [lflags (os/getenv "LFLAGS")]
|
||||
(string/split " " lflags)
|
||||
(if is-win ["/nologo" "/DLL"]
|
||||
(if is-mac
|
||||
["-shared" "-undefined" "dynamic_lookup"]
|
||||
["-shared"]))))
|
||||
(def- CFLAGS
|
||||
(if-let [cflags (os/getenv "CFLAGS")]
|
||||
(string/split " " cflags)
|
||||
(if is-win
|
||||
["/nologo"]
|
||||
["-std=c99" "-Wall" "-Wextra" "-fpic"])))
|
||||
|
||||
# Some defaults
|
||||
(def default-cflags CFLAGS)
|
||||
(def default-lflags LFLAGS)
|
||||
(def default-cc COMPILER)
|
||||
(def default-ld LINKER)
|
||||
|
||||
(defn- opt
|
||||
"Get an option, allowing overrides via dynamic bindings AND some
|
||||
default value dflt if no dynamic binding is set."
|
||||
[opts key dflt]
|
||||
(def ret (or (opts key) (dyn key dflt)))
|
||||
(if (= nil ret)
|
||||
(error (string "option :" key " not set")))
|
||||
ret)
|
||||
|
||||
#
|
||||
# OS and shell helpers
|
||||
#
|
||||
|
||||
(defn shell
|
||||
"Do a shell command"
|
||||
[& args]
|
||||
(def res (os/execute args :p))
|
||||
(unless (zero? res)
|
||||
(error (string "command exited with status " res))))
|
||||
|
||||
(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)))
|
||||
|
||||
(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" "/e")
|
||||
(shell "cp" "-rf" src dest)))
|
||||
|
||||
#
|
||||
# C Compilation
|
||||
#
|
||||
|
||||
(defn- embed-name
|
||||
"Rename a janet symbol for embedding."
|
||||
[path]
|
||||
(->> path
|
||||
(string/replace-all sep "___")
|
||||
(string/replace-all ".janet" "")))
|
||||
|
||||
(defn- embed-c-name
|
||||
"Rename a janet file for embedding."
|
||||
[path]
|
||||
(->> path
|
||||
(string/replace-all sep "___")
|
||||
(string/replace-all ".janet" ".janet.c")
|
||||
(string "build" sep)))
|
||||
|
||||
(defn- embed-o-name
|
||||
"Get object file for c file."
|
||||
[path]
|
||||
(->> path
|
||||
(string/replace-all sep "___")
|
||||
(string/replace-all ".janet" (string ".janet" objext))
|
||||
(string "build" sep)))
|
||||
|
||||
(defn- object-name
|
||||
"Rename a source file so it can be built in a flat source tree."
|
||||
[path]
|
||||
(->> path
|
||||
(string/replace-all sep "___")
|
||||
(string/replace-all ".c" (if is-win ".obj" ".o"))
|
||||
(string "build" sep)))
|
||||
|
||||
(defn- lib-name
|
||||
"Generate name for dynamic library."
|
||||
[name]
|
||||
(string "build" sep name modext))
|
||||
|
||||
(defn- make-define
|
||||
"Generate strings for adding custom defines to the compiler."
|
||||
[define value]
|
||||
(def pre (if is-win "/D" "-D"))
|
||||
(if value
|
||||
(string pre define "=" value)
|
||||
(string pre define)))
|
||||
|
||||
(defn- make-defines
|
||||
"Generate many defines. Takes a dictionary of defines. If a value is
|
||||
true, generates -DNAME (/DNAME on windows), otherwise -DNAME=value."
|
||||
[defines]
|
||||
(seq [[d v] :pairs defines] (make-define d (if (not= v true) v))))
|
||||
|
||||
(defn- getcflags
|
||||
"Generate the c flags from the input options."
|
||||
[opts]
|
||||
@[;(opt opts :cflags CFLAGS)
|
||||
(string (if is-win "/I" "-I") (opt opts :headerpath JANET_HEADERPATH))
|
||||
(string (if is-win "/O" "-O") (opt opts :optimize OPTIMIZE))])
|
||||
|
||||
(defn- compile-c
|
||||
"Compile a C file into an object file."
|
||||
[opts src dest]
|
||||
(def cc (opt opts :compiler COMPILER))
|
||||
(def cflags (getcflags opts))
|
||||
(def defines (interpose " " (make-defines (opt opts :defines {}))))
|
||||
(def headers (or (opts :headers) []))
|
||||
(rule dest [src ;headers]
|
||||
(print "compiling " dest "...")
|
||||
(if is-win
|
||||
(shell cc ;defines "/c" ;cflags (string "/Fo" dest) src)
|
||||
(shell cc "-c" src ;defines ;cflags "-o" dest))))
|
||||
|
||||
(defn- link-c
|
||||
"Link a number of object files together."
|
||||
[opts target & objects]
|
||||
(def ld (opt opts :linker LINKER))
|
||||
(def cflags (getcflags opts))
|
||||
(def lflags (opt opts :lflags LFLAGS))
|
||||
(rule target objects
|
||||
(print "linking " target "...")
|
||||
(if is-win
|
||||
(shell ld ;lflags (string "/OUT:" target) ;objects (string (opt opts :headerpath JANET_HEADERPATH) `\\janet.lib`))
|
||||
(shell ld ;cflags `-o` target ;objects ;lflags))))
|
||||
|
||||
(defn- create-buffer-c
|
||||
"Inline raw byte file as a c file."
|
||||
[source dest name]
|
||||
(rule dest [source]
|
||||
(print "generating " dest "...")
|
||||
(def f (file/open source :r))
|
||||
(if (not f) (error (string "file " f " not found")))
|
||||
(def out (file/open dest :w))
|
||||
(def chunks (seq [b :in (file/read f :all)] (string b)))
|
||||
(file/write out
|
||||
"#include <janet.h>\n"
|
||||
"static const unsigned char bytes[] = {"
|
||||
;(interpose ", " chunks)
|
||||
"};\n\n"
|
||||
"const unsigned char *" name "_embed = bytes;\n"
|
||||
"size_t " name "_embed_size = sizeof(bytes);\n")
|
||||
(file/close out)
|
||||
(file/close f)))
|
||||
|
||||
#
|
||||
# Declaring Artifacts - used in project.janet, targets specifically
|
||||
# tailored for janet.
|
||||
#
|
||||
|
||||
(defn- install-rule
|
||||
"Add install and uninstall rule for moving file from src into destdir."
|
||||
[src destdir]
|
||||
(def parts (string/split sep src))
|
||||
(def name (last parts))
|
||||
(add-body "install"
|
||||
(try (os/mkdir destdir) ([err] nil))
|
||||
(copy src destdir))
|
||||
(add-body "uninstall"
|
||||
(def path (string destdir sep name))
|
||||
(print "removing " path)
|
||||
(try (rm path) ([err]
|
||||
(unless (= err "No such file or directory")
|
||||
(error err))))))
|
||||
|
||||
(defn declare-native
|
||||
"Declare a native binary. This is a shared library that can be loaded
|
||||
dynamically by a janet runtime."
|
||||
[&keys opts]
|
||||
(def sources (opts :source))
|
||||
(def name (opts :name))
|
||||
(def lname (lib-name name))
|
||||
(loop [src :in sources]
|
||||
(compile-c opts src (object-name src)))
|
||||
(def objects (map object-name sources))
|
||||
(when-let [embedded (opts :embedded)]
|
||||
(loop [src :in embedded]
|
||||
(def c-src (embed-c-name src))
|
||||
(def o-src (embed-o-name src))
|
||||
(array/push objects o-src)
|
||||
(create-buffer-c src c-src (embed-name src))
|
||||
(compile-c opts c-src o-src)))
|
||||
(link-c opts lname ;objects)
|
||||
(add-dep "build" lname)
|
||||
(def path (opt opts :modpath JANET_MODPATH))
|
||||
(install-rule lname path))
|
||||
|
||||
(defn declare-source
|
||||
"Create a Janet modules. This does not actually build the module(s),
|
||||
but registers it for packaging and installation."
|
||||
[&keys opts]
|
||||
(def sources (opts :source))
|
||||
(def path (opt opts :modpath JANET_MODPATH))
|
||||
(each s sources
|
||||
(install-rule s path)))
|
||||
|
||||
(defn declare-bin
|
||||
"Declare a generic file to be installed as an executable."
|
||||
[&keys opts]
|
||||
(def main (opts :main))
|
||||
(def binpath (opt opts :binpath JANET_BINPATH))
|
||||
(install-rule main binpath))
|
||||
|
||||
(defn declare-binscript
|
||||
"Declare a janet file to be installed as an executable script. Creates
|
||||
a shim on windows."
|
||||
[&keys opts]
|
||||
(def main (opts :main))
|
||||
(def binpath (opt opts :binpath JANET_BINPATH))
|
||||
(install-rule main binpath)
|
||||
# Create a dud batch file when on windows.
|
||||
(when is-win
|
||||
(def name (last (string/split sep main)))
|
||||
(def bat (string "@echo off\r\njanet %~dp0\\" name "%*"))
|
||||
(def newname (string binpath sep name ".bat"))
|
||||
(add-body "install"
|
||||
(spit newname bat))
|
||||
(add-body "uninstall"
|
||||
(os/rm newname))))
|
||||
|
||||
(defn declare-archive
|
||||
"Build a janet archive. This is a file that bundles together many janet
|
||||
scripts into a janet image. This file can the be moved to any machine with
|
||||
a janet vm and the required dependencies and run there."
|
||||
[&keys opts]
|
||||
(def entry (opts :entry))
|
||||
(def name (opts :name))
|
||||
(def iname (string "build" sep name ".jimage"))
|
||||
(rule iname (or (opts :deps) [])
|
||||
(spit iname (make-image (require entry))))
|
||||
(def path (opt opts :modpath JANET_MODPATH))
|
||||
(install-rule iname path))
|
||||
|
||||
(defn declare-project
|
||||
"Define your project metadata. This should
|
||||
be the first declaration in a project.janet file.
|
||||
Also sets up basic phony targets like clean, build, test, etc."
|
||||
[&keys meta]
|
||||
(setdyn :project meta)
|
||||
(try (os/mkdir "build") ([err] nil))
|
||||
(phony "build" [])
|
||||
(phony "install" ["build"] (print "Installed."))
|
||||
(phony "uninstall" [] (print "Uninstalled."))
|
||||
(phony "clean" [] (rm "build") (print "Deleted build directory."))
|
||||
(phony "test" ["build"]
|
||||
(defn dodir
|
||||
[dir]
|
||||
(each sub (os/dir dir)
|
||||
(def ndir (string dir sep sub))
|
||||
(case (os/stat ndir :mode)
|
||||
:file (when (string/has-suffix? ".janet" ndir)
|
||||
(print "running " ndir " ...")
|
||||
(dofile ndir :exit true))
|
||||
:directory (dodir ndir))))
|
||||
(dodir "test")
|
||||
(print "All tests passed.")))
|
||||
@@ -1,149 +0,0 @@
|
||||
### path.janet
|
||||
###
|
||||
### A library for path manipulation.
|
||||
###
|
||||
### Copyright 2019 © Calvin Rose
|
||||
|
||||
#
|
||||
# Common
|
||||
#
|
||||
|
||||
(def- ext-peg
|
||||
(peg/compile ~{:back (> -1 (+ (* ($) (set "\\/.")) :back))
|
||||
:main :back}))
|
||||
|
||||
(defn ext
|
||||
"Get the file extension for a path."
|
||||
[path]
|
||||
(if-let [m (peg/match ext-peg path (length path))]
|
||||
(let [i (m 0)]
|
||||
(if (= (path i) 46)
|
||||
(string/slice path (m 0) -1)))))
|
||||
|
||||
(defn- redef
|
||||
"Redef a value, keeping all metadata."
|
||||
[from to]
|
||||
(setdyn (symbol to) (dyn (symbol from))))
|
||||
|
||||
#
|
||||
# Generating Macros
|
||||
#
|
||||
|
||||
(defmacro- decl-sep [pre sep] ~(def ,(symbol pre "/sep") ,sep))
|
||||
(defmacro- decl-delim [pre d] ~(def ,(symbol pre "/delim") ,d))
|
||||
|
||||
(defmacro- decl-last-sep
|
||||
[pre sep]
|
||||
~(def- ,(symbol pre "/last-sep-peg")
|
||||
(peg/compile ~{:back (> -1 (+ (* ,sep ($)) :back))
|
||||
:main :back})))
|
||||
|
||||
(defmacro- decl-basename
|
||||
[pre]
|
||||
~(defn ,(symbol pre "/basename")
|
||||
"Gets the base file name of a path."
|
||||
[path]
|
||||
(if-let [m (peg/match
|
||||
,(symbol pre "/last-sep-peg")
|
||||
path
|
||||
(length path))]
|
||||
(let [[p] m]
|
||||
(string/slice path p -1))
|
||||
path)))
|
||||
|
||||
(defmacro- decl-parts
|
||||
[pre sep]
|
||||
~(defn ,(symbol pre "/parts")
|
||||
"Split a path into its parts."
|
||||
[path]
|
||||
(string/split ,sep path)))
|
||||
|
||||
(defmacro- decl-normalize
|
||||
[pre sep lead]
|
||||
~(defn ,(symbol pre "/normalize")
|
||||
"Normalize a path. This removes . and .. in the
|
||||
path, as well as empty path elements."
|
||||
[path]
|
||||
(def els (string/split ,sep path))
|
||||
(def newparts @[])
|
||||
(if (,(symbol pre "/abspath?") path) (array/push newparts ,lead))
|
||||
(each part els
|
||||
(case part
|
||||
"" nil
|
||||
"." nil
|
||||
".." (array/pop newparts)
|
||||
(array/push newparts part)))
|
||||
(string/join newparts ,sep)))
|
||||
|
||||
(defmacro- decl-join
|
||||
[pre sep]
|
||||
~(defn ,(symbol pre "/join")
|
||||
"Join path elements together."
|
||||
[& els]
|
||||
(,(symbol pre "/normalize") (string/join els ,sep))))
|
||||
|
||||
(defmacro- decl-abspath
|
||||
[pre]
|
||||
~(defn ,(symbol pre "/abspath")
|
||||
"Coerce a path to be absolute."
|
||||
[path]
|
||||
(if (,(symbol pre "/abspath?") path)
|
||||
path
|
||||
(,(symbol pre "/join") (os/cwd) path))))
|
||||
|
||||
#
|
||||
# Posix
|
||||
#
|
||||
|
||||
(defn posix/abspath?
|
||||
"Check if a path is absolute."
|
||||
[path]
|
||||
(string/has-prefix? "/" path))
|
||||
|
||||
(redef "ext" "posix/ext")
|
||||
(decl-sep "posix" "/")
|
||||
(decl-delim "posix" ":")
|
||||
(decl-last-sep "posix" "/")
|
||||
(decl-basename "posix")
|
||||
(decl-parts "posix" "/")
|
||||
(decl-normalize "posix" "/" "")
|
||||
(decl-join "posix" "/")
|
||||
(decl-abspath "posix")
|
||||
|
||||
#
|
||||
# Windows
|
||||
#
|
||||
|
||||
(def- abs-peg (peg/compile '(* (range "AZ") ":\\")))
|
||||
(defn win32/abspath?
|
||||
"Check if a path is absolute."
|
||||
[path]
|
||||
(peg/match abs-peg path))
|
||||
|
||||
(redef "ext" "win32/ext")
|
||||
(decl-sep "win32" "\\")
|
||||
(decl-delim "win32" ";")
|
||||
(decl-last-sep "win32" "\\")
|
||||
(decl-basename "win32")
|
||||
(decl-parts "win32" "\\")
|
||||
(decl-normalize "win32" "\\" "C:")
|
||||
(decl-join "win32" "\\")
|
||||
(decl-abspath "win32")
|
||||
|
||||
#
|
||||
# Specialize for current OS
|
||||
#
|
||||
|
||||
(def- syms
|
||||
["ext"
|
||||
"sep"
|
||||
"delim"
|
||||
"basename"
|
||||
"abspath?"
|
||||
"abspath"
|
||||
"parts"
|
||||
"normalize"
|
||||
"join"])
|
||||
(let [pre (if (= :windows (os/which)) "win32" "posix")]
|
||||
(each sym syms
|
||||
(redef (string pre "/" sym) sym)))
|
||||
107
build_win.bat
107
build_win.bat
@@ -13,11 +13,20 @@
|
||||
@if "%1"=="clean" goto CLEAN
|
||||
@if "%1"=="test" goto TEST
|
||||
@if "%1"=="dist" goto DIST
|
||||
@if "%1"=="install" goto INSTALL
|
||||
@if "%1"=="test-install" goto TESTINSTALL
|
||||
@if "%1"=="all" goto ALL
|
||||
|
||||
@rem Set compile and link options here
|
||||
@setlocal
|
||||
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /LD /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
|
||||
|
||||
@rem Add janet build tag
|
||||
if not "%JANET_BUILD%" == "" (
|
||||
@set JANET_COMPILE=%JANET_COMPILE% /DJANET_BUILD="\"%JANET_BUILD%\""
|
||||
)
|
||||
|
||||
mkdir build
|
||||
mkdir build\core
|
||||
@@ -25,30 +34,26 @@ mkdir build\mainclient
|
||||
mkdir build\boot
|
||||
|
||||
@rem Build the xxd tool for generating sources
|
||||
@cl /nologo /c tools/xxd.c /Fobuild\xxd.obj
|
||||
cl /nologo /c tools/xxd.c /Fobuild\xxd.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@link /nologo /out:build\xxd.exe build\xxd.obj
|
||||
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
|
||||
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
|
||||
%JANET_COMPILE% /Fobuild\boot\boot.gen.obj build\boot.gen.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the bootstrap interpretter
|
||||
@rem Build the bootstrap interpreter
|
||||
for %%f in (src\core\*.c) do (
|
||||
@%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
for %%f in (src\boot\*.c) do (
|
||||
@%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
|
||||
@@ -56,12 +61,12 @@ for %%f in (src\boot\*.c) do (
|
||||
build\janet_boot build\core_image.c
|
||||
|
||||
@rem Build the core image
|
||||
@%JANET_COMPILE% /Fobuild\core_image.obj build\core_image.c
|
||||
%JANET_COMPILE% /Fobuild\core_image.obj build\core_image.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the sources
|
||||
for %%f in (src\core\*.c) do (
|
||||
@%JANET_COMPILE% /Fobuild\core\%%~nf.obj %%f
|
||||
%JANET_COMPILE% /Fobuild\core\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
|
||||
@@ -70,7 +75,7 @@ rc /nologo /fobuild\janet_win.res janet_win.rc
|
||||
|
||||
@rem Build the main client
|
||||
for %%f in (src\mainclient\*.c) do (
|
||||
@%JANET_COMPILE% /Fobuild\mainclient\%%~nf.obj %%f
|
||||
%JANET_COMPILE% /Fobuild\mainclient\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
|
||||
@@ -78,6 +83,10 @@ for %%f in (src\mainclient\*.c) do (
|
||||
%JANET_LINK% /out:janet.exe build\core\*.obj build\mainclient\*.obj build\core_image.obj build\janet_win.res
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build static library (libjanet.a)
|
||||
%JANET_LINK_STATIC% /out:build\libjanet.lib build\core\*.obj build\core_image.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Gen amlag
|
||||
setlocal enabledelayedexpansion
|
||||
set "amalg_files="
|
||||
@@ -85,6 +94,11 @@ for %%f in (src\core\*.c) do (
|
||||
set "amalg_files=!amalg_files! %%f"
|
||||
)
|
||||
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. ==
|
||||
@@ -107,15 +121,16 @@ exit /b 0
|
||||
|
||||
@rem Clean build artifacts
|
||||
:CLEAN
|
||||
del janet.exe janet.exp janet.lib
|
||||
del *.exe *.lib *.exp
|
||||
rd /s /q build
|
||||
rd /s /q dist
|
||||
exit /b 0
|
||||
|
||||
@rem Run tests
|
||||
:TEST
|
||||
for %%f in (test/suite*.janet) do (
|
||||
janet.exe test\%%f
|
||||
@if errorlevel 1 goto :TESTFAIL
|
||||
@if errorlevel 1 goto TESTFAIL
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
@@ -123,21 +138,75 @@ exit /b 0
|
||||
:DIST
|
||||
mkdir dist
|
||||
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
|
||||
|
||||
copy janet.lib dist\janet.lib
|
||||
copy janet.exp dist\janet.exp
|
||||
|
||||
copy src\include\janet.h dist\janet.h
|
||||
copy src\conf\janetconf.h dist\janetconf.h
|
||||
|
||||
copy auxlib\cook.janet dist\cook.janet
|
||||
copy build\libjanet.lib dist\libjanet.lib
|
||||
|
||||
copy auxbin\jpm dist\jpm
|
||||
copy tools\jpm.bat dist\jpm.bat
|
||||
|
||||
@rem Create installer
|
||||
"C:\Program Files (x86)\NSIS\makensis.exe" janet-installer.nsi
|
||||
exit /b 0
|
||||
|
||||
@rem Run the installer. (Installs to the local user with default settings)
|
||||
:INSTALL
|
||||
@echo Running Installer...
|
||||
FOR %%a in (janet-*-windows-installer.exe) DO (
|
||||
%%a /S /CurrentUser
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
@rem Test the installation.
|
||||
:TESTINSTALL
|
||||
pushd test\install
|
||||
call jpm clean
|
||||
@if errorlevel 1 goto :TESTINSTALLFAIL
|
||||
call jpm test
|
||||
@if errorlevel 1 goto :TESTINSTALLFAIL
|
||||
call jpm --verbose --modpath=. install https://github.com/janet-lang/json.git
|
||||
@if errorlevel 1 goto :TESTINSTALLFAIL
|
||||
call build\testexec
|
||||
@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
|
||||
@if errorlevel 1 exit /b 1
|
||||
call %0 test
|
||||
@if errorlevel 1 exit /b 1
|
||||
call %0 dist
|
||||
@if errorlevel 1 exit /b 1
|
||||
call %0 install
|
||||
@if errorlevel 1 exit /b 1
|
||||
@echo Done!
|
||||
exit /b 0
|
||||
|
||||
:TESTFAIL
|
||||
|
||||
11
examples/debug.janet
Normal file
11
examples/debug.janet
Normal 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
153
examples/debugger.janet
Normal 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
11
examples/rtest.janet
Normal 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)
|
||||
@@ -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
70
examples/threads.janet
Normal 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")))
|
||||
@@ -1,5 +1,5 @@
|
||||
# Version
|
||||
!define VERSION "1.0.0"
|
||||
!define VERSION "1.6.0"
|
||||
!define PRODUCT_VERSION "${VERSION}.0"
|
||||
VIProductVersion "${PRODUCT_VERSION}"
|
||||
VIFileVersion "${PRODUCT_VERSION}"
|
||||
@@ -14,6 +14,9 @@ VIFileVersion "${PRODUCT_VERSION}"
|
||||
!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME ""
|
||||
!define MULTIUSER_INSTALLMODE_INSTDIR "Janet-${VERSION}"
|
||||
|
||||
# For now, use 32 bit folder as build is 32 bit
|
||||
# !define MULTIUSER_USE_PROGRAMFILES64
|
||||
|
||||
# Includes
|
||||
!include "MultiUser.nsh"
|
||||
!include "MUI2.nsh"
|
||||
@@ -22,7 +25,19 @@ VIFileVersion "${PRODUCT_VERSION}"
|
||||
|
||||
# Basics
|
||||
Name "Janet"
|
||||
OutFile "janet-v${VERSION}-windows-installer.exe"
|
||||
|
||||
# Do some NSIS-fu to figure out at compile time if we are in appveyor
|
||||
!define OUTNAME $%APPVEYOR_REPO_TAG_NAME%
|
||||
!define "CHECK_${OUTNAME}"
|
||||
!define DOLLAR "$"
|
||||
!ifdef CHECK_${DOLLAR}%APPVEYOR_REPO_TAG_NAME%
|
||||
# We are not in the appveyor environment, use version name
|
||||
!define OUTNAME_PART v${VERSION}
|
||||
!else
|
||||
# We are in appveyor, use git tag name for installer
|
||||
!define OUTNAME_PART ${OUTNAME}
|
||||
!endif
|
||||
OutFile "janet-${OUTNAME_PART}-windows-installer.exe"
|
||||
|
||||
# Some Configuration
|
||||
!define APPNAME "Janet"
|
||||
@@ -75,11 +90,12 @@ function .onInit
|
||||
functionEnd
|
||||
|
||||
section "Janet" BfWSection
|
||||
|
||||
createDirectory "$INSTDIR\Library"
|
||||
createDirectory "$INSTDIR\C"
|
||||
createDirectory "$INSTDIR\bin"
|
||||
createDirectory "$INSTDIR\docs"
|
||||
setOutPath "$INSTDIR"
|
||||
setOutPath "$INSTDIR"
|
||||
|
||||
# Bin files
|
||||
file /oname=bin\janet.exe dist\janet.exe
|
||||
@@ -87,16 +103,13 @@ section "Janet" BfWSection
|
||||
file /oname=bin\jpm.janet auxbin\jpm
|
||||
file /oname=bin\jpm.bat tools\jpm.bat
|
||||
|
||||
# Modules
|
||||
file /oname=Library\cook.janet auxlib\cook.janet
|
||||
file /oname=Library\path.janet auxlib\path.janet
|
||||
|
||||
# C headers
|
||||
# C headers and library files
|
||||
file /oname=C\janet.h dist\janet.h
|
||||
file /oname=C\janetconf.h dist\janetconf.h
|
||||
file /oname=C\janet.lib dist\janet.lib
|
||||
file /oname=C\janet.exp dist\janet.exp
|
||||
file /oname=C\janet.c dist\janet.c
|
||||
file /oname=C\libjanet.lib dist\libjanet.lib
|
||||
|
||||
# Documentation
|
||||
file /oname=docs\docs.html dist\doc.html
|
||||
@@ -105,41 +118,43 @@ section "Janet" BfWSection
|
||||
file README.md
|
||||
file LICENSE
|
||||
|
||||
# Uninstaller - See function un.onInit and section "uninstall" for configuration
|
||||
writeUninstaller "$INSTDIR\uninstall.exe"
|
||||
# Uninstaller - See function un.onInit and section "uninstall" for configuration
|
||||
writeUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
# Start Menu
|
||||
createShortCut "$SMPROGRAMS\Janet.lnk" "$INSTDIR\bin\janet.exe" "" "$INSTDIR\logo.ico"
|
||||
# Start Menu
|
||||
createShortCut "$SMPROGRAMS\Janet.lnk" "$INSTDIR\bin\janet.exe" "" "$INSTDIR\logo.ico"
|
||||
|
||||
# Set up Environment variables
|
||||
!insertmacro WriteEnv JANET_PATH "$INSTDIR\Library"
|
||||
!insertmacro WriteEnv JANET_HEADERPATH "$INSTDIR\C"
|
||||
!insertmacro WriteEnv JANET_LIBPATH "$INSTDIR\C"
|
||||
!insertmacro WriteEnv JANET_BINPATH "$INSTDIR\bin"
|
||||
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
# Update path
|
||||
${EnvVarUpdate} $0 "PATH" "A" "HKCU" "$INSTDIR\bin" ; Append
|
||||
${EnvVarUpdate} $0 "PATH" "A" "HKLM" "$INSTDIR\bin" ; Append
|
||||
${If} $MultiUser.InstallMode == "AllUsers"
|
||||
${EnvVarUpdate} $0 "PATH" "A" "HKLM" "$INSTDIR\bin" ; Append
|
||||
${Else}
|
||||
${EnvVarUpdate} $0 "PATH" "A" "HKCU" "$INSTDIR\bin" ; Append
|
||||
${EndIf}
|
||||
|
||||
# Registry information for add/remove programs
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayName" "Janet"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\logo.ico"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "Publisher" "Janet-Lang.org"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "HelpLink" "${HELPURL}"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "URLUpdateInfo" "${HELPURL}"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "URLInfoAbout" "${HELPURL}"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayVersion" "0.6.0"
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "VersionMajor" 0
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "VersionMinor" 6
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "NoModify" 1
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "NoRepair" 1
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "EstimatedSize" 1000
|
||||
# Registry information for add/remove programs
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayName" "Janet"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\logo.ico"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "Publisher" "Janet-Lang.org"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "HelpLink" "${HELPURL}"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "URLUpdateInfo" "${HELPURL}"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "URLInfoAbout" "${HELPURL}"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayVersion" "${VERSION}"
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "NoModify" 1
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "NoRepair" 1
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "EstimatedSize" 1000
|
||||
# Add uninstall
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\" /$MultiUser.InstallMode"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /$MultiUser.InstallMode /S"
|
||||
|
||||
|
||||
sectionEnd
|
||||
|
||||
# Uninstaller
|
||||
@@ -150,10 +165,10 @@ functionEnd
|
||||
|
||||
section "uninstall"
|
||||
|
||||
# Remove Start Menu launcher
|
||||
delete "$SMPROGRAMS\Janet.lnk"
|
||||
# Remove Start Menu launcher
|
||||
delete "$SMPROGRAMS\Janet.lnk"
|
||||
|
||||
# Remove files
|
||||
# Remove files
|
||||
delete "$INSTDIR\logo.ico"
|
||||
delete "$INSTDIR\README.md"
|
||||
delete "$INSTDIR\LICENSE"
|
||||
@@ -165,18 +180,23 @@ section "uninstall"
|
||||
# Remove env vars
|
||||
!insertmacro DelEnv JANET_PATH
|
||||
!insertmacro DelEnv JANET_HEADERPATH
|
||||
!insertmacro DelEnv JANET_LIBPATH
|
||||
!insertmacro DelEnv JANET_BINPATH
|
||||
|
||||
# Unset PATH
|
||||
${un.EnvVarUpdate} $0 "PATH" "R" "HKCU" "$INSTDIR\bin" ; Remove
|
||||
${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" "$INSTDIR\bin" ; Remove
|
||||
${If} $MultiUser.InstallMode == "AllUsers"
|
||||
${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" "$INSTDIR\bin" ; Remove
|
||||
${Else}
|
||||
${un.EnvVarUpdate} $0 "PATH" "R" "HKCU" "$INSTDIR\bin" ; Remove
|
||||
${EndIf}
|
||||
|
||||
# make sure windows knows about the change
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
# Always delete uninstaller as the last action
|
||||
delete "$INSTDIR\uninstall.exe"
|
||||
# Always delete uninstaller as the last action
|
||||
delete "$INSTDIR\uninstall.exe"
|
||||
|
||||
# Remove uninstaller information from the registry
|
||||
# Remove uninstaller information from the registry
|
||||
DeleteRegKey SHCTX "${UNINST_KEY}"
|
||||
|
||||
sectionEnd
|
||||
|
||||
2
janet.1
2
janet.1
@@ -14,7 +14,7 @@ janet \- run the Janet language abstract machine
|
||||
.SH DESCRIPTION
|
||||
Janet is a functional and imperative programming language and bytecode interpreter.
|
||||
It is a modern lisp, but lists are replaced by other data structures with better utility
|
||||
and performance (arrays, tables, structs, tuples). The language also bridging bridging
|
||||
and performance (arrays, tables, structs, tuples). The language also features bridging
|
||||
to native code written in C, meta-programming with macros, and bytecode assembly.
|
||||
|
||||
There is a repl for trying out the language, as well as the ability to run script files.
|
||||
|
||||
202
jpm.1
Normal file
202
jpm.1
Normal file
@@ -0,0 +1,202 @@
|
||||
.TH JPM 1
|
||||
.SH NAME
|
||||
jpm \- the Janet Project Manager, a build tool for Janet
|
||||
.SH SYNOPSIS
|
||||
.B jpm
|
||||
[\fB\-\-flag ...\fR]
|
||||
[\fB\-\-option=value ...\fR]
|
||||
.IR command
|
||||
.IR args ...
|
||||
.SH DESCRIPTION
|
||||
jpm is the build tool that ships with a standard Janet install. It is
|
||||
used for building Janet projects, installing dependencies, installing
|
||||
projects, building native modules, and exporting your Janet project to a
|
||||
standalone executable. Although not required for working with Janet, it
|
||||
removes much of the boilerplate with installing dependencies and
|
||||
building native modules. jpm requires only Janet to run, and uses git
|
||||
to install dependencies (jpm will work without git installed).
|
||||
.SH DOCUMENTATION
|
||||
|
||||
jpm has several subcommands, each used for managing either a single Janet project or
|
||||
all Janet modules installed on the system. Global commands, those that manage modules
|
||||
at the system level, do things like install and uninstall packages, as well as clear the cache.
|
||||
More interesting are the local commands. For more information on jpm usage, see https://janet-lang.org/docs/index.html
|
||||
|
||||
.SH FLAGS
|
||||
|
||||
.TP
|
||||
.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
|
||||
.BR \-\-modpath=/some/path
|
||||
Set the path to install modules to. Defaults to $JANET_MODPATH, $JANET_PATH, or (dyn :syspath) in that order.
|
||||
|
||||
.TP
|
||||
.BR \-\-headerpath=/some/path
|
||||
Set the path the jpm will include when building C source code. This lets
|
||||
you specify the location of janet.h and janetconf.h on your system. On a
|
||||
normal install, this option is not needed.
|
||||
|
||||
.TP
|
||||
.BR \-\-binpath=/some/path
|
||||
Set the path that jpm will install scripts and standalone executables to. Executables
|
||||
defined via declare-execuatble or scripts declared via declare-binscript will be installed
|
||||
here when jpm install is run. Defaults to $JANET_BINPATH, or a reasonable default for the system.
|
||||
See JANET_BINPATH for more.
|
||||
|
||||
.TP
|
||||
.BR \-\-libpath=/some/path
|
||||
Sets the path jpm will use to look for libjanet.a for building standalone executables. libjanet.so
|
||||
is \fBnot\fR used for building native modules or standalone executables, only
|
||||
for linking into applications that want to embed janet as a dynamic module.
|
||||
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
|
||||
Sets the compiler used for compiling native modules and standalone executables. Defaults
|
||||
to cc.
|
||||
|
||||
.TP
|
||||
.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
|
||||
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. Defaults to ar.
|
||||
|
||||
.SH COMMANDS
|
||||
.TP
|
||||
.BR help
|
||||
Shows the usage text and exits immediately.
|
||||
|
||||
.TP
|
||||
.BR build
|
||||
Builds all artifacts specified in the project.janet file in the current directory. Artifacts will
|
||||
be created in the ./build/ directory.
|
||||
|
||||
.TP
|
||||
.BR install\ [\fBrepo\fR]
|
||||
|
||||
When run with no arguments, installs all installable artifacts in the current project to
|
||||
the current JANET_MODPATH for modules and JANET_BINPATH for executables and scripts. Can also
|
||||
take an optional git repository URL and will install all artifacts in that repository instead.
|
||||
When run with an argument, install does not need to be run from a jpm project directory.
|
||||
|
||||
.TP
|
||||
.BR uninstall\ [\fBname\fR]
|
||||
Uninstall a project installed with install. uninstall expects the name of the project, not the
|
||||
repository url, path to installed file or executable name. The name of the project must be specified
|
||||
at the top of the project.janet file in the declare-project form. If no name is given, uninstalls
|
||||
the current project if installed.
|
||||
|
||||
.TP
|
||||
.BR clean
|
||||
Remove all artifacts created by jpm. This just deletes the build folder.
|
||||
|
||||
.TP
|
||||
.BR test
|
||||
Runs jpm tests. jpm will run all janet source files in the test directory as tests. A test
|
||||
is considered failing if it exits with a non-zero exit code.
|
||||
|
||||
.TP
|
||||
.BR deps
|
||||
Install all dependencies that this project requires recursively. jpm does not
|
||||
resolve dependency issues, like conflicting versions of the same module are required, or
|
||||
different modules with the same name. Dependencies are installed with git, so deps requires
|
||||
git to be on the PATH.
|
||||
|
||||
.TP
|
||||
.BR clear-cache
|
||||
jpm caches git repositories that are needed to install modules from a remote
|
||||
source in a global cache ($JANET_PATH/.cache). If these dependencies are out of
|
||||
date or too large, clear-cache will remove the cache and jpm will rebuild it
|
||||
when needed. clear-cache is a global command, so a project.janet is not
|
||||
required.
|
||||
|
||||
.TP
|
||||
.BR run\ [\fBrule\fR]
|
||||
Run a given rule defined in project.janet. Project definitions files (project.janet) usually
|
||||
contain a few artifact declarations, which set up rules that jpm can then resolve, or execute.
|
||||
A project.janet can also create custom rules to create arbitrary files or run arbitrary code, much
|
||||
like make. run will run a single rule or build a single file.
|
||||
|
||||
.TP
|
||||
.BR rules
|
||||
List all rules that can be run via run. This is useful for exploring rules in the project.
|
||||
|
||||
.TP
|
||||
.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
|
||||
.RS
|
||||
The location to look for Janet libraries. This is the only environment variable Janet needs to
|
||||
find native and source code modules. If no JANET_PATH is set, Janet will look in
|
||||
the default location set at compile time, which can be determined with (dyn :syspath)
|
||||
.RE
|
||||
|
||||
.B JANET_MODPATH
|
||||
.RS
|
||||
The location that jpm will use to install libraries to. Defaults to JANET_PATH, but you could
|
||||
set this to a different directory if you want to. Doing so would let you import Janet modules
|
||||
on the normal system path (JANET_PATH or (dyn :syspath)), but install to a different directory. It is also a more reliable way to install
|
||||
This variable is overwritten by the --modpath=/some/path if it is provided.
|
||||
.RE
|
||||
|
||||
.B JANET_HEADERPATH
|
||||
.RS
|
||||
The location that jpm will look for janet header files (janet.h and janetconf.h) that are used
|
||||
to build native modules and standalone executables. If janet.h and janetconf.h are available as
|
||||
default includes on your system, this value is not required. If not provided, will default to
|
||||
(dyn :syspath)/../../include/janet. The --headerpath=/some/path will override this variable.
|
||||
.RE
|
||||
|
||||
.B JANET_LIBPATH
|
||||
.RS
|
||||
Similar to JANET_HEADERPATH, this path is where jpm will look for
|
||||
libjanet.a for creating standalong executables. This does not need to be
|
||||
set on a normal install.
|
||||
If not provided, this will default to (dyn :syspath)/../../lib.
|
||||
The --libpath=/some/path will override this variable.
|
||||
.RE
|
||||
|
||||
.B JANET_BINPATH
|
||||
.RS
|
||||
The directory where jpm will install binary scripts and executables to.
|
||||
Defaults to
|
||||
(dyn :syspath)/../../lib.
|
||||
The --binpath=/some/path will override this variable.
|
||||
.RE
|
||||
|
||||
.B JANET_PKGLIST
|
||||
.RS
|
||||
The git repository URL that contains a listing of packages. This allows installing packages with shortnames, which
|
||||
is mostly a convenience. However, package dependencies can use short names, package listings
|
||||
can be used to choose a particular set of dependency versions for a whole project.
|
||||
|
||||
.SH AUTHOR
|
||||
Written by Calvin Rose <calsrose@gmail.com>
|
||||
62
meson.build
62
meson.build
@@ -20,7 +20,7 @@
|
||||
|
||||
project('janet', 'c',
|
||||
default_options : ['c_std=c99', 'b_lundef=false', 'default_library=both'],
|
||||
version : '1.0.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'
|
||||
@@ -52,7 +53,7 @@ conf.set_quoted('JANET_VERSION', meson.project_version())
|
||||
# Use options
|
||||
conf.set_quoted('JANET_BUILD', get_option('git_hash'))
|
||||
conf.set('JANET_NO_NANBOX', not get_option('nanbox'))
|
||||
conf.set('JANET_SINGLE_THREADED', not get_option('single_threaded'))
|
||||
conf.set('JANET_SINGLE_THREADED', get_option('single_threaded'))
|
||||
conf.set('JANET_NO_DYNAMIC_MODULES', not get_option('dynamic_modules'))
|
||||
conf.set('JANET_NO_DOCSTRINGS', not get_option('docstrings'))
|
||||
conf.set('JANET_NO_SOURCEMAPS', not get_option('sourcemaps'))
|
||||
@@ -65,6 +66,12 @@ conf.set('JANET_RECURSION_GUARD', get_option('recursion_guard'))
|
||||
conf.set('JANET_MAX_PROTO_DEPTH', get_option('max_proto_depth'))
|
||||
conf.set('JANET_MAX_MACRO_EXPAND', get_option('max_macro_expand'))
|
||||
conf.set('JANET_STACK_MAX', get_option('stack_max'))
|
||||
if get_option('os_name') != ''
|
||||
conf.set('JANET_OS_NAME', get_option('os_name'))
|
||||
endif
|
||||
if get_option('arch_name') != ''
|
||||
conf.set('JANET_ARCH_NAME', get_option('arch_name'))
|
||||
endif
|
||||
jconf = configure_file(output : 'janetconf.h',
|
||||
configuration : conf)
|
||||
|
||||
@@ -77,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
|
||||
@@ -123,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',
|
||||
@@ -150,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
|
||||
@@ -161,18 +168,35 @@ 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)
|
||||
|
||||
janet_mainclient = executable('janet', core_src, core_image, init_gen, mainclient_src,
|
||||
# Extra c flags - adding -fvisibility=hidden matches the Makefile and
|
||||
# shaves off about 10k on linux x64, likely similar on other platforms.
|
||||
native_cc = meson.get_compiler('c', native: true)
|
||||
cross_cc = meson.get_compiler('c', native: false)
|
||||
if native_cc.has_argument('-fvisibility=hidden')
|
||||
extra_native_cflags = ['-fvisibility=hidden']
|
||||
else
|
||||
extra_native_cflags = []
|
||||
endif
|
||||
if cross_cc.has_argument('-fvisibility=hidden')
|
||||
extra_cross_cflags = ['-fvisibility=hidden']
|
||||
else
|
||||
extra_cross_cflags = []
|
||||
endif
|
||||
|
||||
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
|
||||
janet_nativeclient = janet_mainclient
|
||||
@@ -191,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
|
||||
@@ -206,7 +236,8 @@ test_files = [
|
||||
'test/suite3.janet',
|
||||
'test/suite4.janet',
|
||||
'test/suite5.janet',
|
||||
'test/suite6.janet'
|
||||
'test/suite6.janet',
|
||||
'test/suite7.janet'
|
||||
]
|
||||
foreach t : test_files
|
||||
test(t, janet_nativeclient, args : files([t]), workdir : meson.current_source_dir())
|
||||
@@ -221,13 +252,10 @@ janet_dep = declare_dependency(include_directories : incdir,
|
||||
|
||||
# Installation
|
||||
install_man('janet.1')
|
||||
install_man('jpm.1')
|
||||
install_headers(['src/include/janet.h', jconf], subdir: 'janet')
|
||||
janet_libs = [
|
||||
'auxlib/cook.janet',
|
||||
'auxlib/path.janet'
|
||||
]
|
||||
janet_binscripts = [
|
||||
'auxbin/jpm'
|
||||
]
|
||||
install_data(sources : janet_libs, install_dir : janet_path)
|
||||
install_data(sources : janet_binscripts, install_dir : 'bin')
|
||||
install_data(sources : ['tools/.keep'], install_dir : 'lib/janet')
|
||||
|
||||
@@ -14,4 +14,7 @@ option('int_types', type : 'boolean', value : true)
|
||||
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
|
||||
option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200)
|
||||
option('max_macro_expand', type : 'integer', min : 1, max : 8000, value : 200)
|
||||
option('stack_max', type : 'integer', min : 8096, max : 1000000000, value : 16384)
|
||||
option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7fffffff)
|
||||
|
||||
option('arch_name', type : 'string', value: '')
|
||||
option('os_name', type : 'string', value: '')
|
||||
|
||||
@@ -50,7 +50,7 @@ int main(int argc, const char **argv) {
|
||||
JanetArray *args = janet_array(argc);
|
||||
for (int i = 0; i < argc; i++)
|
||||
janet_array_push(args, janet_cstringv(argv[i]));
|
||||
janet_def(env, "process/args", janet_wrap_array(args), "Command line arguments.");
|
||||
janet_def(env, "boot/args", janet_wrap_array(args), "Command line arguments.");
|
||||
|
||||
/* Add in options from janetconf.h so boot.janet can configure the image as needed. */
|
||||
JanetTable *opts = janet_table(0);
|
||||
@@ -60,7 +60,7 @@ int main(int argc, const char **argv) {
|
||||
#ifdef JANET_NO_SOURCEMAPS
|
||||
janet_table_put(opts, janet_ckeywordv("no-sourcemaps"), janet_wrap_true());
|
||||
#endif
|
||||
janet_def(env, "process/config", janet_wrap_table(opts), "Boot options");
|
||||
janet_def(env, "boot/config", janet_wrap_table(opts), "Boot options");
|
||||
|
||||
/* Run bootstrap script to generate core image */
|
||||
const char *boot_file;
|
||||
|
||||
1008
src/boot/boot.janet
1008
src/boot/boot.janet
File diff suppressed because it is too large
Load Diff
@@ -27,10 +27,10 @@
|
||||
#define JANETCONF_H
|
||||
|
||||
#define JANET_VERSION_MAJOR 1
|
||||
#define JANET_VERSION_MINOR 0
|
||||
#define JANET_VERSION_MINOR 6
|
||||
#define JANET_VERSION_PATCH 0
|
||||
#define JANET_VERSION_EXTRA ""
|
||||
#define JANET_VERSION "1.0.0"
|
||||
#define JANET_VERSION_EXTRA "-dev"
|
||||
#define JANET_VERSION "1.6.0-dev"
|
||||
|
||||
/* #define JANET_BUILD "local" */
|
||||
|
||||
@@ -56,5 +56,7 @@
|
||||
/* #define JANET_MAX_PROTO_DEPTH 200 */
|
||||
/* #define JANET_MAX_MACRO_EXPAND 200 */
|
||||
/* #define JANET_STACK_MAX 16384 */
|
||||
/* #define JANET_OS_NAME my-custom-os */
|
||||
/* #define JANET_ARCH_NAME pdp-8 */
|
||||
|
||||
#endif /* end of include guard: JANETCONF_H */
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
@@ -33,6 +34,7 @@ JanetArray *janet_array(int32_t capacity) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
|
||||
Janet *data = NULL;
|
||||
if (capacity > 0) {
|
||||
janet_vm_next_collection += capacity * sizeof(Janet);
|
||||
data = (Janet *) malloc(sizeof(Janet) * capacity);
|
||||
if (NULL == data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
@@ -62,11 +64,14 @@ void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth) {
|
||||
Janet *newData;
|
||||
Janet *old = array->data;
|
||||
if (capacity <= array->capacity) return;
|
||||
capacity *= growth;
|
||||
int64_t new_capacity = ((int64_t) capacity) * growth;
|
||||
if (new_capacity > INT32_MAX) new_capacity = INT32_MAX;
|
||||
capacity = (int32_t) new_capacity;
|
||||
newData = realloc(old, capacity * sizeof(Janet));
|
||||
if (NULL == newData) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
janet_vm_next_collection += (capacity - array->capacity) * sizeof(Janet);
|
||||
array->data = newData;
|
||||
array->capacity = capacity;
|
||||
}
|
||||
@@ -153,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));
|
||||
@@ -256,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,
|
||||
@@ -268,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,
|
||||
|
||||
@@ -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},
|
||||
@@ -705,8 +706,8 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
if (!janet_checkint(tup[1])) {
|
||||
janet_asm_error(&a, "expected integer");
|
||||
}
|
||||
mapping.start = janet_unwrap_integer(tup[0]);
|
||||
mapping.end = janet_unwrap_integer(tup[1]);
|
||||
mapping.line = janet_unwrap_integer(tup[0]);
|
||||
mapping.column = janet_unwrap_integer(tup[1]);
|
||||
def->sourcemap[i] = mapping;
|
||||
}
|
||||
}
|
||||
@@ -749,31 +750,31 @@ static const JanetInstructionDef *janet_asm_reverse_lookup(uint32_t instr) {
|
||||
}
|
||||
|
||||
/* Create some constant sized tuples */
|
||||
static Janet tup1(Janet x) {
|
||||
static const Janet *tup1(Janet x) {
|
||||
Janet *tup = janet_tuple_begin(1);
|
||||
tup[0] = x;
|
||||
return janet_wrap_tuple(janet_tuple_end(tup));
|
||||
return janet_tuple_end(tup);
|
||||
}
|
||||
static Janet tup2(Janet x, Janet y) {
|
||||
static const Janet *tup2(Janet x, Janet y) {
|
||||
Janet *tup = janet_tuple_begin(2);
|
||||
tup[0] = x;
|
||||
tup[1] = y;
|
||||
return janet_wrap_tuple(janet_tuple_end(tup));
|
||||
return janet_tuple_end(tup);
|
||||
}
|
||||
static Janet tup3(Janet x, Janet y, Janet z) {
|
||||
static const Janet *tup3(Janet x, Janet y, Janet z) {
|
||||
Janet *tup = janet_tuple_begin(3);
|
||||
tup[0] = x;
|
||||
tup[1] = y;
|
||||
tup[2] = z;
|
||||
return janet_wrap_tuple(janet_tuple_end(tup));
|
||||
return janet_tuple_end(tup);
|
||||
}
|
||||
static Janet tup4(Janet w, Janet x, Janet y, Janet z) {
|
||||
static const Janet *tup4(Janet w, Janet x, Janet y, Janet z) {
|
||||
Janet *tup = janet_tuple_begin(4);
|
||||
tup[0] = w;
|
||||
tup[1] = x;
|
||||
tup[2] = y;
|
||||
tup[3] = z;
|
||||
return janet_wrap_tuple(janet_tuple_end(tup));
|
||||
return janet_tuple_end(tup);
|
||||
}
|
||||
|
||||
/* Given an argument, convert it to the appropriate integer or symbol */
|
||||
@@ -784,41 +785,56 @@ Janet janet_asm_decode_instruction(uint32_t instr) {
|
||||
return janet_wrap_integer((int32_t)instr);
|
||||
}
|
||||
name = janet_csymbolv(def->name);
|
||||
const Janet *ret = NULL;
|
||||
#define oparg(shift, mask) ((instr >> ((shift) << 3)) & (mask))
|
||||
switch (janet_instructions[def->opcode]) {
|
||||
case JINT_0:
|
||||
return tup1(name);
|
||||
ret = tup1(name);
|
||||
break;
|
||||
case JINT_S:
|
||||
return tup2(name, janet_wrap_integer(oparg(1, 0xFFFFFF)));
|
||||
ret = tup2(name, janet_wrap_integer(oparg(1, 0xFFFFFF)));
|
||||
break;
|
||||
case JINT_L:
|
||||
return tup2(name, janet_wrap_integer((int32_t)instr >> 8));
|
||||
ret = tup2(name, janet_wrap_integer((int32_t)instr >> 8));
|
||||
break;
|
||||
case JINT_SS:
|
||||
case JINT_ST:
|
||||
case JINT_SC:
|
||||
case JINT_SU:
|
||||
case JINT_SD:
|
||||
return tup3(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFFFF)));
|
||||
ret = tup3(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFFFF)));
|
||||
break;
|
||||
case JINT_SI:
|
||||
case JINT_SL:
|
||||
return tup3(name,
|
||||
ret = tup3(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer((int32_t)instr >> 16));
|
||||
break;
|
||||
case JINT_SSS:
|
||||
case JINT_SES:
|
||||
case JINT_SSU:
|
||||
return tup4(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFF)),
|
||||
janet_wrap_integer(oparg(3, 0xFF)));
|
||||
ret = tup4(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFF)),
|
||||
janet_wrap_integer(oparg(3, 0xFF)));
|
||||
break;
|
||||
case JINT_SSI:
|
||||
return tup4(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFF)),
|
||||
janet_wrap_integer((int32_t)instr >> 24));
|
||||
ret = tup4(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFF)),
|
||||
janet_wrap_integer((int32_t)instr >> 24));
|
||||
break;
|
||||
}
|
||||
#undef oparg
|
||||
if (ret) {
|
||||
/* Check if break point set */
|
||||
if (instr & 0x80) {
|
||||
janet_tuple_flag(ret) |= JANET_TUPLE_FLAG_BRACKETCTOR;
|
||||
}
|
||||
return janet_wrap_tuple(ret);
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
@@ -849,7 +865,7 @@ Janet janet_disasm(JanetFuncDef *def) {
|
||||
Janet src = def->constants[i];
|
||||
Janet dest;
|
||||
if (janet_checktype(src, JANET_TUPLE)) {
|
||||
dest = tup2(janet_csymbolv("quote"), src);
|
||||
dest = janet_wrap_tuple(tup2(janet_csymbolv("quote"), src));
|
||||
} else {
|
||||
dest = src;
|
||||
}
|
||||
@@ -870,8 +886,8 @@ Janet janet_disasm(JanetFuncDef *def) {
|
||||
for (i = 0; i < def->bytecode_length; i++) {
|
||||
Janet *t = janet_tuple_begin(2);
|
||||
JanetSourceMapping mapping = def->sourcemap[i];
|
||||
t[0] = janet_wrap_integer(mapping.start);
|
||||
t[1] = janet_wrap_integer(mapping.end);
|
||||
t[0] = janet_wrap_integer(mapping.line);
|
||||
t[1] = janet_wrap_integer(mapping.column);
|
||||
sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t));
|
||||
}
|
||||
sourcemap->count = def->bytecode_length;
|
||||
|
||||
@@ -24,12 +24,14 @@
|
||||
#include <janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
/* Initialize a buffer */
|
||||
JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
|
||||
uint8_t *data = NULL;
|
||||
if (capacity > 0) {
|
||||
janet_vm_next_collection += capacity;
|
||||
data = malloc(sizeof(uint8_t) * capacity);
|
||||
if (NULL == data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
@@ -57,8 +59,9 @@ void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth)
|
||||
uint8_t *new_data;
|
||||
uint8_t *old = buffer->data;
|
||||
if (capacity <= buffer->capacity) return;
|
||||
int64_t big_capacity = capacity * growth;
|
||||
int64_t big_capacity = ((int64_t) capacity) * growth;
|
||||
capacity = big_capacity > INT32_MAX ? INT32_MAX : (int32_t) big_capacity;
|
||||
janet_vm_next_collection += capacity - buffer->capacity;
|
||||
new_data = realloc(old, capacity * sizeof(uint8_t));
|
||||
if (NULL == new_data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
@@ -90,6 +93,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
|
||||
if (new_size > buffer->capacity) {
|
||||
int32_t new_capacity = new_size * 2;
|
||||
uint8_t *new_data = realloc(buffer->data, new_capacity * sizeof(uint8_t));
|
||||
janet_vm_next_collection += new_capacity - buffer->capacity;
|
||||
if (NULL == new_data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
@@ -238,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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
115
src/core/capi.c
115
src/core/capi.c
@@ -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));
|
||||
}
|
||||
@@ -101,14 +88,30 @@ type janet_get##name(const Janet *argv, int32_t n) { \
|
||||
return janet_unwrap_##name(x); \
|
||||
}
|
||||
|
||||
Janet janet_getmethod(const uint8_t *method, const JanetMethod *methods) {
|
||||
#define DEFINE_OPT(name, NAME, type) \
|
||||
type janet_opt##name(const Janet *argv, int32_t argc, int32_t n, type dflt) { \
|
||||
if (n >= argc) return dflt; \
|
||||
if (janet_checktype(argv[n], JANET_NIL)) return dflt; \
|
||||
return janet_get##name(argv, n); \
|
||||
}
|
||||
|
||||
#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++;
|
||||
}
|
||||
janet_panicf("unknown method %S invoked", method);
|
||||
return janet_wrap_nil();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_GETTER(number, NUMBER, double)
|
||||
@@ -126,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;
|
||||
@@ -135,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);
|
||||
}
|
||||
@@ -146,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);
|
||||
}
|
||||
@@ -222,11 +255,17 @@ JanetRange janet_getslice(int32_t argc, const Janet *argv) {
|
||||
range.start = 0;
|
||||
range.end = length;
|
||||
} else if (argc == 2) {
|
||||
range.start = janet_gethalfrange(argv, 1, length, "start");
|
||||
range.start = janet_checktype(argv[1], JANET_NIL)
|
||||
? 0
|
||||
: janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = length;
|
||||
} else {
|
||||
range.start = janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = janet_gethalfrange(argv, 2, length, "end");
|
||||
range.start = janet_checktype(argv[1], JANET_NIL)
|
||||
? 0
|
||||
: janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = janet_checktype(argv[2], JANET_NIL)
|
||||
? length
|
||||
: janet_gethalfrange(argv, 2, length, "end");
|
||||
if (range.end < range.start)
|
||||
range.end = range.start;
|
||||
}
|
||||
@@ -272,6 +311,36 @@ 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;
|
||||
return janet_getinteger(argv, n);
|
||||
}
|
||||
|
||||
int64_t janet_optinteger64(const Janet *argv, int32_t argc, int32_t n, int64_t dflt) {
|
||||
if (argc <= n) return dflt;
|
||||
if (janet_checktype(argv[n], JANET_NIL)) return dflt;
|
||||
return janet_getinteger64(argv, n);
|
||||
}
|
||||
|
||||
size_t janet_optsize(const Janet *argv, int32_t argc, int32_t n, size_t dflt) {
|
||||
if (argc <= n) return dflt;
|
||||
if (janet_checktype(argv[n], JANET_NIL)) return dflt;
|
||||
return janet_getsize(argv, n);
|
||||
}
|
||||
|
||||
void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetAbstractType *at, void *dflt) {
|
||||
if (argc <= n) return dflt;
|
||||
if (janet_checktype(argv[n], JANET_NIL)) return dflt;
|
||||
return janet_getabstract(argv, n, at);
|
||||
}
|
||||
|
||||
/* Some definitions for function-like macros */
|
||||
|
||||
JANET_API JanetStructHead *(janet_struct_head)(const JanetKV *st) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -320,33 +320,46 @@ JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Push slots load via janetc_toslots. */
|
||||
void janetc_pushslots(JanetCompiler *c, JanetSlot *slots) {
|
||||
/* Push slots loaded via janetc_toslots. Return the minimum number of slots pushed,
|
||||
* or -1 - min_arity if there is a splice. (if there is no splice, min_arity is also
|
||||
* the maximum possible arity). */
|
||||
int32_t janetc_pushslots(JanetCompiler *c, JanetSlot *slots) {
|
||||
int32_t i;
|
||||
int32_t count = janet_v_count(slots);
|
||||
int32_t min_arity = 0;
|
||||
int has_splice = 0;
|
||||
for (i = 0; i < count;) {
|
||||
if (slots[i].flags & JANET_SLOT_SPLICED) {
|
||||
janetc_emit_s(c, JOP_PUSH_ARRAY, slots[i], 0);
|
||||
i++;
|
||||
has_splice = 1;
|
||||
} else if (i + 1 == count) {
|
||||
janetc_emit_s(c, JOP_PUSH, slots[i], 0);
|
||||
i++;
|
||||
min_arity++;
|
||||
} else if (slots[i + 1].flags & JANET_SLOT_SPLICED) {
|
||||
janetc_emit_s(c, JOP_PUSH, slots[i], 0);
|
||||
janetc_emit_s(c, JOP_PUSH_ARRAY, slots[i + 1], 0);
|
||||
i += 2;
|
||||
min_arity++;
|
||||
has_splice = 1;
|
||||
} else if (i + 2 == count) {
|
||||
janetc_emit_ss(c, JOP_PUSH_2, slots[i], slots[i + 1], 0);
|
||||
i += 2;
|
||||
min_arity += 2;
|
||||
} else if (slots[i + 2].flags & JANET_SLOT_SPLICED) {
|
||||
janetc_emit_ss(c, JOP_PUSH_2, slots[i], slots[i + 1], 0);
|
||||
janetc_emit_s(c, JOP_PUSH_ARRAY, slots[i + 2], 0);
|
||||
i += 3;
|
||||
min_arity += 2;
|
||||
has_splice = 1;
|
||||
} else {
|
||||
janetc_emit_sss(c, JOP_PUSH_3, slots[i], slots[i + 1], slots[i + 2], 0);
|
||||
i += 3;
|
||||
min_arity += 3;
|
||||
}
|
||||
}
|
||||
return has_splice ? (-1 - min_arity) : min_arity;
|
||||
}
|
||||
|
||||
/* Check if a list of slots has any spliced slots */
|
||||
@@ -403,7 +416,67 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun) {
|
||||
/* TODO janet function inlining (no c functions)*/
|
||||
}
|
||||
if (!specialized) {
|
||||
janetc_pushslots(c, slots);
|
||||
int32_t min_arity = janetc_pushslots(c, slots);
|
||||
/* Check for provably incorrect function calls */
|
||||
if (fun.flags & JANET_SLOT_CONSTANT) {
|
||||
|
||||
/* Check for bad arity type if fun is a constant */
|
||||
switch (janet_type(fun.constant)) {
|
||||
case JANET_FUNCTION: {
|
||||
JanetFunction *f = janet_unwrap_function(fun.constant);
|
||||
int32_t min = f->def->min_arity;
|
||||
int32_t max = f->def->max_arity;
|
||||
if (min_arity < 0) {
|
||||
/* Call has splices */
|
||||
min_arity = -1 - min_arity;
|
||||
if (min_arity > max && max >= 0) {
|
||||
const uint8_t *es = janet_formatc(
|
||||
"%v expects at most %d argument, got at least %d",
|
||||
fun.constant, max, min_arity);
|
||||
janetc_error(c, es);
|
||||
}
|
||||
} else {
|
||||
/* Call has no splices */
|
||||
if (min_arity > max && max >= 0) {
|
||||
const uint8_t *es = janet_formatc(
|
||||
"%v expects at most %d argument, got %d",
|
||||
fun.constant, max, min_arity);
|
||||
janetc_error(c, es);
|
||||
}
|
||||
if (min_arity < min) {
|
||||
const uint8_t *es = janet_formatc(
|
||||
"%v expects at least %d argument, got %d",
|
||||
fun.constant, min, min_arity);
|
||||
janetc_error(c, es);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JANET_CFUNCTION:
|
||||
case JANET_ABSTRACT:
|
||||
break;
|
||||
case JANET_KEYWORD:
|
||||
if (min_arity == 0) {
|
||||
const uint8_t *es = janet_formatc("%v expects at least 1 argument, got 0",
|
||||
fun.constant);
|
||||
janetc_error(c, es);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (min_arity > 1 || min_arity == 0) {
|
||||
const uint8_t *es = janet_formatc("%v expects 1 argument, got %d",
|
||||
fun.constant, min_arity);
|
||||
janetc_error(c, es);
|
||||
}
|
||||
if (min_arity < -2) {
|
||||
const uint8_t *es = janet_formatc("%v expects 1 argument, got at least %d",
|
||||
fun.constant, -1 - min_arity);
|
||||
janetc_error(c, es);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((opts.flags & JANET_FOPTS_TAIL) &&
|
||||
/* Prevent top level tail calls for better errors */
|
||||
!(c->scope->flags & JANET_SCOPE_TOP)) {
|
||||
@@ -474,9 +547,9 @@ static int macroexpand1(
|
||||
if (janet_tuple_length(form) == 0)
|
||||
return 0;
|
||||
/* Source map - only set when we get a tuple */
|
||||
if (janet_tuple_sm_start(form) >= 0) {
|
||||
c->current_mapping.start = janet_tuple_sm_start(form);
|
||||
c->current_mapping.end = janet_tuple_sm_end(form);
|
||||
if (janet_tuple_sm_line(form) >= 0) {
|
||||
c->current_mapping.line = janet_tuple_sm_line(form);
|
||||
c->current_mapping.column = janet_tuple_sm_column(form);
|
||||
}
|
||||
/* Bracketed tuples are not specials or macros! */
|
||||
if (janet_tuple_flag(form) & JANET_TUPLE_FLAG_BRACKETCTOR)
|
||||
@@ -496,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);
|
||||
@@ -555,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 {
|
||||
@@ -664,15 +746,15 @@ static void janetc_init(JanetCompiler *c, JanetTable *env, const uint8_t *where)
|
||||
c->recursion_guard = JANET_RECURSION_GUARD;
|
||||
c->env = env;
|
||||
c->source = where;
|
||||
c->current_mapping.start = -1;
|
||||
c->current_mapping.end = -1;
|
||||
c->current_mapping.line = -1;
|
||||
c->current_mapping.column = -1;
|
||||
/* Init result */
|
||||
c->result.error = NULL;
|
||||
c->result.status = JANET_COMPILE_OK;
|
||||
c->result.funcdef = NULL;
|
||||
c->result.macrofiber = NULL;
|
||||
c->result.error_mapping.start = -1;
|
||||
c->result.error_mapping.end = -1;
|
||||
c->result.error_mapping.line = -1;
|
||||
c->result.error_mapping.column = -1;
|
||||
}
|
||||
|
||||
/* Deinitialize a compiler struct */
|
||||
@@ -733,8 +815,8 @@ static Janet cfun(int32_t argc, Janet *argv) {
|
||||
} else {
|
||||
JanetTable *t = janet_table(4);
|
||||
janet_table_put(t, janet_ckeywordv("error"), janet_wrap_string(res.error));
|
||||
janet_table_put(t, janet_ckeywordv("start"), janet_wrap_integer(res.error_mapping.start));
|
||||
janet_table_put(t, janet_ckeywordv("end"), janet_wrap_integer(res.error_mapping.end));
|
||||
janet_table_put(t, janet_ckeywordv("line"), janet_wrap_integer(res.error_mapping.line));
|
||||
janet_table_put(t, janet_ckeywordv("column"), janet_wrap_integer(res.error_mapping.column));
|
||||
if (res.macrofiber) {
|
||||
janet_table_put(t, janet_ckeywordv("fiber"), janet_wrap_fiber(res.macrofiber));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -214,7 +215,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len);
|
||||
JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds);
|
||||
|
||||
/* Push slots load via janetc_toslots. */
|
||||
void janetc_pushslots(JanetCompiler *c, JanetSlot *slots);
|
||||
int32_t janetc_pushslots(JanetCompiler *c, JanetSlot *slots);
|
||||
|
||||
/* Free slots loaded via janetc_toslots */
|
||||
void janetc_freeslots(JanetCompiler *c, JanetSlot *slots);
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include <math.h>
|
||||
#include "compile.h"
|
||||
#include "state.h"
|
||||
#include "util.h"
|
||||
@@ -45,7 +46,14 @@ typedef int Clib;
|
||||
typedef HINSTANCE Clib;
|
||||
#define load_clib(name) LoadLibrary((name))
|
||||
#define symbol_clib(lib, sym) GetProcAddress((lib), (sym))
|
||||
#define error_clib() "could not load dynamic library"
|
||||
static char error_clib_buf[256];
|
||||
static char *error_clib(void) {
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
error_clib_buf, sizeof(error_clib_buf), NULL);
|
||||
error_clib_buf[strlen(error_clib_buf) - 1] = '\0';
|
||||
return error_clib_buf;
|
||||
}
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
typedef void *Clib;
|
||||
@@ -169,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]);
|
||||
}
|
||||
@@ -218,6 +233,10 @@ static Janet janet_core_expand_path(int32_t argc, Janet *argv) {
|
||||
}
|
||||
dot_count = 0;
|
||||
} else {
|
||||
while (dot_count > 0) {
|
||||
--dot_count;
|
||||
*print++ = '.';
|
||||
}
|
||||
dot_count = -1;
|
||||
*print++ = *scan;
|
||||
}
|
||||
@@ -266,6 +285,7 @@ static Janet janet_core_native(int32_t argc, Janet *argv) {
|
||||
janet_panicf("could not load native %S: %S", path, error);
|
||||
}
|
||||
init(env);
|
||||
janet_table_put(env, janet_ckeywordv("native"), argv[0]);
|
||||
return janet_wrap_table(env);
|
||||
}
|
||||
|
||||
@@ -329,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)
|
||||
@@ -447,6 +482,24 @@ static Janet janet_core_untrace(int32_t argc, Janet *argv) {
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet janet_core_check_int(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
|
||||
double num = janet_unwrap_number(argv[0]);
|
||||
return janet_wrap_boolean(num == (double)((int32_t)num));
|
||||
ret_false:
|
||||
return janet_wrap_false();
|
||||
}
|
||||
|
||||
static Janet janet_core_check_nat(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
|
||||
double num = janet_unwrap_number(argv[0]);
|
||||
return janet_wrap_boolean(num >= 0 && (num == (double)((int32_t)num)));
|
||||
ret_false:
|
||||
return janet_wrap_false();
|
||||
}
|
||||
|
||||
static const JanetReg corelib_cfuns[] = {
|
||||
{
|
||||
"native", janet_core_native,
|
||||
@@ -545,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.")
|
||||
},
|
||||
{
|
||||
@@ -622,7 +675,28 @@ 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,
|
||||
JDOC("(int? x)\n\n"
|
||||
"Check if x can be exactly represented as a 32 bit signed two's complement integer.")
|
||||
},
|
||||
{
|
||||
"nat?", janet_core_check_nat,
|
||||
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}
|
||||
};
|
||||
@@ -705,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) */
|
||||
@@ -753,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++ */
|
||||
@@ -800,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 */
|
||||
@@ -829,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),
|
||||
@@ -839,9 +913,21 @@ 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_RETURN
|
||||
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 put_asm[] = {
|
||||
JOP_PUT | (1 << 16) | (2 << 24),
|
||||
@@ -859,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"
|
||||
@@ -874,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."));
|
||||
@@ -895,14 +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", 2, 2, 2, 2, get_asm, sizeof(get_asm),
|
||||
JDOC("(get ds key)\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."));
|
||||
"get", 3, 2, 3, 4, get_asm, sizeof(in_asm),
|
||||
JDOC("(get ds key &opt dflt)\n\n"
|
||||
"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"
|
||||
@@ -1016,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
|
||||
|
||||
@@ -52,31 +52,35 @@ void janet_debug_unbreak(JanetFuncDef *def, int32_t pc) {
|
||||
*/
|
||||
void janet_debug_find(
|
||||
JanetFuncDef **def_out, int32_t *pc_out,
|
||||
const uint8_t *source, int32_t offset) {
|
||||
const uint8_t *source, int32_t sourceLine, int32_t sourceColumn) {
|
||||
/* Scan the heap for right func def */
|
||||
JanetGCObject *current = janet_vm_blocks;
|
||||
/* Keep track of the best source mapping we have seen so far */
|
||||
int32_t besti = -1;
|
||||
int32_t best_range = INT32_MAX;
|
||||
int32_t best_line = -1;
|
||||
int32_t best_column = -1;
|
||||
JanetFuncDef *best_def = NULL;
|
||||
while (NULL != current) {
|
||||
if ((current->flags & JANET_MEM_TYPEBITS) == JANET_MEMORY_FUNCDEF) {
|
||||
JanetFuncDef *def = (JanetFuncDef *)(current + 1);
|
||||
JanetFuncDef *def = (JanetFuncDef *)(current);
|
||||
if (def->sourcemap &&
|
||||
def->source &&
|
||||
!janet_string_compare(source, def->source)) {
|
||||
/* Correct source file, check mappings. The chosen
|
||||
* pc index is the first match with the smallest range. */
|
||||
* pc index is the instruction closest to the given line column, but
|
||||
* not after. */
|
||||
int32_t i;
|
||||
for (i = 0; i < def->bytecode_length; i++) {
|
||||
int32_t start = def->sourcemap[i].start;
|
||||
int32_t end = def->sourcemap[i].end;
|
||||
if (end - start < best_range &&
|
||||
start <= offset &&
|
||||
end >= offset) {
|
||||
best_range = end - start;
|
||||
besti = i;
|
||||
best_def = def;
|
||||
int32_t line = def->sourcemap[i].line;
|
||||
int32_t column = def->sourcemap[i].column;
|
||||
if (line <= sourceLine && line >= best_line) {
|
||||
if (column <= sourceColumn &&
|
||||
(line > best_line || column > best_column)) {
|
||||
best_line = line;
|
||||
best_column = column;
|
||||
besti = i;
|
||||
best_def = def;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,11 +99,13 @@ 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) janet_eprintf("\x1b[31m");
|
||||
|
||||
while (fiber) {
|
||||
janet_v_push(fibers, fiber);
|
||||
fiber = fiber->child;
|
||||
@@ -117,46 +123,48 @@ 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, " at (%d:%d)", mapping.start, mapping.end);
|
||||
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) janet_eprintf("\x1b[0m");
|
||||
|
||||
janet_v_free(fibers);
|
||||
}
|
||||
|
||||
@@ -167,10 +175,11 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
|
||||
/* Helper to find funcdef and bytecode offset to insert or remove breakpoints.
|
||||
* Takes a source file name and byte offset. */
|
||||
static void helper_find(int32_t argc, Janet *argv, JanetFuncDef **def, int32_t *bytecode_offset) {
|
||||
janet_fixarity(argc, 2);
|
||||
janet_fixarity(argc, 3);
|
||||
const uint8_t *source = janet_getstring(argv, 0);
|
||||
int32_t source_offset = janet_getinteger(argv, 1);
|
||||
janet_debug_find(def, bytecode_offset, source, source_offset);
|
||||
int32_t line = janet_getinteger(argv, 1);
|
||||
int32_t col = janet_getinteger(argv, 2);
|
||||
janet_debug_find(def, bytecode_offset, source, line, col);
|
||||
}
|
||||
|
||||
/* Helper to find funcdef and bytecode offset to insert or remove breakpoints.
|
||||
@@ -257,8 +266,8 @@ static Janet doframe(JanetStackFrame *frame) {
|
||||
janet_table_put(t, janet_ckeywordv("pc"), janet_wrap_integer(off));
|
||||
if (def->sourcemap) {
|
||||
JanetSourceMapping mapping = def->sourcemap[off];
|
||||
janet_table_put(t, janet_ckeywordv("source-start"), janet_wrap_integer(mapping.start));
|
||||
janet_table_put(t, janet_ckeywordv("source-end"), janet_wrap_integer(mapping.end));
|
||||
janet_table_put(t, janet_ckeywordv("source-line"), janet_wrap_integer(mapping.line));
|
||||
janet_table_put(t, janet_ckeywordv("source-column"), janet_wrap_integer(mapping.column));
|
||||
}
|
||||
if (def->source) {
|
||||
janet_table_put(t, janet_ckeywordv("source"), janet_wrap_string(def->source));
|
||||
@@ -304,21 +313,29 @@ 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,
|
||||
JDOC("(debug/break source byte-offset)\n\n"
|
||||
"Sets a breakpoint with source a key at a given byte offset. An offset "
|
||||
"of 0 is the first byte in a file. Will throw an error if the breakpoint location "
|
||||
"Sets a breakpoint with source a key at a given line and column. "
|
||||
"Will throw an error if the breakpoint location "
|
||||
"cannot be found. For example\n\n"
|
||||
"\t(debug/break \"core.janet\" 1000)\n\n"
|
||||
"wil set a breakpoint at the 1000th byte of the file core.janet.")
|
||||
},
|
||||
{
|
||||
"debug/unbreak", cfun_debug_unbreak,
|
||||
JDOC("(debug/unbreak source byte-offset)\n\n"
|
||||
"Remove a breakpoint with a source key at a given byte offset. An offset "
|
||||
"of 0 is the first byte in a file. Will throw an error if the breakpoint "
|
||||
JDOC("(debug/unbreak source line column)\n\n"
|
||||
"Remove a breakpoint with a source key at a given line and column. "
|
||||
"Will throw an error if the breakpoint "
|
||||
"cannot be found.")
|
||||
},
|
||||
{
|
||||
@@ -372,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}
|
||||
};
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ static JanetFiber *fiber_alloc(int32_t capacity) {
|
||||
if (NULL == data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
janet_vm_next_collection += sizeof(Janet) * capacity;
|
||||
fiber->data = data;
|
||||
return fiber;
|
||||
}
|
||||
@@ -86,19 +87,27 @@ void janet_fiber_setcapacity(JanetFiber *fiber, int32_t n) {
|
||||
fiber->capacity = n;
|
||||
}
|
||||
|
||||
/* Grow fiber if needed */
|
||||
static void janet_fiber_grow(JanetFiber *fiber, int32_t needed) {
|
||||
int32_t cap = needed > (INT32_MAX / 2) ? INT32_MAX : 2 * needed;
|
||||
janet_fiber_setcapacity(fiber, cap);
|
||||
}
|
||||
|
||||
/* Push a value on the next stack frame */
|
||||
void janet_fiber_push(JanetFiber *fiber, Janet x) {
|
||||
if (fiber->stacktop == INT32_MAX) janet_panic("stack overflow");
|
||||
if (fiber->stacktop >= fiber->capacity) {
|
||||
janet_fiber_setcapacity(fiber, 2 * fiber->stacktop);
|
||||
janet_fiber_grow(fiber, fiber->stacktop);
|
||||
}
|
||||
fiber->data[fiber->stacktop++] = x;
|
||||
}
|
||||
|
||||
/* Push 2 values on the next stack frame */
|
||||
void janet_fiber_push2(JanetFiber *fiber, Janet x, Janet y) {
|
||||
if (fiber->stacktop >= INT32_MAX - 1) janet_panic("stack overflow");
|
||||
int32_t newtop = fiber->stacktop + 2;
|
||||
if (newtop > fiber->capacity) {
|
||||
janet_fiber_setcapacity(fiber, 2 * newtop);
|
||||
janet_fiber_grow(fiber, newtop);
|
||||
}
|
||||
fiber->data[fiber->stacktop] = x;
|
||||
fiber->data[fiber->stacktop + 1] = y;
|
||||
@@ -107,9 +116,10 @@ void janet_fiber_push2(JanetFiber *fiber, Janet x, Janet y) {
|
||||
|
||||
/* Push 3 values on the next stack frame */
|
||||
void janet_fiber_push3(JanetFiber *fiber, Janet x, Janet y, Janet z) {
|
||||
if (fiber->stacktop >= INT32_MAX - 2) janet_panic("stack overflow");
|
||||
int32_t newtop = fiber->stacktop + 3;
|
||||
if (newtop > fiber->capacity) {
|
||||
janet_fiber_setcapacity(fiber, 2 * newtop);
|
||||
janet_fiber_grow(fiber, newtop);
|
||||
}
|
||||
fiber->data[fiber->stacktop] = x;
|
||||
fiber->data[fiber->stacktop + 1] = y;
|
||||
@@ -119,9 +129,10 @@ void janet_fiber_push3(JanetFiber *fiber, Janet x, Janet y, Janet z) {
|
||||
|
||||
/* Push an array on the next stack frame */
|
||||
void janet_fiber_pushn(JanetFiber *fiber, const Janet *arr, int32_t n) {
|
||||
if (fiber->stacktop > INT32_MAX - n) janet_panic("stack overflow");
|
||||
int32_t newtop = fiber->stacktop + n;
|
||||
if (newtop > fiber->capacity) {
|
||||
janet_fiber_setcapacity(fiber, 2 * newtop);
|
||||
janet_fiber_grow(fiber, newtop);
|
||||
}
|
||||
memcpy(fiber->data + fiber->stacktop, arr, n * sizeof(Janet));
|
||||
fiber->stacktop = newtop;
|
||||
@@ -201,6 +212,7 @@ static void janet_env_detach(JanetFuncEnv *env) {
|
||||
if (env) {
|
||||
size_t s = sizeof(Janet) * env->length;
|
||||
Janet *vmem = malloc(s);
|
||||
janet_vm_next_collection += (uint32_t) s;
|
||||
if (NULL == vmem) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
247
src/core/io.c
247
src/core/io.c
@@ -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,
|
||||
|
||||
@@ -84,19 +84,36 @@ static Janet entry_getval(Janet env_entry) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a forward lookup table from an environment (for unmarshaling) */
|
||||
JanetTable *janet_env_lookup(JanetTable *env) {
|
||||
JanetTable *renv = janet_table(env->count);
|
||||
/* Merge values from an environment into an existing lookup table. */
|
||||
void janet_env_lookup_into(JanetTable *renv, JanetTable *env, const char *prefix, int recurse) {
|
||||
while (env) {
|
||||
for (int32_t i = 0; i < env->capacity; i++) {
|
||||
if (janet_checktype(env->data[i].key, JANET_SYMBOL)) {
|
||||
janet_table_put(renv,
|
||||
env->data[i].key,
|
||||
entry_getval(env->data[i].value));
|
||||
if (prefix) {
|
||||
int32_t prelen = (int32_t) strlen(prefix);
|
||||
const uint8_t *oldsym = janet_unwrap_symbol(env->data[i].key);
|
||||
int32_t oldlen = janet_string_length(oldsym);
|
||||
uint8_t *symbuf = janet_smalloc(prelen + oldlen);
|
||||
memcpy(symbuf, prefix, prelen);
|
||||
memcpy(symbuf + prelen, oldsym, oldlen);
|
||||
Janet s = janet_symbolv(symbuf, prelen + oldlen);
|
||||
janet_sfree(symbuf);
|
||||
janet_table_put(renv, s, entry_getval(env->data[i].value));
|
||||
} else {
|
||||
janet_table_put(renv,
|
||||
env->data[i].key,
|
||||
entry_getval(env->data[i].value));
|
||||
}
|
||||
}
|
||||
}
|
||||
env = env->proto;
|
||||
env = recurse ? env->proto : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a forward lookup table from an environment (for unmarshaling) */
|
||||
JanetTable *janet_env_lookup(JanetTable *env) {
|
||||
JanetTable *renv = janet_table(env->count);
|
||||
janet_env_lookup_into(renv, env, NULL, 1);
|
||||
return renv;
|
||||
}
|
||||
|
||||
@@ -241,9 +258,9 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
|
||||
int32_t current = 0;
|
||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||
JanetSourceMapping map = def->sourcemap[i];
|
||||
pushint(st, map.start - current);
|
||||
pushint(st, map.end - map.start);
|
||||
current = map.end;
|
||||
pushint(st, map.line - current);
|
||||
pushint(st, map.column);
|
||||
current = map.line;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,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++))
|
||||
|
||||
@@ -328,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);
|
||||
@@ -810,9 +832,8 @@ static const uint8_t *unmarshal_one_def(
|
||||
}
|
||||
for (int32_t i = 0; i < bytecode_length; i++) {
|
||||
current += readint(st, &data);
|
||||
def->sourcemap[i].start = current;
|
||||
current += readint(st, &data);
|
||||
def->sourcemap[i].end = current;
|
||||
def->sourcemap[i].line = current;
|
||||
def->sourcemap[i].column = readint(st, &data);
|
||||
}
|
||||
} else {
|
||||
def->sourcemap = NULL;
|
||||
@@ -967,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));
|
||||
@@ -1001,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;
|
||||
@@ -1070,7 +1107,7 @@ static const uint8_t *unmarshal_one(
|
||||
#else
|
||||
memcpy(&u.bytes, data + 1, sizeof(double));
|
||||
#endif
|
||||
*out = janet_wrap_number(u.d);
|
||||
*out = janet_wrap_number_safe(u.d);
|
||||
janet_v_push(st->lookup, *out);
|
||||
return data + 9;
|
||||
}
|
||||
|
||||
280
src/core/math.c
280
src/core/math.c
@@ -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,
|
||||
@@ -149,18 +338,28 @@ static const JanetReg math_cfuns[] = {
|
||||
{
|
||||
"math/log", janet_log,
|
||||
JDOC("(math/log x)\n\n"
|
||||
"Returns log base 2 of x.")
|
||||
"Returns log base natural number of x.")
|
||||
},
|
||||
{
|
||||
"math/log10", janet_log10,
|
||||
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
|
||||
}
|
||||
|
||||
216
src/core/os.c
216
src/core/os.c
@@ -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>
|
||||
@@ -64,20 +64,60 @@ extern char **environ;
|
||||
|
||||
/* Full OS functions */
|
||||
|
||||
#define janet_stringify1(x) #x
|
||||
#define janet_stringify(x) janet_stringify1(x)
|
||||
|
||||
static Janet os_which(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
#ifdef JANET_WINDOWS
|
||||
#if defined(JANET_OS_NAME)
|
||||
return janet_ckeywordv(janet_stringify(JANET_OS_NAME));
|
||||
#elif defined(JANET_WINDOWS)
|
||||
return janet_ckeywordv("windows");
|
||||
#elif __APPLE__
|
||||
#elif defined(__APPLE__)
|
||||
return janet_ckeywordv("macos");
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
return janet_ckeywordv("web");
|
||||
#elif defined(__linux__)
|
||||
return janet_ckeywordv("linux");
|
||||
#elif defined(__FreeBSD__)
|
||||
return janet_ckeywordv("freebsd");
|
||||
#elif defined(__NetBSD__)
|
||||
return janet_ckeywordv("netbsd");
|
||||
#elif defined(__OpenBSD__)
|
||||
return janet_ckeywordv("openbsd");
|
||||
#else
|
||||
return janet_ckeywordv("posix");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Detect the ISA we are compiled for */
|
||||
static Janet os_arch(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
/* Check 64-bit vs 32-bit */
|
||||
#if defined(JANET_ARCH_NAME)
|
||||
return janet_ckeywordv(janet_stringify(JANET_ARCH_NAME));
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
return janet_ckeywordv("wasm");
|
||||
#elif (defined(__x86_64__) || defined(_M_X64))
|
||||
return janet_ckeywordv("x86-64");
|
||||
#elif defined(__i386) || defined(_M_IX86)
|
||||
return janet_ckeywordv("x86");
|
||||
#elif defined(_M_ARM64) || defined(__aarch64__)
|
||||
return janet_ckeywordv("aarch64");
|
||||
#elif defined(_M_ARM) || defined(__arm__)
|
||||
return janet_ckeywordv("arm");
|
||||
#elif (defined(__sparc__))
|
||||
return janet_ckeywordv("sparc");
|
||||
#else
|
||||
return janet_ckeywordv("unknown");
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef janet_stringify1
|
||||
#undef janet_stringify
|
||||
|
||||
static Janet os_exit(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 0, 1);
|
||||
if (argc == 0) {
|
||||
@@ -255,7 +295,7 @@ static Janet os_execute(int32_t argc, Janet *argv) {
|
||||
#ifdef JANET_WINDOWS
|
||||
|
||||
JanetBuffer *buf = os_exec_escape(exargs);
|
||||
if (buf->count > 1025) {
|
||||
if (buf->count > 8191) {
|
||||
janet_panic("command line string too long");
|
||||
}
|
||||
const char *path = (const char *) janet_unwrap_string(exargs.items[0]);
|
||||
@@ -332,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);
|
||||
@@ -369,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. */
|
||||
@@ -409,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();
|
||||
}
|
||||
@@ -433,17 +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_info;
|
||||
struct tm t_infos;
|
||||
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);
|
||||
}
|
||||
t_info = localtime(&t);
|
||||
if (argc >= 2 && janet_truthy(argv[2])) {
|
||||
/* local time */
|
||||
#ifdef JANET_WINDOWS
|
||||
localtime_s(&t_infos, &t);
|
||||
t_info = &t_infos;
|
||||
#else
|
||||
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));
|
||||
@@ -468,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
|
||||
}
|
||||
@@ -492,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();
|
||||
}
|
||||
|
||||
@@ -504,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();
|
||||
}
|
||||
|
||||
@@ -532,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();
|
||||
}
|
||||
|
||||
@@ -761,15 +906,37 @@ static const JanetReg os_cfuns[] = {
|
||||
"os/which", os_which,
|
||||
JDOC("(os/which)\n\n"
|
||||
"Check the current operating system. Returns one of:\n\n"
|
||||
"\t:windows - Microsoft Windows\n"
|
||||
"\t:macos - Apple macos\n"
|
||||
"\t:windows\n"
|
||||
"\t:macos\n"
|
||||
"\t:web - Web assembly (emscripten)\n"
|
||||
"\t:linux\n"
|
||||
"\t:freebsd\n"
|
||||
"\t:openbsd\n"
|
||||
"\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"
|
||||
"Get the string value of an environment variable.")
|
||||
},
|
||||
{
|
||||
"os/arch", os_arch,
|
||||
JDOC("(os/arch)\n\n"
|
||||
"Check the ISA that janet was compiled for. Returns one of:\n\n"
|
||||
"\t:x86\n"
|
||||
"\t:x86-64\n"
|
||||
"\t:arm\n"
|
||||
"\t:aarch64\n"
|
||||
"\t:sparc\n"
|
||||
"\t:wasm\n"
|
||||
"\t:unknown\n")
|
||||
},
|
||||
#ifndef JANET_REDUCED_OS
|
||||
{
|
||||
"os/dir", os_dir,
|
||||
@@ -874,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"
|
||||
|
||||
@@ -38,11 +38,11 @@ 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] = {
|
||||
0x00000000, 0xf7ffec72, 0xc7ffffff, 0x17fffffe,
|
||||
0x00000000, 0xf7ffec72, 0xc7ffffff, 0x07fffffe,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
|
||||
};
|
||||
|
||||
@@ -106,7 +106,8 @@ struct JanetParseState {
|
||||
int32_t counter;
|
||||
int32_t argn;
|
||||
int flags;
|
||||
size_t start;
|
||||
size_t line;
|
||||
size_t column;
|
||||
Consumer consumer;
|
||||
};
|
||||
|
||||
@@ -153,7 +154,8 @@ static void pushstate(JanetParser *p, Consumer consumer, int flags) {
|
||||
s.argn = 0;
|
||||
s.flags = flags;
|
||||
s.consumer = consumer;
|
||||
s.start = p->offset;
|
||||
s.line = p->line;
|
||||
s.column = p->column;
|
||||
_pushstate(p, s);
|
||||
}
|
||||
|
||||
@@ -164,8 +166,8 @@ static void popstate(JanetParser *p, Janet val) {
|
||||
if (newtop->flags & PFLAG_CONTAINER) {
|
||||
/* Source mapping info */
|
||||
if (janet_checktype(val, JANET_TUPLE)) {
|
||||
janet_tuple_sm_start(janet_unwrap_tuple(val)) = (int32_t) top.start;
|
||||
janet_tuple_sm_end(janet_unwrap_tuple(val)) = (int32_t) p->offset;
|
||||
janet_tuple_sm_line(janet_unwrap_tuple(val)) = (int32_t) top.line;
|
||||
janet_tuple_sm_column(janet_unwrap_tuple(val)) = (int32_t) top.column;
|
||||
}
|
||||
newtop->argn++;
|
||||
/* Keep track of number of values in the root state */
|
||||
@@ -179,12 +181,13 @@ static void popstate(JanetParser *p, Janet val) {
|
||||
(c == '\'') ? "quote" :
|
||||
(c == ',') ? "unquote" :
|
||||
(c == ';') ? "splice" :
|
||||
(c == '|') ? "short-fn" :
|
||||
(c == '~') ? "quasiquote" : "<unknown>";
|
||||
t[0] = janet_csymbolv(which);
|
||||
t[1] = val;
|
||||
/* Quote source mapping info */
|
||||
janet_tuple_sm_start(t) = (int32_t) newtop->start;
|
||||
janet_tuple_sm_end(t) = (int32_t) p->offset;
|
||||
janet_tuple_sm_line(t) = (int32_t) newtop->line;
|
||||
janet_tuple_sm_column(t) = (int32_t) newtop->column;
|
||||
val = janet_wrap_tuple(janet_tuple_end(t));
|
||||
} else {
|
||||
return;
|
||||
@@ -230,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));
|
||||
@@ -294,7 +297,7 @@ static int stringchar(JanetParser *p, JanetParseState *state, uint8_t c) {
|
||||
return stringend(p, state);
|
||||
}
|
||||
/* normal char */
|
||||
if (c != '\n')
|
||||
if (c != '\n' && c != '\r')
|
||||
push_buf(p, c);
|
||||
return 1;
|
||||
}
|
||||
@@ -326,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);
|
||||
@@ -335,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;
|
||||
@@ -348,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);
|
||||
@@ -492,6 +498,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
|
||||
case ',':
|
||||
case ';':
|
||||
case '~':
|
||||
case '|':
|
||||
pushstate(p, root, PFLAG_READERMAC | c);
|
||||
return 1;
|
||||
case '"':
|
||||
@@ -560,7 +567,16 @@ static void janet_parser_checkdead(JanetParser *parser) {
|
||||
void janet_parser_consume(JanetParser *parser, uint8_t c) {
|
||||
int consumed = 0;
|
||||
janet_parser_checkdead(parser);
|
||||
parser->offset++;
|
||||
if (c == '\r') {
|
||||
parser->line++;
|
||||
parser->column = 0;
|
||||
} else if (c == '\n') {
|
||||
parser->column = 0;
|
||||
if (parser->lookback != '\r')
|
||||
parser->line++;
|
||||
} else {
|
||||
parser->column++;
|
||||
}
|
||||
while (!consumed && !parser->error) {
|
||||
JanetParseState *state = parser->states + parser->statecount - 1;
|
||||
consumed = state->consumer(parser, state, c);
|
||||
@@ -570,11 +586,14 @@ void janet_parser_consume(JanetParser *parser, uint8_t c) {
|
||||
|
||||
void janet_parser_eof(JanetParser *parser) {
|
||||
janet_parser_checkdead(parser);
|
||||
size_t oldcolumn = parser->column;
|
||||
size_t oldline = parser->line;
|
||||
janet_parser_consume(parser, '\n');
|
||||
if (parser->statecount > 1) {
|
||||
parser->error = "unexpected end of source";
|
||||
}
|
||||
parser->offset--;
|
||||
parser->line = oldline;
|
||||
parser->column = oldcolumn;
|
||||
parser->flag = 1;
|
||||
}
|
||||
|
||||
@@ -628,7 +647,8 @@ void janet_parser_init(JanetParser *parser) {
|
||||
parser->statecap = 0;
|
||||
parser->error = NULL;
|
||||
parser->lookback = -1;
|
||||
parser->offset = 0;
|
||||
parser->line = 1;
|
||||
parser->column = 0;
|
||||
parser->pending = 0;
|
||||
parser->flag = 0;
|
||||
|
||||
@@ -646,7 +666,8 @@ void janet_parser_clone(const JanetParser *src, JanetParser *dest) {
|
||||
dest->flag = src->flag;
|
||||
dest->pending = src->pending;
|
||||
dest->lookback = src->lookback;
|
||||
dest->offset = src->offset;
|
||||
dest->line = src->line;
|
||||
dest->column = src->column;
|
||||
dest->error = src->error;
|
||||
|
||||
/* Keep counts */
|
||||
@@ -709,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",
|
||||
@@ -769,7 +790,7 @@ static Janet cfun_parse_insert(int32_t argc, Janet *argv) {
|
||||
JanetParseState *s = p->states + p->statecount - 1;
|
||||
if (s->consumer == tokenchar) {
|
||||
janet_parser_consume(p, ' ');
|
||||
p->offset--;
|
||||
p->column--;
|
||||
s = p->states + p->statecount - 1;
|
||||
}
|
||||
if (s->flags & PFLAG_CONTAINER) {
|
||||
@@ -853,15 +874,12 @@ static Janet cfun_parse_flush(int32_t argc, Janet *argv) {
|
||||
}
|
||||
|
||||
static Janet cfun_parse_where(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
janet_fixarity(argc, 1);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parse_parsertype);
|
||||
if (argc > 1) {
|
||||
int32_t offset = janet_getinteger(argv, 1);
|
||||
p->offset = offset;
|
||||
return argv[0];
|
||||
} else {
|
||||
return janet_wrap_integer(p->offset);
|
||||
}
|
||||
Janet *tup = janet_tuple_begin(2);
|
||||
tup[0] = janet_wrap_integer(p->line);
|
||||
tup[1] = janet_wrap_integer(p->column);
|
||||
return janet_wrap_tuple(janet_tuple_end(tup));
|
||||
}
|
||||
|
||||
static Janet janet_wrap_parse_state(JanetParseState *s, Janet *args,
|
||||
@@ -926,8 +944,8 @@ static Janet janet_wrap_parse_state(JanetParseState *s, Janet *args,
|
||||
janet_table_put(state, janet_ckeywordv("buffer"), janet_wrap_string(buffer));
|
||||
}
|
||||
|
||||
janet_table_put(state, janet_ckeywordv("start"),
|
||||
janet_wrap_integer(s->start));
|
||||
janet_table_put(state, janet_ckeywordv("line"), janet_wrap_integer(s->line));
|
||||
janet_table_put(state, janet_ckeywordv("column"), janet_wrap_integer(s->column));
|
||||
return janet_wrap_table(state);
|
||||
}
|
||||
|
||||
@@ -1037,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[] = {
|
||||
@@ -1119,10 +1137,8 @@ static const JanetReg parse_cfuns[] = {
|
||||
},
|
||||
{
|
||||
"parser/where", cfun_parse_where,
|
||||
JDOC("(parser/where parser &opt offset)\n\n"
|
||||
"Returns the current line number and column number of the parser's location "
|
||||
"in the byte stream as an index, counted from 0. "
|
||||
"If offset is supplied, then the byte offset is updated to that new value.")
|
||||
JDOC("(parser/where parser)\n\n"
|
||||
"Returns the current line number and column of the parser's internal state.")
|
||||
},
|
||||
{
|
||||
"parser/eof", cfun_parse_eof,
|
||||
|
||||
141
src/core/peg.c
141
src/core/peg.c
@@ -59,6 +59,7 @@ typedef enum {
|
||||
RULE_MATCHTIME, /* [rule, constant, tag] */
|
||||
RULE_ERROR, /* [rule] */
|
||||
RULE_DROP, /* [rule] */
|
||||
RULE_BACKMATCH, /* [tag] */
|
||||
} Opcode;
|
||||
|
||||
/* Hold captured patterns and match state */
|
||||
@@ -417,6 +418,24 @@ tail:
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
case RULE_BACKMATCH: {
|
||||
uint32_t search = rule[1];
|
||||
for (int32_t i = s->tags->count - 1; i >= 0; i--) {
|
||||
if (s->tags->data[i] == search) {
|
||||
Janet capture = s->captures->data[i];
|
||||
if (!janet_checktype(capture, JANET_STRING))
|
||||
return NULL;
|
||||
const uint8_t *bytes = janet_unwrap_string(capture);
|
||||
int32_t len = janet_string_length(bytes);
|
||||
if (text + len > s->text_end)
|
||||
return NULL;
|
||||
return memcmp(text, bytes, len) ? NULL : text + len;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +445,6 @@ tail:
|
||||
|
||||
typedef struct {
|
||||
JanetTable *grammar;
|
||||
JanetTable *memoized;
|
||||
JanetTable *tags;
|
||||
Janet *constants;
|
||||
uint32_t *bytecode;
|
||||
@@ -754,12 +772,20 @@ static void spec_reference(Builder *b, int32_t argc, const Janet *argv) {
|
||||
emit_2(r, RULE_GETTAG, search, tag);
|
||||
}
|
||||
|
||||
static void spec_position(Builder *b, int32_t argc, const Janet *argv) {
|
||||
static void spec_tag1(Builder *b, int32_t argc, const Janet *argv, uint32_t op) {
|
||||
peg_arity(b, argc, 0, 1);
|
||||
Reserve r = reserve(b, 2);
|
||||
uint32_t tag = (argc) ? emit_tag(b, argv[0]) : 0;
|
||||
(void) argv;
|
||||
emit_1(r, RULE_POSITION, tag);
|
||||
emit_1(r, op, tag);
|
||||
}
|
||||
|
||||
static void spec_position(Builder *b, int32_t argc, const Janet *argv) {
|
||||
spec_tag1(b, argc, argv, RULE_POSITION);
|
||||
}
|
||||
|
||||
static void spec_backmatch(Builder *b, int32_t argc, const Janet *argv) {
|
||||
spec_tag1(b, argc, argv, RULE_BACKMATCH);
|
||||
}
|
||||
|
||||
static void spec_argument(Builder *b, int32_t argc, const Janet *argv) {
|
||||
@@ -824,6 +850,7 @@ static const SpecialPair peg_specials[] = {
|
||||
{"argument", spec_argument},
|
||||
{"at-least", spec_atleast},
|
||||
{"at-most", spec_atmost},
|
||||
{"backmatch", spec_backmatch},
|
||||
{"backref", spec_reference},
|
||||
{"between", spec_between},
|
||||
{"capture", spec_capture},
|
||||
@@ -850,27 +877,54 @@ static const SpecialPair peg_specials[] = {
|
||||
/* Compile a janet value into a rule and return the rule index. */
|
||||
static uint32_t peg_compile1(Builder *b, Janet peg) {
|
||||
|
||||
/* Check for already compiled rules */
|
||||
Janet check = janet_table_get(b->memoized, peg);
|
||||
if (!janet_checktype(check, JANET_NIL)) {
|
||||
uint32_t rule = (uint32_t) janet_unwrap_number(check);
|
||||
return rule;
|
||||
}
|
||||
|
||||
/* Keep track of the form being compiled for error purposes */
|
||||
Janet old_form = b->form;
|
||||
JanetTable *old_grammar = b->grammar;
|
||||
b->form = peg;
|
||||
|
||||
/* Check depth */
|
||||
if (b->depth-- == 0) {
|
||||
peg_panic(b, "peg grammar recursed too deeply");
|
||||
/* Resolve keyword references */
|
||||
int i = JANET_RECURSION_GUARD;
|
||||
JanetTable *grammar = old_grammar;
|
||||
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, "unknown rule");
|
||||
b->form = peg;
|
||||
b->grammar = grammar;
|
||||
}
|
||||
if (i == 0)
|
||||
peg_panic(b, "reference chain too deep");
|
||||
|
||||
/* Check cache - for tuples we check only the local cache, as
|
||||
* in a different grammar, the same tuple can compile to a different
|
||||
* rule - for example, (+ :a :b) depends on whatever :a and :b are bound to. */
|
||||
Janet check = janet_checktype(peg, JANET_TUPLE)
|
||||
? janet_table_rawget(grammar, peg)
|
||||
: janet_table_get(grammar, peg);
|
||||
if (!janet_checktype(check, JANET_NIL)) {
|
||||
b->form = old_form;
|
||||
b->grammar = old_grammar;
|
||||
return (uint32_t) janet_unwrap_number(check);
|
||||
}
|
||||
|
||||
/* Check depth */
|
||||
if (b->depth-- == 0)
|
||||
peg_panic(b, "peg grammar recursed too deeply");
|
||||
|
||||
/* The final rule to return */
|
||||
uint32_t rule = janet_v_count(b->bytecode);
|
||||
if (!janet_checktype(peg, JANET_KEYWORD) &&
|
||||
!janet_checktype(peg, JANET_STRUCT)) {
|
||||
janet_table_put(b->memoized, peg, janet_wrap_number(rule));
|
||||
|
||||
/* Add to cache. Do not cache structs, as we don't yet know
|
||||
* what rule they will return! We can just as effectively cache
|
||||
* the structs main rule. */
|
||||
if (!janet_checktype(peg, JANET_STRUCT)) {
|
||||
JanetTable *which_grammar = grammar;
|
||||
/* If we are a primitive pattern, add to the global cache (root grammar table) */
|
||||
if (!janet_checktype(peg, JANET_TUPLE)) {
|
||||
while (which_grammar->proto)
|
||||
which_grammar = which_grammar->proto;
|
||||
}
|
||||
janet_table_put(which_grammar, peg, janet_wrap_number(rule));
|
||||
}
|
||||
|
||||
switch (janet_type(peg)) {
|
||||
@@ -893,22 +947,22 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
|
||||
emit_bytes(b, RULE_LITERAL, len, str);
|
||||
break;
|
||||
}
|
||||
case JANET_KEYWORD: {
|
||||
Janet check = janet_table_get(b->grammar, peg);
|
||||
if (janet_checktype(check, JANET_NIL))
|
||||
peg_panic(b, "unknown rule");
|
||||
rule = peg_compile1(b, check);
|
||||
break;
|
||||
}
|
||||
case JANET_STRUCT: {
|
||||
JanetTable *grammar = janet_struct_to_table(janet_unwrap_struct(peg));
|
||||
grammar->proto = b->grammar;
|
||||
b->grammar = grammar;
|
||||
Janet main_rule = janet_table_get(grammar, janet_ckeywordv("main"));
|
||||
/* Build grammar table */
|
||||
const JanetKV *st = janet_unwrap_struct(peg);
|
||||
JanetTable *new_grammar = janet_table(2 * janet_struct_capacity(st));
|
||||
for (int32_t i = 0; i < janet_struct_capacity(st); i++) {
|
||||
if (janet_checktype(st[i].key, JANET_KEYWORD)) {
|
||||
janet_table_put(new_grammar, st[i].key, st[i].value);
|
||||
}
|
||||
}
|
||||
new_grammar->proto = grammar;
|
||||
b->grammar = grammar = new_grammar;
|
||||
/* Run the main rule */
|
||||
Janet main_rule = janet_table_rawget(grammar, janet_ckeywordv("main"));
|
||||
if (janet_checktype(main_rule, JANET_NIL))
|
||||
peg_panic(b, "grammar requires :main rule");
|
||||
rule = peg_compile1(b, main_rule);
|
||||
b->grammar = grammar->proto;
|
||||
break;
|
||||
}
|
||||
case JANET_TUPLE: {
|
||||
@@ -935,6 +989,7 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
|
||||
/* Increase depth again */
|
||||
b->depth++;
|
||||
b->form = old_form;
|
||||
b->grammar = old_grammar;
|
||||
return rule;
|
||||
}
|
||||
|
||||
@@ -962,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++)
|
||||
@@ -975,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);
|
||||
@@ -1029,6 +1088,7 @@ static void peg_unmarshal(void *p, JanetMarshalContext *ctx) {
|
||||
case RULE_NOTNCHAR:
|
||||
case RULE_RANGE:
|
||||
case RULE_POSITION:
|
||||
case RULE_BACKMATCH:
|
||||
/* [1 word] */
|
||||
i += 2;
|
||||
break;
|
||||
@@ -1120,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);
|
||||
@@ -1160,7 +1220,6 @@ static Peg *make_peg(Builder *b) {
|
||||
static Peg *compile_peg(Janet x) {
|
||||
Builder builder;
|
||||
builder.grammar = janet_table(0);
|
||||
builder.memoized = janet_table(0);
|
||||
builder.tags = janet_table(0);
|
||||
builder.constants = NULL;
|
||||
builder.bytecode = NULL;
|
||||
|
||||
@@ -30,14 +30,19 @@
|
||||
#endif
|
||||
|
||||
/* Implements a pretty printer for Janet. The pretty printer
|
||||
* is farily simple and not that flexible, but fast. */
|
||||
* is simple and not that flexible, but fast. */
|
||||
|
||||
/* Temporary buffer size */
|
||||
#define BUFSIZE 64
|
||||
|
||||
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:
|
||||
@@ -310,7 +317,7 @@ struct pretty {
|
||||
|
||||
static void print_newline(struct pretty *S, int just_a_space) {
|
||||
int i;
|
||||
if (just_a_space) {
|
||||
if (just_a_space || (S->flags & JANET_PRETTY_ONELINE)) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
return;
|
||||
}
|
||||
@@ -406,7 +413,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
if (S->depth == 0) {
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
} else {
|
||||
if (!isarray && len >= JANET_PRETTY_IND_ONELINE)
|
||||
if (!isarray && !(S->flags & JANET_PRETTY_ONELINE) && len >= JANET_PRETTY_IND_ONELINE)
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
if (is_dict_value && len >= JANET_PRETTY_IND_ONELINE) print_newline(S, 0);
|
||||
for (i = 0; i < len; i++) {
|
||||
@@ -725,12 +732,20 @@ void janet_buffer_format(
|
||||
janet_description_b(b, argv[arg]);
|
||||
break;
|
||||
}
|
||||
case 'Q':
|
||||
case 'q':
|
||||
case 'P':
|
||||
case 'p': { /* janet pretty , precision = depth */
|
||||
int depth = atoi(precision);
|
||||
if (depth < 1)
|
||||
depth = 4;
|
||||
janet_pretty_(b, depth, (strfrmt[-1] == 'P') ? JANET_PRETTY_COLOR : 0, argv[arg], startlen);
|
||||
char c = strfrmt[-1];
|
||||
int has_color = (c == 'P') || (c == 'Q');
|
||||
int has_oneline = (c == 'Q') || (c == 'q');
|
||||
int flags = 0;
|
||||
flags |= has_color ? JANET_PRETTY_COLOR : 0;
|
||||
flags |= has_oneline ? JANET_PRETTY_ONELINE : 0;
|
||||
janet_pretty_(b, depth, flags, argv[arg], startlen);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
@@ -116,7 +127,7 @@ static JanetSlot janetc_unquote(JanetFopts opts, int32_t argn, const Janet *argv
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
|
||||
/* Preform destructuring. Be careful to
|
||||
/* Perform destructuring. Be careful to
|
||||
* keep the order registers are freed.
|
||||
* Returns if the slot 'right' can be freed. */
|
||||
static int destructure(JanetCompiler *c,
|
||||
@@ -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);
|
||||
}
|
||||
@@ -175,8 +186,8 @@ static int destructure(JanetCompiler *c,
|
||||
static const Janet *janetc_make_sourcemap(JanetCompiler *c) {
|
||||
Janet *tup = janet_tuple_begin(3);
|
||||
tup[0] = c->source ? janet_wrap_string(c->source) : janet_wrap_nil();
|
||||
tup[1] = janet_wrap_integer(c->current_mapping.start);
|
||||
tup[2] = janet_wrap_integer(c->current_mapping.end);
|
||||
tup[1] = janet_wrap_integer(c->current_mapping.line);
|
||||
tup[2] = janet_wrap_integer(c->current_mapping.column);
|
||||
return janet_tuple_end(tup);
|
||||
}
|
||||
|
||||
@@ -282,12 +293,13 @@ static int varleaf(
|
||||
if (c->scope->flags & JANET_SCOPE_TOP) {
|
||||
/* Global var, generate var */
|
||||
JanetSlot refslot;
|
||||
JanetTable *entry = janet_table_clone(reftab);
|
||||
JanetArray *ref = janet_array(1);
|
||||
janet_array_push(ref, janet_wrap_nil());
|
||||
janet_table_put(reftab, janet_ckeywordv("ref"), janet_wrap_array(ref));
|
||||
janet_table_put(reftab, janet_ckeywordv("source-map"),
|
||||
janet_table_put(entry, janet_ckeywordv("ref"), janet_wrap_array(ref));
|
||||
janet_table_put(entry, janet_ckeywordv("source-map"),
|
||||
janet_wrap_tuple(janetc_make_sourcemap(c)));
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(reftab));
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(entry));
|
||||
refslot = janetc_cslot(janet_wrap_array(ref));
|
||||
janetc_emit_ssu(c, JOP_PUT_INDEX, refslot, s, 0, 0);
|
||||
return 1;
|
||||
@@ -312,13 +324,14 @@ static int defleaf(
|
||||
JanetSlot s,
|
||||
JanetTable *tab) {
|
||||
if (c->scope->flags & JANET_SCOPE_TOP) {
|
||||
janet_table_put(tab, janet_ckeywordv("source-map"),
|
||||
JanetTable *entry = janet_table_clone(tab);
|
||||
janet_table_put(entry, janet_ckeywordv("source-map"),
|
||||
janet_wrap_tuple(janetc_make_sourcemap(c)));
|
||||
JanetSlot valsym = janetc_cslot(janet_ckeywordv("value"));
|
||||
JanetSlot tabslot = janetc_cslot(janet_wrap_table(tab));
|
||||
JanetSlot tabslot = janetc_cslot(janet_wrap_table(entry));
|
||||
|
||||
/* Add env entry to env */
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(tab));
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(entry));
|
||||
|
||||
/* Put value in table when evaulated */
|
||||
janetc_emit_sss(c, JOP_PUT, tabslot, valsym, s, 0);
|
||||
@@ -600,6 +613,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
int32_t tempself = janetc_regalloc_temp(&tempscope.ra, JANETC_REGTEMP_0);
|
||||
janetc_emit(c, JOP_LOAD_SELF | (tempself << 8));
|
||||
janetc_emit(c, JOP_TAILCALL | (tempself << 8));
|
||||
janetc_regalloc_freetemp(&c->scope->ra, tempself, JANETC_REGTEMP_0);
|
||||
/* Compile function */
|
||||
JanetFuncDef *def = janetc_pop_funcdef(c);
|
||||
def->name = janet_cstring("_while");
|
||||
@@ -608,7 +622,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
int32_t cloreg = janetc_regalloc_temp(&c->scope->ra, JANETC_REGTEMP_0);
|
||||
janetc_emit(c, JOP_CLOSURE | (cloreg << 8) | (defindex << 16));
|
||||
janetc_emit(c, JOP_CALL | (cloreg << 8) | (cloreg << 16));
|
||||
janetc_regalloc_free(&c->scope->ra, cloreg);
|
||||
janetc_regalloc_freetemp(&c->scope->ra, cloreg, JANETC_REGTEMP_0);
|
||||
c->scope->flags |= JANET_SCOPE_CLOSURE;
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
@@ -658,8 +672,8 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
c->scope->flags |= JANET_SCOPE_CLOSURE;
|
||||
janetc_scope(&fnscope, c, JANET_SCOPE_FUNCTION, "function");
|
||||
|
||||
if (argn < 2) {
|
||||
errmsg = "expected at least 2 arguments to function literal";
|
||||
if (argn == 0) {
|
||||
errmsg = "expected at least 1 argument to function literal";
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -104,6 +104,9 @@ 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;
|
||||
@@ -167,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);
|
||||
}
|
||||
|
||||
@@ -378,15 +381,13 @@ static Janet cfun_string_split(int32_t argc, Janet *argv) {
|
||||
}
|
||||
findsetup(argc, argv, &state, 1);
|
||||
array = janet_array(0);
|
||||
while ((result = kmp_next(&state)) >= 0 && limit--) {
|
||||
while ((result = kmp_next(&state)) >= 0 && --limit) {
|
||||
const uint8_t *slice = janet_string(state.text + lastindex, result - lastindex);
|
||||
janet_array_push(array, janet_wrap_string(slice));
|
||||
lastindex = result + state.patlen;
|
||||
}
|
||||
{
|
||||
const uint8_t *slice = janet_string(state.text + lastindex, state.textlen - lastindex);
|
||||
janet_array_push(array, janet_wrap_string(slice));
|
||||
}
|
||||
const uint8_t *slice = janet_string(state.text + lastindex, state.textlen - lastindex);
|
||||
janet_array_push(array, janet_wrap_string(slice));
|
||||
kmp_deinit(&state);
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
@@ -403,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);
|
||||
@@ -499,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);
|
||||
}
|
||||
|
||||
@@ -523,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,
|
||||
@@ -600,16 +603,19 @@ static const JanetReg string_cfuns[] = {
|
||||
},
|
||||
{
|
||||
"string/split", cfun_string_split,
|
||||
JDOC("(string/split delim str)\n\n"
|
||||
JDOC("(string/split delim str &opt start limit)\n\n"
|
||||
"Splits a string str with delimiter delim and returns an array of "
|
||||
"substrings. The substrings will not contain the delimiter delim. If delim "
|
||||
"is not found, the returned array will have one element.")
|
||||
"is not found, the returned array will have one element. Will start searching "
|
||||
"for delim at the index start (if provided), and return up to a maximum "
|
||||
"of limit results (if provided).")
|
||||
},
|
||||
{
|
||||
"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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -137,6 +137,27 @@ Janet janet_table_get(JanetTable *t, Janet key) {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Get a value out of the table, and record which prototype it was from. */
|
||||
Janet janet_table_get_ex(JanetTable *t, Janet key, JanetTable **which) {
|
||||
JanetKV *bucket = janet_table_find(t, key);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL)) {
|
||||
*which = t;
|
||||
return bucket->value;
|
||||
}
|
||||
/* Check prototypes */
|
||||
{
|
||||
int i;
|
||||
for (i = JANET_MAX_PROTO_DEPTH, t = t->proto; t && i; t = t->proto, --i) {
|
||||
bucket = janet_table_find(t, key);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL)) {
|
||||
*which = t;
|
||||
return bucket->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Get a value out of the table. Don't check prototype tables. */
|
||||
Janet janet_table_rawget(JanetTable *t, Janet key) {
|
||||
JanetKV *bucket = janet_table_find(t, key);
|
||||
@@ -208,6 +229,21 @@ const JanetKV *janet_table_to_struct(JanetTable *t) {
|
||||
return janet_struct_end(st);
|
||||
}
|
||||
|
||||
/* Clone a table. */
|
||||
JanetTable *janet_table_clone(JanetTable *table) {
|
||||
JanetTable *newTable = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable));
|
||||
newTable->count = table->count;
|
||||
newTable->capacity = table->capacity;
|
||||
newTable->deleted = table->deleted;
|
||||
newTable->proto = table->proto;
|
||||
newTable->data = malloc(newTable->capacity * sizeof(JanetKV));
|
||||
if (NULL == newTable->data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(newTable->data, table->data, table->capacity * sizeof(JanetKV));
|
||||
return newTable;
|
||||
}
|
||||
|
||||
/* Merge a table or struct into a table */
|
||||
static void janet_table_mergekv(JanetTable *table, const JanetKV *kvs, int32_t cap) {
|
||||
int32_t i;
|
||||
@@ -268,6 +304,12 @@ static Janet cfun_table_rawget(int32_t argc, Janet *argv) {
|
||||
return janet_table_rawget(table, argv[1]);
|
||||
}
|
||||
|
||||
static Janet cfun_table_clone(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTable *table = janet_gettable(argv, 0);
|
||||
return janet_wrap_table(janet_table_clone(table));
|
||||
}
|
||||
|
||||
static const JanetReg table_cfuns[] = {
|
||||
{
|
||||
"table/new", cfun_table_new,
|
||||
@@ -301,6 +343,12 @@ static const JanetReg table_cfuns[] = {
|
||||
"If a table tab does not contain t directly, the function will return "
|
||||
"nil without checking the prototype. Returns the value in the table.")
|
||||
},
|
||||
{
|
||||
"table/clone", cfun_table_clone,
|
||||
JDOC("(table/clone tab)\n\n"
|
||||
"Create a copy of a table. Updates to the new table will not change the old table, "
|
||||
"and vice versa.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
650
src/core/thread.c
Normal file
650
src/core/thread.c
Normal 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
|
||||
@@ -33,8 +33,8 @@
|
||||
Janet *janet_tuple_begin(int32_t length) {
|
||||
size_t size = sizeof(JanetTupleHead) + (length * sizeof(Janet));
|
||||
JanetTupleHead *head = janet_gcalloc(JANET_MEMORY_TUPLE, size);
|
||||
head->sm_start = -1;
|
||||
head->sm_end = -1;
|
||||
head->sm_line = -1;
|
||||
head->sm_column = -1;
|
||||
head->length = length;
|
||||
return (Janet *)(head->data);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -119,16 +119,16 @@ static Janet cfun_tuple_sourcemap(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
const Janet *tup = janet_gettuple(argv, 0);
|
||||
Janet contents[2];
|
||||
contents[0] = janet_wrap_integer(janet_tuple_head(tup)->sm_start);
|
||||
contents[1] = janet_wrap_integer(janet_tuple_head(tup)->sm_end);
|
||||
contents[0] = janet_wrap_integer(janet_tuple_head(tup)->sm_line);
|
||||
contents[1] = janet_wrap_integer(janet_tuple_head(tup)->sm_column);
|
||||
return janet_wrap_tuple(janet_tuple_n(contents, 2));
|
||||
}
|
||||
|
||||
static Janet cfun_tuple_setmap(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 3);
|
||||
const Janet *tup = janet_gettuple(argv, 0);
|
||||
janet_tuple_head(tup)->sm_start = janet_getinteger(argv, 1);
|
||||
janet_tuple_head(tup)->sm_end = janet_getinteger(argv, 2);
|
||||
janet_tuple_head(tup)->sm_line = janet_getinteger(argv, 1);
|
||||
janet_tuple_head(tup)->sm_column = janet_getinteger(argv, 2);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
},
|
||||
{
|
||||
@@ -158,16 +161,14 @@ static const JanetReg tuple_cfuns[] = {
|
||||
{
|
||||
"tuple/sourcemap", cfun_tuple_sourcemap,
|
||||
JDOC("(tuple/sourcemap tup)\n\n"
|
||||
"Returns the sourcemap metadata attached to a tuple. "
|
||||
"The mapping is represented by a pair of byte offsets into the "
|
||||
"the source code representing the start and end byte indices where "
|
||||
"the tuple is. ")
|
||||
"Returns the sourcemap metadata attached to a tuple, "
|
||||
" which is another tuple (line, column).")
|
||||
},
|
||||
{
|
||||
"tuple/setmap", cfun_tuple_setmap,
|
||||
JDOC("(tuple/setmap tup start end)\n\n"
|
||||
"Set the sourcemap metadata on a tuple. start and end should "
|
||||
"be integers representing byte offsets into the file. Returns tup.")
|
||||
JDOC("(tuple/setmap tup line column)\n\n"
|
||||
"Set the sourcemap metadata on a tuple. line and column indicate "
|
||||
"should be integers.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
@@ -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,50 +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 Janet ta_getter(void *p, Janet key) {
|
||||
Janet value;
|
||||
static JanetMethod tarray_view_methods[6];
|
||||
|
||||
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, 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(array->as.f32[i]);
|
||||
*out = janet_wrap_number_safe(array->as.f32[i]);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_F64:
|
||||
value = janet_wrap_number(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",
|
||||
@@ -208,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) {
|
||||
@@ -441,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;
|
||||
@@ -551,6 +561,15 @@ static const JanetReg ta_cfuns[] = {
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
static JanetMethod tarray_view_methods[] = {
|
||||
{"length", cfun_typed_array_size},
|
||||
{"properties", cfun_typed_array_properties},
|
||||
{"copy-bytes", cfun_typed_array_copy_bytes},
|
||||
{"swap-bytes", cfun_typed_array_swap_bytes},
|
||||
{"slice", cfun_typed_array_slice},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_typed_array(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, ta_cfuns);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
160
src/core/value.c
160
src/core/value.c
@@ -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);
|
||||
}
|
||||
@@ -299,6 +337,38 @@ int32_t janet_length(Janet x) {
|
||||
return janet_struct_length(janet_unwrap_struct(x));
|
||||
case JANET_TABLE:
|
||||
return janet_unwrap_table(x)->count;
|
||||
case JANET_ABSTRACT: {
|
||||
Janet argv[1] = { x };
|
||||
Janet len = janet_mcall("length", 1, argv);
|
||||
if (!janet_checkint(len))
|
||||
janet_panicf("invalid integer length %v", len);
|
||||
return janet_unwrap_integer(len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Janet janet_lengthv(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
janet_panicf("expected %T, got %v", JANET_TFLAG_LENGTHABLE, x);
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
return janet_wrap_integer(janet_string_length(janet_unwrap_string(x)));
|
||||
case JANET_ARRAY:
|
||||
return janet_wrap_integer(janet_unwrap_array(x)->count);
|
||||
case JANET_BUFFER:
|
||||
return janet_wrap_integer(janet_unwrap_buffer(x)->count);
|
||||
case JANET_TUPLE:
|
||||
return janet_wrap_integer(janet_tuple_length(janet_unwrap_tuple(x)));
|
||||
case JANET_STRUCT:
|
||||
return janet_wrap_integer(janet_struct_length(janet_unwrap_struct(x)));
|
||||
case JANET_TABLE:
|
||||
return janet_wrap_integer(janet_unwrap_table(x)->count);
|
||||
case JANET_ABSTRACT: {
|
||||
Janet argv[1] = { x };
|
||||
return janet_mcall("length", 1, argv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,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: {
|
||||
@@ -350,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);
|
||||
}
|
||||
@@ -362,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) {
|
||||
|
||||
@@ -30,7 +30,8 @@ void *janet_v_grow(void *v, int32_t increment, int32_t itemsize) {
|
||||
int32_t dbl_cur = (NULL != v) ? 2 * janet_v__cap(v) : 0;
|
||||
int32_t min_needed = janet_v_count(v) + increment;
|
||||
int32_t m = dbl_cur > min_needed ? dbl_cur : min_needed;
|
||||
int32_t *p = (int32_t *) janet_srealloc(v ? janet_v__raw(v) : 0, itemsize * m + sizeof(int32_t) * 2);
|
||||
size_t newsize = ((size_t) itemsize) * m + sizeof(int32_t) * 2;
|
||||
int32_t *p = (int32_t *) janet_srealloc(v ? janet_v__raw(v) : 0, newsize);
|
||||
if (!v) p[1] = 0;
|
||||
p[0] = m;
|
||||
return p + 2;
|
||||
|
||||
401
src/core/vm.c
401
src/core/vm.c
@@ -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;
|
||||
@@ -57,7 +58,11 @@ JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf = NULL;
|
||||
/* How we dispatch instructions. By default, we use
|
||||
* a switch inside an infinite loop. For GCC/clang, we use
|
||||
* computed gotos. */
|
||||
#ifdef __GNUC__
|
||||
#if defined(__GNUC__) && !defined(__EMSCRIPTEN__)
|
||||
#define JANET_USE_COMPUTED_GOTOS
|
||||
#endif
|
||||
|
||||
#ifdef JANET_USE_COMPUTED_GOTOS
|
||||
#define VM_START() { goto *op_lookup[first_opcode];
|
||||
#define VM_END() }
|
||||
#define VM_OP(op) label_##op :
|
||||
@@ -113,9 +118,16 @@ JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf = NULL;
|
||||
{\
|
||||
Janet op1 = stack[B];\
|
||||
vm_assert_type(op1, JANET_NUMBER);\
|
||||
double x1 = janet_unwrap_number(op1);\
|
||||
stack[A] = janet_wrap_number(x1 op CS);\
|
||||
vm_pcnext();\
|
||||
if (!janet_checktype(op1, JANET_NUMBER)) {\
|
||||
vm_commit();\
|
||||
Janet _argv[2] = { op1, janet_wrap_number(CS) };\
|
||||
stack[A] = janet_mcall(#op, 2, _argv);\
|
||||
vm_pcnext();\
|
||||
} else {\
|
||||
double x1 = janet_unwrap_number(op1);\
|
||||
stack[A] = janet_wrap_number(x1 op CS);\
|
||||
vm_pcnext();\
|
||||
}\
|
||||
}
|
||||
#define _vm_bitop_immediate(op, type1)\
|
||||
{\
|
||||
@@ -131,12 +143,19 @@ JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf = NULL;
|
||||
{\
|
||||
Janet op1 = stack[B];\
|
||||
Janet op2 = stack[C];\
|
||||
vm_assert_type(op1, JANET_NUMBER);\
|
||||
vm_assert_type(op2, JANET_NUMBER);\
|
||||
double x1 = janet_unwrap_number(op1);\
|
||||
double x2 = janet_unwrap_number(op2);\
|
||||
stack[A] = wrap(x1 op x2);\
|
||||
vm_pcnext();\
|
||||
if (!janet_checktype(op1, JANET_NUMBER)) {\
|
||||
vm_commit();\
|
||||
Janet _argv[2] = { op1, op2 };\
|
||||
stack[A] = janet_mcall(#op, 2, _argv);\
|
||||
vm_pcnext();\
|
||||
} else {\
|
||||
vm_assert_type(op1, JANET_NUMBER);\
|
||||
vm_assert_type(op2, JANET_NUMBER);\
|
||||
double x1 = janet_unwrap_number(op1);\
|
||||
double x2 = janet_unwrap_number(op2);\
|
||||
stack[A] = wrap(x1 op x2);\
|
||||
vm_pcnext();\
|
||||
}\
|
||||
}
|
||||
#define vm_binop(op) _vm_binop(op, janet_wrap_number)
|
||||
#define vm_numcomp(op) _vm_binop(op, janet_wrap_boolean)
|
||||
@@ -175,7 +194,7 @@ static void vm_do_trace(JanetFunction *func) {
|
||||
static Janet call_nonfn(JanetFiber *fiber, Janet callee) {
|
||||
int32_t argn = fiber->stacktop - fiber->stackstart;
|
||||
Janet ds, key;
|
||||
if (argn != 1) janet_panicf("%v called with arity %d, expected 1", callee, argn);
|
||||
if (argn != 1) janet_panicf("%v called with %d arguments, possibly expected 1", callee, argn);
|
||||
if (janet_checktypes(callee, JANET_TFLAG_INDEXED | JANET_TFLAG_DICTIONARY |
|
||||
JANET_TFLAG_STRING | JANET_TFLAG_BUFFER | JANET_TFLAG_ABSTRACT)) {
|
||||
ds = callee;
|
||||
@@ -185,14 +204,24 @@ 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. */
|
||||
static Janet resolve_method(Janet name, JanetFiber *fiber) {
|
||||
int32_t argc = fiber->stacktop - fiber->stackstart;
|
||||
if (argc < 1) janet_panicf("method call (%v) takes at least 1 argument, got 0", name);
|
||||
Janet callee = janet_get(fiber->data[fiber->stackstart], name);
|
||||
if (janet_checktype(callee, JANET_NIL))
|
||||
janet_panicf("unknown method %v invoked on %v", name, fiber->data[fiber->stackstart]);
|
||||
return callee;
|
||||
}
|
||||
|
||||
/* Interpreter main loop */
|
||||
static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status) {
|
||||
|
||||
/* opcode -> label lookup if using clang/GCC */
|
||||
#ifdef __GNUC__
|
||||
#ifdef JANET_USE_COMPUTED_GOTOS
|
||||
static void *op_lookup[255] = {
|
||||
&&label_JOP_NOOP,
|
||||
&&label_JOP_ERROR,
|
||||
@@ -246,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,
|
||||
@@ -263,6 +293,190 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
|
||||
&&label_JOP_NUMERIC_GREATER_THAN,
|
||||
&&label_JOP_NUMERIC_GREATER_THAN_EQUAL,
|
||||
&&label_JOP_NUMERIC_EQUAL,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op
|
||||
};
|
||||
#endif
|
||||
@@ -277,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();
|
||||
@@ -582,9 +795,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
|
||||
}
|
||||
if (janet_checktype(callee, JANET_KEYWORD)) {
|
||||
vm_commit();
|
||||
int32_t argc = fiber->stacktop - fiber->stackstart;
|
||||
if (argc < 1) janet_panicf("method call takes at least 1 argument, got %d", argc);
|
||||
callee = janet_get(fiber->data[fiber->stackstart], callee);
|
||||
callee = resolve_method(callee, fiber);
|
||||
}
|
||||
if (janet_checktype(callee, JANET_FUNCTION)) {
|
||||
func = janet_unwrap_function(callee);
|
||||
@@ -616,11 +827,12 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
|
||||
|
||||
VM_OP(JOP_TAILCALL) {
|
||||
Janet callee = stack[D];
|
||||
if (fiber->stacktop > fiber->maxstack) {
|
||||
vm_throw("stack overflow");
|
||||
}
|
||||
if (janet_checktype(callee, JANET_KEYWORD)) {
|
||||
vm_commit();
|
||||
int32_t argc = fiber->stacktop - fiber->stackstart;
|
||||
if (argc < 1) janet_panicf("method call takes at least 1 argument, got %d", argc);
|
||||
callee = janet_get(fiber->data[fiber->stackstart], callee);
|
||||
callee = resolve_method(callee, fiber);
|
||||
}
|
||||
if (janet_checktype(callee, JANET_FUNCTION)) {
|
||||
func = janet_unwrap_function(callee);
|
||||
@@ -680,12 +892,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
|
||||
Janet fv = stack[C];
|
||||
vm_assert_type(fv, JANET_FIBER);
|
||||
JanetFiber *f = janet_unwrap_fiber(fv);
|
||||
JanetFiberStatus status = janet_fiber_status(f);
|
||||
if (status > JANET_STATUS_USER9) {
|
||||
vm_throw("cannot propagate from new or alive fiber");
|
||||
JanetFiberStatus sub_status = janet_fiber_status(f);
|
||||
if (sub_status > JANET_STATUS_USER9) {
|
||||
vm_commit();
|
||||
janet_panicf("cannot propagate from fiber with status :%s",
|
||||
janet_status_names[sub_status]);
|
||||
}
|
||||
janet_vm_fiber->child = f;
|
||||
vm_return((int) status, stack[B]);
|
||||
vm_return((int) sub_status, stack[B]);
|
||||
}
|
||||
|
||||
VM_OP(JOP_PUT)
|
||||
@@ -698,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]);
|
||||
@@ -710,7 +929,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in, JanetFiberStatus status)
|
||||
|
||||
VM_OP(JOP_LENGTH)
|
||||
vm_commit();
|
||||
stack[A] = janet_wrap_integer(janet_length(stack[E]));
|
||||
stack[A] = janet_lengthv(stack[E]);
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_MAKE_ARRAY) {
|
||||
@@ -737,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]);
|
||||
@@ -750,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]);
|
||||
@@ -787,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)
|
||||
@@ -833,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;
|
||||
}
|
||||
|
||||
@@ -906,6 +1193,36 @@ JanetSignal janet_pcall(
|
||||
return janet_continue(fiber, janet_wrap_nil(), out);
|
||||
}
|
||||
|
||||
Janet janet_mcall(const char *name, int32_t argc, Janet *argv) {
|
||||
/* At least 1 argument */
|
||||
if (argc < 1) janet_panicf("method :%s expected at least 1 argument");
|
||||
/* Find method */
|
||||
Janet method;
|
||||
if (janet_checktype(argv[0], JANET_ABSTRACT)) {
|
||||
void *abst = janet_unwrap_abstract(argv[0]);
|
||||
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(abst);
|
||||
if (!type->get || !(type->get)(abst, janet_ckeywordv(name), &method))
|
||||
janet_panicf("abstract value %v does not implement :%s", argv[0], name);
|
||||
} else if (janet_checktype(argv[0], JANET_TABLE)) {
|
||||
JanetTable *table = janet_unwrap_table(argv[0]);
|
||||
method = janet_table_get(table, janet_ckeywordv(name));
|
||||
} else if (janet_checktype(argv[0], JANET_STRUCT)) {
|
||||
const JanetKV *st = janet_unwrap_struct(argv[0]);
|
||||
method = janet_struct_get(st, janet_ckeywordv(name));
|
||||
} else {
|
||||
janet_panicf("could not find method :%s for %v", name, argv[0]);
|
||||
}
|
||||
/* Invoke method */
|
||||
if (janet_checktype(method, JANET_CFUNCTION)) {
|
||||
return (janet_unwrap_cfunction(method))(argc, argv);
|
||||
} else if (janet_checktype(method, JANET_FUNCTION)) {
|
||||
JanetFunction *fun = janet_unwrap_function(method);
|
||||
return janet_call(fun, argc, argv);
|
||||
} else {
|
||||
janet_panicf("method %s has unexpected value %v", name, method);
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup VM */
|
||||
int janet_init(void) {
|
||||
/* Garbage collection */
|
||||
@@ -928,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;
|
||||
}
|
||||
|
||||
@@ -940,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
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <math.h>
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
/* Macro fills */
|
||||
@@ -160,6 +162,7 @@ Janet(janet_wrap_number)(double x) {
|
||||
void *janet_memalloc_empty(int32_t count) {
|
||||
int32_t i;
|
||||
void *mem = malloc(count * sizeof(JanetKV));
|
||||
janet_vm_next_collection += count * sizeof(JanetKV);
|
||||
if (NULL == mem) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
@@ -182,6 +185,12 @@ void janet_memempty(JanetKV *mem, int32_t count) {
|
||||
|
||||
#ifdef JANET_NANBOX_64
|
||||
|
||||
Janet janet_wrap_number_safe(double d) {
|
||||
Janet ret;
|
||||
ret.number = isnan(d) ? NAN : d;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *janet_nanbox_to_pointer(Janet x) {
|
||||
x.i64 &= JANET_NANBOX_PAYLOADBITS;
|
||||
return x.pointer;
|
||||
@@ -222,6 +231,11 @@ Janet janet_wrap_number(double x) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
Janet janet_wrap_number_safe(double d) {
|
||||
double x = isnan(d) ? NAN : d;
|
||||
return janet_wrap_number(x);
|
||||
}
|
||||
|
||||
Janet janet_nanbox32_from_tagi(uint32_t tag, int32_t integer) {
|
||||
Janet ret;
|
||||
ret.tagged.type = tag;
|
||||
@@ -243,6 +257,10 @@ double janet_unwrap_number(Janet x) {
|
||||
|
||||
#else
|
||||
|
||||
Janet janet_wrap_number_safe(double d) {
|
||||
return janet_wrap_number(d);
|
||||
}
|
||||
|
||||
Janet janet_wrap_nil(void) {
|
||||
Janet y;
|
||||
y.type = JANET_NIL;
|
||||
@@ -298,3 +316,4 @@ JANET_WRAP_DEFINE(pointer, void *, JANET_POINTER, pointer)
|
||||
#undef JANET_WRAP_DEFINE
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -171,11 +176,10 @@ extern "C" {
|
||||
/* Maximum depth to follow table prototypes before giving up and returning nil. */
|
||||
#define JANET_MAX_MACRO_EXPAND 200
|
||||
|
||||
/* Define max stack size for stacks before raising a stack overflow error.
|
||||
* If this is not defined, fiber stacks can grow without limit (until memory
|
||||
* runs out) */
|
||||
/* Define default max stack size for stacks before raising a stack overflow error.
|
||||
* This can also be set on a per fiber basis. */
|
||||
#ifndef JANET_STACK_MAX
|
||||
#define JANET_STACK_MAX 16384
|
||||
#define JANET_STACK_MAX 0x7fffffff
|
||||
#endif
|
||||
|
||||
/* Use nanboxed values - uses 8 bytes per value instead of 12 or 16.
|
||||
@@ -229,18 +233,22 @@ 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>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Names of all of the types */
|
||||
JANET_API const char *const janet_type_names[16];
|
||||
JANET_API const char *const janet_signal_names[14];
|
||||
JANET_API const char *const janet_status_names[16];
|
||||
JANET_API extern const char *const janet_type_names[16];
|
||||
JANET_API extern const char *const janet_signal_names[14];
|
||||
JANET_API extern const char *const janet_status_names[16];
|
||||
|
||||
/* Fiber signals */
|
||||
typedef enum {
|
||||
@@ -317,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,
|
||||
@@ -756,8 +773,8 @@ struct JanetTupleHead {
|
||||
JanetGCObject gc;
|
||||
int32_t length;
|
||||
int32_t hash;
|
||||
int32_t sm_start;
|
||||
int32_t sm_end;
|
||||
int32_t sm_line;
|
||||
int32_t sm_column;
|
||||
const Janet data[];
|
||||
};
|
||||
|
||||
@@ -799,8 +816,8 @@ struct JanetAbstractHead {
|
||||
|
||||
/* Source mapping structure for a bytecode instruction */
|
||||
struct JanetSourceMapping {
|
||||
int32_t start;
|
||||
int32_t end;
|
||||
int32_t line;
|
||||
int32_t column;
|
||||
};
|
||||
|
||||
/* A function definition. Contains information needed to instantiate closures. */
|
||||
@@ -813,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 */
|
||||
@@ -870,7 +887,8 @@ struct JanetParser {
|
||||
size_t statecap;
|
||||
size_t bufcount;
|
||||
size_t bufcap;
|
||||
size_t offset;
|
||||
size_t line;
|
||||
size_t column;
|
||||
size_t pending;
|
||||
int lookback;
|
||||
int flag;
|
||||
@@ -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 offset);
|
||||
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);
|
||||
@@ -1134,39 +1176,39 @@ JANET_API void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x);
|
||||
#define janet_tuple_head(t) ((JanetTupleHead *)((char *)t - offsetof(JanetTupleHead, data)))
|
||||
#define janet_tuple_length(t) (janet_tuple_head(t)->length)
|
||||
#define janet_tuple_hash(t) (janet_tuple_head(t)->hash)
|
||||
#define janet_tuple_sm_start(t) (janet_tuple_head(t)->sm_start)
|
||||
#define janet_tuple_sm_end(t) (janet_tuple_head(t)->sm_end)
|
||||
#define janet_tuple_sm_line(t) (janet_tuple_head(t)->sm_line)
|
||||
#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,25 +1225,27 @@ 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);
|
||||
JANET_API JanetTable *janet_table_init(JanetTable *table, int32_t capacity);
|
||||
JANET_API void janet_table_deinit(JanetTable *table);
|
||||
JANET_API Janet janet_table_get(JanetTable *t, Janet key);
|
||||
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);
|
||||
|
||||
/* Fiber */
|
||||
JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv);
|
||||
@@ -1221,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(
|
||||
@@ -1242,6 +1286,7 @@ JANET_API Janet janet_unmarshal(
|
||||
JanetTable *reg,
|
||||
const uint8_t **next);
|
||||
JANET_API JanetTable *janet_env_lookup(JanetTable *env);
|
||||
JANET_API void janet_env_lookup_into(JanetTable *renv, JanetTable *env, const char *prefix, int recurse);
|
||||
|
||||
/* GC */
|
||||
JANET_API void janet_mark(Janet x);
|
||||
@@ -1261,32 +1306,39 @@ JANET_API int janet_verify(JanetFuncDef *def);
|
||||
|
||||
/* Pretty printing */
|
||||
#define JANET_PRETTY_COLOR 1
|
||||
#define JANET_PRETTY_ONELINE 2
|
||||
JANET_API JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, Janet x);
|
||||
|
||||
/* Misc */
|
||||
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);
|
||||
|
||||
/* VM functions */
|
||||
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 */
|
||||
@@ -1299,37 +1351,48 @@ 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 */
|
||||
#ifndef JANET_ENTRY_NAME
|
||||
#define JANET_ENTRY_NAME _janet_init
|
||||
#endif
|
||||
|
||||
#define JANET_MODULE_ENTRY \
|
||||
JANET_API JanetBuildConfig _janet_mod_config(void) { \
|
||||
return janet_config_current(); \
|
||||
} \
|
||||
JANET_API void _janet_init
|
||||
JANET_API void JANET_ENTRY_NAME
|
||||
|
||||
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);
|
||||
@@ -1337,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);
|
||||
@@ -1347,6 +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 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 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 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);
|
||||
@@ -1361,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);
|
||||
|
||||
@@ -1,94 +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))
|
||||
|
||||
# Flag handlers
|
||||
(def handlers :private
|
||||
{"h" (fn [&]
|
||||
(print "usage: " (get process/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 process/args (+ i 1))) 2)
|
||||
"c" (fn [i &]
|
||||
(def e (dofile (get process/args (+ i 1))))
|
||||
(spit (get process/args (+ i 2)) (make-image e))
|
||||
(set *no-file* false)
|
||||
3)
|
||||
"-" (fn [&] (set *handleopts* false) 1)
|
||||
"l" (fn [i &]
|
||||
(dofile (get process/args (+ i 1))
|
||||
:prefix "" :exit *exit-on-error*)
|
||||
2)
|
||||
"e" (fn [i &]
|
||||
(set *no-file* false)
|
||||
(eval-string (get process/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 1)
|
||||
(def lenargs (length process/args))
|
||||
(while (< i lenargs)
|
||||
(def arg (get process/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 offset (parser/where p))
|
||||
(string "janet:" offset ":" (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* "%.20P" "%.20p"))
|
||||
(repl getchunk onsig)))
|
||||
@@ -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 israwmode = 0;
|
||||
static const char *prompt = "> ";
|
||||
static int plen = 2;
|
||||
static char buf[JANET_LINE_MAX];
|
||||
static int len = 0;
|
||||
static int pos = 0;
|
||||
static int cols = 80;
|
||||
static char *history[JANET_HISTORY_MAX];
|
||||
static int history_count = 0;
|
||||
static int historyi = 0;
|
||||
static int sigint_flag = 0;
|
||||
static struct termios 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[] = {
|
||||
@@ -121,16 +123,15 @@ static char *sdup(const char *s) {
|
||||
static int rawmode() {
|
||||
struct termios t;
|
||||
if (!isatty(STDIN_FILENO)) goto fatal;
|
||||
if (tcgetattr(STDIN_FILENO, &termios_start) == -1) goto fatal;
|
||||
t = termios_start;
|
||||
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;
|
||||
t.c_cc[VTIME] = 0;
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) < 0) goto fatal;
|
||||
israwmode = 1;
|
||||
gbl_israwmode = 1;
|
||||
return 0;
|
||||
fatal:
|
||||
errno = ENOTTY;
|
||||
@@ -139,8 +140,8 @@ fatal:
|
||||
|
||||
/* Disable raw mode */
|
||||
static void norawmode() {
|
||||
if (israwmode && tcsetattr(STDIN_FILENO, TCSAFLUSH, &termios_start) != -1)
|
||||
israwmode = 0;
|
||||
if (gbl_israwmode && tcsetattr(STDIN_FILENO, TCSAFLUSH, &gbl_termios_start) != -1)
|
||||
gbl_israwmode = 0;
|
||||
}
|
||||
|
||||
static int curpos() {
|
||||
@@ -171,7 +172,9 @@ static int getcols() {
|
||||
if (cols > start) {
|
||||
char seq[32];
|
||||
snprintf(seq, 32, "\x1b[%dD", cols - start);
|
||||
if (write(STDOUT_FILENO, seq, strlen(seq)) == -1) {}
|
||||
if (write(STDOUT_FILENO, seq, strlen(seq)) == -1) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return cols;
|
||||
} else {
|
||||
@@ -182,7 +185,9 @@ failed:
|
||||
}
|
||||
|
||||
static void clear() {
|
||||
if (write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7) <= 0) {}
|
||||
if (write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7) <= 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void refresh() {
|
||||
@@ -190,38 +195,40 @@ static void refresh() {
|
||||
JanetBuffer b;
|
||||
|
||||
/* Keep cursor position on screen */
|
||||
char *_buf = buf;
|
||||
int _len = len;
|
||||
int _pos = pos;
|
||||
while ((plen + _pos) >= cols) {
|
||||
char *_buf = gbl_buf;
|
||||
int _len = gbl_len;
|
||||
int _pos = gbl_pos;
|
||||
while ((gbl_plen + _pos) >= gbl_cols) {
|
||||
_buf++;
|
||||
_len--;
|
||||
_pos--;
|
||||
}
|
||||
while ((plen + _len) > cols) {
|
||||
while ((gbl_plen + _len) > gbl_cols) {
|
||||
_len--;
|
||||
}
|
||||
|
||||
janet_buffer_init(&b, 0);
|
||||
/* Cursor to left edge, prompt and buffer */
|
||||
/* Cursor to left edge, gbl_prompt and buffer */
|
||||
janet_buffer_push_u8(&b, '\r');
|
||||
janet_buffer_push_cstring(&b, prompt);
|
||||
janet_buffer_push_cstring(&b, gbl_prompt);
|
||||
janet_buffer_push_bytes(&b, (uint8_t *) _buf, _len);
|
||||
/* Erase to right */
|
||||
janet_buffer_push_cstring(&b, "\x1b[0K");
|
||||
/* Move cursor to original position. */
|
||||
snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + plen));
|
||||
snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + gbl_plen));
|
||||
janet_buffer_push_cstring(&b, seq);
|
||||
if (write(STDOUT_FILENO, b.data, b.count) == -1) {}
|
||||
if (write(STDOUT_FILENO, b.data, b.count) == -1) {
|
||||
exit(1);
|
||||
}
|
||||
janet_buffer_deinit(&b);
|
||||
}
|
||||
|
||||
static int insert(char c) {
|
||||
if (len < JANET_LINE_MAX - 1) {
|
||||
if (len == pos) {
|
||||
buf[pos++] = c;
|
||||
buf[++len] = '\0';
|
||||
if (plen + len < cols) {
|
||||
if (gbl_len < JANET_LINE_MAX - 1) {
|
||||
if (gbl_len == gbl_pos) {
|
||||
gbl_buf[gbl_pos++] = c;
|
||||
gbl_buf[++gbl_len] = '\0';
|
||||
if (gbl_plen + gbl_len < gbl_cols) {
|
||||
/* Avoid a full update of the line in the
|
||||
* trivial case. */
|
||||
if (write(STDOUT_FILENO, &c, 1) == -1) return -1;
|
||||
@@ -229,9 +236,9 @@ static int insert(char c) {
|
||||
refresh();
|
||||
}
|
||||
} else {
|
||||
memmove(buf + pos + 1, buf + pos, len - pos);
|
||||
buf[pos++] = c;
|
||||
buf[++len] = '\0';
|
||||
memmove(gbl_buf + gbl_pos + 1, gbl_buf + gbl_pos, gbl_len - gbl_pos);
|
||||
gbl_buf[gbl_pos++] = c;
|
||||
gbl_buf[++gbl_len] = '\0';
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
@@ -239,21 +246,21 @@ static int insert(char c) {
|
||||
}
|
||||
|
||||
static void historymove(int delta) {
|
||||
if (history_count > 1) {
|
||||
free(history[historyi]);
|
||||
history[historyi] = sdup(buf);
|
||||
if (gbl_history_count > 1) {
|
||||
free(gbl_history[gbl_historyi]);
|
||||
gbl_history[gbl_historyi] = sdup(gbl_buf);
|
||||
|
||||
historyi += delta;
|
||||
if (historyi < 0) {
|
||||
historyi = 0;
|
||||
gbl_historyi += delta;
|
||||
if (gbl_historyi < 0) {
|
||||
gbl_historyi = 0;
|
||||
return;
|
||||
} else if (historyi >= history_count) {
|
||||
historyi = history_count - 1;
|
||||
} else if (gbl_historyi >= gbl_history_count) {
|
||||
gbl_historyi = gbl_history_count - 1;
|
||||
return;
|
||||
}
|
||||
strncpy(buf, history[historyi], JANET_LINE_MAX - 1);
|
||||
pos = len = strlen(buf);
|
||||
buf[len] = '\0';
|
||||
strncpy(gbl_buf, gbl_history[gbl_historyi], JANET_LINE_MAX - 1);
|
||||
gbl_pos = gbl_len = strlen(gbl_buf);
|
||||
gbl_buf[gbl_len] = '\0';
|
||||
|
||||
refresh();
|
||||
}
|
||||
@@ -261,62 +268,70 @@ static void historymove(int delta) {
|
||||
|
||||
static void addhistory() {
|
||||
int i, len;
|
||||
char *newline = sdup(buf);
|
||||
char *newline = sdup(gbl_buf);
|
||||
if (!newline) return;
|
||||
len = history_count;
|
||||
len = gbl_history_count;
|
||||
if (len < JANET_HISTORY_MAX) {
|
||||
history[history_count++] = newline;
|
||||
gbl_history[gbl_history_count++] = newline;
|
||||
len++;
|
||||
} else {
|
||||
free(history[JANET_HISTORY_MAX - 1]);
|
||||
free(gbl_history[JANET_HISTORY_MAX - 1]);
|
||||
}
|
||||
for (i = len - 1; i > 0; i--) {
|
||||
history[i] = history[i - 1];
|
||||
gbl_history[i] = gbl_history[i - 1];
|
||||
}
|
||||
history[0] = newline;
|
||||
gbl_history[0] = newline;
|
||||
}
|
||||
|
||||
static void replacehistory() {
|
||||
char *newline = sdup(buf);
|
||||
char *newline = sdup(gbl_buf);
|
||||
if (!newline) return;
|
||||
free(history[0]);
|
||||
history[0] = newline;
|
||||
free(gbl_history[0]);
|
||||
gbl_history[0] = newline;
|
||||
}
|
||||
|
||||
static void kleft() {
|
||||
if (pos > 0) {
|
||||
pos--;
|
||||
if (gbl_pos > 0) {
|
||||
gbl_pos--;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
static void kright() {
|
||||
if (pos != len) {
|
||||
pos++;
|
||||
if (gbl_pos != gbl_len) {
|
||||
gbl_pos++;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
static void kbackspace() {
|
||||
if (pos > 0) {
|
||||
memmove(buf + pos - 1, buf + pos, len - pos);
|
||||
pos--;
|
||||
buf[--len] = '\0';
|
||||
if (gbl_pos > 0) {
|
||||
memmove(gbl_buf + gbl_pos - 1, gbl_buf + gbl_pos, gbl_len - gbl_pos);
|
||||
gbl_pos--;
|
||||
gbl_buf[--gbl_len] = '\0';
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
cols = getcols();
|
||||
plen = 0;
|
||||
len = 0;
|
||||
pos = 0;
|
||||
while (prompt[plen]) plen++;
|
||||
buf[0] = '\0';
|
||||
gbl_cols = getcols();
|
||||
gbl_plen = 0;
|
||||
gbl_len = 0;
|
||||
gbl_pos = 0;
|
||||
while (gbl_prompt[gbl_plen]) gbl_plen++;
|
||||
gbl_buf[0] = '\0';
|
||||
|
||||
addhistory();
|
||||
|
||||
if (write(STDOUT_FILENO, prompt, plen) == -1) return -1;
|
||||
if (write(STDOUT_FILENO, gbl_prompt, gbl_plen) == -1) return -1;
|
||||
for (;;) {
|
||||
char c;
|
||||
int nread;
|
||||
@@ -337,7 +352,7 @@ static int line() {
|
||||
return 0;
|
||||
case 3: /* ctrl-c */
|
||||
errno = EAGAIN;
|
||||
sigint_flag = 1;
|
||||
gbl_sigint_flag = 1;
|
||||
return -1;
|
||||
case 127: /* backspace */
|
||||
case 8: /* ctrl-h */
|
||||
@@ -352,8 +367,8 @@ static int line() {
|
||||
kright();
|
||||
break;
|
||||
case 21:
|
||||
buf[0] = '\0';
|
||||
pos = len = 0;
|
||||
gbl_buf[0] = '\0';
|
||||
gbl_pos = gbl_len = 0;
|
||||
refresh();
|
||||
break;
|
||||
case 26: /* ctrl-z */
|
||||
@@ -378,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;
|
||||
}
|
||||
@@ -399,11 +417,11 @@ static int line() {
|
||||
kleft();
|
||||
break;
|
||||
case 'H':
|
||||
pos = 0;
|
||||
gbl_pos = 0;
|
||||
refresh();
|
||||
break;
|
||||
case 'F':
|
||||
pos = len;
|
||||
gbl_pos = gbl_len;
|
||||
refresh();
|
||||
break;
|
||||
}
|
||||
@@ -413,11 +431,11 @@ static int line() {
|
||||
default:
|
||||
break;
|
||||
case 'H':
|
||||
pos = 0;
|
||||
gbl_pos = 0;
|
||||
refresh();
|
||||
break;
|
||||
case 'F':
|
||||
pos = len;
|
||||
gbl_pos = gbl_len;
|
||||
refresh();
|
||||
break;
|
||||
}
|
||||
@@ -435,9 +453,9 @@ void janet_line_init() {
|
||||
void janet_line_deinit() {
|
||||
int i;
|
||||
norawmode();
|
||||
for (i = 0; i < history_count; i++)
|
||||
free(history[i]);
|
||||
historyi = 0;
|
||||
for (i = 0; i < gbl_history_count; i++)
|
||||
free(gbl_history[i]);
|
||||
gbl_historyi = 0;
|
||||
}
|
||||
|
||||
static int checktermsupport() {
|
||||
@@ -450,9 +468,9 @@ static int checktermsupport() {
|
||||
}
|
||||
|
||||
void janet_line_get(const char *p, JanetBuffer *buffer) {
|
||||
prompt = p;
|
||||
gbl_prompt = p;
|
||||
buffer->count = 0;
|
||||
historyi = 0;
|
||||
gbl_historyi = 0;
|
||||
FILE *out = janet_dynfile("out", stdout);
|
||||
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
|
||||
simpleline(buffer);
|
||||
@@ -464,19 +482,20 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
|
||||
}
|
||||
if (line()) {
|
||||
norawmode();
|
||||
if (sigint_flag) {
|
||||
if (gbl_sigint_flag) {
|
||||
raise(SIGINT);
|
||||
} else {
|
||||
fputc('\n', out);
|
||||
}
|
||||
return;
|
||||
}
|
||||
fflush(stdin);
|
||||
norawmode();
|
||||
fputc('\n', out);
|
||||
janet_buffer_ensure(buffer, len + 1, 2);
|
||||
memcpy(buffer->data, buf, len);
|
||||
buffer->data[len] = '\n';
|
||||
buffer->count = len + 1;
|
||||
janet_buffer_ensure(buffer, gbl_len + 1, 2);
|
||||
memcpy(buffer->data, gbl_buf, gbl_len);
|
||||
buffer->data[gbl_len] = '\n';
|
||||
buffer->count = gbl_len + 1;
|
||||
replacehistory();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -20,26 +20,26 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "line.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <shlwapi.h>
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
||||
#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;
|
||||
JanetTable *env;
|
||||
|
||||
/* Enable color console on windows 10 console and utf8 output. */
|
||||
#ifdef _WIN32
|
||||
/* Enable color console on windows 10 console and utf8 output. */
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD dwMode = 0;
|
||||
GetConsoleMode(hOut, &dwMode);
|
||||
@@ -61,12 +61,22 @@ int main(int argc, char **argv) {
|
||||
|
||||
/* Create args tuple */
|
||||
args = janet_array(argc);
|
||||
for (i = 0; i < argc; i++)
|
||||
for (i = 1; i < argc; i++)
|
||||
janet_array_push(args, janet_cstringv(argv[i]));
|
||||
janet_def(env, "process/args", janet_wrap_array(args), "Command line arguments.");
|
||||
|
||||
/* 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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
8
test/install/.gitignore
vendored
8
test/install/.gitignore
vendored
@@ -1 +1,9 @@
|
||||
/build
|
||||
.cache
|
||||
.manifests
|
||||
json.*
|
||||
jhydro.*
|
||||
circlet.*
|
||||
argparse.*
|
||||
sqlite3.*
|
||||
path.*
|
||||
|
||||
@@ -5,3 +5,6 @@
|
||||
:name "testmod"
|
||||
:source @["testmod.c"])
|
||||
|
||||
(declare-executable
|
||||
:name "testexec"
|
||||
:entry "testexec.janet")
|
||||
|
||||
5
test/install/testexec.janet
Normal file
5
test/install/testexec.janet
Normal file
@@ -0,0 +1,5 @@
|
||||
(use build/testmod)
|
||||
|
||||
(defn main [&]
|
||||
(print "Hello from executable!")
|
||||
(print (get5)))
|
||||
@@ -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")
|
||||
|
||||
@@ -303,5 +305,19 @@
|
||||
# Regression Test
|
||||
(assert (= 1 (((compile '(fn [] 1) @{})))) "regression test")
|
||||
|
||||
# Regression Test #137
|
||||
(def [a b c] (range 10))
|
||||
(assert (= a 0) "regression #137 (1)")
|
||||
(assert (= b 1) "regression #137 (2)")
|
||||
(assert (= c 2) "regression #137 (3)")
|
||||
|
||||
(var [x y z] (range 10))
|
||||
(assert (= x 0) "regression #137 (4)")
|
||||
(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)
|
||||
|
||||
|
||||
@@ -62,8 +62,7 @@
|
||||
|
||||
# String functions
|
||||
(assert (= 3 (string/find "abc" " abcdefghijklmnop")) "string/find 1")
|
||||
(assert (= nil (string/find "" "")) "string/find 2")
|
||||
(assert (= 0 (string/find "A" "A")) "string/find 3")
|
||||
(assert (= 0 (string/find "A" "A")) "string/find 2")
|
||||
(assert (string/has-prefix? "" "foo") "string/has-prefix? 1")
|
||||
(assert (string/has-prefix? "fo" "foo") "string/has-prefix? 2")
|
||||
(assert (not (string/has-prefix? "o" "foo")) "string/has-prefix? 3")
|
||||
@@ -98,6 +97,12 @@
|
||||
(assert (deep= (string/find-all "e" "onetwothree") @[2 9 10]) "string/find-all 1")
|
||||
(assert (deep= (string/find-all "," "onetwothree") @[]) "string/find-all 2")
|
||||
|
||||
(assert-error "string/find error 1" (string/find "" "abcd"))
|
||||
(assert-error "string/split error 1" (string/split "" "abcd"))
|
||||
(assert-error "string/replace error 1" (string/replace "" "." "abcd"))
|
||||
(assert-error "string/replace-all error 1" (string/replace-all "" "." "abcdabcd"))
|
||||
(assert-error "string/find-all error 1" (string/find-all "" "abcd"))
|
||||
|
||||
# Check if abstract test works
|
||||
(assert (abstract? stdout) "abstract? stdout")
|
||||
(assert (abstract? stdin) "abstract? stdin")
|
||||
|
||||
@@ -78,11 +78,15 @@
|
||||
|
||||
# Another regression test - no segfaults
|
||||
(defn afn [x] x)
|
||||
(assert (= 1 (try (afn) ([err] 1))) "bad arity 1")
|
||||
(var afn-var afn)
|
||||
(var identity-var identity)
|
||||
(var map-var map)
|
||||
(var not-var not)
|
||||
(assert (= 1 (try (afn-var) ([err] 1))) "bad arity 1")
|
||||
(assert (= 4 (try ((fn [x y] (+ x y)) 1) ([_] 4))) "bad arity 2")
|
||||
(assert (= 1 (try (identity) ([err] 1))) "bad arity 3")
|
||||
(assert (= 1 (try (map) ([err] 1))) "bad arity 4")
|
||||
(assert (= 1 (try (not) ([err] 1))) "bad arity 5")
|
||||
(assert (= 1 (try (identity-var) ([err] 1))) "bad arity 3")
|
||||
(assert (= 1 (try (map-var) ([err] 1))) "bad arity 4")
|
||||
(assert (= 1 (try (not-var) ([err] 1))) "bad arity 5")
|
||||
|
||||
# Assembly test
|
||||
# Fibonacci sequence, implemented with naive recursion.
|
||||
@@ -113,9 +117,9 @@
|
||||
|
||||
(assert (= 1 ({:ok 1} :ok)) "calling struct")
|
||||
(assert (= 2 (@{:ok 2} :ok)) "calling table")
|
||||
(assert (= :bad (try (@{:ok 2} :ok :no) ([err] :bad))) "calling table too many arguments")
|
||||
(assert (= :bad (try (:ok @{:ok 2} :no) ([err] :bad))) "calling keyword too many arguments")
|
||||
(assert (= :oops (try (1 1) ([err] :oops))) "calling number fails")
|
||||
(assert (= :bad (try ((identity @{:ok 2}) :ok :no) ([err] :bad))) "calling table too many arguments")
|
||||
(assert (= :bad (try ((identity :ok) @{:ok 2} :no) ([err] :bad))) "calling keyword too many arguments")
|
||||
(assert (= :oops (try ((+ 2 -1) 1) ([err] :oops))) "calling number fails")
|
||||
|
||||
# Method test
|
||||
|
||||
@@ -356,6 +360,38 @@
|
||||
(check-match janet-longstring "``` `` ```" true)
|
||||
(check-match janet-longstring "`` ```" false)
|
||||
|
||||
# Backmatch
|
||||
|
||||
(def backmatcher-1 '(* (capture (any "x") :1) "y" (backmatch :1) -1))
|
||||
|
||||
(check-match backmatcher-1 "y" true)
|
||||
(check-match backmatcher-1 "xyx" true)
|
||||
(check-match backmatcher-1 "xxxxxxxyxxxxxxx" true)
|
||||
(check-match backmatcher-1 "xyxx" false)
|
||||
(check-match backmatcher-1 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy" false)
|
||||
(check-match backmatcher-1 (string (string/repeat "x" 10000) "y") false)
|
||||
(check-match backmatcher-1 (string (string/repeat "x" 10000) "y" (string/repeat "x" 10000)) true)
|
||||
|
||||
(def backmatcher-2 '(* '(any "x") "y" (backmatch) -1))
|
||||
|
||||
(check-match backmatcher-2 "y" true)
|
||||
(check-match backmatcher-2 "xyx" true)
|
||||
(check-match backmatcher-2 "xxxxxxxyxxxxxxx" true)
|
||||
(check-match backmatcher-2 "xyxx" false)
|
||||
(check-match backmatcher-2 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy" false)
|
||||
(check-match backmatcher-2 (string (string/repeat "x" 10000) "y") false)
|
||||
(check-match backmatcher-2 (string (string/repeat "x" 10000) "y" (string/repeat "x" 10000)) true)
|
||||
|
||||
(def longstring-2 '(* '(some "`") (some (if-not (backmatch) 1)) (backmatch) -1))
|
||||
|
||||
(check-match longstring-2 "`john" false)
|
||||
(check-match longstring-2 "abc" false)
|
||||
(check-match longstring-2 "` `" true)
|
||||
(check-match longstring-2 "` `" true)
|
||||
(check-match longstring-2 "`` ``" true)
|
||||
(check-match longstring-2 "``` `` ```" true)
|
||||
(check-match longstring-2 "`` ```" false)
|
||||
|
||||
# Optional
|
||||
|
||||
(check-match '(* (opt "hi") -1) "" true)
|
||||
@@ -389,4 +425,22 @@
|
||||
(assert (= (tuple/type (-> '(1 2 3) marshal unmarshal)) :parens) "normal tuple marshalled/unmarshalled")
|
||||
(assert (= (tuple/type (-> '[1 2 3] marshal unmarshal)) :brackets) "normal tuple marshalled/unmarshalled")
|
||||
|
||||
# Check for bad memoization (+ :a) should mean different things in different contexts.
|
||||
(def redef-a
|
||||
~{:a "abc"
|
||||
:c (+ :a)
|
||||
:main (* :c {:a "def" :main (+ :a)} -1)})
|
||||
|
||||
(check-match redef-a "abcdef" true)
|
||||
(check-match redef-a "abcabc" false)
|
||||
(check-match redef-a "defdef" false)
|
||||
|
||||
(def redef-b
|
||||
~{:pork {:pork "beef" :main (+ -1 (* 1 :pork))}
|
||||
:main :pork})
|
||||
|
||||
(check-match redef-b "abeef" true)
|
||||
(check-match redef-b "aabeef" false)
|
||||
(check-match redef-b "aaaaaa" false)
|
||||
|
||||
(end-suite)
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
(assert (= (a 2) (b 1) ) "tarray views pointing same buffer")
|
||||
(assert (= ((tarray/slice b) 3) (b 3) (a 6) 6) "tarray slice")
|
||||
(assert (= ((tarray/slice b 1) 2) (b 3) (a 6) 6) "tarray slice")
|
||||
(assert (= (:length a) (length a)) "length method and function")
|
||||
|
||||
(assert (= ((unmarshal (marshal b)) 3) (b 3)) "marshal")
|
||||
|
||||
@@ -80,13 +81,49 @@
|
||||
(assert-no-error "break 3" (for i 0 10 (if (> i 8) (break i))))
|
||||
(assert-no-error "break 4" ((fn [i] (if (> i 8) (break i))) 100))
|
||||
|
||||
# take
|
||||
|
||||
(assert (deep= (take 0 []) []) "take 1")
|
||||
(assert (deep= (take 10 []) []) "take 2")
|
||||
(assert (deep= (take 0 [1 2 3 4 5]) []) "take 3")
|
||||
(assert (deep= (take 10 [1 2 3]) [1 2 3]) "take 4")
|
||||
(assert (deep= (take -1 [:a :b :c]) []) "take 5")
|
||||
(assert-error :invalid-type (take 3 {}) "take 6")
|
||||
|
||||
# take-until
|
||||
|
||||
(assert (deep= (take-until pos? @[]) []) "take-until 1")
|
||||
(assert (deep= (take-until pos? @[1 2 3]) []) "take-until 2")
|
||||
(assert (deep= (take-until pos? @[-1 -2 -3]) [-1 -2 -3]) "take-until 3")
|
||||
(assert (deep= (take-until pos? @[-1 -2 3]) [-1 -2]) "take-until 4")
|
||||
(assert (deep= (take-until pos? @[-1 1 -2]) [-1]) "take-until 5")
|
||||
(assert (deep= (take-until |(= $ 115) "books") "book") "take-until 6")
|
||||
|
||||
# take-while
|
||||
|
||||
(assert (deep= (take-while neg? @[]) []) "take-while 1")
|
||||
(assert (deep= (take-while neg? @[1 2 3]) []) "take-while 2")
|
||||
(assert (deep= (take-while neg? @[-1 -2 -3]) [-1 -2 -3]) "take-while 3")
|
||||
(assert (deep= (take-while neg? @[-1 -2 3]) [-1 -2]) "take-while 4")
|
||||
(assert (deep= (take-while neg? @[-1 1 -2]) [-1]) "take-while 5")
|
||||
|
||||
# drop
|
||||
|
||||
(assert (deep= (drop 0 []) []) "drop 1")
|
||||
(assert (deep= (drop 10 []) []) "drop 2")
|
||||
(assert (deep= (drop 0 [1 2 3 4 5]) [1 2 3 4 5]) "drop 3")
|
||||
(assert (deep= (drop 10 [1 2 3]) []) "drop 4")
|
||||
(assert (deep= (drop -2 [:a :b :c]) [:a :b :c]) "drop 5")
|
||||
(assert-error :invalid-type (drop 3 {}) "drop 6")
|
||||
|
||||
# drop-until
|
||||
|
||||
(assert (deep= (drop-until pos? @[]) @[]) "drop-until 1")
|
||||
(assert (deep= (drop-until pos? @[1 2 3]) @[1 2 3]) "drop-until 2")
|
||||
(assert (deep= (drop-until pos? @[-1 -2 -3]) @[]) "drop-until 3")
|
||||
(assert (deep= (drop-until pos? @[-1 -2 3]) @[3]) "drop-until 4")
|
||||
(assert (deep= (drop-until pos? @[-1 1 -2]) @[1 -2]) "drop-until 5")
|
||||
(assert (deep= (drop-until pos? @[]) []) "drop-until 1")
|
||||
(assert (deep= (drop-until pos? @[1 2 3]) [1 2 3]) "drop-until 2")
|
||||
(assert (deep= (drop-until pos? @[-1 -2 -3]) []) "drop-until 3")
|
||||
(assert (deep= (drop-until pos? @[-1 -2 3]) [3]) "drop-until 4")
|
||||
(assert (deep= (drop-until pos? @[-1 1 -2]) [1 -2]) "drop-until 5")
|
||||
(assert (deep= (drop-until |(= $ 115) "books") "s") "drop-until 6")
|
||||
|
||||
# Quasiquote bracketed tuples
|
||||
(assert (= (tuple/type ~[1 2 3]) (tuple/type '[1 2 3])) "quasiquote bracket tuples")
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -105,4 +105,176 @@
|
||||
(def res (resume f))
|
||||
(assert-error :abc (propagate res f) "propagate 1")
|
||||
|
||||
# table/clone
|
||||
|
||||
(defn check-table-clone [x msg]
|
||||
(assert (= (table/to-struct x) (table/to-struct (table/clone x))) msg))
|
||||
|
||||
(check-table-clone @{:a 123 :b 34 :c :hello : 945 0 1 2 3 4 5} "table/clone 1")
|
||||
(check-table-clone @{} "table/clone 1")
|
||||
|
||||
# Issue #142
|
||||
|
||||
(def buffer (tarray/buffer 8))
|
||||
(def buffer-float64-view (tarray/new :float64 1 1 0 buffer))
|
||||
(def buffer-uint32-view (tarray/new :uint32 2 1 0 buffer))
|
||||
|
||||
(set (buffer-uint32-view 1) 0xfffe9234)
|
||||
(set (buffer-uint32-view 0) 0x56789abc)
|
||||
|
||||
(assert (buffer-float64-view 0) "issue #142 nanbox hijack 1")
|
||||
(assert (= (type (buffer-float64-view 0)) :number) "issue #142 nanbox hijack 2")
|
||||
(assert (= (type (unmarshal @"\xC8\xbc\x9axV4\x92\xfe\xff")) :number) "issue #142 nanbox hijack 3")
|
||||
|
||||
# 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")
|
||||
|
||||
# module/expand-path regression
|
||||
(with-dyns [:syspath ".janet/.janet"]
|
||||
(assert (= (string (module/expand-path "hello" ":sys:/:all:.janet"))
|
||||
".janet/.janet/hello.janet") "module/expand-path 1"))
|
||||
|
||||
# comp should be variadic
|
||||
(assert (= 10 ((comp +) 1 2 3 4)) "variadic comp 1")
|
||||
(assert (= 11 ((comp inc +) 1 2 3 4)) "variadic comp 2")
|
||||
(assert (= 12 ((comp inc inc +) 1 2 3 4)) "variadic comp 3")
|
||||
(assert (= 13 ((comp inc inc inc +) 1 2 3 4)) "variadic comp 4")
|
||||
(assert (= 14 ((comp inc inc inc inc +) 1 2 3 4)) "variadic comp 5")
|
||||
(assert (= 15 ((comp inc inc inc inc inc +) 1 2 3 4)) "variadic comp 6")
|
||||
(assert (= 16 ((comp inc inc inc inc inc inc +) 1 2 3 4)) "variadic comp 7")
|
||||
|
||||
# Function shorthand
|
||||
(assert (= (|(+ 1 2 3)) 6) "function shorthand 1")
|
||||
(assert (= (|(+ 1 2 3 $) 4) 10) "function shorthand 2")
|
||||
(assert (= (|(+ 1 2 3 $0) 4) 10) "function shorthand 3")
|
||||
(assert (= (|(+ $0 $0 $0 $0) 4) 16) "function shorthand 4")
|
||||
(assert (= (|(+ $ $ $ $) 4) 16) "function shorthand 5")
|
||||
(assert (= (|4) 4) "function shorthand 6")
|
||||
(assert (= (((|||4))) 4) "function shorthand 7")
|
||||
(assert (= (|(+ $1 $1 $1 $1) 2 4) 16) "function shorthand 8")
|
||||
(assert (= (|(+ $0 $1 $3 $2 $6) 0 1 2 3 4 5 6) 12) "function shorthand 9")
|
||||
(assert (= (|(+ $0 $99) ;(range 100)) 99) "function shorthand 10")
|
||||
|
||||
# Simple function break
|
||||
(debug/fbreak map 1)
|
||||
(def f (fiber/new (fn [] (map inc [1 2 3])) :a))
|
||||
(resume f)
|
||||
(assert (= :debug (fiber/status f)) "debug/fbreak")
|
||||
(debug/unfbreak map 1)
|
||||
(map inc [1 2 3])
|
||||
|
||||
(defn idx= [x y] (= (tuple/slice x) (tuple/slice y)))
|
||||
|
||||
# Simple take, drop, etc. tests.
|
||||
(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
0
tools/.keep
Normal file
14
tools/afl/README.md
Normal file
14
tools/afl/README.md
Normal 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/
|
||||
```
|
||||
13
tools/afl/aggregate_cases.sh
Normal file
13
tools/afl/aggregate_cases.sh
Normal 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
36
tools/afl/fuzz.sh
Normal 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
|
||||
4
tools/afl/parser_runner.janet
Normal file
4
tools/afl/parser_runner.janet
Normal 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)))
|
||||
15
tools/afl/parser_testcases/simple.janet
Normal file
15
tools/afl/parser_testcases/simple.janet
Normal file
@@ -0,0 +1,15 @@
|
||||
0
|
||||
123.653
|
||||
true
|
||||
:true
|
||||
{}
|
||||
`
|
||||
hello
|
||||
`
|
||||
|()
|
||||
,()
|
||||
@{:hello "world"}
|
||||
@[1 "hello"]
|
||||
nil
|
||||
(foo 2 3)
|
||||
([{} @{:k ([""])}])
|
||||
6
tools/afl/prepare_to_fuzz.sh
Normal file
6
tools/afl/prepare_to_fuzz.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
set -eux
|
||||
|
||||
export CC=afl-clang
|
||||
make clean
|
||||
make -j $(nproc) all
|
||||
mkdir -p "./fuzz_out"
|
||||
@@ -1,14 +1,16 @@
|
||||
# Creates an amalgamated janet.c
|
||||
|
||||
# Head
|
||||
(def {:year YY :month MM :month-day DD} (os/date))
|
||||
(print "/* Amalgamated build - DO NOT EDIT */")
|
||||
(print "/* Generated " YY "-" (inc MM) "-" (inc DD)
|
||||
" with janet version " janet/version "-" janet/build " */")
|
||||
(print "/* Generated from janet version " janet/version "-" janet/build " */")
|
||||
(print "#define JANET_BUILD \"" janet/build "\"")
|
||||
(print ```#define JANET_AMALG```)
|
||||
(print ```#include "janet.h"```)
|
||||
|
||||
# Body
|
||||
(each path (tuple/slice process/args 2)
|
||||
(each path (tuple/slice (dyn :args) 1)
|
||||
(print "\n/* " path " */\n")
|
||||
(print (slurp path)))
|
||||
|
||||
# maybe will help
|
||||
(:flush stdout)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Format all code with astyle
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@echo off
|
||||
@rem Wrapper arounf jpm
|
||||
@rem Wrapper around jpm
|
||||
|
||||
janet %~dp0\jpm.janet %*
|
||||
janet "%~dp0\jpm.janet" %*
|
||||
|
||||
BIN
tools/nsis-3.04-strlen_8192.zip
Normal file
BIN
tools/nsis-3.04-strlen_8192.zip
Normal file
Binary file not shown.
7
tools/removecr.janet
Normal file
7
tools/removecr.janet
Normal file
@@ -0,0 +1,7 @@
|
||||
# Remove carriage returns from file. Since piping things on
|
||||
# windows may add bad line endings, we can just force removal
|
||||
# with this script.
|
||||
(def fname ((dyn :args) 1))
|
||||
(def source (slurp fname))
|
||||
(def new-source (string/replace-all "\r" "" source))
|
||||
(spit fname new-source :wb)
|
||||
@@ -44,8 +44,7 @@ static int is_symbol_char_gen(uint8_t c) {
|
||||
c == '>' ||
|
||||
c == '@' ||
|
||||
c == '^' ||
|
||||
c == '_' ||
|
||||
c == '|');
|
||||
c == '_');
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
Reference in New Issue
Block a user