mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-29 06:37:41 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			ev-epoll-f
			...
			sockopt-bs
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | ed8d61e0f3 | ||
|   | 38bf2a5131 | 
| @@ -9,4 +9,3 @@ tasks: | ||||
|     gmake | ||||
|     gmake test | ||||
|     sudo gmake install | ||||
|     sudo gmake uninstall | ||||
|   | ||||
| @@ -11,7 +11,6 @@ tasks: | ||||
|     gmake test | ||||
|     doas gmake install | ||||
|     gmake test-install | ||||
|     doas gmake uninstall | ||||
| - meson_min: | | ||||
|     cd janet | ||||
|     meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false | ||||
| @@ -30,3 +29,4 @@ tasks: | ||||
|     ninja | ||||
|     ninja test | ||||
|     doas ninja install | ||||
|  | ||||
|   | ||||
							
								
								
									
										32
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @@ -57,35 +57,3 @@ jobs: | ||||
|       - name: Build the project | ||||
|         shell: cmd | ||||
|         run: make -j CC=gcc | ||||
|  | ||||
|   test-mingw-linux: | ||||
|     name: Build and test with Mingw on Linux + Wine | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@master | ||||
|       - name: Setup Mingw and wine | ||||
|         run: | | ||||
|           sudo dpkg --add-architecture i386 | ||||
|           sudo apt-get update | ||||
|           sudo apt-get install libstdc++6:i386 libgcc-s1:i386 | ||||
|           sudo apt-get install gcc-mingw-w64-x86-64-win32 wine wine32 wine64 | ||||
|       - name: Compile the project | ||||
|         run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine | ||||
|       - name: Test the project | ||||
|         run: make test UNAME=MINGW RUN=wine | ||||
|  | ||||
|   test-arm-linux: | ||||
|     name: Build and test ARM32 cross compilation | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Checkout the repository | ||||
|         uses: actions/checkout@master | ||||
|       - name: Setup qemu and cross compiler | ||||
|         run: | | ||||
|           sudo apt-get update | ||||
|           sudo apt-get install gcc-arm-linux-gnueabi qemu-user | ||||
|       - name: Compile the project | ||||
|         run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc  | ||||
|       - name: Test the project | ||||
|         run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test | ||||
|   | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -57,7 +57,6 @@ xxd.exe | ||||
| # VSCode | ||||
| .vs | ||||
| .clangd | ||||
| .cache | ||||
|  | ||||
| # Swap files | ||||
| *.swp | ||||
|   | ||||
							
								
								
									
										50
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -1,56 +1,6 @@ | ||||
| # Changelog | ||||
| All notable changes to this project will be documented in this file. | ||||
|  | ||||
| ## Unreleased - ??? | ||||
| - 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`. | ||||
| - Fix compiler bug with using the result of `(break x)` expression in some contexts. | ||||
| - Rework internal event loop code to be better behaved on Windows | ||||
| - Update meson build to work better on windows | ||||
|  | ||||
| ## 1.31.0 - 2023-09-17 | ||||
| - Report line and column when using `janet_dobytes` | ||||
| - Add `:unless` loop modifier | ||||
| - Allow calling `reverse` on generators. | ||||
| - Improve performance of a number of core functions including `partition`, `mean`, `keys`, `values`, `pairs`, `interleave`. | ||||
| - Add `lengthable?` | ||||
| - Add `os/sigaction` | ||||
| - Change `every?` and `any?` to behave like the functional versions of the `and` and `or` macros. | ||||
| - Fix bug with garbage collecting threaded abstract types. | ||||
| - Add `:signal` to the `sandbox` function to allow intercepting signals. | ||||
|  | ||||
| ## 1.30.0 - 2023-08-05 | ||||
| - Change indexing of `array/remove` to start from -1 at the end instead of -2. | ||||
| - Add new string escape sequences `\\a`, `\\b`, `\\?`, and `\\'`. | ||||
| - Fix bug with marshalling channels | ||||
| - Add `div` for floored division | ||||
| - Make `div` and `mod` variadic | ||||
| - Support `bnot` for integer types. | ||||
| - Define `(mod x 0)` as `x` | ||||
| - Add `ffi/pointer-cfunction` to convert pointers to cfunctions | ||||
|  | ||||
| ## 1.29.1 - 2023-06-19 | ||||
| - Add support for passing booleans to PEGs for "always" and "never" matching. | ||||
| - Allow dictionary types for `take` and `drop` | ||||
| - Fix bug with closing channels while other fibers were waiting on them - `ev/take`, `ev/give`, and `ev/select`  will now return the correct (documented) value when another fiber closes the channel. | ||||
| - Add `ffi/calling-conventions` to show all available calling conventions for FFI. | ||||
| - Add `net/setsockopt` | ||||
| - Add `signal` argument to `os/proc-kill` to send signals besides `SIGKILL` on Posix. | ||||
| - Add `source` argument to `os/clock` to get different time sources. | ||||
| - Various combinator functions now are variadic like `map` | ||||
| - Add `file/lines` to iterate over lines in a file lazily. | ||||
| - Reorganize test suite to be sorted by module rather than pseudo-randomly. | ||||
| - Add `*task-id*` | ||||
| - Add `env` argument to `fiber/new`. | ||||
| - Add `JANET_NO_AMALG` flag to Makefile to properly incremental builds | ||||
| - Optimize bytecode compiler to generate fewer instructions and improve loops. | ||||
| - Fix bug with `ev/gather` and hung fibers. | ||||
| - Add `os/isatty` | ||||
| - Add `has-key?` and `has-value?` | ||||
| - Make imperative arithmetic macros variadic | ||||
| - `ev/connect` now yields to the event loop instead of blocking while waiting for an ACK. | ||||
|  | ||||
| ## 1.28.0 - 2023-05-13 | ||||
| - Various bug fixes | ||||
| - Make nested short-fn's behave a bit more predictably (it is still not recommended to nest short-fns). | ||||
|   | ||||
							
								
								
									
										44
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								Makefile
									
									
									
									
									
								
							| @@ -39,8 +39,6 @@ JANET_PATH?=$(LIBDIR)/janet | ||||
| JANET_MANPATH?=$(PREFIX)/share/man/man1/ | ||||
| JANET_PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig | ||||
| JANET_DIST_DIR?=janet-dist | ||||
| JANET_BOOT_FLAGS:=. JANET_PATH '$(JANET_PATH)' | ||||
| JANET_TARGET_OBJECTS=build/janet.o build/shell.o | ||||
| JPM_TAG?=master | ||||
| DEBUGGER=gdb | ||||
| SONAME_SETTER=-Wl,-soname, | ||||
| @@ -48,21 +46,14 @@ SONAME_SETTER=-Wl,-soname, | ||||
| # For cross compilation | ||||
| HOSTCC?=$(CC) | ||||
| HOSTAR?=$(AR) | ||||
| # Symbols are (optionally) removed later, keep -g as default! | ||||
| CFLAGS?=-O2 -g | ||||
| CFLAGS?=-O2 | ||||
| LDFLAGS?=-rdynamic | ||||
| RUN:=$(RUN) | ||||
|  | ||||
| COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC | ||||
| BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 $(COMMON_CFLAGS) -g | ||||
| BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 -g $(COMMON_CFLAGS) | ||||
| BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS) | ||||
|  | ||||
| # Disable amalgamated build | ||||
| ifeq ($(JANET_NO_AMALG), 1) | ||||
| 	JANET_TARGET_OBJECTS+=$(patsubst src/%.c,build/%.bin.o,$(JANET_CORE_SOURCES)) | ||||
| 	JANET_BOOT_FLAGS+=image-only | ||||
| endif | ||||
|  | ||||
| # For installation | ||||
| LDCONFIG:=ldconfig "$(LIBDIR)" | ||||
|  | ||||
| @@ -97,7 +88,7 @@ ifeq ($(findstring MINGW,$(UNAME)), MINGW) | ||||
| 	JANET_BOOT:=$(JANET_BOOT).exe | ||||
| endif | ||||
|  | ||||
| $(shell mkdir -p build/core build/c build/boot build/mainclient) | ||||
| $(shell mkdir -p build/core build/c build/boot) | ||||
| all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h | ||||
|  | ||||
| ###################### | ||||
| @@ -181,24 +172,17 @@ $(JANET_BOOT): $(JANET_BOOT_OBJECTS) | ||||
|  | ||||
| # Now the reason we bootstrap in the first place | ||||
| build/c/janet.c: $(JANET_BOOT) src/boot/boot.janet | ||||
| 	$(RUN) $(JANET_BOOT) $(JANET_BOOT_FLAGS) > $@ | ||||
| 	$(RUN) $(JANET_BOOT) . JANET_PATH '$(JANET_PATH)' > $@ | ||||
| 	cksum $@ | ||||
|  | ||||
| ################## | ||||
| ##### Quicky ##### | ||||
| ################## | ||||
|  | ||||
| build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile | ||||
| 	$(HOSTCC) $(BUILD_CFLAGS) -o $@ -c $< | ||||
|  | ||||
| ######################## | ||||
| ##### Amalgamation ##### | ||||
| ######################## | ||||
|  | ||||
| ifeq ($(UNAME), Darwin) | ||||
| SONAME=libjanet.1.31.dylib | ||||
| SONAME=libjanet.1.28.dylib | ||||
| else | ||||
| SONAME=libjanet.so.1.31 | ||||
| SONAME=libjanet.so.1.28 | ||||
| endif | ||||
|  | ||||
| build/c/shell.c: src/mainclient/shell.c | ||||
| @@ -216,13 +200,13 @@ build/janet.o: build/c/janet.c $(JANETCONF_HEADER) src/include/janet.h | ||||
| build/shell.o: build/c/shell.c $(JANETCONF_HEADER) src/include/janet.h | ||||
| 	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@ | ||||
|  | ||||
| $(JANET_TARGET): $(JANET_TARGET_OBJECTS) | ||||
| $(JANET_TARGET): build/janet.o build/shell.o | ||||
| 	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS) | ||||
|  | ||||
| $(JANET_LIBRARY): $(JANET_TARGET_OBJECTS) | ||||
| $(JANET_LIBRARY): build/janet.o build/shell.o | ||||
| 	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS) | ||||
|  | ||||
| $(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS) | ||||
| $(JANET_STATIC_LIBRARY): build/janet.o build/shell.o | ||||
| 	$(HOSTAR) rcs $@ $^ | ||||
|  | ||||
| ################### | ||||
| @@ -239,7 +223,7 @@ repl: $(JANET_TARGET) | ||||
| debug: $(JANET_TARGET) | ||||
| 	$(DEBUGGER) ./$(JANET_TARGET) | ||||
|  | ||||
| VALGRIND_COMMAND=valgrind --leak-check=full --quiet | ||||
| VALGRIND_COMMAND=valgrind --leak-check=full | ||||
|  | ||||
| valgrind: $(JANET_TARGET) | ||||
| 	$(VALGRIND_COMMAND) ./$(JANET_TARGET) | ||||
| @@ -267,7 +251,6 @@ build/janet-%.tar.gz: $(JANET_TARGET) \ | ||||
| 	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/ | ||||
| 	strip -x -S 'build/$(JANET_DIST_DIR)/bin/janet' | ||||
| 	mkdir -p build/$(JANET_DIST_DIR)/include | ||||
| 	cp build/janet.h build/$(JANET_DIST_DIR)/include/ | ||||
| 	mkdir -p build/$(JANET_DIST_DIR)/lib/ | ||||
| @@ -310,10 +293,9 @@ build/janet.pc: $(JANET_TARGET) | ||||
| install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h | ||||
| 	mkdir -p '$(DESTDIR)$(BINDIR)' | ||||
| 	cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet' | ||||
| 	strip -x -S '$(DESTDIR)$(BINDIR)/janet' | ||||
| 	mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet' | ||||
| 	cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet' | ||||
| 	ln -sf ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' | ||||
| 	ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' || true #fixme bsd | ||||
| 	mkdir -p '$(DESTDIR)$(JANET_PATH)' | ||||
| 	mkdir -p '$(DESTDIR)$(LIBDIR)' | ||||
| 	if test $(UNAME) = Darwin ; then \ | ||||
| @@ -359,14 +341,14 @@ uninstall: | ||||
| ################# | ||||
|  | ||||
| format: | ||||
| 	sh tools/format.sh | ||||
| 	tools/format.sh | ||||
|  | ||||
| grammar: build/janet.tmLanguage | ||||
| build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET) | ||||
| 	$(RUN) $(JANET_TARGET) $< > $@ | ||||
|  | ||||
| compile-commands: | ||||
| 	# Requires pip install compiledb | ||||
| 	# Requires pip install copmiledb | ||||
| 	compiledb make | ||||
|  | ||||
| clean: | ||||
|   | ||||
							
								
								
									
										155
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										155
									
								
								README.md
									
									
									
									
									
								
							| @@ -6,12 +6,10 @@ | ||||
|  | ||||
| <img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left"> | ||||
|  | ||||
| **Janet** is a programming language for system scripting, expressive automation, and | ||||
| extending programs written in C or C++ with user scripting capabilities. | ||||
|  | ||||
| Janet makes a good system scripting language, or a language to embed in other programs. | ||||
| It's like Lua and GNU Guile in that regard. It has more built-in functionality and a richer core language than | ||||
| Lua, but smaller than GNU Guile or Python. However, it is much easier to embed and port than Python or Guile. | ||||
| **Janet** is a functional and imperative programming language and bytecode interpreter. It is a | ||||
| Lisp-like language, but lists are replaced | ||||
| by other data structures (arrays, tables (hash table), struct (immutable hash table), tuples). | ||||
| The language also supports bridging to native code written in C, meta-programming with macros, and bytecode assembly. | ||||
|  | ||||
| There is a REPL for trying out the language, as well as the ability | ||||
| to run script files. This client program is separate from the core runtime, so | ||||
| @@ -23,109 +21,38 @@ If you'd like to financially support the ongoing development of Janet, consider | ||||
|  | ||||
| <br> | ||||
|  | ||||
| ## Examples | ||||
| ## Use Cases | ||||
|  | ||||
| See the examples directory for all provided example programs. | ||||
| Janet makes a good system scripting language, or a language to embed in other programs. | ||||
| It's like Lua and Guile in that regard. It has more built-in functionality and a richer core language than | ||||
| Lua, but smaller than GNU Guile or Python. | ||||
|  | ||||
| ### Game of Life | ||||
| ## Features | ||||
|  | ||||
| ```janet | ||||
| # John Conway's Game of Life | ||||
|  | ||||
| (def- window | ||||
|   (seq [x :range [-1 2] | ||||
|          y :range [-1 2] | ||||
|          :when (not (and (zero? x) (zero? y)))] | ||||
|        [x y])) | ||||
|  | ||||
| (defn- neighbors | ||||
|   [[x y]] | ||||
|   (map (fn [[x1 y1]] [(+ x x1) (+ y y1)]) window)) | ||||
|  | ||||
| (defn tick | ||||
|   "Get the next state in the Game Of Life." | ||||
|   [state] | ||||
|   (def cell-set (frequencies state)) | ||||
|   (def neighbor-set (frequencies (mapcat neighbors state))) | ||||
|   (seq [coord :keys neighbor-set | ||||
|          :let [count (get neighbor-set coord)] | ||||
|          :when (or (= count 3) (and (get cell-set coord) (= count 2)))] | ||||
|       coord)) | ||||
|  | ||||
| (defn draw | ||||
|   "Draw cells in the game of life from (x1, y1) to (x2, y2)" | ||||
|   [state x1 y1 x2 y2] | ||||
|   (def cellset @{}) | ||||
|   (each cell state (put cellset cell true)) | ||||
|   (loop [x :range [x1 (+ 1 x2)] | ||||
|          :after (print) | ||||
|          y :range [y1 (+ 1 y2)]] | ||||
|     (file/write stdout (if (get cellset [x y]) "X " ". "))) | ||||
|   (print)) | ||||
|  | ||||
| # Print the first 20 generations of a glider | ||||
| (var *state* '[(0 0) (-1 0) (1 0) (1 1) (0 2)]) | ||||
| (for i 0 20 | ||||
|   (print "generation " i) | ||||
|   (draw *state* -7 -7 7 7) | ||||
|   (set *state* (tick *state*))) | ||||
| ``` | ||||
|  | ||||
| ### TCP Echo Server | ||||
|  | ||||
| ```janet | ||||
| # A simple TCP echo server using the built-in socket networking and event loop. | ||||
|  | ||||
| (defn handler | ||||
|   "Simple handler for connections." | ||||
|   [stream] | ||||
|   (defer (:close stream) | ||||
|     (def id (gensym)) | ||||
|     (def b @"") | ||||
|     (print "Connection " id "!") | ||||
|     (while (:read stream 1024 b) | ||||
|       (printf " %v -> %v" id b) | ||||
|       (:write stream b) | ||||
|       (buffer/clear b)) | ||||
|     (printf "Done %v!" id) | ||||
|     (ev/sleep 0.5))) | ||||
|  | ||||
| (net/server "127.0.0.1" "8000" handler) | ||||
| ``` | ||||
|  | ||||
| ### Windows FFI Hello, World! | ||||
|  | ||||
| ```janet | ||||
| # Use the FFI to popup a Windows message box - no C required | ||||
|  | ||||
| (ffi/context "user32.dll") | ||||
|  | ||||
| (ffi/defbind MessageBoxA :int | ||||
|   [w :ptr text :string cap :string typ :int]) | ||||
|  | ||||
| (MessageBoxA nil "Hello, World!" "Test" 0) | ||||
| ``` | ||||
|  | ||||
| ## Language Features | ||||
|  | ||||
| * 600+ functions and macros in the core library | ||||
| * Built-in socket networking, threading, subprocesses, and file system functions. | ||||
| * Parsing Expression Grammars (PEG) engine as a more robust Regex alternative | ||||
| * Macros and compile-time computation | ||||
| * Per-thread event loop for efficient IO (epoll/IOCP/kqueue) | ||||
| * First-class green threads (continuations) as well as OS threads | ||||
| * Erlang-style supervision trees that integrate with the event loop | ||||
| * Configurable at build time - turn features on or off for a smaller or more featureful build | ||||
| * Minimal setup - one binary and you are good to go! | ||||
| * First-class closures | ||||
| * Garbage collection | ||||
| * Distributed as janet.c and janet.h for embedding into a larger program. | ||||
| * First-class green threads (continuations) | ||||
| * Python-style generators (implemented as a plain macro) | ||||
| * Mutable and immutable arrays (array/tuple) | ||||
| * Mutable and immutable hashtables (table/struct) | ||||
| * Mutable and immutable strings (buffer/string) | ||||
| * Tail recursion | ||||
| * Interface with C functions and dynamically load plugins ("natives"). | ||||
| * Built-in C FFI for when the native bindings are too much work | ||||
| * REPL development with debugger and inspectable runtime | ||||
| * Macros | ||||
| * Multithreading | ||||
| * Per-thread event loop for efficient evented IO | ||||
| * Bytecode interpreter with an assembly interface, as well as bytecode verification | ||||
| * Tail-call optimization | ||||
| * Direct interop with C via abstract types and C functions | ||||
| * Dynamically load C libraries | ||||
| * Functional and imperative standard library | ||||
| * Lexical scoping | ||||
| * Imperative programming as well as functional | ||||
| * REPL | ||||
| * Parsing Expression Grammars built into the core library | ||||
| * 400+ functions and macros in the core library | ||||
| * Embedding Janet in other programs | ||||
| * Interactive environment with detailed stack traces | ||||
|  | ||||
| ## Documentation | ||||
|  | ||||
| @@ -313,6 +240,10 @@ there is no need for dynamic modules, add the define | ||||
|  | ||||
| See the [Embedding Section](https://janet-lang.org/capi/embedding.html) on the website for more information. | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
| See the examples directory for some example Janet code. | ||||
|  | ||||
| ## Discussion | ||||
|  | ||||
| Feel free to ask questions and join the discussion on the [Janet Gitter channel](https://gitter.im/janet-language/community). | ||||
| @@ -320,20 +251,6 @@ Gitter provides Matrix and IRC bridges as well. | ||||
|  | ||||
| ## FAQ | ||||
|  | ||||
| ### How fast is it? | ||||
|  | ||||
| It is about the same speed as most interpreted languages without a JIT compiler. Tight, critical | ||||
| loops should probably be written in C or C++ . Programs tend to be a bit faster than | ||||
| they would be in a language like Python due to the discouragement of slow Object-Oriented abstraction | ||||
| with lots of hash-table lookups, and making late-binding explicit. All values are boxed in an 8-byte | ||||
| representation by default and allocated on the heap, with the exception of numbers, nils and booleans. The | ||||
| PEG engine is a specialized interpreter that can efficiently process string and buffer data. | ||||
|  | ||||
| The GC is simple and stop-the-world, but GC knobs are exposed in the core library and separate threads | ||||
| have isolated heaps and garbage collectors. Data that is shared between threads is reference counted. | ||||
|  | ||||
| YMMV. | ||||
|  | ||||
| ### Where is (favorite feature from other language)? | ||||
|  | ||||
| It may exist, it may not. If you want to propose a major language feature, go ahead and open an issue, but | ||||
| @@ -351,7 +268,7 @@ Nope. There are no cons cells here. | ||||
| ### Is this a Clojure port? | ||||
|  | ||||
| No. It's similar to Clojure superficially because I like Lisps and I like the aesthetics. | ||||
| Internally, Janet is not at all like Clojure, Scheme, or Common Lisp. | ||||
| Internally, Janet is not at all like Clojure. | ||||
|  | ||||
| ### Are the immutable data structures (tuples and structs) implemented as hash tries? | ||||
|  | ||||
| @@ -380,14 +297,6 @@ Usually, one of a few reasons: | ||||
|   without feeling "bolted on", especially when compared to ALGOL-like languages. Adding features | ||||
|   to the core also makes it a bit more difficult to keep Janet maximally portable. | ||||
|  | ||||
| ### Can I bind to Rust/Zig/Go/Java/Nim/C++/D/Pascal/Fortran/Odin/Jai/(Some new "Systems" Programming Language)? | ||||
|  | ||||
| Probably, if that language has a good interface with C. But the programmer may need to do | ||||
| some extra work to map Janet's internal memory model to that of the bound language. Janet | ||||
| also uses `setjmp`/`longjmp` for non-local returns internally. This | ||||
| approach is out of favor with many programmers now and doesn't always play well with other languages | ||||
| that have exceptions or stack-unwinding. | ||||
|  | ||||
| ### Why is my terminal spitting out junk when I run the REPL? | ||||
|  | ||||
| Make sure your terminal supports ANSI escape codes. Most modern terminals will | ||||
|   | ||||
| @@ -41,32 +41,32 @@ if not exist build\boot mkdir build\boot | ||||
| @rem Build the bootstrap interpreter | ||||
| for %%f in (src\core\*.c) do ( | ||||
|     %JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f | ||||
|     @if not errorlevel 0 goto :BUILDFAIL | ||||
|     @if errorlevel 1 goto :BUILDFAIL | ||||
| ) | ||||
| for %%f in (src\boot\*.c) do ( | ||||
|     %JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f | ||||
|     @if not errorlevel 0 goto :BUILDFAIL | ||||
|     @if errorlevel 1 goto :BUILDFAIL | ||||
| ) | ||||
| %JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj | ||||
| @if not errorlevel 0 goto :BUILDFAIL | ||||
| @if errorlevel 1 goto :BUILDFAIL | ||||
| build\janet_boot . > build\c\janet.c | ||||
|  | ||||
| @rem Build the sources | ||||
| %JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c | ||||
| @if not errorlevel 0 goto :BUILDFAIL | ||||
| @if errorlevel 1 goto :BUILDFAIL | ||||
| %JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c | ||||
| @if not errorlevel 0 goto :BUILDFAIL | ||||
| @if errorlevel 1 goto :BUILDFAIL | ||||
|  | ||||
| @rem Build the resources | ||||
| rc /nologo /fobuild\janet_win.res janet_win.rc | ||||
|  | ||||
| @rem Link everything to main client | ||||
| %JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res | ||||
| @if not errorlevel 0 goto :BUILDFAIL | ||||
| @if errorlevel 1 goto :BUILDFAIL | ||||
|  | ||||
| @rem Build static library (libjanet.lib) | ||||
| @rem Build static library (libjanet.a) | ||||
| %JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj | ||||
| @if not errorlevel 0 goto :BUILDFAIL | ||||
| @if errorlevel 1 goto :BUILDFAIL | ||||
|  | ||||
| echo === Successfully built janet.exe for Windows === | ||||
| echo === Run 'build_win test' to run tests. == | ||||
| @@ -98,7 +98,7 @@ exit /b 0 | ||||
| :TEST | ||||
| for %%f in (test/suite*.janet) do ( | ||||
|     janet.exe test\%%f | ||||
|     @if not errorlevel 0 goto TESTFAIL | ||||
|     @if errorlevel 1 goto TESTFAIL | ||||
| ) | ||||
| exit /b 0 | ||||
|  | ||||
| @@ -117,7 +117,6 @@ copy README.md dist\README.md | ||||
|  | ||||
| copy janet.lib dist\janet.lib | ||||
| copy janet.exp dist\janet.exp | ||||
| copy janet.def dist\janet.def | ||||
|  | ||||
| janet.exe tools\patch-header.janet src\include\janet.h src\conf\janetconf.h build\janet.h | ||||
| copy build\janet.h dist\janet.h | ||||
|   | ||||
| @@ -78,6 +78,7 @@ double double_lots( | ||||
|     return i + j; | ||||
| } | ||||
|  | ||||
|  | ||||
| EXPORTER | ||||
| double double_lots_2( | ||||
|     double a, | ||||
| @@ -203,3 +204,5 @@ EXPORTER | ||||
| int sixints_fn_3(SixInts s, int x) { | ||||
|     return x + s.u + s.v + s.w + s.x + s.y + s.z; | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,41 +0,0 @@ | ||||
| ### | ||||
| ### Usage: janet examples/sigaction.janet 1|2|3|4 & | ||||
| ### | ||||
| ### Then at shell: kill -s SIGTERM $! | ||||
| ### | ||||
|  | ||||
| (defn action | ||||
|   [] | ||||
|   (print "Handled SIGTERM!") | ||||
|   (flush) | ||||
|   (os/exit 1)) | ||||
|  | ||||
| (defn main1 | ||||
|   [] | ||||
|   (os/sigaction :term action true) | ||||
|   (forever)) | ||||
|  | ||||
| (defn main2 | ||||
|   [] | ||||
|   (os/sigaction :term action) | ||||
|   (forever)) | ||||
|  | ||||
| (defn main3 | ||||
|   [] | ||||
|   (os/sigaction :term action true) | ||||
|   (forever (ev/sleep math/inf))) | ||||
|  | ||||
| (defn main4 | ||||
|   [] | ||||
|   (os/sigaction :term action) | ||||
|   (forever (ev/sleep math/inf))) | ||||
|  | ||||
| (defn main | ||||
|   [& args] | ||||
|   (def which (scan-number (get args 1 "1"))) | ||||
|   (case which | ||||
|     1 (main1) # should work | ||||
|     2 (main2) # will not work | ||||
|     3 (main3) # should work | ||||
|     4 (main4) # should work | ||||
|     (error "bad main"))) | ||||
| @@ -1,20 +0,0 @@ | ||||
| (def weak-k (table/weak-keys 10)) | ||||
| (def weak-v (table/weak-values 10)) | ||||
| (def weak-kv (table/weak 10)) | ||||
|  | ||||
| (put weak-kv (gensym) 10) | ||||
| (put weak-kv :hello :world) | ||||
| (put weak-k :abc123zz77asda :stuff) | ||||
| (put weak-k true :abc123zz77asda) | ||||
| (put weak-k :zyzzyz false) | ||||
| (put weak-v (gensym) 10) | ||||
| (put weak-v 20 (gensym)) | ||||
| (print "before gc") | ||||
| (tracev weak-k) | ||||
| (tracev weak-v) | ||||
| (tracev weak-kv) | ||||
| (gccollect) | ||||
| (print "after gc") | ||||
| (tracev weak-k) | ||||
| (tracev weak-v) | ||||
| (tracev weak-kv) | ||||
							
								
								
									
										65
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								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.28.0') | ||||
|  | ||||
| # Global settings | ||||
| janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet') | ||||
| @@ -169,7 +169,7 @@ janet_boot = executable('janet-boot', core_src, boot_src, | ||||
|  | ||||
| # Build janet.c | ||||
| janetc = custom_target('janetc', | ||||
|   input : [janet_boot, 'src/boot/boot.janet'], | ||||
|   input : [janet_boot], | ||||
|   output : 'janet.c', | ||||
|   capture : true, | ||||
|   command : [ | ||||
| @@ -182,30 +182,23 @@ if not get_option('single_threaded') | ||||
|   janet_dependencies += thread_dep | ||||
| endif | ||||
|  | ||||
| if cc.has_argument('-fvisibility=hidden') | ||||
|   lib_cflags = ['-fvisibility=hidden'] | ||||
| else | ||||
|   lib_cflags = [] | ||||
| endif | ||||
| libjanet = library('janet', janetc, | ||||
|   include_directories : incdir, | ||||
|   dependencies : janet_dependencies, | ||||
|   version: meson.project_version(), | ||||
|   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') | ||||
|   extra_cflags = ['-fvisibility=hidden', '-DJANET_DLL_IMPORT'] | ||||
|   extra_cflags = ['-fvisibility=hidden'] | ||||
| else | ||||
|   extra_cflags = ['-DJANET_DLL_IMPORT'] | ||||
|   extra_cflags = [] | ||||
| endif | ||||
| janet_mainclient = executable('janet', mainclient_src, | ||||
| janet_mainclient = executable('janet', janetc, mainclient_src, | ||||
|   include_directories : incdir, | ||||
|   dependencies : janet_dependencies, | ||||
|   link_with: [libjanet], | ||||
|   c_args : extra_cflags, | ||||
|   install : true) | ||||
|  | ||||
| @@ -234,34 +227,21 @@ docs = custom_target('docs', | ||||
|  | ||||
| # Tests | ||||
| test_files = [ | ||||
|   'test/suite-array.janet', | ||||
|   'test/suite-asm.janet', | ||||
|   'test/suite-boot.janet', | ||||
|   'test/suite-buffer.janet', | ||||
|   'test/suite-capi.janet', | ||||
|   'test/suite-cfuns.janet', | ||||
|   'test/suite-compile.janet', | ||||
|   'test/suite-corelib.janet', | ||||
|   'test/suite-debug.janet', | ||||
|   'test/suite-ev.janet', | ||||
|   'test/suite-ffi.janet', | ||||
|   'test/suite-inttypes.janet', | ||||
|   'test/suite-io.janet', | ||||
|   'test/suite-marsh.janet', | ||||
|   'test/suite-math.janet', | ||||
|   'test/suite-os.janet', | ||||
|   'test/suite-parse.janet', | ||||
|   'test/suite-peg.janet', | ||||
|   'test/suite-pp.janet', | ||||
|   'test/suite-specials.janet', | ||||
|   'test/suite-string.janet', | ||||
|   'test/suite-strtod.janet', | ||||
|   'test/suite-struct.janet', | ||||
|   'test/suite-symcache.janet', | ||||
|   'test/suite-table.janet', | ||||
|   'test/suite-unknown.janet', | ||||
|   'test/suite-value.janet', | ||||
|   'test/suite-vm.janet' | ||||
|   'test/suite0000.janet', | ||||
|   'test/suite0001.janet', | ||||
|   'test/suite0002.janet', | ||||
|   'test/suite0003.janet', | ||||
|   'test/suite0004.janet', | ||||
|   'test/suite0005.janet', | ||||
|   'test/suite0006.janet', | ||||
|   'test/suite0007.janet', | ||||
|   'test/suite0008.janet', | ||||
|   'test/suite0009.janet', | ||||
|   'test/suite0010.janet', | ||||
|   'test/suite0011.janet', | ||||
|   'test/suite0012.janet', | ||||
|   'test/suite0013.janet', | ||||
|   'test/suite0014.janet' | ||||
| ] | ||||
| foreach t : test_files | ||||
|   test(t, janet_nativeclient, args : files([t]), workdir : meson.current_source_dir()) | ||||
| @@ -288,12 +268,11 @@ patched_janet = custom_target('patched-janeth', | ||||
|   install : true, | ||||
|   install_dir : join_paths(get_option('includedir'), 'janet'), | ||||
|   build_by_default : true, | ||||
|   output : ['janet_' + meson.project_version() + '.h'], | ||||
|   output : ['janet.h'], | ||||
|   command : [janet_nativeclient, '@INPUT@', '@OUTPUT@']) | ||||
|  | ||||
| # Create a version of the janet.h header that matches what jpm often expects | ||||
| if meson.version().version_compare('>=0.61') | ||||
|   install_symlink('janet.h', pointing_to: 'janet/janet_' + meson.project_version() + '.h', install_dir: get_option('includedir')) | ||||
|   install_symlink('janet.h', pointing_to: 'janet_' + meson.project_version() + '.h', install_dir: join_paths(get_option('includedir'), 'janet')) | ||||
|   install_symlink('janet.h', pointing_to: 'janet/janet.h', install_dir: get_option('includedir')) | ||||
| endif | ||||
|  | ||||
|   | ||||
| @@ -18,7 +18,7 @@ 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('interpreter_interrupt', type : 'boolean', value : true) | ||||
| option('interpreter_interrupt', type : 'boolean', value : false) | ||||
| option('ffi', type : 'boolean', value : true) | ||||
| option('ffi_jit', type : 'boolean', value : true) | ||||
|  | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -70,5 +70,6 @@ int system_test() { | ||||
|  | ||||
|     assert(janet_equals(tuple1, tuple2)); | ||||
|  | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -4,10 +4,10 @@ | ||||
| #define JANETCONF_H | ||||
|  | ||||
| #define JANET_VERSION_MAJOR 1 | ||||
| #define JANET_VERSION_MINOR 31 | ||||
| #define JANET_VERSION_MINOR 28 | ||||
| #define JANET_VERSION_PATCH 0 | ||||
| #define JANET_VERSION_EXTRA "" | ||||
| #define JANET_VERSION "1.31.0" | ||||
| #define JANET_VERSION_EXTRA "-dev" | ||||
| #define JANET_VERSION "1.28.0-dev" | ||||
|  | ||||
| /* #define JANET_BUILD "local" */ | ||||
|  | ||||
|   | ||||
| @@ -97,6 +97,14 @@ size_t janet_os_rwlock_size(void) { | ||||
|     return sizeof(void *); | ||||
| } | ||||
|  | ||||
| static int32_t janet_incref(JanetAbstractHead *ab) { | ||||
|     return InterlockedIncrement((LONG volatile *) &ab->gc.data.refcount); | ||||
| } | ||||
|  | ||||
| static int32_t janet_decref(JanetAbstractHead *ab) { | ||||
|     return InterlockedDecrement((LONG volatile *) &ab->gc.data.refcount); | ||||
| } | ||||
|  | ||||
| void janet_os_mutex_init(JanetOSMutex *mutex) { | ||||
|     InitializeCriticalSection((CRITICAL_SECTION *) mutex); | ||||
| } | ||||
| @@ -149,6 +157,14 @@ size_t janet_os_rwlock_size(void) { | ||||
|     return sizeof(pthread_rwlock_t); | ||||
| } | ||||
|  | ||||
| static int32_t janet_incref(JanetAbstractHead *ab) { | ||||
|     return __atomic_add_fetch(&ab->gc.data.refcount, 1, __ATOMIC_RELAXED); | ||||
| } | ||||
|  | ||||
| static int32_t janet_decref(JanetAbstractHead *ab) { | ||||
|     return __atomic_add_fetch(&ab->gc.data.refcount, -1, __ATOMIC_RELAXED); | ||||
| } | ||||
|  | ||||
| void janet_os_mutex_init(JanetOSMutex *mutex) { | ||||
|     pthread_mutexattr_t attr; | ||||
|     pthread_mutexattr_init(&attr); | ||||
| @@ -196,11 +212,11 @@ void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) { | ||||
| #endif | ||||
|  | ||||
| int32_t janet_abstract_incref(void *abst) { | ||||
|     return janet_atomic_inc(&janet_abstract_head(abst)->gc.data.refcount); | ||||
|     return janet_incref(janet_abstract_head(abst)); | ||||
| } | ||||
|  | ||||
| int32_t janet_abstract_decref(void *abst) { | ||||
|     return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount); | ||||
|     return janet_decref(janet_abstract_head(abst)); | ||||
| } | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -30,7 +30,9 @@ | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| static void janet_array_impl(JanetArray *array, int32_t capacity) { | ||||
| /* Creates a new array */ | ||||
| JanetArray *janet_array(int32_t capacity) { | ||||
|     JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray)); | ||||
|     Janet *data = NULL; | ||||
|     if (capacity > 0) { | ||||
|         janet_vm.next_collection += capacity * sizeof(Janet); | ||||
| @@ -42,19 +44,6 @@ static void janet_array_impl(JanetArray *array, int32_t capacity) { | ||||
|     array->count = 0; | ||||
|     array->capacity = capacity; | ||||
|     array->data = data; | ||||
| } | ||||
|  | ||||
| /* Creates a new array */ | ||||
| JanetArray *janet_array(int32_t capacity) { | ||||
|     JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray)); | ||||
|     janet_array_impl(array, capacity); | ||||
|     return array; | ||||
| } | ||||
|  | ||||
| /* Creates a new array with weak references */ | ||||
| JanetArray *janet_array_weak(int32_t capacity) { | ||||
|     JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY_WEAK, sizeof(JanetArray)); | ||||
|     janet_array_impl(array, capacity); | ||||
|     return array; | ||||
| } | ||||
|  | ||||
| @@ -143,15 +132,6 @@ JANET_CORE_FN(cfun_array_new, | ||||
|     return janet_wrap_array(array); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_array_weak, | ||||
|               "(array/weak capacity)", | ||||
|               "Creates a new empty array with a pre-allocated capacity and support for weak references. Similar to `array/new`.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     int32_t cap = janet_getinteger(argv, 0); | ||||
|     JanetArray *array = janet_array_weak(cap); | ||||
|     return janet_wrap_array(array); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_array_new_filled, | ||||
|               "(array/new-filled count &opt value)", | ||||
|               "Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") { | ||||
| @@ -197,8 +177,8 @@ JANET_CORE_FN(cfun_array_peek, | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_array_push, | ||||
|               "(array/push arr & xs)", | ||||
|               "Push all the elements of xs to the end of an array. Modifies the input array and returns it.") { | ||||
|               "(array/push arr x)", | ||||
|               "Insert an element in the end of an array. Modifies the input array and returns it.") { | ||||
|     janet_arity(argc, 1, -1); | ||||
|     JanetArray *array = janet_getarray(argv, 0); | ||||
|     if (INT32_MAX - argc + 1 <= array->count) { | ||||
| @@ -231,7 +211,7 @@ JANET_CORE_FN(cfun_array_slice, | ||||
|               "Takes a slice of array or tuple from `start` to `end`. The range is half open, " | ||||
|               "[start, end). Indexes can also be negative, indicating indexing from the " | ||||
|               "end of the array. By default, `start` is 0 and `end` is the length of the array. " | ||||
|               "Note that if the range is negative, it is taken as (start, end] to allow a full " | ||||
|               "Note that index -1 is synonymous with index `(length arrtup)` to allow a full " | ||||
|               "negative slice range. Returns a new array.") { | ||||
|     JanetView view = janet_getindexed(argv, 0); | ||||
|     JanetRange range = janet_getslice(argc, argv); | ||||
| @@ -279,8 +259,8 @@ JANET_CORE_FN(cfun_array_insert, | ||||
|               "(array/insert arr at & xs)", | ||||
|               "Insert all `xs` into array `arr` at index `at`. `at` should be an integer between " | ||||
|               "0 and the length of the array. A negative value for `at` will index backwards from " | ||||
|               "the end of the array, inserting after the index such that inserting at -1 appends to " | ||||
|               "the array. Returns the array.") { | ||||
|               "the end of the array, such that inserting at -1 appends to the array. " | ||||
|               "Returns the array.") { | ||||
|     size_t chunksize, restsize; | ||||
|     janet_arity(argc, 2, -1); | ||||
|     JanetArray *array = janet_getarray(argv, 0); | ||||
| @@ -317,7 +297,7 @@ JANET_CORE_FN(cfun_array_remove, | ||||
|     int32_t at = janet_getinteger(argv, 1); | ||||
|     int32_t n = 1; | ||||
|     if (at < 0) { | ||||
|         at = array->count + at; | ||||
|         at = array->count + at + 1; | ||||
|     } | ||||
|     if (at < 0 || at > array->count) | ||||
|         janet_panicf("removal index %d out of range [0,%d]", at, array->count); | ||||
| @@ -372,7 +352,6 @@ JANET_CORE_FN(cfun_array_clear, | ||||
| void janet_lib_array(JanetTable *env) { | ||||
|     JanetRegExt array_cfuns[] = { | ||||
|         JANET_CORE_REG("array/new", cfun_array_new), | ||||
|         JANET_CORE_REG("array/weak", cfun_array_weak), | ||||
|         JANET_CORE_REG("array/new-filled", cfun_array_new_filled), | ||||
|         JANET_CORE_REG("array/fill", cfun_array_fill), | ||||
|         JANET_CORE_REG("array/pop", cfun_array_pop), | ||||
|   | ||||
| @@ -75,7 +75,6 @@ static const JanetInstructionDef janet_ops[] = { | ||||
|     {"cmp", JOP_COMPARE}, | ||||
|     {"cncl", JOP_CANCEL}, | ||||
|     {"div", JOP_DIVIDE}, | ||||
|     {"divf", JOP_DIVIDE_FLOOR}, | ||||
|     {"divim", JOP_DIVIDE_IMMEDIATE}, | ||||
|     {"eq", JOP_EQUALS}, | ||||
|     {"eqim", JOP_EQUALS_IMMEDIATE}, | ||||
| @@ -138,7 +137,6 @@ static const JanetInstructionDef janet_ops[] = { | ||||
|     {"sru", JOP_SHIFT_RIGHT_UNSIGNED}, | ||||
|     {"sruim", JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE}, | ||||
|     {"sub", JOP_SUBTRACT}, | ||||
|     {"subim", JOP_SUBTRACT_IMMEDIATE}, | ||||
|     {"tcall", JOP_TAILCALL}, | ||||
|     {"tchck", JOP_TYPECHECK} | ||||
| }; | ||||
| @@ -951,6 +949,7 @@ static Janet janet_disasm_symbolslots(JanetFuncDef *def) { | ||||
|     return janet_wrap_array(symbolslots); | ||||
| } | ||||
|  | ||||
|  | ||||
| static Janet janet_disasm_bytecode(JanetFuncDef *def) { | ||||
|     JanetArray *bcode = janet_array(def->bytecode_length); | ||||
|     for (int32_t i = 0; i < def->bytecode_length; i++) { | ||||
|   | ||||
| @@ -221,20 +221,6 @@ JANET_CORE_FN(cfun_buffer_new_filled, | ||||
|     return janet_wrap_buffer(buffer); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_frombytes, | ||||
|               "(buffer/from-bytes & byte-vals)", | ||||
|               "Creates a buffer from integer parameters with byte values. All integers " | ||||
|               "will be coerced to the range of 1 byte 0-255.") { | ||||
|     int32_t i; | ||||
|     JanetBuffer *buffer = janet_buffer(argc); | ||||
|     for (i = 0; i < argc; i++) { | ||||
|         int32_t c = janet_getinteger(argv, i); | ||||
|         buffer->data[i] = c & 0xFF; | ||||
|     } | ||||
|     buffer->count = argc; | ||||
|     return janet_wrap_buffer(buffer); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_fill, | ||||
|               "(buffer/fill buffer &opt byte)", | ||||
|               "Fill up a buffer with bytes, defaulting to 0s. Does not change the buffer's length. " | ||||
| @@ -338,8 +324,7 @@ static void buffer_push_impl(JanetBuffer *buffer, Janet *argv, int32_t argc_offs | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_push_at, | ||||
|               "(buffer/push-at buffer index & xs)", | ||||
|               "Same as buffer/push, but copies the new data into the buffer " | ||||
|               " at index `index`.") { | ||||
|               "Same as buffer/push, but inserts new data at index `index`.") { | ||||
|     janet_arity(argc, 2, -1); | ||||
|     JanetBuffer *buffer = janet_getbuffer(argv, 0); | ||||
|     int32_t index = janet_getinteger(argv, 1); | ||||
| @@ -368,6 +353,7 @@ JANET_CORE_FN(cfun_buffer_push, | ||||
|     return argv[0]; | ||||
| } | ||||
|  | ||||
|  | ||||
| JANET_CORE_FN(cfun_buffer_clear, | ||||
|               "(buffer/clear buffer)", | ||||
|               "Sets the size of a buffer to 0 and empties it. The buffer retains " | ||||
| @@ -476,15 +462,13 @@ JANET_CORE_FN(cfun_buffer_blit, | ||||
|     int same_buf = src.bytes == dest->data; | ||||
|     int32_t offset_dest = 0; | ||||
|     int32_t offset_src = 0; | ||||
|     if (argc > 2 && !janet_checktype(argv[2], JANET_NIL)) | ||||
|     if (argc > 2) | ||||
|         offset_dest = janet_gethalfrange(argv, 2, dest->count, "dest-start"); | ||||
|     if (argc > 3 && !janet_checktype(argv[3], JANET_NIL)) | ||||
|     if (argc > 3) | ||||
|         offset_src = janet_gethalfrange(argv, 3, src.len, "src-start"); | ||||
|     int32_t length_src; | ||||
|     if (argc > 4) { | ||||
|         int32_t src_end = src.len; | ||||
|         if (!janet_checktype(argv[4], JANET_NIL)) | ||||
|             src_end = janet_gethalfrange(argv, 4, src.len, "src-end"); | ||||
|         int32_t src_end = janet_gethalfrange(argv, 4, src.len, "src-end"); | ||||
|         length_src = src_end - offset_src; | ||||
|         if (length_src < 0) length_src = 0; | ||||
|     } else { | ||||
| @@ -523,7 +507,6 @@ void janet_lib_buffer(JanetTable *env) { | ||||
|     JanetRegExt buffer_cfuns[] = { | ||||
|         JANET_CORE_REG("buffer/new", cfun_buffer_new), | ||||
|         JANET_CORE_REG("buffer/new-filled", cfun_buffer_new_filled), | ||||
|         JANET_CORE_REG("buffer/from-bytes", cfun_buffer_frombytes), | ||||
|         JANET_CORE_REG("buffer/fill", cfun_buffer_fill), | ||||
|         JANET_CORE_REG("buffer/trim", cfun_buffer_trim), | ||||
|         JANET_CORE_REG("buffer/push-byte", cfun_buffer_u8), | ||||
|   | ||||
| @@ -25,7 +25,6 @@ | ||||
| #include <janet.h> | ||||
| #include "gc.h" | ||||
| #include "util.h" | ||||
| #include "regalloc.h" | ||||
| #endif | ||||
|  | ||||
| /* Look up table for instructions */ | ||||
| @@ -37,13 +36,11 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = { | ||||
|     JINT_0, /* JOP_RETURN_NIL, */ | ||||
|     JINT_SSI, /* JOP_ADD_IMMEDIATE, */ | ||||
|     JINT_SSS, /* JOP_ADD, */ | ||||
|     JINT_SSI, /* JOP_SUBTRACT_IMMEDIATE, */ | ||||
|     JINT_SSS, /* JOP_SUBTRACT, */ | ||||
|     JINT_SSI, /* JOP_MULTIPLY_IMMEDIATE, */ | ||||
|     JINT_SSS, /* JOP_MULTIPLY, */ | ||||
|     JINT_SSI, /* JOP_DIVIDE_IMMEDIATE, */ | ||||
|     JINT_SSS, /* JOP_DIVIDE, */ | ||||
|     JINT_SSS, /* JOP_DIVIDE_FLOOR */ | ||||
|     JINT_SSS, /* JOP_MODULO, */ | ||||
|     JINT_SSS, /* JOP_REMAINDER, */ | ||||
|     JINT_SSS, /* JOP_BAND, */ | ||||
| @@ -109,291 +106,6 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = { | ||||
|     JINT_SSS /* JOP_CANCEL, */ | ||||
| }; | ||||
|  | ||||
| /* Remove all noops while preserving jumps and debugging information. | ||||
|  * Useful as part of a filtering compiler pass. */ | ||||
| void janet_bytecode_remove_noops(JanetFuncDef *def) { | ||||
|  | ||||
|     /* Get an instruction rewrite map so we can rewrite jumps */ | ||||
|     uint32_t *pc_map = janet_smalloc(sizeof(uint32_t) * (1 + def->bytecode_length)); | ||||
|     uint32_t new_bytecode_length = 0; | ||||
|     for (int32_t i = 0; i < def->bytecode_length; i++) { | ||||
|         uint32_t instr = def->bytecode[i]; | ||||
|         uint32_t opcode = instr & 0x7F; | ||||
|         pc_map[i] = new_bytecode_length; | ||||
|         if (opcode != JOP_NOOP) { | ||||
|             new_bytecode_length++; | ||||
|         } | ||||
|     } | ||||
|     pc_map[def->bytecode_length] = new_bytecode_length; | ||||
|  | ||||
|     /* Linear scan rewrite bytecode and sourcemap. Also fix jumps. */ | ||||
|     int32_t j = 0; | ||||
|     for (int32_t i = 0; i < def->bytecode_length; i++) { | ||||
|         uint32_t instr = def->bytecode[i]; | ||||
|         uint32_t opcode = instr & 0x7F; | ||||
|         int32_t old_jump_target = 0; | ||||
|         int32_t new_jump_target = 0; | ||||
|         switch (opcode) { | ||||
|             case JOP_NOOP: | ||||
|                 continue; | ||||
|             case JOP_JUMP: | ||||
|                 /* relative pc is in DS field of instruction */ | ||||
|                 old_jump_target = i + (((int32_t)instr) >> 8); | ||||
|                 new_jump_target = pc_map[old_jump_target]; | ||||
|                 instr += (new_jump_target - old_jump_target + (i - j)) << 8; | ||||
|                 break; | ||||
|             case JOP_JUMP_IF: | ||||
|             case JOP_JUMP_IF_NIL: | ||||
|             case JOP_JUMP_IF_NOT: | ||||
|             case JOP_JUMP_IF_NOT_NIL: | ||||
|                 /* relative pc is in ES field of instruction */ | ||||
|                 old_jump_target = i + (((int32_t)instr) >> 16); | ||||
|                 new_jump_target = pc_map[old_jump_target]; | ||||
|                 instr += (new_jump_target - old_jump_target + (i - j)) << 16; | ||||
|                 break; | ||||
|             default: | ||||
|                 break; | ||||
|         } | ||||
|         def->bytecode[j] = instr; | ||||
|         if (def->sourcemap != NULL) { | ||||
|             def->sourcemap[j] = def->sourcemap[i]; | ||||
|         } | ||||
|         j++; | ||||
|     } | ||||
|  | ||||
|     /* Rewrite symbolmap */ | ||||
|     for (int32_t i = 0; i < def->symbolmap_length; i++) { | ||||
|         JanetSymbolMap *sm = def->symbolmap + i; | ||||
|         /* Don't rewrite upvalue mappings */ | ||||
|         if (sm->birth_pc < UINT32_MAX) { | ||||
|             sm->birth_pc = pc_map[sm->birth_pc]; | ||||
|             sm->death_pc = pc_map[sm->death_pc]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     def->bytecode_length = new_bytecode_length; | ||||
|     def->bytecode = janet_realloc(def->bytecode, def->bytecode_length * sizeof(uint32_t)); | ||||
|     janet_sfree(pc_map); | ||||
| } | ||||
|  | ||||
| /* Remove redundant loads, moves and other instructions if possible and convert them to | ||||
|  * noops. Input is assumed valid bytecode. */ | ||||
| void janet_bytecode_movopt(JanetFuncDef *def) { | ||||
|     JanetcRegisterAllocator ra; | ||||
|     int recur = 1; | ||||
|  | ||||
|     /* Iterate this until no more instructions can be removed. */ | ||||
|     while (recur) { | ||||
|         janetc_regalloc_init(&ra); | ||||
|  | ||||
|         /* Look for slots that have writes but no reads (and aren't in the closure bitset). */ | ||||
|         if (def->closure_bitset != NULL) { | ||||
|             for (int32_t i = 0; i < def->slotcount; i++) { | ||||
|                 int32_t index = i >> 5; | ||||
|                 uint32_t mask = 1U << (((uint32_t) i) & 31); | ||||
|                 if (def->closure_bitset[index] & mask) { | ||||
|                     janetc_regalloc_touch(&ra, i); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| #define AA ((instr >> 8)  & 0xFF) | ||||
| #define BB ((instr >> 16) & 0xFF) | ||||
| #define CC (instr >> 24) | ||||
| #define DD (instr >> 8) | ||||
| #define EE (instr >> 16) | ||||
|  | ||||
|         /* Check reads and writes */ | ||||
|         for (int32_t i = 0; i < def->bytecode_length; i++) { | ||||
|             uint32_t instr = def->bytecode[i]; | ||||
|             switch (instr & 0x7F) { | ||||
|  | ||||
|                 /* Group instructions my how they read from slots */ | ||||
|  | ||||
|                 /* No reads or writes */ | ||||
|                 default: | ||||
|                     janet_assert(0, "unhandled instruction"); | ||||
|                 case JOP_JUMP: | ||||
|                 case JOP_NOOP: | ||||
|                 case JOP_RETURN_NIL: | ||||
|                 /* Write A */ | ||||
|                 case JOP_LOAD_INTEGER: | ||||
|                 case JOP_LOAD_CONSTANT: | ||||
|                 case JOP_LOAD_UPVALUE: | ||||
|                 case JOP_CLOSURE: | ||||
|                 /* Write D */ | ||||
|                 case JOP_LOAD_NIL: | ||||
|                 case JOP_LOAD_TRUE: | ||||
|                 case JOP_LOAD_FALSE: | ||||
|                 case JOP_LOAD_SELF: | ||||
|                 case JOP_MAKE_ARRAY: | ||||
|                 case JOP_MAKE_BUFFER: | ||||
|                 case JOP_MAKE_STRING: | ||||
|                 case JOP_MAKE_STRUCT: | ||||
|                 case JOP_MAKE_TABLE: | ||||
|                 case JOP_MAKE_TUPLE: | ||||
|                 case JOP_MAKE_BRACKET_TUPLE: | ||||
|                     break; | ||||
|  | ||||
|                 /* Read A */ | ||||
|                 case JOP_ERROR: | ||||
|                 case JOP_TYPECHECK: | ||||
|                 case JOP_JUMP_IF: | ||||
|                 case JOP_JUMP_IF_NOT: | ||||
|                 case JOP_JUMP_IF_NIL: | ||||
|                 case JOP_JUMP_IF_NOT_NIL: | ||||
|                 case JOP_SET_UPVALUE: | ||||
|                 /* Write E, Read A */ | ||||
|                 case JOP_MOVE_FAR: | ||||
|                     janetc_regalloc_touch(&ra, AA); | ||||
|                     break; | ||||
|  | ||||
|                 /* Read B */ | ||||
|                 case JOP_SIGNAL: | ||||
|                 /* Write A, Read B */ | ||||
|                 case JOP_ADD_IMMEDIATE: | ||||
|                 case JOP_SUBTRACT_IMMEDIATE: | ||||
|                 case JOP_MULTIPLY_IMMEDIATE: | ||||
|                 case JOP_DIVIDE_IMMEDIATE: | ||||
|                 case JOP_SHIFT_LEFT_IMMEDIATE: | ||||
|                 case JOP_SHIFT_RIGHT_IMMEDIATE: | ||||
|                 case JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE: | ||||
|                 case JOP_GREATER_THAN_IMMEDIATE: | ||||
|                 case JOP_LESS_THAN_IMMEDIATE: | ||||
|                 case JOP_EQUALS_IMMEDIATE: | ||||
|                 case JOP_NOT_EQUALS_IMMEDIATE: | ||||
|                 case JOP_GET_INDEX: | ||||
|                     janetc_regalloc_touch(&ra, BB); | ||||
|                     break; | ||||
|  | ||||
|                 /* Read D */ | ||||
|                 case JOP_RETURN: | ||||
|                 case JOP_PUSH: | ||||
|                 case JOP_PUSH_ARRAY: | ||||
|                 case JOP_TAILCALL: | ||||
|                     janetc_regalloc_touch(&ra, DD); | ||||
|                     break; | ||||
|  | ||||
|                 /* Write A, Read E */ | ||||
|                 case JOP_MOVE_NEAR: | ||||
|                 case JOP_LENGTH: | ||||
|                 case JOP_BNOT: | ||||
|                 case JOP_CALL: | ||||
|                     janetc_regalloc_touch(&ra, EE); | ||||
|                     break; | ||||
|  | ||||
|                 /* Read A, B */ | ||||
|                 case JOP_PUT_INDEX: | ||||
|                     janetc_regalloc_touch(&ra, AA); | ||||
|                     janetc_regalloc_touch(&ra, BB); | ||||
|                     break; | ||||
|  | ||||
|                 /* Read A, E */ | ||||
|                 case JOP_PUSH_2: | ||||
|                     janetc_regalloc_touch(&ra, AA); | ||||
|                     janetc_regalloc_touch(&ra, EE); | ||||
|                     break; | ||||
|  | ||||
|                 /* Read B, C */ | ||||
|                 case JOP_PROPAGATE: | ||||
|                 /* Write A, Read B and C */ | ||||
|                 case JOP_BAND: | ||||
|                 case JOP_BOR: | ||||
|                 case JOP_BXOR: | ||||
|                 case JOP_ADD: | ||||
|                 case JOP_SUBTRACT: | ||||
|                 case JOP_MULTIPLY: | ||||
|                 case JOP_DIVIDE: | ||||
|                 case JOP_DIVIDE_FLOOR: | ||||
|                 case JOP_MODULO: | ||||
|                 case JOP_REMAINDER: | ||||
|                 case JOP_SHIFT_LEFT: | ||||
|                 case JOP_SHIFT_RIGHT: | ||||
|                 case JOP_SHIFT_RIGHT_UNSIGNED: | ||||
|                 case JOP_GREATER_THAN: | ||||
|                 case JOP_LESS_THAN: | ||||
|                 case JOP_EQUALS: | ||||
|                 case JOP_COMPARE: | ||||
|                 case JOP_IN: | ||||
|                 case JOP_GET: | ||||
|                 case JOP_GREATER_THAN_EQUAL: | ||||
|                 case JOP_LESS_THAN_EQUAL: | ||||
|                 case JOP_NOT_EQUALS: | ||||
|                 case JOP_CANCEL: | ||||
|                 case JOP_RESUME: | ||||
|                 case JOP_NEXT: | ||||
|                     janetc_regalloc_touch(&ra, BB); | ||||
|                     janetc_regalloc_touch(&ra, CC); | ||||
|                     break; | ||||
|  | ||||
|                 /* Read A, B, C */ | ||||
|                 case JOP_PUT: | ||||
|                 case JOP_PUSH_3: | ||||
|                     janetc_regalloc_touch(&ra, AA); | ||||
|                     janetc_regalloc_touch(&ra, BB); | ||||
|                     janetc_regalloc_touch(&ra, CC); | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* Iterate and set noops on instructions that make writes that no one ever reads. | ||||
|          * Only set noops for instructions with no side effects - moves, loads, etc. that can't | ||||
|          * raise errors (outside of systemic errors like oom or stack overflow). */ | ||||
|         recur = 0; | ||||
|         for (int32_t i = 0; i < def->bytecode_length; i++) { | ||||
|             uint32_t instr = def->bytecode[i]; | ||||
|             switch (instr & 0x7F) { | ||||
|                 default: | ||||
|                     break; | ||||
|                 /* Write D */ | ||||
|                 case JOP_LOAD_NIL: | ||||
|                 case JOP_LOAD_TRUE: | ||||
|                 case JOP_LOAD_FALSE: | ||||
|                 case JOP_LOAD_SELF: | ||||
|                 case JOP_MAKE_ARRAY: | ||||
|                 case JOP_MAKE_TUPLE: | ||||
|                 case JOP_MAKE_BRACKET_TUPLE: { | ||||
|                     if (!janetc_regalloc_check(&ra, DD)) { | ||||
|                         def->bytecode[i] = JOP_NOOP; | ||||
|                         recur = 1; | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|                 /* Write E, Read A */ | ||||
|                 case JOP_MOVE_FAR: { | ||||
|                     if (!janetc_regalloc_check(&ra, EE)) { | ||||
|                         def->bytecode[i] = JOP_NOOP; | ||||
|                         recur = 1; | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|                 /* Write A, Read E */ | ||||
|                 case JOP_MOVE_NEAR: | ||||
|                 /* Write A, Read B */ | ||||
|                 case JOP_GET_INDEX: | ||||
|                 /* Write A */ | ||||
|                 case JOP_LOAD_INTEGER: | ||||
|                 case JOP_LOAD_CONSTANT: | ||||
|                 case JOP_LOAD_UPVALUE: | ||||
|                 case JOP_CLOSURE: { | ||||
|                     if (!janetc_regalloc_check(&ra, AA)) { | ||||
|                         def->bytecode[i] = JOP_NOOP; | ||||
|                         recur = 1; | ||||
|                     } | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         janetc_regalloc_deinit(&ra); | ||||
| #undef AA | ||||
| #undef BB | ||||
| #undef CC | ||||
| #undef DD | ||||
| #undef EE | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Verify some bytecode */ | ||||
| int janet_verify(JanetFuncDef *def) { | ||||
|     int vargs = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG); | ||||
|   | ||||
| @@ -216,32 +216,12 @@ const char *janet_getcstring(const Janet *argv, int32_t n) { | ||||
| } | ||||
|  | ||||
| const char *janet_getcbytes(const Janet *argv, int32_t n) { | ||||
|     /* Ensure buffer 0-padded */ | ||||
|     if (janet_checktype(argv[n], JANET_BUFFER)) { | ||||
|         JanetBuffer *b = janet_unwrap_buffer(argv[n]); | ||||
|         if ((b->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC) && b->count == b->capacity) { | ||||
|             /* Make a copy with janet_smalloc in the rare case we have a buffer that | ||||
|              * cannot be realloced and pushing a 0 byte would panic. */ | ||||
|             char *new_string = janet_smalloc(b->count + 1); | ||||
|             memcpy(new_string, b->data, b->count); | ||||
|             new_string[b->count] = 0; | ||||
|             if (strlen(new_string) != (size_t) b->count) goto badzeros; | ||||
|             return new_string; | ||||
|         } else { | ||||
|             /* Ensure trailing 0 */ | ||||
|             janet_buffer_push_u8(b, 0); | ||||
|             b->count--; | ||||
|             if (strlen((char *)b->data) != (size_t) b->count) goto badzeros; | ||||
|             return (const char *) b->data; | ||||
|         } | ||||
|     } | ||||
|     JanetByteView view = janet_getbytes(argv, n); | ||||
|     const char *cstr = (const char *)view.bytes; | ||||
|     if (strlen(cstr) != (size_t) view.len) goto badzeros; | ||||
|     if (strlen(cstr) != (size_t) view.len) { | ||||
|         janet_panic("bytes contain embedded 0s"); | ||||
|     } | ||||
|     return cstr; | ||||
|  | ||||
| badzeros: | ||||
|     janet_panic("bytes contain embedded 0s"); | ||||
| } | ||||
|  | ||||
| const char *janet_optcbytes(const Janet *argv, int32_t argc, int32_t n, const char *dflt) { | ||||
| @@ -293,14 +273,6 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) { | ||||
|     return janet_unwrap_integer(x); | ||||
| } | ||||
|  | ||||
| uint32_t janet_getuinteger(const Janet *argv, int32_t n) { | ||||
|     Janet x = argv[n]; | ||||
|     if (!janet_checkuint(x)) { | ||||
|         janet_panicf("bad slot #%d, expected 32 bit signed integer, got %v", n, x); | ||||
|     } | ||||
|     return janet_unwrap_integer(x); | ||||
| } | ||||
|  | ||||
| int64_t janet_getinteger64(const Janet *argv, int32_t n) { | ||||
| #ifdef JANET_INT_TYPES | ||||
|     return janet_unwrap_s64(argv[n]); | ||||
| @@ -318,7 +290,7 @@ uint64_t janet_getuinteger64(const Janet *argv, int32_t n) { | ||||
|     return janet_unwrap_u64(argv[n]); | ||||
| #else | ||||
|     Janet x = argv[n]; | ||||
|     if (!janet_checkuint64(x)) { | ||||
|     if (!janet_checkint64(x)) { | ||||
|         janet_panicf("bad slot #%d, expected 64 bit unsigned integer, got %v", n, x); | ||||
|     } | ||||
|     return (uint64_t) janet_unwrap_number(x); | ||||
| @@ -342,20 +314,6 @@ int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const c | ||||
|     return not_raw; | ||||
| } | ||||
|  | ||||
| int32_t janet_getstartrange(const Janet *argv, int32_t argc, int32_t n, int32_t length) { | ||||
|     if (n >= argc || janet_checktype(argv[n], JANET_NIL)) { | ||||
|         return 0; | ||||
|     } | ||||
|     return janet_gethalfrange(argv, n, length, "start"); | ||||
| } | ||||
|  | ||||
| int32_t janet_getendrange(const Janet *argv, int32_t argc, int32_t n, int32_t length) { | ||||
|     if (n >= argc || janet_checktype(argv[n], JANET_NIL)) { | ||||
|         return length; | ||||
|     } | ||||
|     return janet_gethalfrange(argv, n, length, "end"); | ||||
| } | ||||
|  | ||||
| int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which) { | ||||
|     int32_t raw = janet_getinteger(argv, n); | ||||
|     int32_t not_raw = raw; | ||||
| @@ -408,10 +366,24 @@ JanetRange janet_getslice(int32_t argc, const Janet *argv) { | ||||
|     janet_arity(argc, 1, 3); | ||||
|     JanetRange range; | ||||
|     int32_t length = janet_length(argv[0]); | ||||
|     range.start = janet_getstartrange(argv, argc, 1, length); | ||||
|     range.end = janet_getendrange(argv, argc, 2, length); | ||||
|     if (range.end < range.start) | ||||
|         range.end = range.start; | ||||
|     if (argc == 1) { | ||||
|         range.start = 0; | ||||
|         range.end = length; | ||||
|     } else if (argc == 2) { | ||||
|         range.start = janet_checktype(argv[1], JANET_NIL) | ||||
|                       ? 0 | ||||
|                       : janet_gethalfrange(argv, 1, length, "start"); | ||||
|         range.end = length; | ||||
|     } else { | ||||
|         range.start = janet_checktype(argv[1], JANET_NIL) | ||||
|                       ? 0 | ||||
|                       : janet_gethalfrange(argv, 1, length, "start"); | ||||
|         range.end = janet_checktype(argv[2], JANET_NIL) | ||||
|                     ? length | ||||
|                     : janet_gethalfrange(argv, 2, length, "end"); | ||||
|         if (range.end < range.start) | ||||
|             range.end = range.start; | ||||
|     } | ||||
|     return range; | ||||
| } | ||||
|  | ||||
| @@ -491,27 +463,9 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA | ||||
|     return janet_getabstract(argv, n, at); | ||||
| } | ||||
|  | ||||
| /* Atomic refcounts */ | ||||
|  | ||||
| JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) { | ||||
| #ifdef JANET_WINDOWS | ||||
|     return InterlockedIncrement(x); | ||||
| #else | ||||
|     return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) { | ||||
| #ifdef JANET_WINDOWS | ||||
|     return InterlockedDecrement(x); | ||||
| #else | ||||
|     return __atomic_add_fetch(x, -1, __ATOMIC_RELAXED); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /* Some definitions for function-like macros */ | ||||
|  | ||||
| JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) { | ||||
| JANET_API JanetStructHead *(janet_struct_head)(const JanetKV *st) { | ||||
|     return janet_struct_head(st); | ||||
| } | ||||
|  | ||||
| @@ -519,10 +473,10 @@ JANET_API JanetAbstractHead *(janet_abstract_head)(const void *abstract) { | ||||
|     return janet_abstract_head(abstract); | ||||
| } | ||||
|  | ||||
| JANET_API JanetStringHead *(janet_string_head)(JanetString s) { | ||||
| JANET_API JanetStringHead *(janet_string_head)(const uint8_t *s) { | ||||
|     return janet_string_head(s); | ||||
| } | ||||
|  | ||||
| JANET_API JanetTupleHead *(janet_tuple_head)(JanetTuple tuple) { | ||||
| JANET_API JanetTupleHead *(janet_tuple_head)(const Janet *tuple) { | ||||
|     return janet_tuple_head(tuple); | ||||
| } | ||||
|   | ||||
| @@ -99,7 +99,7 @@ static JanetSlot opfunction( | ||||
| static int can_be_imm(Janet x, int8_t *out) { | ||||
|     if (!janet_checkint(x)) return 0; | ||||
|     int32_t integer = janet_unwrap_integer(x); | ||||
|     if (integer > INT8_MAX || integer < INT8_MIN) return 0; | ||||
|     if (integer > 127 || integer < -127) return 0; | ||||
|     *out = (int8_t) integer; | ||||
|     return 1; | ||||
| } | ||||
| @@ -116,11 +116,12 @@ static JanetSlot opreduce( | ||||
|     JanetSlot *args, | ||||
|     int op, | ||||
|     int opim, | ||||
|     Janet nullary, | ||||
|     Janet unary) { | ||||
|     Janet nullary) { | ||||
|     JanetCompiler *c = opts.compiler; | ||||
|     int32_t i, len; | ||||
|     int8_t imm = 0; | ||||
|     int neg = opim < 0; | ||||
|     if (opim < 0) opim = -opim; | ||||
|     len = janet_v_count(args); | ||||
|     JanetSlot t; | ||||
|     if (len == 0) { | ||||
| @@ -131,19 +132,19 @@ static JanetSlot opreduce( | ||||
|         if (op == JOP_SUBTRACT) { | ||||
|             janetc_emit_ssi(c, JOP_MULTIPLY_IMMEDIATE, t, args[0], -1, 1); | ||||
|         } else { | ||||
|             janetc_emit_sss(c, op, t, janetc_cslot(unary), args[0], 1); | ||||
|             janetc_emit_sss(c, op, t, janetc_cslot(nullary), args[0], 1); | ||||
|         } | ||||
|         return t; | ||||
|     } | ||||
|     t = janetc_gettarget(opts); | ||||
|     if (opim && can_slot_be_imm(args[1], &imm)) { | ||||
|         janetc_emit_ssi(c, opim, t, args[0], imm, 1); | ||||
|         janetc_emit_ssi(c, opim, t, args[0], neg ? -imm : imm, 1); | ||||
|     } else { | ||||
|         janetc_emit_sss(c, op, t, args[0], args[1], 1); | ||||
|     } | ||||
|     for (i = 2; i < len; i++) { | ||||
|         if (opim && can_slot_be_imm(args[i], &imm)) { | ||||
|             janetc_emit_ssi(c, opim, t, t, imm, 1); | ||||
|             janetc_emit_ssi(c, opim, t, t, neg ? -imm : imm, 1); | ||||
|         } else { | ||||
|             janetc_emit_sss(c, op, t, t, args[i], 1); | ||||
|         } | ||||
| @@ -154,7 +155,7 @@ static JanetSlot opreduce( | ||||
| /* Function optimizers */ | ||||
|  | ||||
| static JanetSlot do_propagate(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_PROPAGATE, 0, janet_wrap_nil(), janet_wrap_nil()); | ||||
|     return opreduce(opts, args, JOP_PROPAGATE, 0, janet_wrap_nil()); | ||||
| } | ||||
| static JanetSlot do_error(JanetFopts opts, JanetSlot *args) { | ||||
|     janetc_emit_s(opts.compiler, JOP_ERROR, args[0], 0); | ||||
| @@ -171,7 +172,7 @@ static JanetSlot do_debug(JanetFopts opts, JanetSlot *args) { | ||||
|     return t; | ||||
| } | ||||
| static JanetSlot do_in(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_IN, 0, janet_wrap_nil(), janet_wrap_nil()); | ||||
|     return opreduce(opts, args, JOP_IN, 0, janet_wrap_nil()); | ||||
| } | ||||
| static JanetSlot do_get(JanetFopts opts, JanetSlot *args) { | ||||
|     if (janet_v_count(args) == 3) { | ||||
| @@ -191,14 +192,20 @@ static JanetSlot do_get(JanetFopts opts, JanetSlot *args) { | ||||
|         c->buffer[label] |= (current - label) << 16; | ||||
|         return t; | ||||
|     } else { | ||||
|         return opreduce(opts, args, JOP_GET, 0, janet_wrap_nil(), janet_wrap_nil()); | ||||
|         return opreduce(opts, args, JOP_GET, 0, janet_wrap_nil()); | ||||
|     } | ||||
| } | ||||
| static JanetSlot do_next(JanetFopts opts, JanetSlot *args) { | ||||
|     return opfunction(opts, args, JOP_NEXT, janet_wrap_nil()); | ||||
| } | ||||
| static JanetSlot do_modulo(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_MODULO, 0, janet_wrap_nil()); | ||||
| } | ||||
| static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_REMAINDER, 0, janet_wrap_nil()); | ||||
| } | ||||
| static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil(), janet_wrap_nil()); | ||||
|     return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil()); | ||||
| } | ||||
| static JanetSlot do_put(JanetFopts opts, JanetSlot *args) { | ||||
|     if (opts.flags & JANET_FOPTS_DROP) { | ||||
| @@ -255,43 +262,34 @@ static JanetSlot do_apply(JanetFopts opts, JanetSlot *args) { | ||||
| /* Variadic operators specialization */ | ||||
|  | ||||
| static JanetSlot do_add(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_ADD, JOP_ADD_IMMEDIATE, janet_wrap_integer(0), janet_wrap_integer(0)); | ||||
|     return opreduce(opts, args, JOP_ADD, JOP_ADD_IMMEDIATE, janet_wrap_integer(0)); | ||||
| } | ||||
| static JanetSlot do_sub(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_SUBTRACT, JOP_SUBTRACT_IMMEDIATE, janet_wrap_integer(0), janet_wrap_integer(0)); | ||||
|     return opreduce(opts, args, JOP_SUBTRACT, -JOP_ADD_IMMEDIATE, janet_wrap_integer(0)); | ||||
| } | ||||
| static JanetSlot do_mul(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_MULTIPLY, JOP_MULTIPLY_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1)); | ||||
|     return opreduce(opts, args, JOP_MULTIPLY, JOP_MULTIPLY_IMMEDIATE, janet_wrap_integer(1)); | ||||
| } | ||||
| static JanetSlot do_div(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_DIVIDE, JOP_DIVIDE_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1)); | ||||
| } | ||||
| static JanetSlot do_divf(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_DIVIDE_FLOOR, 0, janet_wrap_integer(1), janet_wrap_integer(1)); | ||||
| } | ||||
| static JanetSlot do_modulo(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_MODULO, 0, janet_wrap_integer(0), janet_wrap_integer(1)); | ||||
| } | ||||
| static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_REMAINDER, 0, janet_wrap_integer(0), janet_wrap_integer(1)); | ||||
|     return opreduce(opts, args, JOP_DIVIDE, JOP_DIVIDE_IMMEDIATE, janet_wrap_integer(1)); | ||||
| } | ||||
| static JanetSlot do_band(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_BAND, 0, janet_wrap_integer(-1), janet_wrap_integer(-1)); | ||||
|     return opreduce(opts, args, JOP_BAND, 0, janet_wrap_integer(-1)); | ||||
| } | ||||
| static JanetSlot do_bor(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_BOR, 0, janet_wrap_integer(0), janet_wrap_integer(0)); | ||||
|     return opreduce(opts, args, JOP_BOR, 0, janet_wrap_integer(0)); | ||||
| } | ||||
| static JanetSlot do_bxor(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_BXOR, 0, janet_wrap_integer(0), janet_wrap_integer(0)); | ||||
|     return opreduce(opts, args, JOP_BXOR, 0, janet_wrap_integer(0)); | ||||
| } | ||||
| static JanetSlot do_lshift(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_SHIFT_LEFT, JOP_SHIFT_LEFT_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1)); | ||||
|     return opreduce(opts, args, JOP_SHIFT_LEFT, JOP_SHIFT_LEFT_IMMEDIATE, janet_wrap_integer(1)); | ||||
| } | ||||
| static JanetSlot do_rshift(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_SHIFT_RIGHT, JOP_SHIFT_RIGHT_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1)); | ||||
|     return opreduce(opts, args, JOP_SHIFT_RIGHT, JOP_SHIFT_RIGHT_IMMEDIATE, janet_wrap_integer(1)); | ||||
| } | ||||
| static JanetSlot do_rshiftu(JanetFopts opts, JanetSlot *args) { | ||||
|     return opreduce(opts, args, JOP_SHIFT_RIGHT_UNSIGNED, JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1)); | ||||
|     return opreduce(opts, args, JOP_SHIFT_RIGHT_UNSIGNED, JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, janet_wrap_integer(1)); | ||||
| } | ||||
| static JanetSlot do_bnot(JanetFopts opts, JanetSlot *args) { | ||||
|     return genericSS(opts, JOP_BNOT, args[0]); | ||||
| @@ -385,11 +383,10 @@ static const JanetFunOptimizer optimizers[] = { | ||||
|     {fixarity2, do_propagate}, | ||||
|     {arity2or3, do_get}, | ||||
|     {arity1or2, do_next}, | ||||
|     {NULL, do_modulo}, | ||||
|     {NULL, do_remainder}, | ||||
|     {fixarity2, do_modulo}, | ||||
|     {fixarity2, do_remainder}, | ||||
|     {fixarity2, do_cmp}, | ||||
|     {fixarity2, do_cancel}, | ||||
|     {NULL, do_divf} | ||||
| }; | ||||
|  | ||||
| const JanetFunOptimizer *janetc_funopt(uint32_t flags) { | ||||
|   | ||||
| @@ -746,14 +746,12 @@ static int macroexpand1( | ||||
|     int lock = janet_gclock(); | ||||
|     Janet mf_kw = janet_ckeywordv("macro-form"); | ||||
|     janet_table_put(c->env, mf_kw, x); | ||||
|     Janet ml_kw = janet_ckeywordv("macro-lints"); | ||||
|     if (c->lints) { | ||||
|         janet_table_put(c->env, ml_kw, janet_wrap_array(c->lints)); | ||||
|     } | ||||
|     Janet tempOut; | ||||
|     JanetSignal status = janet_continue(fiberp, janet_wrap_nil(), &tempOut); | ||||
|     janet_table_put(c->env, mf_kw, janet_wrap_nil()); | ||||
|     janet_table_put(c->env, ml_kw, janet_wrap_nil()); | ||||
|     if (c->lints) { | ||||
|         janet_table_put(c->env, janet_ckeywordv("macro-lints"), janet_wrap_array(c->lints)); | ||||
|     } | ||||
|     janet_gcunlock(lock); | ||||
|     if (status != JANET_SIGNAL_OK) { | ||||
|         const uint8_t *es = janet_formatc("(macro) %V", tempOut); | ||||
| @@ -973,21 +971,12 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) { | ||||
|     for (int32_t i = 0; i < janet_v_count(scope->syms); i++) { | ||||
|         SymPair pair = scope->syms[i]; | ||||
|         if (pair.sym2) { | ||||
|             JanetSymbolMap jsm; | ||||
|             if (pair.death_pc == UINT32_MAX) { | ||||
|                 jsm.death_pc = def->bytecode_length; | ||||
|             } else { | ||||
|                 jsm.death_pc = pair.death_pc - scope->bytecode_start; | ||||
|                 pair.death_pc = def->bytecode_length; | ||||
|             } | ||||
|             /* Handle birth_pc == 0 correctly */ | ||||
|             if ((uint32_t) scope->bytecode_start > pair.birth_pc) { | ||||
|                 jsm.birth_pc = 0; | ||||
|             } else { | ||||
|                 jsm.birth_pc = pair.birth_pc - scope->bytecode_start; | ||||
|             } | ||||
|             janet_assert(jsm.birth_pc <= jsm.death_pc, "birth pc after death pc"); | ||||
|             janet_assert(jsm.birth_pc < (uint32_t) def->bytecode_length, "bad birth pc"); | ||||
|             janet_assert(jsm.death_pc <= (uint32_t) def->bytecode_length, "bad death pc"); | ||||
|             JanetSymbolMap jsm; | ||||
|             jsm.birth_pc = pair.birth_pc; | ||||
|             jsm.death_pc = pair.death_pc; | ||||
|             jsm.slot_index = pair.slot.index; | ||||
|             jsm.symbol = pair.sym2; | ||||
|             janet_v_push(locals, jsm); | ||||
| @@ -1000,10 +989,6 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) { | ||||
|     /* Pop the scope */ | ||||
|     janetc_popscope(c); | ||||
|  | ||||
|     /* Do basic optimization */ | ||||
|     janet_bytecode_movopt(def); | ||||
|     janet_bytecode_remove_noops(def); | ||||
|  | ||||
|     return def; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -69,7 +69,6 @@ typedef enum { | ||||
| #define JANET_FUN_REMAINDER 30 | ||||
| #define JANET_FUN_CMP 31 | ||||
| #define JANET_FUN_CANCEL 32 | ||||
| #define JANET_FUN_DIVIDE_FLOOR 33 | ||||
|  | ||||
| /* Compiler typedefs */ | ||||
| typedef struct JanetCompiler JanetCompiler; | ||||
| @@ -268,8 +267,4 @@ JanetSlot janetc_cslot(Janet x); | ||||
| /* Search for a symbol */ | ||||
| JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym); | ||||
|  | ||||
| /* Bytecode optimization */ | ||||
| void janet_bytecode_movopt(JanetFuncDef *def); | ||||
| void janet_bytecode_remove_noops(JanetFuncDef *def); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -426,36 +426,6 @@ JANET_CORE_FN(janet_core_slice, | ||||
|     } | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_core_range, | ||||
|               "(range & args)", | ||||
|               "Create an array of values [start, end) with a given step. " | ||||
|               "With one argument, returns a range [0, end). With two arguments, returns " | ||||
|               "a range [start, end). With three, returns a range with optional step size.") { | ||||
|     janet_arity(argc, 1, 3); | ||||
|     int32_t start = 0, stop = 0, step = 1, count = 0; | ||||
|     if (argc == 3) { | ||||
|         start = janet_getinteger(argv, 0); | ||||
|         stop = janet_getinteger(argv, 1); | ||||
|         step = janet_getinteger(argv, 2); | ||||
|         count = (step > 0) ? (stop - start - 1) / step + 1 : | ||||
|                 ((step < 0) ? (stop - start + 1) / step + 1 : 0); | ||||
|     } else if (argc == 2) { | ||||
|         start = janet_getinteger(argv, 0); | ||||
|         stop = janet_getinteger(argv, 1); | ||||
|         count = stop - start; | ||||
|     } else { | ||||
|         stop = janet_getinteger(argv, 0); | ||||
|         count = stop; | ||||
|     } | ||||
|     count = (count > 0) ? count : 0; | ||||
|     JanetArray *array = janet_array(count); | ||||
|     for (int32_t i = 0; i < count; i++) { | ||||
|         array->data[i] = janet_wrap_number(start + i * step); | ||||
|     } | ||||
|     array->count = count; | ||||
|     return janet_wrap_array(array); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_core_table, | ||||
|               "(table & kvs)", | ||||
|               "Creates a new table from a variadic number of keys and values. " | ||||
| @@ -488,7 +458,7 @@ JANET_CORE_FN(janet_core_getproto, | ||||
|                ? janet_wrap_struct(janet_struct_proto(st)) | ||||
|                : janet_wrap_nil(); | ||||
|     } | ||||
|     janet_panicf("expected struct or table, got %v", argv[0]); | ||||
|     janet_panicf("expected struct|table, got %v", argv[0]); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_core_struct, | ||||
| @@ -659,34 +629,6 @@ ret_false: | ||||
|     return janet_wrap_false(); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_core_is_bytes, | ||||
|               "(bytes? x)", | ||||
|               "Check if x is a string, symbol, keyword, or buffer.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_BYTES)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_core_is_indexed, | ||||
|               "(indexed? x)", | ||||
|               "Check if x is an array or tuple.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_INDEXED)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_core_is_dictionary, | ||||
|               "(dictionary? x)", | ||||
|               "Check if x is a table or struct.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_DICTIONARY)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_core_is_lengthable, | ||||
|               "(lengthable? x)", | ||||
|               "Check if x is a bytes, indexed, or dictionary.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_LENGTHABLE)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(janet_core_signal, | ||||
|               "(signal what x)", | ||||
|               "Raise a signal with payload x. ") { | ||||
| @@ -735,9 +677,6 @@ static const SandboxOption sandbox_options[] = { | ||||
|     {"all", JANET_SANDBOX_ALL}, | ||||
|     {"env", JANET_SANDBOX_ENV}, | ||||
|     {"ffi", JANET_SANDBOX_FFI}, | ||||
|     {"ffi-define", JANET_SANDBOX_FFI_DEFINE}, | ||||
|     {"ffi-jit", JANET_SANDBOX_FFI_JIT}, | ||||
|     {"ffi-use", JANET_SANDBOX_FFI_USE}, | ||||
|     {"fs", JANET_SANDBOX_FS}, | ||||
|     {"fs-read", JANET_SANDBOX_FS_READ}, | ||||
|     {"fs-temp", JANET_SANDBOX_FS_TEMP}, | ||||
| @@ -748,7 +687,6 @@ static const SandboxOption sandbox_options[] = { | ||||
|     {"net-connect", JANET_SANDBOX_NET_CONNECT}, | ||||
|     {"net-listen", JANET_SANDBOX_NET_LISTEN}, | ||||
|     {"sandbox", JANET_SANDBOX_SANDBOX}, | ||||
|     {"signal", JANET_SANDBOX_SIGNAL}, | ||||
|     {"subprocess", JANET_SANDBOX_SUBPROCESS}, | ||||
|     {NULL, 0} | ||||
| }; | ||||
| @@ -760,9 +698,6 @@ JANET_CORE_FN(janet_core_sandbox, | ||||
|               "* :all - disallow all (except IO to stdout, stderr, and stdin)\n" | ||||
|               "* :env - disallow reading and write env variables\n" | ||||
|               "* :ffi - disallow FFI (recommended if disabling anything else)\n" | ||||
|               "* :ffi-define - disallow loading new FFI modules and binding new functions\n" | ||||
|               "* :ffi-jit - disallow calling `ffi/jitfn`\n" | ||||
|               "* :ffi-use - disallow using any previously bound FFI functions and memory-unsafe functions.\n" | ||||
|               "* :fs - disallow access to the file system\n" | ||||
|               "* :fs-read - disallow read access to the file system\n" | ||||
|               "* :fs-temp - disallow creating temporary files\n" | ||||
| @@ -773,7 +708,6 @@ JANET_CORE_FN(janet_core_sandbox, | ||||
|               "* :net-connect - disallow making outbound network connections\n" | ||||
|               "* :net-listen - disallow accepting inbound network connections\n" | ||||
|               "* :sandbox - disallow calling this function\n" | ||||
|               "* :signal - disallow adding or removing signal handlers\n" | ||||
|               "* :subprocess - disallow running subprocesses") { | ||||
|     uint32_t flags = 0; | ||||
|     for (int32_t i = 0; i < argc; i++) { | ||||
| @@ -1045,6 +979,14 @@ static const uint32_t next_asm[] = { | ||||
|     JOP_NEXT | (1 << 24), | ||||
|     JOP_RETURN | ||||
| }; | ||||
| static const uint32_t modulo_asm[] = { | ||||
|     JOP_MODULO | (1 << 24), | ||||
|     JOP_RETURN | ||||
| }; | ||||
| static const uint32_t remainder_asm[] = { | ||||
|     JOP_REMAINDER | (1 << 24), | ||||
|     JOP_RETURN | ||||
| }; | ||||
| static const uint32_t cmp_asm[] = { | ||||
|     JOP_COMPARE | (1 << 24), | ||||
|     JOP_RETURN | ||||
| @@ -1083,12 +1025,7 @@ static void janet_load_libs(JanetTable *env) { | ||||
|         JANET_CORE_REG("module/expand-path", janet_core_expand_path), | ||||
|         JANET_CORE_REG("int?", janet_core_check_int), | ||||
|         JANET_CORE_REG("nat?", janet_core_check_nat), | ||||
|         JANET_CORE_REG("bytes?", janet_core_is_bytes), | ||||
|         JANET_CORE_REG("indexed?", janet_core_is_indexed), | ||||
|         JANET_CORE_REG("dictionary?", janet_core_is_dictionary), | ||||
|         JANET_CORE_REG("lengthable?", janet_core_is_lengthable), | ||||
|         JANET_CORE_REG("slice", janet_core_slice), | ||||
|         JANET_CORE_REG("range", janet_core_range), | ||||
|         JANET_CORE_REG("signal", janet_core_signal), | ||||
|         JANET_CORE_REG("memcmp", janet_core_memcmp), | ||||
|         JANET_CORE_REG("getproto", janet_core_getproto), | ||||
| @@ -1134,6 +1071,14 @@ static void janet_load_libs(JanetTable *env) { | ||||
|  | ||||
| JanetTable *janet_core_env(JanetTable *replacements) { | ||||
|     JanetTable *env = (NULL != replacements) ? replacements : janet_table(0); | ||||
|     janet_quick_asm(env, JANET_FUN_MODULO, | ||||
|                     "mod", 2, 2, 2, 2, modulo_asm, sizeof(modulo_asm), | ||||
|                     JDOC("(mod dividend divisor)\n\n" | ||||
|                          "Returns the modulo of dividend / divisor.")); | ||||
|     janet_quick_asm(env, JANET_FUN_REMAINDER, | ||||
|                     "%", 2, 2, 2, 2, remainder_asm, sizeof(remainder_asm), | ||||
|                     JDOC("(% dividend divisor)\n\n" | ||||
|                          "Returns the remainder of dividend / divisor.")); | ||||
|     janet_quick_asm(env, JANET_FUN_CMP, | ||||
|                     "cmp", 2, 2, 2, 2, cmp_asm, sizeof(cmp_asm), | ||||
|                     JDOC("(cmp x y)\n\n" | ||||
| @@ -1232,18 +1177,6 @@ JanetTable *janet_core_env(JanetTable *replacements) { | ||||
|                           "Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns " | ||||
|                           "the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining " | ||||
|                           "values.")); | ||||
|     templatize_varop(env, JANET_FUN_DIVIDE_FLOOR, "div", 1, 1, JOP_DIVIDE_FLOOR, | ||||
|                      JDOC("(div & xs)\n\n" | ||||
|                           "Returns the floored division of xs. If xs is empty, returns 1. If xs has one value x, returns " | ||||
|                           "the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining " | ||||
|                           "values.")); | ||||
|     templatize_varop(env, JANET_FUN_MODULO, "mod", 0, 1, JOP_MODULO, | ||||
|                      JDOC("(mod & xs)\n\n" | ||||
|                           "Returns the result of applying the modulo operator on the first value of xs with each remaining value. " | ||||
|                           "`(mod x 0)` is defined to be `x`.")); | ||||
|     templatize_varop(env, JANET_FUN_REMAINDER, "%", 0, 1, JOP_REMAINDER, | ||||
|                      JDOC("(% & xs)\n\n" | ||||
|                           "Returns the remainder of dividing the first value of xs by each remaining value.")); | ||||
|     templatize_varop(env, JANET_FUN_BAND, "band", -1, -1, JOP_BAND, | ||||
|                      JDOC("(band & xs)\n\n" | ||||
|                           "Returns the bit-wise and of all values in xs. Each x in xs must be an integer.")); | ||||
|   | ||||
| @@ -314,7 +314,6 @@ static Janet doframe(JanetStackFrame *frame) { | ||||
|     if (frame->func && frame->pc) { | ||||
|         Janet *stack = (Janet *)frame + JANET_FRAME_SIZE; | ||||
|         JanetArray *slots; | ||||
|         janet_assert(def != NULL, "def != NULL"); | ||||
|         off = (int32_t)(frame->pc - def->bytecode); | ||||
|         janet_table_put(t, janet_ckeywordv("pc"), janet_wrap_integer(off)); | ||||
|         if (def->sourcemap) { | ||||
|   | ||||
| @@ -26,7 +26,6 @@ | ||||
| #include "emit.h" | ||||
| #include "vector.h" | ||||
| #include "regalloc.h" | ||||
| #include "util.h" | ||||
| #endif | ||||
|  | ||||
| /* Get a register */ | ||||
| @@ -129,8 +128,7 @@ static void janetc_movenear(JanetCompiler *c, | ||||
|                     ((uint32_t)(src.envindex) << 16) | | ||||
|                     ((uint32_t)(dest) << 8) | | ||||
|                     JOP_LOAD_UPVALUE); | ||||
|     } else if (src.index != dest) { | ||||
|         janet_assert(src.index >= 0, "bad slot"); | ||||
|     } else if (src.index > 0xFF || src.index != dest) { | ||||
|         janetc_emit(c, | ||||
|                     ((uint32_t)(src.index) << 16) | | ||||
|                     ((uint32_t)(dest) << 8) | | ||||
| @@ -157,7 +155,6 @@ static void janetc_moveback(JanetCompiler *c, | ||||
|                     ((uint32_t)(src) << 8) | | ||||
|                     JOP_SET_UPVALUE); | ||||
|     } else if (dest.index != src) { | ||||
|         janet_assert(dest.index >= 0, "bad slot"); | ||||
|         janetc_emit(c, | ||||
|                     ((uint32_t)(dest.index) << 16) | | ||||
|                     ((uint32_t)(src) << 8) | | ||||
|   | ||||
							
								
								
									
										951
									
								
								src/core/ev.c
									
									
									
									
									
								
							
							
						
						
									
										951
									
								
								src/core/ev.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -50,11 +50,6 @@ | ||||
| #define WIN32_LEAN_AND_MEAN | ||||
| #endif | ||||
|  | ||||
| /* needed for inet_pton and InitializeSRWLock */ | ||||
| #ifdef __MINGW32__ | ||||
| #define _WIN32_WINNT _WIN32_WINNT_VISTA | ||||
| #endif | ||||
|  | ||||
| /* Needed for realpath on linux, as well as pthread rwlocks. */ | ||||
| #ifndef _XOPEN_SOURCE | ||||
| #define _XOPEN_SOURCE 600 | ||||
|   | ||||
| @@ -1303,7 +1303,7 @@ JANET_CORE_FN(cfun_ffi_jitfn, | ||||
|               "(ffi/jitfn bytes)", | ||||
|               "Create an abstract type that can be used as the pointer argument to `ffi/call`. The content " | ||||
|               "of `bytes` is architecture specific machine code that will be copied into executable memory.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_JIT); | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_fixarity(argc, 1); | ||||
|     JanetByteView bytes = janet_getbytes(argv, 0); | ||||
|  | ||||
| @@ -1356,7 +1356,7 @@ JANET_CORE_FN(cfun_ffi_call, | ||||
|               "(ffi/call pointer signature & args)", | ||||
|               "Call a raw pointer as a function pointer. The function signature specifies " | ||||
|               "how Janet values in `args` are converted to native machine types.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_USE); | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_arity(argc, 2, -1); | ||||
|     void *function_pointer = janet_ffi_get_callable_pointer(argv, 0); | ||||
|     JanetFFISignature *signature = janet_getabstract(argv, 1, &janet_signature_type); | ||||
| @@ -1364,7 +1364,6 @@ JANET_CORE_FN(cfun_ffi_call, | ||||
|     switch (signature->cc) { | ||||
|         default: | ||||
|         case JANET_FFI_CC_NONE: | ||||
|             (void) function_pointer; | ||||
|             janet_panic("calling convention not supported"); | ||||
| #ifdef JANET_FFI_WIN64_ENABLED | ||||
|         case JANET_FFI_CC_WIN_64: | ||||
| @@ -1381,8 +1380,8 @@ JANET_CORE_FN(cfun_ffi_buffer_write, | ||||
|               "(ffi/write ffi-type data &opt buffer index)", | ||||
|               "Append a native type to a buffer such as it would appear in memory. This can be used " | ||||
|               "to pass pointers to structs in the ffi, or send C/C++/native structs over the network " | ||||
|               "or to files. Returns a modified buffer or a new buffer if one is not supplied.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_USE); | ||||
|               "or to files. Returns a modifed buffer or a new buffer if one is not supplied.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_arity(argc, 2, 4); | ||||
|     JanetFFIType type = decode_ffi_type(argv[0]); | ||||
|     uint32_t el_size = (uint32_t) type_size(type); | ||||
| @@ -1405,7 +1404,7 @@ JANET_CORE_FN(cfun_ffi_buffer_read, | ||||
|               "Parse a native struct out of a buffer and convert it to normal Janet data structures. " | ||||
|               "This function is the inverse of `ffi/write`. `bytes` can also be a raw pointer, although " | ||||
|               "this is unsafe.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_USE); | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_arity(argc, 2, 3); | ||||
|     JanetFFIType type = decode_ffi_type(argv[0]); | ||||
|     size_t offset = (size_t) janet_optnat(argv, argc, 2, 0); | ||||
| @@ -1452,7 +1451,7 @@ JANET_CORE_FN(janet_core_raw_native, | ||||
|               " or run any code from it. This is different than `native`, which will " | ||||
|               "run initialization code to get a module table. If `path` is nil, opens the current running binary. " | ||||
|               "Returns a `core/native`.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_DEFINE); | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_arity(argc, 0, 1); | ||||
|     const char *path = janet_optcstring(argv, argc, 0, NULL); | ||||
|     Clib lib = load_clib(path); | ||||
| @@ -1468,7 +1467,7 @@ JANET_CORE_FN(janet_core_native_lookup, | ||||
|               "(ffi/lookup native symbol-name)", | ||||
|               "Lookup a symbol from a native object. All symbol lookups will return a raw pointer " | ||||
|               "if the symbol is found, else nil.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_DEFINE); | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_fixarity(argc, 2); | ||||
|     JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); | ||||
|     const char *sym = janet_getcstring(argv, 1); | ||||
| @@ -1482,7 +1481,7 @@ JANET_CORE_FN(janet_core_native_close, | ||||
|               "(ffi/close native)", | ||||
|               "Free a native object. Dereferencing pointers to symbols in the object will have undefined " | ||||
|               "behavior after freeing.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_DEFINE); | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_fixarity(argc, 1); | ||||
|     JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type); | ||||
|     if (anative->closed) janet_panic("native object already closed"); | ||||
| @@ -1495,7 +1494,7 @@ JANET_CORE_FN(janet_core_native_close, | ||||
| JANET_CORE_FN(cfun_ffi_malloc, | ||||
|               "(ffi/malloc size)", | ||||
|               "Allocates memory directly using the janet memory allocator. Memory allocated in this way must be freed manually! Returns a raw pointer, or nil if size = 0.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_USE); | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_fixarity(argc, 1); | ||||
|     size_t size = janet_getsize(argv, 0); | ||||
|     if (size == 0) return janet_wrap_nil(); | ||||
| @@ -1505,7 +1504,7 @@ JANET_CORE_FN(cfun_ffi_malloc, | ||||
| JANET_CORE_FN(cfun_ffi_free, | ||||
|               "(ffi/free pointer)", | ||||
|               "Free memory allocated with `ffi/malloc`. Returns nil.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_USE); | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_fixarity(argc, 1); | ||||
|     if (janet_checktype(argv[0], JANET_NIL)) return janet_wrap_nil(); | ||||
|     void *pointer = janet_getpointer(argv, 0); | ||||
| @@ -1520,7 +1519,7 @@ JANET_CORE_FN(cfun_ffi_pointer_buffer, | ||||
|               "to be manipulated with buffer functions. Attempts to resize or extend the buffer " | ||||
|               "beyond its initial capacity will raise an error. As with many FFI functions, this is memory " | ||||
|               "unsafe and can potentially allow out of bounds memory access. Returns a new buffer.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_USE); | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI); | ||||
|     janet_arity(argc, 2, 4); | ||||
|     void *pointer = janet_getpointer(argv, 0); | ||||
|     int32_t capacity = janet_getnat(argv, 1); | ||||
| @@ -1530,42 +1529,6 @@ JANET_CORE_FN(cfun_ffi_pointer_buffer, | ||||
|     return janet_wrap_buffer(janet_pointer_buffer_unsafe(offset_pointer, capacity, count)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_ffi_pointer_cfunction, | ||||
|               "(ffi/pointer-cfunction pointer &opt name source-file source-line)", | ||||
|               "Create a C Function from a raw pointer. Optionally give the cfunction a name and " | ||||
|               "source location for stack traces and debugging.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FFI_USE); | ||||
|     janet_arity(argc, 1, 4); | ||||
|     void *pointer = janet_getpointer(argv, 0); | ||||
|     const char *name = janet_optcstring(argv, argc, 1, NULL); | ||||
|     const char *source = janet_optcstring(argv, argc, 2, NULL); | ||||
|     int32_t line = janet_optinteger(argv, argc, 3, -1); | ||||
|     if ((name != NULL) || (source != NULL) || (line != -1)) { | ||||
|         janet_registry_put((JanetCFunction) pointer, name, NULL, source, line); | ||||
|     } | ||||
|     return janet_wrap_cfunction((JanetCFunction) pointer); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_ffi_supported_calling_conventions, | ||||
|               "(ffi/calling-conventions)", | ||||
|               "Get an array of all supported calling conventions on the current architecture. Some architectures may have some FFI " | ||||
|               "functionality (ffi/malloc, ffi/free, ffi/read, ffi/write, etc.) but not support " | ||||
|               "any calling conventions. This function can be used to get all supported calling conventions " | ||||
|               "that can be used on this architecture. All architectures support the :none calling " | ||||
|               "convention which is a placeholder that cannot be used at runtime.") { | ||||
|     janet_fixarity(argc, 0); | ||||
|     (void) argv; | ||||
|     JanetArray *array = janet_array(4); | ||||
| #ifdef JANET_FFI_WIN64_ENABLED | ||||
|     janet_array_push(array, janet_ckeywordv("win64")); | ||||
| #endif | ||||
| #ifdef JANET_FFI_SYSV64_ENABLED | ||||
|     janet_array_push(array, janet_ckeywordv("sysv64")); | ||||
| #endif | ||||
|     janet_array_push(array, janet_ckeywordv("none")); | ||||
|     return janet_wrap_array(array); | ||||
| } | ||||
|  | ||||
| void janet_lib_ffi(JanetTable *env) { | ||||
|     JanetRegExt ffi_cfuns[] = { | ||||
|         JANET_CORE_REG("ffi/native", janet_core_raw_native), | ||||
| @@ -1583,8 +1546,6 @@ void janet_lib_ffi(JanetTable *env) { | ||||
|         JANET_CORE_REG("ffi/malloc", cfun_ffi_malloc), | ||||
|         JANET_CORE_REG("ffi/free", cfun_ffi_free), | ||||
|         JANET_CORE_REG("ffi/pointer-buffer", cfun_ffi_pointer_buffer), | ||||
|         JANET_CORE_REG("ffi/pointer-cfunction", cfun_ffi_pointer_cfunction), | ||||
|         JANET_CORE_REG("ffi/calling-conventions", cfun_ffi_supported_calling_conventions), | ||||
|         JANET_REG_END | ||||
|     }; | ||||
|     janet_core_cfuns_ext(env, NULL, ffi_cfuns); | ||||
|   | ||||
| @@ -39,10 +39,8 @@ static void fiber_reset(JanetFiber *fiber) { | ||||
|     fiber->env = NULL; | ||||
|     fiber->last_value = janet_wrap_nil(); | ||||
| #ifdef JANET_EV | ||||
|     fiber->waiting = NULL; | ||||
|     fiber->sched_id = 0; | ||||
|     fiber->ev_callback = NULL; | ||||
|     fiber->ev_state = NULL; | ||||
|     fiber->ev_stream = NULL; | ||||
|     fiber->supervisor_channel = NULL; | ||||
| #endif | ||||
|     janet_fiber_set_status(fiber, JANET_STATUS_NEW); | ||||
| @@ -83,10 +81,10 @@ JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t | ||||
|         } | ||||
|         fiber->stacktop = newstacktop; | ||||
|     } | ||||
|     /* Don't panic on failure since we use this to implement janet_pcall */ | ||||
|     if (janet_fiber_funcframe(fiber, callee)) return NULL; | ||||
|     janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_ENTRANCE; | ||||
| #ifdef JANET_EV | ||||
|     fiber->waiting = NULL; | ||||
|     fiber->supervisor_channel = NULL; | ||||
| #endif | ||||
|     return fiber; | ||||
| @@ -479,10 +477,10 @@ JANET_CORE_FN(cfun_fiber_setenv, | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_fiber_new, | ||||
|               "(fiber/new func &opt sigmask env)", | ||||
|               "(fiber/new func &opt sigmask)", | ||||
|               "Create a new fiber with function body func. Can optionally " | ||||
|               "take a set of signals `sigmask` to capture from child fibers, " | ||||
|               "and an environment table `env`. The mask is specified as a keyword where each character " | ||||
|               "take a set of signals to block from the current parent fiber " | ||||
|               "when called. The mask is specified as a keyword where each character " | ||||
|               "is used to indicate a signal to block. If the ev module is enabled, and " | ||||
|               "this fiber is used as an argument to `ev/go`, these \"blocked\" signals " | ||||
|               "will result in messages being sent to the supervisor channel. " | ||||
| @@ -504,18 +502,14 @@ JANET_CORE_FN(cfun_fiber_new, | ||||
|               "exclusive flags are present, the last flag takes precedence.\n\n" | ||||
|               "* :i - inherit the environment from the current fiber\n" | ||||
|               "* :p - the environment table's prototype is the current environment table") { | ||||
|     janet_arity(argc, 1, 3); | ||||
|     janet_arity(argc, 1, 2); | ||||
|     JanetFunction *func = janet_getfunction(argv, 0); | ||||
|     JanetFiber *fiber; | ||||
|     if (func->def->min_arity > 1) { | ||||
|         janet_panicf("fiber function must accept 0 or 1 arguments"); | ||||
|     } | ||||
|     fiber = janet_fiber(func, 64, func->def->min_arity, NULL); | ||||
|     janet_assert(fiber != NULL, "bad fiber arity check"); | ||||
|     if (argc == 3 && !janet_checktype(argv[2], JANET_NIL)) { | ||||
|         fiber->env = janet_gettable(argv, 2); | ||||
|     } | ||||
|     if (argc >= 2) { | ||||
|     if (argc == 2) { | ||||
|         int32_t i; | ||||
|         JanetByteView view = janet_getbytes(argv, 1); | ||||
|         fiber->flags = JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP; | ||||
|   | ||||
| @@ -59,9 +59,6 @@ | ||||
| #define JANET_FIBER_EV_FLAG_CANCELED 0x10000 | ||||
| #define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000 | ||||
| #define JANET_FIBER_FLAG_ROOT 0x40000 | ||||
| #define JANET_FIBER_EV_FLAG_IN_FLIGHT 0x1 | ||||
|  | ||||
| /* used only on windows, should otherwise be unset */ | ||||
|  | ||||
| #define janet_fiber_set_status(f, s) do {\ | ||||
|     (f)->flags &= ~JANET_FIBER_STATUS_MASK;\ | ||||
|   | ||||
							
								
								
									
										171
									
								
								src/core/gc.c
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								src/core/gc.c
									
									
									
									
									
								
							| @@ -132,24 +132,6 @@ static void janet_mark_many(const Janet *values, int32_t n) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Mark a bunch of key values items in memory */ | ||||
| static void janet_mark_keys(const JanetKV *kvs, int32_t n) { | ||||
|     const JanetKV *end = kvs + n; | ||||
|     while (kvs < end) { | ||||
|         janet_mark(kvs->key); | ||||
|         kvs++; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Mark a bunch of key values items in memory */ | ||||
| static void janet_mark_values(const JanetKV *kvs, int32_t n) { | ||||
|     const JanetKV *end = kvs + n; | ||||
|     while (kvs < end) { | ||||
|         janet_mark(kvs->value); | ||||
|         kvs++; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Mark a bunch of key values items in memory */ | ||||
| static void janet_mark_kvs(const JanetKV *kvs, int32_t n) { | ||||
|     const JanetKV *end = kvs + n; | ||||
| @@ -164,9 +146,7 @@ static void janet_mark_array(JanetArray *array) { | ||||
|     if (janet_gc_reachable(array)) | ||||
|         return; | ||||
|     janet_gc_mark(array); | ||||
|     if (janet_gc_type((JanetGCObject *) array) == JANET_MEMORY_ARRAY) { | ||||
|         janet_mark_many(array->data, array->count); | ||||
|     } | ||||
|     janet_mark_many(array->data, array->count); | ||||
| } | ||||
|  | ||||
| static void janet_mark_table(JanetTable *table) { | ||||
| @@ -174,15 +154,7 @@ recur: /* Manual tail recursion */ | ||||
|     if (janet_gc_reachable(table)) | ||||
|         return; | ||||
|     janet_gc_mark(table); | ||||
|     enum JanetMemoryType memtype = janet_gc_type(table); | ||||
|     if (memtype == JANET_MEMORY_TABLE_WEAKK) { | ||||
|         janet_mark_values(table->data, table->capacity); | ||||
|     } else if (memtype == JANET_MEMORY_TABLE_WEAKV) { | ||||
|         janet_mark_keys(table->data, table->capacity); | ||||
|     } else if (memtype == JANET_MEMORY_TABLE) { | ||||
|         janet_mark_kvs(table->data, table->capacity); | ||||
|     } | ||||
|     /* do nothing for JANET_MEMORY_TABLE_WEAKKV */ | ||||
|     janet_mark_kvs(table->data, table->capacity); | ||||
|     if (table->proto) { | ||||
|         table = table->proto; | ||||
|         goto recur; | ||||
| @@ -296,12 +268,6 @@ recur: | ||||
|     if (fiber->supervisor_channel) { | ||||
|         janet_mark_abstract(fiber->supervisor_channel); | ||||
|     } | ||||
|     if (fiber->ev_stream) { | ||||
|         janet_mark_abstract(fiber->ev_stream); | ||||
|     } | ||||
|     if (fiber->ev_callback) { | ||||
|         fiber->ev_callback(fiber, JANET_ASYNC_EVENT_MARK); | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     /* Explicit tail recursion */ | ||||
| @@ -326,17 +292,9 @@ static void janet_deinit_block(JanetGCObject *mem) { | ||||
|         case JANET_MEMORY_TABLE: | ||||
|             janet_free(((JanetTable *) mem)->data); | ||||
|             break; | ||||
|         case JANET_MEMORY_FIBER: { | ||||
|             JanetFiber *f = (JanetFiber *)mem; | ||||
| #ifdef JANET_EV | ||||
|             if (f->ev_state && !(f->flags & JANET_FIBER_EV_FLAG_IN_FLIGHT)) { | ||||
|                 janet_ev_dec_refcount(); | ||||
|                 janet_free(f->ev_state); | ||||
|             } | ||||
| #endif | ||||
|             janet_free(f->data); | ||||
|         } | ||||
|         break; | ||||
|         case JANET_MEMORY_FIBER: | ||||
|             janet_free(((JanetFiber *)mem)->data); | ||||
|             break; | ||||
|         case JANET_MEMORY_BUFFER: | ||||
|             janet_buffer_deinit((JanetBuffer *) mem); | ||||
|             break; | ||||
| @@ -368,98 +326,12 @@ static void janet_deinit_block(JanetGCObject *mem) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Check that a value x has been visited in the mark phase */ | ||||
| static int janet_check_liveref(Janet x) { | ||||
|     switch (janet_type(x)) { | ||||
|         default: | ||||
|             return 1; | ||||
|         case JANET_ARRAY: | ||||
|         case JANET_TABLE: | ||||
|         case JANET_FUNCTION: | ||||
|         case JANET_BUFFER: | ||||
|         case JANET_FIBER: | ||||
|             return janet_gc_reachable(janet_unwrap_pointer(x)); | ||||
|         case JANET_STRING: | ||||
|         case JANET_SYMBOL: | ||||
|         case JANET_KEYWORD: | ||||
|             return janet_gc_reachable(janet_string_head(janet_unwrap_string(x))); | ||||
|         case JANET_ABSTRACT: | ||||
|             return janet_gc_reachable(janet_abstract_head(janet_unwrap_abstract(x))); | ||||
|         case JANET_TUPLE: | ||||
|             return janet_gc_reachable(janet_tuple_head(janet_unwrap_tuple(x))); | ||||
|         case JANET_STRUCT: | ||||
|             return janet_gc_reachable(janet_struct_head(janet_unwrap_struct(x))); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Iterate over all allocated memory, and free memory that is not | ||||
|  * marked as reachable. Flip the gc color flag for next sweep. */ | ||||
| void janet_sweep() { | ||||
|     JanetGCObject *previous = NULL; | ||||
|     JanetGCObject *current = janet_vm.weak_blocks; | ||||
|     JanetGCObject *current = janet_vm.blocks; | ||||
|     JanetGCObject *next; | ||||
|  | ||||
|     /* Sweep weak heap to drop weak refs */ | ||||
|     while (NULL != current) { | ||||
|         next = current->data.next; | ||||
|         if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) { | ||||
|             /* Check for dead references */ | ||||
|             enum JanetMemoryType type = janet_gc_type(current); | ||||
|             if (type == JANET_MEMORY_ARRAY_WEAK) { | ||||
|                 JanetArray *array = (JanetArray *) current; | ||||
|                 for (uint32_t i = 0; i < (uint32_t) array->count; i++) { | ||||
|                     if (!janet_check_liveref(array->data[i])) { | ||||
|                         array->data[i] = janet_wrap_nil(); | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 JanetTable *table = (JanetTable *) current; | ||||
|                 int check_values = (type == JANET_MEMORY_TABLE_WEAKV) || (type == JANET_MEMORY_TABLE_WEAKKV); | ||||
|                 int check_keys = (type == JANET_MEMORY_TABLE_WEAKK) || (type == JANET_MEMORY_TABLE_WEAKKV); | ||||
|                 JanetKV *end = table->data + table->capacity; | ||||
|                 JanetKV *kvs = table->data; | ||||
|                 while (kvs < end) { | ||||
|                     int drop = 0; | ||||
|                     if (check_keys && !janet_check_liveref(kvs->key)) drop = 1; | ||||
|                     if (check_values && !janet_check_liveref(kvs->value)) drop = 1; | ||||
|                     if (drop) { | ||||
|                         /* Inlined from janet_table_remove without search */ | ||||
|                         table->count--; | ||||
|                         table->deleted++; | ||||
|                         kvs->key = janet_wrap_nil(); | ||||
|                         kvs->value = janet_wrap_false(); | ||||
|                     } | ||||
|                     kvs++; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         current = next; | ||||
|     } | ||||
|  | ||||
|     /* Sweep weak heap to free blocks */ | ||||
|     previous = NULL; | ||||
|     current = janet_vm.weak_blocks; | ||||
|     while (NULL != current) { | ||||
|         next = current->data.next; | ||||
|         if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) { | ||||
|             previous = current; | ||||
|             current->flags &= ~JANET_MEM_REACHABLE; | ||||
|         } else { | ||||
|             janet_vm.block_count--; | ||||
|             janet_deinit_block(current); | ||||
|             if (NULL != previous) { | ||||
|                 previous->data.next = next; | ||||
|             } else { | ||||
|                 janet_vm.weak_blocks = next; | ||||
|             } | ||||
|             janet_free(current); | ||||
|         } | ||||
|         current = next; | ||||
|     } | ||||
|  | ||||
|     /* Sweep main heap to free blocks */ | ||||
|     previous = NULL; | ||||
|     current = janet_vm.blocks; | ||||
|     while (NULL != current) { | ||||
|         next = current->data.next; | ||||
|         if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) { | ||||
| @@ -477,7 +349,6 @@ void janet_sweep() { | ||||
|         } | ||||
|         current = next; | ||||
|     } | ||||
|  | ||||
| #ifdef JANET_EV | ||||
|     /* Sweep threaded abstract types for references to decrement */ | ||||
|     JanetKV *items = janet_vm.threaded_abstracts.data; | ||||
| @@ -499,15 +370,14 @@ void janet_sweep() { | ||||
|                     if (head->type->gc) { | ||||
|                         janet_assert(!head->type->gc(head->data, head->size), "finalizer failed"); | ||||
|                     } | ||||
|                     /* Mark as tombstone in place */ | ||||
|                     items[i].key = janet_wrap_nil(); | ||||
|                     items[i].value = janet_wrap_false(); | ||||
|                     janet_vm.threaded_abstracts.deleted++; | ||||
|                     janet_vm.threaded_abstracts.count--; | ||||
|                     /* Free memory */ | ||||
|                     janet_free(janet_abstract_head(abst)); | ||||
|                 } | ||||
|  | ||||
|                 /* Mark as tombstone in place */ | ||||
|                 items[i].key = janet_wrap_nil(); | ||||
|                 items[i].value = janet_wrap_false(); | ||||
|                 janet_vm.threaded_abstracts.deleted++; | ||||
|                 janet_vm.threaded_abstracts.count--; | ||||
|             } | ||||
|  | ||||
|             /* Reset for next sweep */ | ||||
| @@ -535,15 +405,8 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) { | ||||
|  | ||||
|     /* Prepend block to heap list */ | ||||
|     janet_vm.next_collection += size; | ||||
|     if (type < JANET_MEMORY_TABLE_WEAKK) { | ||||
|         /* normal heap */ | ||||
|         mem->data.next = janet_vm.blocks; | ||||
|         janet_vm.blocks = mem; | ||||
|     } else { | ||||
|         /* weak heap */ | ||||
|         mem->data.next = janet_vm.weak_blocks; | ||||
|         janet_vm.weak_blocks = mem; | ||||
|     } | ||||
|     mem->data.next = janet_vm.blocks; | ||||
|     janet_vm.blocks = mem; | ||||
|     janet_vm.block_count++; | ||||
|  | ||||
|     return (void *)mem; | ||||
| @@ -574,8 +437,7 @@ void janet_collect(void) { | ||||
|     uint32_t i; | ||||
|     if (janet_vm.gc_suspend) return; | ||||
|     depth = JANET_RECURSION_GUARD; | ||||
|     janet_vm.gc_mark_phase = 1; | ||||
|     /* Try to prevent many major collections back to back. | ||||
|     /* Try and prevent many major collections back to back. | ||||
|      * A full collection will take O(janet_vm.block_count) time. | ||||
|      * If we have a large heap, make sure our interval is not too | ||||
|      * small so we won't make many collections over it. This is just a | ||||
| @@ -594,7 +456,6 @@ void janet_collect(void) { | ||||
|         Janet x = janet_vm.roots[--janet_vm.root_count]; | ||||
|         janet_mark(x); | ||||
|     } | ||||
|     janet_vm.gc_mark_phase = 0; | ||||
|     janet_sweep(); | ||||
|     janet_vm.next_collection = 0; | ||||
|     janet_free_all_scratch(); | ||||
| @@ -698,9 +559,7 @@ void janet_gcunlock(int handle) { | ||||
|     janet_vm.gc_suspend = handle; | ||||
| } | ||||
|  | ||||
| /* Scratch memory API | ||||
|  * Scratch memory allocations do not need to be free (but optionally can be), and will be automatically cleaned | ||||
|  * up in the next call to janet_collect. */ | ||||
| /* Scratch memory API */ | ||||
|  | ||||
| void *janet_smalloc(size_t size) { | ||||
|     JanetScratch *s = janet_malloc(sizeof(JanetScratch) + size); | ||||
|   | ||||
| @@ -57,10 +57,6 @@ enum JanetMemoryType { | ||||
|     JANET_MEMORY_FUNCENV, | ||||
|     JANET_MEMORY_FUNCDEF, | ||||
|     JANET_MEMORY_THREADED_ABSTRACT, | ||||
|     JANET_MEMORY_TABLE_WEAKK, | ||||
|     JANET_MEMORY_TABLE_WEAKV, | ||||
|     JANET_MEMORY_TABLE_WEAKKV, | ||||
|     JANET_MEMORY_ARRAY_WEAK | ||||
| }; | ||||
|  | ||||
| /* To allocate collectable memory, one must call janet_alloc, initialize the memory, | ||||
|   | ||||
| @@ -118,9 +118,10 @@ int64_t janet_unwrap_s64(Janet x) { | ||||
|         default: | ||||
|             break; | ||||
|         case JANET_NUMBER : { | ||||
|             double d = janet_unwrap_number(x); | ||||
|             if (!janet_checkint64range(d)) break; | ||||
|             return (int64_t) d; | ||||
|             double dbl = janet_unwrap_number(x); | ||||
|             if (fabs(dbl) <=  MAX_INT_IN_DBL) | ||||
|                 return (int64_t)dbl; | ||||
|             break; | ||||
|         } | ||||
|         case JANET_STRING: { | ||||
|             int64_t value; | ||||
| @@ -137,7 +138,7 @@ int64_t janet_unwrap_s64(Janet x) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     janet_panicf("can not convert %t %q to 64 bit signed integer", x, x); | ||||
|     janet_panicf("bad s64 initializer: %t", x); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -146,9 +147,12 @@ uint64_t janet_unwrap_u64(Janet x) { | ||||
|         default: | ||||
|             break; | ||||
|         case JANET_NUMBER : { | ||||
|             double d = janet_unwrap_number(x); | ||||
|             if (!janet_checkuint64range(d)) break; | ||||
|             return (uint64_t) d; | ||||
|             double dbl = janet_unwrap_number(x); | ||||
|             /* Allow negative values to be cast to "wrap around". | ||||
|              * This let's addition and subtraction work as expected. */ | ||||
|             if (fabs(dbl) <=  MAX_INT_IN_DBL) | ||||
|                 return (uint64_t)dbl; | ||||
|             break; | ||||
|         } | ||||
|         case JANET_STRING: { | ||||
|             uint64_t value; | ||||
| @@ -165,7 +169,7 @@ uint64_t janet_unwrap_u64(Janet x) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     janet_panicf("can not convert %t %q to a 64 bit unsigned integer", x, x); | ||||
|     janet_panicf("bad u64 initializer: %t", x); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -239,7 +243,7 @@ JANET_CORE_FN(cfun_to_bytes, | ||||
|               "Write the bytes of an `int/s64` or `int/u64` into a buffer.\n" | ||||
|               "The `buffer` parameter specifies an existing buffer to write to, if unset a new buffer will be created.\n" | ||||
|               "Returns the modified buffer.\n" | ||||
|               "The `endianness` parameter indicates the byte order:\n" | ||||
|               "The `endianness` paramater indicates the byte order:\n" | ||||
|               "- `nil` (unset): system byte order\n" | ||||
|               "- `:le`: little-endian, least significant byte first\n" | ||||
|               "- `:be`: big-endian, most significant byte first\n") { | ||||
| @@ -303,8 +307,8 @@ static int compare_double_double(double x, double y) { | ||||
|  | ||||
| static int compare_int64_double(int64_t x, double y) { | ||||
|     if (isnan(y)) { | ||||
|         return 0; | ||||
|     } else if ((y > JANET_INTMIN_DOUBLE) && (y < JANET_INTMAX_DOUBLE)) { | ||||
|         return 0; // clojure and python do this | ||||
|     } else if ((y > (- ((double) MAX_INT_IN_DBL))) && (y < ((double) MAX_INT_IN_DBL))) { | ||||
|         double dx = (double) x; | ||||
|         return compare_double_double(dx, y); | ||||
|     } else if (y > ((double) INT64_MAX)) { | ||||
| @@ -319,10 +323,10 @@ static int compare_int64_double(int64_t x, double y) { | ||||
|  | ||||
| static int compare_uint64_double(uint64_t x, double y) { | ||||
|     if (isnan(y)) { | ||||
|         return 0; | ||||
|         return 0; // clojure and python do this | ||||
|     } else if (y < 0) { | ||||
|         return 1; | ||||
|     } else if ((y >= 0) && (y < JANET_INTMAX_DOUBLE)) { | ||||
|     } else if ((y >= 0) && (y < ((double) MAX_INT_IN_DBL))) { | ||||
|         double dx = (double) x; | ||||
|         return compare_double_double(dx, y); | ||||
|     } else if (y > ((double) UINT64_MAX)) { | ||||
| @@ -335,9 +339,8 @@ static int compare_uint64_double(uint64_t x, double y) { | ||||
|  | ||||
| static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) { | ||||
|     janet_fixarity(argc, 2); | ||||
|     if (janet_is_int(argv[0]) != JANET_INT_S64) { | ||||
|     if (janet_is_int(argv[0]) != JANET_INT_S64) | ||||
|         janet_panic("compare method requires int/s64 as first argument"); | ||||
|     } | ||||
|     int64_t x = janet_unwrap_s64(argv[0]); | ||||
|     switch (janet_type(argv[1])) { | ||||
|         default: | ||||
| @@ -352,6 +355,7 @@ static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) { | ||||
|                 int64_t y = *(int64_t *)abst; | ||||
|                 return janet_wrap_number((x < y) ? -1 : (x > y ? 1 : 0)); | ||||
|             } else if (janet_abstract_type(abst) == &janet_u64_type) { | ||||
|                 // comparing signed to unsigned -- be careful! | ||||
|                 uint64_t y = *(uint64_t *)abst; | ||||
|                 if (x < 0) { | ||||
|                     return janet_wrap_number(-1); | ||||
| @@ -370,9 +374,8 @@ static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) { | ||||
|  | ||||
| static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) { | ||||
|     janet_fixarity(argc, 2); | ||||
|     if (janet_is_int(argv[0]) != JANET_INT_U64) { | ||||
|     if (janet_is_int(argv[0]) != JANET_INT_U64)  // is this needed? | ||||
|         janet_panic("compare method requires int/u64 as first argument"); | ||||
|     } | ||||
|     uint64_t x = janet_unwrap_u64(argv[0]); | ||||
|     switch (janet_type(argv[1])) { | ||||
|         default: | ||||
| @@ -387,6 +390,7 @@ static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) { | ||||
|                 uint64_t y = *(uint64_t *)abst; | ||||
|                 return janet_wrap_number((x < y) ? -1 : (x > y ? 1 : 0)); | ||||
|             } else if (janet_abstract_type(abst) == &janet_s64_type) { | ||||
|                 // comparing unsigned to signed -- be careful! | ||||
|                 int64_t y = *(int64_t *)abst; | ||||
|                 if (y < 0) { | ||||
|                     return janet_wrap_number(1); | ||||
| @@ -427,7 +431,7 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \ | ||||
| } \ | ||||
|  | ||||
| #define OPMETHODINVERT(T, type, name, oper) \ | ||||
| static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \ | ||||
| static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \ | ||||
|     janet_fixarity(argc, 2); \ | ||||
|     T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \ | ||||
|     *box = janet_unwrap_##type(argv[1]); \ | ||||
| @@ -436,19 +440,6 @@ static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \ | ||||
|     return janet_wrap_abstract(box); \ | ||||
| } \ | ||||
|  | ||||
| #define UNARYMETHOD(T, type, name, oper) \ | ||||
| static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \ | ||||
|     janet_fixarity(argc, 1); \ | ||||
|     T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \ | ||||
|     *box = oper(janet_unwrap_##type(argv[0])); \ | ||||
|     return janet_wrap_abstract(box); \ | ||||
| } \ | ||||
|  | ||||
| #define DIVZERO(name) DIVZERO_##name | ||||
| #define DIVZERO_div janet_panic("division by zero") | ||||
| #define DIVZERO_rem janet_panic("division by zero") | ||||
| #define DIVZERO_mod return janet_wrap_abstract(box) | ||||
|  | ||||
| #define DIVMETHOD(T, type, name, oper) \ | ||||
| static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \ | ||||
|     janet_arity(argc, 2, -1);                       \ | ||||
| @@ -456,19 +447,19 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \ | ||||
|     *box = janet_unwrap_##type(argv[0]); \ | ||||
|     for (int32_t i = 1; i < argc; i++) { \ | ||||
|       T value = janet_unwrap_##type(argv[i]); \ | ||||
|       if (value == 0) DIVZERO(name); \ | ||||
|       if (value == 0) janet_panic("division by zero"); \ | ||||
|       *box oper##= value; \ | ||||
|     } \ | ||||
|     return janet_wrap_abstract(box); \ | ||||
| } \ | ||||
|  | ||||
| #define DIVMETHODINVERT(T, type, name, oper) \ | ||||
| static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \ | ||||
| static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \ | ||||
|     janet_fixarity(argc, 2);                       \ | ||||
|     T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \ | ||||
|     *box = janet_unwrap_##type(argv[1]); \ | ||||
|     T value = janet_unwrap_##type(argv[0]); \ | ||||
|     if (value == 0) DIVZERO(name); \ | ||||
|     if (value == 0) janet_panic("division by zero"); \ | ||||
|     *box oper##= value; \ | ||||
|     return janet_wrap_abstract(box); \ | ||||
| } \ | ||||
| @@ -480,7 +471,7 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \ | ||||
|     *box = janet_unwrap_##type(argv[0]); \ | ||||
|     for (int32_t i = 1; i < argc; i++) { \ | ||||
|       T value = janet_unwrap_##type(argv[i]); \ | ||||
|       if (value == 0) DIVZERO(name); \ | ||||
|       if (value == 0) janet_panic("division by zero"); \ | ||||
|       if ((value == -1) && (*box == INT64_MIN)) janet_panic("INT64_MIN divided by -1"); \ | ||||
|       *box oper##= value; \ | ||||
|     } \ | ||||
| @@ -488,50 +479,26 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \ | ||||
| } \ | ||||
|  | ||||
| #define DIVMETHODINVERT_SIGNED(T, type, name, oper) \ | ||||
| static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \ | ||||
| static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \ | ||||
|     janet_fixarity(argc, 2);                       \ | ||||
|     T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \ | ||||
|     *box = janet_unwrap_##type(argv[1]); \ | ||||
|     T value = janet_unwrap_##type(argv[0]); \ | ||||
|     if (value == 0) DIVZERO(name); \ | ||||
|     if (value == 0) janet_panic("division by zero"); \ | ||||
|     if ((value == -1) && (*box == INT64_MIN)) janet_panic("INT64_MIN divided by -1"); \ | ||||
|     *box oper##= value; \ | ||||
|     return janet_wrap_abstract(box); \ | ||||
| } \ | ||||
|  | ||||
| static Janet cfun_it_s64_divf(int32_t argc, Janet *argv) { | ||||
|     janet_fixarity(argc, 2); | ||||
|     int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t)); | ||||
|     int64_t op1 = janet_unwrap_s64(argv[0]); | ||||
|     int64_t op2 = janet_unwrap_s64(argv[1]); | ||||
|     if (op2 == 0) janet_panic("division by zero"); | ||||
|     int64_t x = op1 / op2; | ||||
|     *box = x - (((op1 ^ op2) < 0) && (x * op2 != op1)); | ||||
|     return janet_wrap_abstract(box); | ||||
| } | ||||
|  | ||||
| static Janet cfun_it_s64_divfi(int32_t argc, Janet *argv) { | ||||
|     janet_fixarity(argc, 2); | ||||
|     int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t)); | ||||
|     int64_t op2 = janet_unwrap_s64(argv[0]); | ||||
|     int64_t op1 = janet_unwrap_s64(argv[1]); | ||||
|     if (op2 == 0) janet_panic("division by zero"); | ||||
|     int64_t x = op1 / op2; | ||||
|     *box = x - (((op1 ^ op2) < 0) && (x * op2 != op1)); | ||||
|     return janet_wrap_abstract(box); | ||||
| } | ||||
|  | ||||
| static Janet cfun_it_s64_mod(int32_t argc, Janet *argv) { | ||||
|     janet_fixarity(argc, 2); | ||||
|     int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t)); | ||||
|     int64_t op1 = janet_unwrap_s64(argv[0]); | ||||
|     int64_t op2 = janet_unwrap_s64(argv[1]); | ||||
|     if (op2 == 0) { | ||||
|         *box = op1; | ||||
|     } else { | ||||
|         int64_t x = op1 % op2; | ||||
|         *box = (((op1 ^ op2) < 0) && (x != 0)) ? x + op2 : x; | ||||
|     } | ||||
|     int64_t x = op1 % op2; | ||||
|     *box = (op1 > 0) | ||||
|            ? ((op2 > 0) ? x : (0 == x ? x : x + op2)) | ||||
|            : ((op2 > 0) ? (0 == x ? x : x + op2) : x); | ||||
|     return janet_wrap_abstract(box); | ||||
| } | ||||
|  | ||||
| @@ -540,43 +507,37 @@ static Janet cfun_it_s64_modi(int32_t argc, Janet *argv) { | ||||
|     int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t)); | ||||
|     int64_t op2 = janet_unwrap_s64(argv[0]); | ||||
|     int64_t op1 = janet_unwrap_s64(argv[1]); | ||||
|     if (op2 == 0) { | ||||
|         *box = op1; | ||||
|     } else { | ||||
|         int64_t x = op1 % op2; | ||||
|         *box = (((op1 ^ op2) < 0) && (x != 0)) ? x + op2 : x; | ||||
|     } | ||||
|     int64_t x = op1 % op2; | ||||
|     *box = (op1 > 0) | ||||
|            ? ((op2 > 0) ? x : (0 == x ? x : x + op2)) | ||||
|            : ((op2 > 0) ? (0 == x ? x : x + op2) : x); | ||||
|     return janet_wrap_abstract(box); | ||||
| } | ||||
|  | ||||
| OPMETHOD(int64_t, s64, add, +) | ||||
| OPMETHOD(int64_t, s64, sub, -) | ||||
| OPMETHODINVERT(int64_t, s64, sub, -) | ||||
| OPMETHODINVERT(int64_t, s64, subi, -) | ||||
| OPMETHOD(int64_t, s64, mul, *) | ||||
| DIVMETHOD_SIGNED(int64_t, s64, div, /) | ||||
| DIVMETHOD_SIGNED(int64_t, s64, rem, %) | ||||
| DIVMETHODINVERT_SIGNED(int64_t, s64, div, /) | ||||
| DIVMETHODINVERT_SIGNED(int64_t, s64, rem, %) | ||||
| DIVMETHODINVERT_SIGNED(int64_t, s64, divi, /) | ||||
| DIVMETHODINVERT_SIGNED(int64_t, s64, remi, %) | ||||
| OPMETHOD(int64_t, s64, and, &) | ||||
| OPMETHOD(int64_t, s64, or, |) | ||||
| OPMETHOD(int64_t, s64, xor, ^) | ||||
| UNARYMETHOD(int64_t, s64, not, ~) | ||||
| OPMETHOD(int64_t, s64, lshift, <<) | ||||
| OPMETHOD(int64_t, s64, rshift, >>) | ||||
| OPMETHOD(uint64_t, u64, add, +) | ||||
| OPMETHOD(uint64_t, u64, sub, -) | ||||
| OPMETHODINVERT(uint64_t, u64, sub, -) | ||||
| OPMETHODINVERT(uint64_t, u64, subi, -) | ||||
| OPMETHOD(uint64_t, u64, mul, *) | ||||
| DIVMETHOD(uint64_t, u64, div, /) | ||||
| DIVMETHOD(uint64_t, u64, rem, %) | ||||
| DIVMETHOD(uint64_t, u64, mod, %) | ||||
| DIVMETHODINVERT(uint64_t, u64, div, /) | ||||
| DIVMETHODINVERT(uint64_t, u64, rem, %) | ||||
| DIVMETHODINVERT(uint64_t, u64, mod, %) | ||||
| DIVMETHODINVERT(uint64_t, u64, divi, /) | ||||
| DIVMETHODINVERT(uint64_t, u64, modi, %) | ||||
| OPMETHOD(uint64_t, u64, and, &) | ||||
| OPMETHOD(uint64_t, u64, or, |) | ||||
| OPMETHOD(uint64_t, u64, xor, ^) | ||||
| UNARYMETHOD(uint64_t, u64, not, ~) | ||||
| OPMETHOD(uint64_t, u64, lshift, <<) | ||||
| OPMETHOD(uint64_t, u64, rshift, >>) | ||||
|  | ||||
| @@ -594,8 +555,6 @@ static JanetMethod it_s64_methods[] = { | ||||
|     {"r*", cfun_it_s64_mul}, | ||||
|     {"/", cfun_it_s64_div}, | ||||
|     {"r/", cfun_it_s64_divi}, | ||||
|     {"div", cfun_it_s64_divf}, | ||||
|     {"rdiv", cfun_it_s64_divfi}, | ||||
|     {"mod", cfun_it_s64_mod}, | ||||
|     {"rmod", cfun_it_s64_modi}, | ||||
|     {"%", cfun_it_s64_rem}, | ||||
| @@ -606,7 +565,6 @@ static JanetMethod it_s64_methods[] = { | ||||
|     {"r|", cfun_it_s64_or}, | ||||
|     {"^", cfun_it_s64_xor}, | ||||
|     {"r^", cfun_it_s64_xor}, | ||||
|     {"~", cfun_it_s64_not}, | ||||
|     {"<<", cfun_it_s64_lshift}, | ||||
|     {">>", cfun_it_s64_rshift}, | ||||
|     {"compare", cfun_it_s64_compare}, | ||||
| @@ -622,19 +580,16 @@ static JanetMethod it_u64_methods[] = { | ||||
|     {"r*", cfun_it_u64_mul}, | ||||
|     {"/", cfun_it_u64_div}, | ||||
|     {"r/", cfun_it_u64_divi}, | ||||
|     {"div", cfun_it_u64_div}, | ||||
|     {"rdiv", cfun_it_u64_divi}, | ||||
|     {"mod", cfun_it_u64_mod}, | ||||
|     {"rmod", cfun_it_u64_modi}, | ||||
|     {"%", cfun_it_u64_rem}, | ||||
|     {"r%", cfun_it_u64_remi}, | ||||
|     {"%", cfun_it_u64_mod}, | ||||
|     {"r%", cfun_it_u64_modi}, | ||||
|     {"&", cfun_it_u64_and}, | ||||
|     {"r&", cfun_it_u64_and}, | ||||
|     {"|", cfun_it_u64_or}, | ||||
|     {"r|", cfun_it_u64_or}, | ||||
|     {"^", cfun_it_u64_xor}, | ||||
|     {"r^", cfun_it_u64_xor}, | ||||
|     {"~", cfun_it_u64_not}, | ||||
|     {"<<", cfun_it_u64_lshift}, | ||||
|     {">>", cfun_it_u64_rshift}, | ||||
|     {"compare", cfun_it_u64_compare}, | ||||
|   | ||||
| @@ -131,7 +131,7 @@ JANET_CORE_FN(cfun_io_temp, | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_io_fopen, | ||||
|               "(file/open path &opt mode buffer-size)", | ||||
|               "(file/open path &opt mode)", | ||||
|               "Open a file. `path` is an absolute or relative path, and " | ||||
|               "`mode` is a set of flags indicating the mode to open the file in. " | ||||
|               "`mode` is a keyword where each character represents a flag. If the file " | ||||
| @@ -143,9 +143,8 @@ JANET_CORE_FN(cfun_io_fopen, | ||||
|               "Following one of the initial flags, 0 or more of the following flags can be appended:\n\n" | ||||
|               "* b - open the file in binary mode (rather than text mode)\n\n" | ||||
|               "* + - append to the file instead of overwriting it\n\n" | ||||
|               "* n - error if the file cannot be opened instead of returning nil\n\n" | ||||
|               "See fopen (<stdio.h>, C99) for further details.") { | ||||
|     janet_arity(argc, 1, 3); | ||||
|               "* n - error if the file cannot be opened instead of returning nil") { | ||||
|     janet_arity(argc, 1, 2); | ||||
|     const uint8_t *fname = janet_getstring(argv, 0); | ||||
|     const uint8_t *fmode; | ||||
|     int32_t flags; | ||||
| @@ -158,15 +157,6 @@ JANET_CORE_FN(cfun_io_fopen, | ||||
|         flags = JANET_FILE_READ; | ||||
|     } | ||||
|     FILE *f = fopen((const char *)fname, (const char *)fmode); | ||||
|     if (f != NULL) { | ||||
|         size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ); | ||||
|         if (bufsize != BUFSIZ) { | ||||
|             int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize); | ||||
|             if (result) { | ||||
|                 janet_panic("failed to set buffer size for file"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return f ? janet_makefile(f, flags) | ||||
|            : (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil()) | ||||
|            : janet_wrap_nil(); | ||||
| @@ -514,6 +504,7 @@ static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline, | ||||
|     return janet_wrap_nil(); | ||||
| } | ||||
|  | ||||
|  | ||||
| static Janet cfun_io_print_impl(int32_t argc, Janet *argv, | ||||
|                                 int newline, const char *name, FILE *dflt_file) { | ||||
|     Janet x = janet_dyn(name); | ||||
|   | ||||
							
								
								
									
										116
									
								
								src/core/marsh.c
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								src/core/marsh.c
									
									
									
									
									
								
							| @@ -154,7 +154,7 @@ static void pushbytes(MarshalState *st, const uint8_t *bytes, int32_t len) { | ||||
|     janet_buffer_push_bytes(st->buf, bytes, len); | ||||
| } | ||||
|  | ||||
| static void pushpointer(MarshalState *st, const void *ptr) { | ||||
| static void pushpointer(MarshalState *st, void *ptr) { | ||||
|     janet_buffer_push_bytes(st->buf, (const uint8_t *) &ptr, sizeof(ptr)); | ||||
| } | ||||
|  | ||||
| @@ -246,7 +246,6 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) { | ||||
|     } | ||||
|     /* Add to lookup */ | ||||
|     janet_v_push(st->seen_defs, def); | ||||
|  | ||||
|     pushint(st, def->flags); | ||||
|     pushint(st, def->slotcount); | ||||
|     pushint(st, def->arity); | ||||
| @@ -267,14 +266,14 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) { | ||||
|  | ||||
|     /* marshal constants */ | ||||
|     for (int32_t i = 0; i < def->constants_length; i++) | ||||
|         marshal_one(st, def->constants[i], flags + 1); | ||||
|         marshal_one(st, def->constants[i], flags); | ||||
|  | ||||
|     /* Marshal symbol map, if needed */ | ||||
|     for (int32_t i = 0; i < def->symbolmap_length; i++) { | ||||
|         pushint(st, (int32_t) def->symbolmap[i].birth_pc); | ||||
|         pushint(st, (int32_t) def->symbolmap[i].death_pc); | ||||
|         pushint(st, (int32_t) def->symbolmap[i].slot_index); | ||||
|         marshal_one(st, janet_wrap_symbol(def->symbolmap[i].symbol), flags + 1); | ||||
|         marshal_one(st, janet_wrap_symbol(def->symbolmap[i].symbol), flags); | ||||
|     } | ||||
|  | ||||
|     /* marshal the bytecode */ | ||||
| @@ -363,15 +362,6 @@ void janet_marshal_int(JanetMarshalContext *ctx, int32_t value) { | ||||
|     pushint(st, value); | ||||
| } | ||||
|  | ||||
| /* Only use in unsafe - don't marshal pointers otherwise */ | ||||
| void janet_marshal_ptr(JanetMarshalContext *ctx, const void *ptr) { | ||||
|     if (!(ctx->flags & JANET_MARSHAL_UNSAFE)) { | ||||
|         janet_panic("can only marshal pointers in unsafe mode"); | ||||
|     } | ||||
|     MarshalState *st = (MarshalState *)(ctx->m_state); | ||||
|     pushpointer(st, ptr); | ||||
| } | ||||
|  | ||||
| void janet_marshal_byte(JanetMarshalContext *ctx, uint8_t value) { | ||||
|     MarshalState *st = (MarshalState *)(ctx->m_state); | ||||
|     pushbyte(st, value); | ||||
| @@ -388,27 +378,18 @@ void janet_marshal_janet(JanetMarshalContext *ctx, Janet x) { | ||||
|     marshal_one(st, x, ctx->flags + 1); | ||||
| } | ||||
|  | ||||
| #ifdef JANET_MARSHAL_DEBUG | ||||
| #define MARK_SEEN() \ | ||||
|     do { if (st->maybe_cycles) { \ | ||||
|         Janet _check = janet_table_get(&st->seen, x); \ | ||||
|         if (!janet_checktype(_check, JANET_NIL)) janet_eprintf("double MARK_SEEN on %v\n", x); \ | ||||
|         janet_eprintf("made reference %d (%t) to %v\n", st->nextid, x, x); \ | ||||
|         janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); \ | ||||
|     } } while (0) | ||||
| #else | ||||
| #define MARK_SEEN() \ | ||||
|     do { if (st->maybe_cycles) { \ | ||||
|         janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); \ | ||||
|     } } while (0) | ||||
| #endif | ||||
|  | ||||
| void janet_marshal_abstract(JanetMarshalContext *ctx, void *abstract) { | ||||
|     MarshalState *st = (MarshalState *)(ctx->m_state); | ||||
|     Janet x = janet_wrap_abstract(abstract); | ||||
|     MARK_SEEN(); | ||||
|     if (st->maybe_cycles) { | ||||
|         janet_table_put(&st->seen, | ||||
|                         janet_wrap_abstract(abstract), | ||||
|                         janet_wrap_integer(st->nextid++)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #define MARK_SEEN() \ | ||||
|     do { if (st->maybe_cycles) janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); } while (0) | ||||
|  | ||||
| static void marshal_one_abstract(MarshalState *st, Janet x, int flags) { | ||||
|     void *abstract = janet_unwrap_abstract(x); | ||||
| #ifdef JANET_EV | ||||
| @@ -430,7 +411,7 @@ static void marshal_one_abstract(MarshalState *st, Janet x, int flags) { | ||||
|     if (at->marshal) { | ||||
|         pushbyte(st, LB_ABSTRACT); | ||||
|         marshal_one(st, janet_csymbolv(at->name), flags + 1); | ||||
|         JanetMarshalContext context = {st, NULL, flags + 1, NULL, at}; | ||||
|         JanetMarshalContext context = {st, NULL, flags, NULL, at}; | ||||
|         at->marshal(abstract, &context); | ||||
|     } else { | ||||
|         janet_panicf("cannot marshal %p", x); | ||||
| @@ -747,22 +728,9 @@ static uint64_t read64(UnmarshalState *st, const uint8_t **atdata) { | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| #ifdef JANET_MARSHAL_DEBUG | ||||
| static void dump_reference_table(UnmarshalState *st) { | ||||
|     for (int32_t i = 0; i < janet_v_count(st->lookup); i++) { | ||||
|         janet_eprintf("  reference %d (%t) = %v\n", i, st->lookup[i], st->lookup[i]); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /* Assert a janet type */ | ||||
| static void janet_asserttype(Janet x, JanetType t, UnmarshalState *st) { | ||||
| static void janet_asserttype(Janet x, JanetType t) { | ||||
|     if (!janet_checktype(x, t)) { | ||||
| #ifdef JANET_MARSHAL_DEBUG | ||||
|         dump_reference_table(st); | ||||
| #else | ||||
|         (void) st; | ||||
| #endif | ||||
|         janet_panicf("expected type %T, got %v", 1 << t, x); | ||||
|     } | ||||
| } | ||||
| @@ -814,7 +782,7 @@ static const uint8_t *unmarshal_one_env( | ||||
|             Janet fiberv; | ||||
|             /* On stack variant */ | ||||
|             data = unmarshal_one(st, data, &fiberv, flags); | ||||
|             janet_asserttype(fiberv, JANET_FIBER, st); | ||||
|             janet_asserttype(fiberv, JANET_FIBER); | ||||
|             env->as.fiber = janet_unwrap_fiber(fiberv); | ||||
|             /* Negative offset indicates untrusted input */ | ||||
|             env->offset = -offset; | ||||
| @@ -912,13 +880,13 @@ static const uint8_t *unmarshal_one_def( | ||||
|         if (def->flags & JANET_FUNCDEF_FLAG_HASNAME) { | ||||
|             Janet x; | ||||
|             data = unmarshal_one(st, data, &x, flags + 1); | ||||
|             janet_asserttype(x, JANET_STRING, st); | ||||
|             janet_asserttype(x, JANET_STRING); | ||||
|             def->name = janet_unwrap_string(x); | ||||
|         } | ||||
|         if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCE) { | ||||
|             Janet x; | ||||
|             data = unmarshal_one(st, data, &x, flags + 1); | ||||
|             janet_asserttype(x, JANET_STRING, st); | ||||
|             janet_asserttype(x, JANET_STRING); | ||||
|             def->source = janet_unwrap_string(x); | ||||
|         } | ||||
|  | ||||
| @@ -948,9 +916,8 @@ static const uint8_t *unmarshal_one_def( | ||||
|                 def->symbolmap[i].slot_index = (uint32_t) readint(st, &data); | ||||
|                 Janet value; | ||||
|                 data = unmarshal_one(st, data, &value, flags + 1); | ||||
|                 if (!janet_checktype(value, JANET_SYMBOL)) { | ||||
|                     janet_panicf("corrupted symbolmap when unmarshalling debug info, got %v", value); | ||||
|                 } | ||||
|                 if (!janet_checktype(value, JANET_SYMBOL)) | ||||
|                     janet_panic("expected symbol in symbol map"); | ||||
|                 def->symbolmap[i].symbol = janet_unwrap_symbol(value); | ||||
|             } | ||||
|             def->symbolmap_length = (uint32_t) symbolmap_length; | ||||
| @@ -1048,11 +1015,9 @@ static const uint8_t *unmarshal_one_fiber( | ||||
|     fiber->env = NULL; | ||||
|     fiber->last_value = janet_wrap_nil(); | ||||
| #ifdef JANET_EV | ||||
|     fiber->waiting = NULL; | ||||
|     fiber->sched_id = 0; | ||||
|     fiber->supervisor_channel = NULL; | ||||
|     fiber->ev_state = NULL; | ||||
|     fiber->ev_callback = NULL; | ||||
|     fiber->ev_stream = NULL; | ||||
| #endif | ||||
|  | ||||
|     /* Push fiber to seen stack */ | ||||
| @@ -1101,7 +1066,7 @@ static const uint8_t *unmarshal_one_fiber( | ||||
|         /* Get function */ | ||||
|         Janet funcv; | ||||
|         data = unmarshal_one(st, data, &funcv, flags + 1); | ||||
|         janet_asserttype(funcv, JANET_FUNCTION, st); | ||||
|         janet_asserttype(funcv, JANET_FUNCTION); | ||||
|         func = janet_unwrap_function(funcv); | ||||
|         def = func->def; | ||||
|  | ||||
| @@ -1147,7 +1112,7 @@ static const uint8_t *unmarshal_one_fiber( | ||||
|         Janet envv; | ||||
|         fiber_flags &= ~JANET_FIBER_FLAG_HASENV; | ||||
|         data = unmarshal_one(st, data, &envv, flags + 1); | ||||
|         janet_asserttype(envv, JANET_TABLE, st); | ||||
|         janet_asserttype(envv, JANET_TABLE); | ||||
|         fiber_env = janet_unwrap_table(envv); | ||||
|     } | ||||
|  | ||||
| @@ -1156,7 +1121,7 @@ static const uint8_t *unmarshal_one_fiber( | ||||
|         Janet fiberv; | ||||
|         fiber_flags &= ~JANET_FIBER_FLAG_HASCHILD; | ||||
|         data = unmarshal_one(st, data, &fiberv, flags + 1); | ||||
|         janet_asserttype(fiberv, JANET_FIBER, st); | ||||
|         janet_asserttype(fiberv, JANET_FIBER); | ||||
|         fiber->child = janet_unwrap_fiber(fiberv); | ||||
|     } | ||||
|  | ||||
| @@ -1200,18 +1165,6 @@ int64_t janet_unmarshal_int64(JanetMarshalContext *ctx) { | ||||
|     return read64(st, &(ctx->data)); | ||||
| } | ||||
|  | ||||
| void *janet_unmarshal_ptr(JanetMarshalContext *ctx) { | ||||
|     if (!(ctx->flags & JANET_MARSHAL_UNSAFE)) { | ||||
|         janet_panic("can only unmarshal pointers in unsafe mode"); | ||||
|     } | ||||
|     UnmarshalState *st = (UnmarshalState *)(ctx->u_state); | ||||
|     void *ptr; | ||||
|     MARSH_EOS(st, ctx->data + sizeof(void *) - 1); | ||||
|     memcpy((char *) &ptr, ctx->data, sizeof(void *)); | ||||
|     ctx->data += sizeof(void *); | ||||
|     return ptr; | ||||
| } | ||||
|  | ||||
| uint8_t janet_unmarshal_byte(JanetMarshalContext *ctx) { | ||||
|     UnmarshalState *st = (UnmarshalState *)(ctx->u_state); | ||||
|     MARSH_EOS(st, ctx->data); | ||||
| @@ -1247,18 +1200,6 @@ void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) { | ||||
|     return p; | ||||
| } | ||||
|  | ||||
| void *janet_unmarshal_abstract_threaded(JanetMarshalContext *ctx, size_t size) { | ||||
| #ifdef JANET_THREADS | ||||
|     void *p = janet_abstract_threaded(ctx->at, size); | ||||
|     janet_unmarshal_abstract_reuse(ctx, p); | ||||
|     return p; | ||||
| #else | ||||
|     (void) ctx; | ||||
|     (void) size; | ||||
|     janet_panic("threaded abstracts not supported"); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *data, Janet *out, int flags) { | ||||
|     Janet key; | ||||
|     data = unmarshal_one(st, data, &key, flags + 1); | ||||
| @@ -1266,9 +1207,7 @@ static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t * | ||||
|     if (at == NULL) janet_panic("unknown abstract type"); | ||||
|     if (at->unmarshal) { | ||||
|         JanetMarshalContext context = {NULL, st, flags, data, at}; | ||||
|         void *abst = at->unmarshal(&context); | ||||
|         janet_assert(abst != NULL, "null pointer abstract"); | ||||
|         *out = janet_wrap_abstract(abst); | ||||
|         *out = janet_wrap_abstract(at->unmarshal(&context)); | ||||
|         if (context.at != NULL) { | ||||
|             janet_panic("janet_unmarshal_abstract not called"); | ||||
|         } | ||||
| @@ -1369,7 +1308,7 @@ static const uint8_t *unmarshal_one( | ||||
|         } | ||||
|         case LB_FIBER: { | ||||
|             JanetFiber *fiber; | ||||
|             data = unmarshal_one_fiber(st, data + 1, &fiber, flags + 1); | ||||
|             data = unmarshal_one_fiber(st, data + 1, &fiber, flags); | ||||
|             *out = janet_wrap_fiber(fiber); | ||||
|             return data; | ||||
|         } | ||||
| @@ -1384,9 +1323,6 @@ static const uint8_t *unmarshal_one( | ||||
|             func = janet_gcalloc(JANET_MEMORY_FUNCTION, sizeof(JanetFunction) + | ||||
|                                  len * sizeof(JanetFuncEnv)); | ||||
|             func->def = NULL; | ||||
|             for (int32_t i = 0; i < len; i++) { | ||||
|                 func->envs[i] = NULL; | ||||
|             } | ||||
|             *out = janet_wrap_function(func); | ||||
|             janet_v_push(st->lookup, *out); | ||||
|             data = unmarshal_one_def(st, data, &def, flags + 1); | ||||
| @@ -1440,7 +1376,7 @@ static const uint8_t *unmarshal_one( | ||||
|                 if (lead == LB_STRUCT_PROTO) { | ||||
|                     Janet proto; | ||||
|                     data = unmarshal_one(st, data, &proto, flags + 1); | ||||
|                     janet_asserttype(proto, JANET_STRUCT, st); | ||||
|                     janet_asserttype(proto, JANET_STRUCT); | ||||
|                     janet_struct_proto(struct_) = janet_unwrap_struct(proto); | ||||
|                 } | ||||
|                 for (int32_t i = 0; i < len; i++) { | ||||
| @@ -1463,7 +1399,7 @@ static const uint8_t *unmarshal_one( | ||||
|                 if (lead == LB_TABLE_PROTO) { | ||||
|                     Janet proto; | ||||
|                     data = unmarshal_one(st, data, &proto, flags + 1); | ||||
|                     janet_asserttype(proto, JANET_TABLE, st); | ||||
|                     janet_asserttype(proto, JANET_TABLE); | ||||
|                     t->proto = janet_unwrap_table(proto); | ||||
|                 } | ||||
|                 for (int32_t i = 0; i < len; i++) { | ||||
|   | ||||
| @@ -119,7 +119,7 @@ double janet_rng_double(JanetRNG *rng) { | ||||
|  | ||||
| JANET_CORE_FN(cfun_rng_make, | ||||
|               "(math/rng &opt seed)", | ||||
|               "Creates a Pseudo-Random number generator, with an optional seed. " | ||||
|               "Creates a Psuedo-Random number generator, with an optional seed. " | ||||
|               "The seed should be an unsigned 32 bit integer or a buffer. " | ||||
|               "Do not use this for cryptography. Returns a core/rng abstract type." | ||||
|              ) { | ||||
| @@ -411,11 +411,11 @@ void janet_lib_math(JanetTable *env) { | ||||
|     JANET_CORE_DEF(env, "math/int32-min", janet_wrap_number(INT32_MIN), | ||||
|                    "The minimum contiguous integer representable by a 32 bit signed integer"); | ||||
|     JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX), | ||||
|                    "The maximum contiguous integer representable by a 32 bit signed integer"); | ||||
|                    "The maximum contiguous integer represtenable by a 32 bit signed integer"); | ||||
|     JANET_CORE_DEF(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE), | ||||
|                    "The minimum contiguous integer representable by a double (2^53)"); | ||||
|     JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE), | ||||
|                    "The maximum contiguous integer representable by a double (-(2^53))"); | ||||
|                    "The maximum contiguous integer represtenable by a double (-(2^53))"); | ||||
| #ifdef NAN | ||||
|     JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)"); | ||||
| #else | ||||
|   | ||||
							
								
								
									
										178
									
								
								src/core/net.c
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								src/core/net.c
									
									
									
									
									
								
							| @@ -24,7 +24,6 @@ | ||||
| #include "features.h" | ||||
| #include <janet.h> | ||||
| #include "util.h" | ||||
| #include "fiber.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef JANET_NET | ||||
| @@ -112,61 +111,12 @@ static void janet_net_socknoblock(JSock s) { | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /* 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; | ||||
|         case JANET_ASYNC_EVENT_CLOSE: | ||||
|             janet_cancel(fiber, janet_cstringv("stream closed")); | ||||
|             janet_async_end(fiber); | ||||
|             return; | ||||
|     } | ||||
| #ifdef JANET_WINDOWS | ||||
|     int res = 0; | ||||
|     int size = sizeof(res); | ||||
|     int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_ERROR, (char *)&res, &size); | ||||
| #else | ||||
|     int res = 0; | ||||
|     socklen_t size = sizeof res; | ||||
|     int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size); | ||||
| #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))); | ||||
|             stream->flags |= JANET_STREAM_TOCLOSE; | ||||
|         } | ||||
|     } else { | ||||
|         janet_cancel(fiber, janet_ev_lasterr()); | ||||
|         stream->flags |= JANET_STREAM_TOCLOSE; | ||||
|     } | ||||
|     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 | ||||
| } | ||||
|  | ||||
| /* State machine for accepting connections. */ | ||||
|  | ||||
| #ifdef JANET_WINDOWS | ||||
|  | ||||
| typedef struct { | ||||
|     JanetListenerState head; | ||||
|     WSAOVERLAPPED overlapped; | ||||
|     JanetFunction *function; | ||||
|     JanetStream *lstream; | ||||
| @@ -174,10 +124,10 @@ typedef struct { | ||||
|     char buf[1024]; | ||||
| } NetStateAccept; | ||||
|  | ||||
| static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err); | ||||
| static int net_sched_accept_impl(NetStateAccept *state, Janet *err); | ||||
|  | ||||
| void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|     NetStateAccept *state = (NetStateAccept *)fiber->ev_state; | ||||
| JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) { | ||||
|     NetStateAccept *state = (NetStateAccept *)s; | ||||
|     switch (event) { | ||||
|         default: | ||||
|             break; | ||||
| @@ -188,58 +138,55 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|             break; | ||||
|         } | ||||
|         case JANET_ASYNC_EVENT_CLOSE: | ||||
|             janet_schedule(fiber, janet_wrap_nil()); | ||||
|             janet_async_end(fiber); | ||||
|             return; | ||||
|             janet_schedule(s->fiber, janet_wrap_nil()); | ||||
|             return JANET_ASYNC_STATUS_DONE; | ||||
|         case JANET_ASYNC_EVENT_COMPLETE: { | ||||
|             if (state->astream->flags & JANET_STREAM_CLOSED) { | ||||
|                 janet_cancel(fiber, janet_cstringv("failed to accept connection")); | ||||
|                 janet_async_end(fiber); | ||||
|                 return; | ||||
|                 janet_cancel(s->fiber, janet_cstringv("failed to accept connection")); | ||||
|                 return JANET_ASYNC_STATUS_DONE; | ||||
|             } | ||||
|             SOCKET lsock = (SOCKET) state->lstream->handle; | ||||
|             if (NO_ERROR != setsockopt((SOCKET) state->astream->handle, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, | ||||
|                                        (char *) &lsock, sizeof(lsock))) { | ||||
|                 janet_cancel(fiber, janet_cstringv("failed to accept connection")); | ||||
|                 janet_async_end(fiber); | ||||
|                 return; | ||||
|                 janet_cancel(s->fiber, janet_cstringv("failed to accept connection")); | ||||
|                 return JANET_ASYNC_STATUS_DONE; | ||||
|             } | ||||
|  | ||||
|             Janet streamv = janet_wrap_abstract(state->astream); | ||||
|             if (state->function) { | ||||
|                 /* Schedule worker */ | ||||
|                 JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv); | ||||
|                 sub_fiber->supervisor_channel = fiber->supervisor_channel; | ||||
|                 janet_schedule(sub_fiber, janet_wrap_nil()); | ||||
|                 JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv); | ||||
|                 fiber->supervisor_channel = s->fiber->supervisor_channel; | ||||
|                 janet_schedule(fiber, janet_wrap_nil()); | ||||
|                 /* Now listen again for next connection */ | ||||
|                 Janet err; | ||||
|                 if (net_sched_accept_impl(state, fiber, &err)) { | ||||
|                     janet_cancel(fiber, err); | ||||
|                     janet_async_end(fiber); | ||||
|                     return; | ||||
|                 if (net_sched_accept_impl(state, &err)) { | ||||
|                     janet_cancel(s->fiber, err); | ||||
|                     return JANET_ASYNC_STATUS_DONE; | ||||
|                 } | ||||
|             } else { | ||||
|                 janet_schedule(fiber, streamv); | ||||
|                 janet_async_end(fiber); | ||||
|                 return; | ||||
|                 janet_schedule(s->fiber, streamv); | ||||
|                 return JANET_ASYNC_STATUS_DONE; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return JANET_ASYNC_STATUS_NOT_DONE; | ||||
| } | ||||
|  | ||||
| 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)); | ||||
|     JanetListenerState *s = janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL); | ||||
|     NetStateAccept *state = (NetStateAccept *)s; | ||||
|     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); | ||||
|     s->tag = &state->overlapped; | ||||
|     if (net_sched_accept_impl(state, &err)) janet_panicv(err); | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
| static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err) { | ||||
| static int net_sched_accept_impl(NetStateAccept *state, Janet *err) { | ||||
|     SOCKET lsock = (SOCKET) state->lstream->handle; | ||||
|     SOCKET asock = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); | ||||
|     if (asock == INVALID_SOCKET) { | ||||
| @@ -251,11 +198,7 @@ static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet | ||||
|     int socksize = sizeof(SOCKADDR_STORAGE) + 16; | ||||
|     if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped)) { | ||||
|         int code = WSAGetLastError(); | ||||
|         if (code == WSA_IO_PENDING) { | ||||
|             /* indicates io is happening async */ | ||||
|             fiber->flags |= JANET_FIBER_EV_FLAG_IN_FLIGHT; | ||||
|             return 0; | ||||
|         } | ||||
|         if (code == WSA_IO_PENDING) return 0; /* indicates io is happening async */ | ||||
|         *err = janet_ev_lasterr(); | ||||
|         return 1; | ||||
|     } | ||||
| @@ -265,12 +208,12 @@ static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet | ||||
| #else | ||||
|  | ||||
| typedef struct { | ||||
|     JanetListenerState head; | ||||
|     JanetFunction *function; | ||||
| } NetStateAccept; | ||||
|  | ||||
| void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|     JanetStream *stream = fiber->ev_stream; | ||||
|     NetStateAccept *state = (NetStateAccept *)fiber->ev_state; | ||||
| JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) { | ||||
|     NetStateAccept *state = (NetStateAccept *)s; | ||||
|     switch (event) { | ||||
|         default: | ||||
|             break; | ||||
| @@ -279,44 +222,41 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) { | ||||
|             break; | ||||
|         } | ||||
|         case JANET_ASYNC_EVENT_CLOSE: | ||||
|             janet_schedule(fiber, janet_wrap_nil()); | ||||
|             janet_async_end(fiber); | ||||
|             return; | ||||
|         case JANET_ASYNC_EVENT_USER: | ||||
|             janet_schedule(s->fiber, janet_wrap_nil()); | ||||
|             return JANET_ASYNC_STATUS_DONE; | ||||
|         case JANET_ASYNC_EVENT_READ: { | ||||
| #if defined(JANET_LINUX) | ||||
|             JSock connfd = accept4(stream->handle, NULL, NULL, SOCK_CLOEXEC); | ||||
|             JSock connfd = accept4(s->stream->handle, NULL, NULL, SOCK_CLOEXEC); | ||||
| #else | ||||
|             /* On BSDs, CLOEXEC should be inherited from server socket */ | ||||
|             JSock connfd = accept(stream->handle, NULL, NULL); | ||||
|             JSock connfd = accept(s->stream->handle, NULL, NULL); | ||||
| #endif | ||||
|             if (JSOCKVALID(connfd)) { | ||||
|                 janet_net_socknoblock(connfd); | ||||
|                 JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE); | ||||
|                 Janet streamv = janet_wrap_abstract(stream); | ||||
|                 if (state->function) { | ||||
|                     JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv); | ||||
|                     sub_fiber->supervisor_channel = fiber->supervisor_channel; | ||||
|                     janet_schedule(sub_fiber, janet_wrap_nil()); | ||||
|                     JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv); | ||||
|                     fiber->supervisor_channel = s->fiber->supervisor_channel; | ||||
|                     janet_schedule(fiber, janet_wrap_nil()); | ||||
|                 } else { | ||||
|                     janet_schedule(fiber, streamv); | ||||
|                     janet_async_end(fiber); | ||||
|                     return; | ||||
|                     janet_schedule(s->fiber, streamv); | ||||
|                     return JANET_ASYNC_STATUS_DONE; | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     return JANET_ASYNC_STATUS_NOT_DONE; | ||||
| } | ||||
|  | ||||
| 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 = (NetStateAccept *) janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL); | ||||
|     state->function = fun; | ||||
|     net_callback_accept(f, JANET_ASYNC_EVENT_USER); | ||||
|     janet_await(); | ||||
| } | ||||
|  | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* Adress info */ | ||||
| @@ -477,6 +417,7 @@ JANET_CORE_FN(cfun_net_connect, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /* Create socket */ | ||||
|     JSock sock = JSOCKDEFAULT; | ||||
|     void *addr = NULL; | ||||
| @@ -519,7 +460,7 @@ JANET_CORE_FN(cfun_net_connect, | ||||
|     if (binding) { | ||||
|         struct addrinfo *rp = NULL; | ||||
|         int did_bind = 0; | ||||
|         for (rp = binding; rp != NULL; rp = rp->ai_next) { | ||||
|         for (rp = ai; rp != NULL; rp = rp->ai_next) { | ||||
|             if (bind(sock, rp->ai_addr, (int) rp->ai_addrlen) == 0) { | ||||
|                 did_bind = 1; | ||||
|                 break; | ||||
| @@ -536,20 +477,14 @@ JANET_CORE_FN(cfun_net_connect, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* Wrap socket in abstract type JanetStream */ | ||||
|     JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE); | ||||
|  | ||||
|     /* Set up the socket for non-blocking IO before connecting */ | ||||
|     janet_net_socknoblock(sock); | ||||
|  | ||||
|     /* Connect to socket */ | ||||
| #ifdef JANET_WINDOWS | ||||
|     int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL); | ||||
|     int err = WSAGetLastError(); | ||||
|     Janet lasterr = janet_ev_lasterr(); | ||||
|     freeaddrinfo(ai); | ||||
| #else | ||||
|     int status = connect(sock, addr, addrlen); | ||||
|     int err = errno; | ||||
|     Janet lasterr = janet_ev_lasterr(); | ||||
|     if (is_unix) { | ||||
|         janet_free(ai); | ||||
|     } else { | ||||
| @@ -557,20 +492,17 @@ JANET_CORE_FN(cfun_net_connect, | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     if (status) { | ||||
| #ifdef JANET_WINDOWS | ||||
|         if (err != WSAEWOULDBLOCK) { | ||||
| #else | ||||
|         if (err != EINPROGRESS) { | ||||
| #endif | ||||
|             JSOCKCLOSE(sock); | ||||
|             Janet lasterr = janet_ev_lasterr(); | ||||
|             janet_panicf("could not connect socket: %V", lasterr); | ||||
|         } | ||||
|     if (status == -1) { | ||||
|         JSOCKCLOSE(sock); | ||||
|         janet_panicf("could not connect socket: %V", lasterr); | ||||
|     } | ||||
|  | ||||
|     net_sched_connect(stream); | ||||
|     janet_await(); | ||||
|     /* Set up the socket for non-blocking IO after connect - TODO - non-blocking connect? */ | ||||
|     janet_net_socknoblock(sock); | ||||
|  | ||||
|     /* Wrap socket in abstract type JanetStream */ | ||||
|     JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE); | ||||
|     return janet_wrap_abstract(stream); | ||||
| } | ||||
|  | ||||
| static const char *serverify_socket(JSock sfd) { | ||||
| @@ -958,7 +890,7 @@ static const struct sockopt_type sockopt_type_list[] = { | ||||
|     { "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER }, | ||||
|     { "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER }, | ||||
|     { "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER }, | ||||
|     { NULL, 0, 0, JANET_POINTER } | ||||
|     { NULL } | ||||
| }; | ||||
|  | ||||
| JANET_CORE_FN(cfun_net_setsockopt, | ||||
| @@ -1010,7 +942,7 @@ JANET_CORE_FN(cfun_net_setsockopt, | ||||
|         const char *addr = janet_getcstring(argv, 2); | ||||
|         memset(&val.v_mreq, 0, sizeof val.v_mreq); | ||||
|         val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY); | ||||
|         inet_pton(AF_INET, addr, &val.v_mreq.imr_multiaddr.s_addr); | ||||
|         val.v_mreq.imr_multiaddr.s_addr = inet_addr(addr); | ||||
|         optlen = sizeof(val.v_mreq); | ||||
|     } else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) { | ||||
|         const char *addr = janet_getcstring(argv, 2); | ||||
|   | ||||
							
								
								
									
										299
									
								
								src/core/os.c
									
									
									
									
									
								
							
							
						
						
									
										299
									
								
								src/core/os.c
									
									
									
									
									
								
							| @@ -289,6 +289,7 @@ JANET_CORE_FN(os_cpu_count, | ||||
| #endif | ||||
| } | ||||
|  | ||||
|  | ||||
| #ifndef JANET_NO_PROCESSES | ||||
|  | ||||
| /* Get env for os_execute */ | ||||
| @@ -517,6 +518,7 @@ static JanetEVGenericMessage janet_proc_wait_subr(JanetEVGenericMessage args) { | ||||
|  | ||||
| /* Callback that is called in main thread when subroutine completes. */ | ||||
| static void janet_proc_wait_cb(JanetEVGenericMessage args) { | ||||
|     janet_ev_dec_refcount(); | ||||
|     JanetProc *proc = (JanetProc *) args.argp; | ||||
|     if (NULL != proc) { | ||||
|         int status = args.tag; | ||||
| @@ -622,111 +624,12 @@ JANET_CORE_FN(os_proc_wait, | ||||
| #endif | ||||
| } | ||||
|  | ||||
| struct keyword_signal { | ||||
|     const char *keyword; | ||||
|     int signal; | ||||
| }; | ||||
|  | ||||
| #ifndef JANET_WINDOWS | ||||
| static const struct keyword_signal signal_keywords[] = { | ||||
| #ifdef SIGKILL | ||||
|     {"kill", SIGKILL}, | ||||
| #endif | ||||
|     {"int", SIGINT}, | ||||
|     {"abrt", SIGABRT}, | ||||
|     {"fpe", SIGFPE}, | ||||
|     {"ill", SIGILL}, | ||||
|     {"segv", SIGSEGV}, | ||||
| #ifdef SIGTERM | ||||
|     {"term", SIGTERM}, | ||||
| #endif | ||||
| #ifdef SIGARLM | ||||
|     {"alrm", SIGALRM}, | ||||
| #endif | ||||
| #ifdef SIGHUP | ||||
|     {"hup", SIGHUP}, | ||||
| #endif | ||||
| #ifdef SIGPIPE | ||||
|     {"pipe", SIGPIPE}, | ||||
| #endif | ||||
| #ifdef SIGQUIT | ||||
|     {"quit", SIGQUIT}, | ||||
| #endif | ||||
| #ifdef SIGUSR1 | ||||
|     {"usr1", SIGUSR1}, | ||||
| #endif | ||||
| #ifdef SIGUSR2 | ||||
|     {"usr2", SIGUSR2}, | ||||
| #endif | ||||
| #ifdef SIGCHLD | ||||
|     {"chld", SIGCHLD}, | ||||
| #endif | ||||
| #ifdef SIGCONT | ||||
|     {"cont", SIGCONT}, | ||||
| #endif | ||||
| #ifdef SIGSTOP | ||||
|     {"stop", SIGSTOP}, | ||||
| #endif | ||||
| #ifdef SIGTSTP | ||||
|     {"tstp", SIGTSTP}, | ||||
| #endif | ||||
| #ifdef SIGTTIN | ||||
|     {"ttin", SIGTTIN}, | ||||
| #endif | ||||
| #ifdef SIGTTOU | ||||
|     {"ttou", SIGTTOU}, | ||||
| #endif | ||||
| #ifdef SIGBUS | ||||
|     {"bus", SIGBUS}, | ||||
| #endif | ||||
| #ifdef SIGPOLL | ||||
|     {"poll", SIGPOLL}, | ||||
| #endif | ||||
| #ifdef SIGPROF | ||||
|     {"prof", SIGPROF}, | ||||
| #endif | ||||
| #ifdef SIGSYS | ||||
|     {"sys", SIGSYS}, | ||||
| #endif | ||||
| #ifdef SIGTRAP | ||||
|     {"trap", SIGTRAP}, | ||||
| #endif | ||||
| #ifdef SIGURG | ||||
|     {"urg", SIGURG}, | ||||
| #endif | ||||
| #ifdef SIGVTALRM | ||||
|     {"vtlarm", SIGVTALRM}, | ||||
| #endif | ||||
| #ifdef SIGXCPU | ||||
|     {"xcpu", SIGXCPU}, | ||||
| #endif | ||||
| #ifdef SIGXFSZ | ||||
|     {"xfsz", SIGXFSZ}, | ||||
| #endif | ||||
|     {NULL, 0}, | ||||
| }; | ||||
|  | ||||
| static int get_signal_kw(const Janet *argv, int32_t n) { | ||||
|     JanetKeyword signal_kw = janet_getkeyword(argv, n); | ||||
|     const struct keyword_signal *ptr = signal_keywords; | ||||
|     while (ptr->keyword) { | ||||
|         if (!janet_cstrcmp(signal_kw, ptr->keyword)) { | ||||
|             return ptr->signal; | ||||
|         } | ||||
|         ptr++; | ||||
|     } | ||||
|     janet_panicf("undefined signal %v", argv[n]); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| JANET_CORE_FN(os_proc_kill, | ||||
|               "(os/proc-kill proc &opt wait signal)", | ||||
|               "(os/proc-kill proc &opt wait)", | ||||
|               "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.") { | ||||
|     janet_arity(argc, 1, 3); | ||||
|               "returns the exit code. Otherwise, returns `proc`.") { | ||||
|     janet_arity(argc, 1, 2); | ||||
|     JanetProc *proc = janet_getabstract(argv, 0, &ProcAT); | ||||
|     if (proc->flags & JANET_PROC_WAITED) { | ||||
|         janet_panicf("cannot kill process that has already finished"); | ||||
| @@ -740,11 +643,7 @@ JANET_CORE_FN(os_proc_kill, | ||||
|     CloseHandle(proc->pHandle); | ||||
|     CloseHandle(proc->tHandle); | ||||
| #else | ||||
|     int signal = -1; | ||||
|     if (argc == 3) { | ||||
|         signal = get_signal_kw(argv, 2); | ||||
|     } | ||||
|     int status = kill(proc->pid, signal == -1 ? SIGKILL : signal); | ||||
|     int status = kill(proc->pid, SIGKILL); | ||||
|     if (status) { | ||||
|         janet_panic(strerror(errno)); | ||||
|     } | ||||
| @@ -803,105 +702,6 @@ static void close_handle(JanetHandle handle) { | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #ifdef JANET_EV | ||||
|  | ||||
| #ifndef JANET_WINDOWS | ||||
| static void janet_signal_callback(JanetEVGenericMessage msg) { | ||||
|     int sig = msg.tag; | ||||
|     if (msg.argi) janet_interpreter_interrupt_handled(NULL); | ||||
|     Janet handlerv = janet_table_get(&janet_vm.signal_handlers, janet_wrap_integer(sig)); | ||||
|     if (!janet_checktype(handlerv, JANET_FUNCTION)) { | ||||
|         /* Let another thread/process try to handle this */ | ||||
|         sigset_t set; | ||||
|         sigemptyset(&set); | ||||
|         sigaddset(&set, sig); | ||||
| #ifdef JANET_THREADS | ||||
|         pthread_sigmask(SIG_BLOCK, &set, NULL); | ||||
| #else | ||||
|         sigprocmask(SIG_BLOCK, &set, NULL); | ||||
| #endif | ||||
|         raise(sig); | ||||
|         return; | ||||
|     } | ||||
|     JanetFunction *handler = janet_unwrap_function(handlerv); | ||||
|     JanetFiber *fiber = janet_fiber(handler, 64, 0, NULL); | ||||
|     janet_schedule_soon(fiber, janet_wrap_nil(), JANET_SIGNAL_OK); | ||||
| } | ||||
|  | ||||
| static void janet_signal_trampoline_no_interrupt(int sig) { | ||||
|     /* Do not interact with global janet state here except for janet_ev_post_event, unsafe! */ | ||||
|     JanetEVGenericMessage msg; | ||||
|     memset(&msg, 0, sizeof(msg)); | ||||
|     msg.tag = sig; | ||||
|     janet_ev_post_event(&janet_vm, janet_signal_callback, msg); | ||||
| } | ||||
|  | ||||
| static void janet_signal_trampoline(int sig) { | ||||
|     /* Do not interact with global janet state here except for janet_ev_post_event, unsafe! */ | ||||
|     JanetEVGenericMessage msg; | ||||
|     memset(&msg, 0, sizeof(msg)); | ||||
|     msg.tag = sig; | ||||
|     msg.argi = 1; | ||||
|     janet_interpreter_interrupt(NULL); | ||||
|     janet_ev_post_event(&janet_vm, janet_signal_callback, msg); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| JANET_CORE_FN(os_sigaction, | ||||
|               "(os/sigaction which &opt handler interrupt-interpreter)", | ||||
|               "Add a signal handler for a given action. Use nil for the `handler` argument to remove a signal handler. " | ||||
|               "All signal handlers are the same as supported by `os/proc-kill`.") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_SIGNAL); | ||||
|     janet_arity(argc, 1, 3); | ||||
| #ifdef JANET_WINDOWS | ||||
|     (void) argv; | ||||
|     janet_panic("unsupported on this platform"); | ||||
| #else | ||||
|     /* TODO - per thread signal masks */ | ||||
|     int rc; | ||||
|     int sig = get_signal_kw(argv, 0); | ||||
|     JanetFunction *handler = janet_optfunction(argv, argc, 1, NULL); | ||||
|     int can_interrupt = janet_optboolean(argv, argc, 2, 0); | ||||
|     Janet oldhandler = janet_table_get(&janet_vm.signal_handlers, janet_wrap_integer(sig)); | ||||
|     if (!janet_checktype(oldhandler, JANET_NIL)) { | ||||
|         janet_gcunroot(oldhandler); | ||||
|     } | ||||
|     if (NULL != handler) { | ||||
|         Janet handlerv = janet_wrap_function(handler); | ||||
|         janet_gcroot(handlerv); | ||||
|         janet_table_put(&janet_vm.signal_handlers, janet_wrap_integer(sig), handlerv); | ||||
|     } else { | ||||
|         janet_table_put(&janet_vm.signal_handlers, janet_wrap_integer(sig), janet_wrap_nil()); | ||||
|     } | ||||
|     struct sigaction action; | ||||
|     sigset_t mask; | ||||
|     sigfillset(&mask); | ||||
|     memset(&action, 0, sizeof(action)); | ||||
|     if (can_interrupt) { | ||||
| #ifdef JANET_NO_INTERPRETER_INTERRUPT | ||||
|         janet_panic("interpreter interrupt not enabled"); | ||||
| #else | ||||
|         action.sa_handler = janet_signal_trampoline; | ||||
| #endif | ||||
|     } else { | ||||
|         action.sa_handler = janet_signal_trampoline_no_interrupt; | ||||
|     } | ||||
|     action.sa_mask = mask; | ||||
|     RETRY_EINTR(rc, sigaction(sig, &action, NULL)); | ||||
|     sigset_t set; | ||||
|     sigemptyset(&set); | ||||
|     sigaddset(&set, sig); | ||||
| #ifdef JANET_THREADS | ||||
|     pthread_sigmask(SIG_UNBLOCK, &set, NULL); | ||||
| #else | ||||
|     sigprocmask(SIG_UNBLOCK, &set, NULL); | ||||
| #endif | ||||
|     return janet_wrap_nil(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* Create piped file for os/execute and os/spawn. Need to be careful that we mark | ||||
|    the error flag if we can't create pipe and don't leak handles. *handle will be cleaned | ||||
|    up by the calling function. If everything goes well, *handle is owned by the calling function, | ||||
| @@ -1174,6 +974,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) { | ||||
|         startupInfo.hStdInput = (HANDLE) _get_osfhandle(0); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     if (pipe_out != JANET_HANDLE_NONE) { | ||||
|         startupInfo.hStdOutput = pipe_out; | ||||
|     } else if (new_out != JANET_HANDLE_NONE) { | ||||
| @@ -1244,16 +1045,14 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) { | ||||
|         posix_spawn_file_actions_addclose(&actions, pipe_in); | ||||
|     } else if (new_in != JANET_HANDLE_NONE && new_in != 0) { | ||||
|         posix_spawn_file_actions_adddup2(&actions, new_in, 0); | ||||
|         if (new_in != new_out && new_in != new_err) | ||||
|             posix_spawn_file_actions_addclose(&actions, new_in); | ||||
|         posix_spawn_file_actions_addclose(&actions, new_in); | ||||
|     } | ||||
|     if (pipe_out != JANET_HANDLE_NONE) { | ||||
|         posix_spawn_file_actions_adddup2(&actions, pipe_out, 1); | ||||
|         posix_spawn_file_actions_addclose(&actions, pipe_out); | ||||
|     } else if (new_out != JANET_HANDLE_NONE && new_out != 1) { | ||||
|         posix_spawn_file_actions_adddup2(&actions, new_out, 1); | ||||
|         if (new_out != new_err) | ||||
|             posix_spawn_file_actions_addclose(&actions, new_out); | ||||
|         posix_spawn_file_actions_addclose(&actions, new_out); | ||||
|     } | ||||
|     if (pipe_err != JANET_HANDLE_NONE) { | ||||
|         posix_spawn_file_actions_adddup2(&actions, pipe_err, 2); | ||||
| @@ -1433,8 +1232,8 @@ JANET_CORE_FN(os_getenv, | ||||
|     janet_sandbox_assert(JANET_SANDBOX_ENV); | ||||
|     janet_arity(argc, 1, 2); | ||||
|     const char *cstr = janet_getcstring(argv, 0); | ||||
|     janet_lock_environ(); | ||||
|     const char *res = getenv(cstr); | ||||
|     janet_lock_environ(); | ||||
|     Janet ret = res | ||||
|                 ? janet_cstringv(res) | ||||
|                 : argc == 2 | ||||
| @@ -1479,32 +1278,14 @@ 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" | ||||
|               "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") { | ||||
|               "(os/clock)", | ||||
|               "Return the number of whole + fractional seconds since some fixed point in time. The clock " | ||||
|               "is guaranteed to be non-decreasing in real time.") { | ||||
|     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); | ||||
|         if (janet_cstrcmp(sourcestr, "realtime") == 0) { | ||||
|             source = JANET_TIME_REALTIME; | ||||
|         } else if (janet_cstrcmp(sourcestr, "monotonic") == 0) { | ||||
|             source = JANET_TIME_MONOTONIC; | ||||
|         } else if (janet_cstrcmp(sourcestr, "cputime") == 0) { | ||||
|             source = JANET_TIME_CPUTIME; | ||||
|         } else { | ||||
|             janet_panicf("expected :realtime, :monotonic, or :cputime, got %v", argv[0]); | ||||
|         } | ||||
|     } | ||||
|     janet_fixarity(argc, 0); | ||||
|     (void) argv; | ||||
|     struct timespec tv; | ||||
|     if (janet_gettime(&tv, source)) janet_panic("could not get time"); | ||||
|     if (janet_gettime(&tv)) janet_panic("could not get time"); | ||||
|     double dtime = tv.tv_sec + (tv.tv_nsec / 1E9); | ||||
|     return janet_wrap_number(dtime); | ||||
| } | ||||
| @@ -1530,23 +1311,6 @@ JANET_CORE_FN(os_sleep, | ||||
|     return janet_wrap_nil(); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_isatty, | ||||
|               "(os/isatty &opt file)", | ||||
|               "Returns true if `file` is a terminal. If `file` is not specified, " | ||||
|               "it will default to standard output.") { | ||||
|     janet_arity(argc, 0, 1); | ||||
|     FILE *f = (argc == 1) ? janet_getfile(argv, 0, NULL) : stdout; | ||||
| #ifdef JANET_WINDOWS | ||||
|     int fd = _fileno(f); | ||||
|     if (fd == -1) janet_panic("not a valid stream"); | ||||
|     return janet_wrap_boolean(_isatty(fd)); | ||||
| #else | ||||
|     int fd = fileno(f); | ||||
|     if (fd == -1) janet_panic(strerror(errno)); | ||||
|     return janet_wrap_boolean(isatty(fd)); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(os_cwd, | ||||
|               "(os/cwd)", | ||||
|               "Returns the current working directory.") { | ||||
| @@ -2336,34 +2100,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 | ||||
|  | ||||
| /* | ||||
| @@ -2613,7 +2349,6 @@ void janet_lib_os(JanetTable *env) { | ||||
|         JANET_CORE_REG("os/date", os_date), /* not high resolution */ | ||||
|         JANET_CORE_REG("os/strftime", os_strftime), | ||||
|         JANET_CORE_REG("os/sleep", os_sleep), | ||||
|         JANET_CORE_REG("os/isatty", os_isatty), | ||||
|  | ||||
|         /* env functions */ | ||||
|         JANET_CORE_REG("os/environ", os_environ), | ||||
| @@ -2650,7 +2385,6 @@ void janet_lib_os(JanetTable *env) { | ||||
|         JANET_CORE_REG("os/execute", os_execute), | ||||
|         JANET_CORE_REG("os/spawn", os_spawn), | ||||
|         JANET_CORE_REG("os/shell", os_shell), | ||||
|         JANET_CORE_REG("os/posix-fork", os_posix_fork), | ||||
|         /* 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), | ||||
| @@ -2664,7 +2398,6 @@ void janet_lib_os(JanetTable *env) { | ||||
| #ifdef JANET_EV | ||||
|         JANET_CORE_REG("os/open", os_open), /* fs read and write */ | ||||
|         JANET_CORE_REG("os/pipe", os_pipe), | ||||
|         JANET_CORE_REG("os/sigaction", os_sigaction), | ||||
| #endif | ||||
| #endif | ||||
|         JANET_REG_END | ||||
|   | ||||
| @@ -259,14 +259,6 @@ static int checkescape(uint8_t c) { | ||||
|             return '\f'; | ||||
|         case 'v': | ||||
|             return '\v'; | ||||
|         case 'a': | ||||
|             return '\a'; | ||||
|         case 'b': | ||||
|             return '\b'; | ||||
|         case '\'': | ||||
|             return '\''; | ||||
|         case '?': | ||||
|             return '?'; | ||||
|         case 'e': | ||||
|             return 27; | ||||
|         case '"': | ||||
|   | ||||
| @@ -1100,7 +1100,7 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) { | ||||
|     Janet fun = argv[1]; | ||||
|     if (!janet_checktype(fun, JANET_FUNCTION) && | ||||
|             !janet_checktype(fun, JANET_CFUNCTION)) { | ||||
|         peg_panicf(b, "expected function or cfunction, got %v", fun); | ||||
|         peg_panicf(b, "expected function|cfunction, got %v", fun); | ||||
|     } | ||||
|     uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0; | ||||
|     uint32_t cindex = emit_constant(b, fun); | ||||
| @@ -1261,13 +1261,6 @@ static uint32_t peg_compile1(Builder *b, Janet peg) { | ||||
|         default: | ||||
|             peg_panic(b, "unexpected peg source"); | ||||
|             return 0; | ||||
|  | ||||
|         case JANET_BOOLEAN: { | ||||
|             int n = janet_unwrap_boolean(peg); | ||||
|             Reserve r = reserve(b, 2); | ||||
|             emit_1(r, n ? RULE_NCHAR : RULE_NOTNCHAR, 0); | ||||
|             break; | ||||
|         } | ||||
|         case JANET_NUMBER: { | ||||
|             int32_t n = peg_getinteger(b, peg); | ||||
|             Reserve r = reserve(b, 2); | ||||
|   | ||||
| @@ -152,12 +152,6 @@ static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, in | ||||
|             case '\v': | ||||
|                 janet_buffer_push_bytes(buffer, (const uint8_t *)"\\v", 2); | ||||
|                 break; | ||||
|             case '\a': | ||||
|                 janet_buffer_push_bytes(buffer, (const uint8_t *)"\\a", 2); | ||||
|                 break; | ||||
|             case '\b': | ||||
|                 janet_buffer_push_bytes(buffer, (const uint8_t *)"\\b", 2); | ||||
|                 break; | ||||
|             case 27: | ||||
|                 janet_buffer_push_bytes(buffer, (const uint8_t *)"\\e", 2); | ||||
|                 break; | ||||
| @@ -250,10 +244,6 @@ void janet_to_string_b(JanetBuffer *buffer, Janet x) { | ||||
|         case JANET_FUNCTION: { | ||||
|             JanetFunction *fun = janet_unwrap_function(x); | ||||
|             JanetFuncDef *def = fun->def; | ||||
|             if (def == NULL) { | ||||
|                 janet_buffer_push_cstring(buffer, "<incomplete function>"); | ||||
|                 break; | ||||
|             } | ||||
|             if (def->name) { | ||||
|                 const uint8_t *n = def->name; | ||||
|                 janet_buffer_push_cstring(buffer, "<function "); | ||||
| @@ -746,7 +736,7 @@ static void pushtypes(JanetBuffer *buffer, int types) { | ||||
|             if (first) { | ||||
|                 first = 0; | ||||
|             } else { | ||||
|                 janet_buffer_push_cstring(buffer, (types == 1) ? " or " : ", "); | ||||
|                 janet_buffer_push_u8(buffer, '|'); | ||||
|             } | ||||
|             janet_buffer_push_cstring(buffer, janet_type_names[i]); | ||||
|         } | ||||
| @@ -785,7 +775,7 @@ static const char *get_fmt_mapping(char c) { | ||||
|         if (format_mappings[i].c == c) | ||||
|             return format_mappings[i].mapping; | ||||
|     } | ||||
|     janet_assert(0, "bad format mapping"); | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| static const char *scanformat( | ||||
| @@ -856,7 +846,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) { | ||||
|                 } | ||||
|                 case 'd': | ||||
|                 case 'i': { | ||||
|                     int64_t n = va_arg(args, int); | ||||
|                     int64_t n = va_arg(args, long); | ||||
|                     nb = snprintf(item, MAX_ITEM, form, n); | ||||
|                     break; | ||||
|                 } | ||||
| @@ -864,7 +854,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, unsigned long); | ||||
|                     nb = snprintf(item, MAX_ITEM, form, n); | ||||
|                     break; | ||||
|                 } | ||||
|   | ||||
| @@ -27,8 +27,6 @@ | ||||
| #include "util.h" | ||||
| #endif | ||||
|  | ||||
| /* The JanetRegisterAllocator is really just a bitset. */ | ||||
|  | ||||
| void janetc_regalloc_init(JanetcRegisterAllocator *ra) { | ||||
|     ra->chunks = NULL; | ||||
|     ra->count = 0; | ||||
| @@ -141,14 +139,6 @@ void janetc_regalloc_free(JanetcRegisterAllocator *ra, int32_t reg) { | ||||
|     ra->chunks[chunk] &= ~ithbit(bit); | ||||
| } | ||||
|  | ||||
| /* Check if a register is set. */ | ||||
| int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg) { | ||||
|     int32_t chunk = reg >> 5; | ||||
|     int32_t bit = reg & 0x1F; | ||||
|     while (chunk >= ra->count) pushchunk(ra); | ||||
|     return !!(ra->chunks[chunk] & ithbit(bit)); | ||||
| } | ||||
|  | ||||
| /* Get a register that will fit in 8 bits (< 256). Do not call this | ||||
|  * twice with the same value of nth without calling janetc_regalloc_free | ||||
|  * on the returned register before. */ | ||||
|   | ||||
| @@ -56,6 +56,5 @@ int32_t janetc_regalloc_temp(JanetcRegisterAllocator *ra, JanetcRegisterTemp nth | ||||
| void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRegisterTemp nth); | ||||
| void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src); | ||||
| void janetc_regalloc_touch(JanetcRegisterAllocator *ra, int32_t reg); | ||||
| int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -57,20 +57,12 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char | ||||
|                 } | ||||
|             } else { | ||||
|                 ret = janet_wrap_string(cres.error); | ||||
|                 int32_t line = (int32_t) parser.line; | ||||
|                 int32_t col = (int32_t) parser.column; | ||||
|                 if ((cres.error_mapping.line > 0) && | ||||
|                         (cres.error_mapping.column > 0)) { | ||||
|                     line = cres.error_mapping.line; | ||||
|                     col = cres.error_mapping.column; | ||||
|                 } | ||||
|                 if (cres.macrofiber) { | ||||
|                     janet_eprintf("%s:%d:%d: compile error", sourcePath, | ||||
|                                   line, col); | ||||
|                     janet_eprintf("compile error in %s: ", sourcePath); | ||||
|                     janet_stacktrace_ext(cres.macrofiber, ret, ""); | ||||
|                 } else { | ||||
|                     janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath, | ||||
|                                   line, col, (const char *)cres.error); | ||||
|                     janet_eprintf("compile error in %s: %s\n", sourcePath, | ||||
|                                   (const char *)cres.error); | ||||
|                 } | ||||
|                 errflags |= 0x02; | ||||
|                 done = 1; | ||||
|   | ||||
| @@ -182,6 +182,7 @@ static int destructure(JanetCompiler *c, | ||||
|                         return 1; | ||||
|                     } | ||||
|  | ||||
|  | ||||
|                     if (!janet_checktype(values[i + 1], JANET_SYMBOL)) { | ||||
|                         janetc_error(c, janet_formatc("expected symbol following '& in destructuring pattern, found %q", values[i + 1])); | ||||
|                         return 1; | ||||
| @@ -263,7 +264,7 @@ static const Janet *janetc_make_sourcemap(JanetCompiler *c) { | ||||
|  | ||||
| static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     if (argn != 2) { | ||||
|         janetc_cerror(opts.compiler, "expected 2 arguments to set"); | ||||
|         janetc_cerror(opts.compiler, "expected 2 arguments"); | ||||
|         return janetc_cslot(janet_wrap_nil()); | ||||
|     } | ||||
|     JanetFopts subopts = janetc_fopts_default(opts.compiler); | ||||
| @@ -305,16 +306,12 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) | ||||
| } | ||||
|  | ||||
| /* Add attributes to a global def or var table */ | ||||
| static JanetTable *handleattr(JanetCompiler *c, const char *kind, int32_t argn, const Janet *argv) { | ||||
| static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv) { | ||||
|     int32_t i; | ||||
|     JanetTable *tab = janet_table(2); | ||||
|     const char *binding_name = janet_type(argv[0]) == JANET_SYMBOL | ||||
|                                ? ((const char *)janet_unwrap_symbol(argv[0])) | ||||
|                                : "<multiple bindings>"; | ||||
|     if (argn < 2) { | ||||
|         janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind)); | ||||
|         return NULL; | ||||
|     } | ||||
|     for (i = 1; i < argn - 1; i++) { | ||||
|         Janet attr = argv[i]; | ||||
|         switch (janet_type(attr)) { | ||||
| @@ -338,52 +335,18 @@ static JanetTable *handleattr(JanetCompiler *c, const char *kind, int32_t argn, | ||||
|     return tab; | ||||
| } | ||||
|  | ||||
| typedef struct SlotHeadPair { | ||||
|     Janet lhs; | ||||
|     JanetSlot rhs; | ||||
| } SlotHeadPair; | ||||
|  | ||||
| SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopts opts, Janet lhs, Janet rhs) { | ||||
|  | ||||
|     /* Detect if we can do an optimization to avoid some allocations */ | ||||
|     int can_destructure_lhs = janet_checktype(lhs, JANET_TUPLE) | ||||
|                               || janet_checktype(lhs, JANET_ARRAY); | ||||
|     int rhs_is_indexed = janet_checktype(rhs, JANET_ARRAY) | ||||
|                          || (janet_checktype(rhs, JANET_TUPLE) && (janet_tuple_flag(janet_unwrap_tuple(rhs)) & JANET_TUPLE_FLAG_BRACKETCTOR)); | ||||
|     uint32_t has_drop = opts.flags & JANET_FOPTS_DROP; | ||||
|  | ||||
| static JanetSlot dohead(JanetCompiler *c, JanetFopts opts, Janet *head, int32_t argn, const Janet *argv) { | ||||
|     JanetFopts subopts = janetc_fopts_default(c); | ||||
|     subopts.flags = opts.flags & ~(JANET_FOPTS_TAIL | JANET_FOPTS_DROP); | ||||
|  | ||||
|     if (has_drop && can_destructure_lhs && rhs_is_indexed) { | ||||
|         /* Code is of the form (def [a b] [1 2]), avoid the allocation of two tuples */ | ||||
|         JanetView view_lhs = {0}; | ||||
|         JanetView view_rhs = {0}; | ||||
|         janet_indexed_view(lhs, &view_lhs.items, &view_lhs.len); | ||||
|         janet_indexed_view(rhs, &view_rhs.items, &view_rhs.len); | ||||
|         int found_amp = 0; | ||||
|         for (int32_t i = 0; i < view_lhs.len; i++) { | ||||
|             if (janet_symeq(view_lhs.items[i], "&")) { | ||||
|                 found_amp = 1; | ||||
|                 /* Good error will be generated later. */ | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if (!found_amp) { | ||||
|             for (int32_t i = 0; i < view_lhs.len; i++) { | ||||
|                 Janet sub_rhs = view_rhs.len <= i ? janet_wrap_nil() : view_rhs.items[i]; | ||||
|                 into = dohead_destructure(c, into, subopts, view_lhs.items[i], sub_rhs); | ||||
|             } | ||||
|             return into; | ||||
|         } | ||||
|     JanetSlot ret; | ||||
|     if (argn < 2) { | ||||
|         janetc_cerror(c, "expected at least 2 arguments"); | ||||
|         return janetc_cslot(janet_wrap_nil()); | ||||
|     } | ||||
|  | ||||
|     /* No optimization, do the simple way */ | ||||
|     *head = argv[0]; | ||||
|     subopts.flags = opts.flags & ~(JANET_FOPTS_TAIL | JANET_FOPTS_DROP); | ||||
|     subopts.hint = opts.hint; | ||||
|     JanetSlot ret = janetc_value(subopts, rhs); | ||||
|     SlotHeadPair shp = {lhs, ret}; | ||||
|     janet_v_push(into, shp); | ||||
|     return into; | ||||
|     ret = janetc_value(subopts, argv[argn - 1]); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* Def or var a symbol in a local scope */ | ||||
| @@ -391,17 +354,7 @@ static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, Janet | ||||
|     int isUnnamedRegister = !(ret.flags & JANET_SLOT_NAMED) && | ||||
|                             ret.index > 0 && | ||||
|                             ret.envindex >= 0; | ||||
|     /* optimization for `(def x my-def)` - don't emit a movn/movf instruction, we can just alias my-def */ | ||||
|     /* TODO - implement optimization for `(def x my-var)` correctly as well w/ de-aliasing */ | ||||
|     int canAlias = !(flags & JANET_SLOT_MUTABLE) && | ||||
|                    !(ret.flags & JANET_SLOT_MUTABLE) && | ||||
|                    (ret.flags & JANET_SLOT_NAMED) && | ||||
|                    (ret.index >= 0) && | ||||
|                    (ret.envindex == -1); | ||||
|     if (canAlias) { | ||||
|         ret.flags &= ~JANET_SLOT_MUTABLE; | ||||
|         isUnnamedRegister = 1; /* don't free slot after use - is an alias for another slot */ | ||||
|     } else if (!isUnnamedRegister) { | ||||
|     if (!isUnnamedRegister) { | ||||
|         /* Slot is not able to be named */ | ||||
|         JanetSlot localslot = janetc_farslot(c); | ||||
|         janetc_copy(c, localslot, ret); | ||||
| @@ -449,23 +402,12 @@ static int varleaf( | ||||
|  | ||||
| static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     JanetCompiler *c = opts.compiler; | ||||
|     JanetTable *attr_table = handleattr(c, "var", argn, argv); | ||||
|     if (c->result.status == JANET_COMPILE_ERROR) { | ||||
|     Janet head; | ||||
|     JanetTable *attr_table = handleattr(c, argn, argv); | ||||
|     JanetSlot ret = dohead(c, opts, &head, argn, argv); | ||||
|     if (c->result.status == JANET_COMPILE_ERROR) | ||||
|         return janetc_cslot(janet_wrap_nil()); | ||||
|     } | ||||
|     SlotHeadPair *into = NULL; | ||||
|     into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]); | ||||
|     if (c->result.status == JANET_COMPILE_ERROR) { | ||||
|         janet_v_free(into); | ||||
|         return janetc_cslot(janet_wrap_nil()); | ||||
|     } | ||||
|     JanetSlot ret; | ||||
|     janet_assert(janet_v_count(into) > 0, "bad destructure"); | ||||
|     for (int32_t i = 0; i < janet_v_count(into); i++) { | ||||
|         destructure(c, into[i].lhs, into[i].rhs, varleaf, attr_table); | ||||
|         ret = into[i].rhs; | ||||
|     } | ||||
|     janet_v_free(into); | ||||
|     destructure(c, argv[0], ret, varleaf, attr_table); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @@ -509,53 +451,16 @@ static int defleaf( | ||||
|  | ||||
| static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     JanetCompiler *c = opts.compiler; | ||||
|     JanetTable *attr_table = handleattr(c, "def", argn, argv); | ||||
|     if (c->result.status == JANET_COMPILE_ERROR) { | ||||
|         return janetc_cslot(janet_wrap_nil()); | ||||
|     } | ||||
|     Janet head; | ||||
|     opts.flags &= ~JANET_FOPTS_HINT; | ||||
|     SlotHeadPair *into = NULL; | ||||
|     into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]); | ||||
|     if (c->result.status == JANET_COMPILE_ERROR) { | ||||
|         janet_v_free(into); | ||||
|     JanetTable *attr_table = handleattr(c, argn, argv); | ||||
|     JanetSlot ret = dohead(c, opts, &head, argn, argv); | ||||
|     if (c->result.status == JANET_COMPILE_ERROR) | ||||
|         return janetc_cslot(janet_wrap_nil()); | ||||
|     } | ||||
|     JanetSlot ret; | ||||
|     janet_assert(janet_v_count(into) > 0, "bad destructure"); | ||||
|     for (int32_t i = 0; i < janet_v_count(into); i++) { | ||||
|         destructure(c, into[i].lhs, into[i].rhs, defleaf, attr_table); | ||||
|         ret = into[i].rhs; | ||||
|     } | ||||
|     janet_v_free(into); | ||||
|     destructure(c, argv[0], ret, defleaf, attr_table); | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| /* 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) { | ||||
|     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; | ||||
|     if (tag != fun_tag) return 0; | ||||
|     if (janet_checktype(tup[1], JANET_NIL)) { | ||||
|         *capture = tup[2]; | ||||
|         return 1; | ||||
|     } else if (janet_checktype(tup[2], JANET_NIL)) { | ||||
|         *capture = tup[1]; | ||||
|         return 1; | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * :condition | ||||
|  * ... | ||||
| @@ -576,7 +481,6 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     JanetScope condscope, tempscope; | ||||
|     const int tail = opts.flags & JANET_FOPTS_TAIL; | ||||
|     const int drop = opts.flags & JANET_FOPTS_DROP; | ||||
|     uint8_t ifnjmp = JOP_JUMP_IF_NOT; | ||||
|  | ||||
|     if (argn < 2 || argn > 3) { | ||||
|         janetc_cerror(c, "expected 2 or 3 arguments to if"); | ||||
| @@ -599,16 +503,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|  | ||||
|     /* Compile condition */ | ||||
|     janetc_scope(&condscope, c, 0, "if"); | ||||
|  | ||||
|     Janet condform = argv[0]; | ||||
|     if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_EQ)) { | ||||
|         ifnjmp = JOP_JUMP_IF_NOT_NIL; | ||||
|     } | ||||
|     if (janetc_check_nil_form(opts, condform, &condform, JANET_FUN_NEQ)) { | ||||
|         ifnjmp = JOP_JUMP_IF_NIL; | ||||
|     } | ||||
|  | ||||
|     cond = janetc_value(condopts, condform); | ||||
|     cond = janetc_value(condopts, argv[0]); | ||||
|  | ||||
|     /* Check constant condition. */ | ||||
|     /* TODO: Use type info for more short circuits */ | ||||
| @@ -631,7 +526,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     } | ||||
|  | ||||
|     /* Compile jump to right */ | ||||
|     labeljr = janetc_emit_si(c, ifnjmp, cond, 0, 0); | ||||
|     labeljr = janetc_emit_si(c, JOP_JUMP_IF_NOT, cond, 0, 0); | ||||
|  | ||||
|     /* Condition left body */ | ||||
|     janetc_scope(&tempscope, c, 0, "if-true"); | ||||
| @@ -641,7 +536,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|  | ||||
|     /* Compile jump to done */ | ||||
|     labeljd = janet_v_count(c->buffer); | ||||
|     if (!tail && !(drop && janet_checktype(falsebody, JANET_NIL))) janetc_emit(c, JOP_JUMP); | ||||
|     if (!tail) janetc_emit(c, JOP_JUMP); | ||||
|  | ||||
|     /* Compile right body */ | ||||
|     labelr = janet_v_count(c->buffer); | ||||
| @@ -687,6 +582,7 @@ static JanetSlot janetc_do(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Compile an upscope form. Upscope forms execute their body sequentially and | ||||
|  * evaluate to the last expression in the body, but without lexical scope. */ | ||||
| static JanetSlot janetc_upscope(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
| @@ -752,8 +648,9 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv) | ||||
|         if (!(scope->flags & JANET_SCOPE_WHILE) && argn) { | ||||
|             /* Closure body with return argument */ | ||||
|             subopts.flags |= JANET_FOPTS_TAIL; | ||||
|             janetc_value(subopts, argv[0]); | ||||
|             return janetc_cslot(janet_wrap_nil()); | ||||
|             JanetSlot ret = janetc_value(subopts, argv[0]); | ||||
|             ret.flags |= JANET_SLOT_RETURNED; | ||||
|             return ret; | ||||
|         } else { | ||||
|             /* while loop IIFE or no argument */ | ||||
|             if (argn) { | ||||
| @@ -761,7 +658,9 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv) | ||||
|                 janetc_value(subopts, argv[0]); | ||||
|             } | ||||
|             janetc_emit(c, JOP_RETURN_NIL); | ||||
|             return janetc_cslot(janet_wrap_nil()); | ||||
|             JanetSlot s = janetc_cslot(janet_wrap_nil()); | ||||
|             s.flags |= JANET_SLOT_RETURNED; | ||||
|             return s; | ||||
|         } | ||||
|     } else { | ||||
|         if (argn) { | ||||
| @@ -774,6 +673,20 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Check if a form matches the pattern (not= nil _) */ | ||||
| static int janetc_check_notnil_form(Janet x, Janet *capture) { | ||||
|     if (!janet_checktype(x, JANET_TUPLE)) return 0; | ||||
|     JanetTuple tup = janet_unwrap_tuple(x); | ||||
|     if (!janet_checktype(tup[0], JANET_FUNCTION)) return 0; | ||||
|     if (3 != janet_tuple_length(tup)) return 0; | ||||
|     JanetFunction *fun = janet_unwrap_function(tup[0]); | ||||
|     uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG; | ||||
|     if (tag != JANET_FUN_NEQ) return 0; | ||||
|     if (!janet_checktype(tup[1], JANET_NIL)) return 0; | ||||
|     *capture = tup[2]; | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * :whiletop | ||||
|  * ... | ||||
| @@ -790,13 +703,12 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv) | ||||
|     JanetScope tempscope; | ||||
|     int32_t labelwt, labeld, labeljt, labelc, i; | ||||
|     int infinite = 0; | ||||
|     int is_nil_form = 0; | ||||
|     int is_notnil_form = 0; | ||||
|     uint8_t ifjmp = JOP_JUMP_IF; | ||||
|     uint8_t ifnjmp = JOP_JUMP_IF_NOT; | ||||
|  | ||||
|     if (argn < 1) { | ||||
|         janetc_cerror(c, "expected at least 1 argument to while"); | ||||
|     if (argn < 2) { | ||||
|         janetc_cerror(c, "expected at least 2 arguments"); | ||||
|         return janetc_cslot(janet_wrap_nil()); | ||||
|     } | ||||
|  | ||||
| @@ -804,16 +716,11 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv) | ||||
|  | ||||
|     janetc_scope(&tempscope, c, JANET_SCOPE_WHILE, "while"); | ||||
|  | ||||
|     /* Check for `(= nil _)` or `(not= nil _)` in condition, and if so, use the | ||||
|     /* Check for `(not= nil _)` in condition, and if so, use the | ||||
|      * 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)) { | ||||
|         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_notnil_form(condform, &condform)) { | ||||
|         is_notnil_form = 1; | ||||
|         ifjmp = JOP_JUMP_IF_NOT_NIL; | ||||
|         ifnjmp = JOP_JUMP_IF_NIL; | ||||
| @@ -825,9 +732,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv) | ||||
|     /* Check for constant condition */ | ||||
|     if (cond.flags & JANET_SLOT_CONSTANT) { | ||||
|         /* Loop never executes */ | ||||
|         int never_executes = is_nil_form | ||||
|                              ? !janet_checktype(cond.constant, JANET_NIL) | ||||
|                              : is_notnil_form | ||||
|         int never_executes = is_notnil_form | ||||
|                              ? janet_checktype(cond.constant, JANET_NIL) | ||||
|                              : !janet_truthy(cond.constant); | ||||
|         if (never_executes) { | ||||
| @@ -1045,7 +950,6 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) { | ||||
|     for (i = 0; i < paramcount; i++) { | ||||
|         Janet param = params[i]; | ||||
|         if (!janet_checktype(param, JANET_SYMBOL)) { | ||||
|             janet_assert(janet_v_count(destructed_params) > j, "out of bounds"); | ||||
|             JanetSlot reg = destructed_params[j++]; | ||||
|             destructure(c, param, reg, defleaf, NULL); | ||||
|             janetc_freeslot(c, reg); | ||||
| @@ -1150,3 +1054,4 @@ const JanetSpecial *janetc_special(const uint8_t *name) { | ||||
|                sizeof(JanetSpecial), | ||||
|                name); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -24,11 +24,6 @@ | ||||
| #include "features.h" | ||||
| #include <janet.h> | ||||
| #include "state.h" | ||||
| #include "util.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef JANET_WINDOWS | ||||
| #include <windows.h> | ||||
| #endif | ||||
|  | ||||
| JANET_THREAD_LOCAL JanetVM janet_vm; | ||||
| @@ -62,10 +57,5 @@ void janet_vm_load(JanetVM *from) { | ||||
|  * use NULL to interrupt the current VM when convenient */ | ||||
| void janet_interpreter_interrupt(JanetVM *vm) { | ||||
|     vm = vm ? vm : &janet_vm; | ||||
|     janet_atomic_inc(&vm->auto_suspend); | ||||
| } | ||||
|  | ||||
| void janet_interpreter_interrupt_handled(JanetVM *vm) { | ||||
|     vm = vm ? vm : &janet_vm; | ||||
|     janet_atomic_dec(&vm->auto_suspend); | ||||
|     vm->auto_suspend = 1; | ||||
| } | ||||
|   | ||||
| @@ -89,7 +89,7 @@ struct JanetVM { | ||||
|  | ||||
|     /* If this flag is true, suspend on function calls and backwards jumps. | ||||
|      * When this occurs, this flag will be reset to 0. */ | ||||
|     volatile JanetAtomicInt auto_suspend; | ||||
|     int auto_suspend; | ||||
|  | ||||
|     /* The current running fiber on the current thread. | ||||
|      * Set and unset by functions in vm.c */ | ||||
| @@ -121,12 +121,10 @@ struct JanetVM { | ||||
|  | ||||
|     /* Garbage collection */ | ||||
|     void *blocks; | ||||
|     void *weak_blocks; | ||||
|     size_t gc_interval; | ||||
|     size_t next_collection; | ||||
|     size_t block_count; | ||||
|     int gc_suspend; | ||||
|     int gc_mark_phase; | ||||
|  | ||||
|     /* GC roots */ | ||||
|     Janet *roots; | ||||
| @@ -156,10 +154,12 @@ struct JanetVM { | ||||
|     JanetQueue spawn; | ||||
|     JanetTimeout *tq; | ||||
|     JanetRNG ev_rng; | ||||
|     volatile JanetAtomicInt listener_count; /* used in signal handler, must be volatile */ | ||||
|     JanetListenerState **listeners; | ||||
|     size_t listener_count; | ||||
|     size_t listener_cap; | ||||
|     size_t extra_listeners; | ||||
|     JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */ | ||||
|     JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */ | ||||
|     JanetTable signal_handlers; | ||||
| #ifdef JANET_WINDOWS | ||||
|     void **iocp; | ||||
| #elif defined(JANET_EV_EPOLL) | ||||
| @@ -175,9 +175,6 @@ struct JanetVM { | ||||
|     int timer; | ||||
|     int timer_enabled; | ||||
| #else | ||||
|     JanetStream **streams; | ||||
|     size_t stream_count; | ||||
|     size_t stream_capacity; | ||||
|     pthread_attr_t new_thread_attr; | ||||
|     JanetHandle selfpipe[2]; | ||||
|     struct pollfd *fds; | ||||
|   | ||||
| @@ -175,9 +175,8 @@ JANET_CORE_FN(cfun_string_slice, | ||||
|               "Returns a substring from a byte sequence. The substring is from " | ||||
|               "index `start` inclusive to index `end`, exclusive. All indexing " | ||||
|               "is from 0. `start` and `end` can also be negative to indicate indexing " | ||||
|               "from the end of the string. Note that if `start` is negative it is " | ||||
|               "exclusive, and if `end` is negative it is inclusive, to allow a full " | ||||
|               "negative slice range.") { | ||||
|               "from the end of the string. Note that index -1 is synonymous with " | ||||
|               "index `(length bytes)` to allow a full negative slice range. ") { | ||||
|     JanetByteView view = janet_getbytes(argv, 0); | ||||
|     JanetRange range = janet_getslice(argc, argv); | ||||
|     return janet_stringv(view.bytes + range.start, range.end - range.start); | ||||
| @@ -536,30 +535,7 @@ JANET_CORE_FN(cfun_string_join, | ||||
| JANET_CORE_FN(cfun_string_format, | ||||
|               "(string/format format & values)", | ||||
|               "Similar to C's `snprintf`, but specialized for operating with Janet values. Returns " | ||||
|               "a new string.\n\n" | ||||
|               "The following conversion specifiers are supported, where the upper case specifiers generate " | ||||
|               "upper case output:\n" | ||||
|               "- `c`: ASCII character.\n" | ||||
|               "- `d`, `i`: integer, formatted as a decimal number.\n" | ||||
|               "- `x`, `X`: integer, formatted as a hexadecimal number.\n" | ||||
|               "- `o`: integer, formatted as an octal number.\n" | ||||
|               "- `f`, `F`: floating point number, formatted as a decimal number.\n" | ||||
|               "- `e`, `E`: floating point number, formatted in scientific notation.\n" | ||||
|               "- `g`, `G`: floating point number, formatted in its shortest form.\n" | ||||
|               "- `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)" | ||||
|               "- `j`: format to jdn (Janet data notation).\n" | ||||
|               "\n" | ||||
|               "The following conversion specifiers are used for \"pretty-printing\", where the upper-case " | ||||
|               "variants generate colored output. These specifiers can take a precision " | ||||
|               "argument to specify the maximum nesting depth to print.\n" | ||||
|               "- `p`, `P`: pretty format, truncating if necessary\n" | ||||
|               "- `m`, `M`: pretty format without truncating.\n" | ||||
|               "- `q`, `Q`: pretty format on one line, truncating if necessary.\n" | ||||
|               "- `n`, `N`: pretty format on one line without truncation.\n") { | ||||
|               "a new string.") { | ||||
|     janet_arity(argc, 1, -1); | ||||
|     JanetBuffer *buffer = janet_buffer(0); | ||||
|     const char *strfrmt = (const char *) janet_getstring(argv, 0); | ||||
|   | ||||
| @@ -108,7 +108,6 @@ static const uint8_t **janet_symcache_findmem( | ||||
|         } | ||||
| notfound: | ||||
|     *success = 0; | ||||
|     janet_assert(firstEmpty != NULL, "symcache failed to get memory"); | ||||
|     return firstEmpty; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -87,27 +87,11 @@ void janet_table_deinit(JanetTable *table) { | ||||
| } | ||||
|  | ||||
| /* Create a new table */ | ||||
|  | ||||
| JanetTable *janet_table(int32_t capacity) { | ||||
|     JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable)); | ||||
|     return janet_table_init_impl(table, capacity, 0); | ||||
| } | ||||
|  | ||||
| JanetTable *janet_table_weakk(int32_t capacity) { | ||||
|     JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKK, sizeof(JanetTable)); | ||||
|     return janet_table_init_impl(table, capacity, 0); | ||||
| } | ||||
|  | ||||
| JanetTable *janet_table_weakv(int32_t capacity) { | ||||
|     JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKV, sizeof(JanetTable)); | ||||
|     return janet_table_init_impl(table, capacity, 0); | ||||
| } | ||||
|  | ||||
| JanetTable *janet_table_weakkv(int32_t capacity) { | ||||
|     JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKKV, sizeof(JanetTable)); | ||||
|     return janet_table_init_impl(table, capacity, 0); | ||||
| } | ||||
|  | ||||
| /* Find the bucket that contains the given key. Will also return | ||||
|  * bucket where key should go if not in the table. */ | ||||
| JanetKV *janet_table_find(JanetTable *t, Janet key) { | ||||
| @@ -127,11 +111,12 @@ static void janet_table_rehash(JanetTable *t, int32_t size) { | ||||
|             JANET_OUT_OF_MEMORY; | ||||
|         } | ||||
|     } | ||||
|     int32_t oldcapacity = t->capacity; | ||||
|     int32_t i, oldcapacity; | ||||
|     oldcapacity = t->capacity; | ||||
|     t->data = newdata; | ||||
|     t->capacity = size; | ||||
|     t->deleted = 0; | ||||
|     for (int32_t i = 0; i < oldcapacity; i++) { | ||||
|     for (i = 0; i < oldcapacity; i++) { | ||||
|         JanetKV *kv = olddata + i; | ||||
|         if (!janet_checktype(kv->key, JANET_NIL)) { | ||||
|             JanetKV *newkv = janet_table_find(t, kv->key); | ||||
| @@ -313,46 +298,11 @@ JANET_CORE_FN(cfun_table_new, | ||||
|               "Creates a new empty table with pre-allocated memory " | ||||
|               "for `capacity` entries. This means that if one knows the number of " | ||||
|               "entries going into a table on creation, extra memory allocation " | ||||
|               "can be avoided. " | ||||
|               "Returns the new table.") { | ||||
|               "can be avoided. Returns the new table.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     int32_t cap = janet_getnat(argv, 0); | ||||
|     return janet_wrap_table(janet_table(cap)); | ||||
| } | ||||
| /* | ||||
|     uint32_t flags = janet_getflags(argv, 1, "kv"); | ||||
|     if (flags == 0) return janet_wrap_table(janet_table(cap)); | ||||
|     if (flags == 1) return janet_wrap_table(janet_table_weakk(cap)); | ||||
|     if (flags == 2) return janet_wrap_table(janet_table_weakv(cap)); | ||||
|     return janet_wrap_table(janet_table_weakkv(cap)); | ||||
|     */ | ||||
|  | ||||
| JANET_CORE_FN(cfun_table_weak, | ||||
|               "(table/weak capacity)", | ||||
|               "Creates a new empty table with weak references to keys and values. Similar to `table/new`. " | ||||
|               "Returns the new table.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     int32_t cap = janet_getnat(argv, 0); | ||||
|     return janet_wrap_table(janet_table_weakkv(cap)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_table_weak_keys, | ||||
|               "(table/weak-keys capacity)", | ||||
|               "Creates a new empty table with weak references to keys and normal references to values. Similar to `table/new`. " | ||||
|               "Returns the new table.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     int32_t cap = janet_getnat(argv, 0); | ||||
|     return janet_wrap_table(janet_table_weakk(cap)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_table_weak_values, | ||||
|               "(table/weak-values capacity)", | ||||
|               "Creates a new empty table with normal references to keys and weak references to values. Similar to `table/new`. " | ||||
|               "Returns the new table.") { | ||||
|     janet_fixarity(argc, 1); | ||||
|     int32_t cap = janet_getnat(argv, 0); | ||||
|     return janet_wrap_table(janet_table_weakv(cap)); | ||||
| } | ||||
|  | ||||
| JANET_CORE_FN(cfun_table_getproto, | ||||
|               "(table/getproto tab)", | ||||
| @@ -427,9 +377,6 @@ JANET_CORE_FN(cfun_table_proto_flatten, | ||||
| void janet_lib_table(JanetTable *env) { | ||||
|     JanetRegExt table_cfuns[] = { | ||||
|         JANET_CORE_REG("table/new", cfun_table_new), | ||||
|         JANET_CORE_REG("table/weak", cfun_table_weak), | ||||
|         JANET_CORE_REG("table/weak-keys", cfun_table_weak_keys), | ||||
|         JANET_CORE_REG("table/weak-values", cfun_table_weak_values), | ||||
|         JANET_CORE_REG("table/to-struct", cfun_table_tostruct), | ||||
|         JANET_CORE_REG("table/getproto", cfun_table_getproto), | ||||
|         JANET_CORE_REG("table/setproto", cfun_table_setproto), | ||||
|   | ||||
| @@ -69,9 +69,9 @@ JANET_CORE_FN(cfun_tuple_slice, | ||||
|               "inclusive to index `end` exclusive. If `start` or `end` are not provided, " | ||||
|               "they default to 0 and the length of `arrtup`, respectively. " | ||||
|               "`start` and `end` can also be negative to indicate indexing " | ||||
|               "from the end of the input. Note that if `start` is negative it is " | ||||
|               "exclusive, and if `end` is negative it is inclusive, to allow a full " | ||||
|               "negative slice range. Returns the new tuple.") { | ||||
|               "from the end of the input. Note that index -1 is synonymous with " | ||||
|               "index `(length arrtup)` to allow a full negative slice range. " | ||||
|               "Returns the new tuple.") { | ||||
|     JanetView view = janet_getindexed(argv, 0); | ||||
|     JanetRange range = janet_getslice(argc, argv); | ||||
|     return janet_wrap_tuple(janet_tuple_n(view.items + range.start, range.end - range.start)); | ||||
|   | ||||
| @@ -499,7 +499,7 @@ typedef struct { | ||||
| static void namebuf_init(NameBuf *namebuf, const char *prefix) { | ||||
|     size_t plen = strlen(prefix); | ||||
|     namebuf->plen = plen; | ||||
|     namebuf->buf = janet_smalloc(namebuf->plen + 256); | ||||
|     namebuf->buf = janet_malloc(namebuf->plen + 256); | ||||
|     if (NULL == namebuf->buf) { | ||||
|         JANET_OUT_OF_MEMORY; | ||||
|     } | ||||
| @@ -508,12 +508,12 @@ static void namebuf_init(NameBuf *namebuf, const char *prefix) { | ||||
| } | ||||
|  | ||||
| static void namebuf_deinit(NameBuf *namebuf) { | ||||
|     janet_sfree(namebuf->buf); | ||||
|     janet_free(namebuf->buf); | ||||
| } | ||||
|  | ||||
| static char *namebuf_name(NameBuf *namebuf, const char *suffix) { | ||||
|     size_t slen = strlen(suffix); | ||||
|     namebuf->buf = janet_srealloc(namebuf->buf, namebuf->plen + 2 + slen); | ||||
|     namebuf->buf = janet_realloc(namebuf->buf, namebuf->plen + 2 + slen); | ||||
|     if (NULL == namebuf->buf) { | ||||
|         JANET_OUT_OF_MEMORY; | ||||
|     } | ||||
| @@ -805,13 +805,6 @@ int janet_checkint(Janet x) { | ||||
|     return janet_checkintrange(dval); | ||||
| } | ||||
|  | ||||
| int janet_checkuint(Janet x) { | ||||
|     if (!janet_checktype(x, JANET_NUMBER)) | ||||
|         return 0; | ||||
|     double dval = janet_unwrap_number(x); | ||||
|     return janet_checkuintrange(dval); | ||||
| } | ||||
|  | ||||
| int janet_checkint64(Janet x) { | ||||
|     if (!janet_checktype(x, JANET_NUMBER)) | ||||
|         return 0; | ||||
| @@ -823,7 +816,7 @@ int janet_checkuint64(Janet x) { | ||||
|     if (!janet_checktype(x, JANET_NUMBER)) | ||||
|         return 0; | ||||
|     double dval = janet_unwrap_number(x); | ||||
|     return janet_checkuint64range(dval); | ||||
|     return dval >= 0 && dval <= JANET_INTMAX_DOUBLE && dval == (uint64_t) dval; | ||||
| } | ||||
|  | ||||
| int janet_checksize(Janet x) { | ||||
| @@ -882,73 +875,34 @@ int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffe | ||||
| /* Clock shims for various platforms */ | ||||
| #ifdef JANET_GETTIME | ||||
| #ifdef JANET_WINDOWS | ||||
| #include <profileapi.h> | ||||
| int janet_gettime(struct timespec *spec, enum JanetTimeSource source) { | ||||
|     if (source == JANET_TIME_REALTIME) { | ||||
|         FILETIME ftime; | ||||
|         GetSystemTimeAsFileTime(&ftime); | ||||
|         int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32); | ||||
|         /* Windows epoch is January 1, 1601 apparently */ | ||||
|         wintime -= 116444736000000000LL; | ||||
|         spec->tv_sec  = wintime / 10000000LL; | ||||
|         /* Resolution is 100 nanoseconds. */ | ||||
|         spec->tv_nsec = wintime % 10000000LL * 100; | ||||
|     } else if (source == JANET_TIME_MONOTONIC) { | ||||
|         LARGE_INTEGER count; | ||||
|         LARGE_INTEGER perf_freq; | ||||
|         QueryPerformanceCounter(&count); | ||||
|         QueryPerformanceFrequency(&perf_freq); | ||||
|         spec->tv_sec = count.QuadPart / perf_freq.QuadPart; | ||||
|         spec->tv_nsec = (long)((count.QuadPart % perf_freq.QuadPart) * 1000000000 / perf_freq.QuadPart); | ||||
|     } else if (source == JANET_TIME_CPUTIME) { | ||||
|         FILETIME creationTime, exitTime, kernelTime, userTime; | ||||
|         GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime); | ||||
|         int64_t tmp = ((int64_t)userTime.dwHighDateTime << 32) + userTime.dwLowDateTime; | ||||
|         spec->tv_sec = tmp / 10000000LL; | ||||
|         spec->tv_nsec = tmp % 10000000LL * 100; | ||||
|     } | ||||
| int janet_gettime(struct timespec *spec) { | ||||
|     FILETIME ftime; | ||||
|     GetSystemTimeAsFileTime(&ftime); | ||||
|     int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32); | ||||
|     /* Windows epoch is January 1, 1601 apparently */ | ||||
|     wintime -= 116444736000000000LL; | ||||
|     spec->tv_sec  = wintime / 10000000LL; | ||||
|     /* Resolution is 100 nanoseconds. */ | ||||
|     spec->tv_nsec = wintime % 10000000LL * 100; | ||||
|     return 0; | ||||
| } | ||||
| /* clock_gettime() wasn't available on Mac until 10.12. */ | ||||
| #elif defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_12) | ||||
| #include <mach/clock.h> | ||||
| #include <mach/mach.h> | ||||
| int janet_gettime(struct timespec *spec, enum JanetTimeSource source) { | ||||
|     if (source == JANET_TIME_REALTIME) { | ||||
|         clock_serv_t cclock; | ||||
|         mach_timespec_t mts; | ||||
|         host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); | ||||
|         clock_get_time(cclock, &mts); | ||||
|         mach_port_deallocate(mach_task_self(), cclock); | ||||
|         spec->tv_sec = mts.tv_sec; | ||||
|         spec->tv_nsec = mts.tv_nsec; | ||||
|     } else if (source == JANET_TIME_MONOTONIC) { | ||||
|         clock_serv_t cclock; | ||||
|         int nsecs; | ||||
|         mach_msg_type_number_t count; | ||||
|         host_get_clock_service(mach_host_self(), clock, &cclock); | ||||
|         clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count); | ||||
|         mach_port_deallocate(mach_task_self(), cclock); | ||||
|         clock_getres(CLOCK_MONOTONIC, spec); | ||||
|     } | ||||
|     if (source == JANET_TIME_CPUTIME) { | ||||
|         clock_t tmp = clock(); | ||||
|         spec->tv_sec = tmp; | ||||
|         spec->tv_nsec = (tmp - spec->tv_sec) * 1.0e9; | ||||
|     } | ||||
| int janet_gettime(struct timespec *spec) { | ||||
|     clock_serv_t cclock; | ||||
|     mach_timespec_t mts; | ||||
|     host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); | ||||
|     clock_get_time(cclock, &mts); | ||||
|     mach_port_deallocate(mach_task_self(), cclock); | ||||
|     spec->tv_sec = mts.tv_sec; | ||||
|     spec->tv_nsec = mts.tv_nsec; | ||||
|     return 0; | ||||
| } | ||||
| #else | ||||
| int janet_gettime(struct timespec *spec, enum JanetTimeSource source) { | ||||
|     clockid_t cid = CLOCK_REALTIME; | ||||
|     if (source == JANET_TIME_REALTIME) { | ||||
|         cid = CLOCK_REALTIME; | ||||
|     } else if (source == JANET_TIME_MONOTONIC) { | ||||
|         cid = CLOCK_MONOTONIC; | ||||
|     } else if (source == JANET_TIME_CPUTIME) { | ||||
|         cid = CLOCK_PROCESS_CPUTIME_ID; | ||||
|     } | ||||
|     return clock_gettime(cid, spec); | ||||
| int janet_gettime(struct timespec *spec) { | ||||
|     return clock_gettime(CLOCK_REALTIME, spec); | ||||
| } | ||||
| #endif | ||||
| #endif | ||||
|   | ||||
| @@ -49,7 +49,7 @@ | ||||
| #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, "C runtime error at line %d in file %s: %s\n",\ | ||||
|         __LINE__,\ | ||||
|         __FILE__,\ | ||||
|         (m));\ | ||||
| @@ -126,12 +126,7 @@ void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetReg | ||||
|  | ||||
| /* Clock gettime */ | ||||
| #ifdef JANET_GETTIME | ||||
| enum JanetTimeSource { | ||||
|     JANET_TIME_REALTIME, | ||||
|     JANET_TIME_MONOTONIC, | ||||
|     JANET_TIME_CPUTIME | ||||
| }; | ||||
| int janet_gettime(struct timespec *spec, enum JanetTimeSource source); | ||||
| int janet_gettime(struct timespec *spec); | ||||
| #endif | ||||
|  | ||||
| /* strdup */ | ||||
|   | ||||
| @@ -439,21 +439,20 @@ int janet_compare(Janet x, Janet y) { | ||||
|     return status - 2; | ||||
| } | ||||
|  | ||||
| static int32_t getter_checkint(JanetType type, Janet key, int32_t max) { | ||||
| static int32_t getter_checkint(Janet key, int32_t max) { | ||||
|     if (!janet_checkint(key)) goto bad; | ||||
|     int32_t ret = janet_unwrap_integer(key); | ||||
|     if (ret < 0) goto bad; | ||||
|     if (ret >= max) goto bad; | ||||
|     return ret; | ||||
| bad: | ||||
|     janet_panicf("expected integer key for %s in range [0, %d), got %v", janet_type_names[type], max, key); | ||||
|     janet_panicf("expected integer key in range [0, %d), got %v", max, key); | ||||
| } | ||||
|  | ||||
| /* Gets a value and returns. Can panic. */ | ||||
| Janet janet_in(Janet ds, Janet key) { | ||||
|     Janet value; | ||||
|     JanetType type = janet_type(ds); | ||||
|     switch (type) { | ||||
|     switch (janet_type(ds)) { | ||||
|         default: | ||||
|             janet_panicf("expected %T, got %v", JANET_TFLAG_LENGTHABLE, ds); | ||||
|             break; | ||||
| @@ -465,19 +464,19 @@ Janet janet_in(Janet ds, Janet key) { | ||||
|             break; | ||||
|         case JANET_ARRAY: { | ||||
|             JanetArray *array = janet_unwrap_array(ds); | ||||
|             int32_t index = getter_checkint(type, key, array->count); | ||||
|             int32_t index = getter_checkint(key, array->count); | ||||
|             value = array->data[index]; | ||||
|             break; | ||||
|         } | ||||
|         case JANET_TUPLE: { | ||||
|             const Janet *tuple = janet_unwrap_tuple(ds); | ||||
|             int32_t len = janet_tuple_length(tuple); | ||||
|             value = tuple[getter_checkint(type, key, len)]; | ||||
|             value = tuple[getter_checkint(key, len)]; | ||||
|             break; | ||||
|         } | ||||
|         case JANET_BUFFER: { | ||||
|             JanetBuffer *buffer = janet_unwrap_buffer(ds); | ||||
|             int32_t index = getter_checkint(type, key, buffer->count); | ||||
|             int32_t index = getter_checkint(key, buffer->count); | ||||
|             value = janet_wrap_integer(buffer->data[index]); | ||||
|             break; | ||||
|         } | ||||
| @@ -485,7 +484,7 @@ Janet janet_in(Janet ds, Janet key) { | ||||
|         case JANET_SYMBOL: | ||||
|         case JANET_KEYWORD: { | ||||
|             const uint8_t *str = janet_unwrap_string(ds); | ||||
|             int32_t index = getter_checkint(type, key, janet_string_length(str)); | ||||
|             int32_t index = getter_checkint(key, janet_string_length(str)); | ||||
|             value = janet_wrap_integer(str[index]); | ||||
|             break; | ||||
|         } | ||||
| @@ -698,16 +697,11 @@ Janet janet_lengthv(Janet x) { | ||||
|             const JanetAbstractType *type = janet_abstract_type(abst); | ||||
|             if (type->length != NULL) { | ||||
|                 size_t len = type->length(abst, janet_abstract_size(abst)); | ||||
|                 /* If len is always less then double, we can never overflow */ | ||||
| #ifdef JANET_32 | ||||
|                 return janet_wrap_number(len); | ||||
| #else | ||||
|                 if (len < (size_t) JANET_INTMAX_INT64) { | ||||
|                 if ((uint64_t) len <= (uint64_t) JANET_INTMAX_INT64) { | ||||
|                     return janet_wrap_number((double) len); | ||||
|                 } else { | ||||
|                     janet_panicf("integer length %u too large", len); | ||||
|                 } | ||||
| #endif | ||||
|             } | ||||
|             Janet argv[1] = { x }; | ||||
|             return janet_mcall("length", 1, argv); | ||||
| @@ -758,14 +752,13 @@ void janet_putindex(Janet ds, int32_t index, Janet value) { | ||||
| } | ||||
|  | ||||
| void janet_put(Janet ds, Janet key, Janet value) { | ||||
|     JanetType type = janet_type(ds); | ||||
|     switch (type) { | ||||
|     switch (janet_type(ds)) { | ||||
|         default: | ||||
|             janet_panicf("expected %T, got %v", | ||||
|                          JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds); | ||||
|         case JANET_ARRAY: { | ||||
|             JanetArray *array = janet_unwrap_array(ds); | ||||
|             int32_t index = getter_checkint(type, key, INT32_MAX - 1); | ||||
|             int32_t index = getter_checkint(key, INT32_MAX - 1); | ||||
|             if (index >= array->count) { | ||||
|                 janet_array_setcount(array, index + 1); | ||||
|             } | ||||
| @@ -774,7 +767,7 @@ void janet_put(Janet ds, Janet key, Janet value) { | ||||
|         } | ||||
|         case JANET_BUFFER: { | ||||
|             JanetBuffer *buffer = janet_unwrap_buffer(ds); | ||||
|             int32_t index = getter_checkint(type, key, INT32_MAX - 1); | ||||
|             int32_t index = getter_checkint(key, INT32_MAX - 1); | ||||
|             if (!janet_checkint(value)) | ||||
|                 janet_panicf("can only put integers in buffers, got %v", value); | ||||
|             if (index >= buffer->count) { | ||||
|   | ||||
| @@ -40,7 +40,7 @@ void *janet_v_grow(void *v, int32_t increment, int32_t itemsize) { | ||||
|  | ||||
| /* Convert a buffer to normal allocated memory (forget capacity) */ | ||||
| void *janet_v_flattenmem(void *v, int32_t itemsize) { | ||||
|     char *p; | ||||
|     int32_t *p; | ||||
|     if (NULL == v) return NULL; | ||||
|     size_t size = (size_t) itemsize * janet_v__cnt(v); | ||||
|     p = janet_malloc(size); | ||||
|   | ||||
							
								
								
									
										101
									
								
								src/core/vm.c
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								src/core/vm.c
									
									
									
									
									
								
							| @@ -116,6 +116,7 @@ | ||||
| #else | ||||
| #define vm_maybe_auto_suspend(COND) do { \ | ||||
|     if ((COND) && janet_vm.auto_suspend) { \ | ||||
|         janet_vm.auto_suspend = 0; \ | ||||
|         fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \ | ||||
|         vm_return(JANET_SIGNAL_INTERRUPT, janet_wrap_nil()); \ | ||||
|     } \ | ||||
| @@ -137,7 +138,7 @@ | ||||
|             vm_pcnext();\ | ||||
|         }\ | ||||
|     } | ||||
| #define _vm_bitop_immediate(op, type1, rangecheck, msg)\ | ||||
| #define _vm_bitop_immediate(op, type1)\ | ||||
|     {\ | ||||
|         Janet op1 = stack[B];\ | ||||
|         if (!janet_checktype(op1, JANET_NUMBER)) {\ | ||||
| @@ -146,15 +147,13 @@ | ||||
|             stack[A] = janet_mcall(#op, 2, _argv);\ | ||||
|             vm_checkgc_pcnext();\ | ||||
|         } else {\ | ||||
|             double y1 = janet_unwrap_number(op1);\ | ||||
|             if (!rangecheck(y1)) { vm_commit(); janet_panicf("value %v out of range for " msg, op1); }\ | ||||
|             type1 x1 = (type1) y1;\ | ||||
|             stack[A] = janet_wrap_number((type1) (x1 op CS));\ | ||||
|             type1 x1 = (type1) janet_unwrap_integer(op1);\ | ||||
|             stack[A] = janet_wrap_integer(x1 op CS);\ | ||||
|             vm_pcnext();\ | ||||
|         }\ | ||||
|     } | ||||
| #define vm_bitop_immediate(op) _vm_bitop_immediate(op, int32_t, janet_checkintrange, "32-bit signed integers"); | ||||
| #define vm_bitopu_immediate(op) _vm_bitop_immediate(op, uint32_t, janet_checkuintrange, "32-bit unsigned integers"); | ||||
| #define vm_bitop_immediate(op) _vm_bitop_immediate(op, int32_t); | ||||
| #define vm_bitopu_immediate(op) _vm_bitop_immediate(op, uint32_t); | ||||
| #define _vm_binop(op, wrap)\ | ||||
|     {\ | ||||
|         Janet op1 = stack[B];\ | ||||
| @@ -171,18 +170,14 @@ | ||||
|         }\ | ||||
|     } | ||||
| #define vm_binop(op) _vm_binop(op, janet_wrap_number) | ||||
| #define _vm_bitop(op, type1, rangecheck, msg)\ | ||||
| #define _vm_bitop(op, type1)\ | ||||
|     {\ | ||||
|         Janet op1 = stack[B];\ | ||||
|         Janet op2 = stack[C];\ | ||||
|         if (janet_checktype(op1, JANET_NUMBER) && janet_checktype(op2, JANET_NUMBER)) {\ | ||||
|             double y1 = janet_unwrap_number(op1);\ | ||||
|             double y2 = janet_unwrap_number(op2);\ | ||||
|             if (!rangecheck(y1)) { vm_commit(); janet_panicf("value %v out of range for " msg, op1); }\ | ||||
|             if (!janet_checkintrange(y2)) { vm_commit(); janet_panicf("rhs must be valid 32-bit signed integer, got %f", op2); }\ | ||||
|             type1 x1 = (type1) y1;\ | ||||
|             int32_t x2 = (int32_t) y2;\ | ||||
|             stack[A] = janet_wrap_number((type1) (x1 op x2));\ | ||||
|             type1 x1 = (type1) janet_unwrap_integer(op1);\ | ||||
|             int32_t x2 = janet_unwrap_integer(op2);\ | ||||
|             stack[A] = janet_wrap_integer(x1 op x2);\ | ||||
|             vm_pcnext();\ | ||||
|         } else {\ | ||||
|             vm_commit();\ | ||||
| @@ -190,8 +185,8 @@ | ||||
|             vm_checkgc_pcnext();\ | ||||
|         }\ | ||||
|     } | ||||
| #define vm_bitop(op) _vm_bitop(op, int32_t, janet_checkintrange, "32-bit signed integers") | ||||
| #define vm_bitopu(op) _vm_bitop(op, uint32_t, janet_checkuintrange, "32-bit unsigned integers") | ||||
| #define vm_bitop(op) _vm_bitop(op, int32_t) | ||||
| #define vm_bitopu(op) _vm_bitop(op, uint32_t) | ||||
| #define vm_compop(op) \ | ||||
|     {\ | ||||
|         Janet op1 = stack[B];\ | ||||
| @@ -300,16 +295,6 @@ static Janet janet_method_lookup(Janet x, const char *name) { | ||||
|     return method_to_fun(janet_ckeywordv(name), x); | ||||
| } | ||||
|  | ||||
| static Janet janet_unary_call(const char *method, Janet arg) { | ||||
|     Janet m = janet_method_lookup(arg, method); | ||||
|     if (janet_checktype(m, JANET_NIL)) { | ||||
|         janet_panicf("could not find method :%s for %v", method, arg); | ||||
|     } else { | ||||
|         Janet argv[1] = { arg }; | ||||
|         return janet_method_invoke(m, 1, argv); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* Call a method first on the righthand side, and then on the left hand side with a prefix */ | ||||
| static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lhs, Janet rhs) { | ||||
|     Janet lm = janet_method_lookup(lhs, lmethod); | ||||
| @@ -346,13 +331,11 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|         &&label_JOP_RETURN_NIL, | ||||
|         &&label_JOP_ADD_IMMEDIATE, | ||||
|         &&label_JOP_ADD, | ||||
|         &&label_JOP_SUBTRACT_IMMEDIATE, | ||||
|         &&label_JOP_SUBTRACT, | ||||
|         &&label_JOP_MULTIPLY_IMMEDIATE, | ||||
|         &&label_JOP_MULTIPLY, | ||||
|         &&label_JOP_DIVIDE_IMMEDIATE, | ||||
|         &&label_JOP_DIVIDE, | ||||
|         &&label_JOP_DIVIDE_FLOOR, | ||||
|         &&label_JOP_MODULO, | ||||
|         &&label_JOP_REMAINDER, | ||||
|         &&label_JOP_BAND, | ||||
| @@ -593,6 +576,8 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|         &&label_unknown_op, | ||||
|         &&label_unknown_op, | ||||
|         &&label_unknown_op, | ||||
|         &&label_unknown_op, | ||||
|         &&label_unknown_op, | ||||
|         &&label_unknown_op | ||||
|     }; | ||||
| #endif | ||||
| @@ -682,9 +667,6 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|     VM_OP(JOP_ADD) | ||||
|     vm_binop(+); | ||||
|  | ||||
|     VM_OP(JOP_SUBTRACT_IMMEDIATE) | ||||
|     vm_binop_immediate(-); | ||||
|  | ||||
|     VM_OP(JOP_SUBTRACT) | ||||
|     vm_binop(-); | ||||
|  | ||||
| @@ -700,33 +682,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|     VM_OP(JOP_DIVIDE) | ||||
|     vm_binop( /); | ||||
|  | ||||
|     VM_OP(JOP_DIVIDE_FLOOR) { | ||||
|         Janet op1 = stack[B]; | ||||
|         Janet op2 = stack[C]; | ||||
|         if (janet_checktype(op1, JANET_NUMBER) && janet_checktype(op2, JANET_NUMBER)) { | ||||
|             double x1 = janet_unwrap_number(op1); | ||||
|             double x2 = janet_unwrap_number(op2); | ||||
|             stack[A] = janet_wrap_number(floor(x1 / x2)); | ||||
|             vm_pcnext(); | ||||
|         } else { | ||||
|             vm_commit(); | ||||
|             stack[A] = janet_binop_call("div", "rdiv", op1, op2); | ||||
|             vm_checkgc_pcnext(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     VM_OP(JOP_MODULO) { | ||||
|         Janet op1 = stack[B]; | ||||
|         Janet op2 = stack[C]; | ||||
|         if (janet_checktype(op1, JANET_NUMBER) && janet_checktype(op2, JANET_NUMBER)) { | ||||
|             double x1 = janet_unwrap_number(op1); | ||||
|             double x2 = janet_unwrap_number(op2); | ||||
|             if (x2 == 0) { | ||||
|                 stack[A] = janet_wrap_number(x1); | ||||
|             } else { | ||||
|                 double intres = x2 * floor(x1 / x2); | ||||
|                 stack[A] = janet_wrap_number(x1 - intres); | ||||
|             } | ||||
|             double intres = x2 * floor(x1 / x2); | ||||
|             stack[A] = janet_wrap_number(x1 - intres); | ||||
|             vm_pcnext(); | ||||
|         } else { | ||||
|             vm_commit(); | ||||
| @@ -761,14 +724,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|  | ||||
|     VM_OP(JOP_BNOT) { | ||||
|         Janet op = stack[E]; | ||||
|         if (janet_checktype(op, JANET_NUMBER)) { | ||||
|             stack[A] = janet_wrap_integer(~janet_unwrap_integer(op)); | ||||
|             vm_pcnext(); | ||||
|         } else { | ||||
|             vm_commit(); | ||||
|             stack[A] = janet_unary_call("~", op); | ||||
|             vm_checkgc_pcnext(); | ||||
|         } | ||||
|         vm_assert_type(op, JANET_NUMBER); | ||||
|         stack[A] = janet_wrap_integer(~janet_unwrap_integer(op)); | ||||
|         vm_pcnext(); | ||||
|     } | ||||
|  | ||||
|     VM_OP(JOP_SHIFT_RIGHT_UNSIGNED) | ||||
| @@ -799,13 +757,13 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|  | ||||
|     VM_OP(JOP_JUMP) | ||||
|     pc += DS; | ||||
|     vm_maybe_auto_suspend(DS <= 0); | ||||
|     vm_maybe_auto_suspend(DS < 0); | ||||
|     vm_next(); | ||||
|  | ||||
|     VM_OP(JOP_JUMP_IF) | ||||
|     if (janet_truthy(stack[A])) { | ||||
|         pc += ES; | ||||
|         vm_maybe_auto_suspend(ES <= 0); | ||||
|         vm_maybe_auto_suspend(ES < 0); | ||||
|     } else { | ||||
|         pc++; | ||||
|     } | ||||
| @@ -816,14 +774,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|         pc++; | ||||
|     } else { | ||||
|         pc += ES; | ||||
|         vm_maybe_auto_suspend(ES <= 0); | ||||
|         vm_maybe_auto_suspend(ES < 0); | ||||
|     } | ||||
|     vm_next(); | ||||
|  | ||||
|     VM_OP(JOP_JUMP_IF_NIL) | ||||
|     if (janet_checktype(stack[A], JANET_NIL)) { | ||||
|         pc += ES; | ||||
|         vm_maybe_auto_suspend(ES <= 0); | ||||
|         vm_maybe_auto_suspend(ES < 0); | ||||
|     } else { | ||||
|         pc++; | ||||
|     } | ||||
| @@ -834,7 +792,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|         pc++; | ||||
|     } else { | ||||
|         pc += ES; | ||||
|         vm_maybe_auto_suspend(ES <= 0); | ||||
|         vm_maybe_auto_suspend(ES < 0); | ||||
|     } | ||||
|     vm_next(); | ||||
|  | ||||
| @@ -861,7 +819,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|     vm_pcnext(); | ||||
|  | ||||
|     VM_OP(JOP_EQUALS_IMMEDIATE) | ||||
|     stack[A] = janet_wrap_boolean(janet_checktype(stack[B], JANET_NUMBER) && (janet_unwrap_number(stack[B]) == (double) CS)); | ||||
|     stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) == (double) CS); | ||||
|     vm_pcnext(); | ||||
|  | ||||
|     VM_OP(JOP_NOT_EQUALS) | ||||
| @@ -869,7 +827,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|     vm_pcnext(); | ||||
|  | ||||
|     VM_OP(JOP_NOT_EQUALS_IMMEDIATE) | ||||
|     stack[A] = janet_wrap_boolean(!janet_checktype(stack[B], JANET_NUMBER) || (janet_unwrap_number(stack[B]) != (double) CS)); | ||||
|     stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) != (double) CS); | ||||
|     vm_pcnext(); | ||||
|  | ||||
|     VM_OP(JOP_COMPARE) | ||||
| @@ -1022,7 +980,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) { | ||||
|             if (func->gc.flags & JANET_FUNCFLAG_TRACE) { | ||||
|                 vm_do_trace(func, fiber->stacktop - fiber->stackstart, fiber->data + fiber->stackstart); | ||||
|             } | ||||
|             vm_commit(); | ||||
|             janet_stack_frame(stack)->pc = pc; | ||||
|             if (janet_fiber_funcframe(fiber, func)) { | ||||
|                 int32_t n = fiber->stacktop - fiber->stackstart; | ||||
|                 janet_panicf("%v called with %d argument%s, expected %d", | ||||
| @@ -1465,7 +1423,6 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o | ||||
|         if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) { | ||||
|             *out = in; | ||||
|             janet_fiber_set_status(fiber, sig); | ||||
|             fiber->last_value = child->last_value; | ||||
|             return sig; | ||||
|         } | ||||
|         /* Check if we need any special handling for certain opcodes */ | ||||
| @@ -1559,7 +1516,7 @@ JanetSignal janet_pcall( | ||||
|         fiber = janet_fiber(fun, 64, argc, argv); | ||||
|     } | ||||
|     if (f) *f = fiber; | ||||
|     if (NULL == fiber) { | ||||
|     if (!fiber) { | ||||
|         *out = janet_cstringv("arity mismatch"); | ||||
|         return JANET_SIGNAL_ERROR; | ||||
|     } | ||||
| @@ -1585,11 +1542,9 @@ int janet_init(void) { | ||||
|  | ||||
|     /* Garbage collection */ | ||||
|     janet_vm.blocks = NULL; | ||||
|     janet_vm.weak_blocks = NULL; | ||||
|     janet_vm.next_collection = 0; | ||||
|     janet_vm.gc_interval = 0x400000; | ||||
|     janet_vm.block_count = 0; | ||||
|     janet_vm.gc_mark_phase = 0; | ||||
|  | ||||
|     janet_symcache_init(); | ||||
|  | ||||
|   | ||||
| @@ -43,10 +43,10 @@ int (janet_truthy)(Janet x) { | ||||
|     return janet_truthy(x); | ||||
| } | ||||
|  | ||||
| JanetStruct(janet_unwrap_struct)(Janet x) { | ||||
| const JanetKV *(janet_unwrap_struct)(Janet x) { | ||||
|     return janet_unwrap_struct(x); | ||||
| } | ||||
| JanetTuple(janet_unwrap_tuple)(Janet x) { | ||||
| const Janet *(janet_unwrap_tuple)(Janet x) { | ||||
|     return janet_unwrap_tuple(x); | ||||
| } | ||||
| JanetFiber *(janet_unwrap_fiber)(Janet x) { | ||||
| @@ -61,16 +61,16 @@ JanetTable *(janet_unwrap_table)(Janet x) { | ||||
| JanetBuffer *(janet_unwrap_buffer)(Janet x) { | ||||
|     return janet_unwrap_buffer(x); | ||||
| } | ||||
| JanetString(janet_unwrap_string)(Janet x) { | ||||
| const uint8_t *(janet_unwrap_string)(Janet x) { | ||||
|     return janet_unwrap_string(x); | ||||
| } | ||||
| JanetSymbol(janet_unwrap_symbol)(Janet x) { | ||||
| const uint8_t *(janet_unwrap_symbol)(Janet x) { | ||||
|     return janet_unwrap_symbol(x); | ||||
| } | ||||
| JanetKeyword(janet_unwrap_keyword)(Janet x) { | ||||
| const uint8_t *(janet_unwrap_keyword)(Janet x) { | ||||
|     return janet_unwrap_keyword(x); | ||||
| } | ||||
| JanetAbstract(janet_unwrap_abstract)(Janet x) { | ||||
| void *(janet_unwrap_abstract)(Janet x) { | ||||
|     return janet_unwrap_abstract(x); | ||||
| } | ||||
| void *(janet_unwrap_pointer)(Janet x) { | ||||
| @@ -102,22 +102,22 @@ Janet(janet_wrap_false)(void) { | ||||
| Janet(janet_wrap_boolean)(int x) { | ||||
|     return janet_wrap_boolean(x); | ||||
| } | ||||
| Janet(janet_wrap_string)(JanetString x) { | ||||
| Janet(janet_wrap_string)(const uint8_t *x) { | ||||
|     return janet_wrap_string(x); | ||||
| } | ||||
| Janet(janet_wrap_symbol)(JanetSymbol x) { | ||||
| Janet(janet_wrap_symbol)(const uint8_t *x) { | ||||
|     return janet_wrap_symbol(x); | ||||
| } | ||||
| Janet(janet_wrap_keyword)(JanetKeyword x) { | ||||
| Janet(janet_wrap_keyword)(const uint8_t *x) { | ||||
|     return janet_wrap_keyword(x); | ||||
| } | ||||
| Janet(janet_wrap_array)(JanetArray *x) { | ||||
|     return janet_wrap_array(x); | ||||
| } | ||||
| Janet(janet_wrap_tuple)(JanetTuple x) { | ||||
| Janet(janet_wrap_tuple)(const Janet *x) { | ||||
|     return janet_wrap_tuple(x); | ||||
| } | ||||
| Janet(janet_wrap_struct)(JanetStruct x) { | ||||
| Janet(janet_wrap_struct)(const JanetKV *x) { | ||||
|     return janet_wrap_struct(x); | ||||
| } | ||||
| Janet(janet_wrap_fiber)(JanetFiber *x) { | ||||
| @@ -135,7 +135,7 @@ Janet(janet_wrap_cfunction)(JanetCFunction x) { | ||||
| Janet(janet_wrap_table)(JanetTable *x) { | ||||
|     return janet_wrap_table(x); | ||||
| } | ||||
| Janet(janet_wrap_abstract)(JanetAbstract x) { | ||||
| Janet(janet_wrap_abstract)(void *x) { | ||||
|     return janet_wrap_abstract(x); | ||||
| } | ||||
| Janet(janet_wrap_pointer)(void *x) { | ||||
| @@ -317,3 +317,4 @@ JANET_WRAP_DEFINE(pointer, void *, JANET_POINTER, pointer) | ||||
| #undef JANET_WRAP_DEFINE | ||||
|  | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -234,28 +234,10 @@ extern "C" { | ||||
| #define JANET_EV_KQUEUE | ||||
| #endif | ||||
|  | ||||
| /* Use poll as last resort */ | ||||
| #if !defined(JANET_WINDOWS) && !defined(JANET_EV_EPOLL) && !defined(JANET_EV_KQUEUE) | ||||
| #define JANET_EV_POLL | ||||
| #endif | ||||
|  | ||||
| /* How to export symbols */ | ||||
| #ifndef JANET_EXPORT | ||||
| #ifdef JANET_WINDOWS | ||||
| #define JANET_EXPORT __declspec(dllexport) | ||||
| #else | ||||
| #define JANET_EXPORT __attribute__((visibility ("default"))) | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| /* How declare API functions */ | ||||
| #ifndef JANET_API | ||||
| #ifdef JANET_WINDOWS | ||||
| #ifdef JANET_DLL_IMPORT | ||||
| #define JANET_API __declspec(dllimport) | ||||
| #else | ||||
| #define JANET_API __declspec(dllexport) | ||||
| #endif | ||||
| #else | ||||
| #define JANET_API __attribute__((visibility ("default"))) | ||||
| #endif | ||||
| @@ -372,6 +354,7 @@ typedef struct JanetOSRWLock JanetOSRWLock; | ||||
| #include <stddef.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
|  | ||||
| /* What to do when out of memory */ | ||||
| #ifndef JANET_OUT_OF_MEMORY | ||||
| #define JANET_OUT_OF_MEMORY do { fprintf(stderr, "%s:%d - janet out of memory\n", __FILE__, __LINE__); exit(1); } while (0) | ||||
| @@ -411,11 +394,12 @@ typedef enum { | ||||
|     JANET_SIGNAL_USER6, | ||||
|     JANET_SIGNAL_USER7, | ||||
|     JANET_SIGNAL_USER8, | ||||
|     JANET_SIGNAL_USER9, | ||||
|     JANET_SIGNAL_INTERRUPT = JANET_SIGNAL_USER8, | ||||
|     JANET_SIGNAL_EVENT = JANET_SIGNAL_USER9, | ||||
|     JANET_SIGNAL_USER9 | ||||
| } JanetSignal; | ||||
|  | ||||
| #define JANET_SIGNAL_EVENT JANET_SIGNAL_USER9 | ||||
| #define JANET_SIGNAL_INTERRUPT JANET_SIGNAL_USER8 | ||||
|  | ||||
| /* Fiber statuses - mostly corresponds to signals. */ | ||||
| typedef enum { | ||||
|     JANET_STATUS_DEAD, | ||||
| @@ -579,62 +563,69 @@ typedef void *JanetAbstract; | ||||
|  | ||||
| #define JANET_STREAM_CLOSED 0x1 | ||||
| #define JANET_STREAM_SOCKET 0x2 | ||||
| #define JANET_STREAM_UNREGISTERED 0x4 | ||||
| #define JANET_STREAM_IOCP 0x4 | ||||
| #define JANET_STREAM_READABLE 0x200 | ||||
| #define JANET_STREAM_WRITABLE 0x400 | ||||
| #define JANET_STREAM_ACCEPTABLE 0x800 | ||||
| #define JANET_STREAM_UDPSERVER 0x1000 | ||||
| #define JANET_STREAM_TOCLOSE 0x10000 | ||||
|  | ||||
| typedef enum { | ||||
|     JANET_ASYNC_EVENT_INIT = 0, | ||||
|     JANET_ASYNC_EVENT_MARK = 1, | ||||
|     JANET_ASYNC_EVENT_DEINIT = 2, | ||||
|     JANET_ASYNC_EVENT_CLOSE = 3, | ||||
|     JANET_ASYNC_EVENT_ERR = 4, | ||||
|     JANET_ASYNC_EVENT_HUP = 5, | ||||
|     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_INIT, | ||||
|     JANET_ASYNC_EVENT_MARK, | ||||
|     JANET_ASYNC_EVENT_DEINIT, | ||||
|     JANET_ASYNC_EVENT_CLOSE, | ||||
|     JANET_ASYNC_EVENT_ERR, | ||||
|     JANET_ASYNC_EVENT_HUP, | ||||
|     JANET_ASYNC_EVENT_READ, | ||||
|     JANET_ASYNC_EVENT_WRITE, | ||||
|     JANET_ASYNC_EVENT_CANCEL, | ||||
|     JANET_ASYNC_EVENT_COMPLETE, /* Used on windows for IOCP */ | ||||
|     JANET_ASYNC_EVENT_USER | ||||
| } JanetAsyncEvent; | ||||
|  | ||||
| #define JANET_ASYNC_LISTEN_READ (1 << JANET_ASYNC_EVENT_READ) | ||||
| #define JANET_ASYNC_LISTEN_WRITE (1 << JANET_ASYNC_EVENT_WRITE) | ||||
|  | ||||
| typedef enum { | ||||
|     JANET_ASYNC_LISTEN_READ = 1, | ||||
|     JANET_ASYNC_LISTEN_WRITE, | ||||
|     JANET_ASYNC_LISTEN_BOTH | ||||
| } JanetAsyncMode; | ||||
|     JANET_ASYNC_STATUS_NOT_DONE, | ||||
|     JANET_ASYNC_STATUS_DONE | ||||
| } JanetAsyncStatus; | ||||
|  | ||||
| /* Typedefs */ | ||||
| typedef struct JanetListenerState JanetListenerState; | ||||
| typedef struct JanetStream JanetStream; | ||||
| typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event); | ||||
| typedef JanetAsyncStatus(*JanetListener)(JanetListenerState *state, JanetAsyncEvent event); | ||||
|  | ||||
| /* Wrapper around file descriptors and HANDLEs that can be polled. */ | ||||
| struct JanetStream { | ||||
|     JanetHandle handle; | ||||
|     uint32_t flags; | ||||
|     uint32_t index; | ||||
|     JanetFiber *read_fiber; | ||||
|     JanetFiber *write_fiber; | ||||
|     /* Linked list of all in-flight IO routines for this stream */ | ||||
|     JanetListenerState *state; | ||||
|     const void *methods; /* Methods for this stream */ | ||||
|     /* internal - used to disallow multiple concurrent reads / writes on the same stream. | ||||
|      * this constraint may be lifted later but allowing such would require more internal book keeping | ||||
|      * for some implementations. You can read and write at the same time on the same stream, though. */ | ||||
|     int _mask; | ||||
| }; | ||||
|  | ||||
| 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); | ||||
|  | ||||
| #endif | ||||
|  | ||||
| /* Janet uses atomic integers in several places for synchronization between threads and | ||||
|  * signals. Define them here */ | ||||
| /* Interface for state machine based event loop */ | ||||
| struct JanetListenerState { | ||||
|     JanetListener machine; | ||||
|     JanetFiber *fiber; | ||||
|     JanetStream *stream; | ||||
|     void *event; /* Used to pass data from asynchronous IO event. Contents depend on both | ||||
|                     implementation of the event loop and the particular event. */ | ||||
| #ifdef JANET_WINDOWS | ||||
| typedef long JanetAtomicInt; | ||||
| #else | ||||
| typedef int32_t JanetAtomicInt; | ||||
|     void *tag; /* Used to associate listeners with an overlapped structure */ | ||||
|     int bytes; /* Used to track how many bytes were transfered. */ | ||||
| #endif | ||||
|     /* internal */ | ||||
|     size_t _index; | ||||
|     int _mask; | ||||
|     JanetListenerState *_next; | ||||
| }; | ||||
| #endif | ||||
| JANET_API JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x); | ||||
| JANET_API JanetAtomicInt janet_atomic_dec(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 | ||||
| @@ -662,10 +653,10 @@ JANET_API JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x); | ||||
|  * external bindings, we should prefer using the Head structs directly, and | ||||
|  * use the host language to add sugar around the manipulation of the Janet types. */ | ||||
|  | ||||
| JANET_API JanetStructHead *janet_struct_head(JanetStruct st); | ||||
| JANET_API JanetStructHead *janet_struct_head(const JanetKV *st); | ||||
| JANET_API JanetAbstractHead *janet_abstract_head(const void *abstract); | ||||
| JANET_API JanetStringHead *janet_string_head(JanetString s); | ||||
| JANET_API JanetTupleHead *janet_tuple_head(JanetTuple tuple); | ||||
| JANET_API JanetStringHead *janet_string_head(const uint8_t *s); | ||||
| JANET_API JanetTupleHead *janet_tuple_head(const Janet *tuple); | ||||
|  | ||||
| /* Some language bindings won't have access to the macro versions. */ | ||||
|  | ||||
| @@ -674,16 +665,16 @@ JANET_API int janet_checktype(Janet x, JanetType type); | ||||
| JANET_API int janet_checktypes(Janet x, int typeflags); | ||||
| JANET_API int janet_truthy(Janet x); | ||||
|  | ||||
| JANET_API JanetStruct janet_unwrap_struct(Janet x); | ||||
| JANET_API JanetTuple janet_unwrap_tuple(Janet x); | ||||
| JANET_API const JanetKV *janet_unwrap_struct(Janet x); | ||||
| JANET_API const Janet *janet_unwrap_tuple(Janet x); | ||||
| JANET_API JanetFiber *janet_unwrap_fiber(Janet x); | ||||
| JANET_API JanetArray *janet_unwrap_array(Janet x); | ||||
| JANET_API JanetTable *janet_unwrap_table(Janet x); | ||||
| JANET_API JanetBuffer *janet_unwrap_buffer(Janet x); | ||||
| JANET_API JanetString janet_unwrap_string(Janet x); | ||||
| JANET_API JanetSymbol janet_unwrap_symbol(Janet x); | ||||
| JANET_API JanetKeyword janet_unwrap_keyword(Janet x); | ||||
| JANET_API JanetAbstract janet_unwrap_abstract(Janet x); | ||||
| JANET_API const uint8_t *janet_unwrap_string(Janet x); | ||||
| JANET_API const uint8_t *janet_unwrap_symbol(Janet x); | ||||
| JANET_API const uint8_t *janet_unwrap_keyword(Janet x); | ||||
| JANET_API void *janet_unwrap_abstract(Janet x); | ||||
| JANET_API void *janet_unwrap_pointer(Janet x); | ||||
| JANET_API JanetFunction *janet_unwrap_function(Janet x); | ||||
| JANET_API JanetCFunction janet_unwrap_cfunction(Janet x); | ||||
| @@ -696,18 +687,18 @@ JANET_API Janet janet_wrap_number(double x); | ||||
| JANET_API Janet janet_wrap_true(void); | ||||
| JANET_API Janet janet_wrap_false(void); | ||||
| JANET_API Janet janet_wrap_boolean(int x); | ||||
| JANET_API Janet janet_wrap_string(JanetString x); | ||||
| JANET_API Janet janet_wrap_symbol(JanetSymbol x); | ||||
| JANET_API Janet janet_wrap_keyword(JanetKeyword x); | ||||
| JANET_API Janet janet_wrap_string(const uint8_t *x); | ||||
| JANET_API Janet janet_wrap_symbol(const uint8_t *x); | ||||
| JANET_API Janet janet_wrap_keyword(const uint8_t *x); | ||||
| JANET_API Janet janet_wrap_array(JanetArray *x); | ||||
| JANET_API Janet janet_wrap_tuple(JanetTuple x); | ||||
| JANET_API Janet janet_wrap_struct(JanetStruct x); | ||||
| JANET_API Janet janet_wrap_tuple(const Janet *x); | ||||
| JANET_API Janet janet_wrap_struct(const JanetKV *x); | ||||
| JANET_API Janet janet_wrap_fiber(JanetFiber *x); | ||||
| JANET_API Janet janet_wrap_buffer(JanetBuffer *x); | ||||
| JANET_API Janet janet_wrap_function(JanetFunction *x); | ||||
| JANET_API Janet janet_wrap_cfunction(JanetCFunction x); | ||||
| JANET_API Janet janet_wrap_table(JanetTable *x); | ||||
| JANET_API Janet janet_wrap_abstract(JanetAbstract x); | ||||
| JANET_API Janet janet_wrap_abstract(void *x); | ||||
| JANET_API Janet janet_wrap_pointer(void *x); | ||||
| JANET_API Janet janet_wrap_integer(int32_t x); | ||||
|  | ||||
| @@ -739,7 +730,6 @@ JANET_API Janet janet_wrap_integer(int32_t x); | ||||
|         ? janet_nanbox_isnumber(x) \ | ||||
|         : janet_nanbox_checkauxtype((x), (t))) | ||||
|  | ||||
| /* Use JANET_API so that modules will use a local version of these functions if possible */ | ||||
| JANET_API void *janet_nanbox_to_pointer(Janet x); | ||||
| JANET_API Janet janet_nanbox_from_pointer(void *p, uint64_t tagmask); | ||||
| JANET_API Janet janet_nanbox_from_cpointer(const void *p, uint64_t tagmask); | ||||
| @@ -786,14 +776,14 @@ JANET_API Janet janet_nanbox_from_bits(uint64_t bits); | ||||
| #define janet_wrap_pointer(s) janet_nanbox_wrap_((s), JANET_POINTER) | ||||
|  | ||||
| /* Unwrap the pointer types */ | ||||
| #define janet_unwrap_struct(x) ((JanetStruct)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_tuple(x) ((JanetTuple)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_struct(x) ((const JanetKV *)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_tuple(x) ((const Janet *)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_fiber(x) ((JanetFiber *)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_array(x) ((JanetArray *)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_table(x) ((JanetTable *)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_buffer(x) ((JanetBuffer *)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_string(x) ((JanetString)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_symbol(x) ((JanetSymbol)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_string(x) ((const uint8_t *)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_symbol(x) ((const uint8_t *)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_keyword(x) ((const uint8_t *)janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_abstract(x) (janet_nanbox_to_pointer(x)) | ||||
| #define janet_unwrap_pointer(x) (janet_nanbox_to_pointer(x)) | ||||
| @@ -835,15 +825,15 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer); | ||||
| #define janet_wrap_cfunction(s) janet_nanbox32_from_tagp(JANET_CFUNCTION, (void *)(s)) | ||||
| #define janet_wrap_pointer(s) janet_nanbox32_from_tagp(JANET_POINTER, (void *)(s)) | ||||
|  | ||||
| #define janet_unwrap_struct(x) ((JanetStruct)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_tuple(x) ((JanetTuple)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_struct(x) ((const JanetKV *)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_tuple(x) ((const Janet *)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_fiber(x) ((JanetFiber *)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_array(x) ((JanetArray *)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_table(x) ((JanetTable *)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_buffer(x) ((JanetBuffer *)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_string(x) ((JanetString)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_symbol(x) ((JanetSymbol)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_keyword(x) ((JanetKeyword)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_string(x) ((const uint8_t *)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_symbol(x) ((const uint8_t *)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_keyword(x) ((const uint8_t *)(x).tagged.payload.pointer) | ||||
| #define janet_unwrap_abstract(x) ((x).tagged.payload.pointer) | ||||
| #define janet_unwrap_pointer(x) ((x).tagged.payload.pointer) | ||||
| #define janet_unwrap_function(x) ((JanetFunction *)(x).tagged.payload.pointer) | ||||
| @@ -858,15 +848,15 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer); | ||||
| #define janet_truthy(x) \ | ||||
|     ((x).type != JANET_NIL && ((x).type != JANET_BOOLEAN || ((x).as.u64 & 0x1))) | ||||
|  | ||||
| #define janet_unwrap_struct(x) ((JanetStruct)(x).as.pointer) | ||||
| #define janet_unwrap_tuple(x) ((JanetTuple)(x).as.pointer) | ||||
| #define janet_unwrap_struct(x) ((const JanetKV *)(x).as.pointer) | ||||
| #define janet_unwrap_tuple(x) ((const Janet *)(x).as.pointer) | ||||
| #define janet_unwrap_fiber(x) ((JanetFiber *)(x).as.pointer) | ||||
| #define janet_unwrap_array(x) ((JanetArray *)(x).as.pointer) | ||||
| #define janet_unwrap_table(x) ((JanetTable *)(x).as.pointer) | ||||
| #define janet_unwrap_buffer(x) ((JanetBuffer *)(x).as.pointer) | ||||
| #define janet_unwrap_string(x) ((JanetString)(x).as.pointer) | ||||
| #define janet_unwrap_symbol(x) ((JanetSymbol)(x).as.pointer) | ||||
| #define janet_unwrap_keyword(x) ((JanetKeyword)(x).as.pointer) | ||||
| #define janet_unwrap_string(x) ((const uint8_t *)(x).as.pointer) | ||||
| #define janet_unwrap_symbol(x) ((const uint8_t *)(x).as.pointer) | ||||
| #define janet_unwrap_keyword(x) ((const uint8_t *)(x).as.pointer) | ||||
| #define janet_unwrap_abstract(x) ((x).as.pointer) | ||||
| #define janet_unwrap_pointer(x) ((x).as.pointer) | ||||
| #define janet_unwrap_function(x) ((JanetFunction *)(x).as.pointer) | ||||
| @@ -878,15 +868,12 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer); | ||||
| #endif | ||||
|  | ||||
| JANET_API int janet_checkint(Janet x); | ||||
| JANET_API int janet_checkuint(Janet x); | ||||
| JANET_API int janet_checkint64(Janet x); | ||||
| JANET_API int janet_checkuint64(Janet x); | ||||
| JANET_API int janet_checksize(Janet x); | ||||
| JANET_API JanetAbstract janet_checkabstract(Janet x, const JanetAbstractType *at); | ||||
| #define janet_checkintrange(x) ((x) >= INT32_MIN && (x) <= INT32_MAX && (x) == (int32_t)(x)) | ||||
| #define janet_checkuintrange(x) ((x) >= 0 && (x) <= UINT32_MAX && (x) == (uint32_t)(x)) | ||||
| #define janet_checkint64range(x) ((x) >= JANET_INTMIN_DOUBLE && (x) <= JANET_INTMAX_DOUBLE && (x) == (int64_t)(x)) | ||||
| #define janet_checkuint64range(x) ((x) >= 0 && (x) <= JANET_INTMAX_DOUBLE && (x) == (uint64_t)(x)) | ||||
| #define janet_unwrap_integer(x) ((int32_t) janet_unwrap_number(x)) | ||||
| #define janet_wrap_integer(x) janet_wrap_number((int32_t)(x)) | ||||
|  | ||||
| @@ -899,7 +886,7 @@ struct JanetGCObject { | ||||
|     int32_t flags; | ||||
|     union { | ||||
|         JanetGCObject *next; | ||||
|         volatile JanetAtomicInt refcount; /* For threaded abstract types */ | ||||
|         int32_t refcount; /* For threaded abstract types */ | ||||
|     } data; | ||||
| }; | ||||
|  | ||||
| @@ -922,10 +909,8 @@ struct JanetFiber { | ||||
|      * that is, fibers that are scheduled on the event loop and behave much like threads | ||||
|      * in a multi-tasking system. It would be possible to move these fields to a new | ||||
|      * type, say "JanetTask", that as separate from fibers to save a bit of space. */ | ||||
|     JanetListenerState *waiting; | ||||
|     uint32_t sched_id; /* Increment everytime fiber is scheduled by event loop */ | ||||
|     JanetEVCallback ev_callback; /* Call this before starting scheduled fibers */ | ||||
|     JanetStream *ev_stream; /* which stream we are waiting on */ | ||||
|     void *ev_state; /* Extra data for ev callback state. On windows, first element must be OVERLAPPED. */ | ||||
|     void *supervisor_channel; /* Channel to push self to when complete */ | ||||
| #endif | ||||
| }; | ||||
| @@ -1274,13 +1259,11 @@ enum JanetOpCode { | ||||
|     JOP_RETURN_NIL, | ||||
|     JOP_ADD_IMMEDIATE, | ||||
|     JOP_ADD, | ||||
|     JOP_SUBTRACT_IMMEDIATE, | ||||
|     JOP_SUBTRACT, | ||||
|     JOP_MULTIPLY_IMMEDIATE, | ||||
|     JOP_MULTIPLY, | ||||
|     JOP_DIVIDE_IMMEDIATE, | ||||
|     JOP_DIVIDE, | ||||
|     JOP_DIVIDE_FLOOR, | ||||
|     JOP_MODULO, | ||||
|     JOP_REMAINDER, | ||||
|     JOP_BAND, | ||||
| @@ -1400,7 +1383,9 @@ JANET_API void janet_stream_flags(JanetStream *stream, uint32_t flags); | ||||
| JANET_API void janet_schedule(JanetFiber *fiber, Janet value); | ||||
| JANET_API void janet_cancel(JanetFiber *fiber, Janet value); | ||||
| JANET_API void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig); | ||||
| JANET_API void janet_schedule_soon(JanetFiber *fiber, Janet value, JanetSignal sig); | ||||
|  | ||||
| /* Start a state machine listening for events from a stream */ | ||||
| JANET_API JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user); | ||||
|  | ||||
| /* Shorthand for yielding to event loop in C */ | ||||
| JANET_NO_RETURN JANET_API void janet_await(void); | ||||
| @@ -1592,7 +1577,6 @@ JANET_API double janet_rng_double(JanetRNG *rng); | ||||
|  | ||||
| /* Array functions */ | ||||
| JANET_API JanetArray *janet_array(int32_t capacity); | ||||
| JANET_API JanetArray *janet_array_weak(int32_t capacity); | ||||
| JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n); | ||||
| JANET_API void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth); | ||||
| JANET_API void janet_array_setcount(JanetArray *array, int32_t count); | ||||
| @@ -1622,7 +1606,7 @@ JANET_API void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x); | ||||
| #define JANET_TUPLE_FLAG_BRACKETCTOR 0x10000 | ||||
|  | ||||
| #define janet_tuple_head(t) ((JanetTupleHead *)((char *)t - offsetof(JanetTupleHead, data))) | ||||
| #define janet_tuple_from_head(gcobject) ((JanetTuple)((char *)gcobject + offsetof(JanetTupleHead, data))) | ||||
| #define janet_tuple_from_head(gcobject) ((const Janet *)((char *)gcobject + offsetof(JanetTupleHead, data))) | ||||
| #define janet_tuple_length(t) (janet_tuple_head(t)->length) | ||||
| #define janet_tuple_hash(t) (janet_tuple_head(t)->hash) | ||||
| #define janet_tuple_sm_line(t) (janet_tuple_head(t)->sm_line) | ||||
| @@ -1668,7 +1652,7 @@ JANET_API JanetSymbol janet_symbol_gen(void); | ||||
|  | ||||
| /* Structs */ | ||||
| #define janet_struct_head(t) ((JanetStructHead *)((char *)t - offsetof(JanetStructHead, data))) | ||||
| #define janet_struct_from_head(t) ((JanetStruct)((char *)gcobject + offsetof(JanetStructHead, data))) | ||||
| #define janet_struct_from_head(t) ((const JanetKV *)((char *)gcobject + offsetof(JanetStructHead, data))) | ||||
| #define janet_struct_length(t) (janet_struct_head(t)->length) | ||||
| #define janet_struct_capacity(t) (janet_struct_head(t)->capacity) | ||||
| #define janet_struct_hash(t) (janet_struct_head(t)->hash) | ||||
| @@ -1809,7 +1793,6 @@ JANET_API void janet_vm_free(JanetVM *vm); | ||||
| JANET_API void janet_vm_save(JanetVM *into); | ||||
| JANET_API void janet_vm_load(JanetVM *from); | ||||
| JANET_API void janet_interpreter_interrupt(JanetVM *vm); | ||||
| JANET_API void janet_interpreter_interrupt_handled(JanetVM *vm); | ||||
| JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out); | ||||
| JANET_API JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig); | ||||
| JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f); | ||||
| @@ -1824,17 +1807,13 @@ JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *pr | ||||
| #define JANET_SANDBOX_SUBPROCESS 2 | ||||
| #define JANET_SANDBOX_NET_CONNECT 4 | ||||
| #define JANET_SANDBOX_NET_LISTEN 8 | ||||
| #define JANET_SANDBOX_FFI_DEFINE 16 | ||||
| #define JANET_SANDBOX_FFI 16 | ||||
| #define JANET_SANDBOX_FS_WRITE 32 | ||||
| #define JANET_SANDBOX_FS_READ 64 | ||||
| #define JANET_SANDBOX_HRTIME 128 | ||||
| #define JANET_SANDBOX_ENV 256 | ||||
| #define JANET_SANDBOX_DYNAMIC_MODULES 512 | ||||
| #define JANET_SANDBOX_FS_TEMP 1024 | ||||
| #define JANET_SANDBOX_FFI_USE 2048 | ||||
| #define JANET_SANDBOX_FFI_JIT 4096 | ||||
| #define JANET_SANDBOX_SIGNAL 8192 | ||||
| #define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT) | ||||
| #define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP) | ||||
| #define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN) | ||||
| #define JANET_SANDBOX_ALL (UINT32_MAX) | ||||
| @@ -1921,6 +1900,7 @@ JANET_API Janet janet_resolve_core(const char *name); | ||||
| #define JANET_DEF_SD(ENV, JNAME, VAL, DOC) \ | ||||
|     janet_def_sm(ENV, JNAME, VAL, DOC, __FILE__, __LINE__) | ||||
|  | ||||
|  | ||||
| /* Choose defaults for source mapping and docstring based on config defs */ | ||||
| #if defined(JANET_NO_SOURCEMAPS) && defined(JANET_NO_DOCSTRINGS) | ||||
| #define JANET_REG JANET_REG_ | ||||
| @@ -1957,10 +1937,10 @@ JANET_API void janet_register(const char *name, JanetCFunction cfun); | ||||
| #endif | ||||
| #ifndef JANET_ENTRY_NAME | ||||
| #define JANET_MODULE_ENTRY \ | ||||
|     JANET_MODULE_PREFIX JANET_EXPORT JanetBuildConfig _janet_mod_config(void) { \ | ||||
|     JANET_MODULE_PREFIX JANET_API JanetBuildConfig _janet_mod_config(void) { \ | ||||
|         return janet_config_current(); \ | ||||
|     } \ | ||||
|     JANET_MODULE_PREFIX JANET_EXPORT void _janet_init | ||||
|     JANET_MODULE_PREFIX JANET_API void _janet_init | ||||
| #else | ||||
| #define JANET_MODULE_ENTRY JANET_MODULE_PREFIX JANET_API void JANET_ENTRY_NAME | ||||
| #endif | ||||
| @@ -2009,8 +1989,6 @@ JANET_API JanetDictView janet_getdictionary(const Janet *argv, int32_t n); | ||||
| JANET_API void *janet_getabstract(const Janet *argv, int32_t n, const JanetAbstractType *at); | ||||
| JANET_API JanetRange janet_getslice(int32_t argc, const Janet *argv); | ||||
| JANET_API int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const char *which); | ||||
| JANET_API int32_t janet_getstartrange(const Janet *argv, int32_t argc, int32_t n, int32_t length); | ||||
| JANET_API int32_t janet_getendrange(const Janet *argv, int32_t argc, int32_t n, int32_t length); | ||||
| JANET_API int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which); | ||||
| JANET_API uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags); | ||||
|  | ||||
| @@ -2069,7 +2047,6 @@ JANET_API int janet_cryptorand(uint8_t *out, size_t n); | ||||
| JANET_API void janet_marshal_size(JanetMarshalContext *ctx, size_t value); | ||||
| JANET_API void janet_marshal_int(JanetMarshalContext *ctx, int32_t value); | ||||
| JANET_API void janet_marshal_int64(JanetMarshalContext *ctx, int64_t value); | ||||
| JANET_API void janet_marshal_ptr(JanetMarshalContext *ctx, const void *value); | ||||
| JANET_API void janet_marshal_byte(JanetMarshalContext *ctx, uint8_t value); | ||||
| JANET_API void janet_marshal_bytes(JanetMarshalContext *ctx, const uint8_t *bytes, size_t len); | ||||
| JANET_API void janet_marshal_janet(JanetMarshalContext *ctx, Janet x); | ||||
| @@ -2079,12 +2056,10 @@ JANET_API void janet_unmarshal_ensure(JanetMarshalContext *ctx, size_t size); | ||||
| JANET_API size_t janet_unmarshal_size(JanetMarshalContext *ctx); | ||||
| JANET_API int32_t janet_unmarshal_int(JanetMarshalContext *ctx); | ||||
| JANET_API int64_t janet_unmarshal_int64(JanetMarshalContext *ctx); | ||||
| JANET_API void *janet_unmarshal_ptr(JanetMarshalContext *ctx); | ||||
| JANET_API uint8_t janet_unmarshal_byte(JanetMarshalContext *ctx); | ||||
| JANET_API void janet_unmarshal_bytes(JanetMarshalContext *ctx, uint8_t *dest, size_t len); | ||||
| JANET_API Janet janet_unmarshal_janet(JanetMarshalContext *ctx); | ||||
| JANET_API JanetAbstract janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size); | ||||
| JANET_API JanetAbstract janet_unmarshal_abstract_threaded(JanetMarshalContext *ctx, size_t size); | ||||
| JANET_API void janet_unmarshal_abstract_reuse(JanetMarshalContext *ctx, void *p); | ||||
|  | ||||
| JANET_API void janet_register_abstract_type(const JanetAbstractType *at); | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
| #ifdef _WIN32 | ||||
| #include <windows.h> | ||||
| #include <shlwapi.h> | ||||
| #include <versionhelpers.h> | ||||
| #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING | ||||
| #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 | ||||
| #endif | ||||
| @@ -146,8 +147,9 @@ static void setup_console_output(void) { | ||||
|     HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); | ||||
|     DWORD dwMode = 0; | ||||
|     GetConsoleMode(hOut, &dwMode); | ||||
|     dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; | ||||
|     dwMode |= ENABLE_PROCESSED_OUTPUT; | ||||
|     if (IsWindows10OrGreater()) { | ||||
|         dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; | ||||
|     } | ||||
|     SetConsoleMode(hOut, dwMode); | ||||
|     if (IsValidCodePage(65001)) { | ||||
|         SetConsoleOutputCP(65001); | ||||
| @@ -163,8 +165,10 @@ static int rawmode(void) { | ||||
|     dwMode &= ~ENABLE_LINE_INPUT; | ||||
|     dwMode &= ~ENABLE_INSERT_MODE; | ||||
|     dwMode &= ~ENABLE_ECHO_INPUT; | ||||
|     dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT; | ||||
|     dwMode &= ~ENABLE_PROCESSED_INPUT; | ||||
|     if (IsWindows10OrGreater()) { | ||||
|         dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT; | ||||
|         dwMode &= ~ENABLE_PROCESSED_INPUT; | ||||
|     } | ||||
|     if (!SetConsoleMode(hOut, dwMode)) return 1; | ||||
|     gbl_israwmode = 1; | ||||
|     return 0; | ||||
| @@ -179,8 +183,10 @@ static void norawmode(void) { | ||||
|     dwMode |= ENABLE_LINE_INPUT; | ||||
|     dwMode |= ENABLE_INSERT_MODE; | ||||
|     dwMode |= ENABLE_ECHO_INPUT; | ||||
|     dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT; | ||||
|     dwMode |= ENABLE_PROCESSED_INPUT; | ||||
|     if (IsWindows10OrGreater()) { | ||||
|         dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT; | ||||
|         dwMode |= ENABLE_PROCESSED_INPUT; | ||||
|     } | ||||
|     SetConsoleMode(hOut, dwMode); | ||||
|     gbl_israwmode = 0; | ||||
| } | ||||
| @@ -548,6 +554,7 @@ static void kdeletew(void) { | ||||
|     refresh(); | ||||
| } | ||||
|  | ||||
|  | ||||
| /* See tools/symchargen.c */ | ||||
| static int is_symbol_char_gen(uint8_t c) { | ||||
|     if (c & 0x80) return 1; | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| (var num-tests-passed 0) | ||||
| (var num-tests-run 0) | ||||
| (var suite-name 0) | ||||
| (var suite-num 0) | ||||
| (var start-time 0) | ||||
|  | ||||
| (def is-verbose (os/getenv "VERBOSE")) | ||||
| @@ -14,12 +14,9 @@ | ||||
|   (++ num-tests-run) | ||||
|   (when x (++ num-tests-passed)) | ||||
|   (def str (string e)) | ||||
|   (def frame (last (debug/stack (fiber/current)))) | ||||
|   (def line-info (string/format "%s:%d" | ||||
|                               (frame :source) (frame :source-line))) | ||||
|   (if x | ||||
|     (when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x)) | ||||
|     (do (eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush))) | ||||
|     (when is-verbose (eprintf "\e[32m✔\e[0m %s: %v" (describe e) x)) | ||||
|     (eprintf "\e[31m✘\e[0m %s: %v" (describe e) x)) | ||||
|   x) | ||||
|  | ||||
| (defmacro assert-error | ||||
| @@ -34,23 +31,16 @@ | ||||
|  | ||||
| (defmacro assert-no-error | ||||
|   [msg & forms] | ||||
|   (def e (gensym)) | ||||
|   (def f (gensym)) | ||||
|   (if is-verbose | ||||
|   ~(try (do ,;forms (,assert true ,msg)) ([,e ,f] (,assert false ,msg) (,debug/stacktrace ,f ,e "\e[31m✘\e[0m "))) | ||||
|   ~(try (do ,;forms (,assert true ,msg)) ([_] (,assert false ,msg))))) | ||||
|   (def errsym (keyword (gensym))) | ||||
|   ~(assert (not= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg)) | ||||
|  | ||||
| (defn start-suite [&opt x] | ||||
|   (default x (dyn :current-file)) | ||||
|   (set suite-name | ||||
|        (cond | ||||
|          (number? x) (string x) | ||||
|          (string x))) | ||||
| (defn start-suite [x] | ||||
|   (set suite-num x) | ||||
|   (set start-time (os/clock)) | ||||
|   (eprint "Starting suite " suite-name "...")) | ||||
|   (eprint "Starting suite " x "...")) | ||||
|  | ||||
| (defn end-suite [] | ||||
|   (def delta (- (os/clock) start-time)) | ||||
|   (eprinf "Finished suite %s in %.3f seconds - " suite-name delta) | ||||
|   (eprinf "Finished suite %d in %.3f seconds - " suite-num delta) | ||||
|   (eprint num-tests-passed " of " num-tests-run " tests passed.") | ||||
|   (if (not= num-tests-passed num-tests-run) (os/exit 1))) | ||||
|   | ||||
| @@ -1,81 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Array tests | ||||
| # e05022f | ||||
| (defn array= | ||||
|   "Check if two arrays are equal in an element by element comparison" | ||||
|   [a b] | ||||
|   (if (and (array? a) (array? b)) | ||||
|     (= (apply tuple a) (apply tuple b)))) | ||||
| (assert (= (apply tuple @[1 2 3 4 5]) (tuple 1 2 3 4 5)) "array to tuple") | ||||
| (def arr (array)) | ||||
| (array/push arr :hello) | ||||
| (array/push arr :world) | ||||
| (assert (array= arr @[:hello :world]) "array comparison") | ||||
| (assert (array= @[1 2 3 4 5] @[1 2 3 4 5]) "array comparison 2") | ||||
| (assert (array= @[:one :two :three :four :five] | ||||
|                 @[:one :two :three :four :five]) "array comparison 3") | ||||
| (assert (array= (array/slice @[1 2 3] 0 2) @[1 2]) "array/slice 1") | ||||
| (assert (array= (array/slice @[0 7 3 9 1 4] 2 -2) @[3 9 1]) "array/slice 2") | ||||
|  | ||||
| # Array remove | ||||
| # 687a3c9 | ||||
| (assert (deep= (array/remove @[1 2 3 4 5] 2) @[1 2 4 5]) "array/remove 1") | ||||
| (assert (deep= (array/remove @[1 2 3 4 5] 2 2) @[1 2 5]) "array/remove 2") | ||||
| (assert (deep= (array/remove @[1 2 3 4 5] 2 200) @[1 2]) "array/remove 3") | ||||
| (assert (deep= (array/remove @[1 2 3 4 5] -2 200) @[1 2 3]) "array/remove 4") | ||||
|  | ||||
|  | ||||
| # array/peek | ||||
| (assert (nil? (array/peek @[])) "array/peek empty") | ||||
|  | ||||
| # array/fill | ||||
| (assert (deep= (array/fill @[1 1] 2) @[2 2]) "array/fill 1") | ||||
|  | ||||
| # array/concat | ||||
| (assert (deep= (array/concat @[1 2] @[3 4] 5 6) @[1 2 3 4 5 6]) "array/concat 1") | ||||
| (def a @[1 2]) | ||||
| (assert (deep= (array/concat a a) @[1 2 1 2]) "array/concat self") | ||||
|  | ||||
| # array/insert | ||||
| (assert (deep= (array/insert @[:a :a :a :a] 2 :b :b) @[:a :a :b :b :a :a]) "array/insert 1") | ||||
| (assert (deep= (array/insert @[:a :b] -1 :c :d) @[:a :b :c :d]) "array/insert 2") | ||||
|  | ||||
| # array/remove | ||||
| (assert-error "removal index 3 out of range [0,2]" (array/remove @[1 2] 3)) | ||||
| (assert-error "expected non-negative integer for argument n, got -1" (array/remove @[1 2] 1 -1)) | ||||
|  | ||||
| # array/pop | ||||
| (assert (= (array/pop @[1]) 1) "array/pop 1") | ||||
| (assert (= (array/pop @[]) nil) "array/pop empty") | ||||
|  | ||||
| # Code coverage | ||||
| (def a @[1]) | ||||
| (array/pop a) | ||||
| (array/trim a) | ||||
| (array/ensure @[1 1] 6 2) | ||||
|  | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,55 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Assembly test | ||||
| # Fibonacci sequence, implemented with naive recursion. | ||||
| # a679f60 | ||||
| (def fibasm (asm '{ | ||||
|   :arity 1 | ||||
|   :bytecode [ | ||||
|     (ltim 1 0 0x2)      # $1 = $0 < 2 | ||||
|     (jmpif 1 :done)     # if ($1) goto :done | ||||
|     (lds 1)             # $1 = self | ||||
|     (addim 0 0 -0x1)    # $0 = $0 - 1 | ||||
|     (push 0)            # push($0), push argument for next function call | ||||
|     (call 2 1)          # $2 = call($1) | ||||
|     (addim 0 0 -0x1)    # $0 = $0 - 1 | ||||
|     (push 0)            # push($0) | ||||
|     (call 0 1)          # $0 = call($1) | ||||
|     (add 0 0 2)        # $0 = $0 + $2 (integers) | ||||
|     :done | ||||
|     (ret 0)             # return $0 | ||||
|   ] | ||||
| })) | ||||
|  | ||||
| (assert (= 0 (fibasm 0)) "fibasm 1") | ||||
| (assert (= 1 (fibasm 1)) "fibasm 2") | ||||
| (assert (= 55 (fibasm 10)) "fibasm 3") | ||||
| (assert (= 6765 (fibasm 20)) "fibasm 4") | ||||
|  | ||||
| # dacbe29 | ||||
| (def f (asm (disasm (fn [x] (fn [y] (+ x y)))))) | ||||
| (assert (= ((f 10) 37) 47) "asm environment tables") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,954 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Let | ||||
| # 807f981 | ||||
| (assert (= (let [a 1 b 2] (+ a b)) 3) "simple let") | ||||
| (assert (= (let [[a b] @[1 2]] (+ a b)) 3) "destructured let") | ||||
| (assert (= (let [[a [c d] b] @[1 (tuple 4 3) 2]] (+ a b c d)) 10) | ||||
|         "double destructured let") | ||||
|  | ||||
| # Macros | ||||
| # b305a7c | ||||
| (defn dub [x] (+ x x)) | ||||
| (assert (= 2 (dub 1)) "defn macro") | ||||
| (do | ||||
|   (defn trip [x] (+ x x x)) | ||||
|   (assert (= 3 (trip 1)) "defn macro triple")) | ||||
| (do | ||||
|   (var i 0) | ||||
|   (when true | ||||
|     (++ i) | ||||
|     (++ i) | ||||
|     (++ i) | ||||
|     (++ i) | ||||
|     (++ i) | ||||
|     (++ i)) | ||||
|   (assert (= i 6) "when macro")) | ||||
|  | ||||
| # Add truthy? to core | ||||
| # ded08b6 | ||||
| (assert (= true ;(map truthy? [0 "" true @{} {} [] '()])) "truthy values") | ||||
| (assert (= false ;(map truthy? [nil false])) "non-truthy values") | ||||
|  | ||||
| ## Polymorphic comparison -- Issue #272 | ||||
| # 81d301a42 | ||||
|  | ||||
| # confirm polymorphic comparison delegation to primitive comparators: | ||||
| (assert (= 0 (cmp 3 3)) "compare-primitive integers (1)") | ||||
| (assert (= -1 (cmp 3 5)) "compare-primitive integers (2)") | ||||
| (assert (= 1 (cmp "foo" "bar")) "compare-primitive strings") | ||||
| (assert (= 0 (compare 1 1)) "compare integers (1)") | ||||
| (assert (= -1 (compare 1 2)) "compare integers (2)") | ||||
| (assert (= 1 (compare "foo" "bar")) "compare strings (1)") | ||||
|  | ||||
| (assert (compare< 1 2 3 4 5 6) "compare less than integers") | ||||
| (assert (not (compare> 1 2 3 4 5 6)) "compare not greater than integers") | ||||
| (assert (compare< 1.0 2.0 3.0 4.0 5.0 6.0) "compare less than reals") | ||||
| (assert (compare> 6 5 4 3 2 1) "compare greater than integers") | ||||
| (assert (compare> 6.0 5.0 4.0 3.0 2.0 1.0) "compare greater than reals") | ||||
| (assert (not (compare< 6.0 5.0 4.0 3.0 2.0 1.0)) "compare less than reals") | ||||
| (assert (compare<= 1 2 3 3 4 5 6) "compare less than or equal to integers") | ||||
| (assert (compare<= 1.0 2.0 3.0 3.0 4.0 5.0 6.0) | ||||
|         "compare less than or equal to reals") | ||||
| (assert (compare>= 6 5 4 4 3 2 1) | ||||
|         "compare greater than or equal to integers") | ||||
| (assert (compare>= 6.0 5.0 4.0 4.0 3.0 2.0 1.0) | ||||
|         "compare greater than or equal to reals") | ||||
| (assert (compare< 1.0 nil false true | ||||
|            (fiber/new (fn [] 1)) | ||||
|            "hi" | ||||
|            (quote hello) | ||||
|            :hello | ||||
|            (array 1 2 3) | ||||
|            (tuple 1 2 3) | ||||
|            (table "a" "b" "c" "d") | ||||
|            (struct 1 2 3 4) | ||||
|            (buffer "hi") | ||||
|            (fn [x] (+ x x)) | ||||
|            print) "compare type ordering") | ||||
|  | ||||
| # test polymorphic compare with 'objects' (table/setproto) | ||||
| (def mynum | ||||
|   @{:type :mynum :v 0 :compare | ||||
|     (fn [self other] | ||||
|       (case (type other) | ||||
|       :number (cmp (self :v) other) | ||||
|       :table (when (= (get other :type) :mynum) | ||||
|                (cmp (self :v) (other :v)))))}) | ||||
|  | ||||
| (let [n3 (table/setproto @{:v 3} mynum)] | ||||
|   (assert (= 0 (compare 3 n3)) "compare num to object (1)") | ||||
|   (assert (= -1 (compare n3 4)) "compare object to num (2)") | ||||
|   (assert (= 1 (compare (table/setproto @{:v 4} mynum) n3)) | ||||
|           "compare object to object") | ||||
|   (assert (compare< 2 n3 4) "compare< poly") | ||||
|   (assert (compare> 4 n3 2) "compare> poly") | ||||
|   (assert (compare<= 2 3 n3 4) "compare<= poly") | ||||
|   (assert (compare= 3 n3 (table/setproto @{:v 3} mynum)) "compare= poly") | ||||
|   (assert (deep= (sorted @[4 5 n3 2] compare<) @[2 n3 4 5]) | ||||
|           "polymorphic sort")) | ||||
|  | ||||
| # Add any? predicate to core | ||||
| # 7478ad11 | ||||
| (assert (= nil (any? [])) "any? 1") | ||||
| (assert (= nil (any? [false nil])) "any? 2") | ||||
| (assert (= false (any? [nil false])) "any? 3") | ||||
| (assert (= 1 (any? [1])) "any? 4") | ||||
| (assert (nan? (any? [nil math/nan nil])) "any? 5") | ||||
| (assert (= true | ||||
|            (any? [nil nil false nil nil true nil nil nil nil false :a nil])) | ||||
|         "any? 6") | ||||
|  | ||||
| (assert (= true (every? [])) "every? 1") | ||||
| (assert (= true (every? [1 true])) "every? 2") | ||||
| (assert (= 1 (every? [true 1])) "every? 3") | ||||
| (assert (= nil (every? [nil])) "every? 4") | ||||
| (assert (= 2 (every? [1 math/nan 2])) "every? 5") | ||||
| (assert (= false | ||||
|            (every? [1 1 true 1 1 false 1 1 1 1 true :a nil])) | ||||
|         "every? 6") | ||||
|  | ||||
| # Some higher order functions and macros | ||||
| # 5e2de33 | ||||
| (def my-array @[1 2 3 4 5 6]) | ||||
| (assert (= (if-let [x (get my-array 5)] x) 6) "if-let 1") | ||||
| (assert (= (if-let [y (get @{} :key)] 10 nil) nil) "if-let 2") | ||||
| (assert (= (if-let [a my-array k (next a)] :t :f) :t) "if-let 3") | ||||
| (assert (= (if-let [a my-array k (next a 5)] :t :f) :f) "if-let 4") | ||||
| (assert (= (if-let [[a b] my-array] a) 1) "if-let 5") | ||||
| (assert (= (if-let [{:a a :b b} {:a 1 :b 2}] b) 2) "if-let 6") | ||||
| (assert (= (if-let [[a b] nil] :t :f) :f) "if-let 7") | ||||
|  | ||||
| # #1191 | ||||
| (var cnt 0) | ||||
| (defmacro upcnt [] (++ cnt)) | ||||
| (assert (= (if-let [a true b true c true] nil (upcnt)) nil) "issue #1191") | ||||
| (assert (= cnt 1) "issue #1191") | ||||
|  | ||||
| (assert (= 14 (sum (map inc @[1 2 3 4]))) "sum map") | ||||
| (def myfun (juxt + - * /)) | ||||
| (assert (= [2 -2 2 0.5] (myfun 2)) "juxt") | ||||
|  | ||||
| # Case statements | ||||
| # 5249228 | ||||
| (assert | ||||
|   (= :six (case (+ 1 2 3) | ||||
|             1 :one | ||||
|             2 :two | ||||
|             3 :three | ||||
|             4 :four | ||||
|             5 :five | ||||
|             6 :six | ||||
|             7 :seven | ||||
|             8 :eight | ||||
|             9 :nine)) "case macro") | ||||
|  | ||||
| (assert (= 7 (case :a :b 5 :c 6 :u 10 7)) "case with default") | ||||
|  | ||||
| # Testing the seq, tabseq, catseq, and loop macros | ||||
| # 547529e | ||||
| (def xs (apply tuple (seq [x :range [0 10] :when (even? x)] | ||||
|                        (tuple (/ x 2) x)))) | ||||
| (assert (= xs '((0 0) (1 2) (2 4) (3 6) (4 8))) "seq macro 1") | ||||
|  | ||||
| # 624be87c9 | ||||
| (def xs (apply tuple (seq [x :down [8 -2] :when (even? x)] | ||||
|                        (tuple (/ x 2) x)))) | ||||
| (assert (= xs '((4 8) (3 6) (2 4) (1 2) (0 0))) "seq macro 2") | ||||
|  | ||||
| # Looping idea | ||||
| # 45f8db0 | ||||
| (def xs | ||||
|   (seq [x :in [-1 0 1] y :in [-1 0 1] :when (not= x y 0)] (tuple x y))) | ||||
| (def txs (apply tuple xs)) | ||||
|  | ||||
| (assert (= txs [[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]]) | ||||
|         "nested seq") | ||||
|  | ||||
| # :unless modifier | ||||
| (assert (deep= (seq [i :range [0 10] :unless (odd? i)] i) | ||||
|                @[0 2 4 6 8]) | ||||
|         ":unless modifier") | ||||
|  | ||||
| # 515891b03 | ||||
| (assert (deep= (tabseq [i :in (range 3)] i (* 3 i)) | ||||
|                @{0 0 1 3 2 6})) | ||||
|  | ||||
| (assert (deep= (tabseq [i :in (range 3)] i) | ||||
|                @{})) | ||||
|  | ||||
| # ccd874fe4 | ||||
| (def xs (catseq [x :range [0 3]] [x x])) | ||||
| (assert (deep= xs @[0 0 1 1 2 2]) "catseq") | ||||
|  | ||||
| # :range-to and :down-to | ||||
| # e0c9910d8 | ||||
| (assert (deep= (seq [x :range-to [0 10]] x) (seq [x :range [0 11]] x)) | ||||
|         "loop :range-to") | ||||
| (assert (deep= (seq [x :down-to [10 0]] x) (seq [x :down [10 -1]] x)) | ||||
|         "loop :down-to") | ||||
|  | ||||
| # one-term :range forms | ||||
| (assert (deep= (seq [x :range [10]] x) (seq [x :range [0 10]] x)) | ||||
|         "one-term :range") | ||||
| (assert (deep= (seq [x :down [10]] x) (seq [x :down [10 0]] x)) | ||||
|         "one-term :down") | ||||
|  | ||||
| # 7880d7320 | ||||
| (def res @{}) | ||||
| (loop [[k v] :pairs @{1 2 3 4 5 6}] | ||||
|   (put res k v)) | ||||
| (assert (and | ||||
|           (= (get res 1) 2) | ||||
|           (= (get res 3) 4) | ||||
|           (= (get res 5) 6)) "loop :pairs") | ||||
|  | ||||
| # Issue #428 | ||||
| # 08a3687eb | ||||
| (var result nil) | ||||
| (defn f [] (yield {:a :ok})) | ||||
| (assert-no-error "issue 428 1" | ||||
|                  (loop [{:a x} :in (fiber/new f)] (set result x))) | ||||
| (assert (= result :ok) "issue 428 2") | ||||
|  | ||||
| # Generators | ||||
| # 184fe31e0 | ||||
| (def gen (generate [x :range [0 100] :when (pos? (% x 4))] x)) | ||||
| (var gencount 0) | ||||
| (loop [x :in gen] | ||||
|   (++ gencount) | ||||
|   (assert (pos? (% x 4)) "generate in loop")) | ||||
| (assert (= gencount 75) "generate loop count") | ||||
|  | ||||
| # Even and odd | ||||
| # ff163a5ae | ||||
| (assert (odd? 9) "odd? 1") | ||||
| (assert (odd? -9) "odd? 2") | ||||
| (assert (not (odd? 10)) "odd? 3") | ||||
| (assert (not (odd? 0)) "odd? 4") | ||||
| (assert (not (odd? -10)) "odd? 5") | ||||
| (assert (not (odd? 1.1)) "odd? 6") | ||||
| (assert (not (odd? -0.1)) "odd? 7") | ||||
| (assert (not (odd? -1.1)) "odd? 8") | ||||
| (assert (not (odd? -1.6)) "odd? 9") | ||||
|  | ||||
| (assert (even? 10) "even? 1") | ||||
| (assert (even? -10) "even? 2") | ||||
| (assert (even? 0) "even? 3") | ||||
| (assert (not (even? 9)) "even? 4") | ||||
| (assert (not (even? -9)) "even? 5") | ||||
| (assert (not (even? 0.1)) "even? 6") | ||||
| (assert (not (even? -0.1)) "even? 7") | ||||
| (assert (not (even? -10.1)) "even? 8") | ||||
| (assert (not (even? -10.6)) "even? 9") | ||||
|  | ||||
| # Map arities | ||||
| # 25ded775a | ||||
| (assert (deep= (map inc [1 2 3]) @[2 3 4])) | ||||
| (assert (deep= (map + [1 2 3] [10 20 30]) @[11 22 33])) | ||||
| (assert (deep= (map + [1 2 3] [10 20 30] [100 200 300]) @[111 222 333])) | ||||
| (assert (deep= (map + [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000]) | ||||
|                @[1111 2222 3333])) | ||||
| (assert (deep= (map + | ||||
|                     [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000] | ||||
|                     [10000 20000 30000]) | ||||
|                @[11111 22222 33333])) | ||||
| # 77e62a2 | ||||
| (assert (deep= (map + | ||||
|                     [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000] | ||||
|                     [10000 20000 30000] [100000 200000 300000]) | ||||
|                @[111111 222222 333333])) | ||||
|  | ||||
| # Mapping uses the shortest sequence | ||||
| # a69799aa4 | ||||
| (assert (deep= (map + [1 2 3 4] [10 20 30]) @[11 22 33])) | ||||
| (assert (deep= (map + [1 2 3 4] [10 20 30] [100 200]) @[111 222])) | ||||
| (assert (deep= (map + [1 2 3 4] [10 20 30] [100 200] [1000]) @[1111])) | ||||
| # 77e62a2 | ||||
| (assert (deep= (map + [1 2 3 4] [10 20 30] [100 200] [1000] []) @[])) | ||||
|  | ||||
| # Variadic arguments to map-like functions | ||||
| # 77e62a2 | ||||
| (assert (deep= (mapcat tuple [1 2 3 4] [5 6 7 8]) @[1 5 2 6 3 7 4 8])) | ||||
| (assert (deep= (keep |(if (> $1 0) (/ $0 $1)) [1 2 3 4 5] [1 2 1 0 1]) | ||||
|                @[1 1 3 5])) | ||||
|  | ||||
| (assert (= (count = [1 3 2 4 3 5 4 2 1] [1 2 3 4 5 4 3 2 1]) 4)) | ||||
|  | ||||
| (assert (= (some not= (range 5) (range 5)) nil)) | ||||
| (assert (= (some = [1 2 3 4 5] [5 4 3 2 1]) true)) | ||||
|  | ||||
| (assert (= (all = (range 5) (range 5)) true)) | ||||
| (assert (= (all not= [1 2 3 4 5] [5 4 3 2 1]) false)) | ||||
|  | ||||
| # 4194374 | ||||
| (assert (= false (deep-not= [1] [1])) "issue #1149") | ||||
|  | ||||
| # Merge sort | ||||
| # f5b29b8 | ||||
| # Imperative (and verbose) merge sort merge | ||||
| (defn merge-sort | ||||
|   [xs ys] | ||||
|   (def ret @[]) | ||||
|   (def xlen (length xs)) | ||||
|   (def ylen (length ys)) | ||||
|   (var i 0) | ||||
|   (var j 0) | ||||
|   # Main merge | ||||
|   (while (if (< i xlen) (< j ylen)) | ||||
|     (def xi (get xs i)) | ||||
|     (def yj (get ys j)) | ||||
|     (if (< xi yj) | ||||
|       (do (array/push ret xi) (set i (+ i 1))) | ||||
|       (do (array/push ret yj) (set j (+ j 1))))) | ||||
|   # Push rest of xs | ||||
|   (while (< i xlen) | ||||
|     (def xi (get xs i)) | ||||
|     (array/push ret xi) | ||||
|     (set i (+ i 1))) | ||||
|   # Push rest of ys | ||||
|   (while (< j ylen) | ||||
|     (def yj (get ys j)) | ||||
|     (array/push ret yj) | ||||
|     (set j (+ j 1))) | ||||
|   ret) | ||||
|  | ||||
| (assert (apply <= (merge-sort @[1 3 5] @[2 4 6])) "merge sort merge 1") | ||||
| (assert (apply <= (merge-sort @[1 2 3] @[4 5 6])) "merge sort merge 2") | ||||
| (assert (apply <= (merge-sort @[1 3 5] @[2 4 6 6 6 9])) "merge sort merge 3") | ||||
| (assert (apply <= (merge-sort '(1 3 5) @[2 4 6 6 6 9])) "merge sort merge 4") | ||||
|  | ||||
| (assert (deep= @[1 2 3 4 5] (sort @[5 3 4 1 2])) "sort 1") | ||||
| (assert (deep= @[{:a 1} {:a 4} {:a 7}] | ||||
|                (sort-by |($ :a) @[{:a 4} {:a 7} {:a 1}])) "sort 2") | ||||
| (assert (deep= @[1 2 3 4 5] (sorted [5 3 4 1 2])) "sort 3") | ||||
| (assert (deep= @[{:a 1} {:a 4} {:a 7}] | ||||
|                (sorted-by |($ :a) [{:a 4} {:a 7} {:a 1}])) "sort 4") | ||||
|  | ||||
| # Sort function | ||||
| # 2ca9300bf | ||||
| (assert (deep= | ||||
|           (range 99) | ||||
|           (sort (mapcat (fn [[x y z]] [z y x]) (partition 3 (range 99))))) | ||||
|         "sort 5") | ||||
| (assert (<= ;(sort (map (fn [x] (math/random)) (range 1000)))) "sort 6") | ||||
|  | ||||
| # #1283   | ||||
| (assert (deep= | ||||
|           (partition 2 (generate [ i :in [:a :b :c :d :e]] i)) | ||||
|           '@[(:a :b) (:c :d) (:e)])) | ||||
| (assert (= (mean (generate [i :in [2 3 5 7 11]] i)) | ||||
|            5.6)) | ||||
|  | ||||
| # And and or | ||||
| # c16a9d846 | ||||
| (assert (= (and true true) true) "and true true") | ||||
| (assert (= (and true false) false) "and true false") | ||||
| (assert (= (and false true) false) "and false true") | ||||
| (assert (= (and true true true) true) "and true true true") | ||||
| (assert (= (and 0 1 2) 2) "and 0 1 2") | ||||
| (assert (= (and 0 1 nil) nil) "and 0 1 nil") | ||||
| (assert (= (and 1) 1) "and 1") | ||||
| (assert (= (and) true) "and with no arguments") | ||||
| (assert (= (and 1 true) true) "and with trailing true") | ||||
| (assert (= (and 1 true 2) 2) "and with internal true") | ||||
|  | ||||
| (assert (= (or true true) true) "or true true") | ||||
| (assert (= (or true false) true) "or true false") | ||||
| (assert (= (or false true) true) "or false true") | ||||
| (assert (= (or false false) false) "or false true") | ||||
| (assert (= (or true true false) true) "or true true false") | ||||
| (assert (= (or 0 1 2) 0) "or 0 1 2") | ||||
| (assert (= (or nil 1 2) 1) "or nil 1 2") | ||||
| (assert (= (or 1) 1) "or 1") | ||||
| (assert (= (or) nil) "or with no arguments") | ||||
|  | ||||
| # And/or checks | ||||
| # 6123c41f1 | ||||
| (assert (= false (and false false)) "and 1") | ||||
| (assert (= false (or false false)) "or 1") | ||||
|  | ||||
| # 11cd1279d | ||||
| (assert (deep= @{:a 1 :b 2 :c 3} (zipcoll '[:a :b :c] '[1 2 3])) "zipcoll") | ||||
|  | ||||
| # bc8be266f | ||||
| (def- a 100) | ||||
| (assert (= a 100) "def-") | ||||
|  | ||||
| # bc8be266f | ||||
| (assert (= :first | ||||
|           (match @[1 3 5] | ||||
|                  @[x y z] :first | ||||
|                  :second)) "match 1") | ||||
|  | ||||
| (def val1 :avalue) | ||||
| (assert (= :second | ||||
|           (match val1 | ||||
|                  @[x y z] :first | ||||
|                  :avalue :second | ||||
|                  :third)) "match 2") | ||||
|  | ||||
| (assert (= 100 | ||||
|            (match @[50 40] | ||||
|                   @[x x] (* x 3) | ||||
|                   @[x y] (+ x y 10) | ||||
|                   0)) "match 3") | ||||
|  | ||||
| # Match checks | ||||
| # 47e8f669f | ||||
| (assert (= :hi (match nil nil :hi)) "match 1") | ||||
| (assert (= :hi (match {:a :hi} {:a a} a)) "match 2") | ||||
| (assert (= nil (match {:a :hi} {:a a :b b} a)) "match 3") | ||||
| (assert (= nil (match [1 2] [a b c] a)) "match 4") | ||||
| (assert (= 2 (match [1 2] [a b] b)) "match 5") | ||||
| # db631097b | ||||
| (assert (= [2 :a :b] (match [1 2 :a :b] [o & rest] rest)) "match 6") | ||||
| (assert (= [] (match @[:a] @[x & r] r :fallback)) "match 7") | ||||
| (assert (= :fallback (match @[1] @[x y & r] r :fallback)) "match 8") | ||||
| (assert (= [1 2 3 4] (match @[1 2 3 4] @[x y z & r] [x y z ;r] :fallback)) | ||||
|         "match 9") | ||||
|  | ||||
| # Test cases for #293 | ||||
| # d3b9b8d45 | ||||
| (assert (= :yes (match [1 2 3] [_ a _] :yes :no)) "match wildcard 1") | ||||
| (assert (= :no (match [1 2 3] [__ a __] :yes :no)) "match wildcard 2") | ||||
| (assert (= :yes (match [1 2 [1 2 3]] [_ a [_ _ _]] :yes :no)) | ||||
|         "match wildcard 3") | ||||
| (assert (= :yes (match [1 2 3] (_ (even? 2)) :yes :no)) "match wildcard 4") | ||||
| (assert (= :yes (match {:a 1} {:a _} :yes :no)) "match wildcard 5") | ||||
| (assert (= false (match {:a 1 :b 2 :c 3} | ||||
|                    {:a a :b _ :c _ :d _} :no | ||||
|                    {:a _ :b _ :c _} false | ||||
|                    :no)) "match wildcard 6") | ||||
| (assert (= nil (match {:a 1 :b 2 :c 3} | ||||
|                  {:a a :b _ :c _ :d _} :no | ||||
|                  {:a _ :b _ :c _} nil | ||||
|                  :no)) "match wildcard 7") | ||||
| # issue #529 - 602010600 | ||||
| (assert (= "t" (match [true nil] [true _] "t")) "match wildcard 8") | ||||
|  | ||||
| # quoted match test | ||||
| # 425a0fcf0 | ||||
| (assert (= :yes (match 'john 'john :yes _ :nope)) "quoted literal match 1") | ||||
| (assert (= :nope (match 'john ''john :yes _ :nope)) "quoted literal match 2") | ||||
|  | ||||
| # Some macros | ||||
| # 7880d7320 | ||||
| (assert (= 2 (if-not 1 3 2)) "if-not 1") | ||||
| (assert (= 3 (if-not false 3)) "if-not 2") | ||||
| (assert (= 3 (if-not nil 3 2)) "if-not 3") | ||||
| (assert (= nil (if-not true 3)) "if-not 4") | ||||
|  | ||||
| (assert (= 4 (unless false (+ 1 2 3) 4)) "unless") | ||||
|  | ||||
| # take | ||||
| # 18da183ef | ||||
| (assert (deep= (take 0 []) []) "take 1") | ||||
| (assert (deep= (take 10 []) []) "take 2") | ||||
| (assert (deep= (take 0 [1 2 3 4 5]) []) "take 3") | ||||
| (assert (deep= (take 10 [1 2 3]) [1 2 3]) "take 4") | ||||
| (assert (deep= (take -1 [:a :b :c]) [:c]) "take 5") | ||||
| # 34019222c | ||||
| (assert (deep= (take 3 (generate [x :in [1 2 3 4 5]] x)) @[1 2 3]) | ||||
|         "take from fiber") | ||||
| # NB: repeatedly resuming a fiber created with `generate` includes a `nil` | ||||
| # as the final element. Thus a generate of 2 elements will create an array | ||||
| # of 3. | ||||
| (assert (= (length (take 4 (generate [x :in [1 2]] x))) 2) | ||||
|         "take from short fiber") | ||||
|  | ||||
| # take-until | ||||
| # 18da183ef | ||||
| (assert (deep= (take-until pos? @[]) []) "take-until 1") | ||||
| (assert (deep= (take-until pos? @[1 2 3]) []) "take-until 2") | ||||
| (assert (deep= (take-until pos? @[-1 -2 -3]) [-1 -2 -3]) "take-until 3") | ||||
| (assert (deep= (take-until pos? @[-1 -2 3]) [-1 -2]) "take-until 4") | ||||
| (assert (deep= (take-until pos? @[-1 1 -2]) [-1]) "take-until 5") | ||||
| (assert (deep= (take-until |(= $ 115) "books") "book") "take-until 6") | ||||
| (assert (deep= (take-until |(= $ 115) (generate [x :in "books"] x)) | ||||
|                @[98 111 111 107]) "take-until from fiber") | ||||
|  | ||||
| # take-while | ||||
| # 18da183ef | ||||
| (assert (deep= (take-while neg? @[]) []) "take-while 1") | ||||
| (assert (deep= (take-while neg? @[1 2 3]) []) "take-while 2") | ||||
| (assert (deep= (take-while neg? @[-1 -2 -3]) [-1 -2 -3]) "take-while 3") | ||||
| (assert (deep= (take-while neg? @[-1 -2 3]) [-1 -2]) "take-while 4") | ||||
| (assert (deep= (take-while neg? @[-1 1 -2]) [-1]) "take-while 5") | ||||
| (assert (deep= (take-while neg? (generate [x :in  @[-1 1 -2]] x)) | ||||
|                @[-1]) "take-while from fiber") | ||||
|  | ||||
| # drop | ||||
| # 18da183ef | ||||
| (assert (deep= (drop 0 []) []) "drop 1") | ||||
| (assert (deep= (drop 10 []) []) "drop 2") | ||||
| (assert (deep= (drop 0 [1 2 3 4 5]) [1 2 3 4 5]) "drop 3") | ||||
| (assert (deep= (drop 10 [1 2 3]) []) "drop 4") | ||||
| (assert (deep= (drop -1 [1 2 3]) [1 2]) "drop 5") | ||||
| (assert (deep= (drop -10 [1 2 3]) []) "drop 6") | ||||
| (assert (deep= (drop 1 "abc") "bc") "drop 7") | ||||
| (assert (deep= (drop 10 "abc") "") "drop 8") | ||||
| (assert (deep= (drop -1 "abc") "ab") "drop 9") | ||||
| (assert (deep= (drop -10 "abc") "") "drop 10") | ||||
|  | ||||
| # drop-until | ||||
| # 75dc08f | ||||
| (assert (deep= (drop-until pos? @[]) []) "drop-until 1") | ||||
| (assert (deep= (drop-until pos? @[1 2 3]) [1 2 3]) "drop-until 2") | ||||
| (assert (deep= (drop-until pos? @[-1 -2 -3]) []) "drop-until 3") | ||||
| (assert (deep= (drop-until pos? @[-1 -2 3]) [3]) "drop-until 4") | ||||
| (assert (deep= (drop-until pos? @[-1 1 -2]) [1 -2]) "drop-until 5") | ||||
| (assert (deep= (drop-until |(= $ 115) "books") "s") "drop-until 6") | ||||
|  | ||||
| # take-drop symmetry #1178 | ||||
| (def items-list ['abcde :abcde "abcde" @"abcde" [1 2 3 4 5] @[1 2 3 4 5]]) | ||||
|  | ||||
| (each items items-list | ||||
|   (def len (length items)) | ||||
|   (for i 0 (+ len 1) | ||||
|     (assert (deep= (take i items) (drop (- i len) items)) (string/format "take-drop symmetry %q %d" items i)) | ||||
|     (assert (deep= (take (- i) items) (drop (- len i) items)) (string/format "take-drop symmetry %q %d" items i)))) | ||||
|  | ||||
| (defn squares [] | ||||
|   (coro | ||||
|     (var [a b] [0 1]) | ||||
|     (forever (yield a) (+= a b) (+= b 2)))) | ||||
|  | ||||
| (def sqr1 (squares)) | ||||
| (assert (deep= (take 10 sqr1) @[0 1 4 9 16 25 36 49 64 81])) | ||||
| (assert (deep= (take 1 sqr1) @[100]) "take fiber next value") | ||||
|  | ||||
| (def sqr2 (drop 10 (squares))) | ||||
| (assert (deep= (take 1 sqr2) @[100]) "drop fiber next value") | ||||
|  | ||||
| (def dict @{:a 1 :b 2 :c 3 :d 4 :e 5}) | ||||
| (def dict1 (take 2 dict)) | ||||
| (def dict2 (drop 2 dict)) | ||||
|  | ||||
| (assert (= (length dict1) 2) "take dictionary") | ||||
| (assert (= (length dict2) 3) "drop dictionary") | ||||
| (assert (deep= (merge dict1 dict2) dict) "take-drop symmetry for dictionary") | ||||
|  | ||||
| # Comment macro | ||||
| # issue #110 - 698e89aba | ||||
| (comment 1) | ||||
| (comment 1 2) | ||||
| (comment 1 2 3) | ||||
| (comment 1 2 3 4) | ||||
|  | ||||
| # comp should be variadic | ||||
| # 5c83ebd75, 02ce3031 | ||||
| (assert (= 10 ((comp +) 1 2 3 4)) "variadic comp 1") | ||||
| (assert (= 11 ((comp inc +) 1 2 3 4)) "variadic comp 2") | ||||
| (assert (= 12 ((comp inc inc +) 1 2 3 4)) "variadic comp 3") | ||||
| (assert (= 13 ((comp inc inc inc +) 1 2 3 4)) "variadic comp 4") | ||||
| (assert (= 14 ((comp inc inc inc inc +) 1 2 3 4)) "variadic comp 5") | ||||
| (assert (= 15 ((comp inc inc inc inc inc +) 1 2 3 4)) "variadic comp 6") | ||||
| (assert (= 16 ((comp inc inc inc inc inc inc +) 1 2 3 4)) | ||||
|         "variadic comp 7") | ||||
|  | ||||
| # Function shorthand | ||||
| # 44e752d73 | ||||
| (assert (= (|(+ 1 2 3)) 6) "function shorthand 1") | ||||
| (assert (= (|(+ 1 2 3 $) 4) 10) "function shorthand 2") | ||||
| (assert (= (|(+ 1 2 3 $0) 4) 10) "function shorthand 3") | ||||
| (assert (= (|(+ $0 $0 $0 $0) 4) 16) "function shorthand 4") | ||||
| (assert (= (|(+ $ $ $ $) 4) 16) "function shorthand 5") | ||||
| (assert (= (|4) 4) "function shorthand 6") | ||||
| (assert (= (((|||4))) 4) "function shorthand 7") | ||||
| (assert (= (|(+ $1 $1 $1 $1) 2 4) 16) "function shorthand 8") | ||||
| (assert (= (|(+ $0 $1 $3 $2 $6) 0 1 2 3 4 5 6) 12) "function shorthand 9") | ||||
| # 5f5147652 | ||||
| (assert (= (|(+ $0 $99) ;(range 100)) 99) "function shorthand 10") | ||||
|  | ||||
| # 655d4b3aa | ||||
| (defn idx= [x y] (= (tuple/slice x) (tuple/slice y))) | ||||
|  | ||||
| # Simple take, drop, etc. tests. | ||||
| (assert (idx= (take 10 (range 100)) (range 10)) "take 10") | ||||
| (assert (idx= (drop 10 (range 100)) (range 10 100)) "drop 10") | ||||
|  | ||||
| # with-vars | ||||
| # 6ceaf9d28 | ||||
| (var abc 123) | ||||
| (assert (= 356 (with-vars [abc 456] (- abc 100))) "with-vars 1") | ||||
| (assert-error "with-vars 2" (with-vars [abc 456] (error :oops))) | ||||
| (assert (= abc 123) "with-vars 3") | ||||
|  | ||||
| # Top level unquote | ||||
| # 2487162cc | ||||
| (defn constantly | ||||
|   [] | ||||
|   (comptime (math/random))) | ||||
|  | ||||
| (assert (= (constantly) (constantly)) "comptime 1") | ||||
|  | ||||
| # issue #232 - b872ee024 | ||||
| (assert-error "arity issue in macro" (eval '(each []))) | ||||
| # c6b639b93 | ||||
| (assert-error "comptime issue" (eval '(comptime (error "oops")))) | ||||
|  | ||||
| # 962cd7e5f | ||||
| (var counter 0) | ||||
| (when-with [x nil |$] | ||||
|            (++ counter)) | ||||
| (when-with [x 10 |$] | ||||
|            (+= counter 10)) | ||||
|  | ||||
| (assert (= 10 counter) "when-with 1") | ||||
|  | ||||
| (if-with [x nil |$] (++ counter) (+= counter 10)) | ||||
| (if-with [x true |$] (+= counter 20) (+= counter 30)) | ||||
|  | ||||
| (assert (= 40 counter) "if-with 1") | ||||
|  | ||||
| # a45509d28 | ||||
| (def a @[]) | ||||
| (eachk x [:a :b :c :d] | ||||
|   (array/push a x)) | ||||
| (assert (deep= (range 4) a) "eachk 1") | ||||
|  | ||||
| # issue 609 - 1fcaffe | ||||
| (with-dyns [:err @""] | ||||
|   (tracev (def my-unique-var-name true)) | ||||
|   (assert my-unique-var-name "tracev upscopes")) | ||||
|  | ||||
| # Prompts and Labels | ||||
| # 59d288c | ||||
| (assert (= 10 (label a (for i 0 10 (if (= i 5) (return a 10))))) "label 1") | ||||
|  | ||||
| (defn recur | ||||
|   [lab x y] | ||||
|   (when (= x y) (return lab :done)) | ||||
|   (def res (label newlab (recur (or lab newlab) (+ x 1) y))) | ||||
|   (if lab :oops res)) | ||||
| (assert (= :done (recur nil 0 10)) "label 2") | ||||
|  | ||||
| (assert (= 10 (prompt :a (for i 0 10 (if (= i 5) (return :a 10))))) | ||||
|         "prompt 1") | ||||
|  | ||||
| (defn- inner-loop | ||||
|   [i] | ||||
|   (if (= i 5) | ||||
|     (return :a 10))) | ||||
|  | ||||
| (assert (= 10 (prompt :a (for i 0 10 (inner-loop i)))) "prompt 2") | ||||
|  | ||||
| (defn- inner-loop2 | ||||
|   [i] | ||||
|   (try | ||||
|     (if (= i 5) | ||||
|       (error 10)) | ||||
|     ([err] (return :a err)))) | ||||
|  | ||||
| (assert (= 10 (prompt :a (for i 0 10 (inner-loop2 i)))) "prompt 3") | ||||
|  | ||||
| # chr | ||||
| # issue 304 - 77343e02e | ||||
| (assert (= (chr "a") 97) "chr 1") | ||||
|  | ||||
| # Reduce2 | ||||
| # 3eb0927a2 | ||||
| (assert (= (reduce + 0 (range 1 10)) (reduce2 + (range 10))) "reduce2 1") | ||||
| # 65379741f | ||||
| (assert (= (reduce * 1 (range 2 10)) (reduce2 * (range 1 10))) "reduce2 2") | ||||
| (assert (= nil (reduce2 * [])) "reduce2 3") | ||||
|  | ||||
| # Accumulate | ||||
| # 3eb0927a2 | ||||
| (assert (deep= (accumulate + 0 (range 5)) @[0 1 3 6 10]) "accumulate 1") | ||||
| (assert (deep= (accumulate2 + (range 5)) @[0 1 3 6 10]) "accumulate2 1") | ||||
| # 65379741f | ||||
| (assert (deep= @[] (accumulate2 + [])) "accumulate2 2") | ||||
| (assert (deep= @[] (accumulate 0 + [])) "accumulate 2") | ||||
|  | ||||
| # in vs get regression | ||||
| # issue #340 - b63a0796f | ||||
| (assert (nil? (first @"")) "in vs get 1") | ||||
| (assert (nil? (last @"")) "in vs get 1") | ||||
|  | ||||
| # index-of | ||||
| # 259812314 | ||||
| (assert (= nil (index-of 10 [])) "index-of 1") | ||||
| (assert (= nil (index-of 10 [1 2 3])) "index-of 2") | ||||
| (assert (= 1 (index-of 2 [1 2 3])) "index-of 3") | ||||
| (assert (= 0 (index-of :a [:a :b :c])) "index-of 4") | ||||
| (assert (= nil (index-of :a {})) "index-of 5") | ||||
| (assert (= :a (index-of :A {:a :A :b :B})) "index-of 6") | ||||
| (assert (= :a (index-of :A @{:a :A :b :B})) "index-of 7") | ||||
| (assert (= 0 (index-of (chr "a") "abc")) "index-of 8") | ||||
| (assert (= nil (index-of (chr "a") "")) "index-of 9") | ||||
| (assert (= nil (index-of 10 @[])) "index-of 10") | ||||
| (assert (= nil (index-of 10 @[1 2 3])) "index-of 11") | ||||
|  | ||||
| # e78a3d1 | ||||
| # NOTE: These is a motivation for the has-value? and has-key? functions below | ||||
|  | ||||
| # returns false despite key present | ||||
| (assert (= false (index-of 8 {true 7 false 8})) | ||||
|         "index-of corner key (false) 1") | ||||
| (assert (= false (index-of 8 @{false 8})) | ||||
|         "index-of corner key (false) 2") | ||||
| # still returns null | ||||
| (assert (= nil (index-of 7 {false 8})) "index-of corner key (false) 3") | ||||
|  | ||||
| # has-value? | ||||
| (assert (= false (has-value? [] "foo")) "has-value? 1") | ||||
| (assert (= true (has-value? [4 7 1 3] 4)) "has-value? 2") | ||||
| (assert (= false (has-value? [4 7 1 3] 22)) "has-value? 3") | ||||
| (assert (= false (has-value? @[1 2 3] 4)) "has-value? 4") | ||||
| (assert (= true (has-value? @[:a :b :c] :a)) "has-value? 5") | ||||
| (assert (= false (has-value? {} :foo)) "has-value? 6") | ||||
| (assert (= true (has-value? {:a :A :b :B} :A)) "has-value? 7") | ||||
| (assert (= true (has-value? {:a :A :b :B} :A)) "has-value? 7") | ||||
| (assert (= true (has-value? @{:a :A :b :B} :A)) "has-value? 8") | ||||
| (assert (= true (has-value? "abc" (chr "a"))) "has-value? 9") | ||||
| (assert (= false (has-value? "abc" "1")) "has-value? 10") | ||||
| # weird true/false corner cases, should align with "index-of corner | ||||
| # key {k}" cases | ||||
| (assert (= true (has-value? {true 7 false 8} 8)) | ||||
|         "has-value? corner key (false) 1") | ||||
| (assert (= true (has-value? @{false 8} 8)) | ||||
|         "has-value? corner key (false) 2") | ||||
| (assert (= false (has-value? {false 8} 7)) | ||||
|         "has-value? corner key (false) 3") | ||||
|  | ||||
| # has-key? | ||||
| (do | ||||
|   (var test-has-key-auto 0) | ||||
|   (defn test-has-key [col key expected &keys {:name name}] | ||||
|     ``Test that has-key has the outcome `expected`, and that if | ||||
|     the result is true, then ensure (in key) does not fail either`` | ||||
|     (assert (boolean? expected)) | ||||
|     (default name (string "has-key? " (++ test-has-key-auto))) | ||||
|     (assert (= expected (has-key? col key)) name) | ||||
|     (if | ||||
|       # guarenteed by `has-key?` to never fail | ||||
|       expected (in col key) | ||||
|       # if `has-key?` is false, then `in` should fail (for indexed types) | ||||
|       # | ||||
|       # For dictionary types, it should return nil | ||||
|       (let [[success retval] (protect (in col key))] | ||||
|         (def should-succeed (dictionary? col)) | ||||
|         (assert | ||||
|           (= success should-succeed) | ||||
|           (string/format | ||||
|             "%s: expected (in col key) to %s, but got %q" | ||||
|             name (if expected "succeed" "fail") retval))))) | ||||
|  | ||||
|   (test-has-key [] 0 false) # 1 | ||||
|   (test-has-key [4 7 1 3] 2 true) # 2 | ||||
|   (test-has-key [4 7 1 3] 22 false) # 3 | ||||
|   (test-has-key @[1 2 3] 4 false) # 4 | ||||
|   (test-has-key @[:a :b :c] 2 true) # 5 | ||||
|   (test-has-key {} :foo false) # 6 | ||||
|   (test-has-key {:a :A :b :B} :a true) # 7 | ||||
|   (test-has-key {:a :A :b :B} :A false) # 8 | ||||
|   (test-has-key @{:a :A :b :B} :a true) # 9 | ||||
|   (test-has-key "abc" 1 true) # 10 | ||||
|   (test-has-key "abc" 4 false) # 11 | ||||
|   # weird true/false corner cases | ||||
|   # | ||||
|   # Tries to mimic the corresponding corner cases in has-value? and | ||||
|   # index-of, but with keys/values inverted | ||||
|   # | ||||
|   # in the first two cases (truthy? (get val col)) would have given false | ||||
|   # negatives | ||||
|   (test-has-key {7 true 8 false} 8 true :name | ||||
|                 "has-key? corner value (false) 1") | ||||
|   (test-has-key @{8 false} 8 true :name | ||||
|                 "has-key? corner value (false) 2") | ||||
|   (test-has-key @{8 false} 7 false :name | ||||
|                 "has-key? corner value (false) 3")) | ||||
|  | ||||
| # Regression | ||||
| # issue #463 - 7e7498350 | ||||
| (assert (= {:x 10} (|(let [x $] ~{:x ,x}) 10)) "issue 463") | ||||
|  | ||||
| # macex testing | ||||
| # 7e7498350 | ||||
| (assert (deep= (macex1 '~{1 2 3 4}) '~{1 2 3 4}) "macex1 qq struct") | ||||
| (assert (deep= (macex1 '~@{1 2 3 4}) '~@{1 2 3 4}) "macex1 qq table") | ||||
| (assert (deep= (macex1 '~(1 2 3 4)) '~[1 2 3 4]) "macex1 qq tuple") | ||||
| (assert (= :brackets (tuple/type (1 (macex1 '~[1 2 3 4])))) | ||||
|         "macex1 qq bracket tuple") | ||||
| (assert (deep= (macex1 '~@[1 2 3 4 ,blah]) '~@[1 2 3 4 ,blah]) | ||||
|         "macex1 qq array") | ||||
|  | ||||
| # Sourcemaps in threading macros | ||||
| # b6175e429 | ||||
| (defn check-threading [macro expansion] | ||||
|   (def expanded (macex1 (tuple macro 0 '(x) '(y)))) | ||||
|   (assert (= expanded expansion) (string macro " expansion value")) | ||||
|   (def smap-x (tuple/sourcemap (get expanded 1))) | ||||
|   (def smap-y (tuple/sourcemap expanded)) | ||||
|   (def line first) | ||||
|   (defn column [t] (t 1)) | ||||
|   (assert (not= smap-x [-1 -1]) (string macro " x sourcemap existence")) | ||||
|   (assert (not= smap-y [-1 -1]) (string macro " y sourcemap existence")) | ||||
|   (assert (or (< (line smap-x) (line smap-y)) | ||||
|               (and (= (line smap-x) (line smap-y)) | ||||
|                    (< (column smap-x) (column smap-y)))) | ||||
|           (string macro " relation between x and y sourcemap"))) | ||||
|  | ||||
| (check-threading '-> '(y (x 0))) | ||||
| (check-threading '->> '(y (x 0))) | ||||
|  | ||||
| # keep-syntax | ||||
| # b6175e429 | ||||
| (let [brak '[1 2 3] | ||||
|       par '(1 2 3)] | ||||
|  | ||||
|   (tuple/setmap brak 2 1) | ||||
|  | ||||
|   (assert (deep= (keep-syntax brak @[1 2 3]) @[1 2 3]) | ||||
|           "keep-syntax brackets ignore array") | ||||
|   (assert (= (keep-syntax! brak @[1 2 3]) '[1 2 3]) | ||||
|           "keep-syntax! brackets replace array") | ||||
|  | ||||
|   (assert (= (keep-syntax! par (map inc @[1 2 3])) '(2 3 4)) | ||||
|           "keep-syntax! parens coerce array") | ||||
|   (assert (not= (keep-syntax! brak @[1 2 3]) '(1 2 3)) | ||||
|           "keep-syntax! brackets not parens") | ||||
|   (assert (not= (keep-syntax! par @[1 2 3]) '[1 2 3]) | ||||
|           "keep-syntax! parens not brackets") | ||||
|   (assert (= (tuple/sourcemap brak) | ||||
|              (tuple/sourcemap (keep-syntax! brak @[1 2 3]))) | ||||
|           "keep-syntax! brackets source map") | ||||
|  | ||||
|   (keep-syntax par brak) | ||||
|   (assert (not= (tuple/sourcemap brak) (tuple/sourcemap par)) | ||||
|           "keep-syntax no mutate") | ||||
|   (assert (= (keep-syntax 1 brak) brak) "keep-syntax brackets ignore type")) | ||||
|  | ||||
| # Curenv | ||||
| # 28439d822, f7c556e | ||||
| (assert (= (curenv) (curenv 0)) "curenv 1") | ||||
| (assert (= (table/getproto (curenv)) (curenv 1)) "curenv 2") | ||||
| (assert (= nil (curenv 1000000)) "curenv 3") | ||||
| (assert (= root-env (curenv 1)) "curenv 4") | ||||
|  | ||||
| # Import macro test | ||||
| # a31e079f9 | ||||
| (assert-no-error "import macro 1" (macex '(import a :as b :fresh maybe))) | ||||
| (assert (deep= ~(,import* "a" :as "b" :fresh maybe) | ||||
|                (macex '(import a :as b :fresh maybe))) "import macro 2") | ||||
|  | ||||
| # #477 walk preserving bracket type | ||||
| # 0a1d902f4 | ||||
| (assert (= :brackets (tuple/type (postwalk identity '[]))) | ||||
|         "walk square brackets 1") | ||||
| (assert (= :brackets (tuple/type (walk identity '[]))) | ||||
|         "walk square brackets 2") | ||||
|  | ||||
| # Issue #751 | ||||
| # 547fda6a4 | ||||
| (def t {:side false}) | ||||
| (assert (nil? (get-in t [:side :note])) "get-in with false value") | ||||
| (assert (= (get-in t [:side :note] "dflt") "dflt") | ||||
|         "get-in with false value and default") | ||||
|  | ||||
| # Evaluate stream with `dofile` | ||||
| # 9cc4e4812 | ||||
| (def [r w] (os/pipe)) | ||||
| (:write w "(setdyn :x 10)") | ||||
| (:close w) | ||||
| (def stream-env (dofile r)) | ||||
| (assert (= (stream-env :x) 10) "dofile stream 1") | ||||
|  | ||||
| # Test thaw and freeze | ||||
| # 9cc0645a1 | ||||
| (def table-to-freeze @{:c 22 :b [1 2 3 4] :d @"test" :e "test2"}) | ||||
| (def table-to-freeze-with-inline-proto | ||||
|   @{:a @[1 2 3] :b @[1 2 3 4] :c 22 :d @"test" :e @"test2"}) | ||||
| (def struct-to-thaw | ||||
|   (struct/with-proto {:a [1 2 3]} :c 22 :b [1 2 3 4] :d "test" :e "test2")) | ||||
| (table/setproto table-to-freeze @{:a @[1 2 3]}) | ||||
|  | ||||
| (assert (deep= {:a [1 2 3] :b [1 2 3 4] :c 22 :d "test" :e "test2"} | ||||
|                (freeze table-to-freeze))) | ||||
| (assert (deep= table-to-freeze-with-inline-proto (thaw table-to-freeze))) | ||||
| (assert (deep= table-to-freeze-with-inline-proto (thaw struct-to-thaw))) | ||||
|  | ||||
| # Make sure Carriage Returns don't end up in doc strings | ||||
| # e528b86 | ||||
| (assert (not (string/find "\r" | ||||
|                           (get ((fiber/getenv (fiber/current)) 'cond) | ||||
|                                :doc ""))) | ||||
|         "no \\r in doc strings") | ||||
|  | ||||
| # cff718f37 | ||||
| (var counter 0) | ||||
| (def thunk (delay (++ counter))) | ||||
| (assert (= (thunk) 1) "delay 1") | ||||
| (assert (= counter 1) "delay 2") | ||||
| (assert (= (thunk) 1) "delay 3") | ||||
| (assert (= counter 1) "delay 4") | ||||
|  | ||||
| # maclintf | ||||
| (def env (table/clone (curenv))) | ||||
| ((compile '(defmacro foo [] (maclintf :strict "oops")) env :anonymous)) | ||||
| (def lints @[]) | ||||
| (compile (tuple/setmap '(foo) 1 2) env :anonymous lints) | ||||
| (assert (deep= lints @[[:strict 1 2 "oops"]]) "maclintf 1") | ||||
|  | ||||
| (def env (table/clone (curenv))) | ||||
| ((compile '(defmacro foo [& body] (maclintf :strict "foo-oops") ~(do ,;body)) env :anonymous)) | ||||
| ((compile '(defmacro bar [] (maclintf :strict "bar-oops")) env :anonymous)) | ||||
| (def lints @[]) | ||||
| # Compile (foo (bar)), but with explicit source map values | ||||
| (def bar-invoke (tuple/setmap '(bar) 3 4)) | ||||
| (compile (tuple/setmap ~(foo ,bar-invoke) 1 2) env :anonymous lints) | ||||
| (assert (deep= lints @[[:strict 1 2 "foo-oops"] | ||||
|                        [:strict 3 4 "bar-oops"]]) | ||||
|         "maclintf 2") | ||||
|  | ||||
| # Bad bytecode wrt. using result from break expression | ||||
| (defn bytecode-roundtrip | ||||
|   [f] | ||||
|   (assert-no-error "bytecode round-trip" (unmarshal (marshal f make-image-dict)))) | ||||
|  | ||||
| (defn case-1 [&] (def x (break 1))) | ||||
| (bytecode-roundtrip case-1) | ||||
| (defn foo [&]) | ||||
| (defn case-2 [&] | ||||
|   (foo (break (foo))) | ||||
|   (foo)) | ||||
| (bytecode-roundtrip case-2) | ||||
| (defn case-3 [&] | ||||
|   (def x (break (do (foo))))) | ||||
| (bytecode-roundtrip case-3) | ||||
| (defn case-4 [&] | ||||
|   (def x (break (break (foo))))) | ||||
| (bytecode-roundtrip case-4) | ||||
| (defn case-4 [&] | ||||
|   (def x (break (break (break))))) | ||||
| (bytecode-roundtrip case-4) | ||||
|  | ||||
| # Debug bytecode of these functions | ||||
| # (pp (disasm case-1)) | ||||
| # (pp (disasm case-2)) | ||||
| # (pp (disasm case-3)) | ||||
|  | ||||
| (end-suite) | ||||
| @@ -1,126 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Buffer blitting | ||||
| # 16ebb1118 | ||||
| (def b (buffer/new-filled 100)) | ||||
| (buffer/bit-set b 100) | ||||
| (buffer/bit-clear b 100) | ||||
| (assert (zero? (sum b)) "buffer bit set and clear") | ||||
| (assert (= false (buffer/bit b 101)) "bit get false") | ||||
| (buffer/bit-toggle b 101) | ||||
| (assert (= true (buffer/bit b 101)) "bit get true") | ||||
| (assert (= 32 (sum b)) "buffer bit set and clear") | ||||
| (assert-error "invalid bit index 1000" (buffer/bit-toggle b 1000)) | ||||
|  | ||||
| (def b2 @"hello world") | ||||
|  | ||||
| (buffer/blit b2 "joyto ") | ||||
| (assert (= (string b2) "joyto world") "buffer/blit 1") | ||||
|  | ||||
| (buffer/blit b2 "joyto" 6) | ||||
| (assert (= (string b2) "joyto joyto") "buffer/blit 2") | ||||
|  | ||||
| (buffer/blit b2 "abcdefg" 5 6) | ||||
| (assert (= (string b2) "joytogjoyto") "buffer/blit 3") | ||||
|  | ||||
| # buffer/push | ||||
|  | ||||
| (assert (deep= (buffer/push @"AA" @"BB") @"AABB") "buffer/push buffer") | ||||
| (assert (deep= (buffer/push @"AA" 66 66) @"AABB") "buffer/push int") | ||||
| (def b @"AA") | ||||
| (assert (deep= (buffer/push b b) @"AAAA") "buffer/push buffer self") | ||||
|  | ||||
| # buffer/push-byte | ||||
| (assert (deep= (buffer/push-byte @"AA" 66) @"AAB") "buffer/push-byte") | ||||
| (assert-error "bad slot #1, expected 32 bit signed integer" (buffer/push-byte @"AA" :flap)) | ||||
|  | ||||
| # Buffer push word | ||||
| # e755f9830 | ||||
| (def b3 @"") | ||||
| (buffer/push-word b3 0xFF 0x11) | ||||
| (assert (= 8 (length b3)) "buffer/push-word 1") | ||||
| (assert (= "\xFF\0\0\0\x11\0\0\0" (string b3)) "buffer/push-word 2") | ||||
| (buffer/clear b3) | ||||
| (buffer/push-word b3 0xFFFFFFFF 0x1100) | ||||
| (assert (= 8 (length b3)) "buffer/push-word 3") | ||||
| (assert (= "\xFF\xFF\xFF\xFF\0\x11\0\0" (string b3)) "buffer/push-word 4") | ||||
| (assert-error "cannot convert 0.5 to machine word" (buffer/push-word @"" 0.5)) | ||||
|  | ||||
| # Buffer push string | ||||
| # 175925207 | ||||
| (def b4 (buffer/new-filled 10 0)) | ||||
| (buffer/push-string b4 b4) | ||||
| (assert (= "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" (string b4)) | ||||
|         "buffer/push-buffer 1") | ||||
| (def b5 @"123") | ||||
| (buffer/push-string b5 "456" @"789") | ||||
| (assert (= "123456789" (string b5)) "buffer/push-buffer 2") | ||||
|  | ||||
| # 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") | ||||
| (assert (deep= @"0123456789" (buffer/from-bytes ;(range 48 58))) "buffer/from-bytes 3") | ||||
| (assert (= 0 (length (buffer/from-bytes))) "buffer/from-bytes 4") | ||||
| (assert (= 5 (length (buffer/from-bytes ;(range 5)))) "buffer/from-bytes 5") | ||||
| (assert-error "bad slot #1, expected 32 bit signed integer" (buffer/from-bytes :abc)) | ||||
|  | ||||
| # some tests for buffer/format | ||||
| # 029394d | ||||
| (assert (= (string (buffer/format @"" "pi = %6.3f" math/pi)) "pi =  3.142") | ||||
|         "%6.3f") | ||||
| (assert (= (string (buffer/format @"" "pi = %+6.3f" math/pi)) "pi = +3.142") | ||||
|         "%6.3f") | ||||
| (assert (= (string (buffer/format @"" "pi = %40.20g" math/pi)) | ||||
|            "pi =                     3.141592653589793116") "%6.3f") | ||||
|  | ||||
| (assert (= (string (buffer/format @"" "🐼 = %6.3f" math/pi)) "🐼 =  3.142") | ||||
|         "UTF-8") | ||||
| (assert (= (string (buffer/format @"" "π = %.8g" math/pi)) "π = 3.1415927") | ||||
|         "π") | ||||
| (assert (= (string (buffer/format @"" "\xCF\x80 = %.8g" math/pi)) | ||||
|            "\xCF\x80 = 3.1415927") "\xCF\x80") | ||||
|  | ||||
| # Regression #301 | ||||
| # a3d4ecddb | ||||
| (def b (buffer/new-filled 128 0x78)) | ||||
| (assert (= 38 (length (buffer/blit @"" b -1 90))) "buffer/blit 1") | ||||
|  | ||||
| (def a @"abcdefghijklm") | ||||
| (assert (deep= @"abcde" (buffer/blit @"" a -1 0 5)) "buffer/blit 2") | ||||
| (assert (deep= @"bcde" (buffer/blit @"" a -1 1 5)) "buffer/blit 3") | ||||
| (assert (deep= @"cde" (buffer/blit @"" a -1 2 5)) "buffer/blit 4") | ||||
| (assert (deep= @"de" (buffer/blit @"" a -1 3 5)) "buffer/blit 5") | ||||
| (assert (deep= @"de" (buffer/blit @"" a nil 3 5)) "buffer/blit 6") | ||||
|  | ||||
| # buffer/push-at | ||||
| # c55d93512 | ||||
| (assert (deep= @"abc456" (buffer/push-at @"abc123" 3 "456")) | ||||
|         "buffer/push-at 1") | ||||
| (assert (deep= @"abc456789" (buffer/push-at @"abc123" 3 "456789")) | ||||
|         "buffer/push-at 2") | ||||
| (assert (deep= @"abc423" (buffer/push-at @"abc123" 3 "4")) | ||||
|         "buffer/push-at 3") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,34 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Inline 3 argument get | ||||
| # a1ea62a | ||||
| (assert (= 10 (do (var a 10) (set a (get '{} :a a)))) "inline get 1") | ||||
|  | ||||
| # Regression #24 | ||||
| # f28477649 | ||||
| (def t (put @{} :hi 1)) | ||||
| (assert (deep= t @{:hi 1}) "regression #24") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,77 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Regression Test | ||||
| # 0378ba78 | ||||
| (assert (= 1 (((compile '(fn [] 1) @{})))) "regression test") | ||||
|  | ||||
| # Fix a compiler bug in the do special form | ||||
| # 3e1e2585 | ||||
| (defn myfun [x] | ||||
|   (var a 10) | ||||
|   (set a (do | ||||
|          (def y x) | ||||
|          (if x 8 9)))) | ||||
|  | ||||
| (assert (= (myfun true) 8) "check do form regression") | ||||
| (assert (= (myfun false) 9) "check do form regression") | ||||
|  | ||||
| # Check x:digits: works as symbol and not a hex number | ||||
| # 5baf70f4 | ||||
| (def x1 100) | ||||
| (assert (= x1 100) "x1 as symbol") | ||||
| (def X1 100) | ||||
| (assert (= X1 100) "X1 as symbol") | ||||
|  | ||||
| # Edge case should cause old compilers to fail due to | ||||
| # if statement optimization | ||||
| # 17283241 | ||||
| (var var-a 1) | ||||
| (var var-b (if false 2 (string "hello"))) | ||||
|  | ||||
| (assert (= var-b "hello") "regression 1") | ||||
|  | ||||
| # d28925fda | ||||
| (assert (= (string '()) (string [])) "empty bracket tuple literal") | ||||
|  | ||||
| # Bracket tuple issue | ||||
| # 340a6c4 | ||||
| (let [do 3] | ||||
|   (assert (= [3 1 2 3] [do 1 2 3]) "bracket tuples are never special forms")) | ||||
| (assert (= ~(,defn 1 2 3) [defn 1 2 3]) "bracket tuples are never macros") | ||||
| (assert (= ~(,+ 1 2 3) [+ 1 2 3]) "bracket tuples are never function calls") | ||||
|  | ||||
| # Crash issue #1174 - bad debug info | ||||
| # e97299f | ||||
| (defn crash [] | ||||
|   (debug/stack (fiber/current))) | ||||
| (do | ||||
|   (math/random) | ||||
|   (defn foo [_] | ||||
|     (crash) | ||||
|     1) | ||||
|   (foo 0) | ||||
|   10) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,181 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # ac50f62 | ||||
| (assert (= 10 (+ 1 2 3 4)) "addition") | ||||
| (assert (= -8 (- 1 2 3 4)) "subtraction") | ||||
| (assert (= 24 (* 1 2 3 4)) "multiplication") | ||||
| # d6967a5 | ||||
| (assert (= 4 (blshift 1 2)) "left shift") | ||||
| (assert (= 1 (brshift 4 2)) "right shift") | ||||
| # unsigned shift | ||||
| (assert (= 32768 (brushift 0x80000000 16)) "right shift unsigned 1") | ||||
| (assert-error "right shift unsigned 2" (= -32768 (brshift 0x80000000 16))) | ||||
| (assert (= -1 (brshift -1 16)) "right shift unsigned 3") | ||||
| # non-immediate forms | ||||
| (assert (= 32768 (brushift 0x80000000 (+ 0 16))) "right shift unsigned non-immediate") | ||||
| (assert-error "right shift non-immediate" (= -32768 (brshift 0x80000000 (+ 0 16)))) | ||||
| (assert (= -1 (brshift -1 (+ 0 16))) "right shift non-immediate 2") | ||||
| (assert (= 32768 (blshift 1 (+ 0 15))) "left shift non-immediate") | ||||
| # 7e46ead | ||||
| (assert (< 1 2 3 4 5 6) "less than integers") | ||||
| (assert (< 1.0 2.0 3.0 4.0 5.0 6.0) "less than reals") | ||||
| (assert (> 6 5 4 3 2 1) "greater than integers") | ||||
| (assert (> 6.0 5.0 4.0 3.0 2.0 1.0) "greater than reals") | ||||
| (assert (<= 1 2 3 3 4 5 6) "less than or equal to integers") | ||||
| (assert (<= 1.0 2.0 3.0 3.0 4.0 5.0 6.0) "less than or equal to reals") | ||||
| (assert (>= 6 5 4 4 3 2 1) "greater than or equal to integers") | ||||
| (assert (>= 6.0 5.0 4.0 4.0 3.0 2.0 1.0) "greater than or equal to reals") | ||||
|  | ||||
| (assert (= 7 (% 20 13)) "rem 1") | ||||
| (assert (= -7 (% -20 13)) "rem 2") | ||||
| (assert (= 7 (% 20 -13)) "rem 3") | ||||
| (assert (= -7 (% -20 -13)) "rem 4") | ||||
| (assert (nan? (% 20 0)) "rem 5") | ||||
|  | ||||
| (assert (= 7 (mod 20 13)) "mod 1") | ||||
| (assert (= 6 (mod -20 13)) "mod 2") | ||||
| (assert (= -6 (mod 20 -13)) "mod 3") | ||||
| (assert (= -7 (mod -20 -13)) "mod 4") | ||||
| (assert (= 20 (mod 20 0)) "mod 5") | ||||
|  | ||||
| (assert (= 1 (div 20 13)) "div 1") | ||||
| (assert (= -2 (div -20 13)) "div 2") | ||||
| (assert (= -2 (div 20 -13)) "div 3") | ||||
| (assert (= 1 (div -20 -13)) "div 4") | ||||
| (assert (= math/inf (div 20 0)) "div 5") | ||||
|  | ||||
| (assert (all = (seq [n :range [0 10]] (mod n 5 3)) | ||||
|                (seq [n :range [0 10]] (% n 5 3)) | ||||
|                [0 1 2 0 1 0 1 2 0 1]) "variadic mod") | ||||
|  | ||||
| (assert (< 1.0 nil false true | ||||
|            (fiber/new (fn [] 1)) | ||||
|            "hi" | ||||
|            (quote hello) | ||||
|            :hello | ||||
|            (array 1 2 3) | ||||
|            (tuple 1 2 3) | ||||
|            (table "a" "b" "c" "d") | ||||
|            (struct 1 2 3 4) | ||||
|            (buffer "hi") | ||||
|            (fn [x] (+ x x)) | ||||
|            print) "type ordering") | ||||
|  | ||||
| # b305a7c9b | ||||
| (assert (= (string (buffer "123" "456")) (string @"123456")) "buffer literal") | ||||
| # 277117165 | ||||
| (assert (= (get {} 1) nil) "get nil from empty struct") | ||||
| (assert (= (get @{} 1) nil) "get nil from empty table") | ||||
| (assert (= (get {:boop :bap} :boop) :bap) "get non nil from struct") | ||||
| (assert (= (get @{:boop :bap} :boop) :bap) "get non nil from table") | ||||
| (assert (= (get @"\0" 0) 0) "get non nil from buffer") | ||||
| (assert (= (get @"\0" 1) nil) "get nil from buffer oob") | ||||
| (assert (put @{} :boop :bap) "can add to empty table") | ||||
| (assert (put @{1 3} :boop :bap) "can add to non-empty table") | ||||
| # 7e46ead | ||||
| (assert (= 7 (bor 3 4)) "bit or") | ||||
| (assert (= 0 (band 3 4)) "bit and") | ||||
| # f41dab8 | ||||
| (assert (= 0xFF (bxor 0x0F 0xF0)) "bit xor") | ||||
| (assert (= 0xF0 (bxor 0xFF 0x0F)) "bit xor 2") | ||||
|  | ||||
| # Some testing for not= | ||||
| # 08f6c642d | ||||
| (assert (not= 1 1 0) "not= 1") | ||||
| (assert (not= 0 1 1) "not= 2") | ||||
|  | ||||
| # Check if abstract test works | ||||
| # d791077e2 | ||||
| (assert (abstract? stdout) "abstract? stdout") | ||||
| (assert (abstract? stdin) "abstract? stdin") | ||||
| (assert (abstract? stderr) "abstract? stderr") | ||||
| (assert (not (abstract? nil)) "not abstract? nil") | ||||
| (assert (not (abstract? 1)) "not abstract? 1") | ||||
| (assert (not (abstract? 3)) "not abstract? 3") | ||||
| (assert (not (abstract? 5)) "not abstract? 5") | ||||
|  | ||||
| # Module path expansion | ||||
| # ff3bb6627 | ||||
| (setdyn :current-file "some-dir/some-file") | ||||
| (defn test-expand [path temp] | ||||
|   (string (module/expand-path path temp))) | ||||
|  | ||||
| (assert (= (test-expand "abc" ":cur:/:all:") "some-dir/abc") | ||||
|         "module/expand-path 1") | ||||
| (assert (= (test-expand "./abc" ":cur:/:all:") "some-dir/abc") | ||||
|         "module/expand-path 2") | ||||
| (assert (= (test-expand "abc/def.txt" ":cur:/:name:") "some-dir/def.txt") | ||||
|         "module/expand-path 3") | ||||
| (assert (= (test-expand "abc/def.txt" ":cur:/:dir:/sub/:name:") | ||||
|            "some-dir/abc/sub/def.txt") "module/expand-path 4") | ||||
| # fc46030e7 | ||||
| (assert (= (test-expand "/abc/../def.txt" ":all:") "/def.txt") | ||||
|         "module/expand-path 5") | ||||
| (assert (= (test-expand "abc/../def.txt" ":all:") "def.txt") | ||||
|         "module/expand-path 6") | ||||
| (assert (= (test-expand "../def.txt" ":all:") "../def.txt") | ||||
|         "module/expand-path 7") | ||||
| (assert (= (test-expand "../././././abcd/../def.txt" ":all:") "../def.txt") | ||||
|         "module/expand-path 8") | ||||
|  | ||||
| # module/expand-path regression | ||||
| # issue #143 - e0fe8476a | ||||
| (with-dyns [:syspath ".janet/.janet"] | ||||
|   (assert (= (string (module/expand-path "hello" ":sys:/:all:.janet")) | ||||
|              ".janet/.janet/hello.janet") "module/expand-path 1")) | ||||
|  | ||||
| # int? | ||||
| (assert (int? 1) "int? 1") | ||||
| (assert (int? -1) "int? -1") | ||||
| (assert (not (int? true)) "int? true") | ||||
| (assert (not (int? 3.14)) "int? 3.14") | ||||
| (assert (not (int? 8589934592)) "int? 8589934592") | ||||
|  | ||||
| # memcmp | ||||
| (assert (= (memcmp "123helloabcd" "1234helloabc" 5 3 4) 0) "memcmp 1") | ||||
| (assert (< (memcmp "123hellaabcd" "1234helloabc" 5 3 4) 0) "memcmp 2") | ||||
| (assert (> (memcmp "123helloabcd" "1234hellaabc" 5 3 4) 0) "memcmp 3") | ||||
| (assert-error "invalid offset-a: 1" (memcmp "a" "b" 1 1 0)) | ||||
| (assert-error "invalid offset-b: 1" (memcmp "a" "b" 1 0 1)) | ||||
|  | ||||
| # Range | ||||
| # a982f351d | ||||
| (assert (deep= (range 10) @[0 1 2 3 4 5 6 7 8 9]) "(range 10)") | ||||
| (assert (deep= (range 5 10) @[5 6 7 8 9]) "(range 5 10)") | ||||
| (assert (deep= (range 0 16 4) @[0 4 8 12]) "(range 0 16 4)") | ||||
| (assert (deep= (range 0 17 4) @[0 4 8 12 16]) "(range 0 17 4)") | ||||
| (assert (deep= (range 16 0 -4) @[16 12 8 4]) "(range 16 0 -4)") | ||||
| (assert (deep= (range 17 0 -4) @[17 13 9 5 1]) "(range 17 0 -4)") | ||||
|  | ||||
| (assert (= (length (range 10)) 10) "(range 10)") | ||||
| (assert (= (length (range -10)) 0) "(range -10)") | ||||
| (assert (= (length (range 1 10)) 9) "(range 1 10)") | ||||
|  | ||||
| # iterating over generator | ||||
| (assert-no-error "iterate over coro 1" (values (generate [x :range [0 10]] x))) | ||||
| (assert-no-error "iterate over coro 2" (keys (generate [x :range [0 10]] x))) | ||||
| (assert-no-error "iterate over coro 3" (pairs (generate [x :range [0 10]] x))) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,34 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Simple function break | ||||
| # a8afc5b81 | ||||
| (debug/fbreak map 1) | ||||
| (def f (fiber/new (fn [] (map inc [1 2 3])) :a)) | ||||
| (resume f) | ||||
| (assert (= :debug (fiber/status f)) "debug/fbreak") | ||||
| (debug/unfbreak map 1) | ||||
| (map inc [1 2 3]) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,288 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose & contributors | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # some tests for bigint | ||||
| # 319575c | ||||
| (def i64 int/s64) | ||||
| (def u64 int/u64) | ||||
|  | ||||
| (assert-no-error | ||||
|   "create some uint64 bigints" | ||||
|   (do | ||||
|     # from number | ||||
|     (def a (u64 10)) | ||||
|     # max double we can convert to int (2^53) | ||||
|     (def b (u64 0x1fffffffffffff)) | ||||
|     (def b (u64 (math/pow 2 53))) | ||||
|     # from string | ||||
|     (def c (u64 "0xffff_ffff_ffff_ffff")) | ||||
|     (def c (u64 "32rvv_vv_vv_vv")) | ||||
|     (def d (u64 "123456789")))) | ||||
|  | ||||
| # Conversion back to an int32 | ||||
| # 88db9751d | ||||
| (assert (= (int/to-number (u64 0xFaFa)) 0xFaFa)) | ||||
| (assert (= (int/to-number (i64 0xFaFa)) 0xFaFa)) | ||||
| (assert (= (int/to-number (u64 9007199254740991)) 9007199254740991)) | ||||
| (assert (= (int/to-number (i64 9007199254740991)) 9007199254740991)) | ||||
| (assert (= (int/to-number (i64 -9007199254740991)) -9007199254740991)) | ||||
|  | ||||
| (assert-error | ||||
|   "u64 out of bounds for safe integer" | ||||
|   (int/to-number (u64 "9007199254740993")) | ||||
|  | ||||
|   (assert-error | ||||
|     "s64 out of bounds for safe integer" | ||||
|     (int/to-number (i64 "-9007199254740993")))) | ||||
|  | ||||
| (assert-error | ||||
|   "int/to-number fails on non-abstract types" | ||||
|   (int/to-number 1)) | ||||
|  | ||||
| (assert-no-error | ||||
|   "create some int64 bigints" | ||||
|   (do | ||||
|     # from number | ||||
|     (def a (i64 -10)) | ||||
|     # max double we can convert to int (2^53) | ||||
|     (def b (i64 0x1fffffffffffff)) | ||||
|     (def b (i64 (math/pow 2 53))) | ||||
|     # from string | ||||
|     (def c (i64 "0x7fff_ffff_ffff_ffff")) | ||||
|     (def d (i64 "123456789")))) | ||||
|  | ||||
| (assert-error | ||||
|   "bad initializers" | ||||
|   (do | ||||
|     # double to big to be converted to uint64 without truncation (2^53 + 1) | ||||
|     (def b (u64 (+ 0xffff_ffff_ffff_ff 1))) | ||||
|     (def b (u64 (+ (math/pow 2 53) 1))) | ||||
|     # out of range 65 bits | ||||
|     (def c (u64 "0x1ffffffffffffffff")) | ||||
|     # just to big | ||||
|     (def d (u64 "123456789123456789123456789")))) | ||||
|  | ||||
| (assert (= (:/ (u64 "0xffff_ffff_ffff_ffff") 8 2) (u64 "0xfffffffffffffff")) | ||||
|         "bigint operations 1") | ||||
| (assert (let [a (u64 0xff)] (= (:+ a a a a) (:* a 2 2))) | ||||
|         "bigint operations 2") | ||||
|  | ||||
| # 5ae520a2c | ||||
| (assert (= (string (i64 -123)) "-123") "i64 prints reasonably") | ||||
| (assert (= (string (u64 123)) "123") "u64 prints reasonably") | ||||
|  | ||||
| # 1db6d0e0b | ||||
| (assert-error | ||||
|   "trap INT64_MIN / -1" | ||||
|   (:/ (int/s64 "-0x8000_0000_0000_0000") -1)) | ||||
|  | ||||
| # int/s64 and int/u64 serialization | ||||
| # 6aea7c7f7 | ||||
| (assert (deep= (int/to-bytes (u64 0)) @"\x00\x00\x00\x00\x00\x00\x00\x00")) | ||||
|  | ||||
| (assert (deep= (int/to-bytes (i64 1) :le) | ||||
|                @"\x01\x00\x00\x00\x00\x00\x00\x00")) | ||||
| (assert (deep= (int/to-bytes (i64 1) :be) | ||||
|                @"\x00\x00\x00\x00\x00\x00\x00\x01")) | ||||
| (assert (deep= (int/to-bytes (i64 -1)) | ||||
|                @"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF")) | ||||
| (assert (deep= (int/to-bytes (i64 -5) :be) | ||||
|                @"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFB")) | ||||
|  | ||||
| (assert (deep= (int/to-bytes (u64 1) :le) | ||||
|                @"\x01\x00\x00\x00\x00\x00\x00\x00")) | ||||
| (assert (deep= (int/to-bytes (u64 1) :be) | ||||
|                @"\x00\x00\x00\x00\x00\x00\x00\x01")) | ||||
| (assert (deep= (int/to-bytes (u64 300) :be) | ||||
|                @"\x00\x00\x00\x00\x00\x00\x01\x2C")) | ||||
|  | ||||
| # int/s64 int/u64 to existing buffer | ||||
| # bbb3e16fd | ||||
| (let [buf1 @"" | ||||
|       buf2 @"abcd"] | ||||
|   (assert (deep= (int/to-bytes (i64 1) :le buf1) | ||||
|                  @"\x01\x00\x00\x00\x00\x00\x00\x00")) | ||||
|   (assert (deep= buf1 @"\x01\x00\x00\x00\x00\x00\x00\x00")) | ||||
|   (assert (deep= (int/to-bytes (u64 300) :be buf2) | ||||
|                  @"abcd\x00\x00\x00\x00\x00\x00\x01\x2C"))) | ||||
|  | ||||
| # int/s64 and int/u64 parameter type checking | ||||
| # 6aea7c7f7 | ||||
| (assert-error | ||||
|   "bad value passed to int/to-bytes" | ||||
|   (int/to-bytes 1)) | ||||
|  | ||||
| # 6aea7c7f7 | ||||
| (assert-error | ||||
|   "invalid endianness passed to int/to-bytes" | ||||
|   (int/to-bytes (u64 0) :little)) | ||||
|  | ||||
| # bbb3e16fd | ||||
| (assert-error | ||||
|   "invalid buffer passed to int/to-bytes" | ||||
|   (int/to-bytes (u64 0) :little :buffer)) | ||||
|  | ||||
| # Right hand operators | ||||
| # 4fe005e3c | ||||
| (assert (= (int/s64 (sum (range 10))) (sum (map int/s64 (range 10)))) | ||||
|         "right hand operators 1") | ||||
| (assert (= (int/s64 | ||||
|              (product (range 1 10))) (product (map int/s64 (range 1 10)))) | ||||
|         "right hand operators 2") | ||||
| (assert (= (int/s64 15) (bor 10 (int/s64 5)) (bor (int/s64 10) 5)) | ||||
|         "right hand operators 3") | ||||
|  | ||||
| # Integer type checks | ||||
| # 11067d7a5 | ||||
| (assert (compare= 0 (- (int/u64 "1000") 1000)) "subtract from int/u64") | ||||
|  | ||||
| (assert (odd? (int/u64 "1001")) "odd? 1") | ||||
| (assert (not (odd? (int/u64 "1000"))) "odd? 2") | ||||
| (assert (odd? (int/s64 "1001")) "odd? 3") | ||||
| (assert (not (odd? (int/s64 "1000"))) "odd? 4") | ||||
| (assert (odd? (int/s64 "-1001")) "odd? 5") | ||||
| (assert (not (odd? (int/s64 "-1000"))) "odd? 6") | ||||
|  | ||||
| (assert (even? (int/u64 "1000")) "even? 1") | ||||
| (assert (not (even? (int/u64 "1001"))) "even? 2") | ||||
| (assert (even? (int/s64 "1000")) "even? 3") | ||||
| (assert (not (even? (int/s64 "1001"))) "even? 4") | ||||
| (assert (even? (int/s64 "-1000")) "even? 5") | ||||
| (assert (not (even? (int/s64 "-1001"))) "even? 6") | ||||
|  | ||||
| # integer type operations | ||||
| (defn opcheck [int x y] | ||||
|   (each op [mod % div] | ||||
|     (assert (compare= (op x y) (op (int x) y)) | ||||
|             (string int " (" op " " x " " y ") expected " (op x y) | ||||
|                     ", got " (op (int x) y))) | ||||
|     (assert (compare= (op x y) (op x (int y))) | ||||
|             (string int " (" op " " x " " y ") expected " (op x y) | ||||
|                     ", got " (op x (int y)))) | ||||
|     (assert (compare= (op x y) (op (int x) (int y))) | ||||
|             (string int " (" op " " x " " y ") expected " (op x y) | ||||
|                     ", got " (op (int x) (int y)))))) | ||||
|  | ||||
| (loop [x :in [-5 -3 0 3 5] | ||||
|        y :in [-4 -3 3 4]] | ||||
|   (opcheck int/s64 x y) | ||||
|   (if (and (>= x 0) (>= y 0)) | ||||
|     (opcheck int/u64 x y))) | ||||
|  | ||||
| (each int [int/s64 int/u64] | ||||
|   (each op [% / div] | ||||
|     (assert-error "division by zero" (op (int 7) 0)) | ||||
|     (assert-error "division by zero" (op 7 (int 0))) | ||||
|     (assert-error "division by zero" (op (int 7) (int 0))))) | ||||
|  | ||||
| (each int [int/s64 int/u64] | ||||
|   (loop [x :in [-5 -3 0 3 5] :when (or (pos? x) (= int int/s64))] | ||||
|     # skip check when comparing negative values with unsigned integers. | ||||
|     (assert (= (int x) (mod (int x) 0)) (string int " mod 0")) | ||||
|     (assert (= (int x) (mod x (int 0))) (string int " mod 0")) | ||||
|     (assert (= (int x) (mod (int x) (int 0))) (string int " mod 0")))) | ||||
|  | ||||
| (loop [x :in [-5 -3 0 3 5]] | ||||
|   (assert (compare= (bnot x) (bnot (int/s64 x))) "int/s64 bnot")) | ||||
|  | ||||
| (loop [x :range [0 10]] | ||||
|   (assert (= (int/u64 "0xFFFF_FFFF_FFFF_FFFF") | ||||
|           (bxor (int/u64 x) (bnot (int/u64 x)))) | ||||
|           "int/u64 bnot")) | ||||
|  | ||||
| # Check for issue #1130 | ||||
| # 7e65c2bda | ||||
| (var d (int/s64 7)) | ||||
| (mod 0 d) | ||||
|  | ||||
| (var d (int/s64 7)) | ||||
| (def result (seq [n :in (range -21 0)] (mod n d))) | ||||
| (assert (deep= result | ||||
|                (map int/s64 @[0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6])) | ||||
|         "issue #1130") | ||||
|  | ||||
| # issue #272 - 81d301a42 | ||||
| (let [MAX_INT_64_STRING "9223372036854775807" | ||||
|       MAX_UINT_64_STRING "18446744073709551615" | ||||
|       MAX_INT_IN_DBL_STRING "9007199254740991" | ||||
|       NAN (math/log -1) | ||||
|       INF (/ 1 0) | ||||
|       MINUS_INF (/ -1 0) | ||||
|       compare-poly-tests | ||||
|       [[(int/s64 3) (int/u64 3) 0] | ||||
|        [(int/s64 -3) (int/u64 3) -1] | ||||
|        [(int/s64 3) (int/u64 2) 1] | ||||
|        [(int/s64 3) 3 0] [(int/s64 3) 4 -1] [(int/s64 3) -9 1] | ||||
|        [(int/u64 3) 3 0] [(int/u64 3) 4 -1] [(int/u64 3) -9 1] | ||||
|        [3 (int/s64 3) 0] [3 (int/s64 4) -1] [3 (int/s64 -5) 1] | ||||
|        [3 (int/u64 3) 0] [3 (int/u64 4) -1] [3 (int/u64 2) 1] | ||||
|        [(int/s64 MAX_INT_64_STRING) (int/u64 MAX_UINT_64_STRING) -1] | ||||
|        [(int/s64 MAX_INT_IN_DBL_STRING) | ||||
|         (scan-number MAX_INT_IN_DBL_STRING) 0] | ||||
|        [(int/u64 MAX_INT_IN_DBL_STRING) | ||||
|         (scan-number MAX_INT_IN_DBL_STRING) 0] | ||||
|        [(+ 1 (int/u64 MAX_INT_IN_DBL_STRING)) | ||||
|         (scan-number MAX_INT_IN_DBL_STRING) 1] | ||||
|        [(int/s64 0) INF -1] [(int/u64 0) INF -1] | ||||
|        [MINUS_INF (int/u64 0) -1] [MINUS_INF (int/s64 0) -1] | ||||
|        [(int/s64 1) NAN 0] [NAN (int/u64 1) 0]]] | ||||
|   (each [x y c] compare-poly-tests | ||||
|     (assert (= c (compare x y)) | ||||
|             (string/format "compare polymorphic %q %q %d" x y c)))) | ||||
|  | ||||
| # marshal | ||||
| (def m1 (u64 3141592654)) | ||||
| (def m2 (unmarshal (marshal m1))) | ||||
| (assert (= m1 m2) "marshal/unmarshal") | ||||
|  | ||||
| # compare u64/u64 | ||||
| (assert (= (compare (u64 1) (u64 2)) -1) "compare 1") | ||||
| (assert (= (compare (u64 1) (u64 1))  0) "compare 2") | ||||
| (assert (= (compare (u64 2) (u64 1)) +1) "compare 3") | ||||
|  | ||||
| # compare i64/i64 | ||||
| (assert (= (compare (i64 -1) (i64 +1)) -1) "compare 4") | ||||
| (assert (= (compare (i64 +1) (i64 +1))  0) "compare 5") | ||||
| (assert (= (compare (i64 +1) (i64 -1)) +1) "compare 6") | ||||
|  | ||||
| # compare u64/i64 | ||||
| (assert (= (compare (u64 1) (i64 2)) -1) "compare 7") | ||||
| (assert (= (compare (u64 1) (i64 -1)) +1) "compare 8") | ||||
| (assert (= (compare (u64 0) (i64 -1)) +1) "compare 9") | ||||
|  | ||||
| # compare i64/u64 | ||||
| (assert (= (compare (i64 1) (u64 2)) -1) "compare 10") | ||||
| (assert (= (compare (i64 -1) (u64 1)) -1) "compare 11") | ||||
| (assert (= (compare (i64 -1) (u64 0)) -1) "compare 12") | ||||
|  | ||||
| # off by 1 error in inttypes | ||||
| # a3e812b86 | ||||
| (assert (= (int/s64 "-0x8000_0000_0000_0000") | ||||
|            (+ (int/s64 "0x7FFF_FFFF_FFFF_FFFF") 1)) "int types wrap around") | ||||
| (assert (= (int/s64 "0x7FFF_FFFF_FFFF_FFFF") | ||||
|            (- (int/s64 "-0x8000_0000_0000_0000") 1)) "int types wrap around") | ||||
|  | ||||
| # Issue #1217 | ||||
| (assert (= (- (int/u64 "0xFFFFFFFF") 1) (int/u64 "0xFFFFFFFE")) "u64 subtract") | ||||
|  | ||||
| (end-suite) | ||||
| @@ -1,82 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose & contributors | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Printing to buffers | ||||
| # d47804d22 | ||||
| (def out-buf @"") | ||||
| (def err-buf @"") | ||||
| (with-dyns [:out out-buf :err err-buf] | ||||
|   (print "Hello") | ||||
|   (prin "hi") | ||||
|   (eprint "Sup") | ||||
|   (eprin "not much.")) | ||||
|  | ||||
| (assert (= (string out-buf) "Hello\nhi") "print and prin to buffer 1") | ||||
| (assert (= (string err-buf) "Sup\nnot much.") | ||||
|         "eprint and eprin to buffer 1") | ||||
|  | ||||
| # Printing to functions | ||||
| # 4e263b8c3 | ||||
| (def out-buf @"") | ||||
| (defn prepend [x] | ||||
|   (with-dyns [:out out-buf] | ||||
|     (prin "> " x))) | ||||
| (with-dyns [:out prepend] | ||||
|   (print "Hello world")) | ||||
|  | ||||
| (assert (= (string out-buf) "> Hello world\n") | ||||
|         "print to buffer via function") | ||||
|  | ||||
| # c2f844157, 3c523d66e | ||||
| (with [f (file/temp)] | ||||
|   (assert (= 0 (file/tell f)) "start of file") | ||||
|   (file/write f "foo\n") | ||||
|   (assert (= 4 (file/tell f)) "after written string") | ||||
|   (file/flush f) | ||||
|   (file/seek f :set 0) | ||||
|   (assert (= 0 (file/tell f)) "start of file again") | ||||
|   (assert (= (string (file/read f :all)) "foo\n") "temp files work")) | ||||
|  | ||||
| # issue #1055 - 2c927ea76 | ||||
| (let [b @""] | ||||
|   (defn dummy [a b c] | ||||
|     (+ a b c)) | ||||
|   (trace dummy) | ||||
|   (defn errout [arg] | ||||
|     (buffer/push b arg)) | ||||
|   (assert (= 6 (with-dyns [*err* errout] (dummy 1 2 3))) | ||||
|           "trace to custom err function") | ||||
|   (assert (deep= @"trace (dummy 1 2 3)\n" b) "trace buffer correct")) | ||||
|  | ||||
|  | ||||
| # xprintf | ||||
| (def b @"") | ||||
| (defn to-b [a] (buffer/push b a)) | ||||
| (xprintf to-b "123") | ||||
| (assert (deep= b @"123\n") "xprintf to buffer") | ||||
|  | ||||
|  | ||||
| (assert-error "cannot print to 3" (xprintf 3 "123")) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,150 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Marshal | ||||
|  | ||||
| # 98f2c6f | ||||
| (def um-lookup (env-lookup (fiber/getenv (fiber/current)))) | ||||
| (def m-lookup (invert um-lookup)) | ||||
|  | ||||
| # 0cf10946b | ||||
| (defn testmarsh [x msg] | ||||
|   (def marshx (marshal x m-lookup)) | ||||
|   (def out (marshal (unmarshal marshx um-lookup) m-lookup)) | ||||
|   (assert (= (string marshx) (string out)) msg)) | ||||
|  | ||||
| (testmarsh nil "marshal nil") | ||||
| (testmarsh false "marshal false") | ||||
| (testmarsh true "marshal true") | ||||
| (testmarsh 1 "marshal small integers") | ||||
| (testmarsh -1 "marshal integers (-1)") | ||||
| (testmarsh 199 "marshal small integers (199)") | ||||
| (testmarsh 5000 "marshal medium integers (5000)") | ||||
| (testmarsh -5000 "marshal small integers (-5000)") | ||||
| (testmarsh 10000 "marshal large integers (10000)") | ||||
| (testmarsh -10000 "marshal large integers (-10000)") | ||||
| (testmarsh 1.0 "marshal double") | ||||
| (testmarsh "doctordolittle" "marshal string") | ||||
| (testmarsh :chickenshwarma "marshal symbol") | ||||
| (testmarsh @"oldmcdonald" "marshal buffer") | ||||
| (testmarsh @[1 2 3 4 5] "marshal array") | ||||
| (testmarsh [tuple 1 2 3 4 5] "marshal tuple") | ||||
| (testmarsh @{1 2 3 4}  "marshal table") | ||||
| (testmarsh {1 2 3 4}  "marshal struct") | ||||
| (testmarsh (fn [x] x) "marshal function 0") | ||||
| (testmarsh (fn name [x] x) "marshal function 1") | ||||
| (testmarsh (fn [x] (+ 10 x 2)) "marshal function 2") | ||||
| (testmarsh (fn thing [x] (+ 11 x x 30)) "marshal function 3") | ||||
| (testmarsh map "marshal function 4") | ||||
| (testmarsh reduce "marshal function 5") | ||||
| (testmarsh (fiber/new (fn [] (yield 1) 2)) "marshal simple fiber 1") | ||||
| (testmarsh (fiber/new (fn [&] (yield 1) 2)) "marshal simple fiber 2") | ||||
|  | ||||
| # issue #53 - 1147482e6 | ||||
| (def strct {:a @[nil]}) | ||||
| (put (strct :a) 0 strct) | ||||
| (testmarsh strct "cyclic struct") | ||||
|  | ||||
| # More marshalling code | ||||
| # issue #53 - 1147482e6 | ||||
| (defn check-image | ||||
|   "Run a marshaling test using the make-image and load-image functions." | ||||
|   [x msg] | ||||
|   (def im (make-image x)) | ||||
|   # (printf "\nimage-hash: %d" (-> im string hash)) | ||||
|   (assert-no-error msg (load-image im))) | ||||
|  | ||||
| (check-image (fn [] (fn [] 1)) "marshal nested functions") | ||||
| (check-image (fiber/new (fn [] (fn [] 1))) | ||||
|              "marshal nested functions in fiber") | ||||
| (check-image (fiber/new (fn [] (fiber/new (fn [] 1)))) | ||||
|              "marshal nested fibers") | ||||
|  | ||||
| # issue #53 - f4908ebc4 | ||||
| (def issue-53-x | ||||
|   (fiber/new | ||||
|     (fn [] | ||||
|       (var y (fiber/new (fn [] (print "1") (yield) (print "2"))))))) | ||||
|  | ||||
| (check-image issue-53-x "issue 53 regression") | ||||
|  | ||||
| # Marshal closure over non resumable fiber | ||||
| # issue #317 - 7c4ffe9b9 | ||||
| (do | ||||
|   (defn f1 | ||||
|     [a] | ||||
|     (defn f1 [] (++ (a 0))) | ||||
|     (defn f2 [] (++ (a 0))) | ||||
|     (error [f1 f2])) | ||||
|   (def [_ tup] (protect (f1 @[0]))) | ||||
|   (def [f1 f2] (unmarshal (marshal tup make-image-dict) load-image-dict)) | ||||
|   (assert (= 1 (f1)) "marshal-non-resumable-closure 1") | ||||
|   (assert (= 2 (f2)) "marshal-non-resumable-closure 2")) | ||||
|  | ||||
| # Marshal closure over currently alive fiber | ||||
| # issue #317 - 7c4ffe9b9 | ||||
| (do | ||||
|   (defn f1 | ||||
|     [a] | ||||
|     (defn f1 [] (++ (a 0))) | ||||
|     (defn f2 [] (++ (a 0))) | ||||
|     (marshal [f1 f2] make-image-dict)) | ||||
|   (def [f1 f2] (unmarshal (f1 @[0]) load-image-dict)) | ||||
|   (assert (= 1 (f1)) "marshal-live-closure 1") | ||||
|   (assert (= 2 (f2)) "marshal-live-closure 2")) | ||||
|  | ||||
| (do | ||||
|   (var a 1) | ||||
|   (defn b [x] (+ a x)) | ||||
|   (def c (unmarshal (marshal b))) | ||||
|   (assert (= 2 (c 1)) "marshal-on-stack-closure 1")) | ||||
|  | ||||
| # Issue #336 cases - don't segfault | ||||
| # b145d4786 | ||||
| (assert-error "unmarshal errors 1" (unmarshal @"\xd6\xb9\xb9")) | ||||
| (assert-error "unmarshal errors 2" (unmarshal @"\xd7bc")) | ||||
| # 5bbd50785 | ||||
| (assert-error "unmarshal errors 3" | ||||
|               (unmarshal "\xd3\x01\xd9\x01\x62\xcf\x03\x78\x79\x7a" | ||||
|                          load-image-dict)) | ||||
| # fcc610f53 | ||||
| (assert-error "unmarshal errors 4" | ||||
|               (unmarshal | ||||
|                 @"\xD7\xCD\0e/p\x98\0\0\x03\x01\x01\x01\x02\0\0\x04\0\xCEe/p../tools | ||||
| \0\0\0/afl\0\0\x01\0erate\xDE\xDE\xDE\xDE\xDE\xDE\xDE\xDE\xDE\xDE | ||||
| \xA8\xDE\xDE\xDE\xDE\xDE\xDE\0\0\0\xDE\xDE_unmarshal_testcase3.ja | ||||
| neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02 | ||||
| \0\0\0\0\0*\xFE\x01\04\x02\0\0'\x03\0\r\0\r\0\r\0\r" load-image-dict)) | ||||
| # XXX: still needed? see 72beeeea | ||||
| (gccollect) | ||||
|  | ||||
| # ev/chan marshalling | ||||
| (compwhen (dyn 'ev/chan) | ||||
|   (def chan (ev/chan 10)) | ||||
|   (ev/give chan chan) | ||||
|   (def newchan (unmarshal (marshal chan))) | ||||
|   (def item (ev/take newchan)) | ||||
|   (assert (= item newchan) "ev/chan marshalling")) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,69 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # First commit removing the integer number type | ||||
| # 6b95326d7 | ||||
| (assert (= 400 (math/sqrt 160000)) "sqrt(160000)=400") | ||||
|  | ||||
| # RNGs | ||||
| # aee168721 | ||||
| (defn test-rng | ||||
|   [rng] | ||||
|   (assert (all identity (seq [i :range [0 1000]] | ||||
|                              (<= (math/rng-int rng i) i))) "math/rng-int test") | ||||
|   (assert (all identity (seq [i :range [0 1000]] | ||||
|     (def x (math/rng-uniform rng)) | ||||
|     (and (>= x 0) (< x 1)))) | ||||
|           "math/rng-uniform test")) | ||||
|  | ||||
| (def seedrng (math/rng 123)) | ||||
| (for i 0 75 | ||||
|   (test-rng (math/rng (:int seedrng)))) | ||||
|  | ||||
| # 70328437f | ||||
| (assert (deep-not= (-> 123 math/rng (:buffer 16)) | ||||
|                    (-> 456 math/rng (:buffer 16))) "math/rng-buffer 1") | ||||
|  | ||||
| (assert-no-error "math/rng-buffer 2" (math/seedrandom "abcdefg")) | ||||
|  | ||||
| # 027b2a8 | ||||
| (defn assert-many [f n e] | ||||
|  (var good true) | ||||
|  (loop [i :range [0 n]] | ||||
|   (if (not (f)) | ||||
|    (set good false))) | ||||
|  (assert good e)) | ||||
|  | ||||
| (assert-many (fn [] (>= 1 (math/random) 0)) 200 "(random) between 0 and 1") | ||||
|  | ||||
| # 06aa0a124 | ||||
| (assert (= (math/gcd 462 1071) 21) "math/gcd 1") | ||||
| (assert (= (math/lcm 462 1071) 23562) "math/lcm 1") | ||||
|  | ||||
| # math gamma | ||||
| # e6babd8 | ||||
| (assert (< 11899423.08 (math/gamma 11.5) 11899423.085) "math/gamma") | ||||
| (assert (< 2605.1158 (math/log-gamma 500) 2605.1159) "math/log-gamma") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,151 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| (def janet (dyn :executable)) | ||||
| (def run (filter next (string/split " " (os/getenv "SUBRUN" "")))) | ||||
|  | ||||
| # OS Date test | ||||
| # 719f7ba0c | ||||
| (assert (deep= {:year-day 0 | ||||
|                 :minutes 30 | ||||
|                 :month 0 | ||||
|                 :dst false | ||||
|                 :seconds 0 | ||||
|                 :year 2014 | ||||
|                 :month-day 0 | ||||
|                 :hours 20 | ||||
|                 :week-day 3} | ||||
|                (os/date 1388608200)) "os/date") | ||||
|  | ||||
| # OS mktime test | ||||
| # 3ee43c3ab | ||||
| (assert (= 1388608200 (os/mktime {:year-day 0 | ||||
|                                   :minutes 30 | ||||
|                                   :month 0 | ||||
|                                   :dst false | ||||
|                                   :seconds 0 | ||||
|                                   :year 2014 | ||||
|                                   :month-day 0 | ||||
|                                   :hours 20 | ||||
|                                   :week-day 3})) "os/mktime") | ||||
|  | ||||
| (def now (os/time)) | ||||
| (assert (= (os/mktime (os/date now)) now) "UTC os/mktime") | ||||
| (assert (= (os/mktime (os/date now true) true) now) "local os/mktime") | ||||
| (assert (= (os/mktime {:year 1970}) 0) "os/mktime default values") | ||||
|  | ||||
| # OS strftime test | ||||
| # 5cd729c4c | ||||
| (assert (= (os/strftime "%Y-%m-%d %H:%M:%S" 0) "1970-01-01 00:00:00") | ||||
|         "strftime UTC epoch") | ||||
| (assert (= (os/strftime "%Y-%m-%d %H:%M:%S" 1388608200) | ||||
|            "2014-01-01 20:30:00") | ||||
|         "strftime january 2014") | ||||
| (assert (= (try (os/strftime "%%%d%t") ([err] err)) | ||||
|            "invalid conversion specifier '%t'") | ||||
|         "invalid conversion specifier") | ||||
|  | ||||
| # 07db4c530 | ||||
| (os/setenv "TESTENV1" "v1") | ||||
| (os/setenv "TESTENV2" "v2") | ||||
| (assert (= (os/getenv "TESTENV1") "v1") "getenv works") | ||||
| (def environ (os/environ)) | ||||
| (assert (= [(environ "TESTENV1") (environ "TESTENV2")] ["v1" "v2"]) | ||||
|         "environ works") | ||||
|  | ||||
| # Ensure randomness puts n of pred into our buffer eventually | ||||
| # 0ac5b243c | ||||
| (defn cryptorand-check | ||||
|   [n pred] | ||||
|   (def max-attempts 10000) | ||||
|   (var attempts 0) | ||||
|   (while (not= attempts max-attempts) | ||||
|     (def cryptobuf (os/cryptorand 10)) | ||||
|     (when (= n (count pred cryptobuf)) | ||||
|       (break)) | ||||
|     (++ attempts)) | ||||
|   (not= attempts max-attempts)) | ||||
|  | ||||
| (def v (math/rng-int (math/rng (os/time)) 100)) | ||||
| (assert (cryptorand-check 0 |(= $ v)) "cryptorand skips value sometimes") | ||||
| (assert (cryptorand-check 1 |(= $ v)) "cryptorand has value sometimes") | ||||
|  | ||||
| (do | ||||
|   (def buf (buffer/new-filled 1)) | ||||
|   (os/cryptorand 1 buf) | ||||
|   (assert (= (in buf 0) 0) "cryptorand doesn't overwrite buffer") | ||||
|   (assert (= (length buf) 2) "cryptorand appends to buffer")) | ||||
|  | ||||
| # 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)) | ||||
|  | ||||
| (def before (os/clock :monotonic)) | ||||
| (def after (os/clock :monotonic)) | ||||
| (assert (>= after before) "monotonic clock is monotonic") | ||||
|  | ||||
| # Perm strings | ||||
| # a0d61e45d | ||||
| (assert (= (os/perm-int "rwxrwxrwx") 8r777) "perm 1") | ||||
| (assert (= (os/perm-int "rwxr-xr-x") 8r755) "perm 2") | ||||
| (assert (= (os/perm-int "rw-r--r--") 8r644) "perm 3") | ||||
|  | ||||
| (assert (= (band (os/perm-int "rwxrwxrwx") 8r077) 8r077) "perm 4") | ||||
| (assert (= (band (os/perm-int "rwxr-xr-x") 8r077) 8r055) "perm 5") | ||||
| (assert (= (band (os/perm-int "rw-r--r--") 8r077) 8r044) "perm 6") | ||||
|  | ||||
| (assert (= (os/perm-string 8r777) "rwxrwxrwx") "perm 7") | ||||
| (assert (= (os/perm-string 8r755) "rwxr-xr-x") "perm 8") | ||||
| (assert (= (os/perm-string 8r644) "rw-r--r--") "perm 9") | ||||
|  | ||||
| # os/execute with environment variables | ||||
| # issue #636 - 7e2c433ab | ||||
| (assert (= 0 (os/execute [;run janet "-e" "(+ 1 2 3)"] :pe | ||||
|                          (merge (os/environ) {"HELLO" "WORLD"}))) | ||||
|         "os/execute with env") | ||||
|  | ||||
| # os/execute regressions | ||||
| # 427f7c362 | ||||
| (for i 0 10 | ||||
|   (assert (= i (os/execute [;run janet "-e" | ||||
|                             (string/format "(os/exit %d)" i)] :p)) | ||||
|           (string "os/execute " i))) | ||||
|  | ||||
| # os/execute IO redirection | ||||
| (assert-no-error "IO redirection" | ||||
|                  (defn devnull [] | ||||
|                    (def os (os/which)) | ||||
|                    (def path (if (or (= os :mingw) (= os :windows)) | ||||
|                                "NUL" | ||||
|                                "/dev/null")) | ||||
|                    (os/open path :w)) | ||||
|                  (with [dn (devnull)] | ||||
|                    (os/execute [;run janet | ||||
|                                 "-e" | ||||
|                                 "(print :foo) (eprint :bar)"] | ||||
|                                :px | ||||
|                                {:out dn :err dn}))) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,192 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # 7e46ead2f | ||||
| (assert (not false) "false literal") | ||||
| (assert true "true literal") | ||||
| (assert (not nil) "nil literal") | ||||
|  | ||||
| (assert (= '(1 2 3) (quote (1 2 3)) (tuple 1 2 3)) "quote shorthand") | ||||
|  | ||||
| # String literals | ||||
| # 45f8db0 | ||||
| (assert (= "abcd" "\x61\x62\x63\x64") "hex escapes") | ||||
| (assert (= "\e" "\x1B") "escape character") | ||||
| (assert (= "\x09" "\t") "tab character") | ||||
|  | ||||
| # Long strings | ||||
| # 7e6342720 | ||||
| (assert (= "hello, world" `hello, world`) "simple long string") | ||||
| (assert (= "hello, \"world\"" `hello, "world"`) | ||||
|         "long string with embedded quotes") | ||||
| (assert (= "hello, \\\\\\ \"world\"" `hello, \\\ "world"`) | ||||
|         "long string with embedded quotes and backslashes") | ||||
|  | ||||
| # | ||||
| # Longstring indentation | ||||
| # | ||||
| # 7aa4241 | ||||
| (defn reindent | ||||
|   "Reindent the contents of a longstring as the Janet parser would. | ||||
|   This include removing leading and trailing newlines." | ||||
|   [text indent] | ||||
|  | ||||
|   # Detect minimum indent | ||||
|   (var rewrite true) | ||||
|   (each index (string/find-all "\n" text) | ||||
|     (for i (+ index 1) (+ index indent 1) | ||||
|       (case (get text i) | ||||
|         nil (break) | ||||
|         (chr "\n") (break) | ||||
|         (chr " ") nil | ||||
|         (set rewrite false)))) | ||||
|  | ||||
|   # Only re-indent if no dedented characters. | ||||
|   (def str | ||||
|     (if rewrite | ||||
|       (peg/replace-all ~(* "\n" (between 0 ,indent " ")) "\n" text) | ||||
|       text)) | ||||
|  | ||||
|   (def first-nl (= (chr "\n") (first str))) | ||||
|   (def last-nl (= (chr "\n") (last str))) | ||||
|   (string/slice str (if first-nl 1 0) (if last-nl -2))) | ||||
|  | ||||
| (defn reindent-reference | ||||
|   "Same as reindent but use parser functionality. Useful for | ||||
|   validating conformance." | ||||
|   [text indent] | ||||
|   (if (empty? text) (break text)) | ||||
|   (def source-code | ||||
|     (string (string/repeat " " indent) "``````" | ||||
|             text | ||||
|             "``````")) | ||||
|   (parse source-code)) | ||||
|  | ||||
| (var indent-counter 0) | ||||
| (defn check-indent | ||||
|   [text indent] | ||||
|   (++ indent-counter) | ||||
|   (let [a (reindent text indent) | ||||
|         b (reindent-reference text indent)] | ||||
|     (assert (= a b) | ||||
|             (string "indent " indent-counter " (indent=" indent ")")))) | ||||
|  | ||||
| (check-indent "" 0) | ||||
| (check-indent "\n" 0) | ||||
| (check-indent "\n" 1) | ||||
| (check-indent "\n\n" 0) | ||||
| (check-indent "\n\n" 1) | ||||
| (check-indent "\nHello, world!" 0) | ||||
| (check-indent "\nHello, world!" 1) | ||||
| (check-indent "Hello, world!" 0) | ||||
| (check-indent "Hello, world!" 1) | ||||
| (check-indent "\n    Hello, world!" 4) | ||||
| (check-indent "\n    Hello, world!\n" 4) | ||||
| (check-indent "\n    Hello, world!\n   " 4) | ||||
| (check-indent "\n    Hello, world!\n    " 4) | ||||
| (check-indent "\n    Hello, world!\n   dedented text\n    " 4) | ||||
| (check-indent "\n    Hello, world!\n    indented text\n    " 4) | ||||
|  | ||||
| # Symbols with @ character | ||||
| # d68eae9 | ||||
| (def @ 1) | ||||
| (assert (= @ 1) "@ symbol") | ||||
| (def @-- 2) | ||||
| (assert (= @-- 2) "@-- symbol") | ||||
| (def @hey 3) | ||||
| (assert (= @hey 3) "@hey symbol") | ||||
|  | ||||
| # Parser clone | ||||
| # 43520ac67 | ||||
| (def p (parser/new)) | ||||
| (assert (= 7 (parser/consume p "(1 2 3 ")) "parser 1") | ||||
| (def p2 (parser/clone p)) | ||||
| (parser/consume p2 ") 1 ") | ||||
| (parser/consume p ") 1 ") | ||||
| (assert (deep= (parser/status p) (parser/status p2)) "parser 2") | ||||
| (assert (deep= (parser/state p) (parser/state p2)) "parser 3") | ||||
|  | ||||
| # Parser errors | ||||
| # 976dfc719 | ||||
| (defn parse-error [input] | ||||
|   (def p (parser/new)) | ||||
|   (parser/consume p input) | ||||
|   (parser/error p)) | ||||
|  | ||||
| # Invalid utf-8 sequences | ||||
| (assert (not= nil (parse-error @"\xc3\x28")) "reject invalid utf-8 symbol") | ||||
| (assert (not= nil (parse-error @":\xc3\x28")) "reject invalid utf-8 keyword") | ||||
|  | ||||
| # Parser line and column numbers | ||||
| # 77b79e989 | ||||
| (defn parser-location [input &opt location] | ||||
|   (def p (parser/new)) | ||||
|   (parser/consume p input) | ||||
|   (if location | ||||
|     (parser/where p ;location) | ||||
|     (parser/where p))) | ||||
|  | ||||
| (assert (= [1 7] (parser-location @"(+ 1 2)")) "parser location 1") | ||||
| (assert (= [5 7] (parser-location @"(+ 1 2)" [5])) "parser location 2") | ||||
| (assert (= [10 10] (parser-location @"(+ 1 2)" [10 10])) "parser location 3") | ||||
|  | ||||
| # Issue #861 - should be valgrind clean | ||||
| # 39c6be7cb | ||||
| (def step1 "(a b c d)\n") | ||||
| (def step2 "(a b)\n") | ||||
| (def p1 (parser/new)) | ||||
| (parser/state p1) | ||||
| (parser/consume p1 step1) | ||||
| (loop [v :iterate (parser/produce p1)]) | ||||
| (parser/state p1) | ||||
| (def p2 (parser/clone p1)) | ||||
| (parser/state p2) | ||||
| (parser/consume p2 step2) | ||||
| (loop [v :iterate (parser/produce p2)]) | ||||
| (parser/state p2) | ||||
|  | ||||
| # parser delimiter errors | ||||
| (defn test-error [delim fmt] | ||||
|   (def p (parser/new)) | ||||
|   (parser/consume p delim) | ||||
|   (parser/eof p) | ||||
|   (def msg (string/format fmt delim)) | ||||
|   (assert (= (parser/error p) msg) "delimiter error")) | ||||
| (each c [ "(" "{" "[" "\"" "``" ] | ||||
|   (test-error c "unexpected end of source, %s opened at line 1, column 1")) | ||||
|  | ||||
| # parser/insert | ||||
| (def p (parser/new)) | ||||
| (parser/consume p "(") | ||||
| (parser/insert p "hello") | ||||
| (parser/consume p ")") | ||||
| (assert (= (parser/produce p) ["hello"])) | ||||
|  | ||||
| (def p (parser/new)) | ||||
| (parser/consume p `("hel`) | ||||
| (parser/insert p `lo`) | ||||
| (parser/consume p `")`) | ||||
| (assert (= (parser/produce p) ["hello"])) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,664 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Peg | ||||
|  | ||||
| # 83f4a11bf | ||||
| (defn check-match | ||||
|   [pat text should-match] | ||||
|   (def result (peg/match pat text)) | ||||
|   (assert (= (not should-match) (not result)) | ||||
|           (string "check-match " text))) | ||||
|  | ||||
| # 798c88b4c | ||||
| (defn check-deep | ||||
|   [pat text what] | ||||
|   (def result (peg/match pat text)) | ||||
|   (assert (deep= result what) (string "check-deep " text))) | ||||
|  | ||||
| # Just numbers | ||||
| # 83f4a11bf | ||||
| (check-match '(* 4 -1) "abcd" true) | ||||
| (check-match '(* 4 -1) "abc" false) | ||||
| (check-match '(* 4 -1) "abcde" false) | ||||
|  | ||||
| # Simple pattern | ||||
| # 83f4a11bf | ||||
| (check-match '(* (some (range "az" "AZ")) -1) "hello" true) | ||||
| (check-match '(* (some (range "az" "AZ")) -1) "hello world" false) | ||||
| (check-match '(* (some (range "az" "AZ")) -1) "1he11o" false) | ||||
| (check-match '(* (some (range "az" "AZ")) -1) "" false) | ||||
|  | ||||
| # Pre compile | ||||
| # ff0d3a008 | ||||
| (def pegleg (peg/compile '{:item "abc" :main (* :item "," :item -1)})) | ||||
|  | ||||
| (peg/match pegleg "abc,abc") | ||||
|  | ||||
| # Bad Grammars | ||||
| # 192705113 | ||||
| (assert-error "peg/compile error 1" (peg/compile nil)) | ||||
| (assert-error "peg/compile error 2" (peg/compile @{})) | ||||
| (assert-error "peg/compile error 3" (peg/compile '{:a "abc" :b "def"})) | ||||
| (assert-error "peg/compile error 4" (peg/compile '(blarg "abc"))) | ||||
| (assert-error "peg/compile error 5" (peg/compile '(1 2 3))) | ||||
|  | ||||
| # IP address | ||||
| # 40845b5c1 | ||||
| (def ip-address | ||||
|   '{:d (range "09") | ||||
|     :0-4 (range "04") | ||||
|     :0-5 (range "05") | ||||
|     :byte (+ | ||||
|             (* "25" :0-5) | ||||
|             (* "2" :0-4 :d) | ||||
|             (* "1" :d :d) | ||||
|             (between 1 2 :d)) | ||||
|     :main (* :byte "." :byte "." :byte "." :byte)}) | ||||
|  | ||||
| (check-match ip-address "10.240.250.250" true) | ||||
| (check-match ip-address "0.0.0.0" true) | ||||
| (check-match ip-address "1.2.3.4" true) | ||||
| (check-match ip-address "256.2.3.4" false) | ||||
| (check-match ip-address "256.2.3.2514" false) | ||||
|  | ||||
| # Substitution test with peg | ||||
| # d7626f8c5 | ||||
| (def grammar '(accumulate (any (+ (/ "dog" "purple panda") (<- 1))))) | ||||
| (defn try-grammar [text] | ||||
|   (assert (= (string/replace-all "dog" "purple panda" text) | ||||
|              (0 (peg/match grammar text))) text)) | ||||
|  | ||||
| (try-grammar "i have a dog called doug the dog. he is good.") | ||||
| (try-grammar "i have a dog called doug the dog. he is a good boy.") | ||||
| (try-grammar "i have a dog called doug the do") | ||||
| (try-grammar "i have a dog called doug the dog") | ||||
| (try-grammar "i have a dog called doug the dogg") | ||||
| (try-grammar "i have a dog called doug the doggg") | ||||
| (try-grammar "i have a dog called doug the dogggg") | ||||
|  | ||||
| # Peg CSV test | ||||
| # 798c88b4c | ||||
| (def csv | ||||
|   '{:field (+ | ||||
|             (* `"` (% (any (+ (<- (if-not `"` 1)) | ||||
|                               (* (constant `"`) `""`)))) `"`) | ||||
|             (<- (any (if-not (set ",\n") 1)))) | ||||
|     :main (* :field (any (* "," :field)) (+ "\n" -1))}) | ||||
|  | ||||
| (defn check-csv | ||||
|   [str res] | ||||
|   (check-deep csv str res)) | ||||
|  | ||||
| (check-csv "1,2,3" @["1" "2" "3"]) | ||||
| (check-csv "1,\"2\",3" @["1" "2" "3"]) | ||||
| (check-csv ``1,"1""",3`` @["1" "1\"" "3"]) | ||||
|  | ||||
| # Nested Captures | ||||
| # 798c88b4c | ||||
| (def grmr '(capture (* (capture "a") (capture 1) (capture "c")))) | ||||
| (check-deep grmr "abc" @["a" "b" "c" "abc"]) | ||||
| (check-deep grmr "acc" @["a" "c" "c" "acc"]) | ||||
|  | ||||
| # Functions in grammar | ||||
| # 798c88b4c | ||||
| (def grmr-triple ~(% (any (/ (<- 1) ,(fn [x] (string x x x)))))) | ||||
| (check-deep grmr-triple "abc" @["aaabbbccc"]) | ||||
| (check-deep grmr-triple "" @[""]) | ||||
| (check-deep grmr-triple " " @["   "]) | ||||
|  | ||||
| (def counter ~(/ (group (any (<- 1))) ,length)) | ||||
| (check-deep counter "abcdefg" @[7]) | ||||
|  | ||||
| # Capture Backtracking | ||||
| # ff0d3a008 | ||||
| (check-deep '(+ (* (capture "c") "d") "ce") "ce" @[]) | ||||
|  | ||||
| # Matchtime capture | ||||
| # 192705113 | ||||
| (def scanner (peg/compile ~(cmt (capture (some 1)) ,scan-number))) | ||||
|  | ||||
| (check-deep scanner "123" @[123]) | ||||
| (check-deep scanner "0x86" @[0x86]) | ||||
| (check-deep scanner "-1.3e-7" @[-1.3e-7]) | ||||
| (check-deep scanner "123A" nil) | ||||
|  | ||||
| # Recursive grammars | ||||
| # 170e785b7 | ||||
| (def g '{:main (+ (* "a" :main "b") "c")}) | ||||
|  | ||||
| (check-match g "c" true) | ||||
| (check-match g "acb" true) | ||||
| (check-match g "aacbb" true) | ||||
| (check-match g "aadbb" false) | ||||
|  | ||||
| # Back reference | ||||
| # d0ec89c7c | ||||
| (def wrapped-string | ||||
|   ~{:pad (any "=") | ||||
|     :open (* "[" (<- :pad :n) "[") | ||||
|     :close (* "]" (cmt (* (-> :n) (<- :pad)) ,=) "]") | ||||
|     :main (* :open (any (if-not :close 1)) :close -1)}) | ||||
|  | ||||
| (check-match wrapped-string "[[]]" true) | ||||
| (check-match wrapped-string "[==[a]==]" true) | ||||
| (check-match wrapped-string "[==[]===]" false) | ||||
| (check-match wrapped-string "[[blark]]" true) | ||||
| (check-match wrapped-string "[[bl[ark]]" true) | ||||
| (check-match wrapped-string "[[bl]rk]]" true) | ||||
| (check-match wrapped-string "[[bl]rk]] " false) | ||||
| (check-match wrapped-string "[=[bl]]rk]=] " false) | ||||
| (check-match wrapped-string "[=[bl]==]rk]=] " false) | ||||
| (check-match wrapped-string "[===[]==]===]" true) | ||||
|  | ||||
| (def janet-longstring | ||||
|   ~{:delim (some "`") | ||||
|     :open (capture :delim :n) | ||||
|     :close (cmt (* (not (> -1 "`")) (-> :n) (<- (backmatch :n))) ,=) | ||||
|     :main (* :open (any (if-not :close 1)) :close -1)}) | ||||
|  | ||||
| (check-match janet-longstring "`john" false) | ||||
| (check-match janet-longstring "abc" false) | ||||
| (check-match janet-longstring "` `" true) | ||||
| (check-match janet-longstring "`  `" true) | ||||
| (check-match janet-longstring "``  ``" true) | ||||
| (check-match janet-longstring "``` `` ```" true) | ||||
| (check-match janet-longstring "``  ```" false) | ||||
| (check-match janet-longstring "`a``b`" false) | ||||
|  | ||||
| # Line and column capture | ||||
| # 776ce586b | ||||
| (def line-col (peg/compile '(any (* (line) (column) 1)))) | ||||
| (check-deep line-col "abcd" @[1 1 1 2 1 3 1 4]) | ||||
| (check-deep line-col "" @[]) | ||||
| (check-deep line-col "abcd\n" @[1 1 1 2 1 3 1 4 1 5]) | ||||
| (check-deep line-col "abcd\nz" @[1 1 1 2 1 3 1 4 1 5 2 1]) | ||||
|  | ||||
| # Backmatch | ||||
| # 711fe64a5 | ||||
| (def backmatcher-1 '(* (capture (any "x") :1) "y" (backmatch :1) -1)) | ||||
|  | ||||
| (check-match backmatcher-1 "y" true) | ||||
| (check-match backmatcher-1 "xyx" true) | ||||
| (check-match backmatcher-1 "xxxxxxxyxxxxxxx" true) | ||||
| (check-match backmatcher-1 "xyxx" false) | ||||
| (check-match backmatcher-1 (string (string/repeat "x" 73) "y") false) | ||||
| (check-match backmatcher-1 (string (string/repeat "x" 10000) "y") false) | ||||
| (check-match backmatcher-1 (string (string/repeat "x" 10000) "y" | ||||
|                                    (string/repeat "x" 10000)) true) | ||||
|  | ||||
| (def backmatcher-2 '(* '(any "x") "y" (backmatch) -1)) | ||||
|  | ||||
| (check-match backmatcher-2 "y" true) | ||||
| (check-match backmatcher-2 "xyx" true) | ||||
| (check-match backmatcher-2 "xxxxxxxyxxxxxxx" true) | ||||
| (check-match backmatcher-2 "xyxx" false) | ||||
| (check-match backmatcher-2 (string (string/repeat "x" 73) "y") false) | ||||
| (check-match backmatcher-2 (string (string/repeat "x" 10000) "y") false) | ||||
| (check-match backmatcher-2 (string (string/repeat "x" 10000) "y" | ||||
|                                    (string/repeat "x" 10000)) true) | ||||
|  | ||||
| (def longstring-2 '(* '(some "`") | ||||
|                       (some (if-not (backmatch) 1)) | ||||
|                       (backmatch) -1)) | ||||
|  | ||||
| (check-match longstring-2 "`john" false) | ||||
| (check-match longstring-2 "abc" false) | ||||
| (check-match longstring-2 "` `" true) | ||||
| (check-match longstring-2 "`  `" true) | ||||
| (check-match longstring-2 "``  ``" true) | ||||
| (check-match longstring-2 "``` `` ```" true) | ||||
| (check-match longstring-2 "``  ```" false) | ||||
|  | ||||
| # Optional | ||||
| # 4eeadd746 | ||||
| (check-match '(* (opt "hi") -1) "" true) | ||||
| (check-match '(* (opt "hi") -1) "hi" true) | ||||
| (check-match '(* (opt "hi") -1) "no" false) | ||||
| (check-match '(* (? "hi") -1) "" true) | ||||
| (check-match '(* (? "hi") -1) "hi" true) | ||||
| (check-match '(* (? "hi") -1) "no" false) | ||||
|  | ||||
| # Drop | ||||
| # b4934cedd | ||||
| (check-deep '(drop '"hello") "hello" @[]) | ||||
| (check-deep '(drop "hello") "hello" @[]) | ||||
|  | ||||
| # Add bytecode verification for peg unmarshaling | ||||
| # e88a9af2f | ||||
| # This should be valgrind clean. | ||||
| (var pegi 3) | ||||
| (defn marshpeg [p] | ||||
|   (assert (-> p peg/compile marshal unmarshal) | ||||
|           (string "peg marshal " (++ pegi)))) | ||||
| (marshpeg '(* 1 2 (set "abcd") "asdasd" (+ "." 3))) | ||||
| (marshpeg '(% (* (+ 1 2 3) (* "drop" "bear") '"hi"))) | ||||
| (marshpeg '(> 123 "abcd")) | ||||
| (marshpeg '{:main (* 1 "hello" :main)}) | ||||
| (marshpeg '(range "AZ")) | ||||
| (marshpeg '(if-not "abcdf" 123)) | ||||
| (marshpeg '(error ($))) | ||||
| (marshpeg '(* "abcd" (constant :hi))) | ||||
| (marshpeg ~(/ "abc" ,identity)) | ||||
| (marshpeg '(if-not "abcdf" 123)) | ||||
| (marshpeg ~(cmt "abcdf" ,identity)) | ||||
| (marshpeg '(group "abc")) | ||||
|  | ||||
| # Peg swallowing errors | ||||
| # 159651117 | ||||
| (assert (try (peg/match ~(/ '1 ,(fn [x] (nil x))) "x") ([err] err)) | ||||
|         "errors should not be swallowed") | ||||
| (assert (try ((fn [x] (nil x))) ([err] err)) | ||||
|         "errors should not be swallowed 2") | ||||
|  | ||||
| # Check for bad memoization (+ :a) should mean different things in | ||||
| # different contexts | ||||
| # 8bc8709d0 | ||||
| (def redef-a | ||||
|   ~{:a "abc" | ||||
|     :c (+ :a) | ||||
|     :main (* :c {:a "def" :main (+ :a)} -1)}) | ||||
|  | ||||
| (check-match redef-a "abcdef" true) | ||||
| (check-match redef-a "abcabc" false) | ||||
| (check-match redef-a "defdef" false) | ||||
|  | ||||
| # 54a04b589 | ||||
| (def redef-b | ||||
|   ~{:pork {:pork "beef" :main (+ -1 (* 1 :pork))} | ||||
|     :main :pork}) | ||||
|  | ||||
| (check-match redef-b "abeef" true) | ||||
| (check-match redef-b "aabeef" false) | ||||
| (check-match redef-b "aaaaaa" false) | ||||
|  | ||||
| # Integer parsing | ||||
| # 45feb5548 | ||||
| (check-deep '(int 1) "a" @[(chr "a")]) | ||||
| (check-deep '(uint 1) "a" @[(chr "a")]) | ||||
| (check-deep '(int-be 1) "a" @[(chr "a")]) | ||||
| (check-deep '(uint-be 1) "a" @[(chr "a")]) | ||||
| (check-deep '(int 1) "\xFF" @[-1]) | ||||
| (check-deep '(uint 1) "\xFF" @[255]) | ||||
| (check-deep '(int-be 1) "\xFF" @[-1]) | ||||
| (check-deep '(uint-be 1) "\xFF" @[255]) | ||||
| (check-deep '(int 2) "\xFF\x7f" @[0x7fff]) | ||||
| (check-deep '(int-be 2) "\x7f\xff" @[0x7fff]) | ||||
| (check-deep '(uint 2) "\xff\x7f" @[0x7fff]) | ||||
| (check-deep '(uint-be 2) "\x7f\xff" @[0x7fff]) | ||||
| (check-deep '(uint-be 2) "\x7f\xff" @[0x7fff]) | ||||
| (when-let [u64 int/u64 | ||||
|            i64 int/s64] | ||||
|   (check-deep '(uint 8) "\xff\x7f\x00\x00\x00\x00\x00\x00" @[(u64 0x7fff)]) | ||||
|   (check-deep '(int 8) "\xff\x7f\x00\x00\x00\x00\x00\x00" @[(i64 0x7fff)]) | ||||
|   (check-deep '(uint 7) "\xff\x7f\x00\x00\x00\x00\x00" @[(u64 0x7fff)]) | ||||
|   (check-deep '(int 7) "\xff\x7f\x00\x00\x00\x00\x00" @[(i64 0x7fff)])) | ||||
|  | ||||
| (check-deep '(* (int 2) -1) "123" nil) | ||||
|  | ||||
| # to/thru bug | ||||
| # issue #640 - 742469a8b | ||||
| (check-deep '(to -1) "aaaa" @[]) | ||||
| (check-deep '(thru -1) "aaaa" @[]) | ||||
| (check-deep ''(to -1) "aaaa" @["aaaa"]) | ||||
| (check-deep ''(thru -1) "aaaa" @["aaaa"]) | ||||
| (check-deep '(to "b") "aaaa" nil) | ||||
| (check-deep '(thru "b") "aaaa" nil) | ||||
|  | ||||
| # unref | ||||
| # 96513665d | ||||
| (def grammar | ||||
|   (peg/compile | ||||
|     ~{:main (* :tagged -1) | ||||
|       :tagged (unref (replace (* :open-tag :value :close-tag) ,struct)) | ||||
|       :open-tag (* (constant :tag) "<" (capture :w+ :tag-name) ">") | ||||
|       :value (* (constant :value) (group (any (+ :tagged :untagged)))) | ||||
|       :close-tag (* "</" (backmatch :tag-name) ">") | ||||
|       :untagged (capture (any (if-not "<" 1)))})) | ||||
| (check-deep grammar "<p><em>foobar</em></p>" | ||||
|             @[{:tag "p" :value @[{:tag "em" :value @["foobar"]}]}]) | ||||
| (check-deep grammar "<p>foobar</p>" @[{:tag "p" :value @["foobar"]}]) | ||||
|  | ||||
| # Using a large test grammar | ||||
| # cf05ff610 | ||||
| (def- specials {'fn true | ||||
|                'var true | ||||
|                'do true | ||||
|                'while true | ||||
|                'def true | ||||
|                'splice true | ||||
|                'set true | ||||
|                'unquote true | ||||
|                'quasiquote true | ||||
|                'quote true | ||||
|                'if true}) | ||||
|  | ||||
| (defn- check-number [text] (and (scan-number text) text)) | ||||
|  | ||||
| (defn capture-sym | ||||
|   [text] | ||||
|   (def sym (symbol text)) | ||||
|   [(if (or (root-env sym) (specials sym)) :coresym :symbol) text]) | ||||
|  | ||||
| (def grammar | ||||
|   ~{:ws (set " \v\t\r\f\n\0") | ||||
|     :readermac (set "';~,") | ||||
|     :symchars (+ (range "09" "AZ" "az" "\x80\xFF") | ||||
|                  (set "!$%&*+-./:<?=>@^_|")) | ||||
|     :token (some :symchars) | ||||
|     :hex (range "09" "af" "AF") | ||||
|     :escape (* "\\" (+ (set `"'0?\abefnrtvz`) | ||||
|                        (* "x" :hex :hex) | ||||
|                        (error (constant "bad hex escape")))) | ||||
|     :comment (/ '(* "#" (any (if-not (+ "\n" -1) 1))) (constant :comment)) | ||||
|     :symbol (/ ':token ,capture-sym) | ||||
|     :keyword (/ '(* ":" (any :symchars)) (constant :keyword)) | ||||
|     :constant (/ '(+ "true" "false" "nil") (constant :constant)) | ||||
|     :bytes (* "\"" (any (+ :escape (if-not "\"" 1))) "\"") | ||||
|     :string (/ ':bytes (constant :string)) | ||||
|     :buffer (/ '(* "@" :bytes) (constant :string)) | ||||
|     :long-bytes {:delim (some "`") | ||||
|                  :open (capture :delim :n) | ||||
|                  :close (cmt (* (not (> -1 "`")) (-> :n) '(backmatch :n)) | ||||
|                              ,=) | ||||
|                  :main (drop (* :open (any (if-not :close 1)) :close))} | ||||
|     :long-string (/ ':long-bytes (constant :string)) | ||||
|     :long-buffer (/ '(* "@" :long-bytes) (constant :string)) | ||||
|     :number (/ (cmt ':token ,check-number) (constant :number)) | ||||
|     :raw-value (+ :comment :constant :number :keyword | ||||
|                   :string :buffer :long-string :long-buffer | ||||
|                   :parray :barray :ptuple :btuple :struct :dict :symbol) | ||||
|     :value (* (? '(some (+ :ws :readermac))) :raw-value '(any :ws)) | ||||
|     :root (any :value) | ||||
|     :root2 (any (* :value :value)) | ||||
|     :ptuple (* '"(" :root (+ '")" (error ""))) | ||||
|     :btuple (* '"[" :root (+ '"]" (error ""))) | ||||
|     :struct (* '"{" :root2 (+ '"}" (error ""))) | ||||
|     :parray (* '"@" :ptuple) | ||||
|     :barray (* '"@" :btuple) | ||||
|     :dict (* '"@"  :struct) | ||||
|     :main (+ :root (error ""))}) | ||||
|  | ||||
| (def p (peg/compile grammar)) | ||||
|  | ||||
| # Just make sure is valgrind clean. | ||||
| (def p (-> p make-image load-image)) | ||||
|  | ||||
| (assert (peg/match p "abc") "complex peg grammar 1") | ||||
| (assert (peg/match p "[1 2 3 4]") "complex peg grammar 2") | ||||
|  | ||||
| ### | ||||
| ### Compiling brainfuck to Janet. | ||||
| ### | ||||
| # 20d5d560f | ||||
| (def- bf-peg | ||||
|   "Peg for compiling brainfuck into a Janet source ast." | ||||
|   (peg/compile | ||||
|     ~{:+ (/ '(some "+") ,(fn [x] ~(+= (DATA POS) ,(length x)))) | ||||
|       :- (/ '(some "-") ,(fn [x] ~(-= (DATA POS) ,(length x)))) | ||||
|       :> (/ '(some ">") ,(fn [x] ~(+= POS ,(length x)))) | ||||
|       :< (/ '(some "<") ,(fn [x] ~(-= POS ,(length x)))) | ||||
|       :. (* "." (constant (prinf "%c" (get DATA POS)))) | ||||
|       :loop (/ (* "[" :main "]") ,(fn [& captures] | ||||
|                                     ~(while (not= (get DATA POS) 0) | ||||
|                                        ,;captures))) | ||||
|       :main (any (+ :s :loop :+ :- :> :< :.))})) | ||||
|  | ||||
| (defn bf | ||||
|   "Run brainfuck." | ||||
|   [text] | ||||
|   (eval | ||||
|     ~(let [DATA (array/new-filled 100 0)] | ||||
|        (var POS 50) | ||||
|        ,;(peg/match bf-peg text)))) | ||||
|  | ||||
| (defn test-bf | ||||
|   "Test some bf for expected output." | ||||
|   [input output] | ||||
|   (def b @"") | ||||
|   (with-dyns [:out b] | ||||
|     (bf input)) | ||||
|   (assert (= (string output) (string b)) | ||||
|           (string "bf input '" | ||||
|                   input | ||||
|                   "' failed, expected " | ||||
|                   (describe output) | ||||
|                   ", got " | ||||
|                   (describe (string b)) | ||||
|                   "."))) | ||||
|  | ||||
| (test-bf (string "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]" | ||||
|                  ">>.>---.+++++++..+++.>>.<-.<.+++.------.--------" | ||||
|                  ".>>+.>++.") "Hello World!\n") | ||||
|  | ||||
| (test-bf (string ">++++++++" | ||||
|                  "[-<+++++++++>]<.>>+>-[+]++>++>+++[>[->+++<<+++>]<<]" | ||||
|                  ">-----.>->+++..+++.>-.<<+[>[+>+]>>]<--------------" | ||||
|                  ".>>.+++.------.--------.>+.>+.") | ||||
|          "Hello World!\n") | ||||
|  | ||||
| (test-bf (string "+[+[<<<+>>>>]+<-<-<<<+<++]<<.<++.<++..+++.<<++.<---" | ||||
|                  ".>>.>.+++.------.>-.>>--.") | ||||
|          "Hello, World!") | ||||
|  | ||||
| # Regression test | ||||
| # issue #300 - 714bd61d5 | ||||
| # Just don't segfault | ||||
| (assert (peg/match '{:main (replace "S" {"S" :spade})} "S7") | ||||
|         "regression #300") | ||||
|  | ||||
| # Lenprefix rule | ||||
| # 8b5bcaee3 | ||||
| (def peg (peg/compile ~(* (lenprefix (/ (* '(any (if-not ":" 1)) ":") | ||||
|                                         ,scan-number) 1) -1))) | ||||
|  | ||||
| (assert (peg/match peg "5:abcde") "lenprefix 1") | ||||
| (assert (not (peg/match peg "5:abcdef")) "lenprefix 2") | ||||
| (assert (not (peg/match peg "5:abcd")) "lenprefix 3") | ||||
|  | ||||
| # Packet capture | ||||
| # 8b5bcaee3 | ||||
| (def peg2 | ||||
|   (peg/compile | ||||
|     ~{# capture packet length in tag :header-len | ||||
|       :packet-header (* (/ ':d+ ,scan-number :header-len) ":") | ||||
|  | ||||
|       # capture n bytes from a backref :header-len | ||||
|       :packet-body '(lenprefix (-> :header-len) 1) | ||||
|  | ||||
|       # header, followed by body, and drop the :header-len capture | ||||
|       :packet (/ (* :packet-header :packet-body) ,|$1) | ||||
|  | ||||
|       # any exact seqence of packets (no extra characters) | ||||
|       :main (* (any :packet) -1)})) | ||||
|  | ||||
| (assert (deep= @["a" "bb" "ccc"] (peg/match peg2 "1:a2:bb3:ccc")) | ||||
|         "lenprefix 4") | ||||
| (assert (deep= @["a" "bb" "cccccc"] (peg/match peg2 "1:a2:bb6:cccccc")) | ||||
|         "lenprefix 5") | ||||
| (assert (= nil (peg/match peg2 "1:a2:bb:5:cccccc")) "lenprefix 6") | ||||
| (assert (= nil (peg/match peg2 "1:a2:bb:7:cccccc")) "lenprefix 7") | ||||
|  | ||||
| # Issue #412 | ||||
| # 677737d34 | ||||
| (assert (peg/match '(* "a" (> -1 "a") "b") "abc") | ||||
|         "lookhead does not move cursor") | ||||
|  | ||||
| # 6d096551f | ||||
| (def peg3 | ||||
|   ~{:main (* "(" (thru ")"))}) | ||||
|  | ||||
| (def peg4 (peg/compile ~(* (thru "(") '(to ")")))) | ||||
|  | ||||
| (assert (peg/match peg3 "(12345)") "peg thru 1") | ||||
| (assert (not (peg/match peg3 " (12345)")) "peg thru 2") | ||||
| (assert (not (peg/match peg3 "(12345")) "peg thru 3") | ||||
|  | ||||
| (assert (= "abc" (0 (peg/match peg4 "123(abc)"))) "peg thru/to 1") | ||||
| (assert (= "abc" (0 (peg/match peg4 "(abc)"))) "peg thru/to 2") | ||||
| (assert (not (peg/match peg4 "123(abc")) "peg thru/to 3") | ||||
|  | ||||
| # 86e12369b | ||||
| (def peg5 (peg/compile [3 "abc"])) | ||||
|  | ||||
| (assert (:match peg5 "abcabcabc") "repeat alias 1") | ||||
| (assert (:match peg5 "abcabcabcac") "repeat alias 2") | ||||
| (assert (not (:match peg5 "abcabc")) "repeat alias 3") | ||||
|  | ||||
| # Peg find and find-all | ||||
| # c26f57362 | ||||
| (def p "/usr/local/bin/janet") | ||||
| (assert (= (peg/find '"n/" p) 13) "peg find 1") | ||||
| (assert (not (peg/find '"t/" p)) "peg find 2") | ||||
| (assert (deep= (peg/find-all '"/" p) @[0 4 10 14]) "peg find-all") | ||||
|  | ||||
| # Peg replace and replace-all | ||||
| # e548e1f6e | ||||
| (defn check-replacer | ||||
|   [x y z] | ||||
|   (assert (= (string/replace x y z) (string (peg/replace x y z))) | ||||
|           "replacer test replace") | ||||
|   (assert (= (string/replace-all x y z) (string (peg/replace-all x y z))) | ||||
|           "replacer test replace-all")) | ||||
| (check-replacer "abc" "Z" "abcabcabcabasciabsabc") | ||||
| (check-replacer "abc" "Z" "") | ||||
| (check-replacer "aba" "ZZZZZZ" "ababababababa") | ||||
| (check-replacer "aba" "" "ababababababa") | ||||
|  | ||||
| # 485099fd6 | ||||
| (check-replacer "aba" string/ascii-upper "ababababababa") | ||||
| (check-replacer "aba" 123 "ababababababa") | ||||
| (assert (= (string (peg/replace-all ~(set "ab") string/ascii-upper "abcaa")) | ||||
|            "ABcAA") | ||||
|         "peg/replace-all cfunction") | ||||
| (assert (= (string (peg/replace-all ~(set "ab") |$ "abcaa")) | ||||
|            "abcaa") | ||||
|         "peg/replace-all function") | ||||
|  | ||||
| # 9dc7e8ed3 | ||||
| (defn peg-test [name f peg subst text expected] | ||||
|   (assert (= (string (f peg subst text)) expected) name)) | ||||
|  | ||||
| (peg-test "peg/replace has access to captures" | ||||
|   peg/replace | ||||
|   ~(sequence "." (capture (set "ab"))) | ||||
|   (fn [str char] (string/format "%s -> %s, " str (string/ascii-upper char))) | ||||
|   ".a.b.c" | ||||
|   ".a -> A, .b.c") | ||||
|  | ||||
| (peg-test "peg/replace-all has access to captures" | ||||
|   peg/replace-all | ||||
|   ~(sequence "." (capture (set "ab"))) | ||||
|   (fn [str char] (string/format "%s -> %s, " str (string/ascii-upper char))) | ||||
|   ".a.b.c" | ||||
|   ".a -> A, .b -> B, .c") | ||||
|  | ||||
| # Peg bug | ||||
| # eab5f67c5 | ||||
| (assert (deep= @[] (peg/match '(any 1) @"")) "peg empty pattern 1") | ||||
| (assert (deep= @[] (peg/match '(any 1) (buffer))) "peg empty pattern 2") | ||||
| (assert (deep= @[] (peg/match '(any 1) "")) "peg empty pattern 3") | ||||
| (assert (deep= @[] (peg/match '(any 1) (string))) "peg empty pattern 4") | ||||
| (assert (deep= @[] (peg/match '(* "test" (any 1)) @"test")) | ||||
|         "peg empty pattern 5") | ||||
| (assert (deep= @[] (peg/match '(* "test" (any 1)) (buffer "test"))) | ||||
|         "peg empty pattern 6") | ||||
|  | ||||
| # number pattern | ||||
| # cccbdc164 | ||||
| (assert (deep= @[111] (peg/match '(number :d+) "111")) | ||||
|         "simple number capture 1") | ||||
| (assert (deep= @[255] (peg/match '(number :w+) "0xff")) | ||||
|         "simple number capture 2") | ||||
|  | ||||
| # Marshal and unmarshal pegs | ||||
| # 446ab037b | ||||
| (def p (-> "abcd" peg/compile marshal unmarshal)) | ||||
| (assert (peg/match p "abcd") "peg marshal 1") | ||||
| (assert (peg/match p "abcdefg") "peg marshal 2") | ||||
| (assert (not (peg/match p "zabcdefg")) "peg marshal 3") | ||||
|  | ||||
| # to/thru bug | ||||
| # issue #971 - a895219d2 | ||||
| (def pattern | ||||
|   (peg/compile | ||||
|     '{:dd (sequence :d :d) | ||||
|       :sep (set "/-") | ||||
|       :date (sequence :dd :sep :dd) | ||||
|       :wsep (some (set " \t")) | ||||
|       :entry (group (sequence (capture :date) :wsep (capture :date))) | ||||
|       :main (some (thru :entry))})) | ||||
|  | ||||
| (def alt-pattern | ||||
|   (peg/compile | ||||
|     '{:dd (sequence :d :d) | ||||
|       :sep (set "/-") | ||||
|       :date (sequence :dd :sep :dd) | ||||
|       :wsep (some (set " \t")) | ||||
|       :entry (group (sequence (capture :date) :wsep (capture :date))) | ||||
|       :main (some (choice :entry 1))})) | ||||
|  | ||||
| (def text "1800-10-818-9-818 16/12\n17/12 19/12\n20/12 11/01") | ||||
| (assert (deep= (peg/match pattern text) (peg/match alt-pattern text)) | ||||
|         "to/thru bug #971") | ||||
|  | ||||
| # 14657a7 | ||||
| (def- sym-prefix-peg | ||||
|   (peg/compile | ||||
|     ~{:symchar (+ (range "\x80\xff" "AZ" "az" "09") | ||||
|                   (set "!$%&*+-./:<?=>@^_")) | ||||
|       :anchor (drop (cmt ($) ,|(= $ 0))) | ||||
|       :cap (* (+ (> -1 (not :symchar)) :anchor) (* ($) '(some :symchar))) | ||||
|       :recur (+ :cap (> -1 :recur)) | ||||
|       :main (> -1 :recur)})) | ||||
|  | ||||
| (assert (deep= (peg/match sym-prefix-peg @"123" 3) @[0 "123"]) | ||||
|         "peg lookback") | ||||
| (assert (deep= (peg/match sym-prefix-peg @"1234" 4) @[0 "1234"]) | ||||
|         "peg lookback 2") | ||||
|  | ||||
| # issue #1027 - 356b39c6f | ||||
| (assert (deep= (peg/replace-all '(* (<- 1) 1 (backmatch)) | ||||
|                                 "xxx" "aba cdc efa") | ||||
|                @"xxx xxx efa") | ||||
|         "peg replace-all 1") | ||||
|  | ||||
| # issue #1026 - 9341081a4 | ||||
| (assert (deep= | ||||
|   (peg/match '(not (* (constant 7) "a")) "hello") | ||||
|   @[]) "peg not") | ||||
|  | ||||
| (assert (deep= | ||||
|   (peg/match '(if-not (* (constant 7) "a") "hello") "hello") | ||||
|   @[]) "peg if-not") | ||||
|  | ||||
| (assert (deep= | ||||
|   (peg/match '(if-not (drop (* (constant 7) "a")) "hello") "hello") | ||||
|   @[]) "peg if-not drop") | ||||
|  | ||||
| (assert (deep= | ||||
|   (peg/match '(if (not (* (constant 7) "a")) "hello") "hello") | ||||
|   @[]) "peg if not") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,65 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose & contributors | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Appending buffer to self | ||||
| # 6b76ac3d1 | ||||
| (with-dyns [:out @""] | ||||
|   (prin "abcd") | ||||
|   (prin (dyn :out)) | ||||
|   (prin (dyn :out)) | ||||
|   (assert (deep= (dyn :out) @"abcdabcdabcdabcd") "print buffer to self")) | ||||
|  | ||||
| # Buffer self blitting, check for use after free | ||||
| # bbcfaf128 | ||||
| (def buf1 @"1234567890") | ||||
| (buffer/blit buf1 buf1 -1) | ||||
| (buffer/blit buf1 buf1 -1) | ||||
| (buffer/blit buf1 buf1 -1) | ||||
| (buffer/blit buf1 buf1 -1) | ||||
| (assert (= (string buf1) (string/repeat "1234567890" 16)) | ||||
|         "buffer blit against self") | ||||
|  | ||||
| # Check for bugs with printing self with buffer/format | ||||
| # bbcfaf128 | ||||
| (def buftemp @"abcd") | ||||
| (assert (= (string (buffer/format buftemp "---%p---" buftemp)) | ||||
|            `abcd---@"abcd"---`) "buffer/format on self 1") | ||||
| (def buftemp @"abcd") | ||||
| (assert (= (string (buffer/format buftemp "---%p %p---" buftemp buftemp)) | ||||
|            `abcd---@"abcd" @"abcd"---`) "buffer/format on self 2") | ||||
|  | ||||
| # 5c364e0 | ||||
| (defn check-jdn [x] | ||||
|   (assert (deep= (parse (string/format "%j" x)) x) "round trip jdn")) | ||||
|  | ||||
| (check-jdn 0) | ||||
| (check-jdn nil) | ||||
| (check-jdn []) | ||||
| (check-jdn @[[] [] 1231 9.123123 -123123 0.1231231230001]) | ||||
| (check-jdn -0.123123123123) | ||||
| (check-jdn 12837192371923) | ||||
| (check-jdn "a string") | ||||
| (check-jdn @"a buffer") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,202 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Regression Test #137 | ||||
| # affcb5b45 | ||||
| (def [a b c] (range 10)) | ||||
| (assert (= a 0) "regression #137 (1)") | ||||
| (assert (= b 1) "regression #137 (2)") | ||||
| (assert (= c 2) "regression #137 (3)") | ||||
|  | ||||
| (var [x y z] (range 10)) | ||||
| (assert (= x 0) "regression #137 (4)") | ||||
| (assert (= y 1) "regression #137 (5)") | ||||
| (assert (= z 2) "regression #137 (6)") | ||||
|  | ||||
| # Test destructuring | ||||
| # 23dcfb986 | ||||
| (do | ||||
|   (def test-tab @{:a 1 :b 2}) | ||||
|   (def {:a a :b b} test-tab) | ||||
|   (assert (= a 1) "dictionary destructuring 1") | ||||
|   (assert (= b 2) "dictionary destructuring 2")) | ||||
| (do | ||||
|   (def test-tab @{'a 1 'b 2 3 4}) | ||||
|   (def {'a a 'b b (+ 1 2) c} test-tab) | ||||
|   (assert (= a 1) "dictionary destructuring 3") | ||||
|   (assert (= b 2) "dictionary destructuring 4") | ||||
|   (assert (= c 4) "dictionary destructuring 5 - expression as key")) | ||||
|  | ||||
| # cb5af974a | ||||
| (let [test-tuple [:a :b 1 2]] | ||||
|   (def [a b one two] test-tuple) | ||||
|   (assert (= a :a) "tuple destructuring 1") | ||||
|   (assert (= b :b) "tuple destructuring 2") | ||||
|   (assert (= two 2) "tuple destructuring 3")) | ||||
| (let [test-tuple [:a :b 1 2]] | ||||
|   (def [a & rest] test-tuple) | ||||
|   (assert (= a :a) "tuple destructuring 4 - rest") | ||||
|   (assert (= rest [:b 1 2]) "tuple destructuring 5 - rest")) | ||||
| (do | ||||
|   (def [a b & rest] [:a :b nil :d]) | ||||
|   (assert (= a :a) "tuple destructuring 6 - rest") | ||||
|   (assert (= b :b) "tuple destructuring 7 - rest") | ||||
|   (assert (= rest [nil :d]) "tuple destructuring 8 - rest")) | ||||
|  | ||||
| # 71cffc973 | ||||
| (do | ||||
|   (def [[a b] x & rest] [[1 2] :a :c :b :a]) | ||||
|   (assert (= a 1) "tuple destructuring 9 - rest") | ||||
|   (assert (= b 2) "tuple destructuring 10 - rest") | ||||
|   (assert (= x :a) "tuple destructuring 11 - rest") | ||||
|   (assert (= rest [:c :b :a]) "tuple destructuring 12 - rest")) | ||||
|  | ||||
| # 651e12cfe | ||||
| (do | ||||
|   (def [a b & rest] [:a :b]) | ||||
|   (assert (= a :a) "tuple destructuring 13 - rest") | ||||
|   (assert (= b :b) "tuple destructuring 14 - rest") | ||||
|   (assert (= rest []) "tuple destructuring 15 - rest")) | ||||
|  | ||||
| (do | ||||
|   (def [[a b & r1] c & r2] [[:a :b 1 2] :c 3 4]) | ||||
|   (assert (= a :a) "tuple destructuring 16 - rest") | ||||
|   (assert (= b :b) "tuple destructuring 17 - rest") | ||||
|   (assert (= c :c) "tuple destructuring 18 - rest") | ||||
|   (assert (= r1 [1 2]) "tuple destructuring 19 - rest") | ||||
|   (assert (= r2 [3 4]) "tuple destructuring 20 - rest")) | ||||
|  | ||||
| # Metadata | ||||
| # ec2d7bf34 | ||||
| (def foo-with-tags :a-tag :bar) | ||||
| (assert (get (dyn 'foo-with-tags) :a-tag) | ||||
|         "extra keywords in def are metadata tags") | ||||
|  | ||||
| (def foo-with-meta {:baz :quux} :bar) | ||||
| (assert (= :quux (get (dyn 'foo-with-meta) :baz)) | ||||
|         "extra struct in def is metadata") | ||||
|  | ||||
| (defn foo-fn-with-meta {:baz :quux} | ||||
|   "This is a function" | ||||
|   [x] | ||||
|   (identity x)) | ||||
| (assert (= :quux (get (dyn 'foo-fn-with-meta) :baz)) | ||||
|         "extra struct in defn is metadata") | ||||
| (assert (= "(foo-fn-with-meta x)\n\nThis is a function" | ||||
|            (get (dyn 'foo-fn-with-meta) :doc)) | ||||
|         "extra string in defn is docstring") | ||||
|  | ||||
| # Break | ||||
| # 4a111b38b | ||||
| (var summation 0) | ||||
| (for i 0 10 | ||||
|   (+= summation i) | ||||
|   (if (= i 7) (break))) | ||||
| (assert (= summation 28) "break 1") | ||||
|  | ||||
| (assert (= nil ((fn [] (break) 4))) "break 2") | ||||
|  | ||||
| # Break with value | ||||
| # 8ba112116 | ||||
| # Shouldn't error out | ||||
| (assert-no-error "break 3" (for i 0 10 (if (> i 8) (break i)))) | ||||
| (assert-no-error "break 4" ((fn [i] (if (> i 8) (break i))) 100)) | ||||
|  | ||||
| # No useless splices | ||||
| # 7d57f8700 | ||||
| (check-compile-error '((splice [1 2 3]) 0)) | ||||
| (check-compile-error '(if ;[1 2] 5)) | ||||
| (check-compile-error '(while ;[1 2 3] (print :hi))) | ||||
| (check-compile-error '(def x ;[1 2 3])) | ||||
| (check-compile-error '(fn [x] ;[x 1 2 3])) | ||||
|  | ||||
| # No splice propagation | ||||
| (check-compile-error '(+ 1 (do ;[2 3 4]) 5)) | ||||
| (check-compile-error '(+ 1 (upscope ;[2 3 4]) 5)) | ||||
| # compiler inlines when condition is constant, ensure that optimization | ||||
| # doesn't break | ||||
| (check-compile-error '(+ 1 (if true ;[3 4]))) | ||||
| (check-compile-error '(+ 1 (if false nil ;[3 4]))) | ||||
|  | ||||
| # Keyword arguments | ||||
| # 3f137ed0b | ||||
| (defn myfn [x y z &keys {:a a :b b :c c}] | ||||
|   (+ x y z a b c)) | ||||
|  | ||||
| (assert (= (+ ;(range 6)) (myfn 0 1 2 :a 3 :b 4 :c 5)) "keyword args 1") | ||||
| (assert (= (+ ;(range 6)) (myfn 0 1 2 :a 1 :b 6 :c 5 :d 11)) | ||||
|         "keyword args 2") | ||||
|  | ||||
| # Named arguments | ||||
| # 87fc339 | ||||
| (defn named-arguments | ||||
|   [&named bob sally joe] | ||||
|   (+ bob sally joe)) | ||||
|  | ||||
| (assert (= 15 (named-arguments :bob 3 :sally 5 :joe 7)) "named arguments 1") | ||||
|  | ||||
| # a117252 | ||||
| (defn named-opt-arguments | ||||
|   [&opt x &named a b c] | ||||
|   (+ x a b c)) | ||||
|  | ||||
| (assert (= 10 (named-opt-arguments 1 :a 2 :b 3 :c 4)) "named arguments 2") | ||||
|  | ||||
| # | ||||
| # fn compilation special | ||||
| # | ||||
| # b8032ec61 | ||||
| (defn myfn1 [[x y z] & more] | ||||
|   more) | ||||
| (defn myfn2 [head & more] | ||||
|   more) | ||||
| (assert (= (myfn1 [1 2 3] 4 5 6) (myfn2 [:a :b :c] 4 5 6)) | ||||
|         "destructuring and varargs") | ||||
|  | ||||
| # Nested quasiquotation | ||||
| # 4199c42fe | ||||
| (def nested ~(a ~(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f)) | ||||
| (assert (deep= nested '(a ~(b ,(+ 1 2) ,(foo 4 d) e) f)) | ||||
|         "nested quasiquote") | ||||
|  | ||||
| # Regression #400 | ||||
| # 7a84fc474 | ||||
| (assert (= nil (while (and false false) | ||||
|                  (fn []) | ||||
|                  (error "should not happen"))) "strangeloop 1") | ||||
| (assert (= nil (while (not= nil nil) | ||||
|                  (fn []) | ||||
|                  (error "should not happen"))) "strangeloop 2") | ||||
|  | ||||
| # 919 | ||||
| # a097537a0 | ||||
| (defn test | ||||
|   [] | ||||
|   (var x 1) | ||||
|   (set x ~(,x ())) | ||||
|   x) | ||||
|  | ||||
| (assert (= (test) '(1 ())) "issue #919") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,39 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Scan number | ||||
| # 798c88b4c | ||||
| (assert (= 1 (scan-number "1")) "scan-number 1") | ||||
| (assert (= -1 (scan-number "-1")) "scan-number -1") | ||||
| (assert (= 1.3e4 (scan-number "1.3e4")) "scan-number 1.3e4") | ||||
|  | ||||
| # Issue #183 - just parse it :) | ||||
| # 688d297a1 | ||||
| 1e-4000000000000000000000 | ||||
|  | ||||
| # For undefined behavior sanitizer | ||||
| # c876e63 | ||||
| 0xf&1fffFFFF | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,94 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # 21bd960 | ||||
| (assert (= (struct 1 2 3 4 5 6 7 8) (struct 7 8 5 6 3 4 1 2)) | ||||
|         "struct order does not matter 1") | ||||
| # 42a88de | ||||
| (assert (= (struct | ||||
|              :apple 1 | ||||
|              6 :bork | ||||
|              '(1 2 3) 5) | ||||
|            (struct | ||||
|              6 :bork | ||||
|              '(1 2 3) 5 | ||||
|              :apple 1)) "struct order does not matter 2") | ||||
|  | ||||
| # Denormal structs | ||||
| # 38a7e4faf | ||||
| (assert (= (length {1 2 nil 3}) 1) "nil key struct literal") | ||||
| (assert (= (length (struct 1 2 nil 3)) 1) "nil key struct ctor") | ||||
|  | ||||
| (assert (= (length (struct (/ 0 0) 2 1 3)) 1) "nan key struct ctor") | ||||
| (assert (= (length {1 2 (/ 0 0) 3}) 1) "nan key struct literal") | ||||
|  | ||||
| (assert (= (length (struct 2 1 3 nil)) 1) "nil value struct ctor") | ||||
| (assert (= (length {1 2 3 nil}) 1) "nil value struct literal") | ||||
|  | ||||
| # Struct duplicate elements | ||||
| # 8bc2987a7 | ||||
| (assert (= {:a 3 :b 2} {:a 1 :b 2 :a 3}) "struct literal duplicate keys") | ||||
| (assert (= {:a 3 :b 2} (struct :a 1 :b 2 :a 3)) | ||||
|         "struct constructor duplicate keys") | ||||
|  | ||||
| # Struct prototypes | ||||
| # 4d983e5 | ||||
| (def x (struct/with-proto {1 2 3 4} 5 6)) | ||||
| (def y (-> x marshal unmarshal)) | ||||
| (def z {1 2 3 4}) | ||||
| (assert (= 2 (get x 1)) "struct get proto value 1") | ||||
| (assert (= 4 (get x 3)) "struct get proto value 2") | ||||
| (assert (= 6 (get x 5)) "struct get proto value 3") | ||||
| (assert (= x y) "struct proto marshal equality 1") | ||||
| (assert (= (getproto x) (getproto y)) "struct proto marshal equality 2") | ||||
| (assert (= 0 (cmp x y)) "struct proto comparison 1") | ||||
| (assert (= 0 (cmp (getproto x) (getproto y))) "struct proto comparison 2") | ||||
| (assert (not= (cmp x z) 0) "struct proto comparison 3") | ||||
| (assert (not= (cmp y z) 0) "struct proto comparison 4") | ||||
| (assert (not= x z) "struct proto comparison 5") | ||||
| (assert (not= y z) "struct proto comparison 6") | ||||
| (assert (= (x 5) 6) "struct proto get 1") | ||||
| (assert (= (y 5) 6) "struct proto get 1") | ||||
| (assert (deep= x y) "struct proto deep= 1") | ||||
| (assert (deep-not= x z) "struct proto deep= 2") | ||||
| (assert (deep-not= y z) "struct proto deep= 3") | ||||
|  | ||||
| # Check missing struct proto bug | ||||
| # 868ec1a7e, e08394c8 | ||||
| (assert (struct/getproto (struct/with-proto {:a 1} :b 2 :c nil)) | ||||
|         "missing struct proto") | ||||
|  | ||||
| # struct/with-proto | ||||
| (assert-error "expected odd number of arguments" (struct/with-proto {} :a)) | ||||
|  | ||||
| # struct/to-table | ||||
| (def s (struct/with-proto {:a 1 :b 2} :name "john" )) | ||||
| (def t1 (struct/to-table s true)) | ||||
| (def t2 (struct/to-table s false)) | ||||
| (assert (deep= t1 @{:name "john"}) "struct/to-table 1") | ||||
| (assert (deep= t2 @{:name "john"}) "struct/to-table 2") | ||||
| (assert (deep= (getproto t1) @{:a 1 :b 2}) "struct/to-table 3") | ||||
| (assert (deep= (getproto t2) nil) "struct/to-table 4") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,42 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Symbol function | ||||
| # 5460ff1 | ||||
| (assert (= (symbol "abc" 1 2 3) 'abc123) "symbol function") | ||||
|  | ||||
| # Gensym tests | ||||
| # 3ccd68843 | ||||
| (assert (not= (gensym) (gensym)) "two gensyms not equal") | ||||
| ((fn [] | ||||
|    (def syms (table)) | ||||
|    (var counter 0) | ||||
|    (while (< counter 128) | ||||
|      (put syms (gensym) true) | ||||
|      (set counter (+ 1 counter))) | ||||
|    (assert (= (length syms) 128) "many symbols"))) | ||||
|  | ||||
| # issue #753 - a78cbd91d | ||||
| (assert (pos? (length (gensym))) "gensym not empty, regression #753") | ||||
|  | ||||
| (end-suite) | ||||
| @@ -1,72 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Denormal tables | ||||
| # 38a7e4faf | ||||
| (assert (= (length @{1 2 nil 3}) 1) "nil key table literal") | ||||
| (assert (= (length (table 1 2 nil 3)) 1) "nil key table ctor") | ||||
|  | ||||
| (assert (= (length (table (/ 0 0) 2 1 3)) 1) "nan key table ctor") | ||||
| (assert (= (length @{1 2 (/ 0 0) 3}) 1) "nan key table literal") | ||||
|  | ||||
| (assert (= (length (table 2 1 3 nil)) 1) "nil value table ctor") | ||||
| (assert (= (length @{1 2 3 nil}) 1) "nil value table literal") | ||||
|  | ||||
| # Table duplicate elements | ||||
| (assert (deep= @{:a 3 :b 2} @{:a 1 :b 2 :a 3}) "table literal duplicate keys") | ||||
| (assert (deep= @{:a 3 :b 2} (table :a 1 :b 2 :a 3)) | ||||
|         "table constructor duplicate keys") | ||||
|  | ||||
| ## Table prototypes | ||||
| # 027b2a81c | ||||
| (def roottab @{ | ||||
|  :parentprop 123 | ||||
| }) | ||||
|  | ||||
| (def childtab @{ | ||||
|  :childprop 456 | ||||
| }) | ||||
|  | ||||
| (table/setproto childtab roottab) | ||||
|  | ||||
| (assert (= 123 (get roottab :parentprop)) "table get 1") | ||||
| (assert (= 123 (get childtab :parentprop)) "table get proto") | ||||
| (assert (= nil (get roottab :childprop)) "table get 2") | ||||
| (assert (= 456 (get childtab :childprop)) "proto no effect") | ||||
|  | ||||
| # b3aed1356 | ||||
| (assert-error | ||||
|   "table rawget regression" | ||||
|   (table/new -1)) | ||||
|  | ||||
| # table/clone | ||||
| # 392813667 | ||||
| (defn check-table-clone [x msg] | ||||
|   (assert (= (table/to-struct x) (table/to-struct (table/clone x))) msg)) | ||||
|  | ||||
| (check-table-clone @{:a 123 :b 34 :c :hello : 945 0 1 2 3 4 5} | ||||
|                    "table/clone 1") | ||||
| (check-table-clone @{} "table/clone 2") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,299 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # Set global variables to prevent some possible compiler optimizations | ||||
| # that defeat point of the test | ||||
| # 2771171 | ||||
| (var zero 0) | ||||
| (var one 1) | ||||
| (var two 2) | ||||
| (var three 3) | ||||
| (var plus +) | ||||
| (assert (= 22 (plus one (plus 1 2 two) (plus 8 (plus zero 1) 4 three))) | ||||
|         "nested function calls") | ||||
|  | ||||
| # McCarthy's 91 function | ||||
| # 2771171 | ||||
| (var f91 nil) | ||||
| (set f91 (fn [n] | ||||
|            (if (> n 100) | ||||
|              (- n 10) | ||||
|              (f91 (f91 (+ n 11)))))) | ||||
| (assert (= 91 (f91 10)) "f91(10) = 91") | ||||
| (assert (= 91 (f91 11)) "f91(11) = 91") | ||||
| (assert (= 91 (f91 20)) "f91(20) = 91") | ||||
| (assert (= 91 (f91 31)) "f91(31) = 91") | ||||
| (assert (= 91 (f91 100)) "f91(100) = 91") | ||||
| (assert (= 91 (f91 101)) "f91(101) = 91") | ||||
| (assert (= 92 (f91 102)) "f91(102) = 92") | ||||
| (assert (= 93 (f91 103)) "f91(103) = 93") | ||||
| (assert (= 94 (f91 104)) "f91(104) = 94") | ||||
|  | ||||
| # Fibonacci | ||||
| # 23196ff | ||||
| (def fib | ||||
|   (do | ||||
|     (var fib nil) | ||||
|     (set fib (fn [n] | ||||
|                (if (< n 2) | ||||
|                  n | ||||
|                  (+ (fib (- n 1)) (fib (- n 2)))))))) | ||||
| (def fib2 | ||||
|   (fn fib2 [n] | ||||
|     (if (< n 2) | ||||
|       n | ||||
|       (+ (fib2 (- n 1)) (fib2 (- n 2)))))) | ||||
|  | ||||
| (assert (= (fib 0) (fib2 0) 0) "fib(0)") | ||||
| (assert (= (fib 1) (fib2 1) 1) "fib(1)") | ||||
| (assert (= (fib 2) (fib2 2) 1) "fib(2)") | ||||
| (assert (= (fib 3) (fib2 3) 2) "fib(3)") | ||||
| (assert (= (fib 4) (fib2 4) 3) "fib(4)") | ||||
| (assert (= (fib 5) (fib2 5) 5) "fib(5)") | ||||
| (assert (= (fib 6) (fib2 6) 8) "fib(6)") | ||||
| (assert (= (fib 7) (fib2 7) 13) "fib(7)") | ||||
| (assert (= (fib 8) (fib2 8) 21) "fib(8)") | ||||
| (assert (= (fib 9) (fib2 9) 34) "fib(9)") | ||||
| (assert (= (fib 10) (fib2 10) 55) "fib(10)") | ||||
|  | ||||
| # Closure in non function scope | ||||
| # 911b0b1 | ||||
| (def outerfun (fn [x y] | ||||
|                 (def c (do | ||||
|                          (def someval (+ 10 y)) | ||||
|                          (def ctemp (if x (fn [] someval) (fn [] y))) | ||||
|                          ctemp | ||||
|                          )) | ||||
|                 (+ 1 2 3 4 5 6 7) | ||||
|                 c)) | ||||
|  | ||||
| (assert (= ((outerfun 1 2)) 12) "inner closure 1") | ||||
| (assert (= ((outerfun nil 2)) 2) "inner closure 2") | ||||
| (assert (= ((outerfun false 3)) 3) "inner closure 3") | ||||
|  | ||||
| # d6967a5 | ||||
| ((fn [] | ||||
|    (var accum 1) | ||||
|    (var counter 0) | ||||
|    (while (< counter 16) | ||||
|      (set accum (blshift accum 1)) | ||||
|      (set counter (+ 1 counter))) | ||||
|    (assert (= accum 65536) "loop in closure"))) | ||||
|  | ||||
| (var accum 1) | ||||
| (var counter 0) | ||||
| (while (< counter 16) | ||||
|   (set accum (blshift accum 1)) | ||||
|   (set counter (+ 1 counter))) | ||||
| (assert (= accum 65536) "loop globally") | ||||
|  | ||||
| # Fiber tests | ||||
| # 21bd960 | ||||
| (def afiber (fiber/new (fn [] | ||||
|                          (def x (yield)) | ||||
|                          (error (string "hello, " x))) :ye)) | ||||
|  | ||||
| (resume afiber) # first resume to prime | ||||
| (def afiber-result (resume afiber "world!")) | ||||
|  | ||||
| (assert (= afiber-result "hello, world!") "fiber error result") | ||||
| (assert (= (fiber/status afiber) :error) "fiber error status") | ||||
|  | ||||
| # Var arg tests | ||||
| # f054586 | ||||
| (def vargf (fn [more] (apply + more))) | ||||
|  | ||||
| (assert (= 0 (vargf @[])) "var arg no arguments") | ||||
| (assert (= 1 (vargf @[1])) "var arg no packed arguments") | ||||
| (assert (= 3 (vargf @[1 2])) "var arg tuple size 1") | ||||
| (assert (= 10 (vargf @[1 2 3 4])) "var arg tuple size 2, 2 normal args") | ||||
| (assert (= 110 (vargf @[1 2 3 4 10 10 10 10 10 10 10 10 10 10])) | ||||
|         "var arg large tuple") | ||||
|  | ||||
| # Higher order functions | ||||
| # d9f24ef | ||||
| (def compose (fn [f g] (fn [& xs] (f (apply g xs))))) | ||||
|  | ||||
| (def -+ (compose - +)) | ||||
| (def +- (compose + -)) | ||||
|  | ||||
| (assert (= (-+ 1 2 3 4) -10) "compose - +") | ||||
| (assert (= (+- 1 2 3 4) -8) "compose + -") | ||||
| (assert (= ((compose -+ +-) 1 2 3 4) 8) "compose -+ +-") | ||||
| (assert (= ((compose +- -+) 1 2 3 4) 10) "compose +- -+") | ||||
|  | ||||
| # UTF-8 | ||||
| # d9f24ef | ||||
| #🐙🐙🐙🐙 | ||||
|  | ||||
| (defn foo [Θa Θb Θc] 0) | ||||
| (def 🦊 :fox) | ||||
| (def 🐮 :cow) | ||||
| (assert (= (string "🐼" 🦊 🐮) "🐼foxcow") "emojis 🙉 :)") | ||||
| (assert (not= 🦊 "🦊") "utf8 strings are not symbols and vice versa") | ||||
| (assert (= "\U01F637" "😷") "unicode escape 1") | ||||
| (assert (= "\u2623" "\U002623" "☣") "unicode escape 2") | ||||
| (assert (= "\u24c2" "\U0024c2" "Ⓜ") "unicode escape 3") | ||||
| (assert (= "\u0061" "a") "unicode escape 4") | ||||
|  | ||||
| # Test max triangle program | ||||
| # c0e373f | ||||
| # Find the maximum path from the top (root) | ||||
| # of the triangle to the leaves of the triangle. | ||||
|  | ||||
| (defn myfold [xs ys] | ||||
|   (let [xs1 [;xs 0] | ||||
|         xs2 [0 ;xs] | ||||
|         m1 (map + xs1 ys) | ||||
|         m2 (map + xs2 ys)] | ||||
|     (map max m1 m2))) | ||||
|  | ||||
| (defn maxpath [t] | ||||
|  (extreme > (reduce myfold () t))) | ||||
|  | ||||
| # Test it | ||||
| # Maximum path is 3 -> 10 -> 3 -> 9 for a total of 25 | ||||
| (def triangle '[ | ||||
|  [3] | ||||
|  [7 10] | ||||
|  [4 3 7] | ||||
|  [8 9 1 3] | ||||
| ]) | ||||
|  | ||||
| (assert (= (maxpath triangle) 25) `max triangle`) | ||||
|  | ||||
| # Large functions | ||||
| # 6822400 | ||||
| (def manydefs (seq [i :range [0 300]] | ||||
|                 (tuple 'def (gensym) (string "value_" i)))) | ||||
| (array/push manydefs (tuple * 10000 3 5 7 9)) | ||||
| (def f (compile ['do ;manydefs] (fiber/getenv (fiber/current)))) | ||||
| (assert (= (f) (* 10000 3 5 7 9)) "long function compilation") | ||||
|  | ||||
| # Closure in while loop | ||||
| # abe7d59 | ||||
| (def closures (seq [i :range [0 5]] (fn [] i))) | ||||
| (assert (= 0 ((get closures 0))) "closure in loop 0") | ||||
| (assert (= 1 ((get closures 1))) "closure in loop 1") | ||||
| (assert (= 2 ((get closures 2))) "closure in loop 2") | ||||
| (assert (= 3 ((get closures 3))) "closure in loop 3") | ||||
| (assert (= 4 ((get closures 4))) "closure in loop 4") | ||||
|  | ||||
| # Another regression test - no segfaults | ||||
| # 6b4824c | ||||
| (defn afn [x] x) | ||||
| (var afn-var afn) | ||||
| (var identity-var identity) | ||||
| (var map-var map) | ||||
| (var not-var not) | ||||
| (assert (= 1 (try (afn-var) ([err] 1))) "bad arity 1") | ||||
| (assert (= 4 (try ((fn [x y] (+ x y)) 1) ([_] 4))) "bad arity 2") | ||||
| (assert (= 1 (try (identity-var) ([err] 1))) "bad arity 3") | ||||
| (assert (= 1 (try (map-var) ([err] 1))) "bad arity 4") | ||||
| (assert (= 1 (try (not-var) ([err] 1))) "bad arity 5") | ||||
|  | ||||
| # Detaching closure over non resumable fiber | ||||
| # issue #317 - 7c4ffe9b9 | ||||
| (do | ||||
|   (defn f1 | ||||
|     [a] | ||||
|     (defn f1 [] (++ (a 0))) | ||||
|     (defn f2 [] (++ (a 0))) | ||||
|     (error [f1 f2])) | ||||
|   (def [_ [f1 f2]] (protect (f1 @[0]))) | ||||
|   # At time of writing, mark phase can detach closure envs. | ||||
|   (gccollect) | ||||
|   (assert (= 1 (f1)) "detach-non-resumable-closure 1") | ||||
|   (assert (= 2 (f2)) "detach-non-resumable-closure 2")) | ||||
|  | ||||
| # Dynamic defs | ||||
| # ec65f03 | ||||
| (def staticdef1 0) | ||||
| (defn staticdef1-inc [] (+ 1 staticdef1)) | ||||
| (assert (= 1 (staticdef1-inc)) "before redefinition without :redef") | ||||
| (def staticdef1 1) | ||||
| (assert (= 1 (staticdef1-inc)) "after redefinition without :redef") | ||||
| (setdyn :redef true) | ||||
| (def dynamicdef2 0) | ||||
| (defn dynamicdef2-inc [] (+ 1 dynamicdef2)) | ||||
| (assert (= 1 (dynamicdef2-inc)) "before redefinition with dyn :redef") | ||||
| (def dynamicdef2 1) | ||||
| (assert (= 2 (dynamicdef2-inc)) "after redefinition with dyn :redef") | ||||
| (setdyn :redef nil) | ||||
|  | ||||
| # missing symbols | ||||
| # issue #914 - 1eb34989d | ||||
| (defn lookup-symbol [sym] (defglobal sym 10) (dyn sym)) | ||||
|  | ||||
| (setdyn :missing-symbol lookup-symbol) | ||||
|  | ||||
| (assert (= (eval-string "(+ a 5)") 15) "lookup missing symbol") | ||||
|  | ||||
| (setdyn :missing-symbol nil) | ||||
| (setdyn 'a nil) | ||||
|  | ||||
| (assert-error "compile error" (eval-string "(+ a 5)")) | ||||
|  | ||||
| # 88813c4 | ||||
| (assert (deep= (in (disasm (defn a [] (def x 10) x)) :symbolmap) | ||||
|                @[[0 2 0 'a] [0 2 1 'x]]) | ||||
|         "symbolmap when *debug* is true") | ||||
|  | ||||
| (defn a [arg] | ||||
|   (def x 10) | ||||
|   (do | ||||
|     (def y 20) | ||||
|     (def z 30) | ||||
|     (+ x y z))) | ||||
| (def symbolslots (in (disasm a) :symbolslots)) | ||||
| (def f (asm (disasm a))) | ||||
| (assert (deep= (in (disasm f) :symbolslots) | ||||
|                symbolslots) | ||||
|         "symbolslots survive disasm/asm") | ||||
|  | ||||
| (comment | ||||
|   (setdyn *debug* true) | ||||
|   (setdyn :pretty-format "%.40M") | ||||
|   (def f (fn [x] (fn [y] (+ x y)))) | ||||
|   (assert (deep= (map last (in (disasm (f 10)) :symbolmap)) | ||||
|                  @['x 'y]) | ||||
|           "symbolmap upvalues")) | ||||
|  | ||||
| (assert (deep= (in (disasm (defn a [arg] | ||||
|                              (def x 10) | ||||
|                              (do | ||||
|                                (def y 20) | ||||
|                                (def z 30) | ||||
|                                (+ x y z)))) :symbolmap) | ||||
|                @[[0 6 0 'arg] | ||||
|                  [0 6 1 'a] | ||||
|                  [0 6 2 'x] | ||||
|                  [1 6 3 'y] | ||||
|                  [2 6 4 'z]]) | ||||
|         "arg & inner symbolmap") | ||||
|  | ||||
| # 4782a76 | ||||
| (assert (= 10 (do (var x 10) (def y x) (++ x) y)) "no invalid aliasing") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,72 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # 3e1e25854 | ||||
| (def test-struct {'def 1 'bork 2 'sam 3 'a 'b 'het @[1 2 3 4 5]}) | ||||
| (assert (= (get test-struct 'def) 1) "struct get") | ||||
| (assert (= (get test-struct 'bork) 2) "struct get") | ||||
| (assert (= (get test-struct 'sam) 3) "struct get") | ||||
| (assert (= (get test-struct 'a) 'b) "struct get") | ||||
| (assert (= :array (type (get test-struct 'het))) "struct get") | ||||
|  | ||||
| # Buffer stuff | ||||
| # 910cfd7dd | ||||
| (defn buffer= | ||||
|   [a b] | ||||
|   (= (string a) (string b))) | ||||
|  | ||||
| (assert (buffer= @"abcd" @"abcd") "buffer equal 1") | ||||
| (assert (buffer= @"abcd" (buffer "ab" "cd")) "buffer equal 2") | ||||
| (assert (not= @"" @"") "buffer not equal 1") | ||||
| (assert (not= @"abcd" @"abcd") "buffer not equal 2") | ||||
|  | ||||
| (defn buffer-factory | ||||
|   [] | ||||
|   @"im am a buffer") | ||||
|  | ||||
| (assert (not= (buffer-factory) (buffer-factory)) "buffer instantiation") | ||||
|  | ||||
| (assert (= (length @"abcdef") 6) "buffer length") | ||||
|  | ||||
| # Tuple comparison | ||||
| # da438a93e | ||||
| (assert (< [1 2 3] [2 2 3]) "tuple comparison 1") | ||||
| (assert (< [1 2 3] [2 2]) "tuple comparison 2") | ||||
| (assert (< [1 2 3] [2 2 3 4]) "tuple comparison 3") | ||||
| (assert (< [1 2 3] [1 2 3 4]) "tuple comparison 4") | ||||
| (assert (< [1 2 3] [1 2 3 -1]) "tuple comparison 5") | ||||
| (assert (> [1 2 3] [1 2]) "tuple comparison 6") | ||||
|  | ||||
| # More numerical tests | ||||
| # e05022f | ||||
| (assert (= 1 1.0) "numerical equal 1") | ||||
| (assert (= 0 0.0) "numerical equal 2") | ||||
| (assert (= 0 -0.0) "numerical equal 3") | ||||
| (assert (= 2_147_483_647 2_147_483_647.0) "numerical equal 4") | ||||
| (assert (= -2_147_483_648 -2_147_483_648.0) "numerical equal 5") | ||||
|  | ||||
| # issue #928 - d7ea122cf | ||||
| (assert (= (hash 0) (hash (* -1 0))) "hash -0 same as hash 0") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
| @@ -1,142 +0,0 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
|  | ||||
| # More fiber semantics | ||||
| # 0fd9224e4 | ||||
| (var myvar 0) | ||||
| (defn fiberstuff [&] | ||||
|   (++ myvar) | ||||
|   (def f (fiber/new (fn [&] (++ myvar) (debug) (++ myvar)))) | ||||
|   (resume f) | ||||
|   (++ myvar)) | ||||
|  | ||||
| (def myfiber (fiber/new fiberstuff :dey)) | ||||
|  | ||||
| (assert (= myvar 0) "fiber creation does not call fiber function") | ||||
| (resume myfiber) | ||||
| (assert (= myvar 2) "fiber debug statement breaks at proper point") | ||||
| (assert (= (fiber/status myfiber) :debug) "fiber enters debug state") | ||||
| (resume myfiber) | ||||
| (assert (= myvar 4) "fiber resumes properly from debug state") | ||||
| (assert (= (fiber/status myfiber) :dead) | ||||
|         "fiber properly dies from debug state") | ||||
|  | ||||
| # yield tests | ||||
| # 171c0ce | ||||
| (def t (fiber/new (fn [&] (yield 1) (yield 2) 3))) | ||||
|  | ||||
| (assert (= 1 (resume t)) "initial transfer to new fiber") | ||||
| (assert (= 2 (resume t)) "second transfer to fiber") | ||||
| (assert (= 3 (resume t)) "return from fiber") | ||||
| (assert (= (fiber/status t) :dead) "finished fiber is dead") | ||||
|  | ||||
| # Fix yields inside nested fibers | ||||
| # 909c906 | ||||
| (def yielder | ||||
|   (coro | ||||
|     (defer (yield :end) | ||||
|       (repeat 5 (yield :item))))) | ||||
| (def items (seq [x :in yielder] x)) | ||||
| (assert (deep= @[:item :item :item :item :item :end] items) | ||||
|         "yield within nested fibers") | ||||
|  | ||||
| # Calling non functions | ||||
| # b9c0fc820 | ||||
| (assert (= 1 ({:ok 1} :ok)) "calling struct") | ||||
| (assert (= 2 (@{:ok 2} :ok)) "calling table") | ||||
| (assert (= :bad (try ((identity @{:ok 2}) :ok :no) ([err] :bad))) | ||||
|         "calling table too many arguments") | ||||
| (assert (= :bad (try ((identity :ok) @{:ok 2} :no) ([err] :bad))) | ||||
|         "calling keyword too many arguments") | ||||
| (assert (= :oops (try ((+ 2 -1) 1) ([err] :oops))) | ||||
|         "calling number fails") | ||||
|  | ||||
| # Method test | ||||
| # d5bab7262 | ||||
| (def Dog @{:bark (fn bark [self what] | ||||
|                    (string (self :name) " says " what "!"))}) | ||||
| (defn make-dog | ||||
|   [name] | ||||
|   (table/setproto @{:name name} Dog)) | ||||
|  | ||||
| (assert (= "fido" ((make-dog "fido") :name)) "oo 1") | ||||
| (def spot (make-dog "spot")) | ||||
| (assert (= "spot says hi!" (:bark spot "hi")) "oo 2") | ||||
|  | ||||
| # Negative tests | ||||
| # 67f26b7d7 | ||||
| (assert-error "+ check types" (+ 1 ())) | ||||
| (assert-error "- check types" (- 1 ())) | ||||
| (assert-error "* check types" (* 1 ())) | ||||
| (assert-error "/ check types" (/ 1 ())) | ||||
| (assert-error "band check types" (band 1 ())) | ||||
| (assert-error "bor check types" (bor 1 ())) | ||||
| (assert-error "bxor check types" (bxor 1 ())) | ||||
| (assert-error "bnot check types" (bnot ())) | ||||
|  | ||||
| # Comparisons | ||||
| # 10dcbc639 | ||||
| (assert (> 1e23 100) "less than immediate 1") | ||||
| (assert (> 1e23 1000) "less than immediate 2") | ||||
| (assert (< 100 1e23) "greater than immediate 1") | ||||
| (assert (< 1000 1e23) "greater than immediate 2") | ||||
|  | ||||
| # Quasiquote bracketed tuples | ||||
| # e239980da | ||||
| (assert (= (tuple/type ~[1 2 3]) (tuple/type '[1 2 3])) | ||||
|         "quasiquote bracket tuples") | ||||
|  | ||||
| # Regression #638 | ||||
| # c68264802 | ||||
| (compwhen | ||||
|   (dyn 'ev/go) | ||||
|   (assert | ||||
|     (= [true :caught] | ||||
|        (protect | ||||
|          (try | ||||
|            (do | ||||
|              (ev/sleep 0) | ||||
|              (with-dyns [] | ||||
|                (ev/sleep 0) | ||||
|                (error "oops"))) | ||||
|            ([err] :caught)))) | ||||
|     "regression #638")) | ||||
|  | ||||
| # | ||||
| # Test propagation of signals via fibers | ||||
| # | ||||
| # b8032ec61 | ||||
| (def f (fiber/new (fn [] (error :abc) 1) :ei)) | ||||
| (def res (resume f)) | ||||
| (assert-error :abc (propagate res f) "propagate 1") | ||||
|  | ||||
| # Cancel test | ||||
| # 28439d822 | ||||
| (def f (fiber/new (fn [&] (yield 1) (yield 2) (yield 3) 4) :yti)) | ||||
| (assert (= 1 (resume f)) "cancel resume 1") | ||||
| (assert (= 2 (resume f)) "cancel resume 2") | ||||
| (assert (= :hi (cancel f :hi)) "cancel resume 3") | ||||
| (assert (= :error (fiber/status f)) "cancel resume 4") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
							
								
								
									
										437
									
								
								test/suite0000.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										437
									
								
								test/suite0000.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,437 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 0) | ||||
|  | ||||
| (assert (= 10 (+ 1 2 3 4)) "addition") | ||||
| (assert (= -8 (- 1 2 3 4)) "subtraction") | ||||
| (assert (= 24 (* 1 2 3 4)) "multiplication") | ||||
| (assert (= 4 (blshift 1 2)) "left shift") | ||||
| (assert (= 1 (brshift 4 2)) "right shift") | ||||
| (assert (< 1 2 3 4 5 6) "less than integers") | ||||
| (assert (< 1.0 2.0 3.0 4.0 5.0 6.0) "less than reals") | ||||
| (assert (> 6 5 4 3 2 1) "greater than integers") | ||||
| (assert (> 6.0 5.0 4.0 3.0 2.0 1.0) "greater than reals") | ||||
| (assert (<= 1 2 3 3 4 5 6) "less than or equal to integers") | ||||
| (assert (<= 1.0 2.0 3.0 3.0 4.0 5.0 6.0) "less than or equal to reals") | ||||
| (assert (>= 6 5 4 4 3 2 1) "greater than or equal to integers") | ||||
| (assert (>= 6.0 5.0 4.0 4.0 3.0 2.0 1.0) "greater than or equal to reals") | ||||
| (assert (= 7 (% 20 13)) "modulo 1") | ||||
| (assert (= -7 (% -20 13)) "modulo 2") | ||||
|  | ||||
| (assert (< 1.0 nil false true | ||||
|            (fiber/new (fn [] 1)) | ||||
|            "hi" | ||||
|            (quote hello) | ||||
|            :hello | ||||
|            (array 1 2 3) | ||||
|            (tuple 1 2 3) | ||||
|            (table "a" "b" "c" "d") | ||||
|            (struct 1 2 3 4) | ||||
|            (buffer "hi") | ||||
|            (fn [x] (+ x x)) | ||||
|            print) "type ordering") | ||||
|  | ||||
| (assert (= (string (buffer "123" "456")) (string @"123456")) "buffer literal") | ||||
| (assert (= (get {} 1) nil) "get nil from empty struct") | ||||
| (assert (= (get @{} 1) nil) "get nil from empty table") | ||||
| (assert (= (get {:boop :bap} :boop) :bap) "get non nil from struct") | ||||
| (assert (= (get @{:boop :bap} :boop) :bap) "get non nil from table") | ||||
| (assert (= (get @"\0" 0) 0) "get non nil from buffer") | ||||
| (assert (= (get @"\0" 1) nil) "get nil from buffer oob") | ||||
| (assert (put @{} :boop :bap) "can add to empty table") | ||||
| (assert (put @{1 3} :boop :bap) "can add to non-empty table") | ||||
|  | ||||
| (assert (not false) "false literal") | ||||
| (assert true "true literal") | ||||
| (assert (not nil) "nil literal") | ||||
| (assert (= 7 (bor 3 4)) "bit or") | ||||
| (assert (= 0 (band 3 4)) "bit and") | ||||
| (assert (= 0xFF (bxor 0x0F 0xF0)) "bit xor") | ||||
| (assert (= 0xF0 (bxor 0xFF 0x0F)) "bit xor 2") | ||||
|  | ||||
| # Set global variables to prevent some possible compiler optimizations that defeat point of the test | ||||
| (var zero 0) | ||||
| (var one 1) | ||||
| (var two 2) | ||||
| (var three 3) | ||||
| (var plus +) | ||||
| (assert (= 22 (plus one (plus 1 2 two) (plus 8 (plus zero 1) 4 three))) "nested function calls") | ||||
|  | ||||
| # String literals | ||||
| (assert (= "abcd" "\x61\x62\x63\x64") "hex escapes") | ||||
| (assert (= "\e" "\x1B") "escape character") | ||||
| (assert (= "\x09" "\t") "tab character") | ||||
|  | ||||
| # McCarthy's 91 function | ||||
| (var f91 nil) | ||||
| (set f91 (fn [n] (if (> n 100) (- n 10) (f91 (f91 (+ n 11)))))) | ||||
| (assert (= 91 (f91 10)) "f91(10) = 91") | ||||
| (assert (= 91 (f91 11)) "f91(11) = 91") | ||||
| (assert (= 91 (f91 20)) "f91(20) = 91") | ||||
| (assert (= 91 (f91 31)) "f91(31) = 91") | ||||
| (assert (= 91 (f91 100)) "f91(100) = 91") | ||||
| (assert (= 91 (f91 101)) "f91(101) = 91") | ||||
| (assert (= 92 (f91 102)) "f91(102) = 92") | ||||
| (assert (= 93 (f91 103)) "f91(103) = 93") | ||||
| (assert (= 94 (f91 104)) "f91(104) = 94") | ||||
|  | ||||
| # Fibonacci | ||||
| (def fib (do (var fib nil) (set fib (fn [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))))) | ||||
| (def fib2 (fn fib2 [n] (if (< n 2) n (+ (fib2 (- n 1)) (fib2 (- n 2)))))) | ||||
|  | ||||
| (assert (= (fib 0) (fib2 0) 0) "fib(0)") | ||||
| (assert (= (fib 1) (fib2 1) 1) "fib(1)") | ||||
| (assert (= (fib 2) (fib2 2) 1) "fib(2)") | ||||
| (assert (= (fib 3) (fib2 3) 2) "fib(3)") | ||||
| (assert (= (fib 4) (fib2 4) 3) "fib(4)") | ||||
| (assert (= (fib 5) (fib2 5) 5) "fib(5)") | ||||
| (assert (= (fib 6) (fib2 6) 8) "fib(6)") | ||||
| (assert (= (fib 7) (fib2 7) 13) "fib(7)") | ||||
| (assert (= (fib 8) (fib2 8) 21) "fib(8)") | ||||
| (assert (= (fib 9) (fib2 9) 34) "fib(9)") | ||||
| (assert (= (fib 10) (fib2 10) 55) "fib(10)") | ||||
|  | ||||
| # Closure in non function scope | ||||
| (def outerfun (fn [x y] | ||||
|                 (def c (do | ||||
|                          (def someval (+ 10 y)) | ||||
|                          (def ctemp (if x (fn [] someval) (fn [] y))) | ||||
|                          ctemp | ||||
|                          )) | ||||
|                 (+ 1 2 3 4 5 6 7) | ||||
|                 c)) | ||||
|  | ||||
| (assert (= ((outerfun 1 2)) 12) "inner closure 1") | ||||
| (assert (= ((outerfun nil 2)) 2) "inner closure 2") | ||||
| (assert (= ((outerfun false 3)) 3) "inner closure 3") | ||||
|  | ||||
| (assert (= '(1 2 3) (quote (1 2 3)) (tuple 1 2 3)) "quote shorthand") | ||||
|  | ||||
| ((fn [] | ||||
|    (var accum 1) | ||||
|    (var count 0) | ||||
|    (while (< count 16) | ||||
|      (set accum (blshift accum 1)) | ||||
|      (set count (+ 1 count))) | ||||
|    (assert (= accum 65536) "loop in closure"))) | ||||
|  | ||||
| (var accum 1) | ||||
| (var count 0) | ||||
| (while (< count 16) | ||||
|   (set accum (blshift accum 1)) | ||||
|   (set count (+ 1 count))) | ||||
| (assert (= accum 65536) "loop globally") | ||||
|  | ||||
| (assert (= (struct 1 2 3 4 5 6 7 8) (struct 7 8 5 6 3 4 1 2)) "struct order does not matter 1") | ||||
| (assert (= (struct | ||||
|              :apple 1 | ||||
|              6 :bork | ||||
|              '(1 2 3) 5) | ||||
|            (struct | ||||
|              6 :bork | ||||
|              '(1 2 3) 5 | ||||
|              :apple 1)) "struct order does not matter 2") | ||||
|  | ||||
| # Symbol function | ||||
|  | ||||
| (assert (= (symbol "abc" 1 2 3) 'abc123) "symbol function") | ||||
|  | ||||
| # Fiber tests | ||||
|  | ||||
| (def afiber (fiber/new (fn [] | ||||
|                          (def x (yield)) | ||||
|                          (error (string "hello, " x))) :ye)) | ||||
|  | ||||
| (resume afiber) # first resume to prime | ||||
| (def afiber-result (resume afiber "world!")) | ||||
|  | ||||
| (assert (= afiber-result "hello, world!") "fiber error result") | ||||
| (assert (= (fiber/status afiber) :error) "fiber error status") | ||||
|  | ||||
| # yield tests | ||||
|  | ||||
| (def t (fiber/new (fn [&] (yield 1) (yield 2) 3))) | ||||
|  | ||||
| (assert (= 1 (resume t)) "initial transfer to new fiber") | ||||
| (assert (= 2 (resume t)) "second transfer to fiber") | ||||
| (assert (= 3 (resume t)) "return from fiber") | ||||
| (assert (= (fiber/status t) :dead) "finished fiber is dead") | ||||
|  | ||||
| # Var arg tests | ||||
|  | ||||
| (def vargf (fn [more] (apply + more))) | ||||
|  | ||||
| (assert (= 0 (vargf @[])) "var arg no arguments") | ||||
| (assert (= 1 (vargf @[1])) "var arg no packed arguments") | ||||
| (assert (= 3 (vargf @[1 2])) "var arg tuple size 1") | ||||
| (assert (= 10 (vargf @[1 2 3 4])) "var arg tuple size 2, 2 normal args") | ||||
| (assert (= 110 (vargf @[1 2 3 4 10 10 10 10 10 10 10 10 10 10])) "var arg large tuple") | ||||
|  | ||||
| # Higher order functions | ||||
|  | ||||
| (def compose (fn [f g] (fn [& xs] (f (apply g xs))))) | ||||
|  | ||||
| (def -+ (compose - +)) | ||||
| (def +- (compose + -)) | ||||
|  | ||||
| (assert (= (-+ 1 2 3 4) -10) "compose - +") | ||||
| (assert (= (+- 1 2 3 4) -8) "compose + -") | ||||
| (assert (= ((compose -+ +-) 1 2 3 4) 8) "compose -+ +-") | ||||
| (assert (= ((compose +- -+) 1 2 3 4) 10) "compose +- -+") | ||||
|  | ||||
| # UTF-8 | ||||
|  | ||||
| #🐙🐙🐙🐙 | ||||
|  | ||||
| (defn foo [Θa Θb Θc] 0) | ||||
| (def 🦊 :fox) | ||||
| (def 🐮 :cow) | ||||
| (assert (= (string "🐼" 🦊 🐮) "🐼foxcow") "emojis 🙉 :)") | ||||
| (assert (not= 🦊 "🦊") "utf8 strings are not symbols and vice versa") | ||||
| (assert (= "\U01F637" "😷") "unicode escape 1") | ||||
| (assert (= "\u2623" "\U002623" "☣") "unicode escape 2") | ||||
| (assert (= "\u24c2" "\U0024c2" "Ⓜ") "unicode escape 3") | ||||
| (assert (= "\u0061" "a") "unicode escape 4") | ||||
|  | ||||
| # Symbols with @ character | ||||
|  | ||||
| (def @ 1) | ||||
| (assert (= @ 1) "@ symbol") | ||||
| (def @-- 2) | ||||
| (assert (= @-- 2) "@-- symbol") | ||||
| (def @hey 3) | ||||
| (assert (= @hey 3) "@hey symbol") | ||||
|  | ||||
| # Merge sort | ||||
|  | ||||
| # Imperative (and verbose) merge sort merge | ||||
| (defn merge | ||||
|   [xs ys] | ||||
|   (def ret @[]) | ||||
|   (def xlen (length xs)) | ||||
|   (def ylen (length ys)) | ||||
|   (var i 0) | ||||
|   (var j 0) | ||||
|   # Main merge | ||||
|   (while (if (< i xlen) (< j ylen)) | ||||
|     (def xi (get xs i)) | ||||
|     (def yj (get ys j)) | ||||
|     (if (< xi yj) | ||||
|       (do (array/push ret xi) (set i (+ i 1))) | ||||
|       (do (array/push ret yj) (set j (+ j 1))))) | ||||
|   # Push rest of xs | ||||
|   (while (< i xlen) | ||||
|     (def xi (get xs i)) | ||||
|     (array/push ret xi) | ||||
|     (set i (+ i 1))) | ||||
|   # Push rest of ys | ||||
|   (while (< j ylen) | ||||
|     (def yj (get ys j)) | ||||
|     (array/push ret yj) | ||||
|     (set j (+ j 1))) | ||||
|   ret) | ||||
|  | ||||
| (assert (apply <= (merge @[1 3 5] @[2 4 6])) "merge sort merge 1") | ||||
| (assert (apply <= (merge @[1 2 3] @[4 5 6])) "merge sort merge 2") | ||||
| (assert (apply <= (merge @[1 3 5] @[2 4 6 6 6 9])) "merge sort merge 3") | ||||
| (assert (apply <= (merge '(1 3 5) @[2 4 6 6 6 9])) "merge sort merge 4") | ||||
|  | ||||
| (assert (deep= @[1 2 3 4 5] (sort @[5 3 4 1 2])) "sort 1") | ||||
| (assert (deep= @[{:a 1} {:a 4} {:a 7}] (sort-by |($ :a) @[{:a 4} {:a 7} {:a 1}])) "sort 2") | ||||
| (assert (deep= @[1 2 3 4 5] (sorted [5 3 4 1 2])) "sort 3") | ||||
| (assert (deep= @[{:a 1} {:a 4} {:a 7}] (sorted-by |($ :a) [{:a 4} {:a 7} {:a 1}])) "sort 4") | ||||
|  | ||||
| # Gensym tests | ||||
|  | ||||
| (assert (not= (gensym) (gensym)) "two gensyms not equal") | ||||
| ((fn [] | ||||
|    (def syms (table)) | ||||
|    (var count 0) | ||||
|    (while (< count 128) | ||||
|      (put syms (gensym) true) | ||||
|      (set count (+ 1 count))) | ||||
|    (assert (= (length syms) 128) "many symbols"))) | ||||
|  | ||||
| # Let | ||||
|  | ||||
| (assert (= (let [a 1 b 2] (+ a b)) 3) "simple let") | ||||
| (assert (= (let [[a b] @[1 2]] (+ a b)) 3) "destructured let") | ||||
| (assert (= (let [[a [c d] b] @[1 (tuple 4 3) 2]] (+ a b c d)) 10) "double destructured let") | ||||
|  | ||||
| # Macros | ||||
|  | ||||
| (defn dub [x] (+ x x)) | ||||
| (assert (= 2 (dub 1)) "defn macro") | ||||
| (do | ||||
|   (defn trip [x] (+ x x x)) | ||||
|   (assert (= 3 (trip 1)) "defn macro triple")) | ||||
| (do | ||||
|   (var i 0) | ||||
|   (when true | ||||
|     (++ i) | ||||
|     (++ i) | ||||
|     (++ i) | ||||
|     (++ i) | ||||
|     (++ i) | ||||
|     (++ i)) | ||||
|   (assert (= i 6) "when macro")) | ||||
|  | ||||
| # Dynamic defs | ||||
|  | ||||
| (def staticdef1 0) | ||||
| (defn staticdef1-inc [] (+ 1 staticdef1)) | ||||
| (assert (= 1 (staticdef1-inc)) "before redefinition without :redef") | ||||
| (def staticdef1 1) | ||||
| (assert (= 1 (staticdef1-inc)) "after redefinition without :redef") | ||||
| (setdyn :redef true) | ||||
| (def dynamicdef2 0) | ||||
| (defn dynamicdef2-inc [] (+ 1 dynamicdef2)) | ||||
| (assert (= 1 (dynamicdef2-inc)) "before redefinition with dyn :redef") | ||||
| (def dynamicdef2 1) | ||||
| (assert (= 2 (dynamicdef2-inc)) "after redefinition with dyn :redef") | ||||
| (setdyn :redef nil) | ||||
|  | ||||
| # Denormal tables and structs | ||||
|  | ||||
| (assert (= (length {1 2 nil 3}) 1) "nil key struct literal") | ||||
| (assert (= (length @{1 2 nil 3}) 1) "nil key table literal") | ||||
| (assert (= (length (struct 1 2 nil 3)) 1) "nil key struct ctor") | ||||
| (assert (= (length (table 1 2 nil 3)) 1) "nil key table ctor") | ||||
|  | ||||
| (assert (= (length (struct (/ 0 0) 2 1 3)) 1) "nan key struct ctor") | ||||
| (assert (= (length (table (/ 0 0) 2 1 3)) 1) "nan key table ctor") | ||||
| (assert (= (length {1 2 nil 3}) 1) "nan key struct literal") | ||||
| (assert (= (length @{1 2 nil 3}) 1) "nan key table literal") | ||||
|  | ||||
| (assert (= (length (struct 2 1 3 nil)) 1) "nil value struct ctor") | ||||
| (assert (= (length (table 2 1 3 nil)) 1) "nil value table ctor") | ||||
| (assert (= (length {1 2 3 nil}) 1) "nil value struct literal") | ||||
| (assert (= (length @{1 2 3 nil}) 1) "nil value table literal") | ||||
|  | ||||
| # Regression Test | ||||
| (assert (= 1 (((compile '(fn [] 1) @{})))) "regression test") | ||||
|  | ||||
| # Regression Test #137 | ||||
| (def [a b c] (range 10)) | ||||
| (assert (= a 0) "regression #137 (1)") | ||||
| (assert (= b 1) "regression #137 (2)") | ||||
| (assert (= c 2) "regression #137 (3)") | ||||
|  | ||||
| (var [x y z] (range 10)) | ||||
| (assert (= x 0) "regression #137 (4)") | ||||
| (assert (= y 1) "regression #137 (5)") | ||||
| (assert (= z 2) "regression #137 (6)") | ||||
|  | ||||
| (assert (= true ;(map truthy? [0 "" true @{} {} [] '()])) "truthy values") | ||||
| (assert (= false ;(map truthy? [nil false])) "non-truthy values") | ||||
|  | ||||
| # Struct and Table duplicate elements | ||||
| (assert (= {:a 3 :b 2} {:a 1 :b 2 :a 3}) "struct literal duplicate keys") | ||||
| (assert (= {:a 3 :b 2} (struct :a 1 :b 2 :a 3)) "struct constructor duplicate keys") | ||||
| (assert (deep= @{:a 3 :b 2} @{:a 1 :b 2 :a 3}) "table literal duplicate keys") | ||||
| (assert (deep= @{:a 3 :b 2} (table :a 1 :b 2 :a 3)) "table constructor duplicate keys") | ||||
|  | ||||
| ## Polymorphic comparison -- Issue #272 | ||||
|  | ||||
| # confirm polymorphic comparison delegation to primitive comparators: | ||||
| (assert (= 0 (cmp 3 3)) "compare-primitive integers (1)") | ||||
| (assert (= -1 (cmp 3 5)) "compare-primitive integers (2)") | ||||
| (assert (= 1 (cmp "foo" "bar")) "compare-primitive strings") | ||||
| (assert (= 0 (compare 1 1)) "compare integers (1)") | ||||
| (assert (= -1 (compare 1 2)) "compare integers (2)") | ||||
| (assert (= 1 (compare "foo" "bar")) "compare strings (1)") | ||||
|  | ||||
| (assert (compare< 1 2 3 4 5 6) "compare less than integers") | ||||
| (assert (not (compare> 1 2 3 4 5 6)) "compare not greater than integers") | ||||
| (assert (compare< 1.0 2.0 3.0 4.0 5.0 6.0) "compare less than reals") | ||||
| (assert (compare> 6 5 4 3 2 1) "compare greater than integers") | ||||
| (assert (compare> 6.0 5.0 4.0 3.0 2.0 1.0) "compare greater than reals") | ||||
| (assert (not (compare< 6.0 5.0 4.0 3.0 2.0 1.0)) "compare less than reals") | ||||
| (assert (compare<= 1 2 3 3 4 5 6) "compare less than or equal to integers") | ||||
| (assert (compare<= 1.0 2.0 3.0 3.0 4.0 5.0 6.0) "compare less than or equal to reals") | ||||
| (assert (compare>= 6 5 4 4 3 2 1) "compare greater than or equal to integers") | ||||
| (assert (compare>= 6.0 5.0 4.0 4.0 3.0 2.0 1.0) "compare greater than or equal to reals") | ||||
| (assert (compare< 1.0 nil false true | ||||
|            (fiber/new (fn [] 1)) | ||||
|            "hi" | ||||
|            (quote hello) | ||||
|            :hello | ||||
|            (array 1 2 3) | ||||
|            (tuple 1 2 3) | ||||
|            (table "a" "b" "c" "d") | ||||
|            (struct 1 2 3 4) | ||||
|            (buffer "hi") | ||||
|            (fn [x] (+ x x)) | ||||
|            print) "compare type ordering") | ||||
|  | ||||
| # test polymorphic compare with 'objects' (table/setproto) | ||||
| (def mynum | ||||
|   @{:type :mynum :v 0 :compare | ||||
|     (fn [self other] | ||||
|       (case (type other) | ||||
|       :number (cmp (self :v) other) | ||||
|       :table (when (= (get other :type) :mynum) | ||||
|                (cmp (self :v) (other :v)))))}) | ||||
|  | ||||
| (let [n3 (table/setproto @{:v 3} mynum)] | ||||
|   (assert (= 0 (compare 3 n3)) "compare num to object (1)") | ||||
|   (assert (= -1 (compare n3 4)) "compare object to num (2)") | ||||
|   (assert (= 1 (compare (table/setproto @{:v 4} mynum) n3)) "compare object to object") | ||||
|   (assert (compare< 2 n3 4) "compare< poly") | ||||
|   (assert (compare> 4 n3 2) "compare> poly") | ||||
|   (assert (compare<= 2 3 n3 4) "compare<= poly") | ||||
|   (assert (compare= 3 n3 (table/setproto @{:v 3} mynum)) "compare= poly") | ||||
|   (assert (deep= (sorted @[4 5 n3 2] compare<) @[2 n3 4 5]) "polymorphic sort")) | ||||
|  | ||||
| (let [MAX_INT_64_STRING "9223372036854775807" | ||||
|       MAX_UINT_64_STRING "18446744073709551615" | ||||
|       MAX_INT_IN_DBL_STRING "9007199254740991" | ||||
|       NAN (math/log -1) | ||||
|       INF (/ 1 0) | ||||
|       MINUS_INF (/ -1 0) | ||||
|       compare-poly-tests | ||||
|       [[(int/s64 3) (int/u64 3) 0] | ||||
|        [(int/s64 -3) (int/u64 3) -1] | ||||
|        [(int/s64 3) (int/u64 2) 1] | ||||
|        [(int/s64 3) 3 0] [(int/s64 3) 4 -1] [(int/s64 3) -9 1] | ||||
|        [(int/u64 3) 3 0] [(int/u64 3) 4 -1] [(int/u64 3) -9 1] | ||||
|        [3 (int/s64 3) 0] [3 (int/s64 4) -1] [3 (int/s64 -5) 1] | ||||
|        [3 (int/u64 3) 0] [3 (int/u64 4) -1] [3 (int/u64 2) 1] | ||||
|        [(int/s64 MAX_INT_64_STRING) (int/u64 MAX_UINT_64_STRING) -1] | ||||
|        [(int/s64 MAX_INT_IN_DBL_STRING) (scan-number MAX_INT_IN_DBL_STRING) 0] | ||||
|        [(int/u64 MAX_INT_IN_DBL_STRING) (scan-number MAX_INT_IN_DBL_STRING) 0] | ||||
|        [(+ 1 (int/u64 MAX_INT_IN_DBL_STRING)) (scan-number MAX_INT_IN_DBL_STRING) 1] | ||||
|        [(int/s64 0) INF -1] [(int/u64 0) INF -1] | ||||
|        [MINUS_INF (int/u64 0) -1] [MINUS_INF (int/s64 0) -1] | ||||
|        [(int/s64 1) NAN 0] [NAN (int/u64 1) 0]]] | ||||
|   (each [x y c] compare-poly-tests | ||||
|     (assert (= c (compare x y)) (string/format "compare polymorphic %q %q %d" x y c)))) | ||||
|  | ||||
| (assert (= nil (any? [])) "any? 1") | ||||
| (assert (= nil (any? [false nil])) "any? 2") | ||||
| (assert (= nil (any? [nil false])) "any? 3") | ||||
| (assert (= 1 (any? [1])) "any? 4") | ||||
| (assert (nan? (any? [nil math/nan nil])) "any? 5") | ||||
| (assert (= true (any? [nil nil false nil nil true nil nil nil nil false :a nil])) "any? 6") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
							
								
								
									
										361
									
								
								test/suite0001.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								test/suite0001.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,361 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 1) | ||||
|  | ||||
| (assert (= 400 (math/sqrt 160000)) "sqrt(160000)=400") | ||||
|  | ||||
| (def test-struct {'def 1 'bork 2 'sam 3 'a 'b 'het @[1 2 3 4 5]}) | ||||
| (assert (= (get test-struct 'def) 1) "struct get") | ||||
| (assert (= (get test-struct 'bork) 2) "struct get") | ||||
| (assert (= (get test-struct 'sam) 3) "struct get") | ||||
| (assert (= (get test-struct 'a) 'b) "struct get") | ||||
| (assert (= :array (type (get test-struct 'het))) "struct get") | ||||
|  | ||||
| (defn myfun [x] | ||||
|   (var a 10) | ||||
|   (set a (do | ||||
|          (def y x) | ||||
|          (if x 8 9)))) | ||||
|  | ||||
| (assert (= (myfun true) 8) "check do form regression") | ||||
| (assert (= (myfun false) 9) "check do form regression") | ||||
|  | ||||
| (defn assert-many [f n e] | ||||
|  (var good true) | ||||
|  (loop [i :range [0 n]] | ||||
|   (if (not (f)) | ||||
|    (set good false))) | ||||
|  (assert good e)) | ||||
|  | ||||
| (assert-many (fn [] (>= 1 (math/random) 0)) 200 "(random) between 0 and 1") | ||||
|  | ||||
| ## Table prototypes | ||||
|  | ||||
| (def roottab @{ | ||||
|  :parentprop 123 | ||||
| }) | ||||
|  | ||||
| (def childtab @{ | ||||
|  :childprop 456 | ||||
| }) | ||||
|  | ||||
| (table/setproto childtab roottab) | ||||
|  | ||||
| (assert (= 123 (get roottab :parentprop)) "table get 1") | ||||
| (assert (= 123 (get childtab :parentprop)) "table get proto") | ||||
| (assert (= nil (get roottab :childprop)) "table get 2") | ||||
| (assert (= 456 (get childtab :childprop)) "proto no effect") | ||||
|  | ||||
| # Long strings | ||||
|  | ||||
| (assert (= "hello, world" `hello, world`) "simple long string") | ||||
| (assert (= "hello, \"world\"" `hello, "world"`) "long string with embedded quotes") | ||||
| (assert (= "hello, \\\\\\ \"world\"" `hello, \\\ "world"`) | ||||
|         "long string with embedded quotes and backslashes") | ||||
|  | ||||
| # More fiber semantics | ||||
|  | ||||
| (var myvar 0) | ||||
| (defn fiberstuff [&] | ||||
|   (++ myvar) | ||||
|   (def f (fiber/new (fn [&] (++ myvar) (debug) (++ myvar)))) | ||||
|   (resume f) | ||||
|   (++ myvar)) | ||||
|  | ||||
| (def myfiber (fiber/new fiberstuff :dey)) | ||||
|  | ||||
| (assert (= myvar 0) "fiber creation does not call fiber function") | ||||
| (resume myfiber) | ||||
| (assert (= myvar 2) "fiber debug statement breaks at proper point") | ||||
| (assert (= (fiber/status myfiber) :debug) "fiber enters debug state") | ||||
| (resume myfiber) | ||||
| (assert (= myvar 4) "fiber resumes properly from debug state") | ||||
| (assert (= (fiber/status myfiber) :dead) "fiber properly dies from debug state") | ||||
|  | ||||
| # Test max triangle program | ||||
|  | ||||
| # Find the maximum path from the top (root) | ||||
| # of the triangle to the leaves of the triangle. | ||||
|  | ||||
| (defn myfold [xs ys] | ||||
|   (let [xs1 [;xs 0] | ||||
|         xs2 [0 ;xs] | ||||
|         m1 (map + xs1 ys) | ||||
|         m2 (map + xs2 ys)] | ||||
|     (map max m1 m2))) | ||||
|  | ||||
| (defn maxpath [t] | ||||
|  (extreme > (reduce myfold () t))) | ||||
|  | ||||
| # Test it | ||||
| # Maximum path is 3 -> 10 -> 3 -> 9 for a total of 25 | ||||
|  | ||||
| (def triangle '[ | ||||
|  [3] | ||||
|  [7 10] | ||||
|  [4 3 7] | ||||
|  [8 9 1 3] | ||||
| ]) | ||||
|  | ||||
| (assert (= (maxpath triangle) 25) `max triangle`) | ||||
|  | ||||
| (assert (= (string/join @["one" "two" "three"]) "onetwothree") "string/join 1 argument") | ||||
| (assert (= (string/join @["one" "two" "three"] ", ") "one, two, three") "string/join 2 arguments") | ||||
| (assert (= (string/join @[] ", ") "") "string/join empty array") | ||||
|  | ||||
| (assert (= (string/find "123" "abc123def") 3) "string/find positive") | ||||
| (assert (= (string/find "1234" "abc123def") nil) "string/find negative") | ||||
|  | ||||
| # Test destructuring | ||||
| (do | ||||
|   (def test-tab @{:a 1 :b 2}) | ||||
|   (def {:a a :b b} test-tab) | ||||
|   (assert (= a 1) "dictionary destructuring 1") | ||||
|   (assert (= b 2) "dictionary destructuring 2")) | ||||
| (do | ||||
|   (def test-tab @{'a 1 'b 2 3 4}) | ||||
|   (def {'a a 'b b (+ 1 2) c} test-tab) | ||||
|   (assert (= a 1) "dictionary destructuring 3") | ||||
|   (assert (= b 2) "dictionary destructuring 4") | ||||
|   (assert (= c 4) "dictionary destructuring 5 - expression as key")) | ||||
| (let [test-tuple [:a :b 1 2]] | ||||
|   (def [a b one two] test-tuple) | ||||
|   (assert (= a :a) "tuple destructuring 1") | ||||
|   (assert (= b :b) "tuple destructuring 2") | ||||
|   (assert (= two 2) "tuple destructuring 3")) | ||||
| (let [test-tuple [:a :b 1 2]] | ||||
|   (def [a & rest] test-tuple) | ||||
|   (assert (= a :a) "tuple destructuring 4 - rest") | ||||
|   (assert (= rest [:b 1 2]) "tuple destructuring 5 - rest")) | ||||
| (do | ||||
|   (def [a b & rest] [:a :b nil :d]) | ||||
|   (assert (= a :a) "tuple destructuring 6 - rest") | ||||
|   (assert (= b :b) "tuple destructuring 7 - rest") | ||||
|   (assert (= rest [nil :d]) "tuple destructuring 8 - rest")) | ||||
| (do | ||||
|   (def [[a b] x & rest] [[1 2] :a :c :b :a]) | ||||
|   (assert (= a 1) "tuple destructuring 9 - rest") | ||||
|   (assert (= b 2) "tuple destructuring 10 - rest") | ||||
|   (assert (= x :a) "tuple destructuring 11 - rest") | ||||
|   (assert (= rest [:c :b :a]) "tuple destructuring 12 - rest")) | ||||
| (do | ||||
|   (def [a b & rest] [:a :b]) | ||||
|   (assert (= a :a) "tuple destructuring 13 - rest") | ||||
|   (assert (= b :b) "tuple destructuring 14 - rest") | ||||
|   (assert (= rest []) "tuple destructuring 15 - rest")) | ||||
|  | ||||
| (do | ||||
|   (def [[a b & r1] c & r2] [[:a :b 1 2] :c 3 4]) | ||||
|   (assert (= a :a) "tuple destructuring 16 - rest") | ||||
|   (assert (= b :b) "tuple destructuring 17 - rest") | ||||
|   (assert (= c :c) "tuple destructuring 18 - rest") | ||||
|   (assert (= r1 [1 2]) "tuple destructuring 19 - rest") | ||||
|   (assert (= r2 [3 4]) "tuple destructuring 20 - rest")) | ||||
|  | ||||
| # Marshal | ||||
|  | ||||
| (def um-lookup (env-lookup (fiber/getenv (fiber/current)))) | ||||
| (def m-lookup (invert um-lookup)) | ||||
|  | ||||
| (defn testmarsh [x msg] | ||||
|   (def marshx (marshal x m-lookup)) | ||||
|   (def out (marshal (unmarshal marshx um-lookup) m-lookup)) | ||||
|   (assert (= (string marshx) (string out)) msg)) | ||||
|  | ||||
| (testmarsh nil "marshal nil") | ||||
| (testmarsh false "marshal false") | ||||
| (testmarsh true "marshal true") | ||||
| (testmarsh 1 "marshal small integers") | ||||
| (testmarsh -1 "marshal integers (-1)") | ||||
| (testmarsh 199 "marshal small integers (199)") | ||||
| (testmarsh 5000 "marshal medium integers (5000)") | ||||
| (testmarsh -5000 "marshal small integers (-5000)") | ||||
| (testmarsh 10000 "marshal large integers (10000)") | ||||
| (testmarsh -10000 "marshal large integers (-10000)") | ||||
| (testmarsh 1.0 "marshal double") | ||||
| (testmarsh "doctordolittle" "marshal string") | ||||
| (testmarsh :chickenshwarma "marshal symbol") | ||||
| (testmarsh @"oldmcdonald" "marshal buffer") | ||||
| (testmarsh @[1 2 3 4 5] "marshal array") | ||||
| (testmarsh [tuple 1 2 3 4 5] "marshal tuple") | ||||
| (testmarsh @{1 2 3 4}  "marshal table") | ||||
| (testmarsh {1 2 3 4}  "marshal struct") | ||||
| (testmarsh (fn [x] x) "marshal function 0") | ||||
| (testmarsh (fn name [x] x) "marshal function 1") | ||||
| (testmarsh (fn [x] (+ 10 x 2)) "marshal function 2") | ||||
| (testmarsh (fn thing [x] (+ 11 x x 30)) "marshal function 3") | ||||
| (testmarsh map "marshal function 4") | ||||
| (testmarsh reduce "marshal function 5") | ||||
| (testmarsh (fiber/new (fn [] (yield 1) 2)) "marshal simple fiber 1") | ||||
| (testmarsh (fiber/new (fn [&] (yield 1) 2)) "marshal simple fiber 2") | ||||
|  | ||||
| (def strct {:a @[nil]}) | ||||
| (put (strct :a) 0 strct) | ||||
| (testmarsh strct "cyclic struct") | ||||
|  | ||||
| # Large functions | ||||
| (def manydefs (seq [i :range [0 300]] (tuple 'def (gensym) (string "value_" i)))) | ||||
| (array/push manydefs (tuple * 10000 3 5 7 9)) | ||||
| (def f (compile ['do ;manydefs] (fiber/getenv (fiber/current)))) | ||||
| (assert (= (f) (* 10000 3 5 7 9)) "long function compilation") | ||||
|  | ||||
| # Some higher order functions and macros | ||||
|  | ||||
| (def my-array @[1 2 3 4 5 6]) | ||||
| (def x (if-let [x (get my-array 5)] x)) | ||||
| (assert (= x 6) "if-let") | ||||
| (def x (if-let [y (get @{} :key)] 10 nil)) | ||||
| (assert (not x) "if-let 2") | ||||
|  | ||||
| (assert (= 14 (sum (map inc @[1 2 3 4]))) "sum map") | ||||
| (def myfun (juxt + - * /)) | ||||
| (assert (= [2 -2 2 0.5] (myfun 2)) "juxt") | ||||
|  | ||||
| # Case statements | ||||
| (assert | ||||
|   (= :six (case (+ 1 2 3) | ||||
|             1 :one | ||||
|             2 :two | ||||
|             3 :three | ||||
|             4 :four | ||||
|             5 :five | ||||
|             6 :six | ||||
|             7 :seven | ||||
|             8 :eight | ||||
|             9 :nine)) "case macro") | ||||
|  | ||||
| (assert (= 7 (case :a :b 5 :c 6 :u 10 7)) "case with default") | ||||
|  | ||||
| # Testing the loop and seq macros | ||||
| (def xs (apply tuple (seq [x :range [0 10] :when (even? x)] (tuple (/ x 2) x)))) | ||||
| (assert (= xs '((0 0) (1 2) (2 4) (3 6) (4 8))) "seq macro 1") | ||||
|  | ||||
| (def xs (apply tuple (seq [x :down [8 -2] :when (even? x)] (tuple (/ x 2) x)))) | ||||
| (assert (= xs '((4 8) (3 6) (2 4) (1 2) (0 0))) "seq macro 2") | ||||
|  | ||||
| (def xs (catseq [x :range [0 3]] [x x])) | ||||
| (assert (deep= xs @[0 0 1 1 2 2]) "catseq") | ||||
|  | ||||
| # :range-to and :down-to | ||||
| (assert (deep= (seq [x :range-to [0 10]] x) (seq [x :range [0 11]] x)) "loop :range-to") | ||||
| (assert (deep= (seq [x :down-to [10 0]] x) (seq [x :down [10 -1]] x)) "loop :down-to") | ||||
|  | ||||
| # Some testing for not= | ||||
| (assert (not= 1 1 0) "not= 1") | ||||
| (assert (not= 0 1 1) "not= 2") | ||||
|  | ||||
| # Closure in while loop | ||||
| (def closures (seq [i :range [0 5]] (fn [] i))) | ||||
| (assert (= 0 ((get closures 0))) "closure in loop 0") | ||||
| (assert (= 1 ((get closures 1))) "closure in loop 1") | ||||
| (assert (= 2 ((get closures 2))) "closure in loop 2") | ||||
| (assert (= 3 ((get closures 3))) "closure in loop 3") | ||||
| (assert (= 4 ((get closures 4))) "closure in loop 4") | ||||
|  | ||||
| # More numerical tests | ||||
| (assert (= 1 1.0) "numerical equal 1") | ||||
| (assert (= 0 0.0) "numerical equal 2") | ||||
| (assert (= 0 -0.0) "numerical equal 3") | ||||
| (assert (= 2_147_483_647 2_147_483_647.0) "numerical equal 4") | ||||
| (assert (= -2_147_483_648 -2_147_483_648.0) "numerical equal 5") | ||||
|  | ||||
| # Array tests | ||||
|  | ||||
| (defn array= | ||||
|   "Check if two arrays are equal in an element by element comparison" | ||||
|   [a b] | ||||
|   (if (and (array? a) (array? b)) | ||||
|     (= (apply tuple a) (apply tuple b)))) | ||||
| (assert (= (apply tuple @[1 2 3 4 5]) (tuple 1 2 3 4 5)) "array to tuple") | ||||
| (def arr (array)) | ||||
| (array/push arr :hello) | ||||
| (array/push arr :world) | ||||
| (assert (array= arr @[:hello :world]) "array comparison") | ||||
| (assert (array= @[1 2 3 4 5] @[1 2 3 4 5]) "array comparison 2") | ||||
| (assert (array= @[:one :two :three :four :five] @[:one :two :three :four :five]) "array comparison 3") | ||||
| (assert (array= (array/slice @[1 2 3] 0 2) @[1 2]) "array/slice 1") | ||||
| (assert (array= (array/slice @[0 7 3 9 1 4] 2 -2) @[3 9 1]) "array/slice 2") | ||||
|  | ||||
| # Even and odd | ||||
|  | ||||
| (assert (odd? 9) "odd? 1") | ||||
| (assert (odd? -9) "odd? 2") | ||||
| (assert (not (odd? 10)) "odd? 3") | ||||
| (assert (not (odd? 0)) "odd? 4") | ||||
| (assert (not (odd? -10)) "odd? 5") | ||||
| (assert (not (odd? 1.1)) "odd? 6") | ||||
| (assert (not (odd? -0.1)) "odd? 7") | ||||
| (assert (not (odd? -1.1)) "odd? 8") | ||||
| (assert (not (odd? -1.6)) "odd? 9") | ||||
|  | ||||
| (assert (even? 10) "even? 1") | ||||
| (assert (even? -10) "even? 2") | ||||
| (assert (even? 0) "even? 3") | ||||
| (assert (not (even? 9)) "even? 4") | ||||
| (assert (not (even? -9)) "even? 5") | ||||
| (assert (not (even? 0.1)) "even? 6") | ||||
| (assert (not (even? -0.1)) "even? 7") | ||||
| (assert (not (even? -10.1)) "even? 8") | ||||
| (assert (not (even? -10.6)) "even? 9") | ||||
|  | ||||
| # Map arities | ||||
| (assert (deep= (map inc [1 2 3]) @[2 3 4])) | ||||
| (assert (deep= (map + [1 2 3] [10 20 30]) @[11 22 33])) | ||||
| (assert (deep= (map + [1 2 3] [10 20 30] [100 200 300]) @[111 222 333])) | ||||
| (assert (deep= (map + [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000]) @[1111 2222 3333])) | ||||
| (assert (deep= (map + [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000] [10000 20000 30000]) @[11111 22222 33333])) | ||||
|  | ||||
| # Mapping uses the shortest sequence | ||||
| (assert (deep= (map + [1 2 3 4] [10 20 30]) @[11 22 33])) | ||||
| (assert (deep= (map + [1 2 3 4] [10 20 30] [100 200]) @[111 222])) | ||||
| (assert (deep= (map + [1 2 3 4] [10 20 30] [100 200] [1000]) @[1111])) | ||||
|  | ||||
| # Sort function | ||||
| (assert (deep= | ||||
|           (range 99) | ||||
|           (sort (mapcat (fn [[x y z]] [z y x]) (partition 3 (range 99))))) "sort 5") | ||||
| (assert (<= ;(sort (map (fn [x] (math/random)) (range 1000)))) "sort 6") | ||||
|  | ||||
| # And and or | ||||
|  | ||||
| (assert (= (and true true) true) "and true true") | ||||
| (assert (= (and true false) false) "and true false") | ||||
| (assert (= (and false true) false) "and false true") | ||||
| (assert (= (and true true true) true) "and true true true") | ||||
| (assert (= (and 0 1 2) 2) "and 0 1 2") | ||||
| (assert (= (and 0 1 nil) nil) "and 0 1 nil") | ||||
| (assert (= (and 1) 1) "and 1") | ||||
| (assert (= (and) true) "and with no arguments") | ||||
| (assert (= (and 1 true) true) "and with trailing true") | ||||
| (assert (= (and 1 true 2) 2) "and with internal true") | ||||
|  | ||||
| (assert (= (or true true) true) "or true true") | ||||
| (assert (= (or true false) true) "or true false") | ||||
| (assert (= (or false true) true) "or false true") | ||||
| (assert (= (or false false) false) "or false true") | ||||
| (assert (= (or true true false) true) "or true true false") | ||||
| (assert (= (or 0 1 2) 0) "or 0 1 2") | ||||
| (assert (= (or nil 1 2) 1) "or nil 1 2") | ||||
| (assert (= (or 1) 1) "or 1") | ||||
| (assert (= (or) nil) "or with no arguments") | ||||
|  | ||||
| (end-suite) | ||||
| @@ -19,20 +19,48 @@ | ||||
| # IN THE SOFTWARE. | ||||
| 
 | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
| (start-suite 2) | ||||
| 
 | ||||
| # 8a346ec | ||||
| (assert (= (string/join @["one" "two" "three"]) "onetwothree") | ||||
|         "string/join 1 argument") | ||||
| (assert (= (string/join @["one" "two" "three"] ", ") "one, two, three") | ||||
|         "string/join 2 arguments") | ||||
| (assert (= (string/join @[] ", ") "") "string/join empty array") | ||||
| # Buffer stuff | ||||
| (defn buffer= | ||||
|   [a b] | ||||
|   (= (string a) (string b))) | ||||
| 
 | ||||
| (assert (= (string/find "123" "abc123def") 3) "string/find positive") | ||||
| (assert (= (string/find "1234" "abc123def") nil) "string/find negative") | ||||
| (assert (buffer= @"abcd" @"abcd") "buffer equal 1") | ||||
| (assert (buffer= @"abcd" (buffer "ab" "cd")) "buffer equal 2") | ||||
| (assert (not= @"" @"") "buffer not equal 1") | ||||
| (assert (not= @"abcd" @"abcd") "buffer not equal 2") | ||||
| 
 | ||||
| (defn buffer-factory | ||||
|   [] | ||||
|   @"im am a buffer") | ||||
| 
 | ||||
| (assert (not= (buffer-factory) (buffer-factory)) "buffer instantiation") | ||||
| 
 | ||||
| (assert (= (length @"abcdef") 6) "buffer length") | ||||
| 
 | ||||
| # Looping idea | ||||
| (def xs | ||||
|   (seq [x :in [-1 0 1] y :in [-1 0 1] :when (not= x y 0)] (tuple x y))) | ||||
| (def txs (apply tuple xs)) | ||||
| 
 | ||||
| (assert (= txs [[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]]) "nested seq") | ||||
| 
 | ||||
| # Generators | ||||
| (def gen (generate [x :range [0 100] :when (pos? (% x 4))] x)) | ||||
| (var gencount 0) | ||||
| (loop [x :in gen] | ||||
|   (++ gencount) | ||||
|   (assert (pos? (% x 4)) "generate in loop")) | ||||
| (assert (= gencount 75) "generate loop count") | ||||
| 
 | ||||
| # Check x:digits: works as symbol and not a hex number | ||||
| (def x1 100) | ||||
| (assert (= x1 100) "x1 as symbol") | ||||
| (def X1 100) | ||||
| (assert (= X1 100) "X1 as symbol") | ||||
| 
 | ||||
| # String functions | ||||
| # f41dab8f6 | ||||
| (assert (= 3 (string/find "abc" "   abcdefghijklmnop")) "string/find 1") | ||||
| (assert (= 0 (string/find "A" "A")) "string/find 2") | ||||
| (assert (string/has-prefix? "" "foo") "string/has-prefix? 1") | ||||
| @@ -41,100 +69,52 @@ | ||||
| (assert (string/has-suffix? "" "foo") "string/has-suffix? 1") | ||||
| (assert (string/has-suffix? "oo" "foo") "string/has-suffix? 2") | ||||
| (assert (not (string/has-suffix? "f" "foo")) "string/has-suffix? 3") | ||||
| (assert (= (string/replace "X" "." "XXX...XXX...XXX")  ".XX...XXX...XXX") | ||||
|         "string/replace 1") | ||||
| (assert (= (string/replace-all "X" "." "XXX...XXX...XXX") "...............") | ||||
|         "string/replace-all 1") | ||||
| (assert (= (string/replace-all "XX" "." "XXX...XXX...XXX") ".X....X....X") | ||||
|         "string/replace-all 2") | ||||
| (assert (= (string/replace "xx" string/ascii-upper "xxyxyxyxxxy") | ||||
|            "XXyxyxyxxxy") "string/replace function") | ||||
| (assert (= (string/replace-all "xx" string/ascii-upper "xxyxyxyxxxy") | ||||
|            "XXyxyxyXXxy") "string/replace-all function") | ||||
| (assert (= (string/replace "x" 12 "xyx") "12yx") | ||||
|         "string/replace stringable") | ||||
| (assert (= (string/replace-all "x" 12 "xyx") "12y12") | ||||
|         "string/replace-all stringable") | ||||
| (assert (= (string/ascii-lower "ABCabc&^%!@:;.") "abcabc&^%!@:;.") | ||||
|         "string/ascii-lower") | ||||
| (assert (= (string/ascii-upper "ABCabc&^%!@:;.") "ABCABC&^%!@:;.") | ||||
|         "string/ascii-lower") | ||||
| (assert (= (string/replace "X" "." "XXX...XXX...XXX")  ".XX...XXX...XXX") "string/replace 1") | ||||
| (assert (= (string/replace-all "X" "." "XXX...XXX...XXX") "...............") "string/replace-all 1") | ||||
| (assert (= (string/replace-all "XX" "." "XXX...XXX...XXX") ".X....X....X") "string/replace-all 2") | ||||
| (assert (= (string/replace "xx" string/ascii-upper "xxyxyxyxxxy") "XXyxyxyxxxy") "string/replace function") | ||||
| (assert (= (string/replace-all "xx" string/ascii-upper "xxyxyxyxxxy") "XXyxyxyXXxy") "string/replace-all function") | ||||
| (assert (= (string/replace "x" 12 "xyx") "12yx") "string/replace stringable") | ||||
| (assert (= (string/replace-all "x" 12 "xyx") "12y12") "string/replace-all stringable") | ||||
| (assert (= (string/ascii-lower "ABCabc&^%!@:;.") "abcabc&^%!@:;.") "string/ascii-lower") | ||||
| (assert (= (string/ascii-upper "ABCabc&^%!@:;.") "ABCABC&^%!@:;.") "string/ascii-lower") | ||||
| (assert (= (string/reverse "") "") "string/reverse 1") | ||||
| (assert (= (string/reverse "a") "a") "string/reverse 2") | ||||
| (assert (= (string/reverse "abc") "cba") "string/reverse 3") | ||||
| (assert (= (string/reverse "abcd") "dcba") "string/reverse 4") | ||||
| (assert (= (string/join @["one" "two" "three"] ",") "one,two,three") | ||||
|         "string/join 1") | ||||
| (assert (= (string/join @["one" "two" "three"] ", ") "one, two, three") | ||||
|         "string/join 2") | ||||
| (assert (= (string/join @["one" "two" "three"]) "onetwothree") | ||||
|         "string/join 3") | ||||
| (assert (= (string/join @["one" "two" "three"] ",") "one,two,three") "string/join 1") | ||||
| (assert (= (string/join @["one" "two" "three"] ", ") "one, two, three") "string/join 2") | ||||
| (assert (= (string/join @["one" "two" "three"]) "onetwothree") "string/join 3") | ||||
| (assert (= (string/join @[] "hi") "") "string/join 4") | ||||
| (assert (= (string/trim " abcd ") "abcd") "string/trim 1") | ||||
| (assert (= (string/trim "abcd \t\t\r\f") "abcd") "string/trim 2") | ||||
| (assert (= (string/trim "\n\n\t abcd") "abcd") "string/trim 3") | ||||
| (assert (= (string/trim "") "") "string/trim 4") | ||||
| (assert (= (string/triml " abcd ") "abcd ") "string/triml 1") | ||||
| (assert (= (string/triml "\tabcd \t\t\r\f") "abcd \t\t\r\f") | ||||
|         "string/triml 2") | ||||
| (assert (= (string/triml "\tabcd \t\t\r\f") "abcd \t\t\r\f") "string/triml 2") | ||||
| (assert (= (string/triml "abcd ") "abcd ") "string/triml 3") | ||||
| (assert (= (string/trimr " abcd ") " abcd") "string/trimr 1") | ||||
| (assert (= (string/trimr "\tabcd \t\t\r\f") "\tabcd") "string/trimr 2") | ||||
| (assert (= (string/trimr " abcd") " abcd") "string/trimr 3") | ||||
| (assert (deep= (string/split "," "one,two,three") @["one" "two" "three"]) | ||||
|         "string/split 1") | ||||
| (assert (deep= (string/split "," "onetwothree") @["onetwothree"]) | ||||
|         "string/split 2") | ||||
| (assert (deep= (string/find-all "e" "onetwothree") @[2 9 10]) | ||||
|         "string/find-all 1") | ||||
| (assert (deep= (string/find-all "," "onetwothree") @[]) | ||||
|         "string/find-all 2") | ||||
| (assert (deep= (string/split "," "one,two,three") @["one" "two" "three"]) "string/split 1") | ||||
| (assert (deep= (string/split "," "onetwothree") @["onetwothree"]) "string/split 2") | ||||
| (assert (deep= (string/find-all "e" "onetwothree") @[2 9 10]) "string/find-all 1") | ||||
| (assert (deep= (string/find-all "," "onetwothree") @[]) "string/find-all 2") | ||||
| 
 | ||||
| # b26a7bb22 | ||||
| (assert-error "string/find error 1" (string/find "" "abcd")) | ||||
| (assert-error "string/split error 1" (string/split "" "abcd")) | ||||
| (assert-error "string/replace error 1" (string/replace "" "." "abcd")) | ||||
| (assert-error "string/replace-all error 1" | ||||
|               (string/replace-all "" "." "abcdabcd")) | ||||
| (assert-error "string/replace-all error 1" (string/replace-all "" "." "abcdabcd")) | ||||
| (assert-error "string/find-all error 1" (string/find-all "" "abcd")) | ||||
| 
 | ||||
| # String bugs | ||||
| # bcba0c027 | ||||
| (assert (deep= (string/find-all "qq" "qqq") @[0 1]) "string/find-all 1") | ||||
| (assert (deep= (string/find-all "q" "qqq") @[0 1 2]) "string/find-all 2") | ||||
| (assert (deep= (string/split "qq" "1qqqqz") @["1" "" "z"]) "string/split 1") | ||||
| (assert (deep= (string/split "aa" "aaa") @["" "a"]) "string/split 2") | ||||
| 
 | ||||
| # some tests for string/format | ||||
| # 0f0c415 | ||||
| (assert (= (string/format "pi = %6.3f" math/pi) "pi =  3.142") "%6.3f") | ||||
| (assert (= (string/format "pi = %+6.3f" math/pi) "pi = +3.142") "%6.3f") | ||||
| (assert (= (string/format "pi = %40.20g" math/pi) | ||||
|            "pi =                     3.141592653589793116") "%6.3f") | ||||
| 
 | ||||
| (assert (= (string/format "🐼 = %6.3f" math/pi) "🐼 =  3.142") "UTF-8") | ||||
| (assert (= (string/format "π = %.8g" math/pi) "π = 3.1415927") "π") | ||||
| (assert (= (string/format "\xCF\x80 = %.8g" math/pi) "\xCF\x80 = 3.1415927") | ||||
|         "\xCF\x80") | ||||
| 
 | ||||
| # String check-set | ||||
| # b4e25e559 | ||||
| (assert (string/check-set "abc" "a") "string/check-set 1") | ||||
| (assert (not (string/check-set "abc" "z")) "string/check-set 2") | ||||
| (assert (string/check-set "abc" "abc") "string/check-set 3") | ||||
| (assert (string/check-set "abc" "") "string/check-set 4") | ||||
| (assert (not (string/check-set "" "aabc")) "string/check-set 5") | ||||
| (assert (not (string/check-set "abc" "abcdefg")) "string/check-set 6") | ||||
| 
 | ||||
| # Trim empty string | ||||
| # issue #174 - 9b605b27b | ||||
| (assert (= "" (string/trim " ")) "string/trim regression") | ||||
| 
 | ||||
| # Keyword and Symbol slice | ||||
| # e9911fee4 | ||||
| (assert (= :keyword (keyword/slice "some_keyword_slice" 5 12)) | ||||
|         "keyword slice") | ||||
| (assert (= 'symbol (symbol/slice "some_symbol_slice" 5 11)) "symbol slice") | ||||
| # Check if abstract test works | ||||
| (assert (abstract? stdout) "abstract? stdout") | ||||
| (assert (abstract? stdin) "abstract? stdin") | ||||
| (assert (abstract? stderr) "abstract? stderr") | ||||
| (assert (not (abstract? nil)) "not abstract? nil") | ||||
| (assert (not (abstract? 1)) "not abstract? 1") | ||||
| (assert (not (abstract? 3)) "not abstract? 3") | ||||
| (assert (not (abstract? 5)) "not abstract? 5") | ||||
| 
 | ||||
| (end-suite) | ||||
| 
 | ||||
							
								
								
									
										497
									
								
								test/suite0003.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										497
									
								
								test/suite0003.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,497 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 3) | ||||
|  | ||||
| (assert (= (length (range 10)) 10) "(range 10)") | ||||
| (assert (= (length (range 1 10)) 9) "(range 1 10)") | ||||
| (assert (deep= @{:a 1 :b 2 :c 3} (zipcoll '[:a :b :c] '[1 2 3])) "zipcoll") | ||||
|  | ||||
| (def- a 100) | ||||
| (assert (= a 100) "def-") | ||||
|  | ||||
| (assert (= :first | ||||
|           (match @[1 3 5] | ||||
|                  @[x y z] :first | ||||
|                  :second)) "match 1") | ||||
|  | ||||
| (def val1 :avalue) | ||||
| (assert (= :second | ||||
|           (match val1 | ||||
|                  @[x y z] :first | ||||
|                  :avalue :second | ||||
|                  :third)) "match 2") | ||||
|  | ||||
| (assert (= 100 | ||||
|            (match @[50 40] | ||||
|                   @[x x] (* x 3) | ||||
|                   @[x y] (+ x y 10) | ||||
|                   0)) "match 3") | ||||
|  | ||||
| # Edge case should cause old compilers to fail due to | ||||
| # if statement optimization | ||||
| (var var-a 1) | ||||
| (var var-b (if false 2 (string "hello"))) | ||||
|  | ||||
| (assert (= var-b "hello") "regression 1") | ||||
|  | ||||
| # Scan number | ||||
|  | ||||
| (assert (= 1 (scan-number "1")) "scan-number 1") | ||||
| (assert (= -1 (scan-number "-1")) "scan-number -1") | ||||
| (assert (= 1.3e4 (scan-number "1.3e4")) "scan-number 1.3e4") | ||||
|  | ||||
| # Some macros | ||||
|  | ||||
| (assert (= 2 (if-not 1 3 2)) "if-not 1") | ||||
| (assert (= 3 (if-not false 3)) "if-not 2") | ||||
| (assert (= 3 (if-not nil 3 2)) "if-not 3") | ||||
| (assert (= nil (if-not true 3)) "if-not 4") | ||||
|  | ||||
| (assert (= 4 (unless false (+ 1 2 3) 4)) "unless") | ||||
|  | ||||
| (def res @{}) | ||||
| (loop [[k v] :pairs @{1 2 3 4 5 6}] | ||||
|   (put res k v)) | ||||
| (assert (and | ||||
|           (= (get res 1) 2) | ||||
|           (= (get res 3) 4) | ||||
|           (= (get res 5) 6)) "loop :pairs") | ||||
|  | ||||
| # Another regression test - no segfaults | ||||
| (defn afn [x] x) | ||||
| (var afn-var afn) | ||||
| (var identity-var identity) | ||||
| (var map-var map) | ||||
| (var not-var not) | ||||
| (assert (= 1 (try (afn-var) ([err] 1))) "bad arity 1") | ||||
| (assert (= 4 (try ((fn [x y] (+ x y)) 1) ([_] 4))) "bad arity 2") | ||||
| (assert (= 1 (try (identity-var) ([err] 1))) "bad arity 3") | ||||
| (assert (= 1 (try (map-var) ([err] 1))) "bad arity 4") | ||||
| (assert (= 1 (try (not-var) ([err] 1))) "bad arity 5") | ||||
|  | ||||
| # Assembly test | ||||
| # Fibonacci sequence, implemented with naive recursion. | ||||
| (def fibasm (asm '{ | ||||
|   :arity 1 | ||||
|   :bytecode [ | ||||
|     (ltim 1 0 0x2)      # $1 = $0 < 2 | ||||
|     (jmpif 1 :done)     # if ($1) goto :done | ||||
|     (lds 1)             # $1 = self | ||||
|     (addim 0 0 -0x1)    # $0 = $0 - 1 | ||||
|     (push 0)            # push($0), push argument for next function call | ||||
|     (call 2 1)          # $2 = call($1) | ||||
|     (addim 0 0 -0x1)    # $0 = $0 - 1 | ||||
|     (push 0)            # push($0) | ||||
|     (call 0 1)          # $0 = call($1) | ||||
|     (add 0 0 2)        # $0 = $0 + $2 (integers) | ||||
|     :done | ||||
|     (ret 0)             # return $0 | ||||
|   ] | ||||
| })) | ||||
|  | ||||
| (assert (= 0 (fibasm 0)) "fibasm 1") | ||||
| (assert (= 1 (fibasm 1)) "fibasm 2") | ||||
| (assert (= 55 (fibasm 10)) "fibasm 3") | ||||
| (assert (= 6765 (fibasm 20)) "fibasm 4") | ||||
|  | ||||
| # Calling non functions | ||||
|  | ||||
| (assert (= 1 ({:ok 1} :ok)) "calling struct") | ||||
| (assert (= 2 (@{:ok 2} :ok)) "calling table") | ||||
| (assert (= :bad (try ((identity @{:ok 2}) :ok :no) ([err] :bad))) "calling table too many arguments") | ||||
| (assert (= :bad (try ((identity :ok) @{:ok 2} :no) ([err] :bad))) "calling keyword too many arguments") | ||||
| (assert (= :oops (try ((+ 2 -1) 1) ([err] :oops))) "calling number fails") | ||||
|  | ||||
| # Method test | ||||
|  | ||||
| (def Dog @{:bark (fn bark [self what] (string (self :name) " says " what "!"))}) | ||||
| (defn make-dog | ||||
|   [name] | ||||
|   (table/setproto @{:name name} Dog)) | ||||
|  | ||||
| (assert (= "fido" ((make-dog "fido") :name)) "oo 1") | ||||
| (def spot (make-dog "spot")) | ||||
| (assert (= "spot says hi!" (:bark spot "hi")) "oo 2") | ||||
|  | ||||
| # Negative tests | ||||
|  | ||||
| (assert-error "+ check types" (+ 1 ())) | ||||
| (assert-error "- check types" (- 1 ())) | ||||
| (assert-error "* check types" (* 1 ())) | ||||
| (assert-error "/ check types" (/ 1 ())) | ||||
| (assert-error "band check types" (band 1 ())) | ||||
| (assert-error "bor check types" (bor 1 ())) | ||||
| (assert-error "bxor check types" (bxor 1 ())) | ||||
| (assert-error "bnot check types" (bnot ())) | ||||
|  | ||||
| # Buffer blitting | ||||
|  | ||||
| (def b (buffer/new-filled 100)) | ||||
| (buffer/bit-set b 100) | ||||
| (buffer/bit-clear b 100) | ||||
| (assert (zero? (sum b)) "buffer bit set and clear") | ||||
| (buffer/bit-toggle b 101) | ||||
| (assert (= 32 (sum b)) "buffer bit set and clear") | ||||
|  | ||||
| (def b2 @"hello world") | ||||
|  | ||||
| (buffer/blit b2 "joyto ") | ||||
| (assert (= (string b2) "joyto world") "buffer/blit 1") | ||||
|  | ||||
| (buffer/blit b2 "joyto" 6) | ||||
| (assert (= (string b2) "joyto joyto") "buffer/blit 2") | ||||
|  | ||||
| (buffer/blit b2 "abcdefg" 5 6) | ||||
| (assert (= (string b2) "joytogjoyto") "buffer/blit 3") | ||||
|  | ||||
| # Buffer self blitting, check for use after free | ||||
| (def buf1 @"1234567890") | ||||
| (buffer/blit buf1 buf1 -1) | ||||
| (buffer/blit buf1 buf1 -1) | ||||
| (buffer/blit buf1 buf1 -1) | ||||
| (buffer/blit buf1 buf1 -1) | ||||
| (assert (= (string buf1) (string/repeat "1234567890" 16)) "buffer blit against self") | ||||
|  | ||||
| # Buffer push word | ||||
|  | ||||
| (def b3 @"") | ||||
| (buffer/push-word b3 0xFF 0x11) | ||||
| (assert (= 8 (length b3)) "buffer/push-word 1") | ||||
| (assert (= "\xFF\0\0\0\x11\0\0\0" (string b3)) "buffer/push-word 2") | ||||
| (buffer/clear b3) | ||||
| (buffer/push-word b3 0xFFFFFFFF 0x1100) | ||||
| (assert (= 8 (length b3)) "buffer/push-word 3") | ||||
| (assert (= "\xFF\xFF\xFF\xFF\0\x11\0\0" (string b3)) "buffer/push-word 4") | ||||
|  | ||||
| # Buffer push string | ||||
|  | ||||
| (def b4 (buffer/new-filled 10 0)) | ||||
| (buffer/push-string b4 b4) | ||||
| (assert (= "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" (string b4)) "buffer/push-buffer 1") | ||||
| (def b5 @"123") | ||||
| (buffer/push-string b5 "456" @"789") | ||||
| (assert (= "123456789" (string b5)) "buffer/push-buffer 2") | ||||
|  | ||||
| # Check for bugs with printing self with buffer/format | ||||
|  | ||||
| (def buftemp @"abcd") | ||||
| (assert (= (string (buffer/format buftemp "---%p---" buftemp)) `abcd---@"abcd"---`) "buffer/format on self 1") | ||||
| (def buftemp @"abcd") | ||||
| (assert (= (string (buffer/format buftemp "---%p %p---" buftemp buftemp)) `abcd---@"abcd" @"abcd"---`) "buffer/format on self 2") | ||||
|  | ||||
| # Peg | ||||
|  | ||||
| (defn check-match | ||||
|   [pat text should-match] | ||||
|   (def result (peg/match pat text)) | ||||
|   (assert (= (not should-match) (not result)) (string "check-match " text))) | ||||
|  | ||||
| (defn check-deep | ||||
|   [pat text what] | ||||
|   (def result (peg/match pat text)) | ||||
|   (assert (deep= result what) (string "check-deep " text))) | ||||
|  | ||||
| # Just numbers | ||||
|  | ||||
| (check-match '(* 4 -1) "abcd" true) | ||||
| (check-match '(* 4 -1) "abc" false) | ||||
| (check-match '(* 4 -1) "abcde" false) | ||||
|  | ||||
| # Simple pattern | ||||
|  | ||||
| (check-match '(* (some (range "az" "AZ")) -1) "hello" true) | ||||
| (check-match '(* (some (range "az" "AZ")) -1) "hello world" false) | ||||
| (check-match '(* (some (range "az" "AZ")) -1) "1he11o" false) | ||||
| (check-match '(* (some (range "az" "AZ")) -1) "" false) | ||||
|  | ||||
| # Pre compile | ||||
|  | ||||
| (def pegleg (peg/compile '{:item "abc" :main (* :item "," :item -1)})) | ||||
|  | ||||
| (peg/match pegleg "abc,abc") | ||||
|  | ||||
| # Bad Grammars | ||||
|  | ||||
| (assert-error "peg/compile error 1" (peg/compile nil)) | ||||
| (assert-error "peg/compile error 2" (peg/compile @{})) | ||||
| (assert-error "peg/compile error 3" (peg/compile '{:a "abc" :b "def"})) | ||||
| (assert-error "peg/compile error 4" (peg/compile '(blarg "abc"))) | ||||
| (assert-error "peg/compile error 5" (peg/compile '(1 2 3))) | ||||
|  | ||||
| # IP address | ||||
|  | ||||
| (def ip-address | ||||
|   '{:d (range "09") | ||||
|     :0-4 (range "04") | ||||
|     :0-5 (range "05") | ||||
|     :byte (+ | ||||
|             (* "25" :0-5) | ||||
|             (* "2" :0-4 :d) | ||||
|             (* "1" :d :d) | ||||
|             (between 1 2 :d)) | ||||
|     :main (* :byte "." :byte "." :byte "." :byte)}) | ||||
|  | ||||
| (check-match ip-address "10.240.250.250" true) | ||||
| (check-match ip-address "0.0.0.0" true) | ||||
| (check-match ip-address "1.2.3.4" true) | ||||
| (check-match ip-address "256.2.3.4" false) | ||||
| (check-match ip-address "256.2.3.2514" false) | ||||
|  | ||||
| # Substitution test with peg | ||||
|  | ||||
| (file/flush stderr) | ||||
| (file/flush stdout) | ||||
|  | ||||
| (def grammar '(accumulate (any (+ (/ "dog" "purple panda") (<- 1))))) | ||||
| (defn try-grammar [text] | ||||
|   (assert (= (string/replace-all "dog" "purple panda" text) (0 (peg/match grammar text))) text)) | ||||
|  | ||||
| (try-grammar "i have a dog called doug the dog. he is good.") | ||||
| (try-grammar "i have a dog called doug the dog. he is a good boy.") | ||||
| (try-grammar "i have a dog called doug the do") | ||||
| (try-grammar "i have a dog called doug the dog") | ||||
| (try-grammar "i have a dog called doug the dogg") | ||||
| (try-grammar "i have a dog called doug the doggg") | ||||
| (try-grammar "i have a dog called doug the dogggg") | ||||
|  | ||||
| # Peg CSV test | ||||
|  | ||||
| (def csv | ||||
|   '{:field (+ | ||||
|             (* `"` (% (any (+ (<- (if-not `"` 1)) (* (constant `"`) `""`)))) `"`) | ||||
|             (<- (any (if-not (set ",\n") 1)))) | ||||
|     :main (* :field (any (* "," :field)) (+ "\n" -1))}) | ||||
|  | ||||
| (defn check-csv | ||||
|   [str res] | ||||
|   (check-deep csv str res)) | ||||
|  | ||||
| (check-csv "1,2,3" @["1" "2" "3"]) | ||||
| (check-csv "1,\"2\",3" @["1" "2" "3"]) | ||||
| (check-csv ``1,"1""",3`` @["1" "1\"" "3"]) | ||||
|  | ||||
| # Nested Captures | ||||
|  | ||||
| (def grmr '(capture (* (capture "a") (capture 1) (capture "c")))) | ||||
| (check-deep grmr "abc" @["a" "b" "c" "abc"]) | ||||
| (check-deep grmr "acc" @["a" "c" "c" "acc"]) | ||||
|  | ||||
| # Functions in grammar | ||||
|  | ||||
| (def grmr-triple ~(% (any (/ (<- 1) ,(fn [x] (string x x x)))))) | ||||
| (check-deep grmr-triple "abc" @["aaabbbccc"]) | ||||
| (check-deep grmr-triple "" @[""]) | ||||
| (check-deep grmr-triple " " @["   "]) | ||||
|  | ||||
| (def counter ~(/ (group (any (<- 1))) ,length)) | ||||
| (check-deep counter "abcdefg" @[7]) | ||||
|  | ||||
| # Capture Backtracking | ||||
|  | ||||
| (check-deep '(+ (* (capture "c") "d") "ce") "ce" @[]) | ||||
|  | ||||
| # Matchtime capture | ||||
|  | ||||
| (def scanner (peg/compile ~(cmt (capture (some 1)) ,scan-number))) | ||||
|  | ||||
| (check-deep scanner "123" @[123]) | ||||
| (check-deep scanner "0x86" @[0x86]) | ||||
| (check-deep scanner "-1.3e-7" @[-1.3e-7]) | ||||
| (check-deep scanner "123A" nil) | ||||
|  | ||||
| # Recursive grammars | ||||
|  | ||||
| (def g '{:main (+ (* "a" :main "b") "c")}) | ||||
|  | ||||
| (check-match g "c" true) | ||||
| (check-match g "acb" true) | ||||
| (check-match g "aacbb" true) | ||||
| (check-match g "aadbb" false) | ||||
|  | ||||
| # Back reference | ||||
|  | ||||
| (def wrapped-string | ||||
|   ~{:pad (any "=") | ||||
|     :open (* "[" (<- :pad :n) "[") | ||||
|     :close (* "]" (cmt (* (-> :n) (<- :pad)) ,=) "]") | ||||
|     :main (* :open (any (if-not :close 1)) :close -1)}) | ||||
|  | ||||
| (check-match wrapped-string "[[]]" true) | ||||
| (check-match wrapped-string "[==[a]==]" true) | ||||
| (check-match wrapped-string "[==[]===]" false) | ||||
| (check-match wrapped-string "[[blark]]" true) | ||||
| (check-match wrapped-string "[[bl[ark]]" true) | ||||
| (check-match wrapped-string "[[bl]rk]]" true) | ||||
| (check-match wrapped-string "[[bl]rk]] " false) | ||||
| (check-match wrapped-string "[=[bl]]rk]=] " false) | ||||
| (check-match wrapped-string "[=[bl]==]rk]=] " false) | ||||
| (check-match wrapped-string "[===[]==]===]" true) | ||||
|  | ||||
| (def janet-longstring | ||||
|   ~{:delim (some "`") | ||||
|     :open (capture :delim :n) | ||||
|     :close (cmt (* (not (> -1 "`")) (-> :n) (<- (backmatch :n))) ,=) | ||||
|     :main (* :open (any (if-not :close 1)) :close -1)}) | ||||
|  | ||||
| (check-match janet-longstring "`john" false) | ||||
| (check-match janet-longstring "abc" false) | ||||
| (check-match janet-longstring "` `" true) | ||||
| (check-match janet-longstring "`  `" true) | ||||
| (check-match janet-longstring "``  ``" true) | ||||
| (check-match janet-longstring "``` `` ```" true) | ||||
| (check-match janet-longstring "``  ```" false) | ||||
| (check-match janet-longstring "`a``b`" false) | ||||
|  | ||||
| # Line and column capture | ||||
|  | ||||
| (def line-col (peg/compile '(any (* (line) (column) 1)))) | ||||
| (check-deep line-col "abcd" @[1 1 1 2 1 3 1 4]) | ||||
| (check-deep line-col "" @[]) | ||||
| (check-deep line-col "abcd\n" @[1 1 1 2 1 3 1 4 1 5]) | ||||
| (check-deep line-col "abcd\nz" @[1 1 1 2 1 3 1 4 1 5 2 1]) | ||||
|  | ||||
| # Backmatch | ||||
|  | ||||
| (def backmatcher-1 '(* (capture (any "x") :1) "y" (backmatch :1) -1)) | ||||
|  | ||||
| (check-match backmatcher-1 "y" true) | ||||
| (check-match backmatcher-1 "xyx" true) | ||||
| (check-match backmatcher-1 "xxxxxxxyxxxxxxx" true) | ||||
| (check-match backmatcher-1 "xyxx" false) | ||||
| (check-match backmatcher-1 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy" false) | ||||
| (check-match backmatcher-1 (string (string/repeat "x" 10000) "y") false) | ||||
| (check-match backmatcher-1 (string (string/repeat "x" 10000) "y" (string/repeat "x" 10000)) true) | ||||
|  | ||||
| (def backmatcher-2 '(* '(any "x") "y" (backmatch) -1)) | ||||
|  | ||||
| (check-match backmatcher-2 "y" true) | ||||
| (check-match backmatcher-2 "xyx" true) | ||||
| (check-match backmatcher-2 "xxxxxxxyxxxxxxx" true) | ||||
| (check-match backmatcher-2 "xyxx" false) | ||||
| (check-match backmatcher-2 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy" false) | ||||
| (check-match backmatcher-2 (string (string/repeat "x" 10000) "y") false) | ||||
| (check-match backmatcher-2 (string (string/repeat "x" 10000) "y" (string/repeat "x" 10000)) true) | ||||
|  | ||||
| (def longstring-2 '(* '(some "`") (some (if-not (backmatch) 1)) (backmatch) -1)) | ||||
|  | ||||
| (check-match longstring-2 "`john" false) | ||||
| (check-match longstring-2 "abc" false) | ||||
| (check-match longstring-2 "` `" true) | ||||
| (check-match longstring-2 "`  `" true) | ||||
| (check-match longstring-2 "``  ``" true) | ||||
| (check-match longstring-2 "``` `` ```" true) | ||||
| (check-match longstring-2 "``  ```" false) | ||||
|  | ||||
| # Optional | ||||
|  | ||||
| (check-match '(* (opt "hi") -1) "" true) | ||||
| (check-match '(* (opt "hi") -1) "hi" true) | ||||
| (check-match '(* (opt "hi") -1) "no" false) | ||||
| (check-match '(* (? "hi") -1) "" true) | ||||
| (check-match '(* (? "hi") -1) "hi" true) | ||||
| (check-match '(* (? "hi") -1) "no" false) | ||||
|  | ||||
| # Drop | ||||
|  | ||||
| (check-deep '(drop '"hello") "hello" @[]) | ||||
| (check-deep '(drop "hello") "hello" @[]) | ||||
|  | ||||
| # Regression #24 | ||||
|  | ||||
| (def t (put @{} :hi 1)) | ||||
| (assert (deep= t @{:hi 1}) "regression #24") | ||||
|  | ||||
| # Peg swallowing errors | ||||
| (assert (try (peg/match ~(/ '1 ,(fn [x] (nil x))) "x") ([err] err)) | ||||
|         "errors should not be swallowed") | ||||
| (assert (try ((fn [x] (nil x))) ([err] err)) | ||||
|         "errors should not be swallowed 2") | ||||
|  | ||||
| # Tuple types | ||||
|  | ||||
| (assert (= (tuple/type '(1 2 3)) :parens) "normal tuple") | ||||
| (assert (= (tuple/type [1 2 3]) :parens) "normal tuple 1") | ||||
| (assert (= (tuple/type '[1 2 3]) :brackets) "bracketed tuple 2") | ||||
| (assert (= (tuple/type (-> '(1 2 3) marshal unmarshal)) :parens) "normal tuple marshalled/unmarshalled") | ||||
| (assert (= (tuple/type (-> '[1 2 3] marshal unmarshal)) :brackets) "normal tuple marshalled/unmarshalled") | ||||
|  | ||||
| # Check for bad memoization (+ :a) should mean different things in different contexts. | ||||
| (def redef-a | ||||
|   ~{:a "abc" | ||||
|     :c (+ :a) | ||||
|     :main (* :c {:a "def" :main (+ :a)} -1)}) | ||||
|  | ||||
| (check-match redef-a "abcdef" true) | ||||
| (check-match redef-a "abcabc" false) | ||||
| (check-match redef-a "defdef" false) | ||||
|  | ||||
| (def redef-b | ||||
|   ~{:pork {:pork "beef" :main (+ -1 (* 1 :pork))} | ||||
|     :main :pork}) | ||||
|  | ||||
| (check-match redef-b "abeef" true) | ||||
| (check-match redef-b "aabeef" false) | ||||
| (check-match redef-b "aaaaaa" false) | ||||
|  | ||||
| # Integer parsing | ||||
|  | ||||
| (check-deep '(int 1) "a" @[(chr "a")]) | ||||
| (check-deep '(uint 1) "a" @[(chr "a")]) | ||||
| (check-deep '(int-be 1) "a" @[(chr "a")]) | ||||
| (check-deep '(uint-be 1) "a" @[(chr "a")]) | ||||
| (check-deep '(int 1) "\xFF" @[-1]) | ||||
| (check-deep '(uint 1) "\xFF" @[255]) | ||||
| (check-deep '(int-be 1) "\xFF" @[-1]) | ||||
| (check-deep '(uint-be 1) "\xFF" @[255]) | ||||
| (check-deep '(int 2) "\xFF\x7f" @[0x7fff]) | ||||
| (check-deep '(int-be 2) "\x7f\xff" @[0x7fff]) | ||||
| (check-deep '(uint 2) "\xff\x7f" @[0x7fff]) | ||||
| (check-deep '(uint-be 2) "\x7f\xff" @[0x7fff]) | ||||
| (check-deep '(uint-be 2) "\x7f\xff" @[0x7fff]) | ||||
| (check-deep '(uint 8) "\xff\x7f\x00\x00\x00\x00\x00\x00" @[(int/u64 0x7fff)]) | ||||
| (check-deep '(int 8) "\xff\x7f\x00\x00\x00\x00\x00\x00" @[(int/s64 0x7fff)]) | ||||
| (check-deep '(uint 7) "\xff\x7f\x00\x00\x00\x00\x00" @[(int/u64 0x7fff)]) | ||||
| (check-deep '(int 7) "\xff\x7f\x00\x00\x00\x00\x00" @[(int/s64 0x7fff)]) | ||||
|  | ||||
| (check-deep '(* (int 2) -1) "123" nil) | ||||
|  | ||||
| # to/thru bug | ||||
| (check-deep '(to -1) "aaaa" @[]) | ||||
| (check-deep '(thru -1) "aaaa" @[]) | ||||
| (check-deep ''(to -1) "aaaa" @["aaaa"]) | ||||
| (check-deep ''(thru -1) "aaaa" @["aaaa"]) | ||||
| (check-deep '(to "b") "aaaa" nil) | ||||
| (check-deep '(thru "b") "aaaa" nil) | ||||
|  | ||||
| # unref | ||||
| (def grammar | ||||
|   (peg/compile | ||||
|     ~{:main (* :tagged -1) | ||||
|       :tagged (unref (replace (* :open-tag :value :close-tag) ,struct)) | ||||
|       :open-tag (* (constant :tag) "<" (capture :w+ :tag-name) ">") | ||||
|       :value (* (constant :value) (group (any (+ :tagged :untagged)))) | ||||
|       :close-tag (* "</" (backmatch :tag-name) ">") | ||||
|       :untagged (capture (any (if-not "<" 1)))})) | ||||
| (check-deep grammar "<p><em>foobar</em></p>" @[{:tag "p" :value @[{:tag "em" :value @["foobar"]}]}]) | ||||
| (check-deep grammar "<p>foobar</p>" @[{:tag "p" :value @["foobar"]}]) | ||||
|  | ||||
| (end-suite) | ||||
							
								
								
									
										86
									
								
								test/suite0004.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								test/suite0004.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| # Copyright (c) 2023 Calvin Rose | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 4) | ||||
| # some tests for string/format and buffer/format | ||||
|  | ||||
| (assert (= (string (buffer/format @"" "pi = %6.3f" math/pi)) "pi =  3.142") "%6.3f") | ||||
| (assert (= (string (buffer/format @"" "pi = %+6.3f" math/pi)) "pi = +3.142") "%6.3f") | ||||
| (assert (= (string (buffer/format @"" "pi = %40.20g" math/pi)) "pi =                     3.141592653589793116") "%6.3f") | ||||
|  | ||||
| (assert (= (string (buffer/format @"" "🐼 = %6.3f" math/pi)) "🐼 =  3.142") "UTF-8") | ||||
| (assert (= (string (buffer/format @"" "π = %.8g" math/pi)) "π = 3.1415927") "π") | ||||
| (assert (= (string (buffer/format @"" "\xCF\x80 = %.8g" math/pi)) "\xCF\x80 = 3.1415927") "\xCF\x80") | ||||
|  | ||||
| (assert (= (string/format "pi = %6.3f" math/pi) "pi =  3.142") "%6.3f") | ||||
| (assert (= (string/format "pi = %+6.3f" math/pi) "pi = +3.142") "%6.3f") | ||||
| (assert (= (string/format "pi = %40.20g" math/pi) "pi =                     3.141592653589793116") "%6.3f") | ||||
|  | ||||
| (assert (= (string/format "🐼 = %6.3f" math/pi) "🐼 =  3.142") "UTF-8") | ||||
| (assert (= (string/format "π = %.8g" math/pi) "π = 3.1415927") "π") | ||||
| (assert (= (string/format "\xCF\x80 = %.8g" math/pi) "\xCF\x80 = 3.1415927") "\xCF\x80") | ||||
|  | ||||
| # Range | ||||
| (assert (deep= (range 10) @[0 1 2 3 4 5 6 7 8 9]) "range 1 argument") | ||||
| (assert (deep= (range 5 10) @[5 6 7 8 9]) "range 2 arguments") | ||||
| (assert (deep= (range 5 10 2) @[5 7 9]) "range 3 arguments") | ||||
|  | ||||
| # More marshalling code | ||||
|  | ||||
| (defn check-image | ||||
|   "Run a marshaling test using the make-image and load-image functions." | ||||
|   [x msg] | ||||
|   (def im (make-image x)) | ||||
|   # (printf "\nimage-hash: %d" (-> im string hash)) | ||||
|   (assert-no-error msg (load-image im))) | ||||
|  | ||||
| (check-image (fn [] (fn [] 1)) "marshal nested functions") | ||||
| (check-image (fiber/new (fn [] (fn [] 1))) "marshal nested functions in fiber") | ||||
| (check-image (fiber/new (fn [] (fiber/new (fn [] 1)))) "marshal nested fibers") | ||||
|  | ||||
| (def issue-53-x  | ||||
|   (fiber/new  | ||||
|     (fn []  | ||||
|       (var y (fiber/new (fn [] (print "1") (yield) (print "2"))))))) | ||||
|  | ||||
| (check-image issue-53-x "issue 53 regression") | ||||
|  | ||||
| # Bracket tuple issue | ||||
|  | ||||
| (def do 3) | ||||
| (assert (= [3 1 2 3] [do 1 2 3]) "bracket tuples are never special forms") | ||||
| (assert (= ~(,defn 1 2 3) [defn 1 2 3]) "bracket tuples are never macros") | ||||
| (assert (= ~(,+ 1 2 3) [+ 1 2 3]) "bracket tuples are never function calls") | ||||
|  | ||||
| # Metadata | ||||
|  | ||||
| (def foo-with-tags :a-tag :bar) | ||||
| (assert (get (dyn 'foo-with-tags) :a-tag) "extra keywords in def are metadata tags") | ||||
|  | ||||
| (def foo-with-meta {:baz :quux} :bar) | ||||
| (assert (= :quux (get (dyn 'foo-with-meta) :baz)) "extra struct in def is metadata") | ||||
|  | ||||
| (defn foo-fn-with-meta {:baz :quux} "This is a function" [x] (identity x)) | ||||
| (assert (= :quux (get (dyn 'foo-fn-with-meta) :baz)) "extra struct in defn is metadata") | ||||
| (assert (= "(foo-fn-with-meta x)\n\nThis is a function" (get (dyn 'foo-fn-with-meta) :doc)) "extra string in defn is docstring") | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
							
								
								
									
										120
									
								
								test/suite0005.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								test/suite0005.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| # Copyright (c) 2023 Calvin Rose & contributors | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 5) | ||||
|  | ||||
| # Array remove | ||||
|  | ||||
| (assert (deep= (array/remove @[1 2 3 4 5] 2) @[1 2 4 5]) "array/remove 1") | ||||
| (assert (deep= (array/remove @[1 2 3 4 5] 2 2) @[1 2 5]) "array/remove 2") | ||||
| (assert (deep= (array/remove @[1 2 3 4 5] 2 200) @[1 2]) "array/remove 3") | ||||
| (assert (deep= (array/remove @[1 2 3 4 5] -3 200) @[1 2 3]) "array/remove 4") | ||||
|  | ||||
| # Break | ||||
|  | ||||
| (var summation 0) | ||||
| (for i 0 10 | ||||
|   (+= summation i) | ||||
|   (if (= i 7) (break))) | ||||
| (assert (= summation 28) "break 1") | ||||
|  | ||||
| (assert (= nil ((fn [] (break) 4))) "break 2") | ||||
|  | ||||
| # Break with value | ||||
|  | ||||
| # Shouldn't error out | ||||
| (assert-no-error "break 3" (for i 0 10 (if (> i 8) (break i)))) | ||||
| (assert-no-error "break 4" ((fn [i] (if (> i 8) (break i))) 100)) | ||||
|  | ||||
| # take | ||||
|  | ||||
| (assert (deep= (take 0 []) []) "take 1") | ||||
| (assert (deep= (take 10 []) []) "take 2") | ||||
| (assert (deep= (take 0 [1 2 3 4 5]) []) "take 3") | ||||
| (assert (deep= (take 10 [1 2 3]) [1 2 3]) "take 4") | ||||
| (assert (deep= (take -1 [:a :b :c]) []) "take 5") | ||||
| (assert (deep= (take 3 (generate [x :in [1 2 3 4 5]] x)) @[1 2 3]) "take from fiber") | ||||
| # NB: repeatedly resuming a fiber created with `generate` includes a `nil` as | ||||
| # the final element. Thus a generate of 2 elements will create an array of 3. | ||||
| (assert (= (length (take 4 (generate [x :in [1 2]] x))) 2) "take from short fiber") | ||||
|  | ||||
| # take-until | ||||
|  | ||||
| (assert (deep= (take-until pos? @[]) []) "take-until 1") | ||||
| (assert (deep= (take-until pos? @[1 2 3]) []) "take-until 2") | ||||
| (assert (deep= (take-until pos? @[-1 -2 -3]) [-1 -2 -3]) "take-until 3") | ||||
| (assert (deep= (take-until pos? @[-1 -2 3]) [-1 -2]) "take-until 4") | ||||
| (assert (deep= (take-until pos? @[-1 1 -2]) [-1]) "take-until 5") | ||||
| (assert (deep= (take-until |(= $ 115) "books") "book") "take-until 6") | ||||
| (assert (deep= (take-until |(= $ 115) (generate [x :in "books"] x)) | ||||
|                @[98 111 111 107]) "take-until from fiber") | ||||
|  | ||||
| # take-while | ||||
|  | ||||
| (assert (deep= (take-while neg? @[]) []) "take-while 1") | ||||
| (assert (deep= (take-while neg? @[1 2 3]) []) "take-while 2") | ||||
| (assert (deep= (take-while neg? @[-1 -2 -3]) [-1 -2 -3]) "take-while 3") | ||||
| (assert (deep= (take-while neg? @[-1 -2 3]) [-1 -2]) "take-while 4") | ||||
| (assert (deep= (take-while neg? @[-1 1 -2]) [-1]) "take-while 5") | ||||
| (assert (deep= (take-while neg? (generate [x :in  @[-1 1 -2]] x)) | ||||
|                @[-1]) "take-while from fiber") | ||||
|  | ||||
| # drop | ||||
|  | ||||
| (assert (deep= (drop 0 []) []) "drop 1") | ||||
| (assert (deep= (drop 10 []) []) "drop 2") | ||||
| (assert (deep= (drop 0 [1 2 3 4 5]) [1 2 3 4 5]) "drop 3") | ||||
| (assert (deep= (drop 10 [1 2 3]) []) "drop 4") | ||||
| (assert (deep= (drop -1 [1 2 3]) [1 2]) "drop 5") | ||||
| (assert (deep= (drop -10 [1 2 3]) []) "drop 6") | ||||
| (assert (deep= (drop 1 "abc") "bc") "drop 7") | ||||
| (assert (deep= (drop 10 "abc") "") "drop 8") | ||||
| (assert (deep= (drop -1 "abc") "ab") "drop 9") | ||||
| (assert (deep= (drop -10 "abc") "") "drop 10") | ||||
| (assert-error :invalid-type (drop 3 {}) "drop 11") | ||||
|  | ||||
| # drop-until | ||||
|  | ||||
| (assert (deep= (drop-until pos? @[]) []) "drop-until 1") | ||||
| (assert (deep= (drop-until pos? @[1 2 3]) [1 2 3]) "drop-until 2") | ||||
| (assert (deep= (drop-until pos? @[-1 -2 -3]) []) "drop-until 3") | ||||
| (assert (deep= (drop-until pos? @[-1 -2 3]) [3]) "drop-until 4") | ||||
| (assert (deep= (drop-until pos? @[-1 1 -2]) [1 -2]) "drop-until 5") | ||||
| (assert (deep= (drop-until |(= $ 115) "books") "s") "drop-until 6") | ||||
|  | ||||
| # Quasiquote bracketed tuples | ||||
| (assert (= (tuple/type ~[1 2 3]) (tuple/type '[1 2 3])) "quasiquote bracket tuples") | ||||
|  | ||||
| # No useless splices | ||||
| (check-compile-error '((splice [1 2 3]) 0)) | ||||
| (check-compile-error '(if ;[1 2] 5)) | ||||
| (check-compile-error '(while ;[1 2 3] (print :hi))) | ||||
| (check-compile-error '(def x ;[1 2 3])) | ||||
| (check-compile-error '(fn [x] ;[x 1 2 3])) | ||||
|  | ||||
| # No splice propagation | ||||
| (check-compile-error '(+ 1 (do ;[2 3 4]) 5)) | ||||
| (check-compile-error '(+ 1 (upscope ;[2 3 4]) 5)) | ||||
| # compiler inlines when condition is constant, ensure that optimization doesn't break | ||||
| (check-compile-error '(+ 1 (if true ;[3 4]))) | ||||
| (check-compile-error '(+ 1 (if false nil ;[3 4]))) | ||||
|  | ||||
| (end-suite) | ||||
							
								
								
									
										272
									
								
								test/suite0006.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								test/suite0006.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,272 @@ | ||||
| # Copyright (c) 2023 Calvin Rose & contributors | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 6) | ||||
|  | ||||
| # some tests for bigint | ||||
|  | ||||
| (def i64 int/s64) | ||||
| (def u64 int/u64) | ||||
|  | ||||
| (assert-no-error | ||||
|  "create some uint64 bigints" | ||||
|  (do | ||||
|    # from number | ||||
|    (def a (u64 10)) | ||||
|    # max double we can convert to int (2^53) | ||||
|    (def b (u64 0x1fffffffffffff)) | ||||
|    (def b (u64 (math/pow 2 53))) | ||||
|    # from string | ||||
|    (def c (u64 "0xffff_ffff_ffff_ffff")) | ||||
|    (def c (u64 "32rvv_vv_vv_vv")) | ||||
|    (def d (u64 "123456789")))) | ||||
|  | ||||
| # Conversion back to an int32 | ||||
| (assert (= (int/to-number (u64 0xFaFa)) 0xFaFa)) | ||||
| (assert (= (int/to-number (i64 0xFaFa)) 0xFaFa)) | ||||
| (assert (= (int/to-number (u64 9007199254740991)) 9007199254740991)) | ||||
| (assert (= (int/to-number (i64 9007199254740991)) 9007199254740991)) | ||||
| (assert (= (int/to-number (i64 -9007199254740991)) -9007199254740991)) | ||||
|  | ||||
| (assert-error | ||||
|   "u64 out of bounds for safe integer" | ||||
|   (int/to-number (u64 "9007199254740993")) | ||||
|  | ||||
| (assert-error | ||||
|   "s64 out of bounds for safe integer" | ||||
|   (int/to-number (i64 "-9007199254740993")))) | ||||
|  | ||||
| (assert-error | ||||
|   "int/to-number fails on non-abstract types" | ||||
|   (int/to-number 1)) | ||||
|  | ||||
| (assert-no-error | ||||
|  "create some int64 bigints" | ||||
|  (do | ||||
|    # from number | ||||
|    (def a (i64 -10)) | ||||
|    # max double we can convert to int (2^53) | ||||
|    (def b (i64 0x1fffffffffffff)) | ||||
|    (def b (i64 (math/pow 2 53))) | ||||
|    # from string | ||||
|    (def c (i64 "0x7fff_ffff_ffff_ffff")) | ||||
|    (def d (i64 "123456789")))) | ||||
|  | ||||
| (assert-error | ||||
|  "bad initializers" | ||||
|  (do | ||||
|    # double to big to be converted to uint64 without truncation (2^53 + 1) | ||||
|    (def b (u64 (+ 0xffff_ffff_ffff_ff 1))) | ||||
|    (def b (u64 (+ (math/pow 2 53) 1))) | ||||
|    # out of range 65 bits | ||||
|    (def c (u64 "0x1ffffffffffffffff")) | ||||
|    # just to big | ||||
|    (def d (u64 "123456789123456789123456789")))) | ||||
|  | ||||
| (assert (= (:/ (u64 "0xffff_ffff_ffff_ffff") 8 2) (u64 "0xfffffffffffffff")) "bigint operations 1") | ||||
| (assert (let [a (u64 0xff)] (= (:+ a a a a) (:* a 2 2))) "bigint operations 2") | ||||
|  | ||||
| (assert (= (string (i64 -123)) "-123") "i64 prints reasonably") | ||||
| (assert (= (string (u64 123)) "123") "u64 prints reasonably") | ||||
|  | ||||
| (assert-error | ||||
|  "trap INT64_MIN / -1" | ||||
|  (:/ (int/s64 "-0x8000_0000_0000_0000") -1)) | ||||
|  | ||||
| # int/s64 and int/u64 serialization | ||||
| (assert (deep= (int/to-bytes (u64 0)) @"\x00\x00\x00\x00\x00\x00\x00\x00")) | ||||
|  | ||||
| (assert (deep= (int/to-bytes (i64 1) :le) @"\x01\x00\x00\x00\x00\x00\x00\x00")) | ||||
| (assert (deep= (int/to-bytes (i64 1) :be) @"\x00\x00\x00\x00\x00\x00\x00\x01")) | ||||
| (assert (deep= (int/to-bytes (i64 -1)) @"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF")) | ||||
| (assert (deep= (int/to-bytes (i64 -5) :be) @"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFB")) | ||||
|  | ||||
| (assert (deep= (int/to-bytes (u64 1) :le) @"\x01\x00\x00\x00\x00\x00\x00\x00")) | ||||
| (assert (deep= (int/to-bytes (u64 1) :be) @"\x00\x00\x00\x00\x00\x00\x00\x01")) | ||||
| (assert (deep= (int/to-bytes (u64 300) :be) @"\x00\x00\x00\x00\x00\x00\x01\x2C")) | ||||
|  | ||||
| # int/s64 int/u64 to existing buffer | ||||
| (let [buf1 @"" | ||||
|       buf2 @"abcd"] | ||||
|   (assert (deep= (int/to-bytes (i64 1) :le buf1) @"\x01\x00\x00\x00\x00\x00\x00\x00")) | ||||
|   (assert (deep= buf1 @"\x01\x00\x00\x00\x00\x00\x00\x00")) | ||||
|   (assert (deep= (int/to-bytes (u64 300) :be buf2) @"abcd\x00\x00\x00\x00\x00\x00\x01\x2C"))) | ||||
|  | ||||
| # int/s64 and int/u64 paramater type checking | ||||
| (assert-error | ||||
|  "bad value passed to int/to-bytes" | ||||
|  (int/to-bytes 1)) | ||||
|  | ||||
| (assert-error | ||||
|   "invalid endianness passed to int/to-bytes" | ||||
|    (int/to-bytes (u64 0) :little)) | ||||
|  | ||||
| (assert-error | ||||
|   "invalid buffer passed to int/to-bytes" | ||||
|    (int/to-bytes (u64 0) :little :buffer)) | ||||
|  | ||||
|  | ||||
| # Dynamic bindings | ||||
| (setdyn :a 10) | ||||
| (assert (= 40 (with-dyns [:a 25 :b 15] (+ (dyn :a) (dyn :b)))) "dyn usage 1") | ||||
| (assert (= 10 (dyn :a)) "dyn usage 2") | ||||
| (assert (= nil (dyn :b)) "dyn usage 3") | ||||
| (setdyn :a 100) | ||||
| (assert (= 100 (dyn :a)) "dyn usage 4") | ||||
|  | ||||
| # Keyword arguments | ||||
| (defn myfn [x y z &keys {:a a :b b :c c}] | ||||
|   (+ x y z a b c)) | ||||
|  | ||||
| (assert (= (+ ;(range 6)) (myfn 0 1 2 :a 3 :b 4 :c 5)) "keyword args 1") | ||||
| (assert (= (+ ;(range 6)) (myfn 0 1 2 :a 1 :b 6 :c 5 :d 11)) "keyword args 2") | ||||
|  | ||||
| # Comment macro | ||||
| (comment 1) | ||||
| (comment 1 2) | ||||
| (comment 1 2 3) | ||||
| (comment 1 2 3 4) | ||||
|  | ||||
| # Parser clone | ||||
| (def p (parser/new)) | ||||
| (assert (= 7 (parser/consume p "(1 2 3 ")) "parser 1") | ||||
| (def p2 (parser/clone p)) | ||||
| (parser/consume p2 ") 1 ") | ||||
| (parser/consume p ") 1 ") | ||||
| (assert (deep= (parser/status p) (parser/status p2)) "parser 2") | ||||
| (assert (deep= (parser/state p) (parser/state p2)) "parser 3") | ||||
|  | ||||
| # Parser errors | ||||
| (defn parse-error [input] | ||||
|   (def p (parser/new)) | ||||
|   (parser/consume p input) | ||||
|   (parser/error p)) | ||||
|  | ||||
| # Invalid utf-8 sequences | ||||
| (assert (not= nil (parse-error @"\xc3\x28")) "reject invalid utf-8 symbol") | ||||
| (assert (not= nil (parse-error @":\xc3\x28")) "reject invalid utf-8 keyword") | ||||
|  | ||||
| # Parser line and column numbers | ||||
| (defn parser-location [input &opt location] | ||||
|   (def p (parser/new)) | ||||
|   (parser/consume p input) | ||||
|   (if location | ||||
|     (parser/where p ;location) | ||||
|     (parser/where p))) | ||||
|  | ||||
| (assert (= [1 7] (parser-location @"(+ 1 2)")) "parser location 1") | ||||
| (assert (= [5 7] (parser-location @"(+ 1 2)" [5])) "parser location 2") | ||||
| (assert (= [10 10] (parser-location @"(+ 1 2)" [10 10])) "parser location 3") | ||||
|  | ||||
| # String check-set | ||||
| (assert (string/check-set "abc" "a") "string/check-set 1") | ||||
| (assert (not (string/check-set "abc" "z")) "string/check-set 2") | ||||
| (assert (string/check-set "abc" "abc") "string/check-set 3") | ||||
| (assert (string/check-set "abc" "") "string/check-set 4") | ||||
| (assert (not (string/check-set "" "aabc")) "string/check-set 5") | ||||
| (assert (not (string/check-set "abc" "abcdefg")) "string/check-set 6") | ||||
|  | ||||
| # Marshal and unmarshal pegs | ||||
| (def p (-> "abcd" peg/compile marshal unmarshal)) | ||||
| (assert (peg/match p "abcd") "peg marshal 1") | ||||
| (assert (peg/match p "abcdefg") "peg marshal 2") | ||||
| (assert (not (peg/match p "zabcdefg")) "peg marshal 3") | ||||
|  | ||||
| # This should be valgrind clean. | ||||
| (var pegi 3) | ||||
| (defn marshpeg [p] | ||||
|   (assert (-> p peg/compile marshal unmarshal) (string "peg marshal " (++ pegi)))) | ||||
| (marshpeg '(* 1 2 (set "abcd") "asdasd" (+ "." 3))) | ||||
| (marshpeg '(% (* (+ 1 2 3) (* "drop" "bear") '"hi"))) | ||||
| (marshpeg '(> 123 "abcd")) | ||||
| (marshpeg '{:main (* 1 "hello" :main)}) | ||||
| (marshpeg '(range "AZ")) | ||||
| (marshpeg '(if-not "abcdf" 123)) | ||||
| (marshpeg '(error ($))) | ||||
| (marshpeg '(* "abcd" (constant :hi))) | ||||
| (marshpeg ~(/ "abc" ,identity)) | ||||
| (marshpeg '(if-not "abcdf" 123)) | ||||
| (marshpeg ~(cmt "abcdf" ,identity)) | ||||
| (marshpeg '(group "abc")) | ||||
|  | ||||
| # Module path expansion | ||||
| (setdyn :current-file "some-dir/some-file") | ||||
| (defn test-expand [path temp] | ||||
|   (string (module/expand-path path temp))) | ||||
|  | ||||
| # Right hand operators | ||||
| (assert (= (int/s64 (sum (range 10))) (sum (map int/s64 (range 10)))) "right hand operators 1") | ||||
| (assert (= (int/s64 (product (range 1 10))) (product (map int/s64 (range 1 10)))) "right hand operators 2") | ||||
| (assert (= (int/s64 15) (bor 10 (int/s64 5)) (bor (int/s64 10) 5)) "right hand operators 3") | ||||
|  | ||||
| (assert (= (test-expand "abc" ":cur:/:all:") "some-dir/abc") "module/expand-path 1") | ||||
| (assert (= (test-expand "./abc" ":cur:/:all:") "some-dir/abc") "module/expand-path 2") | ||||
| (assert (= (test-expand "abc/def.txt" ":cur:/:name:") "some-dir/def.txt") "module/expand-path 3") | ||||
| (assert (= (test-expand "abc/def.txt" ":cur:/:dir:/sub/:name:") "some-dir/abc/sub/def.txt") "module/expand-path 4") | ||||
| (assert (= (test-expand "/abc/../def.txt" ":all:") "/def.txt") "module/expand-path 5") | ||||
| (assert (= (test-expand "abc/../def.txt" ":all:") "def.txt") "module/expand-path 6") | ||||
| (assert (= (test-expand "../def.txt" ":all:") "../def.txt") "module/expand-path 7") | ||||
| (assert (= (test-expand "../././././abcd/../def.txt" ":all:") "../def.txt") "module/expand-path 8") | ||||
|  | ||||
| # Integer type checks | ||||
| (assert (compare= 0 (- (int/u64 "1000") 1000)) "subtract from int/u64") | ||||
|  | ||||
| (assert (odd? (int/u64 "1001")) "odd? 1") | ||||
| (assert (not (odd? (int/u64 "1000"))) "odd? 2") | ||||
| (assert (odd? (int/s64 "1001")) "odd? 3") | ||||
| (assert (not (odd? (int/s64 "1000"))) "odd? 4") | ||||
| (assert (odd? (int/s64 "-1001")) "odd? 5") | ||||
| (assert (not (odd? (int/s64 "-1000"))) "odd? 6") | ||||
|  | ||||
| (assert (even? (int/u64 "1000")) "even? 1") | ||||
| (assert (not (even? (int/u64 "1001"))) "even? 2") | ||||
| (assert (even? (int/s64 "1000")) "even? 3") | ||||
| (assert (not (even? (int/s64 "1001"))) "even? 4") | ||||
| (assert (even? (int/s64 "-1000")) "even? 5") | ||||
| (assert (not (even? (int/s64 "-1001"))) "even? 6") | ||||
|  | ||||
| # integer type operations | ||||
| (defn modcheck [x y] | ||||
|   (assert (= (string (mod x y)) (string (mod (int/s64 x) y))) | ||||
|           (string "int/s64 (mod " x " " y ") expected " (mod x y) ", got " | ||||
|                   (mod (int/s64 x) y))) | ||||
|   (assert (= (string (% x y)) (string (% (int/s64 x) y))) | ||||
|           (string "int/s64 (% " x " " y ") expected " (% x y) ", got " | ||||
|                   (% (int/s64 x) y)))) | ||||
|  | ||||
| (modcheck 1 2) | ||||
| (modcheck 1 3) | ||||
| (modcheck 4 2) | ||||
| (modcheck 4 1) | ||||
| (modcheck 10 3) | ||||
| (modcheck 10 -3) | ||||
| (modcheck -10 3) | ||||
| (modcheck -10 -3) | ||||
|  | ||||
| # Check for issue #1130 | ||||
| (var d (int/s64 7)) | ||||
| (mod 0 d) | ||||
|  | ||||
| (var d (int/s64 7)) | ||||
| (def result (seq [n :in (range -21 0)] (mod n d))) | ||||
| (assert (deep= result (map int/s64 @[0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6])) "issue #1130") | ||||
|  | ||||
| (end-suite) | ||||
							
								
								
									
										336
									
								
								test/suite0007.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								test/suite0007.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,336 @@ | ||||
| # Copyright (c) 2023 Calvin Rose & contributors | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 7) | ||||
|  | ||||
| # Using a large test grammar | ||||
|  | ||||
| (def- specials {'fn true | ||||
|                'var true | ||||
|                'do true | ||||
|                'while true | ||||
|                'def true | ||||
|                'splice true | ||||
|                'set true | ||||
|                'unquote true | ||||
|                'quasiquote true | ||||
|                'quote true | ||||
|                'if true}) | ||||
|  | ||||
| (defn- check-number [text] (and (scan-number text) text)) | ||||
|  | ||||
| (defn capture-sym | ||||
|   [text] | ||||
|   (def sym (symbol text)) | ||||
|   [(if (or (root-env sym) (specials sym)) :coresym :symbol) text]) | ||||
|  | ||||
| (def grammar | ||||
|   ~{:ws (set " \v\t\r\f\n\0") | ||||
|     :readermac (set "';~,") | ||||
|     :symchars (+ (range "09" "AZ" "az" "\x80\xFF") (set "!$%&*+-./:<?=>@^_|")) | ||||
|     :token (some :symchars) | ||||
|     :hex (range "09" "af" "AF") | ||||
|     :escape (* "\\" (+ (set "ntrvzf0e\"\\") | ||||
|                        (* "x" :hex :hex) | ||||
|                        (error (constant "bad hex escape")))) | ||||
|     :comment (/ '(* "#" (any (if-not (+ "\n" -1) 1))) (constant :comment)) | ||||
|     :symbol (/ ':token ,capture-sym) | ||||
|     :keyword (/ '(* ":" (any :symchars)) (constant :keyword)) | ||||
|     :constant (/ '(+ "true" "false" "nil") (constant :constant)) | ||||
|     :bytes (* "\"" (any (+ :escape (if-not "\"" 1))) "\"") | ||||
|     :string (/ ':bytes (constant :string)) | ||||
|     :buffer (/ '(* "@" :bytes) (constant :string)) | ||||
|     :long-bytes {:delim (some "`") | ||||
|                  :open (capture :delim :n) | ||||
|                  :close (cmt (* (not (> -1 "`")) (-> :n) '(backmatch :n)) ,=) | ||||
|                  :main (drop (* :open (any (if-not :close 1)) :close))} | ||||
|     :long-string (/ ':long-bytes (constant :string)) | ||||
|     :long-buffer (/ '(* "@" :long-bytes) (constant :string)) | ||||
|     :number (/ (cmt ':token ,check-number) (constant :number)) | ||||
|     :raw-value (+ :comment :constant :number :keyword | ||||
|                   :string :buffer :long-string :long-buffer | ||||
|                   :parray :barray :ptuple :btuple :struct :dict :symbol) | ||||
|     :value (* (? '(some (+ :ws :readermac))) :raw-value '(any :ws)) | ||||
|     :root (any :value) | ||||
|     :root2 (any (* :value :value)) | ||||
|     :ptuple (* '"(" :root (+ '")" (error ""))) | ||||
|     :btuple (* '"[" :root (+ '"]" (error ""))) | ||||
|     :struct (* '"{" :root2 (+ '"}" (error ""))) | ||||
|     :parray (* '"@" :ptuple) | ||||
|     :barray (* '"@" :btuple) | ||||
|     :dict (* '"@"  :struct) | ||||
|     :main (+ :root (error ""))}) | ||||
|  | ||||
| (def p (peg/compile grammar)) | ||||
|  | ||||
| # Just make sure is valgrind clean. | ||||
| (def p (-> p make-image load-image)) | ||||
|  | ||||
| (assert (peg/match p "abc") "complex peg grammar 1") | ||||
| (assert (peg/match p "[1 2 3 4]") "complex peg grammar 2") | ||||
|  | ||||
| # | ||||
| # fn compilation special | ||||
| # | ||||
| (defn myfn1 [[x y z] & more] | ||||
|   more) | ||||
| (defn myfn2 [head & more] | ||||
|   more) | ||||
| (assert (= (myfn1 [1 2 3] 4 5 6) (myfn2 [:a :b :c] 4 5 6)) "destructuring and varargs") | ||||
|  | ||||
| # | ||||
| # Test propagation of signals via fibers | ||||
| # | ||||
|  | ||||
| (def f (fiber/new (fn [] (error :abc) 1) :ei)) | ||||
| (def res (resume f)) | ||||
| (assert-error :abc (propagate res f) "propagate 1") | ||||
|  | ||||
| # table/clone | ||||
|  | ||||
| (defn check-table-clone [x msg] | ||||
|   (assert (= (table/to-struct x) (table/to-struct (table/clone x))) msg)) | ||||
|  | ||||
| (check-table-clone @{:a 123 :b 34 :c :hello : 945 0 1 2 3 4 5} "table/clone 1") | ||||
| (check-table-clone @{} "table/clone 1") | ||||
|  | ||||
| # Make sure Carriage Returns don't end up in doc strings. | ||||
|  | ||||
| (assert (not (string/find "\r" (get ((fiber/getenv (fiber/current)) 'cond) :doc ""))) "no \\r in doc strings") | ||||
|  | ||||
| # module/expand-path regression | ||||
| (with-dyns [:syspath ".janet/.janet"] | ||||
|   (assert (= (string (module/expand-path "hello" ":sys:/:all:.janet")) | ||||
|              ".janet/.janet/hello.janet") "module/expand-path 1")) | ||||
|  | ||||
| # comp should be variadic | ||||
| (assert (= 10 ((comp +) 1 2 3 4)) "variadic comp 1") | ||||
| (assert (= 11 ((comp inc +) 1 2 3 4)) "variadic comp 2") | ||||
| (assert (= 12 ((comp inc inc +) 1 2 3 4)) "variadic comp 3") | ||||
| (assert (= 13 ((comp inc inc inc +) 1 2 3 4)) "variadic comp 4") | ||||
| (assert (= 14 ((comp inc inc inc inc +) 1 2 3 4)) "variadic comp 5") | ||||
| (assert (= 15 ((comp inc inc inc inc inc +) 1 2 3 4)) "variadic comp 6") | ||||
| (assert (= 16 ((comp inc inc inc inc inc inc +) 1 2 3 4)) "variadic comp 7") | ||||
|  | ||||
| # Function shorthand | ||||
| (assert (= (|(+ 1 2 3)) 6) "function shorthand 1") | ||||
| (assert (= (|(+ 1 2 3 $) 4) 10) "function shorthand 2") | ||||
| (assert (= (|(+ 1 2 3 $0) 4) 10) "function shorthand 3") | ||||
| (assert (= (|(+ $0 $0 $0 $0) 4) 16) "function shorthand 4") | ||||
| (assert (= (|(+ $ $ $ $) 4) 16) "function shorthand 5") | ||||
| (assert (= (|4) 4) "function shorthand 6") | ||||
| (assert (= (((|||4))) 4) "function shorthand 7") | ||||
| (assert (= (|(+ $1 $1 $1 $1) 2 4) 16) "function shorthand 8") | ||||
| (assert (= (|(+ $0 $1 $3 $2 $6) 0 1 2 3 4 5 6) 12) "function shorthand 9") | ||||
| (assert (= (|(+ $0 $99) ;(range 100)) 99) "function shorthand 10") | ||||
|  | ||||
| # Simple function break | ||||
| (debug/fbreak map 1) | ||||
| (def f (fiber/new (fn [] (map inc [1 2 3])) :a)) | ||||
| (resume f) | ||||
| (assert (= :debug (fiber/status f)) "debug/fbreak") | ||||
| (debug/unfbreak map 1) | ||||
| (map inc [1 2 3]) | ||||
|  | ||||
| (defn idx= [x y] (= (tuple/slice x) (tuple/slice y))) | ||||
|  | ||||
| # Simple take, drop, etc. tests. | ||||
| (assert (idx= (take 10 (range 100)) (range 10)) "take 10") | ||||
| (assert (idx= (drop 10 (range 100)) (range 10 100)) "drop 10") | ||||
|  | ||||
| # Printing to buffers | ||||
| (def out-buf @"") | ||||
| (def err-buf @"") | ||||
| (with-dyns [:out out-buf :err err-buf] | ||||
|   (print "Hello") | ||||
|   (prin "hi") | ||||
|   (eprint "Sup") | ||||
|   (eprin "not much.")) | ||||
|  | ||||
| (assert (= (string out-buf) "Hello\nhi") "print and prin to buffer 1") | ||||
| (assert (= (string err-buf) "Sup\nnot much.") "eprint and eprin to buffer 1") | ||||
|  | ||||
| # Printing to functions | ||||
| (def out-buf @"") | ||||
| (defn prepend [x] | ||||
|   (with-dyns [:out out-buf] | ||||
|     (prin "> " x))) | ||||
| (with-dyns [:out prepend] | ||||
|   (print "Hello world")) | ||||
|  | ||||
| (assert (= (string out-buf) "> Hello world\n") "print to buffer via function") | ||||
|  | ||||
| (assert (= (string '()) (string [])) "empty bracket tuple literal") | ||||
|  | ||||
| # with-vars | ||||
| (var abc 123) | ||||
| (assert (= 356 (with-vars [abc 456] (- abc 100))) "with-vars 1") | ||||
| (assert-error "with-vars 2" (with-vars [abc 456] (error :oops))) | ||||
| (assert (= abc 123) "with-vars 3") | ||||
|  | ||||
| # Trim empty string | ||||
| (assert (= "" (string/trim " ")) "string/trim regression") | ||||
|  | ||||
| # RNGs | ||||
|  | ||||
| (defn test-rng | ||||
|   [rng] | ||||
|   (assert (all identity (seq [i :range [0 1000]] | ||||
|                              (<= (math/rng-int rng i) i))) "math/rng-int test") | ||||
|   (assert (all identity (seq [i :range [0 1000]] | ||||
|     (def x (math/rng-uniform rng)) | ||||
|     (and (>= x 0) (< x 1)))) | ||||
|           "math/rng-uniform test")) | ||||
|  | ||||
| (def seedrng (math/rng 123)) | ||||
| (for i 0 75 | ||||
|   (test-rng (math/rng (:int seedrng)))) | ||||
|  | ||||
| (assert (deep-not= (-> 123 math/rng (:buffer 16)) | ||||
|                    (-> 456 math/rng (:buffer 16))) "math/rng-buffer 1") | ||||
|  | ||||
| (assert-no-error "math/rng-buffer 2" (math/seedrandom "abcdefg")) | ||||
|  | ||||
| # OS Date test | ||||
|  | ||||
| (assert (deep= {:year-day 0 | ||||
|                 :minutes 30 | ||||
|                 :month 0 | ||||
|                 :dst false | ||||
|                 :seconds 0 | ||||
|                 :year 2014 | ||||
|                 :month-day 0 | ||||
|                 :hours 20  | ||||
|                 :week-day 3} | ||||
|                (os/date 1388608200)) "os/date") | ||||
|  | ||||
| # OS mktime test | ||||
|  | ||||
| (assert (= 1388608200 (os/mktime {:year-day 0 | ||||
|                                   :minutes 30 | ||||
|                                   :month 0 | ||||
|                                   :dst false | ||||
|                                   :seconds 0 | ||||
|                                   :year 2014 | ||||
|                                   :month-day 0 | ||||
|                                   :hours 20 | ||||
|                                   :week-day 3})) "os/mktime") | ||||
|  | ||||
| (def now (os/time)) | ||||
| (assert (= (os/mktime (os/date now)) now) "UTC os/mktime") | ||||
| (assert (= (os/mktime (os/date now true) true) now) "local os/mktime") | ||||
| (assert (= (os/mktime {:year 1970}) 0) "os/mktime default values") | ||||
|  | ||||
| # OS strftime test | ||||
|  | ||||
| (assert (= (os/strftime "%Y-%m-%d %H:%M:%S" 0) "1970-01-01 00:00:00") "strftime UTC epoch") | ||||
| (assert (= (os/strftime "%Y-%m-%d %H:%M:%S" 1388608200) "2014-01-01 20:30:00") "strftime january 2014") | ||||
| (assert (= (try (os/strftime "%%%d%t") ([err] err)) "invalid conversion specifier '%t'") "invalid conversion specifier") | ||||
|  | ||||
| # Appending buffer to self | ||||
|  | ||||
| (with-dyns [:out @""] | ||||
|   (prin "abcd") | ||||
|   (prin (dyn :out)) | ||||
|   (prin (dyn :out)) | ||||
|   (assert (deep= (dyn :out) @"abcdabcdabcdabcd") "print buffer to self")) | ||||
|  | ||||
| (os/setenv "TESTENV1" "v1") | ||||
| (os/setenv "TESTENV2" "v2") | ||||
| (assert (= (os/getenv "TESTENV1") "v1") "getenv works") | ||||
| (def environ (os/environ)) | ||||
| (assert (= [(environ "TESTENV1") (environ "TESTENV2")] ["v1" "v2"]) "environ works") | ||||
|  | ||||
| # Issue #183 - just parse it :) | ||||
| 1e-4000000000000000000000 | ||||
|  | ||||
| # Ensure randomness puts n of pred into our buffer eventually | ||||
| (defn cryptorand-check | ||||
|   [n pred] | ||||
|   (def max-attempts 10000) | ||||
|   (var attempts 0) | ||||
|   (while (not= attempts max-attempts) | ||||
|     (def cryptobuf (os/cryptorand 10)) | ||||
|     (when (= n (count pred cryptobuf)) | ||||
|       (break)) | ||||
|     (++ attempts)) | ||||
|   (not= attempts max-attempts)) | ||||
|  | ||||
| (def v (math/rng-int (math/rng (os/time)) 100)) | ||||
| (assert (cryptorand-check 0 |(= $ v)) "cryptorand skips value sometimes") | ||||
| (assert (cryptorand-check 1 |(= $ v)) "cryptorand has value sometimes") | ||||
|  | ||||
| (do  | ||||
|   (def buf (buffer/new-filled 1)) | ||||
|   (os/cryptorand 1 buf) | ||||
|   (assert (= (in buf 0) 0) "cryptorand doesn't overwrite buffer") | ||||
|   (assert (= (length buf) 2) "cryptorand appends to buffer")) | ||||
|  | ||||
| # Nested quasiquotation | ||||
|  | ||||
| (def nested ~(a ~(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f)) | ||||
| (assert (deep= nested '(a ~(b ,(+ 1 2) ,(foo 4 d) e) f)) "nested quasiquote") | ||||
|  | ||||
| # Top level unquote | ||||
| (defn constantly | ||||
|   [] | ||||
|   (comptime (math/random))) | ||||
|  | ||||
| (assert (= (constantly) (constantly)) "comptime 1") | ||||
|  | ||||
| (assert-error "arity issue in macro" (eval '(each []))) | ||||
| (assert-error "comptime issue" (eval '(comptime (error "oops")))) | ||||
|  | ||||
| (with [f (file/temp)] | ||||
|   (assert (= 0 (file/tell f)) "start of file") | ||||
|   (file/write f "foo\n") | ||||
|   (assert (= 4 (file/tell f)) "after written string") | ||||
|   (file/flush f) | ||||
|   (file/seek f :set 0) | ||||
|   (assert (= 0 (file/tell f)) "start of file again") | ||||
|   (assert (= (string (file/read f :all)) "foo\n") "temp files work")) | ||||
|  | ||||
| (var counter 0) | ||||
| (when-with [x nil |$] | ||||
|            (++ counter)) | ||||
| (when-with [x 10 |$] | ||||
|            (+= counter 10)) | ||||
|  | ||||
| (assert (= 10 counter) "when-with 1") | ||||
|  | ||||
| (if-with [x nil |$] (++ counter) (+= counter 10)) | ||||
| (if-with [x true |$] (+= counter 20) (+= counter 30)) | ||||
|  | ||||
| (assert (= 40 counter) "if-with 1") | ||||
|  | ||||
| (def a @[]) | ||||
| (eachk x [:a :b :c :d] | ||||
|   (array/push a x)) | ||||
| (assert (deep= (range 4) a) "eachk 1") | ||||
|  | ||||
|  | ||||
| (with-dyns [:err @""] | ||||
|   (tracev (def my-unique-var-name true)) | ||||
|   (assert my-unique-var-name "tracev upscopes")) | ||||
|  | ||||
| (assert (pos? (length (gensym))) "gensym not empty, regression #753") | ||||
|  | ||||
| (end-suite) | ||||
							
								
								
									
										384
									
								
								test/suite0008.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								test/suite0008.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,384 @@ | ||||
| # Copyright (c) 2023 Calvin Rose & contributors | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 8) | ||||
|  | ||||
| ### | ||||
| ### Compiling brainfuck to Janet. | ||||
| ### | ||||
|  | ||||
| (def- bf-peg | ||||
|   "Peg for compiling brainfuck into a Janet source ast." | ||||
|   (peg/compile | ||||
|     ~{:+ (/ '(some "+") ,(fn [x] ~(+= (DATA POS) ,(length x)))) | ||||
|       :- (/ '(some "-") ,(fn [x] ~(-= (DATA POS) ,(length x)))) | ||||
|       :> (/ '(some ">") ,(fn [x] ~(+= POS ,(length x)))) | ||||
|       :< (/ '(some "<") ,(fn [x] ~(-= POS ,(length x)))) | ||||
|       :. (* "." (constant (prinf "%c" (get DATA POS)))) | ||||
|       :loop (/ (* "[" :main "]") ,(fn [& captures] | ||||
|                                     ~(while (not= (get DATA POS) 0) | ||||
|                                        ,;captures))) | ||||
|       :main (any (+ :s :loop :+ :- :> :< :.))})) | ||||
|  | ||||
| (defn bf | ||||
|   "Run brainfuck." | ||||
|   [text] | ||||
|   (eval | ||||
|     ~(let [DATA (array/new-filled 100 0)] | ||||
|        (var POS 50) | ||||
|        ,;(peg/match bf-peg text)))) | ||||
|  | ||||
| (defn test-bf | ||||
|   "Test some bf for expected output." | ||||
|   [input output] | ||||
|   (def b @"") | ||||
|   (with-dyns [:out b] | ||||
|     (bf input)) | ||||
|   (assert (= (string output) (string b)) | ||||
|           (string "bf input '" | ||||
|                   input | ||||
|                   "' failed, expected " | ||||
|                   (describe output) | ||||
|                   ", got " | ||||
|                   (describe (string b)) | ||||
|                   "."))) | ||||
|  | ||||
| (test-bf "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++." "Hello World!\n") | ||||
|  | ||||
| (test-bf ">++++++++[-<+++++++++>]<.>>+>-[+]++>++>+++[>[->+++<<+++>]<<]>-----.>-> | ||||
| +++..+++.>-.<<+[>[+>+]>>]<--------------.>>.+++.------.--------.>+.>+." | ||||
|          "Hello World!\n") | ||||
|  | ||||
| (test-bf "+[+[<<<+>>>>]+<-<-<<<+<++]<<.<++.<++..+++.<<++.<---.>>.>.+++.------.>-.>>--." | ||||
|          "Hello, World!") | ||||
|  | ||||
| # Prompts and Labels | ||||
|  | ||||
| (assert (= 10 (label a (for i 0 10 (if (= i 5) (return a 10))))) "label 1") | ||||
|  | ||||
| (defn recur | ||||
|   [lab x y] | ||||
|   (when (= x y) (return lab :done)) | ||||
|   (def res (label newlab (recur (or lab newlab) (+ x 1) y))) | ||||
|   (if lab :oops res)) | ||||
| (assert (= :done (recur nil 0 10)) "label 2") | ||||
|  | ||||
| (assert (= 10 (prompt :a (for i 0 10 (if (= i 5) (return :a 10))))) "prompt 1") | ||||
|  | ||||
| (defn- inner-loop | ||||
|   [i] | ||||
|   (if (= i 5) | ||||
|     (return :a 10))) | ||||
|  | ||||
| (assert (= 10 (prompt :a (for i 0 10 (inner-loop i)))) "prompt 2") | ||||
|  | ||||
| (defn- inner-loop2 | ||||
|   [i] | ||||
|   (try | ||||
|     (if (= i 5) | ||||
|       (error 10)) | ||||
|     ([err] (return :a err)))) | ||||
|  | ||||
| (assert (= 10 (prompt :a (for i 0 10 (inner-loop2 i)))) "prompt 3") | ||||
|  | ||||
| # Match checks | ||||
|  | ||||
| (assert (= :hi (match nil nil :hi)) "match 1") | ||||
| (assert (= :hi (match {:a :hi} {:a a} a)) "match 2") | ||||
| (assert (= nil (match {:a :hi} {:a a :b b} a)) "match 3") | ||||
| (assert (= nil (match [1 2] [a b c] a)) "match 4") | ||||
| (assert (= 2 (match [1 2] [a b] b)) "match 5") | ||||
| (assert (= [2 :a :b] (match [1 2 :a :b] [o & rest] rest)) "match 6") | ||||
| (assert (= [] (match @[:a] @[x & r] r :fallback)) "match 7") | ||||
| (assert (= :fallback (match @[1] @[x y & r] r :fallback)) "match 8") | ||||
| (assert (= [1 2 3 4] (match @[1 2 3 4] @[x y z & r] [x y z ;r] :fallback)) "match 9") | ||||
|  | ||||
| # And/or checks | ||||
|  | ||||
| (assert (= false (and false false)) "and 1") | ||||
| (assert (= false (or false false)) "or 1") | ||||
|  | ||||
| # #300 Regression test | ||||
|  | ||||
| # Just don't segfault | ||||
| (assert (peg/match '{:main (replace "S" {"S" :spade})} "S7") "regression #300") | ||||
|  | ||||
| # Test cases for #293 | ||||
| (assert (= :yes (match [1 2 3] [_ a _] :yes :no)) "match wildcard 1") | ||||
| (assert (= :no (match [1 2 3] [__ a __] :yes :no)) "match wildcard 2") | ||||
| (assert (= :yes (match [1 2 [1 2 3]] [_ a [_ _ _]] :yes :no)) "match wildcard 3") | ||||
| (assert (= :yes (match [1 2 3] (_ (even? 2)) :yes :no)) "match wildcard 4") | ||||
| (assert (= :yes (match {:a 1} {:a _} :yes :no)) "match wildcard 5") | ||||
| (assert (= false (match {:a 1 :b 2 :c 3} {:a a :b _ :c _ :d _} :no {:a _ :b _ :c _} false :no)) "match wildcard 6") | ||||
| (assert (= nil (match {:a 1 :b 2 :c 3} {:a a :b _ :c _ :d _} :no {:a _ :b _ :c _} nil :no)) "match wildcard 7") | ||||
| (assert (= "t" (match [true nil] [true _] "t")) "match wildcard 8") | ||||
|  | ||||
| # Regression #301 | ||||
| (def b (buffer/new-filled 128 0x78)) | ||||
| (assert (= 38 (length (buffer/blit @"" b -1 90))) "buffer/blit 1") | ||||
|  | ||||
| (def a @"abcdefghijklm") | ||||
| (assert (deep= @"abcde" (buffer/blit @"" a -1 0 5)) "buffer/blit 2") | ||||
| (assert (deep= @"bcde" (buffer/blit @"" a -1 1 5)) "buffer/blit 3") | ||||
| (assert (deep= @"cde" (buffer/blit @"" a -1 2 5)) "buffer/blit 4") | ||||
| (assert (deep= @"de" (buffer/blit @"" a -1 3 5)) "buffer/blit 5") | ||||
|  | ||||
| # chr | ||||
| (assert (= (chr "a") 97) "chr 1") | ||||
|  | ||||
| # Detaching closure over non resumable fiber. | ||||
| (do | ||||
|   (defn f1 | ||||
|     [a] | ||||
|     (defn f1 [] (++ (a 0))) | ||||
|     (defn f2 [] (++ (a 0))) | ||||
|     (error [f1 f2])) | ||||
|   (def [_ [f1 f2]] (protect (f1 @[0]))) | ||||
|   # At time of writing, mark phase can detach closure envs. | ||||
|   (gccollect) | ||||
|   (assert (= 1 (f1)) "detach-non-resumable-closure 1") | ||||
|   (assert (= 2 (f2)) "detach-non-resumable-closure 2")) | ||||
|  | ||||
| # Marshal closure over non resumable fiber. | ||||
| (do | ||||
|   (defn f1 | ||||
|     [a] | ||||
|     (defn f1 [] (++ (a 0))) | ||||
|     (defn f2 [] (++ (a 0))) | ||||
|     (error [f1 f2])) | ||||
|   (def [_ tup] (protect (f1 @[0]))) | ||||
|   (def [f1 f2] (unmarshal (marshal tup make-image-dict) load-image-dict)) | ||||
|   (assert (= 1 (f1)) "marshal-non-resumable-closure 1") | ||||
|   (assert (= 2 (f2)) "marshal-non-resumable-closure 2")) | ||||
|  | ||||
| # Marshal closure over currently alive fiber. | ||||
| (do | ||||
|   (defn f1 | ||||
|     [a] | ||||
|     (defn f1 [] (++ (a 0))) | ||||
|     (defn f2 [] (++ (a 0))) | ||||
|     (marshal [f1 f2] make-image-dict)) | ||||
|   (def [f1 f2] (unmarshal (f1 @[0]) load-image-dict)) | ||||
|   (assert (= 1 (f1)) "marshal-live-closure 1") | ||||
|   (assert (= 2 (f2)) "marshal-live-closure 2")) | ||||
|  | ||||
| (do | ||||
|   (var a 1) | ||||
|   (defn b [x] (+ a x)) | ||||
|   (def c (unmarshal (marshal b))) | ||||
|   (assert (= 2 (c 1)) "marshal-on-stack-closure 1")) | ||||
|  | ||||
| # Reduce2 | ||||
|  | ||||
| (assert (= (reduce + 0 (range 1 10)) (reduce2 + (range 10))) "reduce2 1") | ||||
| (assert (= (reduce * 1 (range 2 10)) (reduce2 * (range 1 10))) "reduce2 2") | ||||
| (assert (= nil (reduce2 * [])) "reduce2 3") | ||||
|  | ||||
| # Accumulate | ||||
|  | ||||
| (assert (deep= (accumulate + 0 (range 5)) @[0 1 3 6 10]) "accumulate 1") | ||||
| (assert (deep= (accumulate2 + (range 5)) @[0 1 3 6 10]) "accumulate2 1") | ||||
| (assert (deep= @[] (accumulate2 + [])) "accumulate2 2") | ||||
| (assert (deep= @[] (accumulate 0 + [])) "accumulate 2") | ||||
|  | ||||
| # Perm strings | ||||
|  | ||||
| (assert (= (os/perm-int "rwxrwxrwx") 8r777) "perm 1") | ||||
| (assert (= (os/perm-int "rwxr-xr-x") 8r755) "perm 2") | ||||
| (assert (= (os/perm-int "rw-r--r--") 8r644) "perm 3") | ||||
|  | ||||
| (assert (= (band (os/perm-int "rwxrwxrwx") 8r077) 8r077) "perm 4") | ||||
| (assert (= (band (os/perm-int "rwxr-xr-x") 8r077) 8r055) "perm 5") | ||||
| (assert (= (band (os/perm-int "rw-r--r--") 8r077) 8r044) "perm 6") | ||||
|  | ||||
| (assert (= (os/perm-string 8r777) "rwxrwxrwx") "perm 7") | ||||
| (assert (= (os/perm-string 8r755) "rwxr-xr-x") "perm 8") | ||||
| (assert (= (os/perm-string 8r644) "rw-r--r--") "perm 9") | ||||
|  | ||||
| # Issue #336 cases - don't segfault | ||||
|  | ||||
| (assert-error "unmarshal errors 1" (unmarshal @"\xd6\xb9\xb9")) | ||||
| (assert-error "unmarshal errors 2" (unmarshal @"\xd7bc")) | ||||
| (assert-error "unmarshal errors 3" (unmarshal "\xd3\x01\xd9\x01\x62\xcf\x03\x78\x79\x7a" load-image-dict)) | ||||
| (assert-error "unmarshal errors 4" | ||||
|               (unmarshal | ||||
|                 @"\xD7\xCD\0e/p\x98\0\0\x03\x01\x01\x01\x02\0\0\x04\0\xCEe/p../tools | ||||
| \0\0\0/afl\0\0\x01\0erate\xDE\xDE\xDE\xDE\xDE\xDE\xDE\xDE\xDE\xDE | ||||
| \xA8\xDE\xDE\xDE\xDE\xDE\xDE\0\0\0\xDE\xDE_unmarshal_testcase3.ja | ||||
| neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02 | ||||
| \0\0\0\0\0*\xFE\x01\04\x02\0\0'\x03\0\r\0\r\0\r\0\r" load-image-dict)) | ||||
|  | ||||
| (gccollect) | ||||
|  | ||||
| # in vs get regression | ||||
| (assert (nil? (first @"")) "in vs get 1") | ||||
| (assert (nil? (last @"")) "in vs get 1") | ||||
|  | ||||
| # For undefined behavior sanitizer | ||||
| 0xf&1fffFFFF | ||||
|  | ||||
| # Tuple comparison | ||||
| (assert (< [1 2 3] [2 2 3]) "tuple comparison 1") | ||||
| (assert (< [1 2 3] [2 2]) "tuple comparison 2") | ||||
| (assert (< [1 2 3] [2 2 3 4]) "tuple comparison 3") | ||||
| (assert (< [1 2 3] [1 2 3 4]) "tuple comparison 4") | ||||
| (assert (< [1 2 3] [1 2 3 -1]) "tuple comparison 5") | ||||
| (assert (> [1 2 3] [1 2]) "tuple comparison 6") | ||||
|  | ||||
| # Lenprefix rule | ||||
|  | ||||
| (def peg (peg/compile ~(* (lenprefix (/ (* '(any (if-not ":" 1)) ":") ,scan-number) 1) -1))) | ||||
|  | ||||
| (assert (peg/match peg "5:abcde") "lenprefix 1") | ||||
| (assert (not (peg/match peg "5:abcdef")) "lenprefix 2") | ||||
| (assert (not (peg/match peg "5:abcd")) "lenprefix 3") | ||||
|  | ||||
| # Packet capture | ||||
|  | ||||
| (def peg2 | ||||
|   (peg/compile | ||||
|     ~{# capture packet length in tag :header-len | ||||
|       :packet-header (* (/ ':d+ ,scan-number :header-len) ":") | ||||
|  | ||||
|       # capture n bytes from a backref :header-len | ||||
|       :packet-body '(lenprefix (-> :header-len) 1) | ||||
|  | ||||
|       # header, followed by body, and drop the :header-len capture | ||||
|       :packet (/ (* :packet-header :packet-body) ,|$1) | ||||
|  | ||||
|       # any exact seqence of packets (no extra characters) | ||||
|       :main (* (any :packet) -1)})) | ||||
|  | ||||
| (assert (deep= @["a" "bb" "ccc"] (peg/match peg2 "1:a2:bb3:ccc")) "lenprefix 4") | ||||
| (assert (deep= @["a" "bb" "cccccc"] (peg/match peg2 "1:a2:bb6:cccccc")) "lenprefix 5") | ||||
| (assert (= nil (peg/match peg2 "1:a2:bb:5:cccccc")) "lenprefix 6") | ||||
| (assert (= nil (peg/match peg2 "1:a2:bb:7:cccccc")) "lenprefix 7") | ||||
|  | ||||
| # Regression #400 | ||||
| (assert (= nil (while (and false false) (fn []) (error "should not happen"))) "strangeloop 1") | ||||
| (assert (= nil (while (not= nil nil) (fn []) (error "should not happen"))) "strangeloop 2") | ||||
|  | ||||
| # Issue #412 | ||||
| (assert (peg/match '(* "a" (> -1 "a") "b") "abc") "lookhead does not move cursor") | ||||
|  | ||||
| (def peg3 | ||||
|   ~{:main (* "(" (thru ")"))}) | ||||
|  | ||||
| (def peg4 (peg/compile ~(* (thru "(") '(to ")")))) | ||||
|  | ||||
| (assert (peg/match peg3 "(12345)") "peg thru 1") | ||||
| (assert (not (peg/match peg3 " (12345)")) "peg thru 2") | ||||
| (assert (not (peg/match peg3 "(12345")) "peg thru 3") | ||||
|  | ||||
| (assert (= "abc" (0 (peg/match peg4 "123(abc)"))) "peg thru/to 1") | ||||
| (assert (= "abc" (0 (peg/match peg4 "(abc)"))) "peg thru/to 2") | ||||
| (assert (not (peg/match peg4 "123(abc")) "peg thru/to 3") | ||||
|  | ||||
| (def peg5 (peg/compile [3 "abc"])) | ||||
|  | ||||
| (assert (:match peg5 "abcabcabc") "repeat alias 1") | ||||
| (assert (:match peg5 "abcabcabcac") "repeat alias 2") | ||||
| (assert (not (:match peg5 "abcabc")) "repeat alias 3") | ||||
|  | ||||
| (defn check-jdn [x] | ||||
|   (assert (deep= (parse (string/format "%j" x)) x) "round trip jdn")) | ||||
|  | ||||
| (check-jdn 0) | ||||
| (check-jdn nil) | ||||
| (check-jdn []) | ||||
| (check-jdn @[[] [] 1231 9.123123 -123123 0.1231231230001]) | ||||
| (check-jdn -0.123123123123) | ||||
| (check-jdn 12837192371923) | ||||
| (check-jdn "a string") | ||||
| (check-jdn @"a buffer") | ||||
|  | ||||
| # Issue 428 | ||||
| (var result nil) | ||||
| (defn f [] (yield {:a :ok})) | ||||
| (assert-no-error "issue 428 1" (loop [{:a x} :in (fiber/new f)] (set result x))) | ||||
| (assert (= result :ok) "issue 428 2") | ||||
|  | ||||
| # Inline 3 argument get | ||||
| (assert (= 10 (do (var a 10) (set a (get '{} :a a)))) "inline get 1") | ||||
|  | ||||
| # Keyword and Symbol slice | ||||
| (assert (= :keyword (keyword/slice "some_keyword_slice" 5 12)) "keyword slice") | ||||
| (assert (= 'symbol (symbol/slice "some_symbol_slice" 5 11)) "symbol slice") | ||||
|  | ||||
| # Peg find and find-all | ||||
| (def p "/usr/local/bin/janet") | ||||
| (assert (= (peg/find '"n/" p) 13) "peg find 1") | ||||
| (assert (not (peg/find '"t/" p)) "peg find 2") | ||||
| (assert (deep= (peg/find-all '"/" p) @[0 4 10 14]) "peg find-all") | ||||
|  | ||||
| # Peg replace and replace-all | ||||
| (defn check-replacer | ||||
|   [x y z] | ||||
|   (assert (= (string/replace x y z) (string (peg/replace x y z))) "replacer test replace") | ||||
|   (assert (= (string/replace-all x y z) (string (peg/replace-all x y z))) "replacer test replace-all")) | ||||
| (check-replacer "abc" "Z" "abcabcabcabasciabsabc") | ||||
| (check-replacer "abc" "Z" "") | ||||
| (check-replacer "aba" "ZZZZZZ" "ababababababa") | ||||
| (check-replacer "aba" "" "ababababababa") | ||||
| (check-replacer "aba" string/ascii-upper "ababababababa") | ||||
| (check-replacer "aba" 123 "ababababababa") | ||||
|  | ||||
| (assert (= (string (peg/replace-all ~(set "ab") string/ascii-upper "abcaa")) | ||||
|            "ABcAA") | ||||
|         "peg/replace-all cfunction") | ||||
| (assert (= (string (peg/replace-all ~(set "ab") |$ "abcaa")) | ||||
|            "abcaa") | ||||
|         "peg/replace-all function") | ||||
|  | ||||
| (defn peg-test [name f peg subst text expected] | ||||
|   (assert (= (string (f peg subst text)) expected) name)) | ||||
|  | ||||
| (peg-test "peg/replace has access to captures" | ||||
|   peg/replace | ||||
|   ~(sequence "." (capture (set "ab"))) | ||||
|   (fn [str char] (string/format "%s -> %s, " str (string/ascii-upper char))) | ||||
|   ".a.b.c" | ||||
|   ".a -> A, .b.c") | ||||
|  | ||||
| (peg-test "peg/replace-all has access to captures" | ||||
|   peg/replace-all | ||||
|   ~(sequence "." (capture (set "ab"))) | ||||
|   (fn [str char] (string/format "%s -> %s, " str (string/ascii-upper char))) | ||||
|   ".a.b.c" | ||||
|   ".a -> A, .b -> B, .c") | ||||
|  | ||||
| # Peg bug | ||||
| (assert (deep= @[] (peg/match '(any 1) @"")) "peg empty pattern 1") | ||||
| (assert (deep= @[] (peg/match '(any 1) (buffer))) "peg empty pattern 2") | ||||
| (assert (deep= @[] (peg/match '(any 1) "")) "peg empty pattern 3") | ||||
| (assert (deep= @[] (peg/match '(any 1) (string))) "peg empty pattern 4") | ||||
| (assert (deep= @[] (peg/match '(* "test" (any 1)) @"test")) "peg empty pattern 5") | ||||
| (assert (deep= @[] (peg/match '(* "test" (any 1)) (buffer "test"))) "peg empty pattern 6") | ||||
|  | ||||
| # number pattern | ||||
| (assert (deep= @[111] (peg/match '(number :d+) "111")) "simple number capture 1") | ||||
| (assert (deep= @[255] (peg/match '(number :w+) "0xff")) "simple number capture 2") | ||||
|  | ||||
| # quoted match test | ||||
| (assert (= :yes (match 'john 'john :yes _ :nope)) "quoted literal match 1") | ||||
| (assert (= :nope (match 'john ''john :yes _ :nope)) "quoted literal match 2") | ||||
|  | ||||
| (end-suite) | ||||
| @@ -19,58 +19,45 @@ | ||||
| # IN THE SOFTWARE. | ||||
| 
 | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
| (start-suite 9) | ||||
| 
 | ||||
| # Subprocess | ||||
| # 5e1a8c86f | ||||
| (def janet (dyn *executable*)) | ||||
| 
 | ||||
| # Subprocess should inherit the "RUN" parameter for fancy testing | ||||
| (def run (filter next (string/split " " (os/getenv "SUBRUN" "")))) | ||||
| (def janet (dyn :executable)) | ||||
| 
 | ||||
| (repeat 10 | ||||
| 
 | ||||
|   (let [p (os/spawn [;run janet "-e" `(print "hello")`] :p {:out :pipe})] | ||||
|   (let [p (os/spawn [janet "-e" `(print "hello")`] :p {:out :pipe})] | ||||
|     (os/proc-wait p) | ||||
|     (def x (:read (p :out) :all)) | ||||
|     (assert (deep= "hello" (string/trim x)) | ||||
|             "capture stdout from os/spawn pre close.")) | ||||
|     (assert (deep= "hello" (string/trim x)) "capture stdout from os/spawn pre close.")) | ||||
| 
 | ||||
|   (let [p (os/spawn [;run janet "-e" `(print "hello")`] :p {:out :pipe})] | ||||
|   (let [p (os/spawn [janet "-e" `(print "hello")`] :p {:out :pipe})] | ||||
|     (def x (:read (p :out) 1024)) | ||||
|     (os/proc-wait p) | ||||
|     (assert (deep= "hello" (string/trim x)) | ||||
|             "capture stdout from os/spawn post close.")) | ||||
|     (assert (deep= "hello" (string/trim x)) "capture stdout from os/spawn post close.")) | ||||
| 
 | ||||
|   (let [p (os/spawn [;run janet "-e" `(file/read stdin :line)`] :px | ||||
|                     {:in :pipe})] | ||||
|   (let [p (os/spawn [janet "-e" `(file/read stdin :line)`] :px {:in :pipe})] | ||||
|     (:write (p :in) "hello!\n") | ||||
|     (assert-no-error "pipe stdin to process" (os/proc-wait p)))) | ||||
| 
 | ||||
| (let [p (os/spawn [;run janet "-e" `(print (file/read stdin :line))`] :px | ||||
|                   {:in :pipe :out :pipe})] | ||||
| (let [p (os/spawn [janet "-e" `(print (file/read stdin :line))`] :px {:in :pipe :out :pipe})] | ||||
|   (:write (p :in) "hello!\n") | ||||
|   (def x (:read (p :out) 1024)) | ||||
|   (assert-no-error "pipe stdin to process 2" (os/proc-wait p)) | ||||
|   (assert (= "hello!" (string/trim x)) "round trip pipeline in process")) | ||||
| 
 | ||||
| (let [p (os/spawn [;run janet "-e" `(do (ev/sleep 30) (os/exit 24)`] :p)] | ||||
| (let [p (os/spawn [janet "-e" `(do (ev/sleep 30) (os/exit 24)`] :p)] | ||||
|   (os/proc-kill p) | ||||
|   (def retval (os/proc-wait p)) | ||||
|   (assert (not= retval 24) "Process was *not* terminated by parent")) | ||||
| 
 | ||||
| (let [p (os/spawn [;run janet "-e" `(do (ev/sleep 30) (os/exit 24)`] :p)] | ||||
|   (os/proc-kill p false :term) | ||||
|   (def retval (os/proc-wait p)) | ||||
|   (assert (not= retval 24) "Process was *not* terminated by parent")) | ||||
| 
 | ||||
| # Parallel subprocesses | ||||
| # 5e1a8c86f | ||||
| 
 | ||||
| (defn calc-1 | ||||
|   "Run subprocess, read from stdout, then wait on subprocess." | ||||
|   [code] | ||||
|   (let [p (os/spawn [;run janet "-e" (string `(printf "%j" ` code `)`)] :px | ||||
|                     {:out :pipe})] | ||||
|   (let [p (os/spawn [janet "-e" (string `(printf "%j" ` code `)`)] :px {:out :pipe})] | ||||
|     (os/proc-wait p) | ||||
|     (def output (:read (p :out) :all)) | ||||
|     (parse output))) | ||||
| @@ -84,13 +71,9 @@ | ||||
|     @[10 26 42]) "parallel subprocesses 1") | ||||
| 
 | ||||
| (defn calc-2 | ||||
|   `` | ||||
|   Run subprocess, wait on subprocess, then read from stdout. Read only up | ||||
|   to 10 bytes instead of :all | ||||
|   `` | ||||
|   "Run subprocess, wait on subprocess, then read from stdout. Read only up to 10 bytes instead of :all" | ||||
|   [code] | ||||
|   (let [p (os/spawn [;run janet "-e" (string `(printf "%j" ` code `)`)] :px | ||||
|                     {:out :pipe})] | ||||
|   (let [p (os/spawn [janet "-e" (string `(printf "%j" ` code `)`)] :px {:out :pipe})] | ||||
|     (def output (:read (p :out) 10)) | ||||
|     (os/proc-wait p) | ||||
|     (parse output))) | ||||
| @@ -104,54 +87,36 @@ | ||||
|     @[10 26 42]) "parallel subprocesses 2") | ||||
| 
 | ||||
| # File piping | ||||
| # a1cc5ca04 | ||||
| 
 | ||||
| (assert-no-error "file writing 1" | ||||
|   (with [f (file/temp)] | ||||
|     (os/execute [;run janet "-e" `(repeat 20 (print :hello))`] :p {:out f}))) | ||||
|     (os/execute [janet "-e" `(repeat 20 (print :hello))`] :p {:out f}))) | ||||
| 
 | ||||
| (assert-no-error "file writing 2" | ||||
|   (with [f (file/open "unique.txt" :w)] | ||||
|     (os/execute [;run janet "-e" `(repeat 20 (print :hello))`] :p {:out f}) | ||||
|     (os/execute [janet "-e" `(repeat 20 (print :hello))`] :p {:out f}) | ||||
|     (file/flush f))) | ||||
| 
 | ||||
| # Issue #593 | ||||
| # a1cc5ca04 | ||||
| (assert-no-error "file writing 3" | ||||
|   (def outfile (file/open "unique.txt" :w)) | ||||
|   (os/execute [;run janet "-e" "(pp (seq [i :range (1 10)] i))"] :p | ||||
|               {:out outfile}) | ||||
|   (os/execute [janet "-e" "(pp (seq [i :range (1 10)] i))"] :p {:out outfile}) | ||||
|   (file/flush outfile) | ||||
|   (file/close outfile) | ||||
|   (os/rm "unique.txt")) | ||||
| 
 | ||||
| # each-line iterator | ||||
| # 70f13f1 | ||||
| (assert-no-error "file/lines iterator" | ||||
|    (def outstream (os/open "unique.txt" :wct)) | ||||
|    (def buf1 "123\n456\n") | ||||
|    (defer (:close outstream) | ||||
|      (:write outstream buf1)) | ||||
|    (var buf2 "") | ||||
|    (with [f (file/open "unique.txt" :r)] | ||||
|      (each line (file/lines f) | ||||
|         (set buf2 (string buf2 line)))) | ||||
|    (assert (= buf1 buf2) "file/lines iterator") | ||||
|    (os/rm "unique.txt")) | ||||
| 
 | ||||
| # Ensure that the stream created by os/open works | ||||
| # e8a86013d | ||||
| 
 | ||||
| (assert-no-error "File writing 4.1" | ||||
|    (def outstream (os/open "unique.txt" :wct)) | ||||
|    (defer (:close outstream) | ||||
|      (:write outstream "123\n") | ||||
|      (:write outstream "456\n")) | ||||
|    # Cast to string to enable comparison | ||||
|    (assert (= "123\n456\n" (string (slurp "unique.txt"))) | ||||
|            "File writing 4.2") | ||||
|    (assert (= "123\n456\n" (string (slurp "unique.txt"))) "File writing 4.2") | ||||
|    (os/rm "unique.txt")) | ||||
| 
 | ||||
| # Test that the stream created by os/open can be read from | ||||
| # 8d8a6534e | ||||
| (comment | ||||
|   (assert-no-error "File reading 1.1" | ||||
|     (def outstream (os/open "unique.txt" :wct)) | ||||
| @@ -161,25 +126,17 @@ | ||||
| 
 | ||||
|     (def outstream (os/open "unique.txt" :r)) | ||||
|     (defer (:close outstream) | ||||
|       (assert (= "123\n456\n" (string (:read outstream :all))) | ||||
|               "File reading 1.2")) | ||||
|       (assert (= "123\n456\n" (string (:read outstream :all))) "File reading 1.2")) | ||||
|     (os/rm "unique.txt"))) | ||||
| 
 | ||||
| # ev/gather | ||||
| # 4f2d1cdc0 | ||||
|   # ev/gather | ||||
| 
 | ||||
| (assert (deep= @[1 2 3] (ev/gather 1 2 3)) "ev/gather 1") | ||||
| (assert (deep= @[] (ev/gather)) "ev/gather 2") | ||||
| (assert-error "ev/gather 3" (ev/gather 1 2 (error 3))) | ||||
| 
 | ||||
| (var cancel-counter 0) | ||||
| (assert-error "ev/gather 4.1" (ev/gather | ||||
|                                (defer (++ cancel-counter) (ev/take (ev/chan))) | ||||
|                                (defer (++ cancel-counter) (ev/take (ev/chan))) | ||||
|                                (error :oops))) | ||||
| (assert (= cancel-counter 2) "ev/gather 4.2") | ||||
| 
 | ||||
| # Net testing | ||||
| # 2904c19ed | ||||
| 
 | ||||
| (repeat 10 | ||||
| 
 | ||||
|   (defn handler | ||||
| @@ -205,11 +162,9 @@ | ||||
|   (test-echo "world") | ||||
|   (test-echo (string/repeat "abcd" 200)) | ||||
| 
 | ||||
|   (:close s) | ||||
|   (gccollect)) | ||||
|   (:close s)) | ||||
| 
 | ||||
| # Test on both server and client | ||||
| # 504411e | ||||
| (defn names-handler | ||||
|   [stream] | ||||
|   (defer (:close stream) | ||||
| @@ -220,7 +175,6 @@ | ||||
|     (assert (= port 8000) "localname port server"))) | ||||
| 
 | ||||
| # Test localname and peername | ||||
| # 077bf5eba | ||||
| (repeat 10 | ||||
|   (with [s (net/server "127.0.0.1" "8000" names-handler)] | ||||
|     (repeat 10 | ||||
| @@ -233,7 +187,7 @@ | ||||
|   (gccollect)) | ||||
| 
 | ||||
| # Create pipe | ||||
| # 12f09ad2d | ||||
| 
 | ||||
| (var pipe-counter 0) | ||||
| (def chan (ev/chan 10)) | ||||
| (let [[reader writer] (os/pipe)] | ||||
| @@ -249,7 +203,6 @@ | ||||
|   (ev/close writer) | ||||
|   (ev/take chan)) | ||||
| 
 | ||||
| # cff52ded5 | ||||
| (var result nil) | ||||
| (var fiber nil) | ||||
| (set fiber | ||||
| @@ -259,11 +212,10 @@ | ||||
| (ev/sleep 0) | ||||
| (ev/cancel fiber "boop") | ||||
| 
 | ||||
| # f0dbc2e | ||||
| (assert (os/execute [;run janet "-e" `(+ 1 2 3)`] :xp) "os/execute self") | ||||
| (assert (os/execute [janet "-e" `(+ 1 2 3)`] :xp) "os/execute self") | ||||
| 
 | ||||
| # Test some channel | ||||
| # e76b8da26 | ||||
| 
 | ||||
| (def c1 (ev/chan)) | ||||
| (def c2 (ev/chan)) | ||||
| (def arr @[]) | ||||
| @@ -305,17 +257,16 @@ | ||||
| (assert (= (slice arr) (slice (range 100))) "ev/chan-close 3") | ||||
| 
 | ||||
| # threaded channels | ||||
| # 868cdb9 | ||||
| 
 | ||||
| (def ch (ev/thread-chan 2)) | ||||
| (def att (ev/thread-chan 109)) | ||||
| (assert att "`att` was nil after creation") | ||||
| (ev/give ch att) | ||||
| (ev/do-thread | ||||
|   (assert (ev/take ch) | ||||
|           "channel packing bug for threaded abstracts on threaded channels.")) | ||||
|   (assert (ev/take ch) "channel packing bug for threaded abstracts on threaded channels.")) | ||||
| 
 | ||||
| # marshal channels | ||||
| # 76be8006a | ||||
| 
 | ||||
| (def ch (ev/chan 10)) | ||||
| (ev/give ch "hello") | ||||
| (ev/give ch "world") | ||||
| @@ -325,45 +276,4 @@ | ||||
| (assert (= item1 "hello")) | ||||
| (assert (= item2 "world")) | ||||
| 
 | ||||
| # ev/take, suspended, channel closed | ||||
| (def ch (ev/chan)) | ||||
| (ev/go |(ev/chan-close ch)) | ||||
| (assert (= (ev/take ch) nil)) | ||||
| 
 | ||||
| # ev/give, suspended, channel closed | ||||
| (def ch (ev/chan)) | ||||
| (ev/go |(ev/chan-close ch)) | ||||
| (assert (= (ev/give ch 1) nil)) | ||||
| 
 | ||||
| # ev/select, suspended take operation, channel closed | ||||
| (def ch (ev/chan)) | ||||
| (ev/go |(ev/chan-close ch)) | ||||
| (assert (= (ev/select ch) [:close ch])) | ||||
| 
 | ||||
| # ev/select, suspended give operation, channel closed | ||||
| (def ch (ev/chan)) | ||||
| (ev/go |(ev/chan-close ch)) | ||||
| (assert (= (ev/select [ch 1]) [:close ch])) | ||||
| 
 | ||||
| # ev/gather check | ||||
| (defn exec-slurp | ||||
|   "Read stdout of subprocess and return it trimmed in a string." | ||||
|   [& args] | ||||
|   (def env (os/environ)) | ||||
|   (put env :out :pipe) | ||||
|   (def proc (os/spawn args :epx env)) | ||||
|   (def out (get proc :out)) | ||||
|   (def buf @"") | ||||
|   (ev/gather | ||||
|     (:read out :all buf) | ||||
|     (:wait proc)) | ||||
|   (string/trimr buf)) | ||||
| (assert-no-error | ||||
|   "ev/with-deadline 1" | ||||
|   (assert (= "hi" | ||||
|              (ev/with-deadline | ||||
|                10 | ||||
|                (exec-slurp ;run janet "-e" "(print :hi)"))) | ||||
|           "exec-slurp 1")) | ||||
| 
 | ||||
| (end-suite) | ||||
							
								
								
									
										256
									
								
								test/suite0010.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								test/suite0010.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | ||||
| # Copyright (c) 2023 Calvin Rose & contributors | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 10) | ||||
|  | ||||
| # index-of | ||||
| (assert (= nil (index-of 10 [])) "index-of 1") | ||||
| (assert (= nil (index-of 10 [1 2 3])) "index-of 2") | ||||
| (assert (= 1 (index-of 2 [1 2 3])) "index-of 3") | ||||
| (assert (= 0 (index-of :a [:a :b :c])) "index-of 4") | ||||
| (assert (= nil (index-of :a {})) "index-of 5") | ||||
| (assert (= :a (index-of :A {:a :A :b :B})) "index-of 6") | ||||
| (assert (= :a (index-of :A @{:a :A :b :B})) "index-of 7") | ||||
| (assert (= 0 (index-of (chr "a") "abc")) "index-of 8") | ||||
| (assert (= nil (index-of (chr "a") "")) "index-of 9") | ||||
| (assert (= nil (index-of 10 @[])) "index-of 10") | ||||
| (assert (= nil (index-of 10 @[1 2 3])) "index-of 11") | ||||
|  | ||||
| # Regression | ||||
| (assert (= {:x 10} (|(let [x $] ~{:x ,x}) 10)) "issue 463") | ||||
|  | ||||
| # macex testing | ||||
| (assert (deep= (macex1 '~{1 2 3 4}) '~{1 2 3 4}) "macex1 qq struct") | ||||
| (assert (deep= (macex1 '~@{1 2 3 4}) '~@{1 2 3 4}) "macex1 qq table") | ||||
| (assert (deep= (macex1 '~(1 2 3 4)) '~[1 2 3 4]) "macex1 qq tuple") | ||||
| (assert (= :brackets (tuple/type (1 (macex1 '~[1 2 3 4])))) "macex1 qq bracket tuple") | ||||
| (assert (deep= (macex1 '~@[1 2 3 4 ,blah]) '~@[1 2 3 4 ,blah]) "macex1 qq array") | ||||
|  | ||||
| # Sourcemaps in threading macros | ||||
| (defn check-threading [macro expansion] | ||||
|   (def expanded (macex1 (tuple macro 0 '(x) '(y)))) | ||||
|   (assert (= expanded expansion) (string macro " expansion value")) | ||||
|   (def smap-x (tuple/sourcemap (get expanded 1))) | ||||
|   (def smap-y (tuple/sourcemap expanded)) | ||||
|   (def line first) | ||||
|   (defn column [t] (t 1)) | ||||
|   (assert (not= smap-x [-1 -1]) (string macro " x sourcemap existence")) | ||||
|   (assert (not= smap-y [-1 -1]) (string macro " y sourcemap existence")) | ||||
|   (assert (or (< (line smap-x) (line smap-y)) | ||||
|               (and (= (line smap-x) (line smap-y)) | ||||
|                    (< (column smap-x) (column smap-y)))) | ||||
|           (string macro " relation between x and y sourcemap"))) | ||||
|  | ||||
| (check-threading '-> '(y (x 0))) | ||||
| (check-threading '->> '(y (x 0))) | ||||
|  | ||||
| # keep-syntax | ||||
| (let [brak '[1 2 3] | ||||
|       par '(1 2 3)] | ||||
|  | ||||
|   (tuple/setmap brak 2 1) | ||||
|  | ||||
|   (assert (deep= (keep-syntax brak @[1 2 3]) @[1 2 3]) "keep-syntax brackets ignore array") | ||||
|   (assert (= (keep-syntax! brak @[1 2 3]) '[1 2 3]) "keep-syntax! brackets replace array") | ||||
|  | ||||
|   (assert (= (keep-syntax! par (map inc @[1 2 3])) '(2 3 4)) "keep-syntax! parens coerce array") | ||||
|   (assert (not= (keep-syntax! brak @[1 2 3]) '(1 2 3)) "keep-syntax! brackets not parens") | ||||
|   (assert (not= (keep-syntax! par @[1 2 3]) '[1 2 3]) "keep-syntax! parens not brackets") | ||||
|   (assert (= (tuple/sourcemap brak) | ||||
|              (tuple/sourcemap (keep-syntax! brak @[1 2 3]))) "keep-syntax! brackets source map") | ||||
|  | ||||
|   (keep-syntax par brak) | ||||
|   (assert (not= (tuple/sourcemap brak) (tuple/sourcemap par)) "keep-syntax no mutate") | ||||
|   (assert (= (keep-syntax 1 brak) brak) "keep-syntax brackets ignore type")) | ||||
|  | ||||
| # Cancel test | ||||
| (def f (fiber/new (fn [&] (yield 1) (yield 2) (yield 3) 4) :yti)) | ||||
| (assert (= 1 (resume f)) "cancel resume 1") | ||||
| (assert (= 2 (resume f)) "cancel resume 2") | ||||
| (assert (= :hi (cancel f :hi)) "cancel resume 3") | ||||
| (assert (= :error (fiber/status f)) "cancel resume 4") | ||||
|  | ||||
| # Curenv | ||||
| (assert (= (curenv) (curenv 0)) "curenv 1") | ||||
| (assert (= (table/getproto (curenv)) (curenv 1)) "curenv 2") | ||||
| (assert (= nil (curenv 1000000)) "curenv 3") | ||||
| (assert (= root-env (curenv 1)) "curenv 4") | ||||
|  | ||||
| # Import macro test | ||||
| (assert-no-error "import macro 1" (macex '(import a :as b :fresh maybe))) | ||||
| (assert (deep= ~(,import* "a" :as "b" :fresh maybe) (macex '(import a :as b :fresh maybe))) "import macro 2") | ||||
|  | ||||
| # #477 walk preserving bracket type | ||||
| (assert (= :brackets (tuple/type (postwalk identity '[]))) "walk square brackets 1") | ||||
| (assert (= :brackets (tuple/type (walk identity '[]))) "walk square brackets 2") | ||||
|  | ||||
| # # off by 1 error in inttypes | ||||
| (assert (= (int/s64 "-0x8000_0000_0000_0000") (+ (int/s64 "0x7FFF_FFFF_FFFF_FFFF") 1)) "int types wrap around") | ||||
|  | ||||
| # | ||||
| # Longstring indentation | ||||
| # | ||||
|  | ||||
| (defn reindent | ||||
|   "Reindent a the contents of a longstring as the Janet parser would. | ||||
|   This include removing leading and trailing newlines." | ||||
|   [text indent] | ||||
|  | ||||
|   # Detect minimum indent | ||||
|   (var rewrite true) | ||||
|   (each index (string/find-all "\n" text) | ||||
|     (for i (+ index 1) (+ index indent 1) | ||||
|       (case (get text i) | ||||
|         nil (break) | ||||
|         (chr "\n") (break) | ||||
|         (chr " ") nil | ||||
|         (set rewrite false)))) | ||||
|  | ||||
|   # Only re-indent if no dedented characters. | ||||
|   (def str | ||||
|     (if rewrite | ||||
|       (peg/replace-all ~(* "\n" (between 0 ,indent " ")) "\n" text) | ||||
|       text)) | ||||
|  | ||||
|   (def first-nl (= (chr "\n") (first str))) | ||||
|   (def last-nl (= (chr "\n") (last str))) | ||||
|   (string/slice str (if first-nl 1 0) (if last-nl -2))) | ||||
|  | ||||
| (defn reindent-reference | ||||
|   "Same as reindent but use parser functionality. Useful for validating conformance." | ||||
|   [text indent] | ||||
|   (if (empty? text) (break text)) | ||||
|   (def source-code | ||||
|     (string (string/repeat " " indent) "``````" | ||||
|             text | ||||
|             "``````")) | ||||
|   (parse source-code)) | ||||
|  | ||||
| (var indent-counter 0) | ||||
| (defn check-indent | ||||
|   [text indent] | ||||
|   (++ indent-counter) | ||||
|   (let [a (reindent text indent) | ||||
|         b (reindent-reference text indent)] | ||||
|     (assert (= a b) (string "indent " indent-counter " (indent=" indent ")")))) | ||||
|  | ||||
| (check-indent "" 0) | ||||
| (check-indent "\n" 0) | ||||
| (check-indent "\n" 1) | ||||
| (check-indent "\n\n" 0) | ||||
| (check-indent "\n\n" 1) | ||||
| (check-indent "\nHello, world!" 0) | ||||
| (check-indent "\nHello, world!" 1) | ||||
| (check-indent "Hello, world!" 0) | ||||
| (check-indent "Hello, world!" 1) | ||||
| (check-indent "\n    Hello, world!" 4) | ||||
| (check-indent "\n    Hello, world!\n" 4) | ||||
| (check-indent "\n    Hello, world!\n   " 4) | ||||
| (check-indent "\n    Hello, world!\n    " 4) | ||||
| (check-indent "\n    Hello, world!\n   dedented text\n    " 4) | ||||
| (check-indent "\n    Hello, world!\n    indented text\n    " 4) | ||||
|  | ||||
| # String bugs | ||||
| (assert (deep= (string/find-all "qq" "qqq") @[0 1]) "string/find-all 1") | ||||
| (assert (deep= (string/find-all "q" "qqq") @[0 1 2]) "string/find-all 2") | ||||
| (assert (deep= (string/split "qq" "1qqqqz") @["1" "" "z"]) "string/split 1") | ||||
| (assert (deep= (string/split "aa" "aaa") @["" "a"]) "string/split 2") | ||||
|  | ||||
| # Comparisons | ||||
| (assert (> 1e23 100) "less than immediate 1") | ||||
| (assert (> 1e23 1000) "less than immediate 2") | ||||
| (assert (< 100 1e23) "greater than immediate 1") | ||||
| (assert (< 1000 1e23) "greater than immediate 2") | ||||
|  | ||||
| # os/execute with environment variables | ||||
| (assert (= 0 (os/execute [(dyn :executable) "-e" "(+ 1 2 3)"] :pe (merge (os/environ) {"HELLO" "WORLD"}))) "os/execute with env") | ||||
|  | ||||
| # Regression #638 | ||||
| (compwhen | ||||
|   (dyn 'ev/go) | ||||
|   (assert | ||||
|     (= [true :caught] | ||||
|        (protect | ||||
|          (try | ||||
|            (do | ||||
|              (ev/sleep 0) | ||||
|              (with-dyns [] | ||||
|                (ev/sleep 0) | ||||
|                (error "oops"))) | ||||
|            ([err] :caught)))) | ||||
|     "regression #638")) | ||||
|  | ||||
|  | ||||
| # Struct prototypes | ||||
| (def x (struct/with-proto {1 2 3 4} 5 6)) | ||||
| (def y (-> x marshal unmarshal)) | ||||
| (def z {1 2 3 4}) | ||||
| (assert (= 2 (get x 1)) "struct get proto value 1") | ||||
| (assert (= 4 (get x 3)) "struct get proto value 2") | ||||
| (assert (= 6 (get x 5)) "struct get proto value 3") | ||||
| (assert (= x y) "struct proto marshal equality 1") | ||||
| (assert (= (getproto x) (getproto y)) "struct proto marshal equality 2") | ||||
| (assert (= 0 (cmp x y)) "struct proto comparison 1") | ||||
| (assert (= 0 (cmp (getproto x) (getproto y))) "struct proto comparison 2") | ||||
| (assert (not= (cmp x z) 0) "struct proto comparison 3") | ||||
| (assert (not= (cmp y z) 0) "struct proto comparison 4") | ||||
| (assert (not= x z) "struct proto comparison 5") | ||||
| (assert (not= y z) "struct proto comparison 6") | ||||
| (assert (= (x 5) 6) "struct proto get 1") | ||||
| (assert (= (y 5) 6) "struct proto get 1") | ||||
| (assert (deep= x y) "struct proto deep= 1") | ||||
| (assert (deep-not= x z) "struct proto deep= 2") | ||||
| (assert (deep-not= y z) "struct proto deep= 3") | ||||
|  | ||||
| # Issue #751 | ||||
| (def t {:side false}) | ||||
| (assert (nil? (get-in t [:side :note])) "get-in with false value") | ||||
| (assert (= (get-in t [:side :note] "dflt") "dflt") | ||||
|         "get-in with false value and default") | ||||
|  | ||||
| (assert (= (math/gcd 462 1071) 21) "math/gcd 1") | ||||
| (assert (= (math/lcm 462 1071) 23562) "math/lcm 1") | ||||
|  | ||||
| # Evaluate stream with `dofile` | ||||
| (def [r w] (os/pipe)) | ||||
| (:write w "(setdyn :x 10)") | ||||
| (:close w) | ||||
| (def stream-env (dofile r)) | ||||
| (assert (= (stream-env :x) 10) "dofile stream 1") | ||||
|  | ||||
| # Issue #861 - should be valgrind clean | ||||
| (def step1 "(a b c d)\n") | ||||
| (def step2 "(a b)\n") | ||||
| (def p1 (parser/new)) | ||||
| (parser/state p1) | ||||
| (parser/consume p1 step1) | ||||
| (loop [v :iterate (parser/produce p1)]) | ||||
| (parser/state p1) | ||||
| (def p2 (parser/clone p1)) | ||||
| (parser/state p2) | ||||
| (parser/consume p2 step2) | ||||
| (loop [v :iterate (parser/produce p2)]) | ||||
| (parser/state p2) | ||||
|  | ||||
| # Check missing struct proto bug. | ||||
| (assert (struct/getproto (struct/with-proto {:a 1} :b 2 :c nil)) "missing struct proto") | ||||
|  | ||||
| (end-suite) | ||||
							
								
								
									
										108
									
								
								test/suite0011.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								test/suite0011.janet
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| # Copyright (c) 2023 Calvin Rose & contributors | ||||
| # | ||||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| # of this software and associated documentation files (the "Software"), to | ||||
| # deal in the Software without restriction, including without limitation the | ||||
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||
| # sell copies of the Software, and to permit persons to whom the Software is | ||||
| # furnished to do so, subject to the following conditions: | ||||
| # | ||||
| # The above copyright notice and this permission notice shall be included in | ||||
| # all copies or substantial portions of the Software. | ||||
| # | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||
| # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||||
| # IN THE SOFTWARE. | ||||
|  | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite 11) | ||||
|  | ||||
| # math gamma | ||||
|  | ||||
| (assert (< 11899423.08 (math/gamma 11.5) 11899423.085) "math/gamma") | ||||
| (assert (< 2605.1158 (math/log-gamma 500) 2605.1159) "math/log-gamma") | ||||
|  | ||||
| # missing symbols | ||||
|  | ||||
| (defn lookup-symbol [sym] (defglobal sym 10) (dyn sym)) | ||||
|  | ||||
| (setdyn :missing-symbol lookup-symbol) | ||||
|  | ||||
| (assert (= (eval-string "(+ a 5)") 15) "lookup missing symbol") | ||||
|  | ||||
| (setdyn :missing-symbol nil) | ||||
| (setdyn 'a nil) | ||||
|  | ||||
| (assert-error "compile error" (eval-string "(+ a 5)")) | ||||
|  | ||||
| # 919 | ||||
| (defn test | ||||
|   [] | ||||
|   (var x 1) | ||||
|   (set x ~(,x ())) | ||||
|   x) | ||||
|  | ||||
| (assert (= (test) '(1 ())) "issue #919") | ||||
|  | ||||
| (assert (= (hash 0) (hash (* -1 0))) "hash -0 same as hash 0") | ||||
|  | ||||
| # os/execute regressions | ||||
| (for i 0 10 | ||||
|   (assert (= i (os/execute [(dyn :executable) "-e" (string/format "(os/exit %d)" i)] :p)) (string "os/execute " i))) | ||||
|  | ||||
| # to/thru bug | ||||
| (def pattern | ||||
|   (peg/compile | ||||
|     '{:dd (sequence :d :d) | ||||
|       :sep (set "/-") | ||||
|       :date (sequence :dd :sep :dd) | ||||
|       :wsep (some (set " \t")) | ||||
|       :entry (group (sequence (capture :date) :wsep (capture :date))) | ||||
|       :main (some (thru :entry))})) | ||||
|  | ||||
| (def alt-pattern | ||||
|   (peg/compile | ||||
|     '{:dd (sequence :d :d) | ||||
|       :sep (set "/-") | ||||
|       :date (sequence :dd :sep :dd) | ||||
|       :wsep (some (set " \t")) | ||||
|       :entry (group (sequence (capture :date) :wsep (capture :date))) | ||||
|       :main (some (choice :entry 1))})) | ||||
|  | ||||
| (def text "1800-10-818-9-818 16/12\n17/12 19/12\n20/12 11/01") | ||||
| (assert (deep= (peg/match pattern text) (peg/match alt-pattern text)) "to/thru bug #971") | ||||
|  | ||||
| (assert-error | ||||
|   "table rawget regression" | ||||
|   (table/new -1)) | ||||
|  | ||||
| # Named arguments | ||||
| (defn named-arguments | ||||
|   [&named bob sally joe] | ||||
|   (+ bob sally joe)) | ||||
|  | ||||
| (assert (= 15 (named-arguments :bob 3 :sally 5 :joe 7)) "named arguments 1") | ||||
|  | ||||
| (defn named-opt-arguments | ||||
|   [&opt x &named a b c] | ||||
|   (+ x a b c)) | ||||
|  | ||||
| (assert (= 10 (named-opt-arguments 1 :a 2 :b 3 :c 4)) "named arguments 2") | ||||
|  | ||||
| (let [b @""] | ||||
|   (defn dummy [a b c] | ||||
|     (+ a b c)) | ||||
|   (trace dummy) | ||||
|   (defn errout [arg] | ||||
|     (buffer/push b arg)) | ||||
|   (assert (= 6 (with-dyns [*err* errout] (dummy 1 2 3))) "trace to custom err function") | ||||
|   (assert (deep= @"trace (dummy 1 2 3)\n" b) "trace buffer correct")) | ||||
|  | ||||
| (def f (asm (disasm (fn [x] (fn [y] (+ x y)))))) | ||||
| (assert (= ((f 10) 37) 47) "asm environment tables") | ||||
|  | ||||
| (end-suite) | ||||
| @@ -19,30 +19,29 @@ | ||||
| # IN THE SOFTWARE. | ||||
| 
 | ||||
| (import ./helper :prefix "" :exit true) | ||||
| (start-suite) | ||||
| (start-suite 12) | ||||
| 
 | ||||
| (var counter 0) | ||||
| (def thunk (delay (++ counter))) | ||||
| (assert (= (thunk) 1) "delay 1") | ||||
| (assert (= counter 1) "delay 2") | ||||
| (assert (= (thunk) 1) "delay 3") | ||||
| (assert (= counter 1) "delay 4") | ||||
| 
 | ||||
| # We should get ARM support... | ||||
| (def has-ffi (dyn 'ffi/native)) | ||||
| (def has-full-ffi | ||||
|   (and has-ffi | ||||
|        (when-let [entry (dyn 'ffi/calling-conventions)] | ||||
|          (def fficc (entry :value)) | ||||
|          (> (length (fficc)) 1)))) # all arches support :none | ||||
| 
 | ||||
| # FFI check | ||||
| # d80356158 | ||||
| (compwhen has-ffi | ||||
|   (ffi/context)) | ||||
| 
 | ||||
| (compwhen has-ffi | ||||
|   (ffi/defbind memcpy :ptr [dest :ptr src :ptr n :size])) | ||||
| (compwhen has-full-ffi | ||||
| (compwhen has-ffi | ||||
|   (def buffer1 @"aaaa") | ||||
|   (def buffer2 @"bbbb") | ||||
|   (memcpy buffer1 buffer2 4) | ||||
|   (assert (= (string buffer1) "bbbb") "ffi 1 - memcpy")) | ||||
| 
 | ||||
| # cfaae47ce | ||||
| (compwhen has-ffi | ||||
|   (assert (= 8 (ffi/size [:int :char])) "size unpacked struct 1") | ||||
|   (assert (= 5 (ffi/size [:pack :int :char])) "size packed struct 1") | ||||
| @@ -50,8 +49,7 @@ | ||||
|   (assert (= 4 (ffi/align [:int :char])) "align 1") | ||||
|   (assert (= 1 (ffi/align [:pack :int :char])) "align 2") | ||||
|   (assert (= 1 (ffi/align [:int :char :pack-all])) "align 3") | ||||
|   (assert (= 26 (ffi/size [:char :pack :int @[:char 21]])) | ||||
|           "array struct size")) | ||||
|   (assert (= 26 (ffi/size [:char :pack :int @[:char 21]])) "array struct size")) | ||||
| 
 | ||||
| (end-suite) | ||||
| 
 | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user