mirror of
				https://github.com/janet-lang/janet
				synced 2025-11-04 09:33:02 +00:00 
			
		
		
		
	Compare commits
	
		
			104 Commits
		
	
	
		
			ev-epoll-f
			...
			v1.34.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f92f3eb6fa | ||
| 
						 | 
					89e74dca3e | ||
| 
						 | 
					f2e86d2f8d | ||
| 
						 | 
					623da131e5 | ||
| 
						 | 
					e89ec31ae5 | ||
| 
						 | 
					68a6ed208e | ||
| 
						 | 
					c01b32c4f3 | ||
| 
						 | 
					ee11ff9da9 | ||
| 
						 | 
					ed56d5d6ff | ||
| 
						 | 
					b317ab755c | ||
| 
						 | 
					9819994999 | ||
| 
						 | 
					e9dbaa81d2 | ||
| 
						 | 
					9f9146ffae | ||
| 
						 | 
					9d9732af97 | ||
| 
						 | 
					ebb8fa9787 | ||
| 
						 | 
					9e6abbf4d4 | ||
| 
						 | 
					6032a6d658 | ||
| 
						 | 
					c29ab22e6d | ||
| 
						 | 
					592ac4904c | ||
| 
						 | 
					03ae2ec153 | ||
| 
						 | 
					3bc42d0d37 | ||
| 
						 | 
					12630d3e54 | ||
| 
						 | 
					c9897f99c3 | ||
| 
						 | 
					e66dc14b3a | ||
| 
						 | 
					7a2868c147 | ||
| 
						 | 
					9e0daaee09 | ||
| 
						 | 
					c293c7de93 | ||
| 
						 | 
					49eb5f8563 | ||
| 
						 | 
					674b375b2c | ||
| 
						 | 
					7e94c091eb | ||
| 
						 | 
					5885ccba61 | ||
| 
						 | 
					431ecd3d1a | ||
| 
						 | 
					f6df8ff935 | ||
| 
						 | 
					3fd70f0951 | ||
| 
						 | 
					bebb635d4f | ||
| 
						 | 
					354896bc4b | ||
| 
						 | 
					5ddefff27e | ||
| 
						 | 
					91827eef4f | ||
| 
						 | 
					9c14c09962 | ||
| 
						 | 
					e85a84171f | ||
| 
						 | 
					3a4f86c3d7 | ||
| 
						 | 
					5e75963312 | ||
| 
						 | 
					184d9289b5 | ||
| 
						 | 
					b7ff9577c0 | ||
| 
						 | 
					942a1aaac6 | ||
| 
						 | 
					69f0fe004d | ||
| 
						 | 
					2a04347a42 | ||
| 
						 | 
					1394f1a5c0 | ||
| 
						 | 
					cf4d19a8ea | ||
| 
						 | 
					23b0fe9f8e | ||
| 
						 | 
					1ba718b15e | ||
| 
						 | 
					df5f79ff35 | ||
| 
						 | 
					6d7e8528ea | ||
| 
						 | 
					197bb73a62 | ||
| 
						 | 
					f91e599451 | ||
| 
						 | 
					5b9aa9237c | ||
| 
						 | 
					61f38fab37 | ||
| 
						 | 
					9142f38cbc | ||
| 
						 | 
					e8ed961572 | ||
| 
						 | 
					be11a2a1ad | ||
| 
						 | 
					ea75086300 | ||
| 
						 | 
					9eeefbd79a | ||
| 
						 | 
					c573a98363 | ||
| 
						 | 
					11d7af3f95 | ||
| 
						 | 
					a10b4f61d8 | ||
| 
						 | 
					a0cb7514f1 | ||
| 
						 | 
					b066edc116 | ||
| 
						 | 
					938f5a689e | ||
| 
						 | 
					772f4c26e8 | ||
| 
						 | 
					6b5d151beb | ||
| 
						 | 
					a9176a77e6 | ||
| 
						 | 
					16f409c6a9 | ||
| 
						 | 
					9593c930de | ||
| 
						 | 
					56f33f514b | ||
| 
						 | 
					1ccd544b94 | ||
| 
						 | 
					93c83a2ee2 | ||
| 
						 | 
					f459e32ada | ||
| 
						 | 
					9b640c8e9c | ||
| 
						 | 
					a3228f4997 | ||
| 
						 | 
					715eb69d92 | ||
| 
						 | 
					df2d5cb3d3 | ||
| 
						 | 
					3b189eab64 | ||
| 
						 | 
					609b629c22 | ||
| 
						 | 
					e74365fe38 | ||
| 
						 | 
					46b34833c2 | ||
| 
						 | 
					045c80869d | ||
| 
						 | 
					2ea2e72ddd | ||
| 
						 | 
					1b17e12fd6 | ||
| 
						 | 
					cc5beda0d2 | ||
| 
						 | 
					a363fd926d | ||
| 
						 | 
					21ebede529 | ||
| 
						 | 
					15d67e9191 | ||
| 
						 | 
					b5996f5f02 | ||
| 
						 | 
					83204dc293 | ||
| 
						 | 
					e3f4142d2a | ||
| 
						 | 
					f18ad36b1b | ||
| 
						 | 
					cb25a2ecd6 | ||
| 
						 | 
					741a5036e8 | ||
| 
						 | 
					549ee95f3d | ||
| 
						 | 
					6ae81058aa | ||
| 
						 | 
					267c603824 | ||
| 
						 | 
					a8f583a372 | ||
| 
						 | 
					2b5d90f73a | ||
| 
						 | 
					4139e426fe | 
@@ -1,4 +1,4 @@
 | 
			
		||||
image: freebsd/12.x
 | 
			
		||||
image: freebsd/14.x
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							@@ -56,7 +56,7 @@ jobs:
 | 
			
		||||
            gcc
 | 
			
		||||
      - name: Build the project
 | 
			
		||||
        shell: cmd
 | 
			
		||||
        run: make -j CC=gcc
 | 
			
		||||
        run: make -j4 CC=gcc JANET_NO_AMALG=1
 | 
			
		||||
 | 
			
		||||
  test-mingw-linux:
 | 
			
		||||
    name: Build and test with Mingw on Linux + Wine
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -34,8 +34,11 @@ local
 | 
			
		||||
 | 
			
		||||
# Common test files I use.
 | 
			
		||||
temp.janet
 | 
			
		||||
temp*.janet
 | 
			
		||||
temp.c
 | 
			
		||||
temp*janet
 | 
			
		||||
temp*.c
 | 
			
		||||
scratch.janet
 | 
			
		||||
scratch.c
 | 
			
		||||
 | 
			
		||||
# Emscripten
 | 
			
		||||
*.bc
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,7 +1,47 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## Unreleased - ???
 | 
			
		||||
## 1.34.0 - 2024-03-22
 | 
			
		||||
- Add a new (split) PEG special by @ianthehenry
 | 
			
		||||
- Add buffer/push-* sized int and float by @pnelson
 | 
			
		||||
- Documentation improvements: @amano-kenji, @llmII, @MaxGyver83, @pepe, @sogaiu.
 | 
			
		||||
- Expose _exit to skip certain cleanup with os/exit.
 | 
			
		||||
- Swap set / body order for each by @sogaiu.
 | 
			
		||||
- Abort on assert failure instead of exit.
 | 
			
		||||
- Fix: os/proc-wait by @llmII.
 | 
			
		||||
- Fix macex1 to keep syntax location for all tuples.
 | 
			
		||||
- Restore if-let tail calls.
 | 
			
		||||
- Don't try and resume fibers that can't be resumed.
 | 
			
		||||
- Register stream on unmarshal.
 | 
			
		||||
- Fix asm roundtrip issue.
 | 
			
		||||
 | 
			
		||||
## 1.33.0 - 2024-01-07
 | 
			
		||||
- Add more + and * keywords to default-peg-grammar by @sogaiu.
 | 
			
		||||
- Use libc strlen in janet_buffer_push_cstring by @williewillus.
 | 
			
		||||
- Be a bit safer with reference counting.
 | 
			
		||||
- Add support for atomic loads in Janet's atomic abstraction.
 | 
			
		||||
- Fix poll event loop CPU usage issue.
 | 
			
		||||
- Add ipv6, shared, and cryptorand options to meson.
 | 
			
		||||
- Add more ipv6 feature detection.
 | 
			
		||||
- Fix loop for forever loop.
 | 
			
		||||
- Cleaned up unused NetStateConnect, fixed janet_async_end() ev refcount by @zevv.
 | 
			
		||||
- Fix warnings w/ MSVC and format.
 | 
			
		||||
- Fix marshal_one_env w/ JANET_MARSHAL_UNSAFE.
 | 
			
		||||
- Fix `(default)`.
 | 
			
		||||
- Fix cannot marshal fiber with c stackframe, in a dynamic way that is fairly conservative.
 | 
			
		||||
- Fix typo for SIGALARM in os/proc-kill.
 | 
			
		||||
- Prevent bytecode optimization from remove mk* instructions.
 | 
			
		||||
- Fix arity typo in peg.c by @pepe.
 | 
			
		||||
- Update Makefile for MinGW.
 | 
			
		||||
- Fix canceling waiting fiber.
 | 
			
		||||
- Add a new (sub) PEG special by @ianthehenry.
 | 
			
		||||
- Fix if net/server's handler has incorrect arity.
 | 
			
		||||
- Fix macex raising on ().
 | 
			
		||||
 | 
			
		||||
## 1.32.1 - 2023-10-15
 | 
			
		||||
- Fix return value from C function `janet_dobytes` when called on Janet functions that yield to event loop.
 | 
			
		||||
- Change C API for event loop interaction - get rid of JanetListener and instead use `janet_async_start` and `janet_async_end`.
 | 
			
		||||
- Rework event loop to make fewer system calls on kqueue and epoll.
 | 
			
		||||
- Expose atomic refcount abstraction in janet.h
 | 
			
		||||
- Add `array/weak` for weak references in arrays
 | 
			
		||||
- Add support for weak tables via `table/weak`, `table/weak-keys`, and `table/weak-values`.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								Makefile
									
									
									
									
									
								
							@@ -33,6 +33,7 @@ CLIBS=-lm -lpthread
 | 
			
		||||
JANET_TARGET=build/janet
 | 
			
		||||
JANET_BOOT=build/janet_boot
 | 
			
		||||
JANET_IMPORT_LIB=build/janet.lib
 | 
			
		||||
JANET_LIBRARY_IMPORT_LIB=build/libjanet.lib
 | 
			
		||||
JANET_LIBRARY=build/libjanet.so
 | 
			
		||||
JANET_STATIC_LIBRARY=build/libjanet.a
 | 
			
		||||
JANET_PATH?=$(LIBDIR)/janet
 | 
			
		||||
@@ -42,6 +43,7 @@ JANET_DIST_DIR?=janet-dist
 | 
			
		||||
JANET_BOOT_FLAGS:=. JANET_PATH '$(JANET_PATH)'
 | 
			
		||||
JANET_TARGET_OBJECTS=build/janet.o build/shell.o
 | 
			
		||||
JPM_TAG?=master
 | 
			
		||||
HAS_SHARED?=1
 | 
			
		||||
DEBUGGER=gdb
 | 
			
		||||
SONAME_SETTER=-Wl,-soname,
 | 
			
		||||
 | 
			
		||||
@@ -51,6 +53,7 @@ HOSTAR?=$(AR)
 | 
			
		||||
# Symbols are (optionally) removed later, keep -g as default!
 | 
			
		||||
CFLAGS?=-O2 -g
 | 
			
		||||
LDFLAGS?=-rdynamic
 | 
			
		||||
LIBJANET_LDFLAGS?=$(LD_FLAGS)
 | 
			
		||||
RUN:=$(RUN)
 | 
			
		||||
 | 
			
		||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
 | 
			
		||||
@@ -93,12 +96,17 @@ endif
 | 
			
		||||
ifeq ($(findstring MINGW,$(UNAME)), MINGW)
 | 
			
		||||
	CLIBS:=-lws2_32 -lpsapi -lwsock32
 | 
			
		||||
	LDFLAGS:=-Wl,--out-implib,$(JANET_IMPORT_LIB)
 | 
			
		||||
	LIBJANET_LDFLAGS:=-Wl,--out-implib,$(JANET_LIBRARY_IMPORT_LIB)
 | 
			
		||||
	JANET_TARGET:=$(JANET_TARGET).exe
 | 
			
		||||
	JANET_BOOT:=$(JANET_BOOT).exe
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$(shell mkdir -p build/core build/c build/boot build/mainclient)
 | 
			
		||||
all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h
 | 
			
		||||
all: $(JANET_TARGET) $(JANET_STATIC_LIBRARY) build/janet.h
 | 
			
		||||
ifeq ($(HAS_SHARED), 1)
 | 
			
		||||
all: $(JANET_LIBRARY)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
######################
 | 
			
		||||
##### Name Files #####
 | 
			
		||||
@@ -196,9 +204,9 @@ build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
 | 
			
		||||
########################
 | 
			
		||||
 | 
			
		||||
ifeq ($(UNAME), Darwin)
 | 
			
		||||
SONAME=libjanet.1.31.dylib
 | 
			
		||||
SONAME=libjanet.1.34.dylib
 | 
			
		||||
else
 | 
			
		||||
SONAME=libjanet.so.1.31
 | 
			
		||||
SONAME=libjanet.so.1.34
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
build/c/shell.c: src/mainclient/shell.c
 | 
			
		||||
@@ -220,7 +228,7 @@ $(JANET_TARGET): $(JANET_TARGET_OBJECTS)
 | 
			
		||||
	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS)
 | 
			
		||||
 | 
			
		||||
$(JANET_LIBRARY): $(JANET_TARGET_OBJECTS)
 | 
			
		||||
	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
 | 
			
		||||
	$(HOSTCC) $(LIBJANET_LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
 | 
			
		||||
 | 
			
		||||
$(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
 | 
			
		||||
	$(HOSTAR) rcs $@ $^
 | 
			
		||||
@@ -263,7 +271,7 @@ dist: build/janet-dist.tar.gz
 | 
			
		||||
 | 
			
		||||
build/janet-%.tar.gz: $(JANET_TARGET) \
 | 
			
		||||
	build/janet.h \
 | 
			
		||||
	janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
 | 
			
		||||
	janet.1 LICENSE CONTRIBUTING.md $(JANET_STATIC_LIBRARY) \
 | 
			
		||||
	README.md build/c/janet.c build/c/shell.c
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/bin
 | 
			
		||||
	cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/
 | 
			
		||||
@@ -271,13 +279,17 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/include
 | 
			
		||||
	cp build/janet.h build/$(JANET_DIST_DIR)/include/
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/lib/
 | 
			
		||||
	cp $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
 | 
			
		||||
	cp $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
 | 
			
		||||
	cp $(JANET_LIBRARY) build/$(JANET_DIST_DIR)/lib/ || true
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/man/man1/
 | 
			
		||||
	cp janet.1 build/$(JANET_DIST_DIR)/man/man1/janet.1
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/src/
 | 
			
		||||
	cp build/c/janet.c build/c/shell.c build/$(JANET_DIST_DIR)/src/
 | 
			
		||||
	cp CONTRIBUTING.md LICENSE README.md build/$(JANET_DIST_DIR)/
 | 
			
		||||
	cd build && tar -czvf ../$@ ./$(JANET_DIST_DIR)
 | 
			
		||||
ifeq ($(HAS_SHARED), 1)
 | 
			
		||||
build/janet-%.tar.gz: $(JANET_LIBRARY)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
#########################
 | 
			
		||||
##### Documentation #####
 | 
			
		||||
@@ -331,6 +343,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
 | 
			
		||||
	cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	cp '$(JANET_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
 | 
			
		||||
	cp '$(JANET_LIBRARY_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
 | 
			
		||||
	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs"
 | 
			
		||||
 | 
			
		||||
install-jpm-git: $(JANET_TARGET)
 | 
			
		||||
 
 | 
			
		||||
@@ -91,7 +91,9 @@ exit /b 0
 | 
			
		||||
:CLEAN
 | 
			
		||||
del *.exe *.lib *.exp
 | 
			
		||||
rd /s /q build
 | 
			
		||||
if exist dist (
 | 
			
		||||
    rd /s /q dist
 | 
			
		||||
)
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
@rem Run tests
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								examples/posix-exec.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								examples/posix-exec.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# Switch to python
 | 
			
		||||
 | 
			
		||||
(print "running in Janet")
 | 
			
		||||
(os/posix-exec ["python"] :p)
 | 
			
		||||
(print "will not print")
 | 
			
		||||
							
								
								
									
										18
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								meson.build
									
									
									
									
									
								
							@@ -20,7 +20,7 @@
 | 
			
		||||
 | 
			
		||||
project('janet', 'c',
 | 
			
		||||
  default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
 | 
			
		||||
  version : '1.31.0')
 | 
			
		||||
  version : '1.34.0')
 | 
			
		||||
 | 
			
		||||
# Global settings
 | 
			
		||||
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
 | 
			
		||||
@@ -61,6 +61,7 @@ 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_NO_IPV6', not get_option('ipv6'))
 | 
			
		||||
conf.set('JANET_NO_EV', not get_option('ev') or get_option('single_threaded'))
 | 
			
		||||
conf.set('JANET_REDUCED_OS', get_option('reduced_os'))
 | 
			
		||||
conf.set('JANET_NO_INT_TYPES', not get_option('int_types'))
 | 
			
		||||
@@ -78,6 +79,7 @@ conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue'))
 | 
			
		||||
conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt'))
 | 
			
		||||
conf.set('JANET_NO_FFI', not get_option('ffi'))
 | 
			
		||||
conf.set('JANET_NO_FFI_JIT', not get_option('ffi_jit'))
 | 
			
		||||
conf.set('JANET_NO_CRYPTORAND', not get_option('cryptorand'))
 | 
			
		||||
if get_option('os_name') != ''
 | 
			
		||||
  conf.set('JANET_OS_NAME', get_option('os_name'))
 | 
			
		||||
endif
 | 
			
		||||
@@ -182,11 +184,13 @@ if not get_option('single_threaded')
 | 
			
		||||
  janet_dependencies += thread_dep
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Allow building with no shared library
 | 
			
		||||
if cc.has_argument('-fvisibility=hidden')
 | 
			
		||||
  lib_cflags = ['-fvisibility=hidden']
 | 
			
		||||
else
 | 
			
		||||
  lib_cflags = []
 | 
			
		||||
endif
 | 
			
		||||
if get_option('shared')
 | 
			
		||||
  libjanet = library('janet', janetc,
 | 
			
		||||
    include_directories : incdir,
 | 
			
		||||
    dependencies : janet_dependencies,
 | 
			
		||||
@@ -194,7 +198,6 @@ libjanet = library('janet', janetc,
 | 
			
		||||
    soversion: version_parts[0] + '.' + version_parts[1],
 | 
			
		||||
    c_args : lib_cflags,
 | 
			
		||||
    install : true)
 | 
			
		||||
 | 
			
		||||
# Extra c flags - adding -fvisibility=hidden matches the Makefile and
 | 
			
		||||
# shaves off about 10k on linux x64, likely similar on other platforms.
 | 
			
		||||
  if cc.has_argument('-fvisibility=hidden')
 | 
			
		||||
@@ -208,6 +211,14 @@ janet_mainclient = executable('janet', mainclient_src,
 | 
			
		||||
    link_with: [libjanet],
 | 
			
		||||
    c_args : extra_cflags,
 | 
			
		||||
    install : true)
 | 
			
		||||
else
 | 
			
		||||
  # No shared library
 | 
			
		||||
  janet_mainclient = executable('janet', mainclient_src, janetc,
 | 
			
		||||
    include_directories : incdir,
 | 
			
		||||
    dependencies : janet_dependencies,
 | 
			
		||||
    c_args : lib_cflags,
 | 
			
		||||
    install : true)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if meson.is_cross_build()
 | 
			
		||||
  native_cc = meson.get_compiler('c', native: true)
 | 
			
		||||
@@ -271,14 +282,15 @@ endforeach
 | 
			
		||||
run_target('repl', command : [janet_nativeclient])
 | 
			
		||||
 | 
			
		||||
# For use as meson subproject (wrap)
 | 
			
		||||
if get_option('shared')
 | 
			
		||||
  janet_dep = declare_dependency(include_directories : incdir,
 | 
			
		||||
    link_with : libjanet)
 | 
			
		||||
 | 
			
		||||
# pkgconfig
 | 
			
		||||
  pkg = import('pkgconfig')
 | 
			
		||||
  pkg.generate(libjanet,
 | 
			
		||||
    subdirs: 'janet',
 | 
			
		||||
    description: 'Library for the Janet programming language.')
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Installation
 | 
			
		||||
install_man('janet.1')
 | 
			
		||||
 
 | 
			
		||||
@@ -11,13 +11,14 @@ option('peg', type : 'boolean', value : true)
 | 
			
		||||
option('int_types', type : 'boolean', value : true)
 | 
			
		||||
option('prf', type : 'boolean', value : false)
 | 
			
		||||
option('net', type : 'boolean', value : true)
 | 
			
		||||
option('ipv6', type : 'boolean', value : true)
 | 
			
		||||
option('ev', type : 'boolean', value : true)
 | 
			
		||||
option('processes', type : 'boolean', value : true)
 | 
			
		||||
option('umask', type : 'boolean', value : true)
 | 
			
		||||
option('realpath', type : 'boolean', value : true)
 | 
			
		||||
option('simple_getline', type : 'boolean', value : false)
 | 
			
		||||
option('epoll', type : 'boolean', value : false)
 | 
			
		||||
option('kqueue', type : 'boolean', value : false)
 | 
			
		||||
option('epoll', type : 'boolean', value : true)
 | 
			
		||||
option('kqueue', type : 'boolean', value : true)
 | 
			
		||||
option('interpreter_interrupt', type : 'boolean', value : true)
 | 
			
		||||
option('ffi', type : 'boolean', value : true)
 | 
			
		||||
option('ffi_jit', type : 'boolean', value : true)
 | 
			
		||||
@@ -29,3 +30,5 @@ option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7f
 | 
			
		||||
 | 
			
		||||
option('arch_name', type : 'string', value: '')
 | 
			
		||||
option('os_name', type : 'string', value: '')
 | 
			
		||||
option('shared', type : 'boolean', value: true)
 | 
			
		||||
option('cryptorand', type : 'boolean', value: true)
 | 
			
		||||
 
 | 
			
		||||
@@ -162,7 +162,7 @@
 | 
			
		||||
  ``Define a default value for an optional argument.
 | 
			
		||||
  Expands to `(def sym (if (= nil sym) val sym))`.``
 | 
			
		||||
  [sym val]
 | 
			
		||||
  ~(def ,sym (if (= nil ,sym) ,val ,sym)))
 | 
			
		||||
  ~(def ,sym (if (,= nil ,sym) ,val ,sym)))
 | 
			
		||||
 | 
			
		||||
(defmacro comment
 | 
			
		||||
  "Ignores the body of the comment."
 | 
			
		||||
@@ -420,10 +420,14 @@
 | 
			
		||||
 | 
			
		||||
(defn- range-template
 | 
			
		||||
  [binding object kind rest op comparison]
 | 
			
		||||
  (let [[start stop step] (check-indexed object)]
 | 
			
		||||
    (case kind
 | 
			
		||||
      :range (for-template binding (if stop start 0) (or stop start) (or step 1) comparison op [rest])
 | 
			
		||||
      :down (for-template binding start (or stop 0) (or step 1) comparison op [rest]))))
 | 
			
		||||
  (check-indexed object)
 | 
			
		||||
  (def [a b c] object)
 | 
			
		||||
  (def [start stop step]
 | 
			
		||||
    (case (length object)
 | 
			
		||||
      1 (case kind :range [0 a 1] :down [a 0 1])
 | 
			
		||||
      2 [a b 1]
 | 
			
		||||
      [a b c]))
 | 
			
		||||
  (for-template binding start stop step comparison op [rest]))
 | 
			
		||||
 | 
			
		||||
(defn- each-template
 | 
			
		||||
  [binding inx kind body]
 | 
			
		||||
@@ -438,8 +442,8 @@
 | 
			
		||||
              :each ~(,in ,ds ,k)
 | 
			
		||||
              :keys k
 | 
			
		||||
              :pairs ~[,k (,in ,ds ,k)]))
 | 
			
		||||
         (set ,k (,next ,ds ,k))
 | 
			
		||||
         ,;body))))
 | 
			
		||||
         ,;body
 | 
			
		||||
         (set ,k (,next ,ds ,k))))))
 | 
			
		||||
 | 
			
		||||
(defn- iterate-template
 | 
			
		||||
  [binding expr body]
 | 
			
		||||
@@ -661,6 +665,9 @@
 | 
			
		||||
  (each x xs (*= accum x))
 | 
			
		||||
  accum)
 | 
			
		||||
 | 
			
		||||
# declare ahead of time
 | 
			
		||||
(var- macexvar nil)
 | 
			
		||||
 | 
			
		||||
(defmacro if-let
 | 
			
		||||
  ``Make multiple bindings, and if all are truthy,
 | 
			
		||||
  evaluate the `tru` form. If any are false or nil, evaluate
 | 
			
		||||
@@ -669,20 +676,19 @@
 | 
			
		||||
  (def len (length bindings))
 | 
			
		||||
  (if (= 0 len) (error "expected at least 1 binding"))
 | 
			
		||||
  (if (odd? len) (error "expected an even number of bindings"))
 | 
			
		||||
  (def res (gensym))
 | 
			
		||||
  (def fal2 (if macexvar (macexvar fal) fal))
 | 
			
		||||
  (defn aux [i]
 | 
			
		||||
    (if (>= i len)
 | 
			
		||||
      ~(do (set ,res ,tru) true)
 | 
			
		||||
      tru
 | 
			
		||||
      (do
 | 
			
		||||
        (def bl (in bindings i))
 | 
			
		||||
        (def br (in bindings (+ 1 i)))
 | 
			
		||||
        (if (symbol? bl)
 | 
			
		||||
          ~(if (def ,bl ,br) ,(aux (+ 2 i)))
 | 
			
		||||
          ~(if (def ,bl ,br) ,(aux (+ 2 i)) ,fal2)
 | 
			
		||||
          ~(if (def ,(def sym (gensym)) ,br)
 | 
			
		||||
             (do (def ,bl ,sym) ,(aux (+ 2 i))))))))
 | 
			
		||||
  ~(do
 | 
			
		||||
     (var ,res nil)
 | 
			
		||||
     (if ,(aux 0) ,res ,fal)))
 | 
			
		||||
             (do (def ,bl ,sym) ,(aux (+ 2 i)))
 | 
			
		||||
             ,fal2)))))
 | 
			
		||||
   (aux 0))
 | 
			
		||||
 | 
			
		||||
(defmacro when-let
 | 
			
		||||
  "Same as `(if-let bindings (do ;body))`."
 | 
			
		||||
@@ -2123,15 +2129,16 @@
 | 
			
		||||
     'upscope expandall})
 | 
			
		||||
 | 
			
		||||
  (defn dotup [t]
 | 
			
		||||
    (if (= nil (next t)) (break ()))
 | 
			
		||||
    (def h (in t 0))
 | 
			
		||||
    (def s (in specs h))
 | 
			
		||||
    (def entry (or (dyn h) {}))
 | 
			
		||||
    (def m (do (def r (get entry :ref)) (if r (in r 0) (get entry :value))))
 | 
			
		||||
    (def m? (in entry :macro))
 | 
			
		||||
    (cond
 | 
			
		||||
      s (s t)
 | 
			
		||||
      s (keep-syntax t (s t))
 | 
			
		||||
      m? (do (setdyn *macro-form* t) (m ;(tuple/slice t 1)))
 | 
			
		||||
      (tuple/slice (map recur t))))
 | 
			
		||||
      (keep-syntax! t (map recur t))))
 | 
			
		||||
 | 
			
		||||
  (def ret
 | 
			
		||||
    (case (type x)
 | 
			
		||||
@@ -2243,6 +2250,8 @@
 | 
			
		||||
    (set current (macex1 current on-binding)))
 | 
			
		||||
  current)
 | 
			
		||||
 | 
			
		||||
(set macexvar macex)
 | 
			
		||||
 | 
			
		||||
(defmacro varfn
 | 
			
		||||
  ``Create a function that can be rebound. `varfn` has the same signature
 | 
			
		||||
  as `defn`, but defines functions in the environment as vars. If a var `name`
 | 
			
		||||
@@ -2333,26 +2342,36 @@
 | 
			
		||||
(def default-peg-grammar
 | 
			
		||||
  `The default grammar used for pegs. This grammar defines several common patterns
 | 
			
		||||
  that should make it easier to write more complex patterns.`
 | 
			
		||||
  ~@{:d (range "09")
 | 
			
		||||
     :a (range "az" "AZ")
 | 
			
		||||
  ~@{:a (range "az" "AZ")
 | 
			
		||||
     :d (range "09")
 | 
			
		||||
     :h (range "09" "af" "AF")
 | 
			
		||||
     :s (set " \t\r\n\0\f\v")
 | 
			
		||||
     :w (range "az" "AZ" "09")
 | 
			
		||||
     :h (range "09" "af" "AF")
 | 
			
		||||
     :S (if-not :s 1)
 | 
			
		||||
     :W (if-not :w 1)
 | 
			
		||||
     :A (if-not :a 1)
 | 
			
		||||
     :D (if-not :d 1)
 | 
			
		||||
     :H (if-not :h 1)
 | 
			
		||||
     :d+ (some :d)
 | 
			
		||||
     :S (if-not :s 1)
 | 
			
		||||
     :W (if-not :w 1)
 | 
			
		||||
     :a+ (some :a)
 | 
			
		||||
     :d+ (some :d)
 | 
			
		||||
     :h+ (some :h)
 | 
			
		||||
     :s+ (some :s)
 | 
			
		||||
     :w+ (some :w)
 | 
			
		||||
     :h+ (some :h)
 | 
			
		||||
     :d* (any :d)
 | 
			
		||||
     :A+ (some :A)
 | 
			
		||||
     :D+ (some :D)
 | 
			
		||||
     :H+ (some :H)
 | 
			
		||||
     :S+ (some :S)
 | 
			
		||||
     :W+ (some :W)
 | 
			
		||||
     :a* (any :a)
 | 
			
		||||
     :w* (any :w)
 | 
			
		||||
     :d* (any :d)
 | 
			
		||||
     :h* (any :h)
 | 
			
		||||
     :s* (any :s)
 | 
			
		||||
     :h* (any :h)})
 | 
			
		||||
     :w* (any :w)
 | 
			
		||||
     :A* (any :A)
 | 
			
		||||
     :D* (any :D)
 | 
			
		||||
     :H* (any :H)
 | 
			
		||||
     :S* (any :S)
 | 
			
		||||
     :W* (any :W)})
 | 
			
		||||
 | 
			
		||||
(setdyn *peg-grammar* default-peg-grammar)
 | 
			
		||||
 | 
			
		||||
@@ -2748,6 +2767,11 @@
 | 
			
		||||
(defn- check-is-dep [x] (unless (or (string/has-prefix? "/" x) (string/has-prefix? "@" x) (string/has-prefix? "." x)) x))
 | 
			
		||||
(defn- check-project-relative [x] (if (string/has-prefix? "/" x) x))
 | 
			
		||||
 | 
			
		||||
(defdyn *module/cache* "Dynamic binding for overriding `module/cache`")
 | 
			
		||||
(defdyn *module/paths* "Dynamic binding for overriding `module/cache`")
 | 
			
		||||
(defdyn *module/loading* "Dynamic binding for overriding `module/cache`")
 | 
			
		||||
(defdyn *module/loaders* "Dynamic binding for overriding `module/loaders`")
 | 
			
		||||
 | 
			
		||||
(def module/cache
 | 
			
		||||
  "A table, mapping loaded module identifiers to their environments."
 | 
			
		||||
  @{})
 | 
			
		||||
@@ -2776,24 +2800,25 @@
 | 
			
		||||
  keyword name of a loader in `module/loaders`. Returns the modified `module/paths`.
 | 
			
		||||
  ```
 | 
			
		||||
  [ext loader]
 | 
			
		||||
  (def mp (dyn *module/paths* module/paths))
 | 
			
		||||
  (defn- find-prefix
 | 
			
		||||
    [pre]
 | 
			
		||||
    (or (find-index |(and (string? ($ 0)) (string/has-prefix? pre ($ 0))) module/paths) 0))
 | 
			
		||||
    (or (find-index |(and (string? ($ 0)) (string/has-prefix? pre ($ 0))) mp) 0))
 | 
			
		||||
  (def dyn-index (find-prefix ":@all:"))
 | 
			
		||||
  (array/insert module/paths dyn-index [(string ":@all:" ext) loader check-dyn-relative])
 | 
			
		||||
  (array/insert mp dyn-index [(string ":@all:" ext) loader check-dyn-relative])
 | 
			
		||||
  (def all-index (find-prefix ".:all:"))
 | 
			
		||||
  (array/insert module/paths all-index [(string ".:all:" ext) loader check-project-relative])
 | 
			
		||||
  (array/insert mp all-index [(string ".:all:" ext) loader check-project-relative])
 | 
			
		||||
  (def sys-index (find-prefix ":sys:"))
 | 
			
		||||
  (array/insert module/paths sys-index [(string ":sys:/:all:" ext) loader check-is-dep])
 | 
			
		||||
  (array/insert mp sys-index [(string ":sys:/:all:" ext) loader check-is-dep])
 | 
			
		||||
  (def curall-index (find-prefix ":cur:/:all:"))
 | 
			
		||||
  (array/insert module/paths curall-index [(string ":cur:/:all:" ext) loader check-relative])
 | 
			
		||||
  module/paths)
 | 
			
		||||
  (array/insert mp curall-index [(string ":cur:/:all:" ext) loader check-relative])
 | 
			
		||||
  mp)
 | 
			
		||||
 | 
			
		||||
(module/add-paths ":native:" :native)
 | 
			
		||||
(module/add-paths "/init.janet" :source)
 | 
			
		||||
(module/add-paths ".janet" :source)
 | 
			
		||||
(module/add-paths ".jimage" :image)
 | 
			
		||||
(array/insert module/paths 0 [(fn is-cached [path] (if (in module/cache path) path)) :preload check-not-relative])
 | 
			
		||||
(array/insert module/paths 0 [(fn is-cached [path] (if (in (dyn *module/cache* module/cache) path) path)) :preload check-not-relative])
 | 
			
		||||
 | 
			
		||||
# Version of fexists that works even with a reduced OS
 | 
			
		||||
(defn- fexists
 | 
			
		||||
@@ -2823,7 +2848,8 @@
 | 
			
		||||
  ```
 | 
			
		||||
  [path]
 | 
			
		||||
  (var ret nil)
 | 
			
		||||
  (each [p mod-kind checker] module/paths
 | 
			
		||||
  (def mp (dyn *module/paths* module/paths))
 | 
			
		||||
  (each [p mod-kind checker] mp
 | 
			
		||||
    (when (mod-filter checker path)
 | 
			
		||||
      (if (function? p)
 | 
			
		||||
        (when-let [res (p path)]
 | 
			
		||||
@@ -2839,7 +2865,7 @@
 | 
			
		||||
                     (when (string? t)
 | 
			
		||||
                       (when (mod-filter chk path)
 | 
			
		||||
                         (module/expand-path path t))))
 | 
			
		||||
          paths (filter identity (map expander module/paths))
 | 
			
		||||
          paths (filter identity (map expander mp))
 | 
			
		||||
          str-parts (interpose "\n    " paths)]
 | 
			
		||||
      [nil (string "could not find module " path ":\n    " ;str-parts)])))
 | 
			
		||||
 | 
			
		||||
@@ -2994,13 +3020,15 @@
 | 
			
		||||
  of files as modules.``
 | 
			
		||||
  @{:native (fn native-loader [path &] (native path (make-env)))
 | 
			
		||||
    :source (fn source-loader [path args]
 | 
			
		||||
              (put module/loading path true)
 | 
			
		||||
              (defer (put module/loading path nil)
 | 
			
		||||
              (def ml (dyn *module/loading* module/loading))
 | 
			
		||||
              (put ml path true)
 | 
			
		||||
              (defer (put ml path nil)
 | 
			
		||||
                (dofile path ;args)))
 | 
			
		||||
    :preload (fn preload-loader [path & args]
 | 
			
		||||
               (when-let [m (in module/cache path)]
 | 
			
		||||
               (def mc (dyn *module/cache* module/cache))
 | 
			
		||||
               (when-let [m (in mc path)]
 | 
			
		||||
                 (if (function? m)
 | 
			
		||||
                   (set (module/cache path) (m path ;args))
 | 
			
		||||
                   (set (mc path) (m path ;args))
 | 
			
		||||
                   m)))
 | 
			
		||||
    :image (fn image-loader [path &] (load-image (slurp path)))})
 | 
			
		||||
 | 
			
		||||
@@ -3008,15 +3036,18 @@
 | 
			
		||||
  [path args kargs]
 | 
			
		||||
  (def [fullpath mod-kind] (module/find path))
 | 
			
		||||
  (unless fullpath (error mod-kind))
 | 
			
		||||
  (if-let [check (if-not (kargs :fresh) (in module/cache fullpath))]
 | 
			
		||||
  (def mc (dyn *module/cache* module/cache))
 | 
			
		||||
  (def ml (dyn *module/loading* module/loading))
 | 
			
		||||
  (def mls (dyn *module/loaders* module/loaders))
 | 
			
		||||
  (if-let [check (if-not (kargs :fresh) (in mc fullpath))]
 | 
			
		||||
    check
 | 
			
		||||
    (if (module/loading fullpath)
 | 
			
		||||
    (if (ml fullpath)
 | 
			
		||||
      (error (string "circular dependency " fullpath " detected"))
 | 
			
		||||
      (do
 | 
			
		||||
        (def loader (if (keyword? mod-kind) (module/loaders mod-kind) mod-kind))
 | 
			
		||||
        (def loader (if (keyword? mod-kind) (mls mod-kind) mod-kind))
 | 
			
		||||
        (unless loader (error (string "module type " mod-kind " unknown")))
 | 
			
		||||
        (def env (loader fullpath args))
 | 
			
		||||
        (put module/cache fullpath env)
 | 
			
		||||
        (put mc fullpath env)
 | 
			
		||||
        env))))
 | 
			
		||||
 | 
			
		||||
(defn require
 | 
			
		||||
@@ -3717,12 +3748,20 @@
 | 
			
		||||
    ~(,ev/thread (fn _spawn-thread [&] ,;body) nil :n))
 | 
			
		||||
 | 
			
		||||
  (defmacro ev/with-deadline
 | 
			
		||||
    `Run a body of code with a deadline, such that if the code does not complete before
 | 
			
		||||
    the deadline is up, it will be canceled.`
 | 
			
		||||
    [deadline & body]
 | 
			
		||||
    ``
 | 
			
		||||
    Create a fiber to execute `body`, schedule the event loop to cancel
 | 
			
		||||
    the task (root fiber) associated with `body`'s fiber, and start
 | 
			
		||||
    `body`'s fiber by resuming it.
 | 
			
		||||
 | 
			
		||||
    The event loop will try to cancel the root fiber if `body`'s fiber
 | 
			
		||||
    has not completed after at least `sec` seconds.
 | 
			
		||||
 | 
			
		||||
    `sec` is a number that can have a fractional part.
 | 
			
		||||
    ``
 | 
			
		||||
    [sec & body]
 | 
			
		||||
    (with-syms [f]
 | 
			
		||||
      ~(let [,f (coro ,;body)]
 | 
			
		||||
         (,ev/deadline ,deadline nil ,f)
 | 
			
		||||
         (,ev/deadline ,sec nil ,f)
 | 
			
		||||
         (,resume ,f))))
 | 
			
		||||
 | 
			
		||||
  (defn- cancel-all [chan fibers reason]
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,10 @@
 | 
			
		||||
#define JANETCONF_H
 | 
			
		||||
 | 
			
		||||
#define JANET_VERSION_MAJOR 1
 | 
			
		||||
#define JANET_VERSION_MINOR 31
 | 
			
		||||
#define JANET_VERSION_MINOR 34
 | 
			
		||||
#define JANET_VERSION_PATCH 0
 | 
			
		||||
#define JANET_VERSION_EXTRA ""
 | 
			
		||||
#define JANET_VERSION "1.31.0"
 | 
			
		||||
#define JANET_VERSION "1.34.0"
 | 
			
		||||
 | 
			
		||||
/* #define JANET_BUILD "local" */
 | 
			
		||||
 | 
			
		||||
@@ -52,6 +52,9 @@
 | 
			
		||||
/* #define JANET_EV_NO_EPOLL */
 | 
			
		||||
/* #define JANET_EV_NO_KQUEUE */
 | 
			
		||||
/* #define JANET_NO_INTERPRETER_INTERRUPT */
 | 
			
		||||
/* #define JANET_NO_IPV6 */
 | 
			
		||||
/* #define JANET_NO_CRYPTORAND */
 | 
			
		||||
/* #define JANET_USE_STDATOMIC */
 | 
			
		||||
 | 
			
		||||
/* Custom vm allocator support */
 | 
			
		||||
/* #include <mimalloc.h> */
 | 
			
		||||
 
 | 
			
		||||
@@ -31,8 +31,6 @@
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <stdatomic.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -560,6 +560,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("vararg"));
 | 
			
		||||
    if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
 | 
			
		||||
 | 
			
		||||
    /* Initialize slotcount */
 | 
			
		||||
    def->slotcount = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG) + def->arity;
 | 
			
		||||
 | 
			
		||||
    /* Check structarg */
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("structarg"));
 | 
			
		||||
    if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
 | 
			
		||||
@@ -784,8 +787,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Verify the func def */
 | 
			
		||||
    if (janet_verify(def)) {
 | 
			
		||||
        janet_asm_error(&a, "invalid assembly");
 | 
			
		||||
    int verify_status = janet_verify(def);
 | 
			
		||||
    if (verify_status) {
 | 
			
		||||
        janet_asm_errorv(&a, janet_formatc("invalid assembly (%d)", verify_status));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Add final flags */
 | 
			
		||||
 
 | 
			
		||||
@@ -135,8 +135,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
 | 
			
		||||
 | 
			
		||||
/* Push a cstring to buffer */
 | 
			
		||||
void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring) {
 | 
			
		||||
    int32_t len = 0;
 | 
			
		||||
    while (cstring[len]) ++len;
 | 
			
		||||
    int32_t len = (int32_t) strlen(cstring);
 | 
			
		||||
    janet_buffer_push_bytes(buffer, (const uint8_t *) cstring, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -321,6 +320,143 @@ JANET_CORE_FN(cfun_buffer_chars,
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int should_reverse_bytes(const Janet *argv, int32_t argc) {
 | 
			
		||||
    JanetKeyword order_kw = janet_getkeyword(argv, argc);
 | 
			
		||||
    if (!janet_cstrcmp(order_kw, "le")) {
 | 
			
		||||
#if JANET_BIG_ENDIAN
 | 
			
		||||
        return 1;
 | 
			
		||||
#endif
 | 
			
		||||
    } else if (!janet_cstrcmp(order_kw, "be")) {
 | 
			
		||||
#if JANET_LITTLE_ENDIAN
 | 
			
		||||
        return 1;
 | 
			
		||||
#endif
 | 
			
		||||
    } else if (!janet_cstrcmp(order_kw, "native")) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panicf("expected endianness :le, :be or :native, got %v", argv[1]);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void reverse_u32(uint8_t bytes[4]) {
 | 
			
		||||
    uint8_t temp;
 | 
			
		||||
    temp = bytes[3];
 | 
			
		||||
    bytes[3] = bytes[0];
 | 
			
		||||
    bytes[0] = temp;
 | 
			
		||||
    temp = bytes[2];
 | 
			
		||||
    bytes[2] = bytes[1];
 | 
			
		||||
    bytes[1] = temp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void reverse_u64(uint8_t bytes[8]) {
 | 
			
		||||
    uint8_t temp;
 | 
			
		||||
    temp = bytes[7];
 | 
			
		||||
    bytes[7] = bytes[0];
 | 
			
		||||
    bytes[0] = temp;
 | 
			
		||||
    temp = bytes[6];
 | 
			
		||||
    bytes[6] = bytes[1];
 | 
			
		||||
    bytes[1] = temp;
 | 
			
		||||
    temp = bytes[5];
 | 
			
		||||
    bytes[5] = bytes[2];
 | 
			
		||||
    bytes[2] = temp;
 | 
			
		||||
    temp = bytes[4];
 | 
			
		||||
    bytes[4] = bytes[3];
 | 
			
		||||
    bytes[3] = temp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_uint16,
 | 
			
		||||
              "(buffer/push-uint16 buffer order data)",
 | 
			
		||||
              "Push a 16 bit unsigned integer data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    union {
 | 
			
		||||
        uint16_t data;
 | 
			
		||||
        uint8_t bytes[2];
 | 
			
		||||
    } u;
 | 
			
		||||
    u.data = (uint16_t) janet_getinteger(argv, 2);
 | 
			
		||||
    if (reverse) {
 | 
			
		||||
        uint8_t temp = u.bytes[1];
 | 
			
		||||
        u.bytes[1] = u.bytes[0];
 | 
			
		||||
        u.bytes[0] = temp;
 | 
			
		||||
    }
 | 
			
		||||
    janet_buffer_push_u16(buffer, *(uint16_t *) u.bytes);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_uint32,
 | 
			
		||||
              "(buffer/push-uint32 buffer order data)",
 | 
			
		||||
              "Push a 32 bit unsigned integer data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    union {
 | 
			
		||||
        uint32_t data;
 | 
			
		||||
        uint8_t bytes[4];
 | 
			
		||||
    } u;
 | 
			
		||||
    u.data = (uint32_t) janet_getinteger(argv, 2);
 | 
			
		||||
    if (reverse)
 | 
			
		||||
        reverse_u32(u.bytes);
 | 
			
		||||
    janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_uint64,
 | 
			
		||||
              "(buffer/push-uint64 buffer order data)",
 | 
			
		||||
              "Push a 64 bit unsigned integer data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    union {
 | 
			
		||||
        uint64_t data;
 | 
			
		||||
        uint8_t bytes[8];
 | 
			
		||||
    } u;
 | 
			
		||||
    u.data = (uint64_t) janet_getuinteger64(argv, 2);
 | 
			
		||||
    if (reverse)
 | 
			
		||||
        reverse_u64(u.bytes);
 | 
			
		||||
    janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_float32,
 | 
			
		||||
              "(buffer/push-float32 buffer order data)",
 | 
			
		||||
              "Push the underlying bytes of a 32 bit float data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    union {
 | 
			
		||||
        float data;
 | 
			
		||||
        uint8_t bytes[4];
 | 
			
		||||
    } u;
 | 
			
		||||
    u.data = (float) janet_getnumber(argv, 2);
 | 
			
		||||
    if (reverse)
 | 
			
		||||
        reverse_u32(u.bytes);
 | 
			
		||||
    janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_float64,
 | 
			
		||||
              "(buffer/push-float64 buffer order data)",
 | 
			
		||||
              "Push the underlying bytes of a 64 bit float data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    union {
 | 
			
		||||
        double data;
 | 
			
		||||
        uint8_t bytes[8];
 | 
			
		||||
    } u;
 | 
			
		||||
    u.data = janet_getnumber(argv, 2);
 | 
			
		||||
    if (reverse)
 | 
			
		||||
        reverse_u64(u.bytes);
 | 
			
		||||
    janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void buffer_push_impl(JanetBuffer *buffer, Janet *argv, int32_t argc_offset, int32_t argc) {
 | 
			
		||||
    for (int32_t i = argc_offset; i < argc; i++) {
 | 
			
		||||
        if (janet_checktype(argv[i], JANET_NUMBER)) {
 | 
			
		||||
@@ -529,6 +665,11 @@ void janet_lib_buffer(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("buffer/push-byte", cfun_buffer_u8),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-word", cfun_buffer_word),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-string", cfun_buffer_chars),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-uint16", cfun_buffer_push_uint16),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-uint32", cfun_buffer_push_uint32),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-uint64", cfun_buffer_push_uint64),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-float32", cfun_buffer_push_float32),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-float64", cfun_buffer_push_float64),
 | 
			
		||||
        JANET_CORE_REG("buffer/push", cfun_buffer_push),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-at", cfun_buffer_push_at),
 | 
			
		||||
        JANET_CORE_REG("buffer/popn", cfun_buffer_popn),
 | 
			
		||||
 
 | 
			
		||||
@@ -226,6 +226,7 @@ void janet_bytecode_movopt(JanetFuncDef *def) {
 | 
			
		||||
                case JOP_LOAD_TRUE:
 | 
			
		||||
                case JOP_LOAD_FALSE:
 | 
			
		||||
                case JOP_LOAD_SELF:
 | 
			
		||||
                    break;
 | 
			
		||||
                case JOP_MAKE_ARRAY:
 | 
			
		||||
                case JOP_MAKE_BUFFER:
 | 
			
		||||
                case JOP_MAKE_STRING:
 | 
			
		||||
@@ -233,6 +234,8 @@ void janet_bytecode_movopt(JanetFuncDef *def) {
 | 
			
		||||
                case JOP_MAKE_TABLE:
 | 
			
		||||
                case JOP_MAKE_TUPLE:
 | 
			
		||||
                case JOP_MAKE_BRACKET_TUPLE:
 | 
			
		||||
                    /* Reads from the stack, don't remove */
 | 
			
		||||
                    janetc_regalloc_touch(&ra, DD);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                /* Read A */
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,13 @@
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_USE_STDATOMIC
 | 
			
		||||
#include <stdatomic.h>
 | 
			
		||||
/* We don't need stdatomic on most compilers since we use compiler builtins for atomic operations.
 | 
			
		||||
 * Some (TCC), explicitly require using stdatomic.h and don't have any exposed builtins (that I know of).
 | 
			
		||||
 * For TCC and similar compilers, one would need -std=c11 or similar then to get access. */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
 | 
			
		||||
#ifdef JANET_TOP_LEVEL_SIGNAL
 | 
			
		||||
    JANET_TOP_LEVEL_SIGNAL(msg);
 | 
			
		||||
@@ -338,7 +345,7 @@ int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const c
 | 
			
		||||
    int32_t not_raw = raw;
 | 
			
		||||
    if (not_raw < 0) not_raw += length + 1;
 | 
			
		||||
    if (not_raw < 0 || not_raw > length)
 | 
			
		||||
        janet_panicf("%s index %d out of range [%d,%d]", which, raw, -length - 1, length);
 | 
			
		||||
        janet_panicf("%s index %d out of range [%d,%d]", which, (int64_t) raw, -(int64_t)length - 1, (int64_t) length);
 | 
			
		||||
    return not_raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -361,7 +368,7 @@ int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const ch
 | 
			
		||||
    int32_t not_raw = raw;
 | 
			
		||||
    if (not_raw < 0) not_raw += length;
 | 
			
		||||
    if (not_raw < 0 || not_raw > length)
 | 
			
		||||
        janet_panicf("%s index %d out of range [%d,%d)", which, raw, -length, length);
 | 
			
		||||
        janet_panicf("%s index %d out of range [%d,%d)", which, (int64_t)raw, -(int64_t)length, (int64_t)length);
 | 
			
		||||
    return not_raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -496,6 +503,8 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA
 | 
			
		||||
JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    return InterlockedIncrement(x);
 | 
			
		||||
#elif defined(JANET_USE_STDATOMIC)
 | 
			
		||||
    return atomic_fetch_add_explicit(x, 1, memory_order_relaxed) + 1;
 | 
			
		||||
#else
 | 
			
		||||
    return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -504,8 +513,20 @@ JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
 | 
			
		||||
JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    return InterlockedDecrement(x);
 | 
			
		||||
#elif defined(JANET_USE_STDATOMIC)
 | 
			
		||||
    return atomic_fetch_add_explicit(x, -1, memory_order_acq_rel) - 1;
 | 
			
		||||
#else
 | 
			
		||||
    return __atomic_add_fetch(x, -1, __ATOMIC_RELAXED);
 | 
			
		||||
    return __atomic_add_fetch(x, -1, __ATOMIC_ACQ_REL);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    return InterlockedOr(x, 0);
 | 
			
		||||
#elif defined(JANET_USE_STDATOMIC)
 | 
			
		||||
    return atomic_load_explicit(x, memory_order_acquire);
 | 
			
		||||
#else
 | 
			
		||||
    return __atomic_load_n(x, __ATOMIC_ACQUIRE);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -110,14 +110,14 @@ JANET_CORE_FN(janet_core_expand_path,
 | 
			
		||||
              "(module/expand-path path template)",
 | 
			
		||||
              "Expands a path template as found in `module/paths` for `module/find`. "
 | 
			
		||||
              "This takes in a path (the argument to require) and a template string, "
 | 
			
		||||
              "to expand the path to a path that can be "
 | 
			
		||||
              "used for importing files. The replacements are as follows:\n\n"
 | 
			
		||||
              "to expand the path to a path that can be used for importing files. "
 | 
			
		||||
              "The replacements are as follows:\n\n"
 | 
			
		||||
              "* :all: -- the value of path verbatim.\n\n"
 | 
			
		||||
              "* :@all: -- Same as :all:, but if `path` starts with the @ character,\n"
 | 
			
		||||
              "           the first path segment is replaced with a dynamic binding\n"
 | 
			
		||||
              "* :@all: -- Same as :all:, but if `path` starts with the @ character, "
 | 
			
		||||
              "the first path segment is replaced with a dynamic binding "
 | 
			
		||||
              "`(dyn <first path segment as keyword>)`.\n\n"
 | 
			
		||||
              "* :cur: -- the current file, or (dyn :current-file)\n\n"
 | 
			
		||||
              "* :dir: -- the directory containing the current file\n\n"
 | 
			
		||||
              "* :cur: -- the directory portion, if any, of (dyn :current-file)\n\n"
 | 
			
		||||
              "* :dir: -- the directory portion, if any, of the path argument\n\n"
 | 
			
		||||
              "* :name: -- the name component of path, with extension if given\n\n"
 | 
			
		||||
              "* :native: -- the extension used to load natives, .so or .dll\n\n"
 | 
			
		||||
              "* :sys: -- the system path, or (dyn :syspath)") {
 | 
			
		||||
@@ -1144,17 +1144,20 @@ JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
                    JDOC("(next ds &opt key)\n\n"
 | 
			
		||||
                         "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 "
 | 
			
		||||
                         "to be seen only once per iteration if the data structure is not mutated "
 | 
			
		||||
                         "during iteration. If key is nil, next returns the first key. If next "
 | 
			
		||||
                         "returns nil, there are no more keys to iterate through."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_PROP,
 | 
			
		||||
                    "propagate", 2, 2, 2, 2, propagate_asm, sizeof(propagate_asm),
 | 
			
		||||
                    JDOC("(propagate x fiber)\n\n"
 | 
			
		||||
                         "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. This function can be used to re-raise an error without "
 | 
			
		||||
                         "losing the original stack trace."));
 | 
			
		||||
                         "Propagate a signal from a fiber to the current fiber and "
 | 
			
		||||
                         "set the last value of the current fiber to `x`.  The signal "
 | 
			
		||||
                         "value is then available as the status of 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`. "
 | 
			
		||||
                         "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"
 | 
			
		||||
 
 | 
			
		||||
@@ -388,8 +388,8 @@ JANET_CORE_FN(cfun_debug_stack,
 | 
			
		||||
JANET_CORE_FN(cfun_debug_stacktrace,
 | 
			
		||||
              "(debug/stacktrace fiber &opt err prefix)",
 | 
			
		||||
              "Prints a nice looking stacktrace for a fiber. Can optionally provide "
 | 
			
		||||
              "an error value to print the stack trace with. If `err` is nil or not "
 | 
			
		||||
              "provided, and no prefix is given, will skip the error line. Returns the fiber.") {
 | 
			
		||||
              "an error value to print the stack trace with. If `prefix` is nil or not "
 | 
			
		||||
              "provided, will skip the error line. Returns the fiber.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    Janet x = argc == 1 ? janet_wrap_nil() : argv[1];
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										117
									
								
								src/core/ev.c
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								src/core/ev.c
									
									
									
									
									
								
							@@ -255,19 +255,29 @@ static void add_timeout(JanetTimeout to) {
 | 
			
		||||
 | 
			
		||||
void janet_async_end(JanetFiber *fiber) {
 | 
			
		||||
    if (fiber->ev_callback) {
 | 
			
		||||
        fiber->ev_callback(fiber, JANET_ASYNC_EVENT_DEINIT);
 | 
			
		||||
        janet_gcunroot(janet_wrap_abstract(fiber->ev_stream));
 | 
			
		||||
        fiber->ev_callback = NULL;
 | 
			
		||||
        if (fiber->ev_state) {
 | 
			
		||||
        if (!(fiber->flags & JANET_FIBER_EV_FLAG_IN_FLIGHT)) {
 | 
			
		||||
            if (fiber->ev_state) {
 | 
			
		||||
                janet_free(fiber->ev_state);
 | 
			
		||||
                janet_ev_dec_refcount();
 | 
			
		||||
            }
 | 
			
		||||
                fiber->ev_state = NULL;
 | 
			
		||||
            }
 | 
			
		||||
            janet_ev_dec_refcount();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_async_start(JanetFiber *fiber, JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, size_t data_size) {
 | 
			
		||||
void janet_async_in_flight(JanetFiber *fiber) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT;
 | 
			
		||||
#else
 | 
			
		||||
    (void) fiber;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state) {
 | 
			
		||||
    JanetFiber *fiber = janet_vm.root_fiber;
 | 
			
		||||
    janet_assert(!fiber->ev_callback, "double async on fiber");
 | 
			
		||||
    if (mode & JANET_ASYNC_LISTEN_READ) stream->read_fiber = fiber;
 | 
			
		||||
    if (mode & JANET_ASYNC_LISTEN_WRITE) stream->write_fiber = fiber;
 | 
			
		||||
@@ -275,14 +285,9 @@ void *janet_async_start(JanetFiber *fiber, JanetStream *stream, JanetAsyncMode m
 | 
			
		||||
    fiber->ev_stream = stream;
 | 
			
		||||
    janet_ev_inc_refcount();
 | 
			
		||||
    janet_gcroot(janet_wrap_abstract(stream));
 | 
			
		||||
    if (data_size) {
 | 
			
		||||
        void *data = janet_malloc(data_size);
 | 
			
		||||
        fiber->ev_state = data;
 | 
			
		||||
        return data;
 | 
			
		||||
    } else {
 | 
			
		||||
        fiber->ev_state = NULL;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    fiber->ev_state = state;
 | 
			
		||||
    callback(fiber, JANET_ASYNC_EVENT_INIT);
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_fiber_did_resume(JanetFiber *fiber) {
 | 
			
		||||
@@ -445,6 +450,9 @@ static void *janet_stream_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
    p->handle = (JanetHandle) janet_unmarshal_int64(ctx);
 | 
			
		||||
#else
 | 
			
		||||
    p->handle = (JanetHandle) janet_unmarshal_int(ctx);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_EV_POLL
 | 
			
		||||
    janet_register_stream(p);
 | 
			
		||||
#endif
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
@@ -1161,6 +1169,7 @@ JANET_CORE_FN(cfun_channel_close,
 | 
			
		||||
                msg.argj = janet_wrap_nil();
 | 
			
		||||
                janet_ev_post_event(vm, janet_thread_chan_cb, msg);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (janet_fiber_can_resume(writer.fiber)) {
 | 
			
		||||
                    if (writer.mode == JANET_CP_MODE_CHOICE_WRITE) {
 | 
			
		||||
                        janet_schedule(writer.fiber, make_close_result(channel));
 | 
			
		||||
                    } else {
 | 
			
		||||
@@ -1168,6 +1177,7 @@ JANET_CORE_FN(cfun_channel_close,
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        JanetChannelPending reader;
 | 
			
		||||
        while (!janet_q_pop(&channel->read_pending, &reader, sizeof(reader))) {
 | 
			
		||||
            if (reader.thread != &janet_vm) {
 | 
			
		||||
@@ -1180,6 +1190,7 @@ JANET_CORE_FN(cfun_channel_close,
 | 
			
		||||
                msg.argj = janet_wrap_nil();
 | 
			
		||||
                janet_ev_post_event(vm, janet_thread_chan_cb, msg);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (janet_fiber_can_resume(reader.fiber)) {
 | 
			
		||||
                    if (reader.mode == JANET_CP_MODE_CHOICE_READ) {
 | 
			
		||||
                        janet_schedule(reader.fiber, make_close_result(channel));
 | 
			
		||||
                    } else {
 | 
			
		||||
@@ -1188,6 +1199,7 @@ JANET_CORE_FN(cfun_channel_close,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_chan_unlock(channel);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
@@ -1279,7 +1291,7 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout);
 | 
			
		||||
int janet_loop_done(void) {
 | 
			
		||||
    return !((janet_vm.spawn.head != janet_vm.spawn.tail) ||
 | 
			
		||||
             janet_vm.tq_count ||
 | 
			
		||||
             janet_vm.listener_count);
 | 
			
		||||
             janet_atomic_load(&janet_vm.listener_count));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetFiber *janet_loop1(void) {
 | 
			
		||||
@@ -1341,7 +1353,7 @@ JanetFiber *janet_loop1(void) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Poll for events */
 | 
			
		||||
    if (janet_vm.tq_count || janet_vm.listener_count) {
 | 
			
		||||
    if (janet_vm.tq_count || janet_atomic_load(&janet_vm.listener_count)) {
 | 
			
		||||
        JanetTimeout to;
 | 
			
		||||
        memset(&to, 0, sizeof(to));
 | 
			
		||||
        int has_timeout;
 | 
			
		||||
@@ -1360,7 +1372,7 @@ JanetFiber *janet_loop1(void) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        /* Run polling implementation only if pending timeouts or pending events */
 | 
			
		||||
        if (janet_vm.tq_count || janet_vm.listener_count) {
 | 
			
		||||
        if (janet_vm.tq_count || janet_atomic_load(&janet_vm.listener_count)) {
 | 
			
		||||
            janet_loop1_impl(has_timeout, to.when);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -1447,7 +1459,7 @@ void janet_ev_deinit(void) {
 | 
			
		||||
    CloseHandle(janet_vm.iocp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_register_stream(JanetStream *stream) {
 | 
			
		||||
static void janet_register_stream(JanetStream *stream) {
 | 
			
		||||
    if (NULL == CreateIoCompletionPort(stream->handle, janet_vm.iocp, (ULONG_PTR) stream, 0)) {
 | 
			
		||||
        janet_panicf("failed to listen for events: %V", janet_ev_lasterr());
 | 
			
		||||
    }
 | 
			
		||||
@@ -1813,8 +1825,8 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
 | 
			
		||||
        JanetStream *stream = janet_vm.streams[i];
 | 
			
		||||
        janet_vm.fds[i + 1].events = 0;
 | 
			
		||||
        janet_vm.fds[i + 1].revents = 0;
 | 
			
		||||
        if (stream->read_fiber) janet_vm.fds[i + 1].events |= POLLIN;
 | 
			
		||||
        if (stream->write_fiber) janet_vm.fds[i + 1].events |= POLLOUT;
 | 
			
		||||
        if (stream->read_fiber && stream->read_fiber->ev_callback) janet_vm.fds[i + 1].events |= POLLIN;
 | 
			
		||||
        if (stream->write_fiber && stream->write_fiber->ev_callback) janet_vm.fds[i + 1].events |= POLLOUT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Poll for events */
 | 
			
		||||
@@ -2026,6 +2038,7 @@ void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value) {
 | 
			
		||||
    if (return_value.fiber == NULL) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (janet_fiber_can_resume(return_value.fiber)) {
 | 
			
		||||
        switch (return_value.tag) {
 | 
			
		||||
            default:
 | 
			
		||||
            case JANET_EV_TCTAG_NIL:
 | 
			
		||||
@@ -2054,6 +2067,7 @@ void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value) {
 | 
			
		||||
                janet_schedule(return_value.fiber, janet_wrap_boolean(return_value.argi));
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_gcunroot(janet_wrap_fiber(return_value.fiber));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2199,7 +2213,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* fallthrough */
 | 
			
		||||
        case JANET_ASYNC_EVENT_USER: {
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT: {
 | 
			
		||||
            int32_t chunk_size = state->bytes_left > JANET_EV_CHUNKSIZE ? JANET_EV_CHUNKSIZE : state->bytes_left;
 | 
			
		||||
            memset(&(state->overlapped), 0, sizeof(OVERLAPPED));
 | 
			
		||||
            int status;
 | 
			
		||||
@@ -2237,7 +2251,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT;
 | 
			
		||||
            janet_async_in_flight(fiber);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
#else
 | 
			
		||||
@@ -2253,7 +2267,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
 | 
			
		||||
    read_more:
 | 
			
		||||
        case JANET_ASYNC_EVENT_HUP:
 | 
			
		||||
        case JANET_ASYNC_EVENT_USER:
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT:
 | 
			
		||||
        case JANET_ASYNC_EVENT_READ: {
 | 
			
		||||
            JanetBuffer *buffer = state->buf;
 | 
			
		||||
            int32_t bytes_left = state->bytes_left;
 | 
			
		||||
@@ -2332,9 +2346,8 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_ev_read_generic(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int is_chunked, JanetReadMode mode, int flags) {
 | 
			
		||||
    JanetFiber *f = janet_vm.root_fiber;
 | 
			
		||||
    StateRead *state = (StateRead *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_READ, ev_callback_read, sizeof(StateRead));
 | 
			
		||||
static JANET_NO_RETURN void janet_ev_read_generic(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int is_chunked, JanetReadMode mode, int flags) {
 | 
			
		||||
    StateRead *state = janet_malloc(sizeof(StateRead));
 | 
			
		||||
    state->is_chunk = is_chunked;
 | 
			
		||||
    state->buf = buf;
 | 
			
		||||
    state->bytes_left = nbytes;
 | 
			
		||||
@@ -2345,23 +2358,23 @@ static void janet_ev_read_generic(JanetStream *stream, JanetBuffer *buf, int32_t
 | 
			
		||||
#else
 | 
			
		||||
    state->flags = flags;
 | 
			
		||||
#endif
 | 
			
		||||
    ev_callback_read(f, JANET_ASYNC_EVENT_USER);
 | 
			
		||||
    janet_async_start(stream, JANET_ASYNC_LISTEN_READ, ev_callback_read, state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) {
 | 
			
		||||
    janet_ev_read_generic(stream, buf, nbytes, 0, JANET_ASYNC_READMODE_READ, 0);
 | 
			
		||||
}
 | 
			
		||||
void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) {
 | 
			
		||||
    janet_ev_read_generic(stream, buf, nbytes, 1, JANET_ASYNC_READMODE_READ, 0);
 | 
			
		||||
}
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) {
 | 
			
		||||
    janet_ev_read_generic(stream, buf, nbytes, 0, JANET_ASYNC_READMODE_RECV, flags);
 | 
			
		||||
}
 | 
			
		||||
void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) {
 | 
			
		||||
    janet_ev_read_generic(stream, buf, nbytes, 1, JANET_ASYNC_READMODE_RECV, flags);
 | 
			
		||||
}
 | 
			
		||||
void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags) {
 | 
			
		||||
    janet_ev_read_generic(stream, buf, nbytes, 0, JANET_ASYNC_READMODE_RECVFROM, flags);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -2431,7 +2444,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_USER: {
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT: {
 | 
			
		||||
            /* Begin write */
 | 
			
		||||
            int32_t len;
 | 
			
		||||
            const uint8_t *bytes;
 | 
			
		||||
@@ -2461,7 +2474,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
                status = WSASendTo(sock, &state->wbuf, 1, NULL, state->flags, to, tolen, &state->overlapped, NULL);
 | 
			
		||||
                if (status) {
 | 
			
		||||
                    if (WSA_IO_PENDING == WSAGetLastError()) {
 | 
			
		||||
                        fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT;
 | 
			
		||||
                        janet_async_in_flight(fiber);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        janet_cancel(fiber, janet_ev_lasterr());
 | 
			
		||||
                        janet_async_end(fiber);
 | 
			
		||||
@@ -2486,7 +2499,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
                status = WriteFile(stream->handle, bytes, len, NULL, &state->overlapped);
 | 
			
		||||
                if (!status) {
 | 
			
		||||
                    if (ERROR_IO_PENDING == GetLastError()) {
 | 
			
		||||
                        fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT;
 | 
			
		||||
                        janet_async_in_flight(fiber);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        janet_cancel(fiber, janet_ev_lasterr());
 | 
			
		||||
                        janet_async_end(fiber);
 | 
			
		||||
@@ -2505,7 +2518,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
            janet_cancel(fiber, janet_cstringv("stream hup"));
 | 
			
		||||
            janet_async_end(fiber);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_USER:
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT:
 | 
			
		||||
        case JANET_ASYNC_EVENT_WRITE: {
 | 
			
		||||
            int32_t start, len;
 | 
			
		||||
            const uint8_t *bytes;
 | 
			
		||||
@@ -2570,10 +2583,8 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_ev_write_generic(JanetStream *stream, void *buf, void *dest_abst, JanetWriteMode mode, int is_buffer, int flags) {
 | 
			
		||||
    JanetFiber *f = janet_vm.root_fiber;
 | 
			
		||||
    StateWrite *state = (StateWrite *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_WRITE,
 | 
			
		||||
                        ev_callback_write, sizeof(StateWrite));
 | 
			
		||||
static JANET_NO_RETURN void janet_ev_write_generic(JanetStream *stream, void *buf, void *dest_abst, JanetWriteMode mode, int is_buffer, int flags) {
 | 
			
		||||
    StateWrite *state = janet_malloc(sizeof(StateWrite));
 | 
			
		||||
    state->is_buffer = is_buffer;
 | 
			
		||||
    state->src.buf = buf;
 | 
			
		||||
    state->dest_abst = dest_abst;
 | 
			
		||||
@@ -2584,31 +2595,31 @@ static void janet_ev_write_generic(JanetStream *stream, void *buf, void *dest_ab
 | 
			
		||||
    state->flags = flags;
 | 
			
		||||
    state->start = 0;
 | 
			
		||||
#endif
 | 
			
		||||
    ev_callback_write(f, JANET_ASYNC_EVENT_USER);
 | 
			
		||||
    janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, ev_callback_write, state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf) {
 | 
			
		||||
    janet_ev_write_generic(stream, buf, NULL, JANET_ASYNC_WRITEMODE_WRITE, 1, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_ev_write_string(JanetStream *stream, JanetString str) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_write_string(JanetStream *stream, JanetString str) {
 | 
			
		||||
    janet_ev_write_generic(stream, (void *) str, NULL, JANET_ASYNC_WRITEMODE_WRITE, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags) {
 | 
			
		||||
    janet_ev_write_generic(stream, buf, NULL, JANET_ASYNC_WRITEMODE_SEND, 1, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_ev_send_string(JanetStream *stream, JanetString str, int flags) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_send_string(JanetStream *stream, JanetString str, int flags) {
 | 
			
		||||
    janet_ev_write_generic(stream, (void *) str, NULL, JANET_ASYNC_WRITEMODE_SEND, 0, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags) {
 | 
			
		||||
    janet_ev_write_generic(stream, buf, dest, JANET_ASYNC_WRITEMODE_SENDTO, 1, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags) {
 | 
			
		||||
JANET_NO_RETURN void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags) {
 | 
			
		||||
    janet_ev_write_generic(stream, (void *) str, dest, JANET_ASYNC_WRITEMODE_SENDTO, 0, flags);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -2941,10 +2952,15 @@ JANET_CORE_FN(cfun_ev_sleep,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ev_deadline,
 | 
			
		||||
              "(ev/deadline sec &opt tocancel tocheck)",
 | 
			
		||||
              "Set a deadline for a fiber `tocheck`. If `tocheck` is not finished after `sec` seconds, "
 | 
			
		||||
              "`tocancel` will be canceled as with `ev/cancel`. "
 | 
			
		||||
              "If `tocancel` and `tocheck` are not given, they default to `(fiber/root)` and "
 | 
			
		||||
              "`(fiber/current)` respectively. Returns `tocancel`.") {
 | 
			
		||||
              "Schedules the event loop to try to cancel the `tocancel` "
 | 
			
		||||
              "task as with `ev/cancel`. After `sec` seconds, the event "
 | 
			
		||||
              "loop will attempt cancellation of `tocancel` if the "
 | 
			
		||||
              "`tocheck` fiber is resumable. `sec` is a number that can "
 | 
			
		||||
              "have a fractional part. `tocancel` defaults to "
 | 
			
		||||
              "`(fiber/root)`, but if specified, must be a task (root "
 | 
			
		||||
              "fiber). `tocheck` defaults to `(fiber/current)`, but if "
 | 
			
		||||
              "specified, should be a fiber. Returns `tocancel` "
 | 
			
		||||
              "immediately.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    double sec = janet_getnumber(argv, 0);
 | 
			
		||||
    JanetFiber *tocancel = janet_optfiber(argv, argc, 1, janet_vm.root_fiber);
 | 
			
		||||
@@ -2999,7 +3015,6 @@ JANET_CORE_FN(janet_cfun_stream_read,
 | 
			
		||||
        if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
        janet_ev_read(stream, buffer, n);
 | 
			
		||||
    }
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_stream_chunk,
 | 
			
		||||
@@ -3014,7 +3029,6 @@ JANET_CORE_FN(janet_cfun_stream_chunk,
 | 
			
		||||
    double to = janet_optnumber(argv, argc, 3, INFINITY);
 | 
			
		||||
    if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
    janet_ev_readchunk(stream, buffer, n);
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_stream_write,
 | 
			
		||||
@@ -3034,7 +3048,6 @@ JANET_CORE_FN(janet_cfun_stream_write,
 | 
			
		||||
        if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
        janet_ev_write_string(stream, bytes.bytes);
 | 
			
		||||
    }
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mutexgc(void *p, size_t size) {
 | 
			
		||||
 
 | 
			
		||||
@@ -662,7 +662,7 @@ JANET_CORE_FN(cfun_fiber_can_resume,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_last_value,
 | 
			
		||||
              "(fiber/last-value)",
 | 
			
		||||
              "(fiber/last-value fiber)",
 | 
			
		||||
              "Get the last value returned or signaled from the fiber.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -185,6 +185,19 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags);
 | 
			
		||||
/* Prevent stack overflows */
 | 
			
		||||
#define MARSH_STACKCHECK if ((flags & 0xFFFF) > JANET_RECURSION_GUARD) janet_panic("stack overflow")
 | 
			
		||||
 | 
			
		||||
/* Quick check if a fiber cannot be marshalled. This is will
 | 
			
		||||
 * have no false positives, but may have false negatives. */
 | 
			
		||||
static int fiber_cannot_be_marshalled(JanetFiber *fiber) {
 | 
			
		||||
    if (janet_fiber_status(fiber) == JANET_STATUS_ALIVE) return 1;
 | 
			
		||||
    int32_t i = fiber->frame;
 | 
			
		||||
    while (i > 0) {
 | 
			
		||||
        JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
 | 
			
		||||
        if (!frame->func) return 1; /* has cfunction on stack */
 | 
			
		||||
        i = frame->prevframe;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Marshal a function env */
 | 
			
		||||
static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
 | 
			
		||||
    MARSH_STACKCHECK;
 | 
			
		||||
@@ -197,7 +210,9 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
 | 
			
		||||
    }
 | 
			
		||||
    janet_env_valid(env);
 | 
			
		||||
    janet_v_push(st->seen_envs, env);
 | 
			
		||||
    if (env->offset > 0 && (JANET_STATUS_ALIVE == janet_fiber_status(env->as.fiber))) {
 | 
			
		||||
 | 
			
		||||
    /* Special case for early detachment */
 | 
			
		||||
    if (env->offset > 0 && fiber_cannot_be_marshalled(env->as.fiber)) {
 | 
			
		||||
        pushint(st, 0);
 | 
			
		||||
        pushint(st, env->length);
 | 
			
		||||
        Janet *values = env->as.fiber->data + env->offset;
 | 
			
		||||
@@ -328,7 +343,7 @@ static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
 | 
			
		||||
    while (i > 0) {
 | 
			
		||||
        JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
 | 
			
		||||
        if (frame->env) frame->flags |= JANET_STACKFRAME_HASENV;
 | 
			
		||||
        if (!frame->func) janet_panic("cannot marshal fiber with c stackframe");
 | 
			
		||||
        if (!frame->func) janet_panicf("cannot marshal fiber with c stackframe (%v)", janet_wrap_cfunction((JanetCFunction) frame->pc));
 | 
			
		||||
        pushint(st, frame->flags);
 | 
			
		||||
        pushint(st, frame->prevframe);
 | 
			
		||||
        int32_t pcdiff = (int32_t)(frame->pc - frame->func->def->bytecode);
 | 
			
		||||
 
 | 
			
		||||
@@ -79,12 +79,20 @@ const JanetAbstractType janet_address_type = {
 | 
			
		||||
 | 
			
		||||
/* maximum number of bytes in a socket address host (post name resolution) */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#ifdef JANET_NO_IPV6
 | 
			
		||||
#define SA_ADDRSTRLEN (INET_ADDRSTRLEN + 1)
 | 
			
		||||
#else
 | 
			
		||||
#define SA_ADDRSTRLEN (INET6_ADDRSTRLEN + 1)
 | 
			
		||||
#endif
 | 
			
		||||
typedef unsigned short in_port_t;
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_SA_MAX(a, b) (((a) > (b))? (a) : (b))
 | 
			
		||||
#ifdef JANET_NO_IPV6
 | 
			
		||||
#define SA_ADDRSTRLEN JANET_SA_MAX(INET_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
 | 
			
		||||
#else
 | 
			
		||||
#define SA_ADDRSTRLEN JANET_SA_MAX(INET6_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static JanetStream *make_stream(JSock handle, uint32_t flags);
 | 
			
		||||
 | 
			
		||||
@@ -114,16 +122,18 @@ static void janet_net_socknoblock(JSock s) {
 | 
			
		||||
 | 
			
		||||
/* State machine for async connect */
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int did_connect;
 | 
			
		||||
} NetStateConnect;
 | 
			
		||||
 | 
			
		||||
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
    JanetStream *stream = fiber->ev_stream;
 | 
			
		||||
    NetStateConnect *state = (NetStateConnect *)fiber->ev_state;
 | 
			
		||||
    switch (event) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
        /* Wait until we have an actual event before checking.
 | 
			
		||||
         * Windows doesn't support async connect with this, just try immediately.*/
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT:
 | 
			
		||||
#endif
 | 
			
		||||
        case JANET_ASYNC_EVENT_DEINIT:
 | 
			
		||||
            return;
 | 
			
		||||
        case JANET_ASYNC_EVENT_CLOSE:
 | 
			
		||||
            janet_cancel(fiber, janet_cstringv("stream closed"));
 | 
			
		||||
            janet_async_end(fiber);
 | 
			
		||||
@@ -140,7 +150,6 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
#endif
 | 
			
		||||
    if (r == 0) {
 | 
			
		||||
        if (res == 0) {
 | 
			
		||||
            state->did_connect = 1;
 | 
			
		||||
            janet_schedule(fiber, janet_wrap_abstract(stream));
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_cancel(fiber, janet_cstringv(strerror(res)));
 | 
			
		||||
@@ -153,13 +162,8 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
    janet_async_end(fiber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void net_sched_connect(JanetStream *stream) {
 | 
			
		||||
    JanetFiber *f = janet_vm.root_fiber;
 | 
			
		||||
    NetStateConnect *state = (NetStateConnect *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, sizeof(NetStateConnect));
 | 
			
		||||
    state->did_connect = 0;
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    net_callback_connect(f, JANET_ASYNC_EVENT_USER);
 | 
			
		||||
#endif
 | 
			
		||||
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
 | 
			
		||||
    janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* State machine for accepting connections. */
 | 
			
		||||
@@ -229,14 +233,16 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
 | 
			
		||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
 | 
			
		||||
    Janet err;
 | 
			
		||||
    JanetFiber *f = janet_vm.root_fiber;
 | 
			
		||||
    NetStateAccept *state = (NetStateAccept *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, sizeof(NetStateAccept));
 | 
			
		||||
    NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
 | 
			
		||||
    memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
 | 
			
		||||
    memset(&state->buf, 0, 1024);
 | 
			
		||||
    state->function = fun;
 | 
			
		||||
    state->lstream = stream;
 | 
			
		||||
    if (net_sched_accept_impl(state, f, &err)) janet_panicv(err);
 | 
			
		||||
    janet_await();
 | 
			
		||||
    if (net_sched_accept_impl(state, janet_root_fiber(), &err)) {
 | 
			
		||||
        janet_free(state);
 | 
			
		||||
        janet_panicv(err);
 | 
			
		||||
    }
 | 
			
		||||
    janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err) {
 | 
			
		||||
@@ -253,7 +259,7 @@ static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet
 | 
			
		||||
        int code = WSAGetLastError();
 | 
			
		||||
        if (code == WSA_IO_PENDING) {
 | 
			
		||||
            /* indicates io is happening async */
 | 
			
		||||
            fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT;
 | 
			
		||||
            janet_async_in_flight(fiber);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        *err = janet_ev_lasterr();
 | 
			
		||||
@@ -282,7 +288,7 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
            janet_schedule(fiber, janet_wrap_nil());
 | 
			
		||||
            janet_async_end(fiber);
 | 
			
		||||
            return;
 | 
			
		||||
        case JANET_ASYNC_EVENT_USER:
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT:
 | 
			
		||||
        case JANET_ASYNC_EVENT_READ: {
 | 
			
		||||
#if defined(JANET_LINUX)
 | 
			
		||||
            JSock connfd = accept4(stream->handle, NULL, NULL, SOCK_CLOEXEC);
 | 
			
		||||
@@ -310,11 +316,10 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
 | 
			
		||||
    JanetFiber *f = janet_vm.root_fiber;
 | 
			
		||||
    NetStateAccept *state = (NetStateAccept *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, sizeof(NetStateAccept));
 | 
			
		||||
    NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
 | 
			
		||||
    memset(state, 0, sizeof(NetStateAccept));
 | 
			
		||||
    state->function = fun;
 | 
			
		||||
    net_callback_accept(f, JANET_ASYNC_EVENT_USER);
 | 
			
		||||
    janet_await();
 | 
			
		||||
    janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -570,7 +575,6 @@ JANET_CORE_FN(cfun_net_connect,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    net_sched_connect(stream);
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *serverify_socket(JSock sfd) {
 | 
			
		||||
@@ -741,6 +745,7 @@ static Janet janet_so_getname(const void *sa_any) {
 | 
			
		||||
            Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai->sin_port))};
 | 
			
		||||
            return janet_wrap_tuple(janet_tuple_n(pair, 2));
 | 
			
		||||
        }
 | 
			
		||||
#ifndef JANET_NO_IPV6
 | 
			
		||||
        case AF_INET6: {
 | 
			
		||||
            const struct sockaddr_in6 *sai6 = sa_any;
 | 
			
		||||
            if (!inet_ntop(AF_INET6, &(sai6->sin6_addr), buffer, sizeof(buffer))) {
 | 
			
		||||
@@ -749,6 +754,7 @@ static Janet janet_so_getname(const void *sa_any) {
 | 
			
		||||
            Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai6->sin6_port))};
 | 
			
		||||
            return janet_wrap_tuple(janet_tuple_n(pair, 2));
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
        case AF_UNIX: {
 | 
			
		||||
            const struct sockaddr_un *sun = sa_any;
 | 
			
		||||
@@ -815,6 +821,7 @@ JANET_CORE_FN(cfun_stream_accept_loop,
 | 
			
		||||
    JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
 | 
			
		||||
    janet_stream_flags(stream, JANET_STREAM_ACCEPTABLE | JANET_STREAM_SOCKET);
 | 
			
		||||
    JanetFunction *fun = janet_getfunction(argv, 1);
 | 
			
		||||
    if (fun->def->min_arity < 1) janet_panic("handler function must take at least 1 argument");
 | 
			
		||||
    janet_sched_accept(stream, fun);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -851,7 +858,6 @@ JANET_CORE_FN(cfun_stream_read,
 | 
			
		||||
        if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
        janet_ev_recv(stream, buffer, n, MSG_NOSIGNAL);
 | 
			
		||||
    }
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_chunk,
 | 
			
		||||
@@ -866,7 +872,6 @@ JANET_CORE_FN(cfun_stream_chunk,
 | 
			
		||||
    double to = janet_optnumber(argv, argc, 3, INFINITY);
 | 
			
		||||
    if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
    janet_ev_recvchunk(stream, buffer, n, MSG_NOSIGNAL);
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_recv_from,
 | 
			
		||||
@@ -881,7 +886,6 @@ JANET_CORE_FN(cfun_stream_recv_from,
 | 
			
		||||
    double to = janet_optnumber(argv, argc, 3, INFINITY);
 | 
			
		||||
    if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
    janet_ev_recvfrom(stream, buffer, n, MSG_NOSIGNAL);
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_write,
 | 
			
		||||
@@ -901,7 +905,6 @@ JANET_CORE_FN(cfun_stream_write,
 | 
			
		||||
        if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
        janet_ev_send_string(stream, bytes.bytes, MSG_NOSIGNAL);
 | 
			
		||||
    }
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_send_to,
 | 
			
		||||
@@ -922,7 +925,6 @@ JANET_CORE_FN(cfun_stream_send_to,
 | 
			
		||||
        if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
        janet_ev_sendto_string(stream, bytes.bytes, dest, MSG_NOSIGNAL);
 | 
			
		||||
    }
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_flush,
 | 
			
		||||
@@ -956,8 +958,10 @@ static const struct sockopt_type sockopt_type_list[] = {
 | 
			
		||||
    { "ip-multicast-ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER },
 | 
			
		||||
    { "ip-add-membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER },
 | 
			
		||||
    { "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER },
 | 
			
		||||
#ifndef JANET_NO_IPV6
 | 
			
		||||
    { "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER },
 | 
			
		||||
    { "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER },
 | 
			
		||||
#endif
 | 
			
		||||
    { NULL, 0, 0, JANET_POINTER }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -994,7 +998,9 @@ JANET_CORE_FN(cfun_net_setsockopt,
 | 
			
		||||
    union {
 | 
			
		||||
        int v_int;
 | 
			
		||||
        struct ip_mreq v_mreq;
 | 
			
		||||
#ifndef JANET_NO_IPV6
 | 
			
		||||
        struct ipv6_mreq v_mreq6;
 | 
			
		||||
#endif
 | 
			
		||||
    } val;
 | 
			
		||||
 | 
			
		||||
    void *optval = (void *)&val;
 | 
			
		||||
@@ -1012,12 +1018,14 @@ JANET_CORE_FN(cfun_net_setsockopt,
 | 
			
		||||
        val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY);
 | 
			
		||||
        inet_pton(AF_INET, addr, &val.v_mreq.imr_multiaddr.s_addr);
 | 
			
		||||
        optlen = sizeof(val.v_mreq);
 | 
			
		||||
#ifndef JANET_NO_IPV6
 | 
			
		||||
    } else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) {
 | 
			
		||||
        const char *addr = janet_getcstring(argv, 2);
 | 
			
		||||
        memset(&val.v_mreq6, 0, sizeof val.v_mreq6);
 | 
			
		||||
        val.v_mreq6.ipv6mr_interface = 0;
 | 
			
		||||
        inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr);
 | 
			
		||||
        optlen = sizeof(val.v_mreq6);
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panicf("invalid socket option type");
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										203
									
								
								src/core/os.c
									
									
									
									
									
								
							
							
						
						
									
										203
									
								
								src/core/os.c
									
									
									
									
									
								
							@@ -229,10 +229,11 @@ JANET_CORE_FN(os_compiler,
 | 
			
		||||
#undef janet_stringify
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_exit,
 | 
			
		||||
              "(os/exit &opt x)",
 | 
			
		||||
              "(os/exit &opt x force)",
 | 
			
		||||
              "Exit from janet with an exit code equal to x. If x is not an integer, "
 | 
			
		||||
              "the exit with status equal the hash of x.") {
 | 
			
		||||
    janet_arity(argc, 0, 1);
 | 
			
		||||
              "the exit with status equal the hash of x. If `force` is truthy will exit immediately and "
 | 
			
		||||
              "skip cleanup code.") {
 | 
			
		||||
    janet_arity(argc, 0, 2);
 | 
			
		||||
    int status;
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
        status = EXIT_SUCCESS;
 | 
			
		||||
@@ -242,7 +243,11 @@ JANET_CORE_FN(os_exit,
 | 
			
		||||
        status = EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
    janet_deinit();
 | 
			
		||||
    if (argc >= 2 && janet_truthy(argv[1])) {
 | 
			
		||||
        _exit(status);
 | 
			
		||||
    } else {
 | 
			
		||||
        exit(status);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -500,8 +505,11 @@ static int proc_get_status(JanetProc *proc) {
 | 
			
		||||
        status = WEXITSTATUS(status);
 | 
			
		||||
    } else if (WIFSTOPPED(status)) {
 | 
			
		||||
        status = WSTOPSIG(status) + 128;
 | 
			
		||||
    } else {
 | 
			
		||||
    } else if (WIFSIGNALED(status)) {
 | 
			
		||||
        status = WTERMSIG(status) + 128;
 | 
			
		||||
    } else {
 | 
			
		||||
        /* Could possibly return -1 but for now, just panic */
 | 
			
		||||
        janet_panicf("Undefined status code for process termination, %d.", status);
 | 
			
		||||
    }
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
@@ -529,10 +537,12 @@ static void janet_proc_wait_cb(JanetEVGenericMessage args) {
 | 
			
		||||
            JanetString s = janet_formatc("command failed with non-zero exit code %d", status);
 | 
			
		||||
            janet_cancel(args.fiber, janet_wrap_string(s));
 | 
			
		||||
        } else {
 | 
			
		||||
            if (janet_fiber_can_resume(args.fiber)) {
 | 
			
		||||
                janet_schedule(args.fiber, janet_wrap_integer(status));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* End ev check */
 | 
			
		||||
 | 
			
		||||
@@ -611,7 +621,11 @@ os_proc_wait_impl(JanetProc *proc) {
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_proc_wait,
 | 
			
		||||
              "(os/proc-wait proc)",
 | 
			
		||||
              "Block until the subprocess completes. Returns the subprocess return code.") {
 | 
			
		||||
              "Suspend the current fiber until the subprocess completes. Returns the subprocess return code. "
 | 
			
		||||
              "os/proc-wait cannot be called twice on the same process. If `ev/with-deadline` cancels `os/proc-wait` "
 | 
			
		||||
              "with an error or os/proc-wait is cancelled with any error caused by anything else, os/proc-wait still "
 | 
			
		||||
              "finishes in the background. Only after os/proc-wait finishes, a process is cleaned up by the operating "
 | 
			
		||||
              "system. Thus, a process becomes a zombie process if os/proc-wait is not called.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
@@ -640,7 +654,7 @@ static const struct keyword_signal signal_keywords[] = {
 | 
			
		||||
#ifdef SIGTERM
 | 
			
		||||
    {"term", SIGTERM},
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SIGARLM
 | 
			
		||||
#ifdef SIGALRM
 | 
			
		||||
    {"alrm", SIGALRM},
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef SIGHUP
 | 
			
		||||
@@ -722,10 +736,11 @@ static int get_signal_kw(const Janet *argv, int32_t n) {
 | 
			
		||||
JANET_CORE_FN(os_proc_kill,
 | 
			
		||||
              "(os/proc-kill proc &opt wait signal)",
 | 
			
		||||
              "Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process "
 | 
			
		||||
              "handle on windows. If `wait` is truthy, will wait for the process to finish and "
 | 
			
		||||
              "returns the exit code. Otherwise, returns `proc`. If signal is specified send it instead."
 | 
			
		||||
              "Signal keywords are named after their C counterparts but in lowercase with the leading "
 | 
			
		||||
              "`SIG` stripped. Signals are ignored on windows.") {
 | 
			
		||||
              "handle on windows. If os/proc-wait already finished for proc, os/proc-kill raises an error. After "
 | 
			
		||||
              "sending signal to proc, if `wait` is truthy, will wait for the process to finish and return the exit "
 | 
			
		||||
              "code by calling os/proc-wait. Otherwise, returns `proc`. If signal is specified, send it instead. "
 | 
			
		||||
              "Signal keywords are named after their C counterparts but in lowercase with the leading `SIG` stripped. "
 | 
			
		||||
              "Signals are ignored on windows.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
 | 
			
		||||
    if (proc->flags & JANET_PROC_WAITED) {
 | 
			
		||||
@@ -764,8 +779,9 @@ JANET_CORE_FN(os_proc_kill,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_proc_close,
 | 
			
		||||
              "(os/proc-close proc)",
 | 
			
		||||
              "Wait on a process if it has not been waited on, and close pipes created by `os/spawn` "
 | 
			
		||||
              "if they have not been closed. Returns nil.") {
 | 
			
		||||
              "Close pipes created by `os/spawn` if they have not been closed. Then, if os/proc-wait was not already "
 | 
			
		||||
              "called on proc, os/proc-wait is called on it, and it returns the exit code returned by os/proc-wait. "
 | 
			
		||||
              "Otherwise, returns nil.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
@@ -875,8 +891,9 @@ JANET_CORE_FN(os_sigaction,
 | 
			
		||||
    }
 | 
			
		||||
    struct sigaction action;
 | 
			
		||||
    sigset_t mask;
 | 
			
		||||
    sigfillset(&mask);
 | 
			
		||||
    sigaddset(&mask, sig);
 | 
			
		||||
    memset(&action, 0, sizeof(action));
 | 
			
		||||
    action.sa_flags |= SA_RESTART;
 | 
			
		||||
    if (can_interrupt) {
 | 
			
		||||
#ifdef JANET_NO_INTERPRETER_INTERRUPT
 | 
			
		||||
        janet_panic("interpreter interrupt not enabled");
 | 
			
		||||
@@ -1081,11 +1098,18 @@ static JanetFile *get_stdio_for_handle(JanetHandle handle, void *orig, int iswri
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_EXECUTE_EXECUTE,
 | 
			
		||||
    JANET_EXECUTE_SPAWN,
 | 
			
		||||
    JANET_EXECUTE_EXEC
 | 
			
		||||
} JanetExecuteMode;
 | 
			
		||||
 | 
			
		||||
static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS);
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
 | 
			
		||||
    /* Get flags */
 | 
			
		||||
    int is_spawn = mode == JANET_EXECUTE_SPAWN;
 | 
			
		||||
    uint64_t flags = 0;
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
        flags = janet_getflags(argv, 1, "epxd");
 | 
			
		||||
@@ -1109,7 +1133,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
 | 
			
		||||
    int pipe_owner_flags = (is_spawn && (flags & 0x8)) ? JANET_PROC_ALLOW_ZOMBIE : 0;
 | 
			
		||||
 | 
			
		||||
    /* Get optional redirections */
 | 
			
		||||
    if (argc > 2) {
 | 
			
		||||
    if (argc > 2 && (mode != JANET_EXECUTE_EXEC)) {
 | 
			
		||||
        JanetDictView tab = janet_getdictionary(argv, 2);
 | 
			
		||||
        Janet maybe_stdin = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("in"));
 | 
			
		||||
        Janet maybe_stdout = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("out"));
 | 
			
		||||
@@ -1230,12 +1254,32 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
 | 
			
		||||
     * of posix_spawn would modify the argv array passed in. */
 | 
			
		||||
    char *const *cargv = (char *const *)child_argv;
 | 
			
		||||
 | 
			
		||||
    /* Use posix_spawn to spawn new process */
 | 
			
		||||
 | 
			
		||||
    if (use_environ) {
 | 
			
		||||
        janet_lock_environ();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* exec mode */
 | 
			
		||||
    if (mode == JANET_EXECUTE_EXEC) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
        janet_panic("not supported on windows");
 | 
			
		||||
#else
 | 
			
		||||
        int status;
 | 
			
		||||
        if (!use_environ) {
 | 
			
		||||
            environ = envp;
 | 
			
		||||
        }
 | 
			
		||||
        do {
 | 
			
		||||
            if (janet_flag_at(flags, 1)) {
 | 
			
		||||
                status = execvp(cargv[0], cargv);
 | 
			
		||||
            } else {
 | 
			
		||||
                status = execv(cargv[0], cargv);
 | 
			
		||||
            }
 | 
			
		||||
        } while (status == -1 && errno == EINTR);
 | 
			
		||||
        janet_panicf("%p: %s", cargv[0], strerror(errno ? errno : ENOENT));
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Use posix_spawn to spawn new process */
 | 
			
		||||
 | 
			
		||||
    /* Posix spawn setup */
 | 
			
		||||
    posix_spawn_file_actions_t actions;
 | 
			
		||||
    posix_spawn_file_actions_init(&actions);
 | 
			
		||||
@@ -1342,22 +1386,63 @@ JANET_CORE_FN(os_execute,
 | 
			
		||||
              "* :d - Don't try and terminate the process on garbage collection (allow spawning zombies).\n"
 | 
			
		||||
              "`env` is a table or struct mapping environment variables to values. It can also "
 | 
			
		||||
              "contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. "
 | 
			
		||||
              "These arguments should be core/file values. "
 | 
			
		||||
              "Returns the exit status of the program.") {
 | 
			
		||||
    return os_execute_impl(argc, argv, 0);
 | 
			
		||||
              ":in, :out, and :err should be core/file values or core/stream values. core/file values and core/stream "
 | 
			
		||||
              "values passed to :in, :out, and :err should be closed manually because os/execute doesn't close them. "
 | 
			
		||||
              "Returns the exit code of the program.") {
 | 
			
		||||
    return os_execute_impl(argc, argv, JANET_EXECUTE_EXECUTE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_spawn,
 | 
			
		||||
              "(os/spawn args &opt flags env)",
 | 
			
		||||
              "Execute a program on the system and return a handle to the process. Otherwise, takes the "
 | 
			
		||||
              "same arguments as `os/execute`. Does not wait for the process. "
 | 
			
		||||
              "For each of the :in, :out, and :err keys to the `env` argument, one "
 | 
			
		||||
              "can also pass in the keyword `:pipe` "
 | 
			
		||||
              "to get streams for standard IO of the subprocess that can be read from and written to. "
 | 
			
		||||
              "The returned value `proc` has the fields :in, :out, :err, :return-code, and "
 | 
			
		||||
              "the additional field :pid on unix-like platforms. Use `(os/proc-wait proc)` to rejoin the "
 | 
			
		||||
              "subprocess or `(os/proc-kill proc)`.") {
 | 
			
		||||
    return os_execute_impl(argc, argv, 1);
 | 
			
		||||
              "same arguments as `os/execute`. Does not wait for the process. For each of the :in, :out, and :err keys "
 | 
			
		||||
              "of the `env` argument, one can also pass in the keyword `:pipe` to get streams for standard IO of the "
 | 
			
		||||
              "subprocess that can be read from and written to. The returned value `proc` has the fields :in, :out, "
 | 
			
		||||
              ":err, and the additional field :pid on unix-like platforms. `(os/proc-wait proc)` must be called to "
 | 
			
		||||
              "rejoin the subprocess. After `(os/proc-wait proc)` finishes, proc gains a new field, :return-code. "
 | 
			
		||||
              "If :x flag is passed to os/spawn, non-zero exit code will cause os/proc-wait to raise an error. "
 | 
			
		||||
              "If pipe streams created with :pipe keyword are not closed in time, janet can run out of file "
 | 
			
		||||
              "descriptors. They can be closed individually, or `os/proc-close` can close all pipe streams on proc. "
 | 
			
		||||
              "If pipe streams aren't read before `os/proc-wait` finishes, then pipe buffers become full, and the "
 | 
			
		||||
              "process cannot finish because the process cannot print more on pipe buffers which are already full. "
 | 
			
		||||
              "If the process cannot finish, os/proc-wait cannot finish, either.") {
 | 
			
		||||
    return os_execute_impl(argc, argv, JANET_EXECUTE_SPAWN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_posix_exec,
 | 
			
		||||
              "(os/posix-exec args &opt flags env)",
 | 
			
		||||
              "Use the execvpe or execve system calls to replace the current process with an interface similar to os/execute. "
 | 
			
		||||
              "Hoever, instead of creating a subprocess, the current process is replaced. Is not supported on windows, and "
 | 
			
		||||
              "does not allow redirection of stdio.") {
 | 
			
		||||
    return os_execute_impl(argc, argv, JANET_EXECUTE_EXEC);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_posix_fork,
 | 
			
		||||
              "(os/posix-fork)",
 | 
			
		||||
              "Make a `fork` system call and create a new process. Return nil if in the new process, otherwise a core/process object (as returned by os/spawn). "
 | 
			
		||||
              "Not supported on all systems (POSIX only).") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS);
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    janet_panic("not supported");
 | 
			
		||||
#else
 | 
			
		||||
    pid_t result;
 | 
			
		||||
    do {
 | 
			
		||||
        result = fork();
 | 
			
		||||
    } while (result == -1 && errno == EINTR);
 | 
			
		||||
    if (result == -1) {
 | 
			
		||||
        janet_panic(strerror(errno));
 | 
			
		||||
    }
 | 
			
		||||
    if (result) {
 | 
			
		||||
        JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc));
 | 
			
		||||
        memset(proc, 0, sizeof(JanetProc));
 | 
			
		||||
        proc->pid = result;
 | 
			
		||||
        proc->flags = JANET_PROC_ALLOW_ZOMBIE;
 | 
			
		||||
        return janet_wrap_abstract(proc);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
@@ -1479,20 +1564,24 @@ JANET_CORE_FN(os_time,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_clock,
 | 
			
		||||
              "(os/clock &opt source)",
 | 
			
		||||
              "Return the number of whole + fractional seconds of the requested clock source.\n\n"
 | 
			
		||||
              "(os/clock &opt source format)",
 | 
			
		||||
              "Return the current time of the requested clock source.\n\n"
 | 
			
		||||
              "The `source` argument selects the clock source to use, when not specified the default "
 | 
			
		||||
              "is `:realtime`:\n"
 | 
			
		||||
              "- :realtime: Return the real (i.e., wall-clock) time. This clock is affected by discontinuous "
 | 
			
		||||
              "  jumps in the system time\n"
 | 
			
		||||
              "- :monotonic: Return the number of whole + fractional seconds since some fixed point in "
 | 
			
		||||
              "  time. The clock is guaranteed to be non-decreasing in real time.\n"
 | 
			
		||||
              "- :cputime: Return the CPU time consumed by this process  (i.e. all threads in the process)\n") {
 | 
			
		||||
              "- :cputime: Return the CPU time consumed by this process  (i.e. all threads in the process)\n"
 | 
			
		||||
              "The `format` argument selects the type of output, when not specified the default is `:double`:\n"
 | 
			
		||||
              "- :double: Return the number of seconds + fractional seconds as a double\n"
 | 
			
		||||
              "- :int: Return the number of seconds as an integer\n"
 | 
			
		||||
              "- :tuple: Return a 2 integer tuple [seconds, nanoseconds]\n") {
 | 
			
		||||
    enum JanetTimeSource source;
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_HRTIME);
 | 
			
		||||
    janet_arity(argc, 0, 1);
 | 
			
		||||
    enum JanetTimeSource source = JANET_TIME_REALTIME;
 | 
			
		||||
    if (argc == 1) {
 | 
			
		||||
        JanetKeyword sourcestr = janet_getkeyword(argv, 0);
 | 
			
		||||
    janet_arity(argc, 0, 2);
 | 
			
		||||
 | 
			
		||||
    JanetKeyword sourcestr = janet_optkeyword(argv, argc, 0, (const uint8_t *) "realtime");
 | 
			
		||||
    if (janet_cstrcmp(sourcestr, "realtime") == 0) {
 | 
			
		||||
        source = JANET_TIME_REALTIME;
 | 
			
		||||
    } else if (janet_cstrcmp(sourcestr, "monotonic") == 0) {
 | 
			
		||||
@@ -1502,11 +1591,24 @@ JANET_CORE_FN(os_clock,
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panicf("expected :realtime, :monotonic, or :cputime, got %v", argv[0]);
 | 
			
		||||
    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct timespec tv;
 | 
			
		||||
    if (janet_gettime(&tv, source)) janet_panic("could not get time");
 | 
			
		||||
 | 
			
		||||
    JanetKeyword formatstr = janet_optkeyword(argv, argc, 1, (const uint8_t *) "double");
 | 
			
		||||
    if (janet_cstrcmp(formatstr, "double") == 0) {
 | 
			
		||||
        double dtime = tv.tv_sec + (tv.tv_nsec / 1E9);
 | 
			
		||||
        return janet_wrap_number(dtime);
 | 
			
		||||
    } else if (janet_cstrcmp(formatstr, "int") == 0) {
 | 
			
		||||
        return janet_wrap_number(tv.tv_sec);
 | 
			
		||||
    } else if (janet_cstrcmp(formatstr, "tuple") == 0) {
 | 
			
		||||
        Janet tup[2] = {janet_wrap_integer(tv.tv_sec),
 | 
			
		||||
                        janet_wrap_integer(tv.tv_nsec)
 | 
			
		||||
                       };
 | 
			
		||||
        return janet_wrap_tuple(janet_tuple_n(tup, 2));
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panicf("expected :double, :int, or :tuple, got %v", argv[1]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_sleep,
 | 
			
		||||
@@ -2336,34 +2438,6 @@ JANET_CORE_FN(os_permission_int,
 | 
			
		||||
    return janet_wrap_integer(os_get_unix_mode(argv, 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_posix_fork,
 | 
			
		||||
              "(os/posix-fork)",
 | 
			
		||||
              "Make a `fork` system call and create a new process. Return nil if in the new process, otherwise a core/process object (as returned by os/spawn). "
 | 
			
		||||
              "Not supported on all systems (POSIX only).") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_SUBPROCESS);
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    janet_panic("not supported");
 | 
			
		||||
#else
 | 
			
		||||
    pid_t result;
 | 
			
		||||
    do {
 | 
			
		||||
        result = fork();
 | 
			
		||||
    } while (result == -1 && errno == EINTR);
 | 
			
		||||
    if (result == -1) {
 | 
			
		||||
        janet_panic(strerror(errno));
 | 
			
		||||
    }
 | 
			
		||||
    if (result) {
 | 
			
		||||
        JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc));
 | 
			
		||||
        memset(proc, 0, sizeof(JanetProc));
 | 
			
		||||
        proc->pid = result;
 | 
			
		||||
        proc->flags = JANET_PROC_ALLOW_ZOMBIE;
 | 
			
		||||
        return janet_wrap_abstract(proc);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -2651,6 +2725,7 @@ void janet_lib_os(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("os/spawn", os_spawn),
 | 
			
		||||
        JANET_CORE_REG("os/shell", os_shell),
 | 
			
		||||
        JANET_CORE_REG("os/posix-fork", os_posix_fork),
 | 
			
		||||
        JANET_CORE_REG("os/posix-exec", os_posix_exec),
 | 
			
		||||
        /* no need to sandbox process management if you can't create processes
 | 
			
		||||
         * (allows for limited functionality if use exposes C-functions to create specific processes) */
 | 
			
		||||
        JANET_CORE_REG("os/proc-wait", os_proc_wait),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										106
									
								
								src/core/peg.c
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								src/core/peg.c
									
									
									
									
									
								
							@@ -39,6 +39,10 @@
 | 
			
		||||
typedef struct {
 | 
			
		||||
    const uint8_t *text_start;
 | 
			
		||||
    const uint8_t *text_end;
 | 
			
		||||
    /* text_end can be restricted by some rules, but
 | 
			
		||||
       outer_text_end will always contain the real end of
 | 
			
		||||
       input, which we need to generate a line mapping */
 | 
			
		||||
    const uint8_t *outer_text_end;
 | 
			
		||||
    const uint32_t *bytecode;
 | 
			
		||||
    const Janet *constants;
 | 
			
		||||
    JanetArray *captures;
 | 
			
		||||
@@ -114,12 +118,12 @@ static LineCol get_linecol_from_position(PegState *s, int32_t position) {
 | 
			
		||||
    /* Generate if not made yet */
 | 
			
		||||
    if (s->linemaplen < 0) {
 | 
			
		||||
        int32_t newline_count = 0;
 | 
			
		||||
        for (const uint8_t *c = s->text_start; c < s->text_end; c++) {
 | 
			
		||||
        for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
 | 
			
		||||
            if (*c == '\n') newline_count++;
 | 
			
		||||
        }
 | 
			
		||||
        int32_t *mem = janet_smalloc(sizeof(int32_t) * newline_count);
 | 
			
		||||
        size_t index = 0;
 | 
			
		||||
        for (const uint8_t *c = s->text_start; c < s->text_end; c++) {
 | 
			
		||||
        for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
 | 
			
		||||
            if (*c == '\n') mem[index++] = (int32_t)(c - s->text_start);
 | 
			
		||||
        }
 | 
			
		||||
        s->linemaplen = newline_count;
 | 
			
		||||
@@ -179,7 +183,7 @@ static const uint8_t *peg_rule(
 | 
			
		||||
    const uint32_t *rule,
 | 
			
		||||
    const uint8_t *text) {
 | 
			
		||||
tail:
 | 
			
		||||
    switch (*rule & 0x1F) {
 | 
			
		||||
    switch (*rule) {
 | 
			
		||||
        default:
 | 
			
		||||
            janet_panic("unexpected opcode");
 | 
			
		||||
            return NULL;
 | 
			
		||||
@@ -482,6 +486,68 @@ tail:
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_SUB: {
 | 
			
		||||
            const uint8_t *text_start = text;
 | 
			
		||||
            const uint32_t *rule_window = s->bytecode + rule[1];
 | 
			
		||||
            const uint32_t *rule_subpattern = s->bytecode + rule[2];
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *window_end = peg_rule(s, rule_window, text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (!window_end) {
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
            const uint8_t *saved_end = s->text_end;
 | 
			
		||||
            s->text_end = window_end;
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *next_text = peg_rule(s, rule_subpattern, text_start);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            s->text_end = saved_end;
 | 
			
		||||
 | 
			
		||||
            if (!next_text) {
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return window_end;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_SPLIT: {
 | 
			
		||||
            const uint8_t *saved_end = s->text_end;
 | 
			
		||||
            const uint32_t *rule_separator = s->bytecode + rule[1];
 | 
			
		||||
            const uint32_t *rule_subpattern = s->bytecode + rule[2];
 | 
			
		||||
 | 
			
		||||
            const uint8_t *separator_end = NULL;
 | 
			
		||||
            do {
 | 
			
		||||
                const uint8_t *text_start = text;
 | 
			
		||||
                CapState cs = cap_save(s);
 | 
			
		||||
                down1(s);
 | 
			
		||||
                while (text <= s->text_end) {
 | 
			
		||||
                    separator_end = peg_rule(s, rule_separator, text);
 | 
			
		||||
                    cap_load(s, cs);
 | 
			
		||||
                    if (separator_end) {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    text++;
 | 
			
		||||
                }
 | 
			
		||||
                up1(s);
 | 
			
		||||
 | 
			
		||||
                if (separator_end) {
 | 
			
		||||
                    s->text_end = text;
 | 
			
		||||
                    text = separator_end;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                down1(s);
 | 
			
		||||
                const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, text_start);
 | 
			
		||||
                up1(s);
 | 
			
		||||
                s->text_end = saved_end;
 | 
			
		||||
 | 
			
		||||
                if (!subpattern_end) {
 | 
			
		||||
                    return NULL;
 | 
			
		||||
                }
 | 
			
		||||
            } while (separator_end);
 | 
			
		||||
 | 
			
		||||
            return s->text_end;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_REPLACE:
 | 
			
		||||
        case RULE_MATCHTIME: {
 | 
			
		||||
            uint32_t tag = rule[3];
 | 
			
		||||
@@ -1107,6 +1173,22 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    emit_3(r, RULE_MATCHTIME, subrule, cindex, tag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_sub(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    peg_fixarity(b, argc, 2);
 | 
			
		||||
    Reserve r = reserve(b, 3);
 | 
			
		||||
    uint32_t subrule1 = peg_compile1(b, argv[0]);
 | 
			
		||||
    uint32_t subrule2 = peg_compile1(b, argv[1]);
 | 
			
		||||
    emit_2(r, RULE_SUB, subrule1, subrule2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_split(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    peg_fixarity(b, argc, 2);
 | 
			
		||||
    Reserve r = reserve(b, 3);
 | 
			
		||||
    uint32_t subrule1 = peg_compile1(b, argv[0]);
 | 
			
		||||
    uint32_t subrule2 = peg_compile1(b, argv[1]);
 | 
			
		||||
    emit_2(r, RULE_SPLIT, subrule1, subrule2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
#define JANET_MAX_READINT_WIDTH 8
 | 
			
		||||
#else
 | 
			
		||||
@@ -1190,6 +1272,8 @@ static const SpecialPair peg_specials[] = {
 | 
			
		||||
    {"sequence", spec_sequence},
 | 
			
		||||
    {"set", spec_set},
 | 
			
		||||
    {"some", spec_some},
 | 
			
		||||
    {"split", spec_split},
 | 
			
		||||
    {"sub", spec_sub},
 | 
			
		||||
    {"thru", spec_thru},
 | 
			
		||||
    {"to", spec_to},
 | 
			
		||||
    {"uint", spec_uint_le},
 | 
			
		||||
@@ -1431,7 +1515,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
        uint32_t instr = bytecode[i];
 | 
			
		||||
        uint32_t *rule = bytecode + i;
 | 
			
		||||
        op_flags[i] |= 0x02;
 | 
			
		||||
        switch (instr & 0x1F) {
 | 
			
		||||
        switch (instr) {
 | 
			
		||||
            case RULE_LITERAL:
 | 
			
		||||
                i += 2 + ((rule[1] + 3) >> 2);
 | 
			
		||||
                break;
 | 
			
		||||
@@ -1524,6 +1608,15 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
                op_flags[rule[1]] |= 0x01;
 | 
			
		||||
                i += 4;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_SUB:
 | 
			
		||||
            case RULE_SPLIT:
 | 
			
		||||
                /* [rule, rule] */
 | 
			
		||||
                if (rule[1] >= blen) goto bad;
 | 
			
		||||
                if (rule[2] >= blen) goto bad;
 | 
			
		||||
                op_flags[rule[1]] |= 0x01;
 | 
			
		||||
                op_flags[rule[2]] |= 0x01;
 | 
			
		||||
                i += 3;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_ERROR:
 | 
			
		||||
            case RULE_DROP:
 | 
			
		||||
            case RULE_NOT:
 | 
			
		||||
@@ -1652,7 +1745,7 @@ typedef struct {
 | 
			
		||||
static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
 | 
			
		||||
    PegCall ret;
 | 
			
		||||
    int32_t min = get_replace ? 3 : 2;
 | 
			
		||||
    janet_arity(argc, get_replace, -1);
 | 
			
		||||
    janet_arity(argc, min, -1);
 | 
			
		||||
    if (janet_checktype(argv[0], JANET_ABSTRACT) &&
 | 
			
		||||
            janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) {
 | 
			
		||||
        ret.peg = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
@@ -1677,6 +1770,7 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
 | 
			
		||||
    ret.s.mode = PEG_MODE_NORMAL;
 | 
			
		||||
    ret.s.text_start = ret.bytes.bytes;
 | 
			
		||||
    ret.s.text_end = ret.bytes.bytes + ret.bytes.len;
 | 
			
		||||
    ret.s.outer_text_end = ret.s.text_end;
 | 
			
		||||
    ret.s.depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    ret.s.captures = janet_array(0);
 | 
			
		||||
    ret.s.tagged_captures = janet_array(0);
 | 
			
		||||
@@ -1771,7 +1865,7 @@ JANET_CORE_FN(cfun_peg_replace_all,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_replace,
 | 
			
		||||
              "(peg/replace peg repl text &opt start & args)",
 | 
			
		||||
              "(peg/replace peg subst text &opt start & args)",
 | 
			
		||||
              "Replace first match of `peg` in `text` with `subst`, returning a new buffer. "
 | 
			
		||||
              "The peg does not need to make captures to do replacement. "
 | 
			
		||||
              "If `subst` is a function, it will be called with the "
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <float.h>
 | 
			
		||||
 | 
			
		||||
/* Implements a pretty printer for Janet. The pretty printer
 | 
			
		||||
 * is simple and not that flexible, but fast. */
 | 
			
		||||
@@ -38,11 +39,15 @@
 | 
			
		||||
/* Temporary buffer size */
 | 
			
		||||
#define BUFSIZE 64
 | 
			
		||||
 | 
			
		||||
/* Preprocessor hacks */
 | 
			
		||||
#define STR_HELPER(x) #x
 | 
			
		||||
#define STR(x) STR_HELPER(x)
 | 
			
		||||
 | 
			
		||||
static void number_to_string_b(JanetBuffer *buffer, double x) {
 | 
			
		||||
    janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
 | 
			
		||||
    const char *fmt = (x == floor(x) &&
 | 
			
		||||
                       x <= JANET_INTMAX_DOUBLE &&
 | 
			
		||||
                       x >= JANET_INTMIN_DOUBLE) ? "%.0f" : "%g";
 | 
			
		||||
                       x >= JANET_INTMIN_DOUBLE) ? "%.0f" : ("%." STR(DBL_DIG) "g");
 | 
			
		||||
    int count;
 | 
			
		||||
    if (x == 0.0) {
 | 
			
		||||
        /* Prevent printing of '-0' */
 | 
			
		||||
@@ -772,6 +777,8 @@ struct FmtMapping {
 | 
			
		||||
/* Janet uses fixed width integer types for most things, so map
 | 
			
		||||
 * format specifiers to these fixed sizes */
 | 
			
		||||
static const struct FmtMapping format_mappings[] = {
 | 
			
		||||
    {'D', PRId64},
 | 
			
		||||
    {'I', PRIi64},
 | 
			
		||||
    {'d', PRId64},
 | 
			
		||||
    {'i', PRIi64},
 | 
			
		||||
    {'o', PRIo64},
 | 
			
		||||
@@ -850,13 +857,19 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
            c = scanformat(c, form, width, precision);
 | 
			
		||||
            switch (*c++) {
 | 
			
		||||
                case 'c': {
 | 
			
		||||
                    int n = va_arg(args, long);
 | 
			
		||||
                    int n = va_arg(args, int);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 'd':
 | 
			
		||||
                case 'i': {
 | 
			
		||||
                    int64_t n = va_arg(args, int);
 | 
			
		||||
                    int64_t n = (int64_t) va_arg(args, int32_t);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 'D':
 | 
			
		||||
                case 'I': {
 | 
			
		||||
                    int64_t n = va_arg(args, int64_t);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -864,7 +877,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
                case 'X':
 | 
			
		||||
                case 'o':
 | 
			
		||||
                case 'u': {
 | 
			
		||||
                    uint64_t n = va_arg(args, unsigned int);
 | 
			
		||||
                    uint64_t n = va_arg(args, uint64_t);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -908,7 +921,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
                    janet_buffer_push_cstring(b, typestr(va_arg(args, Janet)));
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'T': {
 | 
			
		||||
                    int types = va_arg(args, long);
 | 
			
		||||
                    int types = va_arg(args, int);
 | 
			
		||||
                    pushtypes(b, types);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -1017,6 +1030,8 @@ void janet_buffer_format(
 | 
			
		||||
                                  janet_getinteger(argv, arg));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 'D':
 | 
			
		||||
                case 'I':
 | 
			
		||||
                case 'd':
 | 
			
		||||
                case 'i': {
 | 
			
		||||
                    int64_t n = janet_getinteger64(argv, arg);
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
    int errflags = 0, done = 0;
 | 
			
		||||
    int32_t index = 0;
 | 
			
		||||
    Janet ret = janet_wrap_nil();
 | 
			
		||||
    JanetFiber *fiber = NULL;
 | 
			
		||||
    const uint8_t *where = sourcePath ? janet_cstring(sourcePath) : NULL;
 | 
			
		||||
 | 
			
		||||
    if (where) janet_gcroot(janet_wrap_string(where));
 | 
			
		||||
@@ -47,7 +48,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
            JanetCompileResult cres = janet_compile(form, env, where);
 | 
			
		||||
            if (cres.status == JANET_COMPILE_OK) {
 | 
			
		||||
                JanetFunction *f = janet_thunk(cres.funcdef);
 | 
			
		||||
                JanetFiber *fiber = janet_fiber(f, 64, 0, NULL);
 | 
			
		||||
                fiber = janet_fiber(f, 64, 0, NULL);
 | 
			
		||||
                fiber->env = env;
 | 
			
		||||
                JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
 | 
			
		||||
                if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
 | 
			
		||||
@@ -112,9 +113,14 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Enter the event loop if we are not already in it */
 | 
			
		||||
    if (janet_vm.stackn == 0) {
 | 
			
		||||
        janet_gcroot(ret);
 | 
			
		||||
        if (fiber) {
 | 
			
		||||
            janet_gcroot(janet_wrap_fiber(fiber));
 | 
			
		||||
        }
 | 
			
		||||
        janet_loop();
 | 
			
		||||
        janet_gcunroot(ret);
 | 
			
		||||
        if (fiber) {
 | 
			
		||||
            janet_gcunroot(janet_wrap_fiber(fiber));
 | 
			
		||||
            ret = fiber->last_value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (out) *out = ret;
 | 
			
		||||
 
 | 
			
		||||
@@ -149,7 +149,7 @@ static int destructure(JanetCompiler *c,
 | 
			
		||||
                       JanetTable *attr) {
 | 
			
		||||
    switch (janet_type(left)) {
 | 
			
		||||
        default:
 | 
			
		||||
            janetc_error(c, janet_formatc("unexpected type in destruction, got %v", left));
 | 
			
		||||
            janetc_error(c, janet_formatc("unexpected type in destructuring, got %v", left));
 | 
			
		||||
            return 1;
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
            /* Leaf, assign right to left */
 | 
			
		||||
@@ -531,17 +531,11 @@ static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a form matches the pattern (= nil _) or (not= nil _) */
 | 
			
		||||
static int janetc_check_nil_form(JanetFopts opts, Janet x, Janet *capture, uint32_t fun_tag) {
 | 
			
		||||
static int janetc_check_nil_form(Janet x, Janet *capture, uint32_t fun_tag) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_TUPLE)) return 0;
 | 
			
		||||
    JanetTuple tup = janet_unwrap_tuple(x);
 | 
			
		||||
    if (3 != janet_tuple_length(tup)) return 0;
 | 
			
		||||
    Janet op1 = tup[0];
 | 
			
		||||
    if (janet_checktype(op1, JANET_SYMBOL)) {
 | 
			
		||||
        Janet entry = janet_table_get(opts.compiler->env, op1);
 | 
			
		||||
        if (janet_checktype(entry, JANET_TABLE)) {
 | 
			
		||||
            op1 = janet_table_get(janet_unwrap_table(entry), janet_ckeywordv("value"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (!janet_checktype(op1, JANET_FUNCTION)) return 0;
 | 
			
		||||
    JanetFunction *fun = janet_unwrap_function(op1);
 | 
			
		||||
    uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG;
 | 
			
		||||
@@ -601,10 +595,9 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    janetc_scope(&condscope, c, 0, "if");
 | 
			
		||||
 | 
			
		||||
    Janet condform = argv[0];
 | 
			
		||||
    if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_EQ)) {
 | 
			
		||||
    if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
 | 
			
		||||
        ifnjmp = JOP_JUMP_IF_NOT_NIL;
 | 
			
		||||
    }
 | 
			
		||||
    if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_NEQ)) {
 | 
			
		||||
    } else if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
 | 
			
		||||
        ifnjmp = JOP_JUMP_IF_NIL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -613,7 +606,11 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    /* Check constant condition. */
 | 
			
		||||
    /* TODO: Use type info for more short circuits */
 | 
			
		||||
    if (cond.flags & JANET_SLOT_CONSTANT) {
 | 
			
		||||
        if (!janet_truthy(cond.constant)) {
 | 
			
		||||
        int swap_condition = 0;
 | 
			
		||||
        if (ifnjmp == JOP_JUMP_IF_NOT && !janet_truthy(cond.constant)) swap_condition = 1;
 | 
			
		||||
        if (ifnjmp == JOP_JUMP_IF_NIL && janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
 | 
			
		||||
        if (ifnjmp == JOP_JUMP_IF_NOT_NIL && !janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
 | 
			
		||||
        if (swap_condition) {
 | 
			
		||||
            /* Swap the true and false bodies */
 | 
			
		||||
            Janet temp = falsebody;
 | 
			
		||||
            falsebody = truebody;
 | 
			
		||||
@@ -808,12 +805,12 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
     * jmpnl or jmpnn instructions. This let's us implement `(each ...)`
 | 
			
		||||
     * more efficiently. */
 | 
			
		||||
    Janet condform = argv[0];
 | 
			
		||||
    if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_EQ)) {
 | 
			
		||||
    if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
 | 
			
		||||
        is_nil_form = 1;
 | 
			
		||||
        ifjmp = JOP_JUMP_IF_NIL;
 | 
			
		||||
        ifnjmp = JOP_JUMP_IF_NOT_NIL;
 | 
			
		||||
    }
 | 
			
		||||
    if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_NEQ)) {
 | 
			
		||||
    if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
 | 
			
		||||
        is_notnil_form = 1;
 | 
			
		||||
        ifjmp = JOP_JUMP_IF_NOT_NIL;
 | 
			
		||||
        ifnjmp = JOP_JUMP_IF_NIL;
 | 
			
		||||
 
 | 
			
		||||
@@ -549,8 +549,8 @@ JANET_CORE_FN(cfun_string_format,
 | 
			
		||||
              "- `a`, `A`: floating point number, formatted as a hexadecimal number.\n"
 | 
			
		||||
              "- `s`: formatted as a string, precision indicates padding and maximum length.\n"
 | 
			
		||||
              "- `t`: emit the type of the given value.\n"
 | 
			
		||||
              "- `v`: format with (describe x)"
 | 
			
		||||
              "- `V`: format with (string x)"
 | 
			
		||||
              "- `v`: format with (describe x)\n"
 | 
			
		||||
              "- `V`: format with (string x)\n"
 | 
			
		||||
              "- `j`: format to jdn (Janet data notation).\n"
 | 
			
		||||
              "\n"
 | 
			
		||||
              "The following conversion specifiers are used for \"pretty-printing\", where the upper-case "
 | 
			
		||||
 
 | 
			
		||||
@@ -234,6 +234,7 @@ const uint8_t *janet_symbol_gen(void) {
 | 
			
		||||
    head->hash = hash;
 | 
			
		||||
    sym = (uint8_t *)(head->data);
 | 
			
		||||
    memcpy(sym, janet_vm.gensym_counter, sizeof(janet_vm.gensym_counter));
 | 
			
		||||
    sym[head->length] = 0;
 | 
			
		||||
    janet_symcache_put((const uint8_t *)sym, bucket);
 | 
			
		||||
    return (const uint8_t *)sym;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -960,6 +960,7 @@ void arc4random_buf(void *buf, size_t nbytes);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int janet_cryptorand(uint8_t *out, size_t n) {
 | 
			
		||||
#ifndef JANET_NO_CRYPTORAND
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    for (size_t i = 0; i < n; i += sizeof(unsigned int)) {
 | 
			
		||||
        unsigned int v;
 | 
			
		||||
@@ -971,7 +972,10 @@ int janet_cryptorand(uint8_t *out, size_t n) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
#elif defined(JANET_LINUX) || defined(JANET_CYGWIN) || ( defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_7) )
 | 
			
		||||
#elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
 | 
			
		||||
    arc4random_buf(out, n);
 | 
			
		||||
    return 0;
 | 
			
		||||
#else
 | 
			
		||||
    /* We should be able to call getrandom on linux, but it doesn't seem
 | 
			
		||||
       to be uniformly supported on linux distros.
 | 
			
		||||
       On Mac, arc4random_buf wasn't available on until 10.7.
 | 
			
		||||
@@ -993,12 +997,10 @@ int janet_cryptorand(uint8_t *out, size_t n) {
 | 
			
		||||
    }
 | 
			
		||||
    RETRY_EINTR(rc, close(randfd));
 | 
			
		||||
    return 0;
 | 
			
		||||
#elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
 | 
			
		||||
    arc4random_buf(out, n);
 | 
			
		||||
    return 0;
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
    (void) n;
 | 
			
		||||
    (void) out;
 | 
			
		||||
    (void) n;
 | 
			
		||||
    return -1;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -49,11 +49,11 @@
 | 
			
		||||
#ifndef JANET_EXIT
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#define JANET_EXIT(m) do { \
 | 
			
		||||
    fprintf(stderr, "janet interpreter runtime error at line %d in file %s: %s\n",\
 | 
			
		||||
    fprintf(stderr, "janet internal error at line %d in file %s: %s\n",\
 | 
			
		||||
        __LINE__,\
 | 
			
		||||
        __FILE__,\
 | 
			
		||||
        (m));\
 | 
			
		||||
    exit(1);\
 | 
			
		||||
    abort();\
 | 
			
		||||
} while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -596,8 +596,7 @@ typedef enum {
 | 
			
		||||
    JANET_ASYNC_EVENT_READ = 6,
 | 
			
		||||
    JANET_ASYNC_EVENT_WRITE = 7,
 | 
			
		||||
    JANET_ASYNC_EVENT_COMPLETE = 8, /* Used on windows for IOCP */
 | 
			
		||||
    JANET_ASYNC_EVENT_FAILED = 9, /* Used on windows for IOCP */
 | 
			
		||||
    JANET_ASYNC_EVENT_USER = 10
 | 
			
		||||
    JANET_ASYNC_EVENT_FAILED = 9 /* Used on windows for IOCP */
 | 
			
		||||
} JanetAsyncEvent;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
@@ -606,9 +605,7 @@ typedef enum {
 | 
			
		||||
    JANET_ASYNC_LISTEN_BOTH
 | 
			
		||||
} JanetAsyncMode;
 | 
			
		||||
 | 
			
		||||
/* Typedefs */
 | 
			
		||||
typedef struct JanetStream JanetStream;
 | 
			
		||||
typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event);
 | 
			
		||||
 | 
			
		||||
/* Wrapper around file descriptors and HANDLEs that can be polled. */
 | 
			
		||||
struct JanetStream {
 | 
			
		||||
@@ -620,9 +617,24 @@ struct JanetStream {
 | 
			
		||||
    const void *methods; /* Methods for this stream */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event);
 | 
			
		||||
 | 
			
		||||
/* Start listening for events from a stream on the current root fiber. After
 | 
			
		||||
 * calling this, users should call janet_await() before returning from the
 | 
			
		||||
 * current C Function. This also will call janet_await.
 | 
			
		||||
 * mode is which events to listen for, and callback is the function pointer to
 | 
			
		||||
 * call when ever an event is sent from the event loop. state is an optional (can be NULL)
 | 
			
		||||
 * pointer to data allocated with janet_malloc. This pointer will be passed to callback as
 | 
			
		||||
 * fiber->ev_state. It will also be freed for you by the runtime when the event loop determines
 | 
			
		||||
 * it can no longer be referenced. On windows, the contents of state MUST contained an OVERLAPPED struct. */
 | 
			
		||||
JANET_API JANET_NO_RETURN void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state);
 | 
			
		||||
 | 
			
		||||
/* Do not send any more events to the given callback. Call this after scheduling fiber to be resume
 | 
			
		||||
 * or canceled. */
 | 
			
		||||
JANET_API void janet_async_end(JanetFiber *fiber);
 | 
			
		||||
JANET_API void *janet_async_start(JanetFiber *fiber, JanetStream *stream,
 | 
			
		||||
                                  JanetAsyncMode mode, JanetEVCallback callback, size_t data_size);
 | 
			
		||||
 | 
			
		||||
/* Needed for windows to mark a fiber as waiting for an IOCP completion event. Noop on other platforms. */
 | 
			
		||||
JANET_API void janet_async_in_flight(JanetFiber *fiber);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -635,6 +647,7 @@ typedef int32_t JanetAtomicInt;
 | 
			
		||||
#endif
 | 
			
		||||
JANET_API JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x);
 | 
			
		||||
JANET_API JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x);
 | 
			
		||||
JANET_API JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x);
 | 
			
		||||
 | 
			
		||||
/* We provide three possible implementations of Janets. The preferred
 | 
			
		||||
 * nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the
 | 
			
		||||
@@ -1488,22 +1501,22 @@ JANET_API void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGeneric
 | 
			
		||||
JANET_API void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value);
 | 
			
		||||
 | 
			
		||||
/* Read async from a stream */
 | 
			
		||||
JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Write async to a stream */
 | 
			
		||||
JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf);
 | 
			
		||||
JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str);
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags);
 | 
			
		||||
JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags);
 | 
			
		||||
JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags);
 | 
			
		||||
JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -2127,7 +2140,9 @@ typedef enum {
 | 
			
		||||
    RULE_LINE,         /* [tag] */
 | 
			
		||||
    RULE_COLUMN,       /* [tag] */
 | 
			
		||||
    RULE_UNREF,        /* [rule, tag] */
 | 
			
		||||
    RULE_CAPTURE_NUM   /* [rule, tag] */
 | 
			
		||||
    RULE_CAPTURE_NUM,  /* [rule, tag] */
 | 
			
		||||
    RULE_SUB,          /* [rule, rule] */
 | 
			
		||||
    RULE_SPLIT         /* [rule, rule] */
 | 
			
		||||
} JanetPegOpcod;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -502,10 +502,10 @@ static void kright(void) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void krightw(void) {
 | 
			
		||||
    while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) {
 | 
			
		||||
    while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) {
 | 
			
		||||
        gbl_pos++;
 | 
			
		||||
    }
 | 
			
		||||
    while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) {
 | 
			
		||||
    while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) {
 | 
			
		||||
        gbl_pos++;
 | 
			
		||||
    }
 | 
			
		||||
    refresh();
 | 
			
		||||
 
 | 
			
		||||
@@ -51,5 +51,13 @@
 | 
			
		||||
(def f (asm (disasm (fn [x] (fn [y] (+ x y))))))
 | 
			
		||||
(assert (= ((f 10) 37) 47) "asm environment tables")
 | 
			
		||||
 | 
			
		||||
# issue #1424
 | 
			
		||||
(assert-no-error "arity > used slots (issue #1424)"
 | 
			
		||||
                 (asm
 | 
			
		||||
                   (disasm
 | 
			
		||||
                     (fn []
 | 
			
		||||
                       (def foo (fn [one two] one))
 | 
			
		||||
                       (foo 100 200)))))
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -241,6 +241,16 @@
 | 
			
		||||
  (assert (pos? (% x 4)) "generate in loop"))
 | 
			
		||||
(assert (= gencount 75) "generate loop count")
 | 
			
		||||
 | 
			
		||||
# more loop checks
 | 
			
		||||
(assert (deep= (seq [i :range [0 10]] i) @[0 1 2 3 4 5 6 7 8 9]) "seq 1")
 | 
			
		||||
(assert (deep= (seq [i :range [0 10 2]] i) @[0 2 4 6 8]) "seq 2")
 | 
			
		||||
(assert (deep= (seq [i :range [10]] i) @[0 1 2 3 4 5 6 7 8 9]) "seq 3")
 | 
			
		||||
(assert (deep= (seq [i :range-to [10]] i) @[0 1 2 3 4 5 6 7 8 9 10]) "seq 4")
 | 
			
		||||
(def gen (generate [x :range-to [0 nil 2]] x))
 | 
			
		||||
(assert (deep= (take 5 gen) @[0 2 4 6 8]) "generate nil limit")
 | 
			
		||||
(def gen (generate [x :range [0 nil 2]] x))
 | 
			
		||||
(assert (deep= (take 5 gen) @[0 2 4 6 8]) "generate nil limit 2")
 | 
			
		||||
 | 
			
		||||
# Even and odd
 | 
			
		||||
# ff163a5ae
 | 
			
		||||
(assert (odd? 9) "odd? 1")
 | 
			
		||||
@@ -945,10 +955,25 @@
 | 
			
		||||
(defn case-4 [&]
 | 
			
		||||
  (def x (break (break (break)))))
 | 
			
		||||
(bytecode-roundtrip case-4)
 | 
			
		||||
(defn case-5 []
 | 
			
		||||
  (def foo (fn [one two] one))
 | 
			
		||||
  (foo 100 200))
 | 
			
		||||
(bytecode-roundtrip case-5)
 | 
			
		||||
 | 
			
		||||
# Debug bytecode of these functions
 | 
			
		||||
# (pp (disasm case-1))
 | 
			
		||||
# (pp (disasm case-2))
 | 
			
		||||
# (pp (disasm case-3))
 | 
			
		||||
 | 
			
		||||
# Regression #1330
 | 
			
		||||
(defn regress-1330 [&]
 | 
			
		||||
  (def a [1 2 3])
 | 
			
		||||
  (def b [;a])
 | 
			
		||||
  (identity a))
 | 
			
		||||
(assert (= [1 2 3] (regress-1330)) "regression 1330")
 | 
			
		||||
 | 
			
		||||
# Issue 1341
 | 
			
		||||
(assert (= () '() (macex '())) "macex ()")
 | 
			
		||||
(assert (= '[] (macex '[])) "macex []")
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
 
 | 
			
		||||
@@ -77,6 +77,46 @@
 | 
			
		||||
(buffer/push-string b5 "456" @"789")
 | 
			
		||||
(assert (= "123456789" (string b5)) "buffer/push-buffer 2")
 | 
			
		||||
 | 
			
		||||
(def buffer-uint16-be @"")
 | 
			
		||||
(buffer/push-uint16 buffer-uint16-be :be 0x0102)
 | 
			
		||||
(assert (= "\x01\x02" (string buffer-uint16-be)) "buffer/push-uint16 big endian")
 | 
			
		||||
 | 
			
		||||
(def buffer-uint16-le @"")
 | 
			
		||||
(buffer/push-uint16 buffer-uint16-le :le 0x0102)
 | 
			
		||||
(assert (= "\x02\x01" (string buffer-uint16-le)) "buffer/push-uint16 little endian")
 | 
			
		||||
 | 
			
		||||
(def buffer-uint16-negative @"")
 | 
			
		||||
(buffer/push-uint16 buffer-uint16-negative :be -1)
 | 
			
		||||
(assert (= "\xff\xff" (string buffer-uint16-negative)) "buffer/push-uint16 negative")
 | 
			
		||||
 | 
			
		||||
(def buffer-uint32-be @"")
 | 
			
		||||
(buffer/push-uint32 buffer-uint32-be :be 0x01020304)
 | 
			
		||||
(assert (= "\x01\x02\x03\x04" (string buffer-uint32-be)) "buffer/push-uint32 big endian")
 | 
			
		||||
 | 
			
		||||
(def buffer-uint32-le @"")
 | 
			
		||||
(buffer/push-uint32 buffer-uint32-le :le 0x01020304)
 | 
			
		||||
(assert (= "\x04\x03\x02\x01" (string buffer-uint32-le)) "buffer/push-uint32 little endian")
 | 
			
		||||
 | 
			
		||||
(def buffer-uint32-negative @"")
 | 
			
		||||
(buffer/push-uint32 buffer-uint32-negative :be -1)
 | 
			
		||||
(assert (= "\xff\xff\xff\xff" (string buffer-uint32-negative)) "buffer/push-uint32 negative")
 | 
			
		||||
 | 
			
		||||
(def buffer-float32-be @"")
 | 
			
		||||
(buffer/push-float32 buffer-float32-be :be 1.234)
 | 
			
		||||
(assert (= "\x3f\x9d\xf3\xb6" (string buffer-float32-be)) "buffer/push-float32 big endian")
 | 
			
		||||
 | 
			
		||||
(def buffer-float32-le @"")
 | 
			
		||||
(buffer/push-float32 buffer-float32-le :le 1.234)
 | 
			
		||||
(assert (= "\xb6\xf3\x9d\x3f" (string buffer-float32-le)) "buffer/push-float32 little endian")
 | 
			
		||||
 | 
			
		||||
(def buffer-float64-be @"")
 | 
			
		||||
(buffer/push-float64 buffer-float64-be :be 1.234)
 | 
			
		||||
(assert (= "\x3f\xf3\xbe\x76\xc8\xb4\x39\x58" (string buffer-float64-be)) "buffer/push-float64 big endian")
 | 
			
		||||
 | 
			
		||||
(def buffer-float64-le @"")
 | 
			
		||||
(buffer/push-float64 buffer-float64-le :le 1.234)
 | 
			
		||||
(assert (= "\x58\x39\xb4\xc8\x76\xbe\xf3\x3f" (string buffer-float64-le)) "buffer/push-float64 little endian")
 | 
			
		||||
 | 
			
		||||
# Buffer from bytes
 | 
			
		||||
(assert (deep= @"" (buffer/from-bytes)) "buffer/from-bytes 1")
 | 
			
		||||
(assert (deep= @"ABC" (buffer/from-bytes 65 66 67)) "buffer/from-bytes 2")
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,9 @@
 | 
			
		||||
(import ./helper :prefix "" :exit true)
 | 
			
		||||
(start-suite)
 | 
			
		||||
 | 
			
		||||
(def test-port (os/getenv "JANET_TEST_PORT" "8761"))
 | 
			
		||||
(def test-host (os/getenv "JANET_TEST_HOST" "127.0.0.1"))
 | 
			
		||||
 | 
			
		||||
# Subprocess
 | 
			
		||||
# 5e1a8c86f
 | 
			
		||||
(def janet (dyn *executable*))
 | 
			
		||||
@@ -192,11 +195,11 @@
 | 
			
		||||
      (net/write stream b)
 | 
			
		||||
      (buffer/clear b)))
 | 
			
		||||
 | 
			
		||||
  (def s (net/server "127.0.0.1" "8000" handler))
 | 
			
		||||
  (def s (net/server test-host test-port handler))
 | 
			
		||||
  (assert s "made server 1")
 | 
			
		||||
 | 
			
		||||
  (defn test-echo [msg]
 | 
			
		||||
    (with [conn (net/connect "127.0.0.1" "8000")]
 | 
			
		||||
    (with [conn (net/connect test-host test-port)]
 | 
			
		||||
      (net/write conn msg)
 | 
			
		||||
      (def res (net/read conn 1024))
 | 
			
		||||
      (assert (= (string res) msg) (string "echo " msg))))
 | 
			
		||||
@@ -216,18 +219,18 @@
 | 
			
		||||
    # prevent immediate close
 | 
			
		||||
    (ev/read stream 1)
 | 
			
		||||
    (def [host port] (net/localname stream))
 | 
			
		||||
    (assert (= host "127.0.0.1") "localname host server")
 | 
			
		||||
    (assert (= port 8000) "localname port server")))
 | 
			
		||||
    (assert (= host test-host) "localname host server")
 | 
			
		||||
    (assert (= port (scan-number test-port)) "localname port server")))
 | 
			
		||||
 | 
			
		||||
# Test localname and peername
 | 
			
		||||
# 077bf5eba
 | 
			
		||||
(repeat 10
 | 
			
		||||
  (with [s (net/server "127.0.0.1" "8000" names-handler)]
 | 
			
		||||
  (with [s (net/server test-host test-port names-handler)]
 | 
			
		||||
    (repeat 10
 | 
			
		||||
      (with [conn (net/connect "127.0.0.1" "8000")]
 | 
			
		||||
      (with [conn (net/connect test-host test-port)]
 | 
			
		||||
        (def [host port] (net/peername conn))
 | 
			
		||||
        (assert (= host "127.0.0.1") "peername host client ")
 | 
			
		||||
        (assert (= port 8000) "peername port client")
 | 
			
		||||
        (assert (= host test-host) "peername host client ")
 | 
			
		||||
        (assert (= port (scan-number test-port)) "peername port client")
 | 
			
		||||
        # let server close
 | 
			
		||||
        (ev/write conn " "))))
 | 
			
		||||
  (gccollect))
 | 
			
		||||
@@ -366,4 +369,10 @@
 | 
			
		||||
               (exec-slurp ;run janet "-e" "(print :hi)")))
 | 
			
		||||
          "exec-slurp 1"))
 | 
			
		||||
 | 
			
		||||
# valgrind-able check for #1337
 | 
			
		||||
(def superv (ev/chan 10))
 | 
			
		||||
(def f (ev/go |(ev/sleep 1e9) nil superv))
 | 
			
		||||
(ev/cancel f (gensym))
 | 
			
		||||
(ev/take superv)
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
 
 | 
			
		||||
@@ -96,11 +96,23 @@
 | 
			
		||||
  (assert (= (in buf 0) 0) "cryptorand doesn't overwrite buffer")
 | 
			
		||||
  (assert (= (length buf) 2) "cryptorand appends to buffer"))
 | 
			
		||||
 | 
			
		||||
(assert-no-error "realtime clock" (os/clock))
 | 
			
		||||
(assert-no-error "realtime clock" (os/clock nil))
 | 
			
		||||
(assert-no-error "realtime clock" (os/clock nil nil))
 | 
			
		||||
 | 
			
		||||
# 80db68210
 | 
			
		||||
(assert-no-error "realtime clock" (os/clock :realtime))
 | 
			
		||||
(assert-no-error "cputime clock" (os/clock :cputime))
 | 
			
		||||
(assert-no-error "monotonic clock" (os/clock :monotonic))
 | 
			
		||||
 | 
			
		||||
(assert-no-error "realtime clock double output" (os/clock nil :double))
 | 
			
		||||
(assert-no-error "realtime clock int output" (os/clock nil :int))
 | 
			
		||||
(assert-no-error "realtime clock tuple output" (os/clock nil :tuple))
 | 
			
		||||
 | 
			
		||||
(assert-error "invalid clock" (os/clock :a))
 | 
			
		||||
(assert-error "invalid output" (os/clock :realtime :b))
 | 
			
		||||
(assert-error "invalid clock and output" (os/clock :a :b))
 | 
			
		||||
 | 
			
		||||
(def before (os/clock :monotonic))
 | 
			
		||||
(def after (os/clock :monotonic))
 | 
			
		||||
(assert (>= after before) "monotonic clock is monotonic")
 | 
			
		||||
@@ -148,4 +160,3 @@
 | 
			
		||||
                               {:out dn :err dn})))
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -263,6 +263,9 @@
 | 
			
		||||
(marshpeg '(if-not "abcdf" 123))
 | 
			
		||||
(marshpeg ~(cmt "abcdf" ,identity))
 | 
			
		||||
(marshpeg '(group "abc"))
 | 
			
		||||
(marshpeg '(sub "abcdf" "abc"))
 | 
			
		||||
(marshpeg '(* (sub 1 1)))
 | 
			
		||||
(marshpeg '(split "," (+ "a" "b" "c")))
 | 
			
		||||
 | 
			
		||||
# Peg swallowing errors
 | 
			
		||||
# 159651117
 | 
			
		||||
@@ -660,5 +663,98 @@
 | 
			
		||||
  (peg/match '(if (not (* (constant 7) "a")) "hello") "hello")
 | 
			
		||||
  @[]) "peg if not")
 | 
			
		||||
 | 
			
		||||
(defn test [name peg input expected]
 | 
			
		||||
  (assert (deep= (peg/match peg input) expected) name))
 | 
			
		||||
 | 
			
		||||
(test "sub: matches the same input twice"
 | 
			
		||||
  ~(sub "abcd" "abc")
 | 
			
		||||
  "abcdef"
 | 
			
		||||
  @[])
 | 
			
		||||
 | 
			
		||||
(test "sub: second pattern cannot match more than the first pattern"
 | 
			
		||||
  ~(sub "abcd" "abcde")
 | 
			
		||||
  "abcdef"
 | 
			
		||||
  nil)
 | 
			
		||||
 | 
			
		||||
(test "sub: fails if first pattern fails"
 | 
			
		||||
  ~(sub "x" "abc")
 | 
			
		||||
  "abcdef"
 | 
			
		||||
  nil)
 | 
			
		||||
 | 
			
		||||
(test "sub: fails if second pattern fails"
 | 
			
		||||
  ~(sub "abc" "x")
 | 
			
		||||
  "abcdef"
 | 
			
		||||
  nil)
 | 
			
		||||
 | 
			
		||||
(test "sub: keeps captures from both patterns"
 | 
			
		||||
  ~(sub '"abcd" '"abc")
 | 
			
		||||
  "abcdef"
 | 
			
		||||
  @["abcd" "abc"])
 | 
			
		||||
 | 
			
		||||
(test "sub: second pattern can reference captures from first"
 | 
			
		||||
  ~(* (constant 5 :tag) (sub (capture "abc" :tag) (backref :tag)))
 | 
			
		||||
  "abcdef"
 | 
			
		||||
  @[5 "abc" "abc"])
 | 
			
		||||
 | 
			
		||||
(test "sub: second pattern can't see past what the first pattern matches"
 | 
			
		||||
  ~(sub "abc" (* "abc" -1))
 | 
			
		||||
  "abcdef"
 | 
			
		||||
  @[])
 | 
			
		||||
 | 
			
		||||
(test "sub: positions inside second match are still relative to the entire input"
 | 
			
		||||
  ~(* "one\ntw" (sub "o" (* ($) (line) (column))))
 | 
			
		||||
  "one\ntwo\nthree\n"
 | 
			
		||||
  @[6 2 3])
 | 
			
		||||
 | 
			
		||||
(test "sub: advances to the end of the first pattern's match"
 | 
			
		||||
  ~(* (sub "abc" "ab") "d")
 | 
			
		||||
  "abcdef"
 | 
			
		||||
  @[])
 | 
			
		||||
 | 
			
		||||
(test "split: basic functionality"
 | 
			
		||||
  ~(split "," '1)
 | 
			
		||||
  "a,b,c"
 | 
			
		||||
  @["a" "b" "c"])
 | 
			
		||||
 | 
			
		||||
(test "split: drops captures from separator pattern"
 | 
			
		||||
  ~(split '"," '1)
 | 
			
		||||
  "a,b,c"
 | 
			
		||||
  @["a" "b" "c"])
 | 
			
		||||
 | 
			
		||||
(test "split: can match empty subpatterns"
 | 
			
		||||
  ~(split "," ':w*)
 | 
			
		||||
  ",a,,bar,,,c,,"
 | 
			
		||||
  @["" "a" "" "bar" "" "" "c" "" ""])
 | 
			
		||||
 | 
			
		||||
(test "split: subpattern is limited to only text before the separator"
 | 
			
		||||
  ~(split "," '(to -1))
 | 
			
		||||
  "a,,bar,c"
 | 
			
		||||
  @["a" "" "bar" "c"])
 | 
			
		||||
 | 
			
		||||
(test "split: fails if any subpattern fails"
 | 
			
		||||
  ~(split "," '"a")
 | 
			
		||||
  "a,a,b"
 | 
			
		||||
  nil)
 | 
			
		||||
 | 
			
		||||
(test "split: separator does not have to match anything"
 | 
			
		||||
  ~(split "x" '(to -1))
 | 
			
		||||
  "a,a,b"
 | 
			
		||||
  @["a,a,b"])
 | 
			
		||||
 | 
			
		||||
(test "split: always consumes entire input"
 | 
			
		||||
  ~(split 1 '"")
 | 
			
		||||
  "abc"
 | 
			
		||||
  @["" "" "" ""])
 | 
			
		||||
 | 
			
		||||
(test "split: separator can be an arbitrary PEG"
 | 
			
		||||
  ~(split :s+ '(to -1))
 | 
			
		||||
  "a   b      c"
 | 
			
		||||
  @["a" "b" "c"])
 | 
			
		||||
 | 
			
		||||
(test "split: does not advance past the end of the input"
 | 
			
		||||
  ~(* (split "," ':w+) 0)
 | 
			
		||||
  "a,b,c"
 | 
			
		||||
  @["a" "b" "c"])
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -198,5 +198,9 @@
 | 
			
		||||
 | 
			
		||||
(assert (= (test) '(1 ())) "issue #919")
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
# Regression #1327
 | 
			
		||||
(def x "A")
 | 
			
		||||
(def x (if (= nil x) "B" x))
 | 
			
		||||
(assert (= x "A"))
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user