1
0
mirror of https://github.com/janet-lang/janet synced 2025-11-07 19:13:02 +00:00

Compare commits

..

102 Commits

Author SHA1 Message Date
Calvin Rose
4ae372262b 1.9.1 release. 2020-05-12 09:19:09 -05:00
Calvin Rose
02167a15d1 Add new Makefile options to meson. 2020-05-12 09:04:38 -05:00
Calvin Rose
b50a4669d2 Update README and CHANGELOG. 2020-05-12 08:56:58 -05:00
Calvin Rose
c947bda604 Remove .breakall and .clearall conditionally.
If disasm not available, these functions cannot be implemented.
2020-05-12 08:52:36 -05:00
Calvin Rose
00451777fe Add meson builds to sourcehut CI. 2020-05-12 08:46:26 -05:00
Calvin Rose
a65386e925 Merge branch 'master' of github.com:janet-lang/janet 2020-05-11 01:10:58 -05:00
Calvin Rose
2d7d154ffc Merge pull request #392 from t6/patch-meson
Unbreak Meson build
2020-05-11 00:08:25 -04:00
Calvin Rose
3100080a50 Add NO_UMASK and NO_REALPATH config options. 2020-05-10 23:07:54 -05:00
Tobias Kortkamp
7275370ae5 Unbreak Meson build
The Meson build system
Version: 0.54.0
Source dir: /wrkdirs/usr/ports/lang/janet/work/janet-1.9.0
Build dir: /wrkdirs/usr/ports/lang/janet/work/janet-1.9.0/_build
Build type: native build

meson.build:225:2: ERROR: Expecting rbracket got string.
  'test/suite9.janet'
  ^
For a block that started at 215,13
test_files = [
             ^
2020-05-11 06:02:26 +02:00
Calvin Rose
e013381e72 Conditionally ignore pclose as well as popen. 2020-05-10 21:06:52 -05:00
Calvin Rose
d05bb1c125 Fix nanboxing issue. 2020-05-10 20:14:47 -05:00
Calvin Rose
273d1ff2d0 Fix external grammar to disallow | and \ in symbols. 2020-05-10 16:57:42 -05:00
Calvin Rose
235605bfa4 1.9.0 Release.
Fix up some documentation as well.
2020-05-10 16:45:33 -05:00
Calvin Rose
e8b3587946 Silence clang warnings about missing initializers. 2020-05-10 16:00:55 -05:00
Calvin Rose
9040ac6a0c Silence some warnings about pointer signedness. 2020-05-09 23:58:45 -05:00
Calvin Rose
a73ba56ebb Address #387
Introduce linker flags vs. library flags in jpm
in a backwards compatible way - most usage of lflags was for library
flags, so we will preserve that behavior.
2020-05-09 21:11:26 -05:00
Calvin Rose
1168f47768 Update default path for installed binaries.
This is useful for installing binaries in a default install
(when JANET_PATH or JANET_MODPATH is not explicitly set).
2020-05-09 19:02:12 -05:00
Calvin Rose
73dba691b1 Re-disable processes on emscripten build. 2020-05-09 12:04:47 -05:00
Calvin Rose
b1f76139a7 Add several configurable options - #379 2020-05-09 12:00:01 -05:00
Calvin Rose
6b986fecb0 Merge branch 'master' of github.com:janet-lang/janet 2020-05-09 11:07:15 -05:00
Calvin Rose
535ab8302b Add errorf to core. 2020-05-09 11:06:20 -05:00
Calvin Rose
7125b3430c Merge pull request #383 from andrewchambers/rngdoc
Improve rng doc string accuracy.
2020-05-09 11:35:01 -04:00
Calvin Rose
0615d09b7a Merge pull request #385 from andrewchambers/fnctlfix
Avoid setting O_CLOEXEC on stdin/stdout/stderr.
2020-05-09 11:34:37 -04:00
Calvin Rose
1add0c7d43 make test-install should be easier to clean. 2020-05-09 10:30:09 -05:00
Calvin Rose
8194f5ccaf Refactor jpm.
Make install and uninstall commands variadic.
Add :libs option to many decalre commands. This behaves much like
lflags, but will be places after all linker flags are given.
2020-05-09 10:22:46 -05:00
Andrew Chambers
057486cf56 Avoid setting O_CLOEXEC on stdin/stdout/stderr. 2020-05-09 22:26:50 +12:00
Andrew Chambers
f94e726271 Improve rng doc string accuracy. 2020-05-09 12:11:08 +12:00
Calvin Rose
95660002e1 fix include sys/fcntl.h to fcntl.h 2020-05-07 14:54:03 -05:00
Calvin Rose
95c669389b Merge pull request #378 from andrewchambers/tweak
Tweak comment, remove extra include.
2020-05-07 10:33:19 -04:00
Calvin Rose
084fc9776d Use SOCK_CLOEXEC correctly. 2020-05-07 07:55:08 -05:00
Andrew Chambers
1498fdb7b0 Tweak comment, remove extra include. 2020-05-07 20:44:04 +12:00
Calvin Rose
79c3139748 Check for SOCK_CLOEXEC.
Not available on all platforms.
2020-05-06 23:44:01 -05:00
Calvin Rose
bdd64f5656 Merge branch 'master' of github.com:janet-lang/janet 2020-05-06 18:52:57 -05:00
Calvin Rose
dc3e9fb77c Add CLOEXECs when getting file descriptors (#374)
This should help address leaking file descriptors in multithreaded
programs. There are a few cases where a race can occur though, as
some apis (fopen and mktemp).
2020-05-06 18:33:25 -05:00
Calvin Rose
4b417c0e9d Merge pull request #375 from andrewchambers/mttemp
Set the CLOEXEC flag on file/temp files.
2020-05-06 18:19:11 -04:00
Andrew Chambers
06c28f3a4d Set the CLOEXEC flag on file/temp files. 2020-05-06 11:16:08 +12:00
Calvin Rose
688fe6db5e Merge pull request #370 from andrewchambers/spawnrace
Fix (mostly nonsensible) race condition in multi threaded processes
2020-05-05 10:33:41 -04:00
Calvin Rose
9aefb59afe Format jpm with spork. 2020-05-05 09:21:50 -05:00
Calvin Rose
e3862b86b5 Use spork indent on boot.janet. 2020-05-05 09:17:09 -05:00
Calvin Rose
125cd222bb Pretty print tab characters as \t. 2020-05-05 00:03:12 -05:00
Andrew Chambers
a0f351c9fa Fix (mostly nonsensible) race condition in multi threaded processes using os/execute with os/setenv. 2020-05-05 16:03:13 +12:00
Calvin Rose
f7b49a2c91 Improve use of @ in match. 2020-05-04 18:28:20 -05:00
Calvin Rose
fd70b47768 Merge branch 'master' of github.com:janet-lang/janet 2020-05-02 23:40:11 -05:00
Calvin Rose
5d1fd390a6 Fix debugger regression. 2020-05-02 23:40:00 -05:00
Calvin Rose
8b5663e385 Merge pull request #363 from uasi/fix-typo-in-doc
Fix typo in doc
2020-05-02 12:11:18 -04:00
Calvin Rose
8b5bcaee3c Add lenprefix combinator to pegs.
This lets peg match n repeitions of a pattern, where
n is supplied from other parsed input and is not a constant.
2020-05-02 10:39:35 -05:00
Tomoki Aonuma
ca845aa256 Fix typo in doc 2020-05-02 02:36:55 +09:00
Calvin Rose
761ea65d81 Add fiber/roor and allow net/server to take
a numeric port.
2020-04-30 23:21:26 -05:00
Calvin Rose
1dc32d5e3d Revert inclusion of dedent.
Dedent has been moved to spork as misc function.
There are two many different, incompatible ways to 'dedent'
as string, and it seems rather specific to add to the core like it is.
2020-04-30 21:35:22 -05:00
Calvin Rose
1c0a015cc8 s/WSALastError/WSAGetLastError()/g 2020-04-30 13:26:14 -05:00
Calvin Rose
bee415217d Fix extra bindings. 2020-04-29 21:57:19 -05:00
Calvin Rose
73989f5cc7 Consolidate windows and posix socket code.
Also remove code that ignored sigpipe and instead try
our best to ignore through various platform specific mechanisms.
2020-04-29 21:07:21 -05:00
Calvin Rose
dd458c8ab5 Make JANET_NO_ASSEMBLER not break build. 2020-04-28 23:04:24 -05:00
Calvin Rose
63e9790123 Fix flag check in pretty print. 2020-04-28 10:00:24 -05:00
Calvin Rose
70e1f3ac81 Fix regression in repl. 2020-04-28 08:20:07 -05:00
Calvin Rose
67f1872f4a Expose debugger-env
This makes it easier/possible to use the debugging
functionality in a more flexible way.
2020-04-27 23:32:21 -05:00
Calvin Rose
8bbb7907d6 Run parser error handler in the correct env in run-context. 2020-04-27 20:29:16 -05:00
Calvin Rose
c98e1f3cae Update documentation for net/read and net/chunk. 2020-04-27 19:26:05 -05:00
Calvin Rose
6b0f93ce8a Update documentation for the -q flag. 2020-04-27 18:57:53 -05:00
Calvin Rose
80f19a0ab7 Fix behavior of -q flag.
Don't surpress errors at the repl.
2020-04-27 18:12:22 -05:00
Calvin Rose
41894eb285 Add docstrings to net.c 2020-04-26 14:11:47 -05:00
Calvin Rose
3535efd977 Remove %u format specifiers. 2020-04-26 13:47:36 -05:00
Calvin Rose
f6bd41ada7 Add %M, %m, %N, and %n formatters.
These will not truncate long values.
2020-04-26 13:17:28 -05:00
Calvin Rose
7b5f40772f Disable networking for emscripten build. 2020-04-26 12:37:27 -05:00
Calvin Rose
d2ebf4b52d Merge branch 'net' 2020-04-26 12:27:37 -05:00
Calvin Rose
0fe5c672a6 Use dedent in jpm create-executable. 2020-04-26 12:14:43 -05:00
Calvin Rose
ce7d51f9be Add dedent to core.
Makes longstrings easier to use - can be combined with comptime
for overhead free long strings.
2020-04-26 11:53:26 -05:00
Calvin Rose
cc1f84d1d3 Fix NSIS installer after moving jpm. 2020-04-26 08:58:53 -05:00
Calvin Rose
74126d9f24 Merge branch 'master' of github.com:janet-lang/janet 2020-04-26 08:55:59 -05:00
Calvin Rose
69eb9531da Rename auxbin/jpm -> jpm. 2020-04-26 08:55:32 -05:00
Calvin Rose
da4d8254fa Silence warning in MSVC about VLAs.
When janet.h is included as a C++ header in
MSVC, shows warnings.
2020-04-25 17:13:25 -04:00
Calvin Rose
57332c5ccf Change order of declarations for MSVC C++ support. 2020-04-25 15:42:44 -05:00
Calvin Rose
9bc5ac05c4 Add the parse function. 2020-04-25 12:46:32 -04:00
Calvin Rose
0a4d58468e Remove Debug build from appveyor. 2020-04-25 12:06:22 -04:00
Calvin Rose
8ce092da68 Update create-dirs to work on windows style paths. 2020-04-25 12:01:09 -04:00
Calvin Rose
fce1529bf2 Re-enable NSIS unicode. 2020-04-25 10:01:27 -05:00
Calvin Rose
e579d1d89f Add jpm rule-tree.
Useful for debugging jpm. This funtionality also maybe reused for
for showing a dependency tree as well.
2020-04-20 18:31:14 -05:00
Calvin Rose
42c257d0fc Merge branch 'master' into net 2020-04-19 13:38:51 -05:00
Calvin Rose
d5e5c98dc8 Merge branch 'net' of github.com:janet-lang/janet into net 2020-04-19 13:37:54 -05:00
Calvin Rose
3e60e82529 Add circular dependency detection.
This detection will not stop compilation, as errors
in general do not stop compilation unless exit on error
is passed inside an import, but should notify the user something
is going on.
2020-04-19 09:35:14 -05:00
Calvin Rose
60f8dd0bfc Renable :source argument to dofile.
Allows for some more interesting usage of
loaders.
2020-04-19 08:54:24 -05:00
Calvin Rose
0d3c6abee8 POLLER -> POLLERR 2020-04-18 19:15:59 -04:00
Calvin Rose
4a693222b4 Port net code to windows.
Use winsock2 and WSAPoll. Not the most high performance
solution but should work well.
2020-04-18 19:14:38 -04:00
Calvin Rose
0745c15d7b Fix return value from shell.c 2020-04-18 15:31:46 -05:00
Calvin Rose
2904c19ed9 Switch to poll from select.
Simpler and more flexible interface, and also lets
us use epoll more easily on linux, which is the most important
plantform to optimize for network performance.
2020-04-18 15:22:20 -05:00
Calvin Rose
4ac382e553 Add alias JANET_SIGNAL_EVENT. 2020-04-17 16:27:02 -05:00
Calvin Rose
596111c988 Merge branch 'master' into net 2020-04-17 15:08:26 -05:00
Calvin Rose
65403ec9fe Merge branch 'master' into net 2020-03-07 14:06:51 -06:00
Calvin Rose
90b3730a0a Merge branch 'master' into net 2020-03-07 13:34:13 -06:00
Calvin Rose
16202216b2 Address #291
When resuming a fiber with a child, the root fiber was set incorrectly.
2020-03-05 19:18:45 -06:00
Calvin Rose
8f1527712e Merge branch 'master' into net 2020-03-05 18:08:35 -06:00
Calvin Rose
01a79dc965 Remove extra functionality. 2020-02-20 20:10:03 -06:00
Calvin Rose
0df220780a Fix issues with #282
Bad handling of write errors, as well as janet_root_fiber().
2020-02-20 19:54:31 -06:00
Calvin Rose
f4a46ba6ea Add methods to streams.
This makes streams polymorphic with files in many cases.
printf family functions still need porting.
2020-02-12 09:32:41 -06:00
Calvin Rose
79bb9e54d5 Remove direct references to file descriptors.
If a descriptor is freed by the Janet code, other
uses of that descriptor, say in the event loop, need
to know that it has been closed.
2020-02-11 08:57:44 -06:00
Calvin Rose
135aff9e17 Add janet_loop() call to static binaries. 2020-02-09 20:02:35 -06:00
Calvin Rose
8ae6ae65a1 Merge branch 'master' into net 2020-02-09 20:00:58 -06:00
Calvin Rose
f4d7fd97f6 Working TCP echo server and client.
Required a few changes to APIs, namely janet_root_fiber()
to get topmost fiber that is active in the current scheduler.
This is distinct from janet_current_fiber(), which gets the bottom
most fiber in the fiber stack - it might have a parent, and so cannot
be reliably resumed.
This is the kind of situation that makes symmetric coroutines more
attractive.
2020-02-09 20:00:50 -06:00
Calvin Rose
7f1f684b21 Merge branch 'master' into net 2020-02-03 20:46:32 -06:00
Calvin Rose
eda61455d3 Work on tcp server code. 2020-02-03 09:29:51 -06:00
Calvin Rose
c5907258c3 Merge branch 'master' into net 2020-02-02 13:16:47 -06:00
Calvin Rose
c0d2140d14 Begin net/ module in core.
Humble beginnings.
2020-02-01 20:39:54 -06:00
45 changed files with 1658 additions and 568 deletions

12
.builds/meson.yml Normal file
View File

@@ -0,0 +1,12 @@
image: openbsd/latest
sources:
- https://git.sr.ht/~bakpakin/janet
packages:
- meson
tasks:
- build: |
cd janet
meson setup build --buildtype=release
cd build
ninja
ninja test

23
.builds/meson_min.yml Normal file
View File

@@ -0,0 +1,23 @@
image: openbsd/latest
sources:
- https://git.sr.ht/~bakpakin/janet
packages:
- meson
tasks:
- build: |
cd janet
meson setup build --buildtype=release
cd build
meson configure -Dsingle_threaded=true
meson configure -Dnanbox=false
meson configure -Ddynamic_modules=false
meson configure -Ddocstrings=false
meson configure -Dnet=false
meson configure -Dsourcemaps=false
meson configure -Dpeg=false
meson configure -Dassembler=false
meson configure -Dint_types=false
meson configure -Dtyped_arrays=false
meson configure -Dreduced_os=true
meson configure -Dprf=false
ninja # will not pass tests but should build

View File

@@ -1,7 +1,22 @@
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased - ???
## 1.9.1 - 2020-05-12
- Add :prefix option to declare-source
- Re-enable minimal builds with the debugger.
- Add several flags for configuring Janet on different platforms.
- Fix broken meson build from 1.9.0 and add meson to CI.
- Fix compilation issue when nanboxing is disabled.
## 1.9.0 - 2020-05-10
- Add `:ldflags` option to many jpm declare functions.
- Add `errorf` to core.
- Add `lenprefix` combinator to PEGs.
- Add `%M`, `%m`, `%N`, and `%n` formatters to formatting functions. These are the
same as `%Q`, `%q`, `%P`, and `%p`, but will not truncate long values.
- Add `fiber/root`.
- Add beta `net/` module to core for socket based networking.
- Add the `parse` function to parse strings of source code more conveniently.
- Add `jpm rule-tree` subcommand.
- Add `--offline` flag to jpm to force use of the cache.
- Allow sending pointers and C functions across threads via `thread/send`.
@@ -22,7 +37,7 @@ All notable changes to this project will be documented in this file.
- Add os/umask
- Add os/perm-int
- Add os/perm-string
- Add :octal-permissions option for os/stat.
- Add :int-permissions option for os/stat.
- Add `jpm repl` subcommand, as well as `post-deps` macro in project.janet files.
- Various bug fixes.

View File

@@ -96,6 +96,7 @@ JANET_CORE_SOURCES=src/core/abstract.c \
src/core/io.c \
src/core/marsh.c \
src/core/math.c \
src/core/net.c \
src/core/os.c \
src/core/parse.c \
src/core/peg.c \
@@ -194,12 +195,12 @@ 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
./$(JANET_TARGET) -k 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
$(VALGRIND_COMMAND) ./$(JANET_TARGET) -k jpm
callgrind: $(JANET_TARGET)
for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done
@@ -213,7 +214,7 @@ dist: build/janet-dist.tar.gz
build/janet-%.tar.gz: $(JANET_TARGET) \
src/include/janet.h src/conf/janetconf.h \
jpm.1 janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
build/doc.html README.md build/janet.c build/shell.c auxbin/jpm
build/doc.html README.md build/janet.c build/shell.c jpm
$(eval JANET_DIST_DIR = "janet-$(shell basename $*)")
mkdir -p build/$(JANET_DIST_DIR)
cp -r $^ build/$(JANET_DIST_DIR)/
@@ -258,7 +259,7 @@ install: $(JANET_TARGET) build/janet.pc
cp $(JANET_STATIC_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.a'
ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.so'
ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(DESTDIR)$(LIBDIR)/$(SONAME)
cp -rf auxbin/* '$(DESTDIR)$(BINDIR)'
cp -rf jpm '$(DESTDIR)$(BINDIR)'
mkdir -p '$(DESTDIR)$(MANPATH)'
cp janet.1 '$(DESTDIR)$(MANPATH)'
cp jpm.1 '$(DESTDIR)$(MANPATH)'
@@ -289,6 +290,7 @@ build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET)
clean:
-rm -rf build vgcore.* callgrind.*
-rm -rf test/install/build test/install/modpath
test-install:
cd test/install \
@@ -298,10 +300,11 @@ test-install:
&& 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
&& mkdir -p modpath \
&& jpm --verbose --testdeps --modpath=./modpath install https://github.com/janet-lang/json.git
cd test/install && jpm --verbose --test --modpath=./modpath install https://github.com/janet-lang/jhydro.git
cd test/install && jpm --verbose --test --modpath=./modpath install https://github.com/janet-lang/path.git
cd test/install && jpm --verbose --test --modpath=./modpath install https://github.com/janet-lang/argparse.git
.PHONY: clean install repl debug valgrind test \
valtest emscripten dist uninstall docs grammar format

View File

@@ -4,6 +4,8 @@
[![Build Status](https://travis-ci.org/janet-lang/janet.svg?branch=master)](https://travis-ci.org/janet-lang/janet)
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/freebsd.yml.svg)](https://builds.sr.ht/~bakpakin/janet/freebsd.yml?)
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/openbsd.yml.svg)](https://builds.sr.ht/~bakpakin/janet/openbsd.yml?)
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/meson.yml.svg)](https://builds.sr.ht/~bakpakin/janet/meson.yml?)
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/meson_min.yml.svg)](https://builds.sr.ht/~bakpakin/janet/meson_min.yml?)
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left">

View File

@@ -4,7 +4,6 @@ image:
- Visual Studio 2019
configuration:
- Release
- Debug
platform:
- x64
- x86
@@ -14,23 +13,24 @@ environment:
matrix:
fast_finish: true
# skip unsupported combinations
init:
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %platform%
before_build:
- choco install nsis -y -pre --version 3.05
- 7z e "tools\nsis-3.05-strlen_8192.zip" -o"C:\Program Files (x86)\NSIS\" -y
build_script:
install:
- set JANET_BUILD=%appveyor_repo_commit:~0,7%
- choco install nsis -y -pre --version 3.05
# Replace makensis.exe and files with special long string build. This should
# prevent issues when setting PATH during installation.
- 7z e "tools\nsis-3.05-strlen_8192.zip" -o"C:\Program Files (x86)\NSIS\" -y
- build_win all
test_script:
- 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 /P janet_outname=<build\version.txt
build: off
artifacts:
- name: janet.c

View File

@@ -116,7 +116,7 @@ copy src\include\janet.h dist\janet.h
copy src\conf\janetconf.h dist\janetconf.h
copy build\libjanet.lib dist\libjanet.lib
copy auxbin\jpm dist\jpm
copy .\jpm dist\jpm
copy tools\jpm.bat dist\jpm.bat
@rem Create installer

6
examples/tcpclient.janet Normal file
View File

@@ -0,0 +1,6 @@
(with [conn (net/connect "127.0.0.1" "8000")]
(printf "Connected to %q!" conn)
(:write conn "Echo...")
(print "Wrote to connection...")
(def res (:read conn 1024))
(pp res))

13
examples/tcpserver.janet Normal file
View File

@@ -0,0 +1,13 @@
(defn handler
"Simple handler for connections."
[stream]
(defer (:close stream)
(def id (gensym))
(def b @"")
(print "Connection " id "!")
(while (:read stream 1024 b)
(:write stream b)
(buffer/clear b))
(printf "Done %v!" id)))
(net/server "127.0.0.1" "8000" handler)

View File

@@ -1,6 +1,7 @@
# This file is invoked by build_win.bat
# Relevant configuration variables are set there.
SetCompressor /FINAL /SOLID lzma
Unicode True
!echo "Program Files: ${PROGRAMFILES}"
@@ -110,7 +111,7 @@ section "Janet" BfWSection
# Bin files
file /oname=bin\janet.exe dist\janet.exe
file /oname=logo.ico assets\icon.ico
file /oname=bin\jpm.janet auxbin\jpm
file /oname=bin\jpm.janet .\jpm
file /oname=bin\jpm.bat tools\jpm.bat
# C headers and library files

View File

@@ -175,7 +175,7 @@ after an error. Persistent mode can be good for debugging and testing.
.TP
.BR \-q
Quiet output. Don't print a repl prompt or expression results to stdout.
Hide the logo in the repl.
.TP
.BR \-k

View File

@@ -15,6 +15,145 @@
(def- statext (if is-win ".static.lib" ".a"))
(def- absprefix (if is-win "C:\\" "/"))
#
# Defaults
#
(def- exe-dir
"Directory containing jpm script"
(do
(def exe (dyn :current-file))
(def i (last (string/find-all sep exe)))
(slice exe 0 i)))
(def JANET_MODPATH (or (os/getenv "JANET_MODPATH") (dyn :syspath)))
# Default based on janet binary location
(def JANET_HEADERPATH (or (os/getenv "JANET_HEADERPATH")
(string exe-dir "/../include/janet")))
(def JANET_LIBPATH (or (os/getenv "JANET_LIBPATH")
(string exe-dir "/../lib")))
# We want setting JANET_PATH to contain installed binaries. However, it is convenient
# to have globally installed binaries got to the same place as jpm itself, which is on
# the $PATH.
(def JANET_BINPATH (or (os/getenv "JANET_BINPATH")
(if-let [mp (os/getenv "JANET_MODPATH")] (string mp "/bin"))
(if-let [mp (os/getenv "JANET_PATH")] (string mp "/bin"))
exe-dir))
#
# Utilities
#
(defn find-manifest-dir
"Get the path to the directory containing manifests for installed
packages."
[]
(string (dyn :modpath JANET_MODPATH) sep ".manifests"))
(defn find-manifest
"Get the full path of a manifest file given a package name."
[name]
(string (find-manifest-dir) sep name ".jdn"))
(defn find-cache
"Return the path to the global cache."
[]
(def path (dyn :modpath JANET_MODPATH))
(string path sep ".cache"))
(defn rm
"Remove a directory and all sub directories."
[path]
(if (= (os/lstat path :mode) :directory)
(do
(each subpath (os/dir path)
(rm (string path sep subpath)))
(os/rmdir path))
(os/rm path)))
(defn- rimraf
"Hard delete directory tree"
[path]
(if is-win
# windows get rid of read-only files
(os/shell `rmdir /S /Q "` path `"`))
(rm path))
(defn clear-cache
"Clear the global git cache."
[]
(def cache (find-cache))
(print "clearing " cache "...")
(rimraf cache))
(def- default-pkglist
(or (os/getenv "JANET_PKGLIST") "https://github.com/janet-lang/pkgs.git"))
(defn- pslurp
"Like slurp, but with file/popen instead file/open. Also trims output"
[cmd]
(string/trim (with [f (file/popen cmd)] (:read f :all))))
(def- path-splitter
"split paths on / and \\."
(peg/compile ~(any (* '(any (if-not (set `\/`) 1)) (+ (set `\/`) -1)))))
(defn create-dirs
"Create all directories needed for a file (mkdir -p)."
[dest]
(def segs (peg/match path-splitter dest))
(for i 1 (length segs)
(def path (string/join (slice segs 0 i) sep))
(unless (empty? path) (os/mkdir path))))
(def- filepath-replacer
"Convert url with potential bad characters into a file path element."
(peg/compile ~(% (any (+ (/ '(set "<>:\"/\\|?*") "_") '1)))))
(defn filepath-replace
"Remove special characters from a string or path
to make it into a path segment."
[repo]
(get (peg/match filepath-replacer repo) 0))
(defn shell
"Do a shell command"
[& args]
(if (dyn :verbose)
(print ;(interpose " " args)))
(def res (os/execute args :p))
(unless (zero? res)
(error (string "command exited with status " res))))
(defn copy
"Copy a file or directory recursively from one location to another."
[src dest]
(print "copying " src " to " dest "...")
(if is-win
(let [end (last (peg/match path-splitter src))
isdir (= (os/stat src :mode) :directory)]
(shell "xcopy" src (if isdir (string dest "\\" end) dest) "/y" "/s" "/e" "/i"))
(shell "cp" "-rf" src dest)))
(defn mkdir
"Create a directory if it doesn't exist. If it does exist, do nothing.
If we can't create it, give a friendly error. Return true if created, false if
existing. Throw an error if we can't create it."
[dir]
(os/mkdir dir))
(defn- abspath
"Create an absolute path. Does not resolve . and .. (useful for
generating entries in install manifest file)."
[path]
(if (if is-win
(peg/match '(+ "\\" (* (range "AZ" "az") ":\\")) path)
(string/has-prefix? "/" path))
path
(string (os/cwd) sep path)))
#
# Rule Engine
#
@@ -100,91 +239,6 @@
(each thunk thunks (thunk)))
(unless phony target))
#
# Configuration
#
(def- exe-dir
"Directory containing jpm script"
(do
(def exe (dyn :current-file))
(def i (last (string/find-all sep exe)))
(slice exe 0 i)))
(def JANET_MODPATH (or (os/getenv "JANET_MODPATH") (dyn :syspath)))
# Default based on janet binary location
(def JANET_HEADERPATH (or (os/getenv "JANET_HEADERPATH")
(string exe-dir "/../include/janet")))
(def JANET_LIBPATH (or (os/getenv "JANET_LIBPATH")
(string exe-dir "/../lib")))
(def JANET_BINPATH (or (os/getenv "JANET_BINPATH")
(string (dyn :syspath) "/bin")))
#
# Compilation Defaults
#
(def default-compiler (or (os/getenv "CC") (if is-win "cl.exe" "cc")))
(def default-linker (or (os/getenv "CC") (if is-win "link.exe" "cc")))
(def default-archiver (or (os/getenv "AR") (if is-win "lib.exe" "ar")))
# Detect threads
(def env (fiber/getenv (fiber/current)))
(def threads? (not (not (env 'thread/new))))
# Default flags for natives, but not required
(def default-lflags (if is-win ["/nologo"] []))
(def default-cflags
(if is-win
["/nologo" "/MD"]
["-std=c99" "-Wall" "-Wextra"]))
# Link to pthreads
(def- thread-flags (if is-win [] (if threads? ["-lpthread"] [])))
# Required flags for dynamic libraries. These
# are used no matter what for dynamic libraries.
(def- dynamic-cflags
(if is-win
["/LD"]
["-fPIC"]))
(def- dynamic-lflags
(if is-win
["/DLL" ;thread-flags]
(if is-mac
["-shared" "-undefined" "dynamic_lookup" ;thread-flags]
["-shared" ;thread-flags])))
(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)
(defn check-cc
"Ensure we have a c compiler."
[]
(if is-win
(do
(if (os/getenv "INCLUDE") (break))
(error "Run jpm inside a Developer Command Prompt.
jpm needs a c compiler to compile natives. You can install the MSVC compiler from
microsoft.com"))
(do)))
(defn create-dirs
"Create all directories needed for a file (mkdir -p)."
[dest]
(def segs (string/split "/" dest))
(for i 1 (length segs)
(def path (string/join (slice segs 0 i) "/"))
(unless (empty? path) (os/mkdir path))))
#
# Importing a file
#
@@ -204,7 +258,7 @@
(def env (make-env))
(put env :jpm-no-deps no-deps)
(loop [k :keys _env :when (symbol? k)]
(unless ((_env k) :private) (put env k (_env k))))
(unless ((_env k) :private) (put env k (_env k))))
env)
(defn require-jpm
@@ -236,64 +290,71 @@
(unless (dyn :jpm-no-deps)
~',(reduce |(eval $1) nil body)))
#
# OS and shell helpers
#
(def- path-splitter
"split paths on / and \\."
(peg/compile ~(any (* '(any (if-not (set `\/`) 1)) (+ (set `\/`) -1)))))
(def- filepath-replacer
"Convert url with potential bad characters into a file path element."
(peg/compile ~(% (any (+ (/ '(set "<>:\"/\\|?*") "_") '1)))))
(defn filepath-replace
"Remove special characters from a string or path
to make it into a path segment."
[repo]
(get (peg/match filepath-replacer repo) 0))
(defn shell
"Do a shell command"
[& args]
(if (dyn :verbose)
(print ;(interpose " " 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/lstat 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
(let [end (last (peg/match path-splitter src))
isdir (= (os/stat src :mode) :directory)]
(shell "xcopy" src (if isdir (string dest "\\" end) dest) "/y" "/s" "/e" "/i"))
(shell "cp" "-rf" src dest)))
(defn mkdir
"Create a directory if it doesn't exist. If it does exist, do nothing.
If we can't create it, give a friendly error. Return true if created, false if
existing. Throw an error if we can't create it."
[dir]
(os/mkdir dir))
#
# C Compilation
#
(def default-compiler (or (os/getenv "CC") (if is-win "cl.exe" "cc")))
(def default-linker (or (os/getenv "CC") (if is-win "link.exe" "cc")))
(def default-archiver (or (os/getenv "AR") (if is-win "lib.exe" "ar")))
# Detect threads
(def env (fiber/getenv (fiber/current)))
(def threads? (not (not (env 'thread/new))))
# Default libraries to link
(def- thread-flags
(if is-win []
(if threads? ["-lpthread"] [])))
# lflags needed for the janet binary.
(def janet-lflags
(case (os/which)
:macos ["-ldl" "-lm" ;thread-flags]
:windows [;thread-flags]
:linux ["-lm" "-ldl" "-lrt" ;thread-flags]
["-lm" ;thread-flags]))
# Default flags for natives, but not required
(def default-lflags (if is-win ["/nologo"] []))
(def default-cflags
(if is-win
["/nologo" "/MD"]
["-std=c99" "-Wall" "-Wextra"]))
# Required flags for dynamic libraries. These
# are used no matter what for dynamic libraries.
(def- dynamic-cflags
(if is-win
["/LD"]
["-fPIC"]))
(def- dynamic-lflags
(if is-win
["/DLL" ;thread-flags]
(if is-mac
["-shared" "-undefined" "dynamic_lookup" ;thread-flags]
["-shared" ;thread-flags])))
(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)
(defn check-cc
"Ensure we have a c compiler."
[]
(if is-win
(do
(if (os/getenv "INCLUDE") (break))
(error "Run jpm inside a Developer Command Prompt.
jpm needs a c compiler to compile natives. You can install the MSVC compiler from
microsoft.com"))
(do)))
(defn- embed-name
"Rename a janet symbol for embedding."
[path]
@@ -379,13 +440,14 @@
(def cflags (getcflags opts))
(def lflags [;(opt opts :lflags default-lflags)
;(if (opts :static) [] dynamic-lflags)])
(def ldflags [;(opt opts :ldflags [])])
(rule target objects
(check-cc)
(print "linking " target "...")
(create-dirs target)
(if is-win
(shell linker ;lflags (string "/OUT:" target) ;objects (win-import-library))
(shell linker ;cflags `-o` target ;objects ;lflags))))
(shell linker ;ldflags (string "/OUT:" target) ;objects (win-import-library) ;lflags)
(shell linker ;cflags ;ldflags `-o` target ;objects ;lflags))))
(defn- archive-c
"Link object files together to make a static library."
@@ -434,6 +496,78 @@
[path]
(string (string/slice path 0 (- -1 (length modext))) statext))
(defn- make-bin-source
[declarations lookup-into-invocations]
(string
declarations
```
int main(int argc, const char **argv) {
janet_init();
/* Get core env */
JanetTable *env = janet_core_env(NULL);
JanetTable *lookup = janet_env_lookup(env);
JanetTable *temptab;
int handle = janet_gclock();
/* Load natives into unmarshalling dictionary */
```
lookup-into-invocations
```
/* Unmarshal bytecode */
Janet marsh_out = janet_unmarshal(
janet_payload_image_embed,
janet_payload_image_embed_size,
0,
lookup,
NULL);
/* Verify the marshalled object is a function */
if (!janet_checktype(marsh_out, JANET_FUNCTION)) {
fprintf(stderr, "invalid bytecode image - expected function.");
return 1;
}
JanetFunction *jfunc = janet_unwrap_function(marsh_out);
/* Check arity */
janet_arity(argc, jfunc->def->min_arity, jfunc->def->max_arity);
/* Collect command line arguments */
JanetArray *args = janet_array(argc);
for (int i = 0; i < argc; i++) {
janet_array_push(args, janet_cstringv(argv[i]));
}
/* Create enviornment */
temptab = janet_table(0);
temptab = env;
janet_table_put(temptab, janet_ckeywordv("args"), janet_wrap_array(args));
janet_gcroot(janet_wrap_table(temptab));
/* Unlock GC */
janet_gcunlock(handle);
/* Run everything */
JanetFiber *fiber = janet_fiber(jfunc, 64, argc, argc ? args->data : NULL);
fiber->env = temptab;
Janet out;
JanetSignal result = janet_continue(fiber, janet_wrap_nil(), &out);
if (result != JANET_SIGNAL_OK && result != JANET_SIGNAL_EVENT) {
janet_stacktrace(fiber, out);
janet_deinit();
return result;
}
#ifdef JANET_NET
janet_loop();
#endif
janet_deinit();
return 0;
}
```))
(defn- create-executable
"Links an image with libjanet.a (or .lib) to produce an
executable. Also will try to link native modules into the
@@ -450,6 +584,7 @@
(def entry-env (dofile source))
(def main ((entry-env 'main) :value))
(def dep-lflags @[])
(def dep-ldflags @[])
# Create marshalling dictionary
(def mdict (invert (env-lookup root-env)))
@@ -483,142 +618,35 @@
"\", 0);\n\n")
(when-let [lfs (meta :lflags)]
(array/concat dep-lflags lfs))
(when-let [lfs (meta :ldflags)]
(array/concat dep-ldflags lfs))
(buffer/push-string declarations
"extern void "
(meta :static-entry)
"(JanetTable *);\n"))
# Build image
(def image (marshal main mdict))
# Make image byte buffer
(create-buffer-c-impl image cimage_dest "janet_payload_image")
# Append main function
(spit cimage_dest (string
"\n"
declarations
```
int main(int argc, const char **argv) {
janet_init();
/* Get core env */
JanetTable *env = janet_core_env(NULL);
JanetTable *lookup = janet_env_lookup(env);
JanetTable *temptab;
int handle = janet_gclock();
/* Load natives into unmarshalling dictionary */
```
lookup-into-invocations
```
/* Unmarshal bytecode */
Janet marsh_out = janet_unmarshal(
janet_payload_image_embed,
janet_payload_image_embed_size,
0,
lookup,
NULL);
/* Verify the marshalled object is a function */
if (!janet_checktype(marsh_out, JANET_FUNCTION)) {
fprintf(stderr, "invalid bytecode image - expected function.");
return 1;
}
JanetFunction *jfunc = janet_unwrap_function(marsh_out);
/* Check arity */
janet_arity(argc, jfunc->def->min_arity, jfunc->def->max_arity);
/* Collect command line arguments */
JanetArray *args = janet_array(argc);
for (int i = 0; i < argc; i++) {
janet_array_push(args, janet_cstringv(argv[i]));
}
/* Create enviornment */
temptab = janet_table(0);
temptab = env;
janet_table_put(temptab, janet_ckeywordv("args"), janet_wrap_array(args));
janet_gcroot(janet_wrap_table(temptab));
/* Unlock GC */
janet_gcunlock(handle);
/* Run everything */
JanetFiber *fiber = janet_fiber(jfunc, 64, argc, argc ? args->data : NULL);
fiber->env = temptab;
Janet out;
JanetSignal result = janet_continue(fiber, janet_wrap_nil(), &out);
if (result) {
janet_stacktrace(fiber, out);
janet_deinit();
return result;
}
janet_deinit();
return 0;
}
```) :ab)
# Compile and link final exectable
(do
(def extra-lflags (case (os/which)
:macos ["-ldl" "-lm" ;thread-flags]
:windows [;thread-flags]
:linux ["-lm" "-ldl" "-lrt" ;thread-flags]
#default
["-lm" ;thread-flags]))
(def cc (opt opts :compiler default-compiler))
(def lflags [;dep-lflags ;(opt opts :lflags default-lflags) ;extra-lflags])
(def cflags (getcflags opts))
(def defines (make-defines (opt opts :defines {})))
(print "compiling and linking " dest "...")
(if is-win
(shell cc ;cflags cimage_dest ;static-libs (libjanet) ;lflags `/link` (string "/OUT:" dest))
(shell cc ;cflags `-o` dest cimage_dest ;static-libs (libjanet) ;lflags)))))
(defn- abspath
"Create an absolute path. Does not resolve . and .. (useful for
generating entries in install manifest file)."
[path]
(if (if is-win
(peg/match '(+ "\\" (* (range "AZ" "az") ":\\")) path)
(string/has-prefix? "/" path))
path
(string (os/cwd) sep path)))
(spit cimage_dest (make-bin-source declarations lookup-into-invocations) :ab)
# Compile and link final exectable
(do
(def cc (opt opts :compiler default-compiler))
(def ldflags [;dep-ldflags ;(opt opts :ldflags [])])
(def lflags [;static-libs (libjanet) ;dep-lflags ;(opt opts :lflags default-lflags) ;janet-lflags])
(def cflags (getcflags opts))
(def defines (make-defines (opt opts :defines {})))
(print "compiling and linking " dest "...")
(if is-win
(shell cc ;cflags ;ldflags cimage_dest ;lflags `/link` (string "/OUT:" dest))
(shell cc ;cflags ;ldflags `-o` dest cimage_dest ;lflags)))))
#
# Public utilities
# Installation and Dependencies
#
(defn parse
"Read a string of Janet source and parse out the first expression."
[src]
(let [p (parser/new)]
(:consume p src)
(if (= :error (:status p))
(error (string "Could not parse: " (parser/error p))))
(:produce p)))
(defn find-manifest-dir
"Get the path to the directory containing manifests for installed
packages."
[]
(string (dyn :modpath JANET_MODPATH) sep ".manifests"))
(defn find-manifest
"Get the full path of a manifest file given a package name."
[name]
(string (find-manifest-dir) sep name ".jdn"))
(defn find-cache
"Return the path to the global cache."
[]
(def path (dyn :modpath JANET_MODPATH))
(string path sep ".cache"))
(defn uninstall
"Uninstall bundle named name"
[name]
@@ -632,23 +660,6 @@ int main(int argc, const char **argv) {
(rm manifest)
(print "Uninstalled.")))
(defn- rimraf
"Hard delete directory tree"
[path]
(if is-win
# windows get rid of read-only files
(os/shell `rmdir /S /Q "` path `"`))
(rm path))
(defn clear-cache
"Clear the global git cache."
[]
(def cache (find-cache))
(print "clearing " cache "...")
(rimraf cache))
(def- default-pkglist (or (os/getenv "JANET_PKGLIST") "https://github.com/janet-lang/pkgs.git"))
(defn install-git
"Install a bundle from git. If the bundle is already installed, the bundle
is reinistalled (but not rebuilt if artifacts are cached)."
@@ -663,9 +674,9 @@ int main(int argc, const char **argv) {
(unless (string/find ":" repo)
(def pkgs
(try (require "pkgs")
([err f]
(install-git (dyn :pkglist default-pkglist))
(require "pkgs"))))
([err f]
(install-git (dyn :pkglist default-pkglist))
(require "pkgs"))))
(def next-repo (get-in pkgs ['packages :value (symbol repo)]))
(unless next-repo
(error (string "package " repo " not found.")))
@@ -682,11 +693,11 @@ int main(int argc, const char **argv) {
(error (string "did not find cached repo for dependency " repo))
(set fresh true))
(when (mkdir module-dir)
(set fresh true)
(print "cloning repository " repo " to " module-dir)
(unless (zero? (os/execute ["git" "clone" repo module-dir] :p))
(rimraf module-dir)
(error (string "could not clone git dependency " repo)))))
(set fresh true)
(print "cloning repository " repo " to " module-dir)
(unless (zero? (os/execute ["git" "clone" repo module-dir] :p))
(rimraf module-dir)
(error (string "could not clone git dependency " repo)))))
(def olddir (os/cwd))
(try
(with-dyns [:rules @{}
@@ -719,11 +730,6 @@ int main(int argc, const char **argv) {
(mkdir destdir)
(copy src destdir)))
(defn- pslurp
"Like slurp, but with file/popen instead file/open. Also trims output"
[cmd]
(string/trim (with [f (file/popen cmd)] (:read f :all))))
(defn- make-lockfile
[&opt filename]
(default filename "lockfile.jdn")
@@ -782,12 +788,12 @@ int main(int argc, const char **argv) {
(compile-c opts src (out-path src ".c" objext)))
(def objects (map (fn [path] (out-path path ".c" objext)) sources))
(when-let [embedded (opts :embedded)]
(loop [src :in embedded]
(def c-src (out-path src ".janet" ".janet.c"))
(def o-src (out-path src ".janet" (if is-win ".janet.obj" ".janet.o")))
(array/push objects o-src)
(create-buffer-c src c-src (embed-name src))
(compile-c opts c-src o-src)))
(loop [src :in embedded]
(def c-src (out-path src ".janet" ".janet.c"))
(def o-src (out-path src ".janet" (if is-win ".janet.obj" ".janet.o")))
(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)
(install-rule lname path)
@@ -801,6 +807,7 @@ int main(int argc, const char **argv) {
"# Metadata for static library %s\n\n%.20p"
(string name statext)
{:static-entry ename
:ldflags ~',(opts :ldflags)
:lflags ~',(opts :lflags)})))
(add-dep "build" metaname)
(install-rule metaname path)
@@ -815,21 +822,24 @@ int main(int argc, const char **argv) {
(compile-c opts src (out-path src ".c" sobjext) true))
(def sobjects (map (fn [path] (out-path path ".c" sobjext)) sources))
(when-let [embedded (opts :embedded)]
(loop [src :in embedded]
(def c-src (out-path src ".janet" ".janet.c"))
(def o-src (out-path src ".janet" sjobjext))
(array/push sobjects o-src)
# Buffer c-src is already declared by dynamic module
(compile-c opts c-src o-src true)))
(loop [src :in embedded]
(def c-src (out-path src ".janet" ".janet.c"))
(def o-src (out-path src ".janet" sjobjext))
(array/push sobjects o-src)
# Buffer c-src is already declared by dynamic module
(compile-c opts c-src o-src true)))
(archive-c opts sname ;sobjects)
(add-dep "build" sname)
(install-rule sname path)))
(defn declare-source
"Create a Janet modules. This does not actually build the module(s),
but registers it for packaging and installation."
[&keys {:source sources}]
(def path (dyn :modpath JANET_MODPATH))
"Create Janet modules. This does not actually build the module(s),
but registers them for packaging and installation. :source should be an
array of files and directores to copy into JANET_MODPATH or JANET_PATH.
:prefix can optionally be given to modify the destination path to be
(string JANET_PATH prefix source)."
[&keys {:source sources :prefix prefix}]
(def path (string (dyn :modpath JANET_MODPATH) (or prefix "")))
(if (bytes? sources)
(install-rule sources path)
(each s sources
@@ -846,10 +856,10 @@ int main(int argc, const char **argv) {
is marshalled into bytecode which is then embedded in a final executable for distribution.\n\n
This executable can be installed as well to the --binpath given."
[&keys {:install install :name name :entry entry :headers headers
:cflags cflags :lflags lflags :deps deps}]
:cflags cflags :lflags lflags :deps deps :ldflags ldflags}]
(def name (if is-win (string name ".exe") name))
(def dest (string "build" sep name))
(create-executable @{:cflags cflags :lflags lflags} entry dest)
(create-executable @{:cflags cflags :lflags lflags :ldflags ldflags} entry dest)
(add-dep "build" dest)
(when headers
(each h headers (add-dep dest h)))
@@ -885,8 +895,8 @@ int main(int argc, const char **argv) {
(eachp [i d] (sorted root-deps)
(print-rule-tree
d (dec depth)
(string prefix-part (if (= i l) " └─" " ├─"))
(string prefix-part (if (= i l) " " " │ ")))))))
(string prefix-part (if (= i l) " └─" " ├─"))
(string prefix-part (if (= i l) " " " │ ")))))))
(defn declare-archive
"Build a janet archive. This is a file that bundles together many janet
@@ -989,10 +999,10 @@ on a project, or from anywhere to do operations on the global module cache (modp
Subcommands are:
build : build all artifacts
help : show this help text
install (repo or name) : install artifacts. If a repo is given, install the contents of that
install (repo or name)... : install artifacts. If a repo is given, install the contents of that
git repository, assuming that the repository is a jpm project. If not, build
and install the current project.
uninstall (module) : uninstall a module. If no module is given, uninstall the module
uninstall (module)... : uninstall a module. If no module is given, uninstall the module
defined by the current directory.
show-paths : prints the paths that will be used to install things.
clean : remove any generated files or artifacts
@@ -1056,20 +1066,20 @@ Flags are:
(local-rule "clean"))
(defn install
[&opt repo]
(if repo
(install-git repo)
(local-rule "install")))
[& repo]
(if (empty? repo)
(local-rule "install")
(each rep repo (install-git rep))))
(defn test
[]
(local-rule "test"))
(defn- uninstall-cmd
[&opt what]
(if what
(uninstall what)
(local-rule "uninstall")))
[& what]
(if (empty? what)
(local-rule "uninstall")
(each wha what (uninstall wha))))
(defn deps
[]
@@ -1104,10 +1114,10 @@ Flags are:
(def env
(try
(require-jpm "./project.janet")
([err f]
(if (= "cannot open ./project.janet" err)
(put (make-jpm-env) :project {})
(propagate err f)))))
([err f]
(if (= "cannot open ./project.janet" err)
(put (make-jpm-env) :project {})
(propagate err f)))))
(setdyn :pretty-format (if-not (dyn :nocolor) "%.20Q" "%.20q"))
(setdyn :err-color (if-not (dyn :nocolor) true))
(def p (env :project))

9
jpm.1
View File

@@ -100,19 +100,20 @@ Builds all artifacts specified in the project.janet file in the current director
be created in the ./build/ directory.
.TP
.BR install\ [\fBrepo\fR]
.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.
When run with an argument, install does not need to be run from a jpm project directory. Will also
install multiple dependencies in one command.
.TP
.BR uninstall\ [\fBname\fR]
.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.
the current project if installed. Will also uninstall multiple packages in one command.
.TP
.BR clean

View File

@@ -20,7 +20,7 @@
project('janet', 'c',
default_options : ['c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.9.0-dev')
version : '1.9.1')
# Global settings
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
@@ -59,6 +59,7 @@ conf.set('JANET_NO_DOCSTRINGS', not get_option('docstrings'))
conf.set('JANET_NO_SOURCEMAPS', not get_option('sourcemaps'))
conf.set('JANET_NO_ASSEMBLER', not get_option('assembler'))
conf.set('JANET_NO_PEG', not get_option('peg'))
conf.set('JANET_NO_NET', not get_option('net'))
conf.set('JANET_REDUCED_OS', get_option('reduced_os'))
conf.set('JANET_NO_TYPED_ARRAY', not get_option('typed_array'))
conf.set('JANET_NO_INT_TYPES', not get_option('int_types'))
@@ -67,6 +68,9 @@ 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'))
conf.set('JANET_NO_UMASK', not get_option('umask'))
conf.set('JANET_NO_REALPATH', not get_option('realpath'))
conf.set('JANET_NO_PROCESSES', not get_option('processes'))
if get_option('os_name') != ''
conf.set('JANET_OS_NAME', get_option('os_name'))
endif
@@ -112,6 +116,7 @@ core_src = [
'src/core/io.c',
'src/core/marsh.c',
'src/core/math.c',
'src/core/net.c',
'src/core/os.c',
'src/core/parse.c',
'src/core/peg.c',
@@ -219,7 +224,8 @@ test_files = [
'test/suite5.janet',
'test/suite6.janet',
'test/suite7.janet',
'test/suite8.janet'
'test/suite8.janet',
'test/suite9.janet'
]
foreach t : test_files
test(t, janet_nativeclient, args : files([t]), workdir : meson.current_source_dir())
@@ -241,8 +247,5 @@ pkg.generate(libjanet,
install_man('janet.1')
install_man('jpm.1')
install_headers(['src/include/janet.h', jconf], subdir: 'janet')
janet_binscripts = [
'auxbin/jpm'
]
install_data(sources : janet_binscripts, install_dir : get_option('bindir'))
install_data(sources : ['jpm'], install_dir : get_option('bindir'))
install_data(sources : ['tools/.keep'], install_dir : join_paths(get_option('libdir'), 'janet'))

View File

@@ -11,6 +11,10 @@ option('peg', type : 'boolean', value : true)
option('typed_array', type : 'boolean', value : true)
option('int_types', type : 'boolean', value : true)
option('prf', type : 'boolean', value : true)
option('net', type : 'boolean', value : true)
option('processes', type : 'boolean', value : true)
option('umask', type : 'boolean', value : true)
option('realpath', type : 'boolean', value : true)
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200)

View File

@@ -141,6 +141,11 @@
[x &opt err]
(if x x (error (if err err "assert failure"))))
(defn errorf
"A combination of error and string/format. Equivalent to (error (string/format fmt ;args))"
[fmt & args]
(error (string/format fmt ;args)))
(defmacro default
"Define a default value for an optional argument.
Expands to (def sym (if (= nil sym) val sym))"
@@ -240,8 +245,8 @@
[& body]
(let [f (gensym) r (gensym)]
~(let [,f (,fiber/new (fn [] ,;body) :ie)
,r (,resume ,f)]
[(,not= :error (,fiber/status ,f)) ,r])))
,r (,resume ,f)]
[(,not= :error (,fiber/status ,f)) ,r])))
(defmacro and
"Evaluates to the last argument if all preceding elements are truthy, otherwise
@@ -369,7 +374,7 @@
"Similar to with, but if binding is false or nil, evaluates
the falsey path. Otherwise, evaluates the truthy path. In both cases,
ctor is bound to binding."
[[binding ctor dtor] truthy &opt falsey ]
[[binding ctor dtor] truthy &opt falsey]
~(if-let [,binding ,ctor]
,(apply defer [(or dtor :close) binding] [truthy])
,falsey))
@@ -618,9 +623,9 @@
(case (length functions)
0 nil
1 (in functions 0)
2 (let [[f g] functions] (fn [& x] (f (g ;x))))
3 (let [[f g h] functions] (fn [& x] (f (g (h ;x)))))
4 (let [[f g h i] functions] (fn [& x] (f (g (h (i ;x))))))
2 (let [[f g] functions] (fn [& x] (f (g ;x))))
3 (let [[f g h] functions] (fn [& x] (f (g (h ;x)))))
4 (let [[f g h i] functions] (fn [& x] (f (g (h (i ;x))))))
(let [[f g h i] functions]
(comp (fn [x] (f (g (h (i x)))))
;(tuple/slice functions 4 -1)))))
@@ -1070,7 +1075,7 @@
[bindings & body]
(def dyn-forms
(seq [i :range [0 (length bindings) 2]]
~(setdyn ,(bindings i) ,(bindings (+ i 1)))))
~(setdyn ,(bindings i) ,(bindings (+ i 1)))))
~(,resume (,fiber/new (fn [] ,;dyn-forms ,;body) :p)))
(defmacro with-vars
@@ -1084,12 +1089,12 @@
(def setnew (seq [i :range [0 len 2]] ['set (vars i) (vars (+ i 1))]))
(def restoreold (seq [i :range [0 len 2]] ['set (vars i) (temp (/ i 2))]))
(with-syms [ret f s]
~(do
,;saveold
(def ,f (,fiber/new (fn [] ,;setnew ,;body) :ti))
(def ,ret (,resume ,f))
,;restoreold
(if (= (,fiber/status ,f) :dead) ,ret (,propagate ,ret ,f)))))
~(do
,;saveold
(def ,f (,fiber/new (fn [] ,;setnew ,;body) :ti))
(def ,ret (,resume ,f))
,;restoreold
(if (= (,fiber/status ,f) :dead) ,ret (,propagate ,ret ,f)))))
(defn partial
"Partial function application."
@@ -1407,9 +1412,9 @@
~(do (def ,pattern ,expr) ,(onmatch))))
(and (tuple? pattern) (= :parens (tuple/type pattern)))
(if (and (= (pattern 0) '@) (symbol? (pattern 1)))
(if (= (get pattern 0) '@)
# Unification with external values
~(if (= ,(pattern 1) ,expr) ,(onmatch) ,sentinel)
~(if (= ,(get pattern 1) ,expr) ,(onmatch) ,sentinel)
(match-1
(in pattern 0) expr
(fn []
@@ -1443,7 +1448,7 @@
(if (= key nil)
(onmatch)
~(do (def ,$val (,get ,$dict ,key))
,(match-1 [(in pattern key) [not= nil $val]] $val aux seen)))))
,(match-1 [(in pattern key) [not= nil $val]] $val aux seen)))))
,sentinel)))
:else ~(if (= ,pattern ,expr) ,(onmatch) ,sentinel)))
@@ -1751,8 +1756,8 @@
:array (tuple/slice (map freeze x))
:tuple (tuple/slice (map freeze x))
:table (if-let [p (table/getproto x)]
(freeze (merge (table/clone p) x))
(struct ;(map freeze (kvs x))))
(freeze (merge (table/clone p) x))
(struct ;(map freeze (kvs x))))
:struct (struct ;(map freeze (kvs x)))
:buffer (string x)
x))
@@ -1951,6 +1956,7 @@
(default on-parse-error bad-parse)
(default evaluator (fn evaluate [x &] (x)))
(default where "<anonymous>")
(default guard :ydt)
# Are we done yet?
(var going true)
@@ -1977,12 +1983,19 @@
(string err " on line " line ", column " column)
err))
(on-compile-error msg errf where))))
(or guard :a)))
guard))
(fiber/setenv f env)
(while (fiber/can-resume? f)
(def res (resume f resumeval))
(when good (when going (set resumeval (onstatus f res))))))
(defn parse-err
"Handle parser error in the correct environment"
[p where]
(def f (coro (on-parse-error p where)))
(fiber/setenv f env)
(resume f))
# Loop
(def buf @"")
(while going
@@ -2000,12 +2013,12 @@
(while (parser/has-more p)
(eval1 (parser/produce p)))
(when (= (parser/status p) :error)
(on-parse-error p where))))
(parse-err p where))))
# Check final parser state
(while (parser/has-more p)
(eval1 (parser/produce p)))
(when (= (parser/status p) :error)
(on-parse-error p where))
(parse-err p where))
(in env :exit-value env))
@@ -2052,6 +2065,19 @@
(res)
(error (res :error))))
(defn parse
"Parse a string and return the first value. For complex parsing, such as for a repl with error handling,
use the parser api."
[str]
(let [p (parser/new)]
(parser/consume p str)
(parser/eof p)
(if (parser/has-more p)
(parser/produce p)
(if (= :error (parser/status p))
(error (parser/error p))
(error "no value")))))
(def make-image-dict
"A table used in combination with marshal to marshal code (images), such that
(make-image x) is the same as (marshal x make-image-dict)."
@@ -2105,11 +2131,12 @@
(defn- find-prefix
[pre]
(or (find-index |(and (string? ($ 0)) (string/has-prefix? pre ($ 0))) module/paths) 0))
(array/insert module/paths 0 [(string ":cur:/:all:" ext) loader check-.])
(def all-index (find-prefix ":all:"))
(array/insert module/paths all-index [(string ":all:" ext) loader not-check-.])
(def sys-index (find-prefix ":sys:"))
(array/insert module/paths sys-index [(string ":sys:/:all:" ext) loader not-check-.])
(def curall-index (find-prefix ":cur:/:all:"))
(array/insert module/paths curall-index [(string ":cur:/:all:" ext) loader check-.])
module/paths)
(module/add-paths ":native:" :native)
@@ -2127,7 +2154,7 @@
(when f
(def res
(try (do (file/read f 1) true)
([err] nil)))
([err] nil)))
(file/close f)
res))))
@@ -2149,8 +2176,8 @@
(when (mod-filter checker path)
(if (function? p)
(when-let [res (p path)]
(set ret [res mod-kind])
(break))
(set ret [res mod-kind])
(break))
(do
(def fullpath (string (module/expand-path path p)))
(when (fexists fullpath)
@@ -2333,12 +2360,15 @@
[&opt nth frame-idx]
(in (.slots frame-idx) (or nth 0)))
# Conditional compilation for disasm
(def disasm-alias (if-let [x (_env 'disasm)] (x :value)))
(defn .disasm
"Gets the assembly for the current function."
[&opt n]
(def frame (.frame n))
(def func (frame :function))
(disasm func))
(disasm-alias func))
(defn .bytecode
"Get the bytecode for the current function."
@@ -2350,10 +2380,10 @@
[&opt n]
(def frame (.frame n))
(def func (frame :function))
(def dasm (disasm func))
(def bytecode (dasm 'bytecode))
(def dasm (disasm-alias func))
(def bytecode (in dasm 'bytecode))
(def pc (frame :pc))
(def sourcemap (dasm 'sourcemap))
(def sourcemap (in dasm 'sourcemap))
(var last-loc [-2 -2])
(print "\n signal: " (.signal))
(print " function: " (dasm 'name) " [" (in dasm 'source "") "]")
@@ -2375,14 +2405,6 @@
(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" all-source "\n"))
(defn .breakall
"Set breakpoints on all instructions in the current function."
[&opt n]
@@ -2401,6 +2423,22 @@
(debug/unfbreak fun i))
(print "Cleared " (length bytecode) " breakpoints in " fun))
(unless (get _env 'disasm)
(put _env '.disasm nil)
(put _env '.bytecode nil)
(put _env '.breakall nil)
(put _env '.clearall nil)
(put _env '.ppasm nil))
(put _env 'disasm-alias nil)
(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" all-source "\n"))
(defn .break
"Set breakpoint at the current pc."
[]
@@ -2441,8 +2479,11 @@
(set res (debug/step (.fiber))))
res)
(def debugger-env
"An environment that contains dot prefixed functions for debugging."
@{})
(def- debugger-keys (filter (partial string/has-prefix? ".") (keys _env)))
(def- debugger-env @{})
(each k debugger-keys (put debugger-env k (_env k)) (put _env k nil))
(put _env 'debugger-keys nil)
@@ -2495,19 +2536,19 @@
(fn [f x]
(if (= :dead (fiber/status f))
(put e '_ @{:value x})
(do
(put e '_ @{:value x})
(printf (get e :pretty-format "%q") x)
(flush))
(if (e :debug)
(enter-debugger f x)
(do (debug/stacktrace f x) (eflush))))))
(run-context {:env env
:chunks chunks
:expander (fn [x] [pp x])
:on-status (or onsignal (make-onsignal env 1))
:source "repl"}))
(put _env 'debugger-env nil)
###
###
### CLI Tool Main
@@ -2537,6 +2578,9 @@
(def- importers {'import true 'import* true 'use true 'dofile true 'require true})
# conditional compilation for reduced os
(def- getenv-alias (if-let [entry (in _env 'os/getenv)] (entry :value) (fn [&])))
(defn cli-main
"Entrance for the Janet CLI tool. Call this functions with the command line
arguments as an array or tuple of strings to invoke the CLI interface."
@@ -2554,8 +2598,8 @@
(var *debug* false)
(var *compile-only* false)
(if-let [jp (os/getenv "JANET_PATH")] (setdyn :syspath jp))
(if-let [jp (os/getenv "JANET_HEADERPATH")] (setdyn :headerpath jp))
(if-let [jp (getenv-alias "JANET_PATH")] (setdyn :syspath jp))
(if-let [jp (getenv-alias "JANET_HEADERPATH")] (setdyn :headerpath jp))
# Flag handlers
(def handlers
@@ -2570,7 +2614,7 @@
-d : Set the debug flag in the repl
-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)
-q : Hide logo (quiet)
-k : Compile scripts but do not execute (flycheck)
-m syspath : Set system path for loading global modules
-c source output : Compile janet source code into an image
@@ -2662,16 +2706,15 @@
(def getter (if *raw-stdin* getstdin getline))
(defn getchunk [buf p]
(getter (getprompt p) buf env))
(def onsig (if *quiet* (fn [x &] x) nil))
(setdyn :pretty-format (if *colorize* "%.20Q" "%.20q"))
(setdyn :err-color (if *colorize* true))
(repl getchunk onsig env)))
(repl getchunk nil env)))
(put _env 'no-side-effects nil)
(put _env 'is-safe-def nil)
(put _env 'safe-forms nil)
(put _env 'importers nil)
(put _env 'getenv-alias nil)
###
###
@@ -2758,6 +2801,7 @@
"src/core/io.c"
"src/core/marsh.c"
"src/core/math.c"
"src/core/net.c"
"src/core/os.c"
"src/core/parse.c"
"src/core/peg.c"

View File

@@ -28,9 +28,9 @@
#define JANET_VERSION_MAJOR 1
#define JANET_VERSION_MINOR 9
#define JANET_VERSION_PATCH 0
#define JANET_VERSION_PATCH 1
#define JANET_VERSION_EXTRA ""
#define JANET_VERSION "1.9.0-dev"
#define JANET_VERSION "1.9.1"
/* #define JANET_BUILD "local" */
@@ -45,14 +45,22 @@
/* #define JANET_NO_DOCSTRINGS */
/* #define JANET_NO_SOURCEMAPS */
/* #define JANET_REDUCED_OS */
/* Other settings */
/* #define JANET_NO_PROCESSES */
/* #define JANET_NO_ASSEMBLER */
/* #define JANET_NO_PEG */
/* #define JANET_NO_NET */
/* #define JANET_NO_TYPED_ARRAY */
/* #define JANET_NO_INT_TYPES */
/* Other settings */
/* #define JANET_NO_PRF */
/* #define JANET_NO_UTC_MKTIME */
/* #define JANET_NO_REALPATH */
/* #define JANET_NO_SYMLINKS */
/* #define JANET_NO_UMASK */
/* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */
/* #define JANET_EXIT(msg) do { printf("C assert failed executing janet: %s\n", msg); exit(1); } while (0) */
/* #define JANET_TOP_LEVEL_SIGNAL(msg) call_my_function((msg), stderr) */
/* #define JANET_RECURSION_GUARD 1024 */
/* #define JANET_MAX_PROTO_DEPTH 200 */
/* #define JANET_MAX_MACRO_EXPAND 200 */

View File

@@ -964,7 +964,7 @@ static const JanetReg asm_cfuns[] = {
"asm", cfun_asm,
JDOC("(asm assembly)\n\n"
"Returns a new function that is the compiled result of the assembly.\n"
"The syntax for the assembly can be found on the janet wiki. Will throw an\n"
"The syntax for the assembly can be found on the Janet website. Will throw an\n"
"error on invalid assembly.")
},
{

View File

@@ -27,6 +27,15 @@
#include "fiber.h"
#endif
JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
#ifdef JANET_TOP_LEVEL_SIGNAL
JANET_TOP_LEVEL_SIGNAL(msg);
#else
fputs(msg, stdout);
exit(1);
#endif
}
void janet_signalv(JanetSignal sig, Janet message) {
if (janet_vm_return_reg != NULL) {
*janet_vm_return_reg = message;
@@ -37,8 +46,8 @@ void janet_signalv(JanetSignal sig, Janet message) {
longjmp(*janet_vm_jmp_buf, sig);
#endif
} else {
fputs((const char *)janet_formatc("janet top level signal - %v\n", message), stdout);
exit(1);
const char *str = (const char *)janet_formatc("janet top level signal - %v\n", message);
janet_top_level_signal(str);
}
}

View File

@@ -855,10 +855,10 @@ static const JanetReg compile_cfuns[] = {
{
"compile", cfun,
JDOC("(compile ast &opt env source)\n\n"
"Compiles an Abstract Syntax Tree (ast) into a janet function. "
"Compiles an Abstract Syntax Tree (ast) into a function. "
"Pair the compile function with parsing functionality to implement "
"eval. Returns a janet function and does not modify ast. Throws an "
"error if the ast cannot be compiled.")
"eval. Returns a new function and does not modify ast. Returns an error "
"struct with keys :line, :column, and :error if compilation fails.")
},
{NULL, NULL, NULL}
};

View File

@@ -643,8 +643,8 @@ static const JanetReg corelib_cfuns[] = {
{
"hash", janet_core_hash,
JDOC("(hash value)\n\n"
"Gets a hash value for any janet value. The hash is an integer can be used "
"as a cheap hash function for all janet objects. If two values are strictly equal, "
"Gets a hash for any value. The hash is an integer can be used "
"as a cheap hash function for all values. If two values are strictly equal, "
"then they will have the same hash value.")
},
{
@@ -685,9 +685,9 @@ static const JanetReg corelib_cfuns[] = {
"\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 extension if given\n"
"\t:name:\tthe name component of path, with extension if given\n"
"\t:native:\tthe extension used to load natives, .so or .dll\n"
"\t:sys:\tthe system path, or (syn :syspath)")
"\t:sys:\tthe system path, or (dyn :syspath)")
},
{
"int?", janet_core_check_int,
@@ -742,7 +742,7 @@ static void janet_quick_asm(
janet_def(env, name, janet_wrap_function(janet_thunk(def)), doc);
}
/* Macros for easier inline janet assembly */
/* Macros for easier inline assembly */
#define SSS(op, a, b, c) ((op) | ((a) << 8) | ((b) << 16) | ((c) << 24))
#define SS(op, a, b) ((op) | ((a) << 8) | ((b) << 16))
#define SSI(op, a, b, I) ((op) | ((a) << 8) | ((b) << 16) | ((uint32_t)(I) << 24))
@@ -1004,6 +1004,9 @@ static void janet_load_libs(JanetTable *env) {
#ifdef JANET_THREADS
janet_lib_thread(env);
#endif
#ifdef JANET_NET
janet_lib_net(env);
#endif
}
#ifdef JANET_BOOTSTRAP
@@ -1021,7 +1024,7 @@ JanetTable *janet_core_env(JanetTable *replacements) {
janet_quick_asm(env, JANET_FUN_NEXT,
"next", 2, 1, 2, 2, next_asm, sizeof(next_asm),
JDOC("(next ds &opt key)\n\n"
"Gets the next key in a datastructure. Can be used to iterate through "
"Gets the next key in a data structure. Can be used to iterate through "
"the keys of a data structure in an unspecified order. Keys are guaranteed "
"to be seen only once per iteration if they data structure is not mutated "
"during iteration. If key is nil, next returns the first key. If next "
@@ -1032,7 +1035,8 @@ JanetTable *janet_core_env(JanetTable *replacements) {
"Propagate a signal from a fiber to the current fiber. The resulting "
"stack trace from the current fiber will include frames from fiber. If "
"fiber is in a state that can be resumed, resuming the current fiber will "
"first resume fiber."));
"first resume fiber. This function can be used to re-raise an error without "
"losing the original stack trace."));
janet_quick_asm(env, JANET_FUN_DEBUG,
"debug", 1, 0, 1, 1, debug_asm, sizeof(debug_asm),
JDOC("(debug &opt x)\n\n"
@@ -1104,7 +1108,7 @@ JanetTable *janet_core_env(JanetTable *replacements) {
JDOC("(/ & xs)\n\n"
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
"values. Division by two integers uses truncating division."));
"values."));
templatize_varop(env, JANET_FUN_BAND, "band", -1, -1, JOP_BAND,
JDOC("(band & xs)\n\n"
"Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));

View File

@@ -26,7 +26,11 @@
#define JANET_FEATURES_H_defined
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#define _POSIX_C_SOURCE 200809L
#endif
#if defined(WIN32) || defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#endif
/* Needed for realpath on linux */

View File

@@ -405,6 +405,10 @@ JanetFiber *janet_current_fiber(void) {
return janet_vm_fiber;
}
JanetFiber *janet_root_fiber(void) {
return janet_vm_root_fiber;
}
/* CFuns */
static Janet cfun_fiber_getenv(int32_t argc, Janet *argv) {
@@ -508,6 +512,12 @@ static Janet cfun_fiber_current(int32_t argc, Janet *argv) {
return janet_wrap_fiber(janet_vm_fiber);
}
static Janet cfun_fiber_root(int32_t argc, Janet *argv) {
(void) argv;
janet_fixarity(argc, 0);
return janet_wrap_fiber(janet_vm_root_fiber);
}
static Janet cfun_fiber_maxstack(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
JanetFiber *fiber = janet_getfiber(argv, 0);
@@ -575,6 +585,12 @@ static const JanetReg fiber_cfuns[] = {
"\t:alive - the fiber is currently running and cannot be resumed\n"
"\t:new - the fiber has just been created and not yet run")
},
{
"fiber/root", cfun_fiber_root,
JDOC("(fiber/root)\n\n"
"Returns the current root fiber. The root fiber is the oldest ancestor "
"that does not have a parent.")
},
{
"fiber/current", cfun_fiber_current,
JDOC("(fiber/current)\n\n"

View File

@@ -389,6 +389,9 @@ void janet_collect(void) {
if (janet_vm_gc_suspend) return;
depth = JANET_RECURSION_GUARD;
orig_rootcount = janet_vm_root_count;
#ifdef JANET_NET
janet_net_markloop();
#endif
for (i = 0; i < orig_rootcount; i++)
janet_mark(janet_vm_roots[i]);
while (orig_rootcount < janet_vm_root_count) {
@@ -530,7 +533,7 @@ void *janet_srealloc(void *mem, size_t size) {
if (i == 0) break;
}
}
janet_exit("invalid janet_srealloc");
JANET_EXIT("invalid janet_srealloc");
}
void janet_sfinalizer(void *mem, JanetScratchFinalizer finalizer) {
@@ -551,5 +554,5 @@ void janet_sfree(void *mem) {
if (i == 0) break;
}
}
janet_exit("invalid janet_sfree");
JANET_EXIT("invalid janet_sfree");
}

View File

@@ -30,7 +30,9 @@
#include <errno.h>
#ifndef JANET_WINDOWS
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
static int cfun_io_gc(void *p, size_t len);
@@ -87,18 +89,17 @@ static Janet makef(FILE *f, int flags) {
JanetFile *iof = (JanetFile *) janet_abstract(&janet_file_type, sizeof(JanetFile));
iof->file = f;
iof->flags = flags;
#ifndef JANET_WINDOWS
/* While we would like fopen to set cloexec by default (like O_CLOEXEC) with the e flag, that is
* not standard. */
if (!(flags & JANET_FILE_NOT_CLOSEABLE))
fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
#endif
return janet_wrap_abstract(iof);
}
/* Open a process */
#ifdef __EMSCRIPTEN__
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
(void) argc;
(void) argv;
janet_panic("not implemented on this platform");
return janet_wrap_nil();
}
#else
#ifndef JANET_NO_PROCESSES
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
janet_arity(argc, 1, 2);
const uint8_t *fname = janet_getstring(argv, 0);
@@ -129,6 +130,7 @@ static Janet cfun_io_popen(int32_t argc, Janet *argv) {
static Janet cfun_io_temp(int32_t argc, Janet *argv) {
(void)argv;
janet_fixarity(argc, 0);
// XXX use mkostemp when we can to avoid CLOEXEC race.
FILE *tmp = tmpfile();
if (!tmp)
janet_panicf("unable to create temporary file - %s", strerror(errno));
@@ -239,12 +241,24 @@ static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
return argv[0];
}
#ifdef JANET_WINDOWS
#define pclose _pclose
#define WEXITSTATUS(x) x
#endif
/* Cleanup a file */
static int cfun_io_gc(void *p, size_t len) {
(void) len;
JanetFile *iof = (JanetFile *)p;
if (!(iof->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
return fclose(iof->file);
/* We can't panic inside a gc, so just ignore bad statuses here */
if (iof->flags & JANET_FILE_PIPED) {
#ifndef JANET_NO_PROCESSES
pclose(iof->file);
#endif
} else {
fclose(iof->file);
}
}
return 0;
}
@@ -258,14 +272,12 @@ static Janet cfun_io_fclose(int32_t argc, Janet *argv) {
if (iof->flags & (JANET_FILE_NOT_CLOSEABLE))
janet_panic("file not closable");
if (iof->flags & JANET_FILE_PIPED) {
#ifdef JANET_WINDOWS
#define pclose _pclose
#define WEXITSTATUS(x) x
#endif
#ifndef JANET_NO_PROCESSES
int status = pclose(iof->file);
iof->flags |= JANET_FILE_CLOSED;
if (status == -1) janet_panic("could not close file");
return janet_wrap_integer(WEXITSTATUS(status));
#endif
} else {
if (fclose(iof->file)) janet_panic("could not close file");
iof->flags |= JANET_FILE_CLOSED;
@@ -640,6 +652,7 @@ static const JanetReg io_cfuns[] = {
"for the relative number of bytes to seek in the file. n may be a real "
"number to handle large files of more the 4GB. Returns the file handle.")
},
#ifndef JANET_NO_PROCESSES
{
"file/popen", cfun_io_popen,
JDOC("(file/popen path &opt mode)\n\n"
@@ -648,6 +661,7 @@ static const JanetReg io_cfuns[] = {
"process can be read from the file. In :w mode, the stdin of the process "
"can be written to. Returns the new file.")
},
#endif
{NULL, NULL, NULL}
};

View File

@@ -1417,17 +1417,17 @@ static const JanetReg marsh_cfuns[] = {
{
"marshal", cfun_marshal,
JDOC("(marshal x &opt reverse-lookup buffer)\n\n"
"Marshal a janet value into a buffer and return the buffer. The buffer "
"Marshal a value into a buffer and return the buffer. The buffer "
"can the later be unmarshalled to reconstruct the initial value. "
"Optionally, one can pass in a reverse lookup table to not marshal "
"aliased values that are found in the table. Then a forward"
"lookup table can be used to recover the original janet value when "
"lookup table can be used to recover the original value when "
"unmarshalling.")
},
{
"unmarshal", cfun_unmarshal,
JDOC("(unmarshal buffer &opt lookup)\n\n"
"Unmarshal a janet value from a buffer. An optional lookup table "
"Unmarshal a value from a buffer. An optional lookup table "
"can be provided to allow for aliases to be resolved. Returns the value "
"unmarshalled from the buffer.")
},

View File

@@ -413,7 +413,7 @@ static const JanetReg math_cfuns[] = {
"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. "
"The seed should be an unsigned 32 bit integer or a buffer. "
"Do not use this for cryptography. Returns a core/rng abstract type.")
},
{
@@ -480,7 +480,7 @@ static const JanetReg math_cfuns[] = {
},
{
"math/next", janet_nextafter,
JDOC("(math/next y)\n\n"
JDOC("(math/next x y)\n\n"
"Returns the next representable floating point value after x in the direction of y.")
},
{NULL, NULL, NULL}

677
src/core/net.c Normal file
View File

@@ -0,0 +1,677 @@
/*
* Copyright (c) 2020 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 "features.h"
#include <janet.h>
#include "util.h"
#endif
#ifdef JANET_WINDOWS
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "Advapi32.lib")
#else
#include <unistd.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <netdb.h>
#include <fcntl.h>
#endif
/*
* Streams
*/
#define JANET_STREAM_CLOSED 1
#define JANET_STREAM_READABLE 2
#define JANET_STREAM_WRITABLE 4
static int janet_stream_close(void *p, size_t s);
static int janet_stream_getter(void *p, Janet key, Janet *out);
static const JanetAbstractType StreamAT = {
"core/stream",
janet_stream_close,
NULL,
janet_stream_getter,
JANET_ATEND_GET
};
#ifdef JANET_WINDOWS
typedef struct {
SOCKET fd;
int flags;
} JanetStream;
#define JSOCKCLOSE(x) closesocket(x)
#define JSOCKDEFAULT INVALID_SOCKET
#define JLASTERR WSAGetLastError()
#define JSOCKVALID(x) ((x) != INVALID_SOCKET)
#define JEINTR WSAEINTR
#define JEWOULDBLOCK WSAEWOULDBLOCK
#define JEAGAIN WSAEWOULDBLOCK
#define JPOLL WSAPoll
#define JPollStruct WSAPOLLFD
#define JSock SOCKET
#define JReadInt long
#define JSOCKFLAGS 0
static JanetStream *make_stream(SOCKET fd, int flags) {
u_long iMode = 0;
JanetStream *stream = janet_abstract(&StreamAT, sizeof(JanetStream));
ioctlsocket(fd, FIONBIO, &iMode);
stream->fd = fd;
stream->flags = flags;
return stream;
}
#else
typedef struct {
int fd;
int flags;
} JanetStream;
#define JSOCKCLOSE(x) close(x)
#define JSOCKDEFAULT 0
#define JLASTERR errno
#define JSOCKVALID(x) ((x) >= 0)
#define JEINTR EINTR
#define JEWOULDBLOCK EWOULDBLOCK
#define JEAGAIN EAGAIN
#define JPOLL poll
#define JPollStruct struct pollfd
#define JSock int
#define JReadInt ssize_t
#ifdef SOCK_CLOEXEC
#define JSOCKFLAGS SOCK_CLOEXEC
#else
#define JSOCKFLAGS 0
#endif
static JanetStream *make_stream(int fd, int flags) {
JanetStream *stream = janet_abstract(&StreamAT, sizeof(JanetStream));
#ifndef SOCK_CLOEXEC
int extra = O_CLOEXEC;
#else
int extra = 0;
#endif
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK | extra);
stream->fd = fd;
stream->flags = flags;
return stream;
}
#endif
/* We pass this flag to all send calls to prevent sigpipe */
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
static int janet_stream_close(void *p, size_t s) {
(void) s;
JanetStream *stream = p;
if (!(stream->flags & JANET_STREAM_CLOSED)) {
stream->flags |= JANET_STREAM_CLOSED;
JSOCKCLOSE(stream->fd);
}
return 0;
}
/*
* Event loop
*/
/* This large struct describes a waiting file descriptor, as well
* as what to do when we get an event for it. It is a variant type, where
* each variant implements a simple state machine. */
typedef struct {
/* File descriptor to listen for events on. */
JanetStream *stream;
/* Fiber to resume when event finishes. Can be NULL, in which case,
* no fiber is resumed when event completes. */
JanetFiber *fiber;
/* What kind of event we are listening for.
* As more IO functionality get's added, we can
* expand this. */
enum {
JLE_READ_CHUNK,
JLE_READ_SOME,
JLE_READ_ACCEPT,
JLE_CONNECT,
JLE_WRITE_FROM_BUFFER,
JLE_WRITE_FROM_STRINGLIKE
} event_type;
/* Each variant can have a different payload. */
union {
/* JLE_READ_CHUNK/JLE_READ_SOME */
struct {
int32_t bytes_left;
JanetBuffer *buf;
} read_chunk;
/* JLE_READ_ACCEPT */
struct {
JanetFunction *handler;
} read_accept;
/* JLE_WRITE_FROM_BUFFER */
struct {
JanetBuffer *buf;
int32_t start;
} write_from_buffer;
/* JLE_WRITE_FROM_STRINGLIKE */
struct {
const uint8_t *str;
int32_t start;
} write_from_stringlike;
} data;
} JanetLoopFD;
#define JANET_LOOPFD_MAX 1024
/* Global loop data */
JANET_THREAD_LOCAL JPollStruct janet_vm_pollfds[JANET_LOOPFD_MAX];
JANET_THREAD_LOCAL JanetLoopFD janet_vm_loopfds[JANET_LOOPFD_MAX];
JANET_THREAD_LOCAL int janet_vm_loop_count;
/* We could also add/remove gc roots. This is easier for now. */
void janet_net_markloop(void) {
for (int i = 0; i < janet_vm_loop_count; i++) {
JanetLoopFD lfd = janet_vm_loopfds[i];
if (lfd.fiber != NULL) {
janet_mark(janet_wrap_fiber(lfd.fiber));
}
janet_mark(janet_wrap_abstract(lfd.stream));
switch (lfd.event_type) {
default:
break;
case JLE_READ_CHUNK:
case JLE_READ_SOME:
janet_mark(janet_wrap_buffer(lfd.data.read_chunk.buf));
break;
case JLE_READ_ACCEPT:
janet_mark(janet_wrap_function(lfd.data.read_accept.handler));
break;
case JLE_CONNECT:
break;
case JLE_WRITE_FROM_BUFFER:
janet_mark(janet_wrap_buffer(lfd.data.write_from_buffer.buf));
break;
case JLE_WRITE_FROM_STRINGLIKE:
janet_mark(janet_wrap_string(lfd.data.write_from_stringlike.str));
}
}
}
/* Add a loop fd to the global event loop */
static int janet_loop_schedule(JanetLoopFD lfd, short events) {
if (janet_vm_loop_count == JANET_LOOPFD_MAX) {
return -1;
}
int index = janet_vm_loop_count++;
janet_vm_loopfds[index] = lfd;
janet_vm_pollfds[index].fd = lfd.stream->fd;
janet_vm_pollfds[index].events = events;
janet_vm_pollfds[index].revents = 0;
return index;
}
/* Remove event from list */
static void janet_loop_rmindex(int index) {
janet_vm_loopfds[index] = janet_vm_loopfds[--janet_vm_loop_count];
janet_vm_pollfds[index] = janet_vm_pollfds[janet_vm_loop_count];
}
/* Return delta in number of loop fds. Abstracted out so
* we can separate out the polling logic */
static size_t janet_loop_event(size_t index) {
JanetLoopFD *jlfd = janet_vm_loopfds + index;
JanetStream *stream = jlfd->stream;
JSock fd = stream->fd;
int ret = 1;
int should_resume = 0;
Janet resumeval = janet_wrap_nil();
if (stream->flags & JANET_STREAM_CLOSED) {
should_resume = 1;
ret = 0;
} else {
switch (jlfd->event_type) {
case JLE_READ_CHUNK:
case JLE_READ_SOME: {
JanetBuffer *buffer = jlfd->data.read_chunk.buf;
int32_t bytes_left = jlfd->data.read_chunk.bytes_left;
janet_buffer_extra(buffer, bytes_left);
if (!(stream->flags & JANET_STREAM_READABLE)) {
should_resume = 1;
ret = 0;
break;
}
JReadInt nread;
do {
nread = recv(fd, buffer->data + buffer->count, bytes_left, 0);
} while (nread == -1 && JLASTERR == JEINTR);
if (JLASTERR == JEAGAIN || JLASTERR == JEWOULDBLOCK) {
ret = 1;
break;
}
if (nread > 0) {
buffer->count += nread;
bytes_left -= nread;
} else {
bytes_left = 0;
}
if (jlfd->event_type == JLE_READ_SOME || bytes_left == 0) {
should_resume = 1;
if (nread > 0) {
resumeval = janet_wrap_buffer(buffer);
}
ret = 0;
} else {
jlfd->data.read_chunk.bytes_left = bytes_left;
ret = 1;
}
break;
}
case JLE_READ_ACCEPT: {
JSock connfd = accept(fd, NULL, NULL);
if (JSOCKVALID(connfd)) {
/* Made a new connection socket */
JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
Janet streamv = janet_wrap_abstract(stream);
JanetFunction *handler = jlfd->data.read_accept.handler;
Janet out;
JanetFiber *fiberp = NULL;
/* Launch connection fiber */
JanetSignal sig = janet_pcall(handler, 1, &streamv, &out, &fiberp);
if (sig != JANET_SIGNAL_OK && sig != JANET_SIGNAL_EVENT) {
janet_stacktrace(fiberp, out);
}
}
ret = JANET_LOOPFD_MAX;
break;
}
case JLE_WRITE_FROM_BUFFER:
case JLE_WRITE_FROM_STRINGLIKE: {
int32_t start, len;
const uint8_t *bytes;
if (!(stream->flags & JANET_STREAM_WRITABLE)) {
should_resume = 1;
ret = 0;
break;
}
if (jlfd->event_type == JLE_WRITE_FROM_BUFFER) {
JanetBuffer *buffer = jlfd->data.write_from_buffer.buf;
bytes = buffer->data;
len = buffer->count;
start = jlfd->data.write_from_buffer.start;
} else {
bytes = jlfd->data.write_from_stringlike.str;
len = janet_string_length(bytes);
start = jlfd->data.write_from_stringlike.start;
}
if (start < len) {
int32_t nbytes = len - start;
JReadInt nwrote;
do {
nwrote = send(fd, bytes + start, nbytes, MSG_NOSIGNAL);
} while (nwrote == -1 && JLASTERR == JEINTR);
if (nwrote > 0) {
start += nwrote;
} else {
start = len;
}
}
if (start >= len) {
should_resume = 1;
ret = 0;
} else {
if (jlfd->event_type == JLE_WRITE_FROM_BUFFER) {
jlfd->data.write_from_buffer.start = start;
} else {
jlfd->data.write_from_stringlike.start = start;
}
ret = 1;
}
break;
}
case JLE_CONNECT: {
break;
}
}
}
/* Resume a fiber for some events */
if (NULL != jlfd->fiber && should_resume) {
/* Resume the fiber */
Janet out;
JanetSignal sig = janet_continue(jlfd->fiber, resumeval, &out);
if (sig != JANET_SIGNAL_OK && sig != JANET_SIGNAL_EVENT) {
janet_stacktrace(jlfd->fiber, out);
}
}
/* Remove this handler from the handler pool. */
if (should_resume) janet_loop_rmindex((int) index);
return ret;
}
static void janet_loop1(void) {
/* Remove closed file descriptors */
for (int i = 0; i < janet_vm_loop_count;) {
if (janet_vm_loopfds[i].stream->flags & JANET_STREAM_CLOSED) {
janet_loop_rmindex(i);
} else {
i++;
}
}
/* Poll */
if (janet_vm_loop_count == 0) return;
int ready;
do {
ready = JPOLL(janet_vm_pollfds, janet_vm_loop_count, -1);
} while (ready == -1 && JLASTERR == JEINTR);
if (ready == -1) return;
/* Handle events */
for (int i = 0; i < janet_vm_loop_count;) {
int revents = janet_vm_pollfds[i].revents;
janet_vm_pollfds[i].revents = 0;
if ((janet_vm_pollfds[i].events | POLLHUP | POLLERR) & revents) {
size_t delta = janet_loop_event(i);
i += (int) delta;
} else {
i++;
}
}
}
void janet_loop(void) {
while (janet_vm_loop_count) {
janet_loop1();
}
}
/*
* Scheduling Helpers
*/
#define JANET_SCHED_FSOME 1
JANET_NO_RETURN static void janet_sched_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) {
JanetLoopFD lfd;
lfd.stream = stream;
lfd.fiber = janet_root_fiber();
lfd.event_type = (flags & JANET_SCHED_FSOME) ? JLE_READ_SOME : JLE_READ_CHUNK;
lfd.data.read_chunk.buf = buf;
lfd.data.read_chunk.bytes_left = nbytes;
janet_loop_schedule(lfd, POLLIN);
janet_signalv(JANET_SIGNAL_EVENT, janet_wrap_nil());
}
JANET_NO_RETURN static void janet_sched_write_buffer(JanetStream *stream, JanetBuffer *buf) {
JanetLoopFD lfd;
lfd.stream = stream;
lfd.fiber = janet_root_fiber();
lfd.event_type = JLE_WRITE_FROM_BUFFER;
lfd.data.write_from_buffer.buf = buf;
lfd.data.write_from_buffer.start = 0;
janet_loop_schedule(lfd, POLLOUT);
janet_signalv(JANET_SIGNAL_EVENT, janet_wrap_nil());
}
JANET_NO_RETURN static void janet_sched_write_stringlike(JanetStream *stream, const uint8_t *str) {
JanetLoopFD lfd;
lfd.stream = stream;
lfd.fiber = janet_root_fiber();
lfd.event_type = JLE_WRITE_FROM_STRINGLIKE;
lfd.data.write_from_stringlike.str = str;
lfd.data.write_from_stringlike.start = 0;
janet_loop_schedule(lfd, POLLOUT);
janet_signalv(JANET_SIGNAL_EVENT, janet_wrap_nil());
}
/* Needs argc >= offset + 2 */
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset) {
/* Get host and port */
const char *host = janet_getcstring(argv, offset);
const char *port;
if (janet_checkint(argv[offset + 1])) {
port = (const char *)janet_to_string(argv[offset + 1]);
} else {
port = janet_getcstring(argv, offset + 1);
}
/* getaddrinfo */
struct addrinfo *ai = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_PASSIVE;
int status = getaddrinfo(host, port, &hints, &ai);
if (status) {
janet_panicf("could not get address info: %s", gai_strerror(status));
}
return ai;
}
/*
* C Funs
*/
static Janet cfun_net_connect(int32_t argc, Janet *argv) {
janet_fixarity(argc, 2);
struct addrinfo *ai = janet_get_addrinfo(argv, 0);
/* Create socket */
JSock sock = socket(ai->ai_family, ai->ai_socktype | JSOCKFLAGS, ai->ai_protocol);
if (!JSOCKVALID(sock)) {
freeaddrinfo(ai);
janet_panic("could not create socket");
}
/* Connect to socket */
int status = connect(sock, ai->ai_addr, (int) ai->ai_addrlen);
freeaddrinfo(ai);
if (status == -1) {
JSOCKCLOSE(sock);
janet_panic("could not connect to socket");
}
/* Wrap socket in abstract type JanetStream */
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
return janet_wrap_abstract(stream);
}
static Janet cfun_net_server(int32_t argc, Janet *argv) {
janet_fixarity(argc, 3);
/* Get host, port, and handler*/
JanetFunction *fun = janet_getfunction(argv, 2);
struct addrinfo *ai = janet_get_addrinfo(argv, 0);
/* Check all addrinfos in a loop for the first that we can bind to. */
JSock sfd = JSOCKDEFAULT;
struct addrinfo *rp = NULL;
for (rp = ai; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
if (!JSOCKVALID(sfd)) continue;
/* Set various socket options */
int enable = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
JSOCKCLOSE(sfd);
janet_panic("setsockopt(SO_REUSEADDR) failed");
}
#ifdef SO_NOSIGPIPE
if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, &enable, sizeof(int)) < 0) {
JSOCKCLOSE(sfd);
janet_panic("setsockopt(SO_NOSIGPIPE) failed");
}
#endif
#ifdef SO_REUSEPORT
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
JSOCKCLOSE(sfd);
janet_panic("setsockopt(SO_REUSEPORT) failed");
}
#endif
/* Bind */
if (bind(sfd, rp->ai_addr, (int) rp->ai_addrlen) == 0) break;
JSOCKCLOSE(sfd);
}
if (NULL == rp) {
freeaddrinfo(ai);
janet_panic("could not bind to any sockets");
}
/* listen */
int status = listen(sfd, 1024);
freeaddrinfo(ai);
if (status) {
JSOCKCLOSE(sfd);
janet_panic("could not listen on file descriptor");
}
/* Put sfd on our loop */
JanetLoopFD lfd = {0};
lfd.stream = make_stream(sfd, 0);
lfd.event_type = JLE_READ_ACCEPT;
lfd.data.read_accept.handler = fun;
janet_loop_schedule(lfd, POLLIN);
return janet_wrap_abstract(lfd.stream);
}
static Janet cfun_stream_read(int32_t argc, Janet *argv) {
janet_arity(argc, 2, 3);
JanetStream *stream = janet_getabstract(argv, 0, &StreamAT);
int32_t n = janet_getnat(argv, 1);
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, 10);
janet_sched_read(stream, buffer, n, JANET_SCHED_FSOME);
}
static Janet cfun_stream_chunk(int32_t argc, Janet *argv) {
janet_arity(argc, 2, 3);
JanetStream *stream = janet_getabstract(argv, 0, &StreamAT);
int32_t n = janet_getnat(argv, 1);
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, 10);
janet_sched_read(stream, buffer, n, 0);
}
static Janet cfun_stream_close(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
JanetStream *stream = janet_getabstract(argv, 0, &StreamAT);
janet_stream_close(stream, 0);
return janet_wrap_nil();
}
static Janet cfun_stream_write(int32_t argc, Janet *argv) {
janet_fixarity(argc, 2);
JanetStream *stream = janet_getabstract(argv, 0, &StreamAT);
if (janet_checktype(argv[1], JANET_BUFFER)) {
janet_sched_write_buffer(stream, janet_getbuffer(argv, 1));
} else {
JanetByteView bytes = janet_getbytes(argv, 1);
janet_sched_write_stringlike(stream, bytes.bytes);
}
}
static const JanetMethod stream_methods[] = {
{"chunk", cfun_stream_chunk},
{"close", cfun_stream_close},
{"read", cfun_stream_read},
{"write", cfun_stream_write},
{NULL, NULL}
};
static int janet_stream_getter(void *p, Janet key, Janet *out) {
(void) p;
if (!janet_checktype(key, JANET_KEYWORD)) return 0;
return janet_getmethod(janet_unwrap_keyword(key), stream_methods, out);
}
static const JanetReg net_cfuns[] = {
{
"net/server", cfun_net_server,
JDOC("(net/server host port handler)\n\n"
"Start a TCP server. handler is a function that will be called with a stream "
"on each connection to the server. Returns a new stream that is neither readable nor "
"writeable.")
},
{
"net/read", cfun_stream_read,
JDOC("(net/read stream nbytes &opt buf)\n\n"
"Read up to n bytes from a stream, suspending the current fiber until the bytes are available. "
"If less than n bytes are available (and more than 0), will push those bytes and return early. "
"Returns a buffer with up to n more bytes in it.")
},
{
"net/chunk", cfun_stream_chunk,
JDOC("(net/chunk stream nbytes &opt buf)\n\n"
"Same a net/read, but will wait for all n bytes to arrive rather than return early.")
},
{
"net/write", cfun_stream_write,
JDOC("(net/write stream data)\n\n"
"Write data to a stream, suspending the current fiber until the write "
"completes. Returns stream.")
},
{
"net/close", cfun_stream_close,
JDOC("(net/close stream)\n\n"
"Close a stream so that no further communication can occur.")
},
{
"net/connect", cfun_net_connect,
JDOC("(net/connect host port)\n\n"
"Open a connection to communicate with a server. Returns a duplex stream "
"that can be used to communicate with the server.")
},
{NULL, NULL, NULL}
};
void janet_lib_net(JanetTable *env) {
janet_vm_loop_count = 0;
#ifdef JANET_WINDOWS
WSADATA wsaData;
janet_assert(!WSAStartup(MAKEWORD(2, 2), &wsaData), "could not start winsock");
#endif
janet_core_cfuns(env, NULL, net_cfuns);
}
void janet_net_deinit(void) {
#ifdef JANET_WINDOWS
WSACleanup();
#endif
}

View File

@@ -182,18 +182,9 @@ static Janet os_exit(int32_t argc, Janet *argv) {
return janet_wrap_nil();
}
#ifdef JANET_REDUCED_OS
/* Provide a dud os/getenv so boot.janet and init.janet work, but nothing else */
static Janet os_getenv(int32_t argc, Janet *argv) {
(void) argv;
janet_arity(argc, 1, 2);
return janet_wrap_nil();
}
#else
/* Provide full os functionality */
#ifndef JANET_REDUCED_OS
#ifndef JANET_NO_PROCESSES
/* Get env for os_execute */
static char **os_execute_env(int32_t argc, const Janet *argv) {
char **envp = NULL;
@@ -389,15 +380,26 @@ static Janet os_execute(int32_t argc, Janet *argv) {
char *const *cargv = (char *const *)child_argv;
/* Use posix_spawn to spawn new process */
int use_environ = !janet_flag_at(flags, 0);
if (use_environ) {
janet_lock_environ();
}
pid_t pid;
if (janet_flag_at(flags, 1)) {
status = posix_spawnp(&pid,
child_argv[0], NULL, NULL, cargv,
janet_flag_at(flags, 0) ? envp : environ);
use_environ ? environ : envp);
} else {
status = posix_spawn(&pid,
child_argv[0], NULL, NULL, cargv,
janet_flag_at(flags, 0) ? envp : environ);
use_environ ? environ : envp);
}
if (use_environ) {
janet_unlock_environ();
}
/* Wait for child */
@@ -433,6 +435,8 @@ static Janet os_shell(int32_t argc, Janet *argv) {
: janet_wrap_boolean(stat);
}
#endif /* JANET_NO_PROCESSES */
static Janet os_environ(int32_t argc, Janet *argv) {
(void) argv;
janet_fixarity(argc, 0);
@@ -606,7 +610,7 @@ static Janet os_cryptorand(int32_t argc, Janet *argv) {
In both cases, use this fallback path for now... */
int rc;
int randfd;
RETRY_EINTR(randfd, open("/dev/urandom", O_RDONLY));
RETRY_EINTR(randfd, open("/dev/urandom", O_RDONLY | O_CLOEXEC));
if (randfd < 0)
janet_panic(genericerr);
while (n > 0) {
@@ -753,8 +757,8 @@ static Janet os_mktime(int32_t argc, Janet *argv) {
t = mktime(&t_info);
} else {
/* utc time */
#ifdef __sun
janet_panic("os/mktime UTC not supported on Solaris");
#ifdef JANET_NO_UTC_MKTIME
janet_panic("os/mktime UTC not supported on this platform");
return janet_wrap_nil();
#else
t = timegm(&t_info);
@@ -768,6 +772,12 @@ static Janet os_mktime(int32_t argc, Janet *argv) {
return janet_wrap_number((double)t);
}
#ifdef JANET_NO_SYMLINKS
#define j_symlink link
#else
#define j_symlink symlink
#endif
static Janet os_link(int32_t argc, Janet *argv) {
janet_arity(argc, 2, 3);
#ifdef JANET_WINDOWS
@@ -778,7 +788,7 @@ static Janet os_link(int32_t argc, Janet *argv) {
#else
const char *oldpath = janet_getcstring(argv, 0);
const char *newpath = janet_getcstring(argv, 1);
int res = ((argc == 3 && janet_truthy(argv[2])) ? symlink : link)(oldpath, newpath);
int res = ((argc == 3 && janet_truthy(argv[2])) ? j_symlink : link)(oldpath, newpath);
if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath);
return janet_wrap_nil();
#endif
@@ -794,12 +804,14 @@ static Janet os_symlink(int32_t argc, Janet *argv) {
#else
const char *oldpath = janet_getcstring(argv, 0);
const char *newpath = janet_getcstring(argv, 1);
int res = symlink(oldpath, newpath);
int res = j_symlink(oldpath, newpath);
if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath);
return janet_wrap_nil();
#endif
}
#undef j_symlink
static Janet os_mkdir(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
const char *path = janet_getcstring(argv, 0);
@@ -865,6 +877,7 @@ static Janet os_remove(int32_t argc, Janet *argv) {
return janet_wrap_nil();
}
#ifndef JANET_NO_SYMLINKS
static Janet os_readlink(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
#ifdef JANET_WINDOWS
@@ -881,6 +894,7 @@ static Janet os_readlink(int32_t argc, Janet *argv) {
return janet_stringv((const uint8_t *)buffer, len);
#endif
}
#endif
#ifdef JANET_WINDOWS
@@ -1149,6 +1163,7 @@ static Janet os_chmod(int32_t argc, Janet *argv) {
return janet_wrap_nil();
}
#ifndef JANET_NO_UMASK
static Janet os_umask(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
int mask = (int) os_getmode(argv, 0);
@@ -1159,6 +1174,7 @@ static Janet os_umask(int32_t argc, Janet *argv) {
#endif
return janet_wrap_integer(janet_perm_to_unix(res));
}
#endif
static Janet os_dir(int32_t argc, Janet *argv) {
janet_arity(argc, 1, 2);
@@ -1208,9 +1224,9 @@ static Janet os_rename(int32_t argc, Janet *argv) {
static Janet os_realpath(int32_t argc, Janet *argv) {
janet_fixarity(argc, 1);
#ifdef JANET_WINDOWS
#ifdef JANET_NO_REALPATH
(void) argv;
janet_panic("os/realpath not supported on Windows");
janet_panic("os/realpath not supported on this platform");
#else
const char *src = janet_getcstring(argv, 0);
char *dest = realpath(src, NULL);
@@ -1251,12 +1267,8 @@ static const JanetReg os_cfuns[] = {
"\t:freebsd\n"
"\t:openbsd\n"
"\t:netbsd\n"
"\t:posix - A POSIX compatible system (default)")
},
{
"os/getenv", os_getenv,
JDOC("(os/getenv variable &opt dflt)\n\n"
"Get the string value of an environment variable.")
"\t:posix - A POSIX compatible system (default)\n\n"
"May also return a custom keyword specified at build time.")
},
{
"os/arch", os_arch,
@@ -1276,11 +1288,16 @@ static const JanetReg os_cfuns[] = {
JDOC("(os/environ)\n\n"
"Get a copy of the os environment table.")
},
{
"os/getenv", os_getenv,
JDOC("(os/getenv variable &opt dflt)\n\n"
"Get the string value of an environment variable.")
},
{
"os/dir", os_dir,
JDOC("(os/dir dir &opt array)\n\n"
"Iterate over files and subdirectories in a directory. Returns an array of paths parts, "
"with only the filename or directory name and no prefix.")
"with only the file name or directory name and no prefix.")
},
{
"os/stat", os_stat,
@@ -1299,7 +1316,7 @@ static const JanetReg os_cfuns[] = {
"\t:blocks - number of blocks in file. 0 on windows\n"
"\t:blocksize - size of blocks in file. 0 on windows\n"
"\t:accessed - timestamp when file last accessed\n"
"\t:changed - timestamp when file last chnaged (permissions changed)\n"
"\t:changed - timestamp when file last changed (permissions changed)\n"
"\t:modified - timestamp when file last modified (content changed)\n")
},
{
@@ -1326,11 +1343,13 @@ static const JanetReg os_cfuns[] = {
JDOC("(os/cd path)\n\n"
"Change current directory to path. Returns nil on success, errors on failure.")
},
#ifndef JANET_NO_UMASK
{
"os/umask", os_umask,
JDOC("(os/umask mask)\n\n"
"Set a new umask, returns the old umask.")
},
#endif
{
"os/mkdir", os_mkdir,
JDOC("(os/mkdir path)\n\n"
@@ -1356,6 +1375,7 @@ static const JanetReg os_cfuns[] = {
"Iff symlink is falsey or not provided, "
"creates a hard link. Does not work on Windows.")
},
#ifndef JANET_NO_SYMLINKS
{
"os/symlink", os_symlink,
JDOC("(os/symlink oldpath newpath)\n\n"
@@ -1366,6 +1386,8 @@ static const JanetReg os_cfuns[] = {
JDOC("(os/readlink path)\n\n"
"Read the contents of a symbolic link. Does not work on Windows.\n")
},
#endif
#ifndef JANET_NO_PROCESSES
{
"os/execute", os_execute,
JDOC("(os/execute args &opts flags env)\n\n"
@@ -1383,6 +1405,7 @@ static const JanetReg os_cfuns[] = {
JDOC("(os/shell str)\n\n"
"Pass a command string str directly to the system shell.")
},
#endif
{
"os/setenv", os_setenv,
JDOC("(os/setenv variable value)\n\n"

View File

@@ -790,7 +790,7 @@ static int parsermark(void *p, size_t size) {
janet_mark(parser->args[i]);
}
if (parser->flag & JANET_PARSER_GENERATED_ERROR) {
janet_mark(janet_wrap_string(parser->error));
janet_mark(janet_wrap_string((const uint8_t *) parser->error));
}
return 0;
}
@@ -928,7 +928,7 @@ static Janet cfun_parse_error(int32_t argc, Janet *argv) {
const char *err = janet_parser_error(p);
if (err) {
return (p->flag & JANET_PARSER_GENERATED_ERROR)
? janet_wrap_string(err)
? janet_wrap_string((const uint8_t *) err)
: janet_cstringv(err);
}
return janet_wrap_nil();
@@ -1139,7 +1139,7 @@ static const JanetReg parse_cfuns[] = {
"parser/new", cfun_parse_parser,
JDOC("(parser/new)\n\n"
"Creates and returns a new parser object. Parsers are state machines "
"that can receive bytes, and generate a stream of janet values.")
"that can receive bytes, and generate a stream of values.")
},
{
"parser/clone", cfun_parse_clone,

View File

@@ -413,6 +413,38 @@ tail:
return NULL;
}
case RULE_LENPREFIX: {
int oldmode = s->mode;
s->mode = PEG_MODE_NORMAL;
const uint8_t *next_text;
CapState cs = cap_save(s);
down1(s);
next_text = peg_rule(s, s->bytecode + rule[1], text);
up1(s);
if (NULL == next_text) return NULL;
s->mode = oldmode;
int32_t num_sub_captures = s->captures->count - cs.cap;
Janet lencap;
if (num_sub_captures <= 0 ||
(lencap = s->captures->data[cs.cap], !janet_checkint(lencap))) {
cap_load(s, cs);
return NULL;
}
int32_t nrep = janet_unwrap_integer(lencap);
/* drop captures from len pattern */
cap_load(s, cs);
for (int32_t i = 0; i < nrep; i++) {
down1(s);
next_text = peg_rule(s, s->bytecode + rule[2], next_text);
up1(s);
if (NULL == next_text) {
cap_load(s, cs);
return NULL;
}
}
return next_text;
}
}
}
@@ -657,6 +689,9 @@ static void spec_if(Builder *b, int32_t argc, const Janet *argv) {
static void spec_ifnot(Builder *b, int32_t argc, const Janet *argv) {
spec_branch(b, argc, argv, RULE_IFNOT);
}
static void spec_lenprefix(Builder *b, int32_t argc, const Janet *argv) {
spec_branch(b, argc, argv, RULE_LENPREFIX);
}
static void spec_between(Builder *b, int32_t argc, const Janet *argv) {
peg_fixarity(b, argc, 3);
@@ -847,6 +882,7 @@ static const SpecialPair peg_specials[] = {
{"group", spec_group},
{"if", spec_if},
{"if-not", spec_ifnot},
{"lenprefix", spec_lenprefix},
{"look", spec_look},
{"not", spec_not},
{"opt", spec_opt},
@@ -1100,6 +1136,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
break;
case RULE_IF:
case RULE_IFNOT:
case RULE_LENPREFIX:
/* [rule_a, rule_b (b if not a)] */
if (rule[1] >= blen) goto bad;
if (rule[2] >= blen) goto bad;
@@ -1283,8 +1320,7 @@ static const JanetReg peg_cfuns[] = {
"peg/match", cfun_peg_match,
JDOC("(peg/match peg text &opt start & args)\n\n"
"Match a Parsing Expression Grammar to a byte string and return an array of captured values. "
"Returns nil if text does not match the language defined by peg. The syntax of PEGs are very "
"similar to those defined by LPeg, and have similar capabilities.")
"Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.")
},
{NULL, NULL, NULL}
};

View File

@@ -155,6 +155,9 @@ static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, in
case '\\':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\\", 2);
break;
case '\t':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\t", 2);
break;
default:
if (c < 32 || c > 126) {
uint8_t buf[4];
@@ -527,7 +530,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
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);
if (len > JANET_PRETTY_ARRAY_LIMIT) {
if (len > JANET_PRETTY_ARRAY_LIMIT && !(S->flags & JANET_PRETTY_NOTRUNC)) {
for (i = 0; i < 3; i++) {
if (i) print_newline(S, 0);
janet_pretty_one(S, arr[i], 0);
@@ -591,7 +594,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
if (is_dict_value && len >= JANET_PRETTY_DICT_ONELINE) print_newline(S, 0);
for (i = 0; i < cap; i++) {
if (!janet_checktype(kvs[i].key, JANET_NIL)) {
if (counter == JANET_PRETTY_DICT_LIMIT) {
if (counter == JANET_PRETTY_DICT_LIMIT && !(S->flags & JANET_PRETTY_NOTRUNC)) {
print_newline(S, 0);
janet_buffer_push_cstring(S->buffer, "...");
break;
@@ -751,7 +754,6 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X': {
int32_t n = va_arg(args, long);
@@ -802,6 +804,10 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
pushtypes(b, types);
break;
}
case 'M':
case 'm':
case 'N':
case 'n':
case 'Q':
case 'q':
case 'P':
@@ -809,11 +815,13 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
int depth = atoi(precision);
if (depth < 1) depth = 4;
char d = c[-1];
int has_color = (d == 'P') || (d == 'Q');
int has_oneline = (d == 'Q') || (d == 'q');
int has_color = (d == 'P') || (d == 'Q') || (d == 'M') || (d == 'N');
int has_oneline = (d == 'Q') || (d == 'q') || (d == 'N') || (d == 'n');
int has_notrunc = (d == 'M') || (d == 'm') || (d == 'N') || (d == 'n');
int flags = 0;
flags |= has_color ? JANET_PRETTY_COLOR : 0;
flags |= has_oneline ? JANET_PRETTY_ONELINE : 0;
flags |= has_notrunc ? JANET_PRETTY_NOTRUNC : 0;
janet_pretty_(b, depth, flags, va_arg(args, Janet), startlen);
break;
}
@@ -904,7 +912,6 @@ void janet_buffer_format(
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X': {
int32_t n = janet_getinteger(argv, arg);
@@ -946,19 +953,24 @@ void janet_buffer_format(
janet_description_b(b, argv[arg]);
break;
}
case 'M':
case 'm':
case 'N':
case 'n':
case 'Q':
case 'q':
case 'P':
case 'p': { /* janet pretty , precision = depth */
int depth = atoi(precision);
if (depth < 1)
depth = 4;
char c = strfrmt[-1];
int has_color = (c == 'P') || (c == 'Q');
int has_oneline = (c == 'Q') || (c == 'q');
if (depth < 1) depth = 4;
char d = strfrmt[-1];
int has_color = (d == 'P') || (d == 'Q') || (d == 'M') || (d == 'N');
int has_oneline = (d == 'Q') || (d == 'q') || (d == 'N') || (d == 'n');
int has_notrunc = (d == 'M') || (d == 'm') || (d == 'N') || (d == 'n');
int flags = 0;
flags |= has_color ? JANET_PRETTY_COLOR : 0;
flags |= has_oneline ? JANET_PRETTY_ONELINE : 0;
flags |= has_notrunc ? JANET_PRETTY_NOTRUNC : 0;
janet_pretty_(b, depth, flags, argv[arg], startlen);
break;
}

View File

@@ -145,7 +145,7 @@ void janetc_regalloc_free(JanetcRegisterAllocator *ra, int32_t reg) {
int32_t janetc_regalloc_temp(JanetcRegisterAllocator *ra, JanetcRegisterTemp nth) {
int32_t oldmax = ra->max;
if (ra->regtemps & (1 << nth)) {
janet_exit("regtemp already allocated");
JANET_EXIT("regtemp already allocated");
}
ra->regtemps |= 1 << nth;
int32_t reg = janetc_regalloc_1(ra);

View File

@@ -50,7 +50,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
JanetFiber *fiber = janet_fiber(f, 64, 0, NULL);
fiber->env = env;
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
if (status != JANET_SIGNAL_OK) {
if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
janet_stacktrace(fiber, ret);
errflags |= 0x01;
done = 1;

View File

@@ -43,6 +43,7 @@ extern JANET_THREAD_LOCAL int janet_vm_stackn;
/* The current running fiber on the current thread.
* Set and unset by janet_run. */
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_root_fiber;
/* The current pointer to the inner most jmp_buf. The current
* return point for panics. */

View File

@@ -542,7 +542,7 @@ static const JanetReg string_cfuns[] = {
{
"string/from-bytes", cfun_string_frombytes,
JDOC("(string/from-bytes & byte-vals)\n\n"
"Creates a string from integer params with byte values. All integers "
"Creates a string from integer parameters with byte values. All integers "
"will be coerced to the range of 1 byte 0-255.")
},
{
@@ -573,7 +573,7 @@ static const JanetReg string_cfuns[] = {
},
{
"string/find-all", cfun_string_findall,
JDOC("(string/find patt str)\n\n"
JDOC("(string/find-all patt str)\n\n"
"Searches for all instances of pattern patt in string "
"str. Returns an array of all indices of found patterns. Overlapping "
"instances of the pattern are not counted, meaning a byte in string "
@@ -627,7 +627,7 @@ static const JanetReg string_cfuns[] = {
{
"string/format", cfun_string_format,
JDOC("(string/format format & values)\n\n"
"Similar to snprintf, but specialized for operating with janet. Returns "
"Similar to snprintf, but specialized for operating with Janet values. Returns "
"a new string.")
},
{

View File

@@ -465,11 +465,15 @@ static int thread_worker(JanetMailboxPair *pair) {
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) {
if (sig != JANET_SIGNAL_OK && sig < JANET_SIGNAL_USER0) {
janet_eprintf("in thread %v: ", janet_wrap_abstract(janet_make_thread(pair->newbox, encode)));
janet_stacktrace(fiber, out);
}
#ifdef JANET_NET
janet_loop();
#endif
/* Normal exit */
destroy_mailbox_pair(pair);
janet_deinit();

View File

@@ -32,10 +32,10 @@
#include <errno.h>
/* Handle runtime errors */
#ifndef janet_exit
#ifndef JANET_EXIT
#include <stdio.h>
#define janet_exit(m) do { \
printf("C runtime error at line %d in file %s: %s\n",\
#define JANET_EXIT(m) do { \
fprintf(stderr, "C runtime error at line %d in file %s: %s\n",\
__LINE__,\
__FILE__,\
(m));\
@@ -44,13 +44,13 @@
#endif
#define janet_assert(c, m) do { \
if (!(c)) janet_exit((m)); \
if (!(c)) JANET_EXIT((m)); \
} while (0)
/* What to do when out of memory */
#ifndef JANET_OUT_OF_MEMORY
#include <stdio.h>
#define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0)
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0)
#endif
/* Omit docstrings in some builds */
@@ -126,5 +126,10 @@ void janet_lib_inttypes(JanetTable *env);
#ifdef JANET_THREADS
void janet_lib_thread(JanetTable *env);
#endif
#ifdef JANET_NET
void janet_lib_net(JanetTable *env);
void janet_net_deinit(void);
void janet_net_markloop(void);
#endif
#endif

View File

@@ -38,6 +38,7 @@ JANET_THREAD_LOCAL JanetTable *janet_vm_registry;
JANET_THREAD_LOCAL JanetTable *janet_vm_abstract_registry;
JANET_THREAD_LOCAL int janet_vm_stackn = 0;
JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber = NULL;
JANET_THREAD_LOCAL JanetFiber *janet_vm_root_fiber = NULL;
JANET_THREAD_LOCAL Janet *janet_vm_return_reg = NULL;
JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf = NULL;
@@ -1244,7 +1245,9 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
janet_vm_stackn = oldn;
janet_gcunlock(handle);
if (signal != JANET_SIGNAL_OK) janet_panicv(*janet_vm_return_reg);
if (signal != JANET_SIGNAL_OK) {
janet_panicv(*janet_vm_return_reg);
}
return *janet_vm_return_reg;
}
@@ -1276,10 +1279,12 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
/* Continue child fiber if it exists */
if (fiber->child) {
if (janet_vm_root_fiber == NULL) janet_vm_root_fiber = fiber;
JanetFiber *child = fiber->child;
janet_vm_stackn++;
JanetSignal sig = janet_continue(child, in, &in);
janet_vm_stackn--;
if (janet_vm_root_fiber == fiber) janet_vm_root_fiber = NULL;
if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
*out = in;
return sig;
@@ -1308,6 +1313,7 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
Janet *old_vm_return_reg = janet_vm_return_reg;
/* Setup fiber */
if (janet_vm_root_fiber == NULL) janet_vm_root_fiber = fiber;
janet_vm_fiber = fiber;
janet_gcroot(janet_wrap_fiber(fiber));
janet_fiber_set_status(fiber, JANET_STATUS_ALIVE);
@@ -1333,6 +1339,7 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
janet_gcunroot(janet_wrap_fiber(fiber));
/* Restore global state */
if (janet_vm_root_fiber == fiber) janet_vm_root_fiber = NULL;
janet_vm_gc_suspend = handle;
janet_vm_fiber = old_vm_fiber;
janet_vm_stackn = oldn;
@@ -1414,6 +1421,10 @@ int janet_init(void) {
janet_vm_core_env = NULL;
/* Seed RNG */
janet_rng_seed(janet_default_rng(), 0);
/* Fibers */
janet_vm_fiber = NULL;
janet_vm_root_fiber = NULL;
janet_vm_stackn = 0;
/* Threads */
#ifdef JANET_THREADS
janet_threads_init();
@@ -1433,7 +1444,12 @@ void janet_deinit(void) {
janet_vm_abstract_registry = NULL;
janet_vm_core_env = NULL;
free(janet_vm_traversal_base);
janet_vm_fiber = NULL;
janet_vm_root_fiber = NULL;
#ifdef JANET_THREADS
janet_threads_deinit();
#endif
#ifdef JANET_NET
janet_net_deinit();
#endif
}

View File

@@ -27,6 +27,12 @@
extern "C" {
#endif
/* Variable length arrays are ok */
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4200 )
#endif
/***** START SECTION CONFIG *****/
#include "janetconf.h"
@@ -124,6 +130,17 @@ extern "C" {
/* Check emscripten */
#ifdef __EMSCRIPTEN__
#define JANET_NO_DYNAMIC_MODULES
#define JANET_NO_PROCESSES
#endif
/* Check sun */
#ifdef __sun
#define JANET_NO_UTC_MKTIME
#endif
/* Add some windows flags */
#ifdef JANET_WINDOWS
#define JANET_NO_REALPATH
#endif
/* Define how global janet state is declared */
@@ -159,6 +176,11 @@ extern "C" {
#define JANET_TYPED_ARRAY
#endif
/* Enable or disable networking */
#if !defined(JANET_NO_NET) && !defined(__EMSCRIPTEN__)
#define JANET_NET
#endif
/* Enable or disable large int types (for now 64 bit, maybe 128 / 256 bit integer types) */
#ifndef JANET_NO_INT_TYPES
#define JANET_INT_TYPES
@@ -289,6 +311,8 @@ typedef enum {
JANET_SIGNAL_USER9
} JanetSignal;
#define JANET_SIGNAL_EVENT JANET_SIGNAL_USER9
/* Fiber statuses - mostly corresponds to signals. */
typedef enum {
JANET_STATUS_DEAD,
@@ -309,14 +333,6 @@ typedef enum {
JANET_STATUS_ALIVE
} JanetFiberStatus;
#ifdef JANET_NANBOX_64
typedef union Janet Janet;
#elif defined(JANET_NANBOX_32)
typedef union Janet Janet;
#else
typedef struct Janet Janet;
#endif
/* Use type punning for GC objects */
typedef struct JanetGCObject JanetGCObject;
@@ -347,15 +363,6 @@ 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 {
@@ -377,6 +384,61 @@ typedef enum JanetType {
JANET_POINTER
} JanetType;
/* Recursive type (Janet) */
#ifdef JANET_NANBOX_64
typedef union Janet Janet;
union Janet {
uint64_t u64;
int64_t i64;
double number;
void *pointer;
};
#elif defined(JANET_NANBOX_32)
typedef union Janet Janet;
union Janet {
struct {
#ifdef JANET_BIG_ENDIAN
uint32_t type;
union {
int32_t integer;
void *pointer;
} payload;
#else
union {
int32_t integer;
void *pointer;
} payload;
uint32_t type;
#endif
} tagged;
double number;
uint64_t u64;
};
#else
typedef struct Janet Janet;
struct Janet {
union {
uint64_t u64;
double number;
int32_t integer;
void *pointer;
const void *cpointer;
} as;
JanetType type;
};
#endif
/* C functions */
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;
#define JANET_COUNT_TYPES (JANET_POINTER + 1)
/* Type flags */
@@ -485,13 +547,6 @@ JANET_API Janet janet_wrap_integer(int32_t x);
#include <math.h>
/* 64 Nanboxed Janet value */
union Janet {
uint64_t u64;
int64_t i64;
double number;
void *pointer;
};
#define janet_u64(x) ((x).u64)
#define JANET_NANBOX_TAGBITS 0xFFFF800000000000llu
@@ -576,27 +631,6 @@ JANET_API Janet janet_nanbox_from_bits(uint64_t bits);
#elif defined(JANET_NANBOX_32)
/* 32 bit nanboxed janet */
union Janet {
struct {
#ifdef JANET_BIG_ENDIAN
uint32_t type;
union {
int32_t integer;
void *pointer;
} payload;
#else
union {
int32_t integer;
void *pointer;
} payload;
uint32_t type;
#endif
} tagged;
double number;
uint64_t u64;
};
#define JANET_DOUBLE_OFFSET 0xFFFF
#define janet_u64(x) ((x).u64)
@@ -647,18 +681,6 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
#else
/* A general janet value type for more standard C */
struct Janet {
union {
uint64_t u64;
double number;
int32_t integer;
void *pointer;
const void *cpointer;
} as;
JanetType type;
};
#define janet_u64(x) ((x).as.u64)
#define janet_type(x) ((x).type)
#define janet_checktype(x, t) ((x).type == (t))
@@ -1111,6 +1133,11 @@ extern enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT];
/***** START SECTION MAIN *****/
/* Event Loop */
#ifdef JANET_NET
JANET_API void janet_loop(void);
#endif
/* Parsing */
extern JANET_API const JanetAbstractType janet_parser_type;
JANET_API void janet_parser_init(JanetParser *parser);
@@ -1286,6 +1313,7 @@ JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32
JANET_API JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t argc, const Janet *argv);
JANET_API JanetFiberStatus janet_fiber_status(JanetFiber *fiber);
JANET_API JanetFiber *janet_current_fiber(void);
JANET_API JanetFiber *janet_root_fiber(void);
/* Treat similar types through uniform interfaces for iteration */
JANET_API int janet_indexed_view(Janet seq, const Janet **data, int32_t *len);
@@ -1345,6 +1373,7 @@ JANET_API int janet_verify(JanetFuncDef *def);
/* Pretty printing */
#define JANET_PRETTY_COLOR 1
#define JANET_PRETTY_ONELINE 2
#define JANET_PRETTY_NOTRUNC 4
JANET_API JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, Janet x);
/* Misc */
@@ -1558,6 +1587,7 @@ typedef enum {
RULE_ERROR, /* [rule] */
RULE_DROP, /* [rule] */
RULE_BACKMATCH, /* [tag] */
RULE_LENPREFIX, /* [rule_a, rule_b (repeat rule_b rule_a times)] */
} JanetPegOpcode;
typedef struct {
@@ -1654,6 +1684,11 @@ JANET_API int janet_thread_send(JanetThread *thread, Janet msg, double timeout);
/***** END SECTION MAIN *****/
/* Re-enable popped variable length array warnings */
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -1011,10 +1011,15 @@ int main(int argc, char **argv) {
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) {
if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
janet_stacktrace(fiber, out);
}
#ifdef JANET_NET
status = JANET_SIGNAL_OK;
janet_loop();
#endif
/* Deinitialize vm */
janet_deinit();
janet_line_deinit();

View File

@@ -1,4 +1,5 @@
/build
/modpath
.cache
.manifests
json.*

View File

@@ -252,4 +252,33 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02
(assert (< [1 2 3] [1 2 3 -1]) "tuple comparison 5")
(assert (> [1 2 3] [1 2]) "tuple comparison 6")
# Lenprefix rule
(def peg (peg/compile ~(* (lenprefix (/ (* '(any (if-not ":" 1)) ":") ,scan-number) 1) -1)))
(assert (peg/match peg "5:abcde") "lenprefix 1")
(assert (not (peg/match peg "5:abcdef")) "lenprefix 2")
(assert (not (peg/match peg "5:abcd")) "lenprefix 3")
# Packet capture
(def peg2
(peg/compile
~{# capture packet length in tag :header-len
:packet-header (* (/ ':d+ ,scan-number :header-len) ":")
# capture n bytes from a backref :header-len
:packet-body '(lenprefix (-> :header-len) 1)
# header, followed by body, and drop the :header-len capture
:packet (/ (* :packet-header :packet-body) ,|$1)
# any exact seqence of packets (no extra characters)
:main (* (any :packet) -1)}))
(assert (deep= @["a" "bb" "ccc"] (peg/match peg2 "1:a2:bb3:ccc")) "lenprefix 4")
(assert (deep= @["a" "bb" "cccccc"] (peg/match peg2 "1:a2:bb6:cccccc")) "lenprefix 5")
(assert (= nil (peg/match peg2 "1:a2:bb:5:cccccc")) "lenprefix 6")
(assert (= nil (peg/match peg2 "1:a2:bb:7:cccccc")) "lenprefix 7")
(end-suite)

51
test/suite9.janet Normal file
View File

@@ -0,0 +1,51 @@
# Copyright (c) 2020 Calvin Rose & contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
(import ./helper :prefix "" :exit true)
(start-suite 9)
# Net testing
(defn handler
"Simple handler for connections."
[stream]
(defer (:close stream)
(def id (gensym))
(def b @"")
(:read stream 1024 b)
(:write stream b)
(buffer/clear b)))
(def s (net/server "127.0.0.1" "8000" handler))
(assert s "made server 1")
(defn test-echo [msg]
(with [conn (net/connect "127.0.0.1" "8000")]
(:write conn msg)
(def res (:read conn 1024))
(assert (= (string res) msg) (string "echo " msg))))
(test-echo "hello")
(test-echo "world")
(test-echo (string/repeat "abcd" 200))
(:close s)
(end-suite)

View File

@@ -230,53 +230,53 @@
<key>name</key>
<string>punctuation.other.janet</string>
</dict>
<!-- string>(?&lt;![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*]) token match here (?![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])</string -->
<!-- string>(?&lt;![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*]) token match here (?![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])</string -->
<key>literal</key>
<dict>
<key>match</key>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])(true|false|nil)(?![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])</string>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])(true|false|nil)(?![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])</string>
<key>name</key>
<string>constant.language.janet</string>
</dict>
<key>corelib</key>
<dict>
<key>match</key>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])(%ALLSYMBOLS%)(?![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])</string>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])(%ALLSYMBOLS%)(?![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])</string>
<key>name</key>
<string>keyword.control.janet</string>
</dict>
<key>keysym</key>
<dict>
<key>match</key>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*]):[\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*]*</string>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*]):[\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*]*</string>
<key>name</key>
<string>constant.keyword.janet</string>
</dict>
<key>symbol</key>
<dict>
<key>match</key>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])[\.a-zA-Z_\-=!@\$%^&amp;?|\\/&lt;&gt;*][\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*]*</string>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])[\.a-zA-Z_\-=!@\$%^&amp;?/&lt;&gt;*][\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*]*</string>
<key>name</key>
<string>variable.other.janet</string>
</dict>
<key>hex-number</key>
<dict>
<key>match</key>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])[-+]?0x([_\da-fA-F]+|[_\da-fA-F]+\.[_\da-fA-F]*|\.[_\da-fA-F]+)(&amp;[+-]?[\da-fA-F]+)?(?![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])</string>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])[-+]?0x([_\da-fA-F]+|[_\da-fA-F]+\.[_\da-fA-F]*|\.[_\da-fA-F]+)(&amp;[+-]?[\da-fA-F]+)?(?![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])</string>
<key>name</key>
<string>constant.numeric.hex.janet</string>
</dict>
<key>dec-number</key>
<dict>
<key>match</key>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])[-+]?([_\d]+|[_\d]+\.[_\d]*|\.[_\d]+)([eE&amp;][+-]?[\d]+)?(?![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])</string>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])[-+]?([_\d]+|[_\d]+\.[_\d]*|\.[_\d]+)([eE&amp;][+-]?[\d]+)?(?![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])</string>
<key>name</key>
<string>constant.numeric.decimal.janet</string>
</dict>
<key>r-number</key>
<dict>
<key>match</key>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])[-+]?\d\d?r([_\w]+|[_\w]+\.[_\w]*|\.[_\w]+)(&amp;[+-]?[\w]+)?(?![\.:\w_\-=!@\$%^&amp;?|\\/&lt;&gt;*])</string>
<string>(?&lt;![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])[-+]?\d\d?r([_\w]+|[_\w]+\.[_\w]*|\.[_\w]+)(&amp;[+-]?[\w]+)?(?![\.:\w_\-=!@\$%^&amp;?/&lt;&gt;*])</string>
<key>name</key>
<string>constant.numeric.decimal.janet</string>
</dict>