├── tools ├── .keep ├── msi │ ├── JanetDialog.png │ ├── JanetTopBanner.png │ └── LICENSE.rtf ├── afl │ ├── prepare_to_fuzz.sh │ ├── parser_runner.janet │ ├── parser_testcases │ │ └── simple.janet │ ├── unmarshal_runner.janet │ ├── aggregate_cases.sh │ ├── generate_unmarshal_testcases.janet │ ├── fuzz.sh │ └── README.md ├── patch-header.janet ├── removecr.janet ├── format.sh ├── amalg.janet ├── hashbench │ └── ints1.janet ├── update_copyright.janet ├── symcharsgen.c └── gendoc.janet ├── examples ├── numarray │ ├── .gitignore │ ├── project.janet │ ├── test │ │ └── numarray_tests.janet │ └── numarray.c ├── sample-bad-bundle2 │ ├── badmod.janet │ ├── info.jdn │ └── bundle.janet ├── hello.janet ├── sample-bundle-aliases │ ├── aliases-mod.janet │ ├── bundle.janet │ └── info.jdn ├── sample-dep1 │ ├── dep1.janet │ └── bundle │ │ ├── info.jdn │ │ └── init.janet ├── sample-dep2 │ ├── bundle │ │ ├── info.jdn │ │ └── init.janet │ └── dep2.janet ├── sample-bad-bundle1 │ └── info.jdn ├── lineloop.janet ├── jitfn │ ├── hello.bin │ ├── hello.nasm │ └── jitfn.janet ├── sample-bundle │ ├── bundle │ │ ├── init.janet │ │ └── info.jdn │ └── mymod.janet ├── posix-exec.janet ├── udpclient.janet ├── ffi │ ├── win32.janet │ ├── gtk.janet │ ├── so.c │ └── test.janet ├── echoclient.janet ├── tcpclient.janet ├── udpserver.janet ├── debug.janet ├── abstract-unix-socket.janet ├── fizzbuzz.janet ├── rtest.janet ├── channel.janet ├── error.janet ├── echoserve.janet ├── primes.janet ├── iterate-fiber.janet ├── evsleep.janet ├── maxtriangle.janet ├── 3sum.janet ├── weak-tables.janet ├── async-execute.janet ├── select.janet ├── threaded-channels.janet ├── tcpserver.janet ├── select2.janet ├── sigaction.janet ├── assembly.janet ├── marshal-stress.janet ├── evlocks.janet ├── urlloader.janet ├── colors.janet ├── chatserver.janet ├── life.janet ├── lazyseqs.janet └── debugger.janet ├── janet_win.rc ├── assets ├── icon.ico ├── janet-big.png ├── janet-w200.png └── icon_svg.svg ├── .gitattributes ├── .builds ├── freebsd.yml ├── linux.yml └── openbsd.yml ├── src ├── boot │ ├── tests.h │ ├── buffer_test.c │ ├── array_test.c │ ├── number_test.c │ ├── table_test.c │ ├── system_test.c │ └── boot.c ├── core │ ├── symcache.h │ ├── vector.c │ ├── state.c │ ├── emit.h │ ├── regalloc.h │ ├── vector.h │ ├── gc.h │ ├── features.h │ ├── fiber.h │ ├── regalloc.c │ ├── state.h │ └── abstract.c └── conf │ └── janetconf.h ├── .github ├── cosmo │ ├── setup │ └── build └── workflows │ ├── codeql.yml │ ├── release.yml │ └── test.yml ├── LICENSE ├── test ├── fuzzers │ └── fuzz_dostring.c ├── suite-cfuns.janet ├── suite-debug.janet ├── suite-tuple.janet ├── amalg │ └── main.c ├── suite-strtod.janet ├── suite-symcache.janet ├── suite-capi.janet ├── suite-ev2.janet ├── suite-asm.janet ├── suite-pp.janet ├── suite-math.janet ├── suite-ffi.janet ├── suite-compile.janet ├── suite-table.janet ├── suite-io.janet ├── suite-value.janet ├── helper.janet ├── suite-array.janet ├── suite-struct.janet ├── suite-vm.janet └── suite-bundle.janet ├── meson_options.txt ├── mkfile ├── .gitignore └── CONTRIBUTING.md /tools/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/numarray/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /janet_win.rc: -------------------------------------------------------------------------------- 1 | IDI_MYICON ICON "assets\icon.ico" -------------------------------------------------------------------------------- /examples/sample-bad-bundle2/badmod.janet: -------------------------------------------------------------------------------- 1 | (def abc 123) 2 | -------------------------------------------------------------------------------- /examples/hello.janet: -------------------------------------------------------------------------------- 1 | # Prints hello 2 | 3 | (print "hello, world!") 4 | -------------------------------------------------------------------------------- /examples/sample-bundle-aliases/aliases-mod.janet: -------------------------------------------------------------------------------- 1 | (defn fun [x] (range x)) 2 | -------------------------------------------------------------------------------- /examples/sample-dep1/dep1.janet: -------------------------------------------------------------------------------- 1 | (defn function 2 | [x] 3 | (+ x x)) 4 | -------------------------------------------------------------------------------- /examples/sample-dep2/bundle/info.jdn: -------------------------------------------------------------------------------- 1 | @{ 2 | :name "sample-dep2" 3 | } 4 | -------------------------------------------------------------------------------- /examples/sample-dep2/dep2.janet: -------------------------------------------------------------------------------- 1 | (defn function 2 | [x] 3 | (* x x)) 4 | -------------------------------------------------------------------------------- /assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janet-lang/janet/HEAD/assets/icon.ico -------------------------------------------------------------------------------- /examples/sample-bad-bundle1/info.jdn: -------------------------------------------------------------------------------- 1 | @{ 2 | :name "sample-bad-bundle1" 3 | } 4 | -------------------------------------------------------------------------------- /examples/sample-bad-bundle2/info.jdn: -------------------------------------------------------------------------------- 1 | @{ 2 | :name "sample-bad-bundle2" 3 | } 4 | -------------------------------------------------------------------------------- /assets/janet-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janet-lang/janet/HEAD/assets/janet-big.png -------------------------------------------------------------------------------- /assets/janet-w200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janet-lang/janet/HEAD/assets/janet-w200.png -------------------------------------------------------------------------------- /examples/lineloop.janet: -------------------------------------------------------------------------------- 1 | (while (not (empty? (def line (getline)))) 2 | (prin "line: " line)) 3 | -------------------------------------------------------------------------------- /examples/jitfn/hello.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janet-lang/janet/HEAD/examples/jitfn/hello.bin -------------------------------------------------------------------------------- /tools/msi/JanetDialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janet-lang/janet/HEAD/tools/msi/JanetDialog.png -------------------------------------------------------------------------------- /tools/msi/JanetTopBanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janet-lang/janet/HEAD/tools/msi/JanetTopBanner.png -------------------------------------------------------------------------------- /examples/sample-dep1/bundle/info.jdn: -------------------------------------------------------------------------------- 1 | @{ 2 | :name "sample-dep1" 3 | :dependencies [{:name "sample-dep2"}] 4 | } 5 | -------------------------------------------------------------------------------- /examples/sample-dep1/bundle/init.janet: -------------------------------------------------------------------------------- 1 | (defn install 2 | [manifest &] 3 | (bundle/add-file manifest "dep1.janet")) 4 | -------------------------------------------------------------------------------- /examples/sample-dep2/bundle/init.janet: -------------------------------------------------------------------------------- 1 | (defn install 2 | [manifest &] 3 | (bundle/add-file manifest "dep2.janet")) 4 | -------------------------------------------------------------------------------- /examples/sample-bundle/bundle/init.janet: -------------------------------------------------------------------------------- 1 | (defn install 2 | [manifest &] 3 | (bundle/add-file manifest "mymod.janet")) 4 | -------------------------------------------------------------------------------- /examples/sample-bundle/bundle/info.jdn: -------------------------------------------------------------------------------- 1 | @{ 2 | :name "sample-bundle" 3 | :dependencies ["sample-dep1" "sample-dep2"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/sample-bundle-aliases/bundle.janet: -------------------------------------------------------------------------------- 1 | (defn install 2 | [manifest &] 3 | (bundle/add-file manifest "aliases-mod.janet")) 4 | -------------------------------------------------------------------------------- /examples/sample-bundle-aliases/info.jdn: -------------------------------------------------------------------------------- 1 | @{ 2 | :name "sample-bundle-aliases" 3 | :dependencies ["sample-dep1" "sample-dep2"] 4 | } 5 | -------------------------------------------------------------------------------- /tools/afl/prepare_to_fuzz.sh: -------------------------------------------------------------------------------- 1 | set -eux 2 | 3 | export CC=afl-clang 4 | make clean 5 | make -j $(nproc) all 6 | mkdir -p "./fuzz_out" 7 | -------------------------------------------------------------------------------- /examples/posix-exec.janet: -------------------------------------------------------------------------------- 1 | # Switch to python 2 | 3 | (print "running in Janet") 4 | (os/posix-exec ["python"] :p) 5 | (print "will not print") 6 | -------------------------------------------------------------------------------- /examples/sample-bundle/mymod.janet: -------------------------------------------------------------------------------- 1 | (import dep1) 2 | (import dep2) 3 | 4 | (defn myfn 5 | [x] 6 | (def y (dep2/function x)) 7 | (dep1/function y)) 8 | -------------------------------------------------------------------------------- /tools/afl/parser_runner.janet: -------------------------------------------------------------------------------- 1 | (def p (parser/new)) 2 | (parser/consume p (slurp ((dyn :args) 1))) 3 | (while (parser/has-more p) 4 | (pp (parser/produce p))) 5 | -------------------------------------------------------------------------------- /examples/sample-bad-bundle2/bundle.janet: -------------------------------------------------------------------------------- 1 | (defn install 2 | [manifest &] 3 | (bundle/add-file manifest "badmod.janet")) 4 | 5 | (defn check 6 | [&] 7 | (error "Check failed!")) 8 | -------------------------------------------------------------------------------- /examples/udpclient.janet: -------------------------------------------------------------------------------- 1 | (def conn (net/connect "127.0.0.1" "8009" :datagram)) 2 | (:write conn (string/format "%q" (os/cryptorand 16))) 3 | (def x (:read conn 1024)) 4 | (pp x) 5 | 6 | -------------------------------------------------------------------------------- /tools/patch-header.janet: -------------------------------------------------------------------------------- 1 | # Patch janet.h 2 | (def [_ janeth janetconf output] (dyn :args)) 3 | (spit output (string/replace `#include "janetconf.h"` (slurp janetconf) (slurp janeth))) 4 | -------------------------------------------------------------------------------- /examples/ffi/win32.janet: -------------------------------------------------------------------------------- 1 | (ffi/context "user32.dll") 2 | 3 | (ffi/defbind MessageBoxA :int 4 | [w :ptr text :string cap :string typ :int]) 5 | 6 | (MessageBoxA nil "Hello, World!" "Test" 0) 7 | 8 | -------------------------------------------------------------------------------- /examples/echoclient.janet: -------------------------------------------------------------------------------- 1 | (with [conn (net/connect "127.0.0.1" 8000)] 2 | (print "writing abcdefg...") 3 | (:write conn "abcdefg") 4 | (print "reading...") 5 | (printf "got: %v" (:read conn 1024))) 6 | -------------------------------------------------------------------------------- /examples/numarray/project.janet: -------------------------------------------------------------------------------- 1 | (declare-project 2 | :name "numarray" 3 | :description "Example c lib with abstract type") 4 | 5 | (declare-native 6 | :name "numarray" 7 | :source @["numarray.c"]) 8 | -------------------------------------------------------------------------------- /tools/afl/parser_testcases/simple.janet: -------------------------------------------------------------------------------- 1 | 0 2 | 123.653 3 | true 4 | :true 5 | {} 6 | ` 7 | hello 8 | ` 9 | |() 10 | ,() 11 | @{:hello "world"} 12 | @[1 "hello"] 13 | nil 14 | (foo 2 3) 15 | ([{} @{:k ([""])}]) 16 | -------------------------------------------------------------------------------- /examples/tcpclient.janet: -------------------------------------------------------------------------------- 1 | (with [conn (net/connect "127.0.0.1" "8000")] 2 | (printf "Connected to %q!" conn) 3 | (:write conn "Echo...") 4 | (print "Wrote to connection...") 5 | (def res (:read conn 1024)) 6 | (pp res)) 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.janet linguist-language=Janet 2 | *.janet text eol=lf 3 | *.c text eol=lf 4 | *.h text eol=lf 5 | *.md text eol=lf 6 | *.yml text eol=lf 7 | *.build text eol=lf 8 | *.txt text eol=lf 9 | *.sh text eol=lf 10 | -------------------------------------------------------------------------------- /examples/udpserver.janet: -------------------------------------------------------------------------------- 1 | (def server (net/listen "127.0.0.1" "8009" :datagram)) 2 | (while true 3 | (def buf @"") 4 | (def who (:recv-from server 1024 buf)) 5 | (printf "got %q from %v, echoing!" buf who) 6 | (:send-to server who buf)) 7 | -------------------------------------------------------------------------------- /tools/afl/unmarshal_runner.janet: -------------------------------------------------------------------------------- 1 | # Unmarshal garbage. 2 | (def v (unmarshal (slurp ((dyn :args) 1)) load-image-dict)) 3 | # Trigger leaks or use after free. 4 | (gccollect) 5 | # Attempt to use generated value. 6 | (marshal v make-image-dict) 7 | -------------------------------------------------------------------------------- /examples/debug.janet: -------------------------------------------------------------------------------- 1 | # Load this file and run (myfn) to see the debugger 2 | 3 | (defn myfn 4 | [] 5 | (debug) 6 | (for i 0 10 (print i))) 7 | 8 | (debug/fbreak myfn 3) 9 | 10 | # Enable debugging in repl with 11 | # (setdyn :debug true) 12 | -------------------------------------------------------------------------------- /.builds/freebsd.yml: -------------------------------------------------------------------------------- 1 | image: freebsd/14.x 2 | sources: 3 | - https://git.sr.ht/~bakpakin/janet 4 | packages: 5 | - gmake 6 | tasks: 7 | - build: | 8 | cd janet 9 | gmake 10 | gmake test 11 | sudo gmake install 12 | sudo gmake uninstall 13 | -------------------------------------------------------------------------------- /examples/abstract-unix-socket.janet: -------------------------------------------------------------------------------- 1 | # Linux only - uses abstract unix domain sockets 2 | (ev/spawn (net/server :unix "@abc123" (fn [conn] (print (:read conn 1024)) (:close conn)))) 3 | (ev/sleep 1) 4 | (def s (net/connect :unix "@abc123" :stream)) 5 | (:write s "hello") 6 | (:close s) 7 | -------------------------------------------------------------------------------- /examples/fizzbuzz.janet: -------------------------------------------------------------------------------- 1 | # A simple fizz buzz example 2 | 3 | (loop [i :range [1 101] 4 | :let [fizz (zero? (% i 3)) 5 | buzz (zero? (% i 5))]] 6 | (print (cond 7 | (and fizz buzz) "fizzbuzz" 8 | fizz "fizz" 9 | buzz "buzz" 10 | i))) 11 | -------------------------------------------------------------------------------- /src/boot/tests.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTS_H_DNMBUYYL 2 | #define TESTS_H_DNMBUYYL 3 | 4 | /* Tests */ 5 | extern int array_test(); 6 | extern int buffer_test(); 7 | extern int number_test(); 8 | extern int system_test(); 9 | extern int table_test(); 10 | 11 | #endif /* end of include guard: TESTS_H_DNMBUYYL */ 12 | -------------------------------------------------------------------------------- /tools/removecr.janet: -------------------------------------------------------------------------------- 1 | # Remove carriage returns from file. Since piping things on 2 | # windows may add bad line endings, we can just force removal 3 | # with this script. 4 | (def fname ((dyn :args) 1)) 5 | (def source (slurp fname)) 6 | (def new-source (string/replace-all "\r" "" source)) 7 | (spit fname new-source :wb) 8 | -------------------------------------------------------------------------------- /examples/rtest.janet: -------------------------------------------------------------------------------- 1 | # How random is the RNG really? 2 | 3 | (def counts (seq [_ :range [0 100]] 0)) 4 | 5 | (for i 0 1000000 6 | (let [x (math/random) 7 | intrange (math/floor (* 100 x)) 8 | oldcount (counts intrange)] 9 | (put counts intrange (if oldcount (+ 1 oldcount) 1)))) 10 | 11 | (pp counts) 12 | -------------------------------------------------------------------------------- /tools/afl/aggregate_cases.sh: -------------------------------------------------------------------------------- 1 | set -eux 2 | 3 | n=0 4 | for tc in $(echo ./fuzz_out/$1/*/hangs/* ./fuzz_out/$1/*/crashes/*) 5 | do 6 | if ! test -e $tc 7 | then 8 | continue 9 | fi 10 | mkdir -p ./fuzz_out/$1_aggregated/ 11 | cp "$tc" $(printf "./fuzz_out/$1_aggregated/$1-%04d.test" $n) 12 | n=$((n + 1)) 13 | done 14 | -------------------------------------------------------------------------------- /examples/numarray/test/numarray_tests.janet: -------------------------------------------------------------------------------- 1 | (import /build/numarray) 2 | 3 | (def a (numarray/new 30)) 4 | (print (get a 20)) 5 | (print (a 20)) 6 | 7 | (put a 5 3.14) 8 | (print (a 5)) 9 | (set (a 5) 100) 10 | (print (a 5)) 11 | 12 | # (numarray/scale a 5)) 13 | # ((a :scale) a 5) 14 | (:scale a 5) 15 | (for i 0 10 (print (a i))) 16 | 17 | (print "sum=" (:sum a)) 18 | -------------------------------------------------------------------------------- /examples/jitfn/hello.nasm: -------------------------------------------------------------------------------- 1 | BITS 64 2 | 3 | ;;; 4 | ;;; Code 5 | ;;; 6 | mov rax, 1 ; write( 7 | mov rdi, 1 ; STDOUT_FILENO, 8 | lea rsi, [rel msg] ; msg, 9 | mov rdx, msglen ; sizeof(msg) 10 | syscall ; ); 11 | ret ; return; 12 | 13 | ;;; 14 | ;;; Constants 15 | ;;; 16 | msg: db "Hello, world!", 10 17 | msglen: equ $ - msg 18 | -------------------------------------------------------------------------------- /examples/channel.janet: -------------------------------------------------------------------------------- 1 | (def c (ev/chan 4)) 2 | 3 | (defn writer [] 4 | (for i 0 10 5 | (ev/sleep 0.1) 6 | (print "writer giving item " i "...") 7 | (ev/give c (string "item " i)))) 8 | 9 | (defn reader [name] 10 | (forever 11 | (print "reader " name " got " (ev/take c)))) 12 | 13 | (ev/call writer) 14 | (each letter [:a :b :c :d :e :f :g] 15 | (ev/call reader letter)) 16 | -------------------------------------------------------------------------------- /tools/format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Format all code with astyle 4 | 5 | STYLEOPTS="--style=attach --indent-switches --convert-tabs \ 6 | --align-pointer=name --pad-header --pad-oper --unpad-paren --indent-labels --formatted" 7 | 8 | astyle $STYLEOPTS */*.c 9 | astyle $STYLEOPTS */*/*.c 10 | astyle $STYLEOPTS */*/*.h 11 | rm -f */*.c.orig 12 | rm -f */*/*.c.orig 13 | rm -f */*/*.h.orig 14 | -------------------------------------------------------------------------------- /examples/error.janet: -------------------------------------------------------------------------------- 1 | # An example file that errors out. Run with ./janet examples/error.janet 2 | # to see stack trace for runtime errors. 3 | 4 | (defn bork [x] 5 | 6 | (defn bark [x] 7 | (print "Woof!") 8 | (print x) 9 | (error x) 10 | (print "Woof!")) 11 | 12 | (bark (* 2 x)) 13 | (bark (* 3 x))) 14 | 15 | (defn pupper [] 16 | (bork 3) 17 | 1) 18 | 19 | (do (pupper) 1) 20 | -------------------------------------------------------------------------------- /examples/echoserve.janet: -------------------------------------------------------------------------------- 1 | (defn handler 2 | "Simple handler for connections." 3 | [stream] 4 | (defer (:close stream) 5 | (def id (gensym)) 6 | (def b @"") 7 | (print "Connection " id "!") 8 | (while (:read stream 1024 b) 9 | (printf " %v -> %v" id b) 10 | (:write stream b) 11 | (buffer/clear b)) 12 | (printf "Done %v!" id) 13 | (ev/sleep 0.5))) 14 | 15 | (net/server "127.0.0.1" "8000" handler) 16 | -------------------------------------------------------------------------------- /examples/primes.janet: -------------------------------------------------------------------------------- 1 | # Return an array of primes. This is a trivial and extremely naive algorithm. 2 | 3 | (defn primes 4 | "Returns a list of prime numbers less than n." 5 | [n] 6 | (def list @[]) 7 | (for i 2 n 8 | (var isprime? true) 9 | (def len (length list)) 10 | (for j 0 len 11 | (def trial (get list j)) 12 | (if (zero? (% i trial)) (set isprime? false))) 13 | (if isprime? (array/push list i))) 14 | list) 15 | 16 | (pp (primes 100)) 17 | -------------------------------------------------------------------------------- /examples/iterate-fiber.janet: -------------------------------------------------------------------------------- 1 | (def f 2 | (coro 3 | (for i 0 10 4 | (yield (string "yield " i)) 5 | (os/sleep 0)))) 6 | 7 | (print "simple yielding") 8 | (each item f (print "got: " item ", now " (fiber/status f))) 9 | 10 | (def f 11 | (coro 12 | (for i 0 10 13 | (yield (string "yield " i)) 14 | (ev/sleep 0)))) 15 | 16 | (print "complex yielding") 17 | (each item f (print "got: " item ", now " (fiber/status f))) 18 | 19 | (print (fiber/status f)) 20 | -------------------------------------------------------------------------------- /examples/jitfn/jitfn.janet: -------------------------------------------------------------------------------- 1 | ### 2 | ### Relies on NASM being installed to assemble code. 3 | ### Only works on x86-64 Linux. 4 | ### 5 | ### Before running, compile hello.nasm to hello.bin with 6 | ### $ nasm hello.nasm -o hello.bin 7 | 8 | (def bin (slurp "hello.bin")) 9 | (def f (ffi/jitfn bin)) 10 | (def signature (ffi/signature :default :void)) 11 | (ffi/call f signature) 12 | (print "called a jitted function with FFI!") 13 | (print "machine code: " (describe (string/slice f))) 14 | -------------------------------------------------------------------------------- /examples/evsleep.janet: -------------------------------------------------------------------------------- 1 | (defn worker 2 | "Run for a number of iterations." 3 | [name iterations] 4 | (for i 0 iterations 5 | (ev/sleep 1) 6 | (print "worker " name " iteration " i))) 7 | 8 | (ev/call worker :a 10) 9 | (ev/sleep 0.2) 10 | (ev/call worker :b 5) 11 | (ev/sleep 0.3) 12 | (ev/call worker :c 12) 13 | 14 | (defn worker2 15 | [name] 16 | (repeat 10 17 | (ev/sleep 0.2) 18 | (print name " working"))) 19 | 20 | (ev/go worker2 :bob) 21 | (ev/go worker2 :joe) 22 | (ev/go worker2 :sally) 23 | -------------------------------------------------------------------------------- /examples/maxtriangle.janet: -------------------------------------------------------------------------------- 1 | # Find the maximum path from the top (root) 2 | # of the triangle to the leaves of the triangle. 3 | 4 | (defn myfold [xs ys] 5 | (let [m1 (map + [;xs 0] ys) 6 | m2 (map + [0 ;xs] ys)] 7 | (map max m1 m2))) 8 | 9 | (defn maxpath [t] 10 | (extreme > (reduce myfold () t))) 11 | 12 | # Test it 13 | # Maximum path is 3 -> 10 -> 3 -> 9 for a total of 25 14 | 15 | (def triangle 16 | '[[3] 17 | [7 10] 18 | [4 3 7] 19 | [8 9 1 3]]) 20 | 21 | (print (maxpath triangle)) 22 | -------------------------------------------------------------------------------- /tools/amalg.janet: -------------------------------------------------------------------------------- 1 | # Creates an amalgamated janet.c 2 | 3 | # Head 4 | (print "/* Amalgamated build - DO NOT EDIT */") 5 | (print "/* Generated from janet version " janet/version "-" janet/build " */") 6 | (print "#define JANET_BUILD \"" janet/build "\"") 7 | (print ```#define JANET_AMALG```) 8 | (print ```#define _POSIX_C_SOURCE 200112L```) 9 | (print ```#include "janet.h"```) 10 | 11 | # Body 12 | (each path (tuple/slice (dyn :args) 1) 13 | (print "\n/* " path " */\n") 14 | (print (slurp path))) 15 | 16 | # maybe will help 17 | (:flush stdout) 18 | -------------------------------------------------------------------------------- /examples/3sum.janet: -------------------------------------------------------------------------------- 1 | (defn sum3 2 | "Solve the 3SUM problem in O(n^2) time." 3 | [s] 4 | (def tab @{}) 5 | (def solutions @{}) 6 | (def len (length s)) 7 | (for k 0 len 8 | (put tab (s k) k)) 9 | (for i 0 len 10 | (for j 0 len 11 | (def k (get tab (- 0 (s i) (s j)))) 12 | (when (and k (not= k i) (not= k j) (not= i j)) 13 | (put solutions {i true j true k true} true)))) 14 | (map keys (keys solutions))) 15 | 16 | (def arr @[2 4 1 3 8 7 -3 -1 12 -5 -8]) 17 | (printf "3sum of %P: " arr) 18 | (printf "%P\n" (sum3 arr)) 19 | -------------------------------------------------------------------------------- /examples/weak-tables.janet: -------------------------------------------------------------------------------- 1 | (def weak-k (table/weak-keys 10)) 2 | (def weak-v (table/weak-values 10)) 3 | (def weak-kv (table/weak 10)) 4 | 5 | (put weak-kv (gensym) 10) 6 | (put weak-kv :hello :world) 7 | (put weak-k :abc123zz77asda :stuff) 8 | (put weak-k true :abc123zz77asda) 9 | (put weak-k :zyzzyz false) 10 | (put weak-v (gensym) 10) 11 | (put weak-v 20 (gensym)) 12 | (print "before gc") 13 | (tracev weak-k) 14 | (tracev weak-v) 15 | (tracev weak-kv) 16 | (gccollect) 17 | (print "after gc") 18 | (tracev weak-k) 19 | (tracev weak-v) 20 | (tracev weak-kv) 21 | -------------------------------------------------------------------------------- /examples/async-execute.janet: -------------------------------------------------------------------------------- 1 | (defn dowork [name n] 2 | (print name " starting work...") 3 | (os/execute [(dyn :executable) "-e" (string "(os/sleep " n ")")] :p) 4 | (print name " finished work!")) 5 | 6 | # Will be done in parallel 7 | (print "starting group A") 8 | (ev/call dowork "A 2" 2) 9 | (ev/call dowork "A 1" 1) 10 | (ev/call dowork "A 3" 3) 11 | 12 | (ev/sleep 4) 13 | 14 | # Will also be done in parallel 15 | (print "starting group B") 16 | (ev/call dowork "B 2" 2) 17 | (ev/call dowork "B 1" 1) 18 | (ev/call dowork "B 3" 3) 19 | 20 | (ev/sleep 4) 21 | 22 | (print "all work done") 23 | -------------------------------------------------------------------------------- /examples/select.janet: -------------------------------------------------------------------------------- 1 | (def channels 2 | (seq [:repeat 5] (ev/chan 4))) 3 | 4 | (defn writer [c] 5 | (for i 0 3 6 | (def item (string i ":" (mod (hash c) 999))) 7 | (ev/sleep 0.1) 8 | (print "writer giving item " item " to " c "...") 9 | (ev/give c item)) 10 | (print "Done!")) 11 | 12 | (defn reader [name] 13 | (forever 14 | (def [_ c x] (ev/rselect ;channels)) 15 | (print "reader " name " got " x " from " c))) 16 | 17 | # Readers 18 | (each letter [:a :b :c :d :e :f :g] 19 | (ev/call reader letter)) 20 | 21 | # Writers 22 | (each c channels 23 | (ev/call writer c)) 24 | -------------------------------------------------------------------------------- /.github/cosmo/setup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | sudo apt update 5 | sudo apt-get install -y ca-certificates libssl-dev\ 6 | qemu qemu-utils qemu-user-static\ 7 | texinfo groff\ 8 | cmake ninja-build bison zip\ 9 | pkg-config build-essential autoconf re2c 10 | 11 | # download cosmocc 12 | cd /sc 13 | wget https://github.com/jart/cosmopolitan/releases/download/3.3.3/cosmocc-3.3.3.zip 14 | mkdir -p cosmocc 15 | cd cosmocc 16 | unzip ../cosmocc-3.3.3.zip 17 | 18 | # register 19 | cd /sc/cosmocc 20 | sudo cp ./bin/ape-x86_64.elf /usr/bin/ape 21 | sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" 22 | -------------------------------------------------------------------------------- /examples/threaded-channels.janet: -------------------------------------------------------------------------------- 1 | (def chan (ev/thread-chan 10)) 2 | 3 | (ev/spawn 4 | (ev/sleep 0) 5 | (print "started fiber!") 6 | (ev/give chan (math/random)) 7 | (ev/give chan (math/random)) 8 | (ev/give chan (math/random)) 9 | (ev/sleep 0.5) 10 | (for i 0 10 11 | (print "giving to channel...") 12 | (ev/give chan (math/random)) 13 | (ev/sleep 1)) 14 | (print "finished fiber!") 15 | (:close chan)) 16 | 17 | (ev/do-thread 18 | (print "started thread!") 19 | (ev/sleep 1) 20 | (while (def x (do (print "taking from channel...") (ev/take chan))) 21 | (print "got " x " from thread!")) 22 | (print "finished thread!")) 23 | -------------------------------------------------------------------------------- /examples/tcpserver.janet: -------------------------------------------------------------------------------- 1 | (defn handler 2 | "Simple handler for connections." 3 | [stream] 4 | (defer (:close stream) 5 | (def id (gensym)) 6 | (def b @"") 7 | (print "Connection " id "!") 8 | (while (:read stream 1024 b) 9 | (repeat 10 (print "work for " id " ...") (ev/sleep 0.1)) 10 | (:write stream b) 11 | (buffer/clear b)) 12 | (printf "Done %v!" id))) 13 | 14 | # Run server. 15 | (let [server (net/server "127.0.0.1" "8000")] 16 | (print "Starting echo server on 127.0.0.1:8000") 17 | (forever 18 | (if-let [conn (:accept server)] 19 | (ev/call handler conn) 20 | (print "no new connections")))) 21 | -------------------------------------------------------------------------------- /tools/hashbench/ints1.janet: -------------------------------------------------------------------------------- 1 | (def f @{}) 2 | (var collisions 0) 3 | (loop [x :range [0 300] y :range [0 300]] 4 | (def key (hash (+ (* x 1000) y))) 5 | (if (in f key) 6 | (++ collisions)) 7 | (put f key true)) 8 | (print "ints 1 collisions: " collisions) 9 | 10 | (def f @{}) 11 | (var collisions 0) 12 | (loop [x :range [100000 101000] y :range [100000 101000]] 13 | (def key (hash [x y])) 14 | (if (in f key) (++ collisions)) 15 | (put f key true)) 16 | (print "int pair 1 collisions: " collisions) 17 | 18 | (def f @{}) 19 | (var collisions 0) 20 | (loop [x :range [10000 11000] y :range [10000 11000]] 21 | (def key (hash [x y])) 22 | (if (in f key) (++ collisions)) 23 | (put f key true)) 24 | (print "int pair 2 collisions: " collisions) 25 | -------------------------------------------------------------------------------- /examples/select2.janet: -------------------------------------------------------------------------------- 1 | ### 2 | ### examples/select2.janet 3 | ### 4 | ### Mix reads and writes in select. 5 | ### 6 | 7 | (def c1 (ev/chan 40)) 8 | (def c2 (ev/chan 40)) 9 | (def c3 (ev/chan 40)) 10 | (def c4 (ev/chan 40)) 11 | 12 | (def c5 (ev/chan 4)) 13 | 14 | (defn worker 15 | [c n x] 16 | (forever 17 | (ev/sleep n) 18 | (ev/give c x))) 19 | 20 | (defn writer-worker 21 | [c] 22 | (forever 23 | (ev/sleep 0.2) 24 | (print "writing " (ev/take c)))) 25 | 26 | (ev/call worker c1 1 :item1) 27 | (ev/sleep 0.2) 28 | (ev/call worker c2 1 :item2) 29 | (ev/sleep 0.1) 30 | (ev/call worker c3 1 :item3) 31 | (ev/sleep 0.2) 32 | (ev/call worker c4 1 :item4) 33 | (ev/sleep 0.1) 34 | (ev/call worker c4 1 :item5) 35 | (ev/call writer-worker c5) 36 | 37 | (forever (pp (ev/rselect c1 c2 c3 c4 [c5 :thing]))) 38 | -------------------------------------------------------------------------------- /.builds/linux.yml: -------------------------------------------------------------------------------- 1 | image: archlinux 2 | sources: 3 | - https://git.sr.ht/~bakpakin/janet 4 | packages: 5 | - meson 6 | tasks: 7 | - with-epoll: | 8 | cd janet 9 | meson setup with-epoll --buildtype=release 10 | cd with-epoll 11 | meson configure -Depoll=true 12 | ninja 13 | ninja test 14 | - no-epoll: | 15 | cd janet 16 | meson setup no-epoll --buildtype=release 17 | cd no-epoll 18 | meson configure -Depoll=false 19 | ninja 20 | ninja test 21 | sudo ninja install 22 | - meson_min: | 23 | cd janet 24 | 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 25 | cd build_meson_min 26 | ninja 27 | -------------------------------------------------------------------------------- /tools/afl/generate_unmarshal_testcases.janet: -------------------------------------------------------------------------------- 1 | 2 | (os/mkdir "./tools/afl/unmarshal_testcases/") 3 | 4 | (defn spit-case [n v] 5 | (spit 6 | (string "./tools/afl/unmarshal_testcases/" (string n)) 7 | (marshal v make-image-dict))) 8 | 9 | (def cases [ 10 | nil 11 | 12 | "abc" 13 | 14 | :def 15 | 16 | 'hij 17 | 18 | 123 19 | 20 | (int/s64 123) 21 | 22 | "7" 23 | 24 | [1 2 3] 25 | 26 | @[1 2 3] 27 | 28 | {:a 123} 29 | 30 | @{:b 'xyz} 31 | 32 | (peg/compile 33 | '{:a (* "a" :b "a") 34 | :b (* "b" (+ :a 0) "b") 35 | :main (* "(" :b ")")}) 36 | 37 | (fn f [a] (fn [] {:ab a})) 38 | 39 | (fn f [a] (print "hello world!")) 40 | 41 | (do 42 | (defn f [a] (yield) @[1 "2"]) 43 | (def fb (fiber/new f)) 44 | (resume fb) 45 | fb) 46 | ]) 47 | 48 | (eachk i cases 49 | (spit-case i (in cases i))) 50 | -------------------------------------------------------------------------------- /examples/sigaction.janet: -------------------------------------------------------------------------------- 1 | ### 2 | ### Usage: janet examples/sigaction.janet 1|2|3|4 & 3 | ### 4 | ### Then at shell: kill -s SIGTERM $! 5 | ### 6 | 7 | (defn action 8 | [] 9 | (print "Handled SIGTERM!") 10 | (flush) 11 | (os/exit 1)) 12 | 13 | (defn main1 14 | [] 15 | (os/sigaction :term action true) 16 | (forever)) 17 | 18 | (defn main2 19 | [] 20 | (os/sigaction :term action) 21 | (forever)) 22 | 23 | (defn main3 24 | [] 25 | (os/sigaction :term action true) 26 | (forever (ev/sleep math/inf))) 27 | 28 | (defn main4 29 | [] 30 | (os/sigaction :term action) 31 | (forever (ev/sleep math/inf))) 32 | 33 | (defn main 34 | [& args] 35 | (def which (scan-number (get args 1 "1"))) 36 | (case which 37 | 1 (main1) # should work 38 | 2 (main2) # will not work 39 | 3 (main3) # should work 40 | 4 (main4) # should work 41 | (error "bad main"))) 42 | -------------------------------------------------------------------------------- /tools/afl/fuzz.sh: -------------------------------------------------------------------------------- 1 | set -eux 2 | 3 | NFUZZ=${NFUZZ:-1} 4 | children="" 5 | 6 | function finish { 7 | for pid in $children 8 | do 9 | set +e 10 | kill -s INT $pid 11 | done 12 | wait 13 | } 14 | trap finish EXIT 15 | 16 | test -e ./tools/afl/$1_testcases 17 | test -e ./tools/afl/$1_runner.janet 18 | 19 | echo "running fuzz master..." 20 | xterm -e \ 21 | "afl-fuzz -i ./tools/afl/$1_testcases -o ./fuzz_out/$1 -M Fuzz$1_0 -- ./build/janet ./tools/afl/$1_runner.janet @@" & 22 | children="$! $children" 23 | echo "waiting for afl to get started before starting secondary fuzzers" 24 | sleep 10 25 | 26 | NFUZZ=$((NFUZZ - 1)) 27 | 28 | for N in $(seq $NFUZZ) 29 | do 30 | xterm -e \ 31 | "afl-fuzz -i ./tools/afl/$1_testcases -o ./fuzz_out/$1 -S Fuzz$1_$N -- ./build/janet ./tools/afl/$1_runner.janet @@" & 32 | children="$! $children" 33 | done 34 | 35 | echo "waiting for child terminals to exit." 36 | wait 37 | -------------------------------------------------------------------------------- /examples/assembly.janet: -------------------------------------------------------------------------------- 1 | # Example of dst bytecode assembly 2 | 3 | # Fibonacci sequence, implemented with naive recursion. 4 | (def fibasm 5 | (asm 6 | '{:arity 1 7 | :bytecode @[(ltim 1 0 0x2) # $1 = $0 < 2 8 | (jmpif 1 :done) # if ($1) goto :done 9 | (lds 1) # $1 = self 10 | (addim 0 0 -0x1) # $0 = $0 - 1 11 | (push 0) # push($0), push argument for next function call 12 | (call 2 1) # $2 = call($1) 13 | (addim 0 0 -0x1) # $0 = $0 - 1 14 | (push 0) # push($0) 15 | (call 0 1) # $0 = call($1) 16 | (add 0 0 2) # $0 = $0 + $2 (integers) 17 | :done 18 | (ret 0) # return $0 19 | ]})) 20 | 21 | # Test it 22 | 23 | (defn testn 24 | [n] 25 | (print "fibasm(" n ") = " (fibasm n))) 26 | 27 | (for i 0 10 (testn i)) 28 | -------------------------------------------------------------------------------- /tools/afl/README.md: -------------------------------------------------------------------------------- 1 | # AFL Fuzzing scripts 2 | 3 | To use these, you need to install afl (of course), and xterm. A tiling window manager helps manage 4 | many concurrent fuzzer instances. 5 | 6 | Note, afl sometimes requires system configuration, if you find AFL quitting prematurely, try manually 7 | launching it and addressing any error messages. 8 | 9 | ## Fuzz the parser 10 | ``` 11 | $ sh ./tools/afl/prepare_to_fuzz.sh 12 | $ export NFUZZ=1 13 | $ sh ./tools/afl/fuzz.sh parser 14 | Ctrl+C when done to close all fuzzer terminals. 15 | $ sh ./tools/afl/aggregate_cases.sh parser 16 | $ ls ./fuzz_out/parser_aggregated/ 17 | ``` 18 | 19 | ## Fuzz the unmarshaller 20 | ``` 21 | $ janet ./tools/afl/generate_unmarshal_testcases.janet 22 | $ sh ./tools/afl/prepare_to_fuzz.sh 23 | $ export NFUZZ=1 24 | $ sh ./tools/afl/fuzz.sh unmarshal 25 | Ctrl+C when done to close all fuzzer terminals. 26 | $ sh ./tools/afl/aggregate_cases.sh unmarshal 27 | $ ls ./fuzz_out/unmarshal_aggregated/ 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/marshal-stress.janet: -------------------------------------------------------------------------------- 1 | (defn init-db [c] 2 | (def res @{:clients @{}}) 3 | (var i 0) 4 | (repeat c 5 | (def n (string "client" i)) 6 | (put-in res [:clients n] @{:name n :projects @{}}) 7 | (++ i) 8 | (repeat c 9 | (def pn (string "project" i)) 10 | (put-in res [:clients n :projects pn] @{:name pn}) 11 | (++ i) 12 | (repeat c 13 | (def tn (string "task" i)) 14 | (put-in res [:clients n :projects pn :tasks tn] @{:name pn}) 15 | (++ i)))) 16 | res) 17 | 18 | (loop [c :range [30 80 1]] 19 | (var s (os/clock)) 20 | (print "Marshal DB with " c " clients, " 21 | (* c c) " projects and " 22 | (* c c c) " tasks. " 23 | "Total " (+ (* c c c) (* c c) c) " tables") 24 | (def buf (marshal (init-db c) @{} @"")) 25 | (print "Buffer is " (length buf) " bytes") 26 | (print "Duration " (- (os/clock) s)) 27 | (set s (os/clock)) 28 | (gccollect) 29 | (print "Collected garbage in " (- (os/clock) s))) 30 | 31 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: "2 7 * * 4" 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ cpp ] 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v3 31 | with: 32 | languages: ${{ matrix.language }} 33 | queries: +security-and-quality 34 | tools: linked 35 | 36 | - name: Autobuild 37 | uses: github/codeql-action/autobuild@v3 38 | 39 | - name: Perform CodeQL Analysis 40 | uses: github/codeql-action/analyze@v3 41 | with: 42 | category: "/language:${{ matrix.language }}" 43 | -------------------------------------------------------------------------------- /examples/evlocks.janet: -------------------------------------------------------------------------------- 1 | (defn sleep 2 | "Sleep the entire thread, not just a single fiber." 3 | [n] 4 | (os/sleep (* 0.1 n))) 5 | 6 | (defn work [lock n] 7 | (ev/acquire-lock lock) 8 | (print "working " n "...") 9 | (sleep n) 10 | (print "done working...") 11 | (ev/release-lock lock)) 12 | 13 | (defn reader 14 | [rwlock n] 15 | (ev/acquire-rlock rwlock) 16 | (print "reading " n "...") 17 | (sleep n) 18 | (print "done reading " n "...") 19 | (ev/release-rlock rwlock)) 20 | 21 | (defn writer 22 | [rwlock n] 23 | (ev/acquire-wlock rwlock) 24 | (print "writing " n "...") 25 | (sleep n) 26 | (print "done writing...") 27 | (ev/release-wlock rwlock)) 28 | 29 | (defn test-lock 30 | [] 31 | (def lock (ev/lock)) 32 | (for i 3 7 33 | (ev/spawn-thread 34 | (work lock i)))) 35 | 36 | (defn test-rwlock 37 | [] 38 | (def rwlock (ev/rwlock)) 39 | (for i 0 20 40 | (if (> 0.1 (math/random)) 41 | (ev/spawn-thread (writer rwlock i)) 42 | (ev/spawn-thread (reader rwlock i))))) 43 | 44 | (test-rwlock) 45 | (test-lock) 46 | -------------------------------------------------------------------------------- /examples/urlloader.janet: -------------------------------------------------------------------------------- 1 | # An example of using Janet's extensible module system to import files from 2 | # URL. To try this, run `janet -l ./examples/urlloader.janet` from the command 3 | # line, and then at the REPL type: 4 | # 5 | # (import https://raw.githubusercontent.com/janet-lang/janet/master/examples/colors.janet :as c) 6 | # 7 | # This will import a file using curl. You can then try: 8 | # 9 | # (print (c/color :green "Hello!")) 10 | # 11 | # This is a bit of a toy example (it just shells out to curl), but it is very 12 | # powerful and will work well in many cases. 13 | 14 | (defn- load-url 15 | [url args] 16 | (def p (os/spawn ["curl" url "-s"] :p {:out :pipe})) 17 | (def res (dofile (p :out) :source url ;args)) 18 | (:wait p) 19 | res) 20 | 21 | (defn- check-http-url 22 | [path] 23 | (if (or (string/has-prefix? "http://" path) 24 | (string/has-prefix? "https://" path)) 25 | path)) 26 | 27 | # Add the module loader and path tuple to right places 28 | (array/push module/paths [check-http-url :janet-http]) 29 | (put module/loaders :janet-http load-url) 30 | -------------------------------------------------------------------------------- /.builds/openbsd.yml: -------------------------------------------------------------------------------- 1 | image: openbsd/7.7 2 | sources: 3 | - https://git.sr.ht/~bakpakin/janet 4 | packages: 5 | - gmake 6 | - meson 7 | tasks: 8 | - gmake: | 9 | cd janet 10 | gmake 11 | gmake test 12 | doas gmake install 13 | doas gmake uninstall 14 | - meson_min: | 15 | cd janet 16 | 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 17 | cd build_meson_min 18 | ninja 19 | - meson_reduced: | 20 | cd janet 21 | meson setup build_meson_reduced --buildtype=release -Dreduced_os=true 22 | cd build_meson_reduced 23 | ninja 24 | - meson_prf: | 25 | cd janet 26 | meson setup build_meson_prf --buildtype=release -Dprf=true 27 | cd build_meson_prf 28 | ninja 29 | ninja test 30 | - meson_default: | 31 | cd janet 32 | meson setup build_meson_default --buildtype=release 33 | cd build_meson_default 34 | ninja 35 | ninja test 36 | doas ninja install 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025 Calvin Rose and contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /examples/colors.janet: -------------------------------------------------------------------------------- 1 | # Ansi terminal colors 2 | 3 | (def- colormap 4 | {:black 30 5 | :bg-black 40 6 | :red 31 7 | :bg-red 41 8 | :green 32 9 | :bg-green 42 10 | :yellow 33 11 | :bg-yellow 43 12 | :blue 34 13 | :bg-blue 44 14 | :magenta 35 15 | :bg-magenta 45 16 | :cyan 36 17 | :bg-cyan 46 18 | :white 37 19 | :bg-white 47 20 | 21 | :bright-black 90 22 | :bg-bright-black 100 23 | :bright-red 91 24 | :bg-bright-red 101 25 | :bright-green 92 26 | :bg-bright-green 102 27 | :bright-yellow 93 28 | :bg-bright-yellow 103 29 | :bright-blue 94 30 | :bg-bright-blue 104 31 | :bright-magenta 95 32 | :bg-bright-magenta 105 33 | :bright-cyan 96 34 | :bg-bright-cyan 106 35 | :bright-white 97 36 | :bg-bright-white 107}) 37 | 38 | (defn color 39 | "Take a string made by concatenating xs and colorize it for an ANSI terminal." 40 | [c & xs] 41 | (def code (get colormap c)) 42 | (if (not code) (error (string "color " c " unknown"))) 43 | (string "\e[" code "m" ;xs "\e[0m")) 44 | 45 | # Print all colors 46 | 47 | (loop [c :keys colormap] (print (color c c))) 48 | -------------------------------------------------------------------------------- /examples/chatserver.janet: -------------------------------------------------------------------------------- 1 | (def conmap @{}) 2 | 3 | (defn broadcast [em msg] 4 | (eachk par conmap 5 | (if (not= par em) 6 | (if-let [tar (get conmap par)] 7 | (net/write tar (string/format "[%s]:%s" em msg)))))) 8 | 9 | (defn handler 10 | [connection] 11 | (print "connection: " connection) 12 | (net/write connection "Whats your name?\n") 13 | (def name (string/trim (string (ev/read connection 100)))) 14 | (print name " connected") 15 | (if (get conmap name) 16 | (do 17 | (net/write connection "Name already taken!") 18 | (:close connection)) 19 | (do 20 | (put conmap name connection) 21 | (net/write connection (string/format "Welcome %s\n" name)) 22 | (defer (do 23 | (put conmap name nil) 24 | (:close connection)) 25 | (while (def msg (ev/read connection 100)) 26 | (broadcast name (string msg))) 27 | (print name " disconnected"))))) 28 | 29 | (defn main [& args] 30 | (printf "STARTING SERVER...") 31 | (flush) 32 | (def my-server (net/listen "127.0.0.1" "8000")) 33 | (forever 34 | (def connection (net/accept my-server)) 35 | (ev/call handler connection))) 36 | -------------------------------------------------------------------------------- /examples/life.janet: -------------------------------------------------------------------------------- 1 | # A game of life implementation 2 | 3 | (def- window 4 | (seq [x :range [-1 2] 5 | y :range [-1 2] 6 | :when (not (and (zero? x) (zero? y)))] 7 | [x y])) 8 | 9 | (defn- neighbors 10 | [[x y]] 11 | (map (fn [[x1 y1]] [(+ x x1) (+ y y1)]) window)) 12 | 13 | (defn tick 14 | "Get the next state in the Game Of Life." 15 | [state] 16 | (def cell-set (frequencies state)) 17 | (def neighbor-set (frequencies (mapcat neighbors state))) 18 | (seq [coord :keys neighbor-set 19 | :let [count (get neighbor-set coord)] 20 | :when (or (= count 3) (and (get cell-set coord) (= count 2)))] 21 | coord)) 22 | 23 | (defn draw 24 | "Draw cells in the game of life from (x1, y1) to (x2, y2)" 25 | [state x1 y1 x2 y2] 26 | (def cellset @{}) 27 | (each cell state (put cellset cell true)) 28 | (loop [x :range [x1 (+ 1 x2)] 29 | :after (print) 30 | y :range [y1 (+ 1 y2)]] 31 | (file/write stdout (if (get cellset [x y]) "X " ". "))) 32 | (print)) 33 | 34 | # 35 | # Run the example 36 | # 37 | 38 | (var *state* '[(0 0) (-1 0) (1 0) (1 1) (0 2)]) 39 | 40 | (for i 0 20 41 | (print "generation " i) 42 | (draw *state* -7 -7 7 7) 43 | (set *state* (tick *state*))) 44 | -------------------------------------------------------------------------------- /assets/icon_svg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /tools/msi/LICENSE.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Arial;}} 2 | {\*\generator Riched20 10.0.18362}\viewkind4\uc1 3 | \pard\sa200\sl276\slmult1\fs16\lang9 Copyright (c) 2023 Calvin Rose and contributors\par 4 | 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:\par 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\par 6 | 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.\par 7 | } 8 | -------------------------------------------------------------------------------- /.github/cosmo/build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eux 3 | 4 | COSMO_DIR="/sc/cosmocc" 5 | 6 | # build x86_64 7 | X86_64_CC="/sc/cosmocc/bin/x86_64-unknown-cosmo-cc" 8 | X86_64_AR="/sc/cosmocc/bin/x86_64-unknown-cosmo-ar" 9 | mkdir -p /sc/cosmocc/x86_64 10 | make -j CC="$X86_64_CC" AR="$X86_64_AR" HAS_SHARED=0 JANET_NO_AMALG=1 11 | cp build/janet /sc/cosmocc/x86_64/janet 12 | make clean 13 | 14 | # build aarch64 15 | AARCH64_CC="/sc/cosmocc/bin/aarch64-unknown-cosmo-cc" 16 | AARCH64_AR="/sc/cosmocc/bin/aarch64-unknown-cosmo-ar" 17 | mkdir -p /sc/cosmocc/aarch64 18 | make -j CC="$AARCH64_CC" AR="$AARCH64_AR" HAS_SHARED=0 JANET_NO_AMALG=1 19 | cp build/janet /sc/cosmocc/aarch64/janet 20 | make clean 21 | 22 | # fat binary 23 | apefat () { 24 | OUTPUT="$1" 25 | OLDNAME_X86_64="$(basename -- "$2")" 26 | OLDNAME_AARCH64="$(basename -- "$3")" 27 | TARG_FOLD="$(dirname "$OUTPUT")" 28 | "$COSMO_DIR/bin/apelink" -l "$COSMO_DIR/bin/ape-x86_64.elf" \ 29 | -l "$COSMO_DIR/bin/ape-aarch64.elf" \ 30 | -M "$COSMO_DIR/bin/ape-m1.c" \ 31 | -o "$OUTPUT" \ 32 | "$2" \ 33 | "$3" 34 | cp "$2" "$TARG_FOLD/$OLDNAME_X86_64.x86_64" 35 | cp "$3" "$TARG_FOLD/$OLDNAME_AARCH64.aarch64" 36 | } 37 | 38 | apefat /sc/cosmocc/janet.com /sc/cosmocc/x86_64/janet /sc/cosmocc/aarch64/janet 39 | -------------------------------------------------------------------------------- /test/fuzzers/fuzz_dostring.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 6 | 7 | /* init Janet */ 8 | janet_init(); 9 | 10 | /* fuzz the parser */ 11 | JanetParser parser; 12 | janet_parser_init(&parser); 13 | for (int i = 0, done = 0; i < size; i++) { 14 | switch (janet_parser_status(&parser)) { 15 | case JANET_PARSE_DEAD: 16 | case JANET_PARSE_ERROR: 17 | done = 1; 18 | break; 19 | case JANET_PARSE_PENDING: 20 | if (i == size) { 21 | janet_parser_eof(&parser); 22 | } else { 23 | janet_parser_consume(&parser, data[i]); 24 | } 25 | break; 26 | case JANET_PARSE_ROOT: 27 | if (i >= size) { 28 | janet_parser_eof(&parser); 29 | } else { 30 | janet_parser_consume(&parser, data[i]); 31 | } 32 | break; 33 | } 34 | 35 | if (done == 1) 36 | break; 37 | } 38 | janet_parser_deinit(&parser); 39 | 40 | /* cleanup Janet */ 41 | janet_deinit(); 42 | 43 | return 0; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /tools/update_copyright.janet: -------------------------------------------------------------------------------- 1 | (def usage (string "usage: janet " (first (dyn :args)) " ")) 2 | 3 | (def ignores [".git"]) 4 | (def exts ["LICENSE" "Makefile" ".build" ".c" ".h" ".janet"]) 5 | 6 | (defn arg [i] 7 | (defn bail [] (print usage) (quit)) 8 | (if-not (= 3 (length (dyn :args))) 9 | (bail) 10 | (if-let [val (get (dyn :args) i)] 11 | val 12 | (bail)))) 13 | 14 | (def oy (arg 1)) 15 | (def ny (arg 2)) 16 | (def od (string "Copyright (c) " oy " Calvin Rose")) 17 | (def nd (string "Copyright (c) " ny " Calvin Rose")) 18 | 19 | (defn join [dir name] 20 | (os/realpath (string dir "/" name))) 21 | 22 | (defn add-children [dir paths] 23 | (loop [name :in (os/dir dir) 24 | :unless (has-value? ignores name)] 25 | (array/push paths (join dir name)))) 26 | 27 | (defn ends-in? [exts s] 28 | (find (fn [ext] (string/has-suffix? ext s)) exts)) 29 | 30 | (defn update-disclaimer [path] 31 | (if-let [_ (ends-in? exts path) 32 | oc (slurp path) 33 | pos (string/find od oc) 34 | nc (string (string/slice oc 0 pos) nd (string/slice oc (+ pos (length od))))] 35 | (spit path nc))) 36 | 37 | (def cwd (os/cwd)) 38 | (def paths (if (string/has-suffix? "janet" cwd) 39 | @[cwd] 40 | @[(join cwd "..")])) 41 | (loop [p :in paths] 42 | (if (= :directory ((os/stat p) :mode)) 43 | (add-children p paths) 44 | (update-disclaimer p))) 45 | -------------------------------------------------------------------------------- /test/suite-cfuns.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Inline 3 argument get 25 | # a1ea62a 26 | (assert (= 10 (do (var a 10) (set a (get '{} :a a)))) "inline get 1") 27 | 28 | # Regression #24 29 | # f28477649 30 | (def t (put @{} :hi 1)) 31 | (assert (deep= t @{:hi 1}) "regression #24") 32 | 33 | (end-suite) 34 | 35 | -------------------------------------------------------------------------------- /test/suite-debug.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Simple function break 25 | # a8afc5b81 26 | (debug/fbreak map 1) 27 | (def f (fiber/new (fn [] (map inc [1 2 3])) :a)) 28 | (resume f) 29 | (assert (= :debug (fiber/status f)) "debug/fbreak") 30 | (debug/unfbreak map 1) 31 | (map inc [1 2 3]) 32 | 33 | (end-suite) 34 | 35 | -------------------------------------------------------------------------------- /test/suite-tuple.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | (assert (= [1 2 3] (tuple/join [1] [2] [3])) "tuple/join 1") 25 | (assert (= [] (tuple/join)) "tuple/join 2") 26 | (assert (= [:a :b :c] (tuple/join @[:a :b] [] [:c])) "tuple/join 3") 27 | (assert (= ["abc123" "def456"] (tuple/join ["abc123" "def456"])) "tuple/join 4") 28 | 29 | (end-suite) 30 | 31 | -------------------------------------------------------------------------------- /src/core/symcache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef JANET_SYMCACHE_H_defined 24 | #define JANET_SYMCACHE_H_defined 25 | 26 | #ifndef JANET_AMALG 27 | #include "features.h" 28 | #include 29 | #endif 30 | 31 | /* Initialize the cache (allocate cache memory) */ 32 | void janet_symcache_init(void); 33 | void janet_symcache_deinit(void); 34 | void janet_symbol_deinit(const uint8_t *sym); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /test/amalg/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | /* A simple client for checking if the amalgamated Janet source compiles 24 | * correctly. */ 25 | 26 | #include 27 | 28 | int main(int argc, const char *argv[]) { 29 | (void) argc; 30 | (void) argv; 31 | janet_init(); 32 | JanetTable *env = janet_core_env(NULL); 33 | janet_dostring(env, "(print `hello, world!`)", "main", NULL); 34 | janet_deinit(); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /test/suite-strtod.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Scan number 25 | # 798c88b4c 26 | (assert (= 1 (scan-number "1")) "scan-number 1") 27 | (assert (= -1 (scan-number "-1")) "scan-number -1") 28 | (assert (= 1.3e4 (scan-number "1.3e4")) "scan-number 1.3e4") 29 | 30 | # Issue #183 - just parse it :) 31 | # 688d297a1 32 | 1e-4000000000000000000000 33 | 34 | # For undefined behavior sanitizer 35 | # c876e63 36 | 0xf&1fffFFFF 37 | 38 | (end-suite) 39 | 40 | -------------------------------------------------------------------------------- /test/suite-symcache.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Symbol function 25 | # 5460ff1 26 | (assert (= (symbol "abc" 1 2 3) 'abc123) "symbol function") 27 | 28 | # Gensym tests 29 | # 3ccd68843 30 | (assert (not= (gensym) (gensym)) "two gensyms not equal") 31 | ((fn [] 32 | (def syms (table)) 33 | (var counter 0) 34 | (while (< counter 128) 35 | (put syms (gensym) true) 36 | (set counter (+ 1 counter))) 37 | (assert (= (length syms) 128) "many symbols"))) 38 | 39 | # issue #753 - a78cbd91d 40 | (assert (pos? (length (gensym))) "gensym not empty, regression #753") 41 | 42 | (end-suite) 43 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('git_hash', type : 'string', value : 'meson') 2 | 3 | option('single_threaded', type : 'boolean', value : false) 4 | option('nanbox', type : 'boolean', value : true) 5 | option('dynamic_modules', type : 'boolean', value : true) 6 | option('docstrings', type : 'boolean', value : true) 7 | option('sourcemaps', type : 'boolean', value : true) 8 | option('reduced_os', type : 'boolean', value : false) 9 | option('assembler', type : 'boolean', value : true) 10 | option('peg', type : 'boolean', value : true) 11 | option('int_types', type : 'boolean', value : true) 12 | option('prf', type : 'boolean', value : false) 13 | option('net', type : 'boolean', value : true) 14 | option('ipv6', type : 'boolean', value : true) 15 | option('ev', type : 'boolean', value : true) 16 | option('processes', type : 'boolean', value : true) 17 | option('umask', type : 'boolean', value : true) 18 | option('realpath', type : 'boolean', value : true) 19 | option('simple_getline', type : 'boolean', value : false) 20 | option('epoll', type : 'boolean', value : true) 21 | option('kqueue', type : 'boolean', value : true) 22 | option('interpreter_interrupt', type : 'boolean', value : true) 23 | option('ffi', type : 'boolean', value : true) 24 | option('ffi_jit', type : 'boolean', value : true) 25 | option('filewatch', type : 'boolean', value : true) 26 | 27 | option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024) 28 | option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200) 29 | option('max_macro_expand', type : 'integer', min : 1, max : 8000, value : 200) 30 | option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7fffffff) 31 | 32 | option('arch_name', type : 'string', value: '') 33 | option('thread_local_prefix', type : 'string', value: '') 34 | option('os_name', type : 'string', value: '') 35 | option('shared', type : 'boolean', value: true) 36 | option('cryptorand', type : 'boolean', value: true) 37 | -------------------------------------------------------------------------------- /test/suite-capi.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Tuple types 25 | # c6edf03ae 26 | (assert (= (tuple/type '(1 2 3)) :parens) "normal tuple") 27 | (assert (= (tuple/type [1 2 3]) :parens) "normal tuple 1") 28 | (assert (= (tuple/type '[1 2 3]) :brackets) "bracketed tuple 2") 29 | (assert (= (tuple/type (-> '(1 2 3) marshal unmarshal)) :parens) 30 | "normal tuple marshalled/unmarshalled") 31 | (assert (= (tuple/type (-> '[1 2 3] marshal unmarshal)) :brackets) 32 | "normal tuple marshalled/unmarshalled") 33 | 34 | # Dynamic bindings 35 | # 7918add47, 513d551d 36 | (setdyn :a 10) 37 | (assert (= 40 (with-dyns [:a 25 :b 15] (+ (dyn :a) (dyn :b)))) "dyn usage 1") 38 | (assert (= 10 (dyn :a)) "dyn usage 2") 39 | (assert (= nil (dyn :b)) "dyn usage 3") 40 | (setdyn :a 100) 41 | (assert (= 100 (dyn :a)) "dyn usage 4") 42 | 43 | (end-suite) 44 | 45 | -------------------------------------------------------------------------------- /mkfile: -------------------------------------------------------------------------------- 1 | $target 39 | 40 | build/janet.$O: build/c/janet.c src/conf/janetconf.h src/include/janet.h 41 | $CC $CFLAGS -D^$JANET_CONFIG -o $target $prereq(1) 42 | 43 | build/shell.$O: src/mainclient/shell.c src/conf/janetconf.h src/include/janet.h 44 | $CC $CFLAGS -D^$JANET_CONFIG -o $target $prereq(1) 45 | 46 | min_needed ? dbl_cur : min_needed; 34 | size_t newsize = ((size_t) itemsize) * m + sizeof(int32_t) * 2; 35 | int32_t *p = (int32_t *) janet_srealloc(v ? janet_v__raw(v) : 0, newsize); 36 | if (!v) p[1] = 0; 37 | p[0] = m; 38 | return p + 2; 39 | } 40 | 41 | /* Convert a buffer to normal allocated memory (forget capacity) */ 42 | void *janet_v_flattenmem(void *v, int32_t itemsize) { 43 | char *p; 44 | if (NULL == v) return NULL; 45 | size_t size = (size_t) itemsize * janet_v__cnt(v); 46 | p = janet_malloc(size); 47 | if (NULL != p) { 48 | safe_memcpy(p, v, size); 49 | return p; 50 | } else { 51 | JANET_OUT_OF_MEMORY; 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /tools/symcharsgen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | static int is_symbol_char_gen(uint8_t c) { 27 | if (c & 0x80) return 1; 28 | if (c >= 'a' && c <= 'z') return 1; 29 | if (c >= 'A' && c <= 'Z') return 1; 30 | if (c >= '0' && c <= '9') return 1; 31 | return (c == '!' || 32 | c == '$' || 33 | c == '%' || 34 | c == '&' || 35 | c == '*' || 36 | c == '+' || 37 | c == '-' || 38 | c == '.' || 39 | c == '/' || 40 | c == ':' || 41 | c == '<' || 42 | c == '?' || 43 | c == '=' || 44 | c == '>' || 45 | c == '@' || 46 | c == '^' || 47 | c == '_'); 48 | } 49 | 50 | int main() { 51 | printf("static const uint32_t symchars[8] = {\n "); 52 | for (int i = 0; i < 256; i += 32) { 53 | uint32_t block = 0; 54 | for (int j = 0; j < 32; j++) { 55 | block |= is_symbol_char_gen(i + j) << j; 56 | } 57 | printf("0x%08x%s", block, (i == (256 - 32)) ? "" : ", "); 58 | } 59 | printf("\n};\n"); 60 | return 0; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /test/suite-ev2.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose & contributors 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Issue #1629 25 | (def thread-channel (ev/thread-chan 100)) 26 | (def super (ev/thread-chan 10)) 27 | (defn worker [] 28 | (while true 29 | (def item (ev/take thread-channel)) 30 | (when (= item :deadline) 31 | (ev/deadline 0.1 nil (fiber/current) true)))) 32 | (ev/thread worker nil :n super) 33 | (ev/give thread-channel :item) 34 | (ev/sleep 0.05) 35 | (ev/give thread-channel :item) 36 | (ev/sleep 0.05) 37 | (ev/give thread-channel :deadline) 38 | (ev/sleep 0.05) 39 | (ev/give thread-channel :item) 40 | (ev/sleep 0.05) 41 | (ev/give thread-channel :item) 42 | (ev/sleep 0.15) 43 | (assert (deep= '(:error "deadline expired" nil) (ev/take super)) "deadline expirataion") 44 | 45 | # Another variant 46 | (def thread-channel (ev/thread-chan 100)) 47 | (def super (ev/thread-chan 10)) 48 | (defn worker [] 49 | (while true 50 | (def item (ev/take thread-channel)) 51 | (when (= item :deadline) 52 | (ev/deadline 0.1)))) 53 | (ev/thread worker nil :n super) 54 | (ev/give thread-channel :deadline) 55 | (ev/sleep 0.2) 56 | (assert (deep= '(:error "deadline expired" nil) (ev/take super)) "deadline expirataion") 57 | 58 | (end-suite) 59 | -------------------------------------------------------------------------------- /src/core/state.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef JANET_AMALG 24 | #include "features.h" 25 | #include 26 | #include "state.h" 27 | #include "util.h" 28 | #endif 29 | 30 | #ifdef JANET_WINDOWS 31 | #include 32 | #endif 33 | 34 | JANET_THREAD_LOCAL JanetVM janet_vm; 35 | 36 | JanetVM *janet_local_vm(void) { 37 | return &janet_vm; 38 | } 39 | 40 | JanetVM *janet_vm_alloc(void) { 41 | JanetVM *mem = janet_malloc(sizeof(JanetVM)); 42 | if (NULL == mem) { 43 | JANET_OUT_OF_MEMORY; 44 | } 45 | return mem; 46 | } 47 | 48 | void janet_vm_free(JanetVM *vm) { 49 | janet_free(vm); 50 | } 51 | 52 | void janet_vm_save(JanetVM *into) { 53 | *into = janet_vm; 54 | } 55 | 56 | void janet_vm_load(JanetVM *from) { 57 | janet_vm = *from; 58 | } 59 | 60 | /* Trigger suspension of the Janet vm by trying to 61 | * exit the interpreter loop when convenient. You can optionally 62 | * use NULL to interrupt the current VM when convenient */ 63 | void janet_interpreter_interrupt(JanetVM *vm) { 64 | vm = vm ? vm : &janet_vm; 65 | janet_atomic_inc(&vm->auto_suspend); 66 | } 67 | 68 | void janet_interpreter_interrupt_handled(JanetVM *vm) { 69 | vm = vm ? vm : &janet_vm; 70 | janet_atomic_dec(&vm->auto_suspend); 71 | } 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Target 2 | dst 3 | !*/**/dst 4 | janet 5 | !*/**/janet 6 | /build 7 | /builddir 8 | /Build 9 | /Release 10 | /Debug 11 | /Emscripten 12 | /src/include/generated/*.h 13 | janet-*.tar.gz 14 | dist 15 | 16 | # jpm lockfile 17 | lockfile.janet 18 | 19 | # Kakoune (fzf via fd) 20 | .fdignore 21 | 22 | # VSCode 23 | .vscode 24 | 25 | # Eclipse 26 | .project 27 | .cproject 28 | 29 | # Gnome Builder 30 | .buildconfig 31 | 32 | # Local directory for testing 33 | local 34 | 35 | # Common test files I use. 36 | temp.janet 37 | temp.c 38 | temp*janet 39 | temp*.c 40 | scratch.janet 41 | scratch.c 42 | 43 | # Emscripten 44 | *.bc 45 | janet.js 46 | janet.wasm 47 | 48 | # Generated files 49 | *.gen.h 50 | *.gen.c 51 | *.tmp 52 | temp.* 53 | 54 | # Generate test files 55 | *.out 56 | .orig 57 | 58 | # Tools 59 | xxd 60 | xxd.exe 61 | 62 | # VSCode 63 | .vs 64 | .clangd 65 | .cache 66 | 67 | # Swap files 68 | *.swp 69 | 70 | # Tags 71 | tags 72 | 73 | # Valgrind files 74 | vgcore.* 75 | *.out.* 76 | 77 | # WiX artifacts 78 | *.msi 79 | *.wixpdb 80 | 81 | # Makefile config 82 | /config.mk 83 | 84 | # Created by https://www.gitignore.io/api/c 85 | 86 | ### C ### 87 | # Prerequisites 88 | *.d 89 | 90 | # Object files 91 | *.o 92 | *.ko 93 | *.obj 94 | *.elf 95 | 96 | # Linker output 97 | *.ilk 98 | *.map 99 | *.exp 100 | 101 | # Precompiled Headers 102 | *.gch 103 | *.pch 104 | 105 | # Libraries 106 | *.lib 107 | *.a 108 | *.la 109 | *.lo 110 | 111 | # Shared objects (inc. Windows DLLs) 112 | *.dll 113 | *.so 114 | *.so.* 115 | *.dylib 116 | 117 | # Executables 118 | *.exe 119 | *.out 120 | *.app 121 | *.i*86 122 | *.x86_64 123 | *.hex 124 | 125 | # Debug files 126 | *.dSYM/ 127 | *.su 128 | *.idb 129 | *.pdb 130 | 131 | # GGov 132 | *.gcov 133 | 134 | # Kernel Module Compile Results 135 | *.mod* 136 | *.cmd 137 | modules.order 138 | Module.symvers 139 | Mkfile.old 140 | dkms.conf 141 | 142 | # Coverage files 143 | *.cov 144 | 145 | # End of https://www.gitignore.io/api/c 146 | 147 | # Created by https://www.gitignore.io/api/cmake 148 | 149 | ### CMake ### 150 | CMakeCache.txt 151 | CMakeFiles 152 | CMakeScripts 153 | Testing 154 | cmake_install.cmake 155 | install_manifest.txt 156 | compile_commands.json 157 | CTestTestfile.cmake 158 | 159 | # End of https://www.gitignore.io/api/cmake 160 | 161 | # Astyle 162 | *.orig 163 | -------------------------------------------------------------------------------- /src/boot/buffer_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include "tests.h" 27 | 28 | int buffer_test() { 29 | 30 | int i; 31 | JanetBuffer *buffer1, *buffer2; 32 | 33 | buffer1 = janet_buffer(100); 34 | buffer2 = janet_buffer(0); 35 | 36 | janet_buffer_push_cstring(buffer1, "hello, world!\n"); 37 | 38 | janet_buffer_push_u8(buffer2, 'h'); 39 | janet_buffer_push_u8(buffer2, 'e'); 40 | janet_buffer_push_u8(buffer2, 'l'); 41 | janet_buffer_push_u8(buffer2, 'l'); 42 | janet_buffer_push_u8(buffer2, 'o'); 43 | janet_buffer_push_u8(buffer2, ','); 44 | janet_buffer_push_u8(buffer2, ' '); 45 | janet_buffer_push_u8(buffer2, 'w'); 46 | janet_buffer_push_u8(buffer2, 'o'); 47 | janet_buffer_push_u8(buffer2, 'r'); 48 | janet_buffer_push_u8(buffer2, 'l'); 49 | janet_buffer_push_u8(buffer2, 'd'); 50 | janet_buffer_push_u8(buffer2, '!'); 51 | janet_buffer_push_u8(buffer2, '\n'); 52 | 53 | assert(buffer1->count == buffer2->count); 54 | assert(buffer1->capacity >= buffer1->count); 55 | assert(buffer2->capacity >= buffer2->count); 56 | 57 | for (i = 0; i < buffer1->count; i++) { 58 | assert(buffer1->data[i] == buffer2->data[i]); 59 | } 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /test/suite-asm.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Assembly test 25 | # Fibonacci sequence, implemented with naive recursion. 26 | # a679f60 27 | (def fibasm (asm '{ 28 | :arity 1 29 | :bytecode [ 30 | (ltim 1 0 0x2) # $1 = $0 < 2 31 | (jmpif 1 :done) # if ($1) goto :done 32 | (lds 1) # $1 = self 33 | (addim 0 0 -0x1) # $0 = $0 - 1 34 | (push 0) # push($0), push argument for next function call 35 | (call 2 1) # $2 = call($1) 36 | (addim 0 0 -0x1) # $0 = $0 - 1 37 | (push 0) # push($0) 38 | (call 0 1) # $0 = call($1) 39 | (add 0 0 2) # $0 = $0 + $2 (integers) 40 | :done 41 | (ret 0) # return $0 42 | ] 43 | })) 44 | 45 | (assert (= 0 (fibasm 0)) "fibasm 1") 46 | (assert (= 1 (fibasm 1)) "fibasm 2") 47 | (assert (= 55 (fibasm 10)) "fibasm 3") 48 | (assert (= 6765 (fibasm 20)) "fibasm 4") 49 | 50 | # dacbe29 51 | (def f (asm (disasm (fn [x] (fn [y] (+ x y)))))) 52 | (assert (= ((f 10) 37) 47) "asm environment tables") 53 | 54 | # issue #1424 55 | (assert-no-error "arity > used slots (issue #1424)" 56 | (asm 57 | (disasm 58 | (fn [] 59 | (def foo (fn [one two] one)) 60 | (foo 100 200))))) 61 | 62 | (end-suite) 63 | 64 | -------------------------------------------------------------------------------- /test/suite-pp.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose & contributors 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Appending buffer to self 25 | # 6b76ac3d1 26 | (with-dyns [:out @""] 27 | (prin "abcd") 28 | (prin (dyn :out)) 29 | (prin (dyn :out)) 30 | (assert (deep= (dyn :out) @"abcdabcdabcdabcd") "print buffer to self")) 31 | 32 | # Buffer self blitting, check for use after free 33 | # bbcfaf128 34 | (def buf1 @"1234567890") 35 | (buffer/blit buf1 buf1 -1) 36 | (buffer/blit buf1 buf1 -1) 37 | (buffer/blit buf1 buf1 -1) 38 | (buffer/blit buf1 buf1 -1) 39 | (assert (= (string buf1) (string/repeat "1234567890" 16)) 40 | "buffer blit against self") 41 | 42 | # Check for bugs with printing self with buffer/format 43 | # bbcfaf128 44 | (def buftemp @"abcd") 45 | (assert (= (string (buffer/format buftemp "---%p---" buftemp)) 46 | `abcd---@"abcd"---`) "buffer/format on self 1") 47 | (def buftemp @"abcd") 48 | (assert (= (string (buffer/format buftemp "---%p %p---" buftemp buftemp)) 49 | `abcd---@"abcd" @"abcd"---`) "buffer/format on self 2") 50 | 51 | # 5c364e0 52 | (defn check-jdn [x] 53 | (assert (deep= (parse (string/format "%j" x)) x) "round trip jdn")) 54 | 55 | (check-jdn 0) 56 | (check-jdn nil) 57 | (check-jdn []) 58 | (check-jdn @[[] [] 1231 9.123123 -123123 0.1231231230001]) 59 | (check-jdn -0.123123123123) 60 | (check-jdn 12837192371923) 61 | (check-jdn "a string") 62 | (check-jdn @"a buffer") 63 | 64 | (end-suite) 65 | 66 | -------------------------------------------------------------------------------- /src/core/emit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef JANET_EMIT_H 24 | #define JANET_EMIT_H 25 | 26 | #ifndef JANET_AMALG 27 | #include "compile.h" 28 | #endif 29 | 30 | void janetc_emit(JanetCompiler *c, uint32_t instr); 31 | 32 | int32_t janetc_allocfar(JanetCompiler *c); 33 | int32_t janetc_allocnear(JanetCompiler *c, JanetcRegisterTemp); 34 | 35 | int32_t janetc_emit_s(JanetCompiler *c, uint8_t op, JanetSlot s, int wr); 36 | int32_t janetc_emit_sl(JanetCompiler *c, uint8_t op, JanetSlot s, int32_t label); 37 | int32_t janetc_emit_st(JanetCompiler *c, uint8_t op, JanetSlot s, int32_t tflags); 38 | int32_t janetc_emit_si(JanetCompiler *c, uint8_t op, JanetSlot s, int16_t immediate, int wr); 39 | int32_t janetc_emit_su(JanetCompiler *c, uint8_t op, JanetSlot s, uint16_t immediate, int wr); 40 | int32_t janetc_emit_ss(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, int wr); 41 | int32_t janetc_emit_ssi(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, int8_t immediate, int wr); 42 | int32_t janetc_emit_ssu(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, uint8_t immediate, int wr); 43 | int32_t janetc_emit_sss(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, JanetSlot s3, int wr); 44 | 45 | /* Check if two slots are equivalent */ 46 | int janetc_sequal(JanetSlot x, JanetSlot y); 47 | 48 | /* Move value from one slot to another. Cannot copy to constant slots. */ 49 | void janetc_copy(JanetCompiler *c, JanetSlot dest, JanetSlot src); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /test/suite-math.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # First commit removing the integer number type 25 | # 6b95326d7 26 | (assert (= 400 (math/sqrt 160000)) "sqrt(160000)=400") 27 | 28 | # RNGs 29 | # aee168721 30 | (defn test-rng 31 | [rng] 32 | (assert (all identity (seq [i :range [0 1000]] 33 | (<= (math/rng-int rng i) i))) "math/rng-int test") 34 | (assert (all identity (seq [i :range [0 1000]] 35 | (def x (math/rng-uniform rng)) 36 | (and (>= x 0) (< x 1)))) 37 | "math/rng-uniform test")) 38 | 39 | (def seedrng (math/rng 123)) 40 | (for i 0 75 41 | (test-rng (math/rng (:int seedrng)))) 42 | 43 | # 70328437f 44 | (assert (deep-not= (-> 123 math/rng (:buffer 16)) 45 | (-> 456 math/rng (:buffer 16))) "math/rng-buffer 1") 46 | 47 | (assert-no-error "math/rng-buffer 2" (math/seedrandom "abcdefg")) 48 | 49 | # 027b2a8 50 | (defn assert-many [f n e] 51 | (var good true) 52 | (loop [i :range [0 n]] 53 | (if (not (f)) 54 | (set good false))) 55 | (assert good e)) 56 | 57 | (assert-many (fn [] (>= 1 (math/random) 0)) 200 "(random) between 0 and 1") 58 | 59 | # 06aa0a124 60 | (assert (= (math/gcd 462 1071) 21) "math/gcd 1") 61 | (assert (= (math/lcm 462 1071) 23562) "math/lcm 1") 62 | 63 | # math gamma 64 | # e6babd8 65 | (assert (< 11899423.08 (math/gamma 11.5) 11899423.085) "math/gamma") 66 | (assert (< 2605.1158 (math/log-gamma 500) 2605.1159) "math/log-gamma") 67 | 68 | (end-suite) 69 | 70 | -------------------------------------------------------------------------------- /src/boot/array_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include "tests.h" 27 | 28 | int array_test() { 29 | 30 | int i; 31 | JanetArray *array1, *array2; 32 | 33 | array1 = janet_array(10); 34 | array2 = janet_array(0); 35 | 36 | janet_array_push(array1, janet_cstringv("one")); 37 | janet_array_push(array1, janet_cstringv("two")); 38 | janet_array_push(array1, janet_cstringv("three")); 39 | janet_array_push(array1, janet_cstringv("four")); 40 | janet_array_push(array1, janet_cstringv("five")); 41 | janet_array_push(array1, janet_cstringv("six")); 42 | janet_array_push(array1, janet_cstringv("seven")); 43 | 44 | assert(array1->count == 7); 45 | assert(array1->capacity >= 7); 46 | assert(janet_equals(array1->data[0], janet_cstringv("one"))); 47 | 48 | janet_array_push(array2, janet_cstringv("one")); 49 | janet_array_push(array2, janet_cstringv("two")); 50 | janet_array_push(array2, janet_cstringv("three")); 51 | janet_array_push(array2, janet_cstringv("four")); 52 | janet_array_push(array2, janet_cstringv("five")); 53 | janet_array_push(array2, janet_cstringv("six")); 54 | janet_array_push(array2, janet_cstringv("seven")); 55 | 56 | for (i = 0; i < array2->count; i++) { 57 | assert(janet_equals(array1->data[i], array2->data[i])); 58 | } 59 | 60 | janet_array_pop(array1); 61 | janet_array_pop(array1); 62 | 63 | assert(array1->count == 5); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /test/suite-ffi.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose & contributors 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | (def has-ffi (dyn 'ffi/native)) 25 | (def has-full-ffi 26 | (and has-ffi 27 | (when-let [entry (dyn 'ffi/calling-conventions)] 28 | (def fficc (entry :value)) 29 | (> (length (fficc)) 1)))) # all arches support :none 30 | 31 | # FFI check 32 | # d80356158 33 | (compwhen has-ffi 34 | (ffi/context)) 35 | 36 | (compwhen has-ffi 37 | (ffi/defbind memcpy :ptr [dest :ptr src :ptr n :size])) 38 | (compwhen has-full-ffi 39 | (def buffer1 @"aaaa") 40 | (def buffer2 @"bbbb") 41 | (memcpy buffer1 buffer2 4) 42 | (assert (= (string buffer1) "bbbb") "ffi 1 - memcpy")) 43 | 44 | # cfaae47ce 45 | (compwhen has-ffi 46 | (assert (= 8 (ffi/size [:int :char])) "size unpacked struct 1") 47 | (assert (= 5 (ffi/size [:pack :int :char])) "size packed struct 1") 48 | (assert (= 5 (ffi/size [:int :pack-all :char])) "size packed struct 2") 49 | (assert (= 4 (ffi/align [:int :char])) "align 1") 50 | (assert (= 1 (ffi/align [:pack :int :char])) "align 2") 51 | (assert (= 1 (ffi/align [:int :char :pack-all])) "align 3") 52 | (assert (= 26 (ffi/size [:char :pack :int @[:char 21]])) 53 | "array struct size")) 54 | 55 | (compwhen has-ffi 56 | (assert-error "bad struct issue #1512" (ffi/struct :void))) 57 | 58 | (compwhen has-ffi 59 | (def buf @"") 60 | (ffi/write :u8 10 buf) 61 | (assert (= 1 (length buf))) 62 | (ffi/write :u8 10 buf) 63 | (assert (= 2 (length buf)))) 64 | 65 | (end-suite) 66 | -------------------------------------------------------------------------------- /src/boot/number_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "tests.h" 29 | 30 | /* Check a subset of numbers against system implementation. 31 | * Note that this depends on the system implementation being correct, 32 | * which may not be the case for old or non compliant systems. Also, 33 | * we cannot check against bases other 10. */ 34 | 35 | /* Compare valid c numbers to system implementation. */ 36 | static void test_valid_str(const char *str) { 37 | int err; 38 | double cnum, jnum; 39 | jnum = 0.0; 40 | cnum = atof(str); 41 | err = janet_scan_number((const uint8_t *) str, (int32_t) strlen(str), &jnum); 42 | assert(!err); 43 | assert(cnum == jnum); 44 | } 45 | 46 | int number_test() { 47 | #ifdef JANET_PLAN9 48 | return 0; 49 | #endif 50 | test_valid_str("1.0"); 51 | test_valid_str("1"); 52 | test_valid_str("2.1"); 53 | test_valid_str("1e10"); 54 | test_valid_str("2e10"); 55 | test_valid_str("1e-10"); 56 | test_valid_str("2e-10"); 57 | test_valid_str("1.123123e10"); 58 | test_valid_str("1.123123e-10"); 59 | test_valid_str("-1.23e2"); 60 | test_valid_str("-4.5e15"); 61 | test_valid_str("-4.5e151"); 62 | test_valid_str("-4.5e200"); 63 | test_valid_str("-4.5e123"); 64 | test_valid_str("123123123123123123132123"); 65 | test_valid_str("0000000011111111111111111111111111"); 66 | test_valid_str(".112312333333323123123123123123123"); 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /test/suite-compile.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Regression Test 25 | # 0378ba78 26 | (assert (= 1 (((compile '(fn [] 1) @{})))) "regression test") 27 | 28 | # Fix a compiler bug in the do special form 29 | # 3e1e2585 30 | (defn myfun [x] 31 | (var a 10) 32 | (set a (do 33 | (def y x) 34 | (if x 8 9)))) 35 | 36 | (assert (= (myfun true) 8) "check do form regression") 37 | (assert (= (myfun false) 9) "check do form regression") 38 | 39 | # Check x:digits: works as symbol and not a hex number 40 | # 5baf70f4 41 | (def x1 100) 42 | (assert (= x1 100) "x1 as symbol") 43 | (def X1 100) 44 | (assert (= X1 100) "X1 as symbol") 45 | 46 | # Edge case should cause old compilers to fail due to 47 | # if statement optimization 48 | # 17283241 49 | (var var-a 1) 50 | (var var-b (if false 2 (string "hello"))) 51 | 52 | (assert (= var-b "hello") "regression 1") 53 | 54 | # d28925fda 55 | (assert (= (string '()) (string [])) "empty bracket tuple literal") 56 | 57 | # Bracket tuple issue 58 | # 340a6c4 59 | (let [do 3] 60 | (assert (= [3 1 2 3] [do 1 2 3]) "bracket tuples are never special forms")) 61 | (assert (= ~(,defn 1 2 3) [defn 1 2 3]) "bracket tuples are never macros") 62 | (assert (= ~(,+ 1 2 3) [+ 1 2 3]) "bracket tuples are never function calls") 63 | 64 | # Crash issue #1174 - bad debug info 65 | # e97299f 66 | (defn crash [] 67 | (debug/stack (fiber/current))) 68 | (do 69 | (math/random) 70 | (defn foo [_] 71 | (crash) 72 | 1) 73 | (foo 0) 74 | 10) 75 | 76 | (end-suite) 77 | 78 | -------------------------------------------------------------------------------- /src/core/regalloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | /* Implements a simple first fit register allocator for the compiler. */ 24 | 25 | #ifndef JANET_REGALLOC_H 26 | #define JANET_REGALLOC_H 27 | 28 | #include 29 | 30 | /* Placeholder for allocating temporary registers */ 31 | typedef enum { 32 | JANETC_REGTEMP_0, 33 | JANETC_REGTEMP_1, 34 | JANETC_REGTEMP_2, 35 | JANETC_REGTEMP_3, 36 | JANETC_REGTEMP_4, 37 | JANETC_REGTEMP_5, 38 | JANETC_REGTEMP_6, 39 | JANETC_REGTEMP_7 40 | } JanetcRegisterTemp; 41 | 42 | typedef struct { 43 | uint32_t *chunks; 44 | int32_t count; /* number of chunks in chunks */ 45 | int32_t capacity; /* amount allocated for chunks */ 46 | int32_t max; /* The maximum allocated register so far */ 47 | int32_t regtemps; /* Hold which temp. registers are allocated. */ 48 | } JanetcRegisterAllocator; 49 | 50 | void janetc_regalloc_init(JanetcRegisterAllocator *ra); 51 | void janetc_regalloc_deinit(JanetcRegisterAllocator *ra); 52 | 53 | int32_t janetc_regalloc_1(JanetcRegisterAllocator *ra); 54 | void janetc_regalloc_free(JanetcRegisterAllocator *ra, int32_t reg); 55 | int32_t janetc_regalloc_temp(JanetcRegisterAllocator *ra, JanetcRegisterTemp nth); 56 | void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRegisterTemp nth); 57 | void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src); 58 | void janetc_regalloc_touch(JanetcRegisterAllocator *ra, int32_t reg); 59 | int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/conf/janetconf.h: -------------------------------------------------------------------------------- 1 | /* This will be generated by the build system if this file is not used */ 2 | 3 | #ifndef JANETCONF_H 4 | #define JANETCONF_H 5 | 6 | #define JANET_VERSION_MAJOR 1 7 | #define JANET_VERSION_MINOR 40 8 | #define JANET_VERSION_PATCH 1 9 | #define JANET_VERSION_EXTRA "" 10 | #define JANET_VERSION "1.40.1" 11 | 12 | /* #define JANET_BUILD "local" */ 13 | 14 | /* These settings all affect linking, so use cautiously. */ 15 | /* #define JANET_SINGLE_THREADED */ 16 | /* #define JANET_THREAD_LOCAL _Thread_local */ 17 | /* #define JANET_NO_DYNAMIC_MODULES */ 18 | /* #define JANET_NO_NANBOX */ 19 | /* #define JANET_API __attribute__((visibility ("default"))) */ 20 | 21 | /* These settings should be specified before amalgamation is 22 | * built. Any build with these set should be considered non-standard, and 23 | * certain Janet libraries should be expected not to work. */ 24 | /* #define JANET_NO_DOCSTRINGS */ 25 | /* #define JANET_NO_SOURCEMAPS */ 26 | /* #define JANET_REDUCED_OS */ 27 | /* #define JANET_NO_PROCESSES */ 28 | /* #define JANET_NO_ASSEMBLER */ 29 | /* #define JANET_NO_PEG */ 30 | /* #define JANET_NO_NET */ 31 | /* #define JANET_NO_INT_TYPES */ 32 | /* #define JANET_NO_EV */ 33 | /* #define JANET_NO_FILEWATCH */ 34 | /* #define JANET_NO_REALPATH */ 35 | /* #define JANET_NO_SYMLINKS */ 36 | /* #define JANET_NO_UMASK */ 37 | /* #define JANET_NO_THREADS */ 38 | /* #define JANET_NO_FFI */ 39 | /* #define JANET_NO_FFI_JIT */ 40 | 41 | /* Other settings */ 42 | /* #define JANET_DEBUG */ 43 | /* #define JANET_PRF */ 44 | /* #define JANET_NO_UTC_MKTIME */ 45 | /* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */ 46 | /* #define JANET_EXIT(msg) do { printf("C assert failed executing janet: %s\n", msg); exit(1); } while (0) */ 47 | /* #define JANET_TOP_LEVEL_SIGNAL(msg) call_my_function((msg), stderr) */ 48 | /* #define JANET_RECURSION_GUARD 1024 */ 49 | /* #define JANET_MAX_PROTO_DEPTH 200 */ 50 | /* #define JANET_MAX_MACRO_EXPAND 200 */ 51 | /* #define JANET_STACK_MAX 16384 */ 52 | /* #define JANET_OS_NAME my-custom-os */ 53 | /* #define JANET_ARCH_NAME pdp-8 */ 54 | /* #define JANET_EV_NO_EPOLL */ 55 | /* #define JANET_EV_NO_KQUEUE */ 56 | /* #define JANET_NO_INTERPRETER_INTERRUPT */ 57 | /* #define JANET_NO_IPV6 */ 58 | /* #define JANET_NO_CRYPTORAND */ 59 | /* #define JANET_USE_STDATOMIC */ 60 | 61 | /* Custom vm allocator support */ 62 | /* #include */ 63 | /* #define janet_malloc(X) mi_malloc((X)) */ 64 | /* #define janet_realloc(X, Y) mi_realloc((X), (Y)) */ 65 | /* #define janet_calloc(X, Y) mi_calloc((X), (Y)) */ 66 | /* #define janet_free(X) mi_free((X)) */ 67 | 68 | /* Main client settings, does not affect library code */ 69 | /* #define JANET_SIMPLE_GETLINE */ 70 | 71 | #endif /* end of include guard: JANETCONF_H */ 72 | -------------------------------------------------------------------------------- /test/suite-table.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Denormal tables 25 | # 38a7e4faf 26 | (assert (= (length @{1 2 nil 3}) 1) "nil key table literal") 27 | (assert (= (length (table 1 2 nil 3)) 1) "nil key table ctor") 28 | 29 | (assert (= (length (table (/ 0 0) 2 1 3)) 1) "nan key table ctor") 30 | (assert (= (length @{1 2 (/ 0 0) 3}) 1) "nan key table literal") 31 | 32 | (assert (= (length (table 2 1 3 nil)) 1) "nil value table ctor") 33 | (assert (= (length @{1 2 3 nil}) 1) "nil value table literal") 34 | 35 | # Table duplicate elements 36 | (assert (deep= @{:a 3 :b 2} @{:a 1 :b 2 :a 3}) "table literal duplicate keys") 37 | (assert (deep= @{:a 3 :b 2} (table :a 1 :b 2 :a 3)) 38 | "table constructor duplicate keys") 39 | 40 | ## Table prototypes 41 | # 027b2a81c 42 | (def roottab @{ 43 | :parentprop 123 44 | }) 45 | 46 | (def childtab @{ 47 | :childprop 456 48 | }) 49 | 50 | (table/setproto childtab roottab) 51 | 52 | (assert (= 123 (get roottab :parentprop)) "table get 1") 53 | (assert (= 123 (get childtab :parentprop)) "table get proto") 54 | (assert (= nil (get roottab :childprop)) "table get 2") 55 | (assert (= 456 (get childtab :childprop)) "proto no effect") 56 | 57 | # b3aed1356 58 | (assert-error 59 | "table rawget regression" 60 | (table/new -1)) 61 | 62 | # table/clone 63 | # 392813667 64 | (defn check-table-clone [x msg] 65 | (assert (= (table/to-struct x) (table/to-struct (table/clone x))) msg)) 66 | 67 | (check-table-clone @{:a 123 :b 34 :c :hello : 945 0 1 2 3 4 5} 68 | "table/clone 1") 69 | (check-table-clone @{} "table/clone 2") 70 | 71 | (end-suite) 72 | 73 | -------------------------------------------------------------------------------- /src/core/vector.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef JANET_VECTOR_H_defined 24 | #define JANET_VECTOR_H_defined 25 | 26 | #ifndef JANET_AMALG 27 | #include "features.h" 28 | #include 29 | #endif 30 | 31 | /* 32 | * vector code modified from 33 | * https://github.com/nothings/stb/blob/master/stretchy_buffer.h 34 | */ 35 | 36 | /* This is mainly used code such as the assembler or compiler, which 37 | * need vector like data structures that are only garbage collected in case 38 | * of an error, and normally rely on malloc/free. */ 39 | 40 | #define janet_v_free(v) (((v) != NULL) ? (janet_sfree(janet_v__raw(v)), 0) : 0) 41 | #define janet_v_push(v, x) (janet_v__maybegrow(v, 1), (v)[janet_v__cnt(v)++] = (x)) 42 | #define janet_v_pop(v) (janet_v_count(v) ? janet_v__cnt(v)-- : 0) 43 | #define janet_v_count(v) (((v) != NULL) ? janet_v__cnt(v) : 0) 44 | #define janet_v_last(v) ((v)[janet_v__cnt(v) - 1]) 45 | #define janet_v_empty(v) (((v) != NULL) ? (janet_v__cnt(v) = 0) : 0) 46 | #define janet_v_flatten(v) (janet_v_flattenmem((v), sizeof(*(v)))) 47 | 48 | #define janet_v__raw(v) ((int32_t *)(v) - 2) 49 | #define janet_v__cap(v) janet_v__raw(v)[0] 50 | #define janet_v__cnt(v) janet_v__raw(v)[1] 51 | 52 | #define janet_v__needgrow(v, n) ((v) == NULL || janet_v__cnt(v) + (n) >= janet_v__cap(v)) 53 | #define janet_v__maybegrow(v, n) (janet_v__needgrow((v), (n)) ? janet_v__grow((v), (n)) : 0) 54 | #define janet_v__grow(v, n) ((v) = janet_v_grow((v), (n), sizeof(*(v)))) 55 | 56 | /* Actual functions defined in vector.c */ 57 | void *janet_v_grow(void *v, int32_t increment, int32_t itemsize); 58 | void *janet_v_flattenmem(void *v, int32_t itemsize); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/core/gc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef JANET_GC_H 24 | #define JANET_GC_H 25 | 26 | #ifndef JANET_AMALG 27 | #include "features.h" 28 | #include 29 | #endif 30 | 31 | /* The metadata header associated with an allocated block of memory */ 32 | #define janet_gc_header(mem) ((JanetGCObject *)(mem)) 33 | 34 | #define JANET_MEM_TYPEBITS 0xFF 35 | #define JANET_MEM_REACHABLE 0x100 36 | #define JANET_MEM_DISABLED 0x200 37 | 38 | #define janet_gc_settype(m, t) ((janet_gc_header(m)->flags |= (0xFF & (t)))) 39 | #define janet_gc_type(m) (janet_gc_header(m)->flags & 0xFF) 40 | 41 | #define janet_gc_mark(m) (janet_gc_header(m)->flags |= JANET_MEM_REACHABLE) 42 | #define janet_gc_reachable(m) (janet_gc_header(m)->flags & JANET_MEM_REACHABLE) 43 | 44 | /* Memory types for the GC. Different from JanetType to include funcenv and funcdef. */ 45 | enum JanetMemoryType { 46 | JANET_MEMORY_NONE, 47 | JANET_MEMORY_STRING, 48 | JANET_MEMORY_SYMBOL, 49 | JANET_MEMORY_ARRAY, 50 | JANET_MEMORY_TUPLE, 51 | JANET_MEMORY_TABLE, 52 | JANET_MEMORY_STRUCT, 53 | JANET_MEMORY_FIBER, 54 | JANET_MEMORY_BUFFER, 55 | JANET_MEMORY_FUNCTION, 56 | JANET_MEMORY_ABSTRACT, 57 | JANET_MEMORY_FUNCENV, 58 | JANET_MEMORY_FUNCDEF, 59 | JANET_MEMORY_THREADED_ABSTRACT, 60 | JANET_MEMORY_TABLE_WEAKK, 61 | JANET_MEMORY_TABLE_WEAKV, 62 | JANET_MEMORY_TABLE_WEAKKV, 63 | JANET_MEMORY_ARRAY_WEAK 64 | }; 65 | 66 | /* To allocate collectable memory, one must call janet_alloc, initialize the memory, 67 | * and then call when janet_enablegc when it is initialized and reachable by the gc (on the JANET stack) */ 68 | void *janet_gcalloc(enum JanetMemoryType type, size_t size); 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /src/core/features.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | /* Feature test macros */ 24 | 25 | #ifndef JANET_FEATURES_H_defined 26 | #define JANET_FEATURES_H_defined 27 | 28 | #if defined(__NetBSD__) || defined(__APPLE__) || defined(__OpenBSD__) \ 29 | || defined(__bsdi__) || defined(__DragonFly__) || defined(__FreeBSD__) 30 | /* Use BSD source on any BSD systems, include OSX */ 31 | # define _BSD_SOURCE 32 | # define _POSIX_C_SOURCE 200809L 33 | #else 34 | /* Use POSIX feature flags */ 35 | # ifndef _POSIX_C_SOURCE 36 | # define _POSIX_C_SOURCE 200809L 37 | # endif 38 | #endif 39 | 40 | #if defined(__APPLE__) 41 | #define _DARWIN_C_SOURCE 42 | #endif 43 | 44 | /* Needed for sched.h for cpu count */ 45 | #ifdef __linux__ 46 | #define _GNU_SOURCE 47 | #endif 48 | 49 | #if defined(WIN32) || defined(_WIN32) 50 | #define WIN32_LEAN_AND_MEAN 51 | #endif 52 | 53 | /* needed for inet_pton and InitializeSRWLock */ 54 | #ifdef __MINGW32__ 55 | #define _WIN32_WINNT _WIN32_WINNT_VISTA 56 | #endif 57 | 58 | /* Needed for realpath on linux, as well as pthread rwlocks. */ 59 | #ifndef _XOPEN_SOURCE 60 | #define _XOPEN_SOURCE 600 61 | #endif 62 | #if _XOPEN_SOURCE < 600 63 | #undef _XOPEN_SOURCE 64 | #define _XOPEN_SOURCE 600 65 | #endif 66 | 67 | /* Needed for timegm and other extensions when building with -std=c99. 68 | * It also defines realpath, etc, which would normally require 69 | * _XOPEN_SOURCE >= 500. */ 70 | #if !defined(_NETBSD_SOURCE) && defined(__NetBSD__) 71 | #define _NETBSD_SOURCE 72 | #endif 73 | 74 | /* Needed for several things when building with -std=c99. */ 75 | #if !__BSD_VISIBLE && (defined(__DragonFly__) || defined(__FreeBSD__)) 76 | #define __BSD_VISIBLE 1 77 | #endif 78 | 79 | #define _FILE_OFFSET_BITS 64 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /test/suite-io.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose & contributors 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Printing to buffers 25 | # d47804d22 26 | (def out-buf @"") 27 | (def err-buf @"") 28 | (with-dyns [:out out-buf :err err-buf] 29 | (print "Hello") 30 | (prin "hi") 31 | (eprint "Sup") 32 | (eprin "not much.")) 33 | 34 | (assert (= (string out-buf) "Hello\nhi") "print and prin to buffer 1") 35 | (assert (= (string err-buf) "Sup\nnot much.") 36 | "eprint and eprin to buffer 1") 37 | 38 | # Printing to functions 39 | # 4e263b8c3 40 | (def out-buf @"") 41 | (defn prepend [x] 42 | (with-dyns [:out out-buf] 43 | (prin "> " x))) 44 | (with-dyns [:out prepend] 45 | (print "Hello world")) 46 | 47 | (assert (= (string out-buf) "> Hello world\n") 48 | "print to buffer via function") 49 | 50 | # c2f844157, 3c523d66e 51 | (with [f (file/temp)] 52 | (assert (= 0 (file/tell f)) "start of file") 53 | (file/write f "foo\n") 54 | (assert (= 4 (file/tell f)) "after written string") 55 | (file/flush f) 56 | (file/seek f :set 0) 57 | (assert (= 0 (file/tell f)) "start of file again") 58 | (assert (= (string (file/read f :all)) "foo\n") "temp files work")) 59 | 60 | # issue #1055 - 2c927ea76 61 | (let [b @""] 62 | (defn dummy [a b c] 63 | (+ a b c)) 64 | (trace dummy) 65 | (defn errout [arg] 66 | (buffer/push b arg)) 67 | (assert (= 6 (with-dyns [*err* errout] (dummy 1 2 3))) 68 | "trace to custom err function") 69 | (assert (deep= @"trace (dummy 1 2 3)\n" b) "trace buffer correct")) 70 | 71 | 72 | # xprintf 73 | (def b @"") 74 | (defn to-b [a] (buffer/push b a)) 75 | (xprintf to-b "123") 76 | (assert (deep= b @"123\n") "xprintf to buffer") 77 | 78 | 79 | (assert-error "cannot print to 3" (xprintf 3 "123")) 80 | 81 | (end-suite) 82 | 83 | -------------------------------------------------------------------------------- /test/suite-value.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # 3e1e25854 25 | (def test-struct {'def 1 'bork 2 'sam 3 'a 'b 'het @[1 2 3 4 5]}) 26 | (assert (= (get test-struct 'def) 1) "struct get") 27 | (assert (= (get test-struct 'bork) 2) "struct get") 28 | (assert (= (get test-struct 'sam) 3) "struct get") 29 | (assert (= (get test-struct 'a) 'b) "struct get") 30 | (assert (= :array (type (get test-struct 'het))) "struct get") 31 | 32 | # Buffer stuff 33 | # 910cfd7dd 34 | (defn buffer= 35 | [a b] 36 | (= (string a) (string b))) 37 | 38 | (assert (buffer= @"abcd" @"abcd") "buffer equal 1") 39 | (assert (buffer= @"abcd" (buffer "ab" "cd")) "buffer equal 2") 40 | (assert (not= @"" @"") "buffer not equal 1") 41 | (assert (not= @"abcd" @"abcd") "buffer not equal 2") 42 | 43 | (defn buffer-factory 44 | [] 45 | @"i am a buffer") 46 | 47 | (assert (not= (buffer-factory) (buffer-factory)) "buffer instantiation") 48 | 49 | (assert (= (length @"abcdef") 6) "buffer length") 50 | 51 | # Tuple comparison 52 | # da438a93e 53 | (assert (< [1 2 3] [2 2 3]) "tuple comparison 1") 54 | (assert (< [1 2 3] [2 2]) "tuple comparison 2") 55 | (assert (< [1 2 3] [2 2 3 4]) "tuple comparison 3") 56 | (assert (< [1 2 3] [1 2 3 4]) "tuple comparison 4") 57 | (assert (< [1 2 3] [1 2 3 -1]) "tuple comparison 5") 58 | (assert (> [1 2 3] [1 2]) "tuple comparison 6") 59 | 60 | # More numerical tests 61 | # e05022f 62 | (assert (= 1 1.0) "numerical equal 1") 63 | (assert (= 0 0.0) "numerical equal 2") 64 | (assert (= 0 -0.0) "numerical equal 3") 65 | (assert (= 2_147_483_647 2_147_483_647.0) "numerical equal 4") 66 | (assert (= -2_147_483_648 -2_147_483_648.0) "numerical equal 5") 67 | 68 | # issue #928 - d7ea122cf 69 | (assert (= (hash 0) (hash (* -1 0))) "hash -0 same as hash 0") 70 | 71 | (end-suite) 72 | 73 | -------------------------------------------------------------------------------- /examples/lazyseqs.janet: -------------------------------------------------------------------------------- 1 | # An example implementation of functional, lazy 2 | # sequences, as in clojure. The lazy seq is essentially 3 | # A lazy linked list, where the next value is a function 4 | # that must be called (realizing it), and the memoized. 5 | # Use with (import "./path/to/this/file" :prefix "seq.") 6 | 7 | (defmacro delay 8 | "Lazily evaluate a series of expressions. Returns a function that 9 | returns the result of the last expression. Will only evaluate the 10 | body once, and then memoizes the result." 11 | [& forms] 12 | (def state (gensym)) 13 | (def loaded (gensym)) 14 | ~(do 15 | (var ,state nil) 16 | (var ,loaded nil) 17 | (fn [] 18 | (if ,loaded 19 | ,state 20 | (do 21 | (set ,loaded true) 22 | (set ,state (do ,;forms))))))) 23 | 24 | # Use tuples instead of structs to save memory 25 | (def- HEAD 0) 26 | (def- TAIL 1) 27 | 28 | (defn empty-seq 29 | "The empty sequence." 30 | [] nil) 31 | 32 | (defmacro cons 33 | "Create a new sequence by prepending a value to the original sequence." 34 | [h t] 35 | (def x (tuple h t)) 36 | (fn [] x)) 37 | 38 | (defn empty? 39 | "Check if a sequence is empty." 40 | [s] 41 | (not (s))) 42 | 43 | (defn head 44 | "Get the next value of the sequence." 45 | [s] 46 | (get (s) HEAD)) 47 | 48 | (defn tail 49 | "Get the rest of a sequence" 50 | [s] 51 | (get (s) TAIL)) 52 | 53 | (defn lazy-range 54 | "Return a sequence of integers [start, end)." 55 | [start end &] 56 | (if end 57 | (if (< start end) 58 | (delay (tuple start (lazy-range (+ 1 start) end))) 59 | empty-seq) 60 | (lazy-range 0 start))) 61 | 62 | (defn lazy-map 63 | "Return a sequence that is the result of applying f to each value in s." 64 | [f s] 65 | (delay 66 | (def x (s)) 67 | (if x (tuple (f (get x HEAD)) (map f (get x TAIL)))))) 68 | 69 | (defn realize 70 | "Force evaluation of a lazy sequence." 71 | [s] 72 | (when (s) (realize (tail s)))) 73 | 74 | (defn realize-map 75 | "Evaluate f on each member of the sequence. Forces evaluation." 76 | [f s] 77 | (when (s) (f (head s)) (realize-map f (tail s)))) 78 | 79 | (defn drop 80 | "Ignores the first n values of the sequence and returns the rest." 81 | [n s] 82 | (delay 83 | (def x (s)) 84 | (if (and x (pos? n)) ((drop (- n 1) (get x TAIL)))))) 85 | 86 | (defn take 87 | "Returns at most the first n values of s." 88 | [n s] 89 | (delay 90 | (def x (s)) 91 | (if (and x (pos? n)) 92 | (tuple (get x HEAD) (take (- n 1) (get x TAIL)))))) 93 | 94 | (defn randseq 95 | "Return a sequence of random numbers." 96 | [] 97 | (delay (tuple (math/random) (randseq)))) 98 | 99 | (defn take-while 100 | "Returns a sequence of values until the predicate is false." 101 | [pred s] 102 | (delay 103 | (def x (s)) 104 | (when x 105 | (def thehead (get HEAD x)) 106 | (if thehead (tuple thehead (take-while pred (get TAIL x))))))) 107 | -------------------------------------------------------------------------------- /test/helper.janet: -------------------------------------------------------------------------------- 1 | # Helper code for running tests 2 | 3 | (var num-tests-passed 0) 4 | (var num-tests-run 0) 5 | (var suite-name 0) 6 | (var start-time 0) 7 | (var skip-count 0) 8 | (var skip-n 0) 9 | 10 | (def is-verbose (os/getenv "VERBOSE")) 11 | 12 | (defn- assert-no-tail 13 | "Override's the default assert with some nice error handling." 14 | [x &opt e] 15 | (++ num-tests-run) 16 | (when (pos? skip-n) 17 | (-- skip-n) 18 | (++ skip-count) 19 | (break x)) 20 | (default e "assert error") 21 | (when x (++ num-tests-passed)) 22 | (def str (string e)) 23 | (def stack (debug/stack (fiber/current))) 24 | (def frame (last stack)) 25 | (def line-info (string/format "%s:%d" 26 | (frame :source) (frame :source-line))) 27 | (if x 28 | (when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x)) 29 | (do 30 | (eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush))) 31 | x) 32 | 33 | (defn skip-asserts 34 | "Skip some asserts" 35 | [n] 36 | (+= skip-n n) 37 | nil) 38 | 39 | (defmacro assert 40 | [x &opt e] 41 | (def xx (gensym)) 42 | (default e (string/format "%j" x)) 43 | ~(do 44 | (def ,xx ,x) 45 | (,assert-no-tail ,xx ,e) 46 | ,xx)) 47 | 48 | (defmacro assert-error 49 | [msg & forms] 50 | (def errsym (keyword (gensym))) 51 | ~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg)) 52 | 53 | (defmacro assert-error-value 54 | [msg errval & forms] 55 | (def e (gensym)) 56 | ~(assert (= ,errval (try (do ,;forms) ([,e] ,e))) ,msg)) 57 | 58 | (defn check-compile-error 59 | [form] 60 | (def result (compile form)) 61 | (assert (table? result) (string/format "expected compilation error for %j, but compiled without error" form))) 62 | 63 | (defmacro assert-no-error 64 | [msg & forms] 65 | (def e (gensym)) 66 | (def f (gensym)) 67 | (if is-verbose 68 | ~(try (do ,;forms (,assert true ,msg)) ([,e ,f] (,assert false ,msg) (,debug/stacktrace ,f ,e "\e[31m✘\e[0m "))) 69 | ~(try (do ,;forms (,assert true ,msg)) ([_] (,assert false ,msg))))) 70 | 71 | (defn start-suite [&opt x] 72 | (default x (dyn :current-file)) 73 | (set suite-name 74 | (cond 75 | (number? x) (string x) 76 | (string x))) 77 | (set start-time (os/clock)) 78 | (eprint "Starting suite " suite-name "...")) 79 | 80 | (defn end-suite [] 81 | (def delta (- (os/clock) start-time)) 82 | (eprinf "Finished suite %s in %.3f seconds - " suite-name delta) 83 | (eprint num-tests-passed " of " num-tests-run " tests passed (" skip-count " skipped).") 84 | (if (not= (+ skip-count num-tests-passed) num-tests-run) (os/exit 1))) 85 | 86 | (defn rmrf 87 | "rm -rf in janet" 88 | [x] 89 | (case (os/lstat x :mode) 90 | nil nil 91 | :directory (do 92 | (each y (os/dir x) 93 | (rmrf (string x "/" y))) 94 | (os/rmdir x)) 95 | (os/rm x)) 96 | nil) 97 | 98 | (defn randdir 99 | "Get a random directory name" 100 | [] 101 | (string "tmp_dir_" (slice (string (math/random) ".tmp") 2))) 102 | -------------------------------------------------------------------------------- /src/boot/table_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include "tests.h" 27 | 28 | int table_test() { 29 | 30 | JanetTable *t1, *t2; 31 | 32 | t1 = janet_table(10); 33 | t2 = janet_table(0); 34 | 35 | janet_table_put(t1, janet_cstringv("hello"), janet_wrap_integer(2)); 36 | janet_table_put(t1, janet_cstringv("akey"), janet_wrap_integer(5)); 37 | janet_table_put(t1, janet_cstringv("box"), janet_wrap_boolean(0)); 38 | janet_table_put(t1, janet_cstringv("square"), janet_cstringv("avalue")); 39 | 40 | assert(t1->count == 4); 41 | assert(t1->capacity >= t1->count); 42 | 43 | assert(janet_equals(janet_table_get(t1, janet_cstringv("hello")), janet_wrap_integer(2))); 44 | assert(janet_equals(janet_table_get(t1, janet_cstringv("akey")), janet_wrap_integer(5))); 45 | assert(janet_equals(janet_table_get(t1, janet_cstringv("box")), janet_wrap_boolean(0))); 46 | assert(janet_equals(janet_table_get(t1, janet_cstringv("square")), janet_cstringv("avalue"))); 47 | 48 | janet_table_remove(t1, janet_cstringv("hello")); 49 | janet_table_put(t1, janet_cstringv("box"), janet_wrap_nil()); 50 | 51 | assert(t1->count == 2); 52 | 53 | assert(janet_equals(janet_table_get(t1, janet_cstringv("hello")), janet_wrap_nil())); 54 | assert(janet_equals(janet_table_get(t1, janet_cstringv("box")), janet_wrap_nil())); 55 | 56 | janet_table_put(t2, janet_csymbolv("t2key1"), janet_wrap_integer(10)); 57 | janet_table_put(t2, janet_csymbolv("t2key2"), janet_wrap_integer(100)); 58 | janet_table_put(t2, janet_csymbolv("some key "), janet_wrap_integer(-2)); 59 | janet_table_put(t2, janet_csymbolv("a thing"), janet_wrap_integer(10)); 60 | 61 | assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key1")), janet_wrap_integer(10))); 62 | assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key2")), janet_wrap_integer(100))); 63 | 64 | assert(t2->count == 4); 65 | assert(janet_equals(janet_table_remove(t2, janet_csymbolv("t2key1")), janet_wrap_integer(10))); 66 | assert(t2->count == 3); 67 | assert(janet_equals(janet_table_remove(t2, janet_csymbolv("t2key2")), janet_wrap_integer(100))); 68 | assert(t2->count == 2); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /tools/gendoc.janet: -------------------------------------------------------------------------------- 1 | # Generate documentation 2 | 3 | (def- prelude 4 | ``` 5 | 6 | 7 | 8 | 9 | Janet Language Documentation 10 | 11 | 23 | 24 | ```) 25 | 26 | (def- postlude 27 | ``` 28 | 29 | ```) 30 | 31 | (def- escapes 32 | {10 "
" 33 | 09 "    " 34 | 38 "&" 35 | 60 "<" 36 | 62 ">" 37 | 34 """ 38 | 39 "'" 39 | 47 "/"}) 40 | 41 | (defn- trim-lead 42 | "Trim leading newlines" 43 | [str] 44 | (var i 0) 45 | (while (= 10 (get str i)) (++ i)) 46 | (string/slice str i)) 47 | 48 | (defn- html-escape 49 | "Escape special characters for HTML encoding." 50 | [str] 51 | (def buf @"") 52 | (loop [byte :in str] 53 | (if-let [rep (get escapes byte)] 54 | (buffer/push-string buf rep) 55 | (buffer/push-byte buf byte))) 56 | buf) 57 | 58 | (def- months '("January" "February" "March" "April" "May" "June" "July" "August" "September" 59 | "October" "November" "December")) 60 | (defn nice-date 61 | "Get the current date nicely formatted" 62 | [] 63 | (let [date (os/date) 64 | M (months (date :month)) 65 | D (+ (date :month-day) 1) 66 | Y (date :year) 67 | HH (date :hours) 68 | MM (date :minutes) 69 | SS (date :seconds)] 70 | (string/format "%s %d, %d at %.2d:%.2d:%.2d" 71 | M D Y HH MM SS))) 72 | 73 | (defn- make-title 74 | "Generate title" 75 | [] 76 | (string "

Janet Core API

" 77 | "

Version " janet/version "-" janet/build "

" 78 | "

Generated " 79 | (nice-date) 80 | "

" 81 | "
")) 82 | 83 | (defn- emit-item 84 | "Generate documentation for one entry." 85 | [key env-entry] 86 | (let [{:macro macro 87 | :value val 88 | :ref ref 89 | :source-map sm 90 | :doc docstring} env-entry 91 | html-key (html-escape key) 92 | binding-type (cond 93 | macro :macro 94 | ref (string :var " (" (type (get ref 0)) ")") 95 | (type val)) 96 | source-ref (if-let [[path start end] sm] 97 | (string "" path " (" start ":" end ")") 98 | "")] 99 | (string "

" html-key "

\n" 100 | "" binding-type "\n" 101 | "

" (trim-lead (html-escape docstring)) "

\n" 102 | source-ref))) 103 | 104 | # Generate parts and print them to stdout 105 | (def parts (seq [[k entry] 106 | :in (sort (pairs (table/getproto (fiber/getenv (fiber/current))))) 107 | :when (symbol? k) 108 | :when (and (get entry :doc) (not (get entry :private)))] 109 | (emit-item k entry))) 110 | (print 111 | prelude 112 | (make-title) 113 | ;(interpose "
\n" parts) 114 | postlude) 115 | -------------------------------------------------------------------------------- /src/boot/system_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "tests.h" 29 | 30 | int system_test() { 31 | 32 | #ifdef JANET_32 33 | assert(sizeof(void *) == 4); 34 | #else 35 | assert(sizeof(void *) == 8); 36 | #endif 37 | 38 | /* Check the version defines are self consistent */ 39 | char version_combined[256]; 40 | sprintf(version_combined, "%d.%d.%d%s", JANET_VERSION_MAJOR, JANET_VERSION_MINOR, JANET_VERSION_PATCH, JANET_VERSION_EXTRA); 41 | assert(!strcmp(JANET_VERSION, version_combined)); 42 | 43 | /* Reflexive testing and nanbox testing */ 44 | assert(janet_equals(janet_wrap_nil(), janet_wrap_nil())); 45 | assert(janet_equals(janet_wrap_false(), janet_wrap_false())); 46 | assert(janet_equals(janet_wrap_true(), janet_wrap_true())); 47 | assert(janet_equals(janet_wrap_integer(1), janet_wrap_integer(1))); 48 | assert(janet_equals(janet_wrap_integer(INT32_MAX), janet_wrap_integer(INT32_MAX))); 49 | assert(janet_equals(janet_wrap_integer(-2), janet_wrap_integer(-2))); 50 | assert(janet_equals(janet_wrap_integer(INT32_MIN), janet_wrap_integer(INT32_MIN))); 51 | assert(janet_equals(janet_wrap_number(1.4), janet_wrap_number(1.4))); 52 | assert(janet_equals(janet_wrap_number(3.14159265), janet_wrap_number(3.14159265))); 53 | #ifdef NAN 54 | #ifdef JANET_PLAN9 55 | // Plan 9 traps NaNs by default; disable that. 56 | setfcr(0); 57 | #endif 58 | assert(janet_checktype(janet_wrap_number(NAN), JANET_NUMBER)); 59 | #else 60 | assert(janet_checktype(janet_wrap_number(0.0 / 0.0), JANET_NUMBER)); 61 | #endif 62 | 63 | assert(NULL != &janet_wrap_nil); 64 | 65 | assert(janet_equals(janet_cstringv("a string."), janet_cstringv("a string."))); 66 | assert(janet_equals(janet_csymbolv("sym"), janet_csymbolv("sym"))); 67 | 68 | Janet *t1 = janet_tuple_begin(3); 69 | t1[0] = janet_wrap_nil(); 70 | t1[1] = janet_wrap_integer(4); 71 | t1[2] = janet_cstringv("hi"); 72 | Janet tuple1 = janet_wrap_tuple(janet_tuple_end(t1)); 73 | 74 | Janet *t2 = janet_tuple_begin(3); 75 | t2[0] = janet_wrap_nil(); 76 | t2[1] = janet_wrap_integer(4); 77 | t2[2] = janet_cstringv("hi"); 78 | Janet tuple2 = janet_wrap_tuple(janet_tuple_end(t2)); 79 | 80 | assert(janet_equals(tuple1, tuple2)); 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /src/core/fiber.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef JANET_FIBER_H_defined 24 | #define JANET_FIBER_H_defined 25 | 26 | #ifndef JANET_AMALG 27 | #include 28 | #endif 29 | 30 | /* Fiber signal masks. */ 31 | #define JANET_FIBER_MASK_ERROR 2 32 | #define JANET_FIBER_MASK_DEBUG 4 33 | #define JANET_FIBER_MASK_YIELD 8 34 | 35 | #define JANET_FIBER_MASK_USER0 (16 << 0) 36 | #define JANET_FIBER_MASK_USER1 (16 << 1) 37 | #define JANET_FIBER_MASK_USER2 (16 << 2) 38 | #define JANET_FIBER_MASK_USER3 (16 << 3) 39 | #define JANET_FIBER_MASK_USER4 (16 << 4) 40 | #define JANET_FIBER_MASK_USER5 (16 << 5) 41 | #define JANET_FIBER_MASK_USER6 (16 << 6) 42 | #define JANET_FIBER_MASK_USER7 (16 << 7) 43 | #define JANET_FIBER_MASK_USER8 (16 << 8) 44 | #define JANET_FIBER_MASK_USER9 (16 << 9) 45 | 46 | #define JANET_FIBER_MASK_USERN(N) (16 << (N)) 47 | #define JANET_FIBER_MASK_USER 0x3FF0 48 | 49 | #define JANET_FIBER_STATUS_MASK 0x3F0000 50 | #define JANET_FIBER_RESUME_SIGNAL 0x400000 51 | #define JANET_FIBER_STATUS_OFFSET 16 52 | 53 | #define JANET_FIBER_BREAKPOINT 0x1000000 54 | #define JANET_FIBER_RESUME_NO_USEVAL 0x2000000 55 | #define JANET_FIBER_RESUME_NO_SKIP 0x4000000 56 | #define JANET_FIBER_DID_LONGJUMP 0x8000000 57 | #define JANET_FIBER_FLAG_MASK 0xF000000 58 | 59 | #define JANET_FIBER_EV_FLAG_CANCELED 0x10000 60 | #define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000 61 | #define JANET_FIBER_FLAG_ROOT 0x40000 62 | #define JANET_FIBER_EV_FLAG_IN_FLIGHT 0x1 63 | 64 | /* used only on windows, should otherwise be unset */ 65 | 66 | #define janet_fiber_set_status(f, s) do {\ 67 | (f)->flags &= ~JANET_FIBER_STATUS_MASK;\ 68 | (f)->flags |= (s) << JANET_FIBER_STATUS_OFFSET;\ 69 | } while (0) 70 | 71 | #define janet_stack_frame(s) ((JanetStackFrame *)((s) - JANET_FRAME_SIZE)) 72 | #define janet_fiber_frame(f) janet_stack_frame((f)->data + (f)->frame) 73 | void janet_fiber_setcapacity(JanetFiber *fiber, int32_t n); 74 | void janet_fiber_push(JanetFiber *fiber, Janet x); 75 | void janet_fiber_push2(JanetFiber *fiber, Janet x, Janet y); 76 | void janet_fiber_push3(JanetFiber *fiber, Janet x, Janet y, Janet z); 77 | void janet_fiber_pushn(JanetFiber *fiber, const Janet *arr, int32_t n); 78 | int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func); 79 | int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func); 80 | void janet_fiber_cframe(JanetFiber *fiber, JanetCFunction cfun); 81 | void janet_fiber_popframe(JanetFiber *fiber); 82 | void janet_env_maybe_detach(JanetFuncEnv *env); 83 | int janet_env_valid(JanetFuncEnv *env); 84 | 85 | #ifdef JANET_EV 86 | void janet_fiber_did_resume(JanetFiber *fiber); 87 | #endif 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /test/suite-array.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # Array tests 25 | # e05022f 26 | (defn array= 27 | "Check if two arrays are equal in an element by element comparison" 28 | [a b] 29 | (if (and (array? a) (array? b)) 30 | (= (apply tuple a) (apply tuple b)))) 31 | (assert (= (apply tuple @[1 2 3 4 5]) (tuple 1 2 3 4 5)) "array to tuple") 32 | (def arr (array)) 33 | (array/push arr :hello) 34 | (array/push arr :world) 35 | (assert (array= arr @[:hello :world]) "array comparison") 36 | (assert (array= @[1 2 3 4 5] @[1 2 3 4 5]) "array comparison 2") 37 | (assert (array= @[:one :two :three :four :five] 38 | @[:one :two :three :four :five]) "array comparison 3") 39 | (assert (array= (array/slice @[1 2 3] 0 2) @[1 2]) "array/slice 1") 40 | (assert (array= (array/slice @[0 7 3 9 1 4] 2 -2) @[3 9 1]) "array/slice 2") 41 | 42 | # Array remove 43 | # 687a3c9 44 | (assert (deep= (array/remove @[1 2 3 4 5] 2) @[1 2 4 5]) "array/remove 1") 45 | (assert (deep= (array/remove @[1 2 3 4 5] 2 2) @[1 2 5]) "array/remove 2") 46 | (assert (deep= (array/remove @[1 2 3 4 5] 2 200) @[1 2]) "array/remove 3") 47 | (assert (deep= (array/remove @[1 2 3 4 5] -2 200) @[1 2 3]) "array/remove 4") 48 | 49 | # array/peek 50 | (assert (nil? (array/peek @[])) "array/peek empty") 51 | 52 | # array/fill 53 | (assert (deep= (array/fill @[1 1] 2) @[2 2]) "array/fill 1") 54 | 55 | # array/concat 56 | (assert (deep= (array/concat @[1 2] @[3 4] 5 6) @[1 2 3 4 5 6]) "array/concat 1") 57 | (def a @[1 2]) 58 | (assert (deep= (array/concat a a) @[1 2 1 2]) "array/concat self") 59 | 60 | # array/insert 61 | (assert (deep= (array/insert @[:a :a :a :a] 2 :b :b) @[:a :a :b :b :a :a]) "array/insert 1") 62 | (assert (deep= (array/insert @[:a :b] -1 :c :d) @[:a :b :c :d]) "array/insert 2") 63 | 64 | # array/remove 65 | (assert-error "removal index 3 out of range [0,2]" (array/remove @[1 2] 3)) 66 | (assert-error "expected non-negative integer for argument n, got -1" (array/remove @[1 2] 1 -1)) 67 | 68 | # array/pop 69 | (assert (= (array/pop @[1]) 1) "array/pop 1") 70 | (assert (= (array/pop @[]) nil) "array/pop empty") 71 | 72 | # Code coverage 73 | (def a @[1]) 74 | (array/pop a) 75 | (array/trim a) 76 | (array/ensure @[1 1] 6 2) 77 | 78 | # array/join 79 | (assert (deep= @[1 2 3] (array/join @[] [1] [2] [3])) "array/join 1") 80 | (assert (deep= @[] (array/join @[])) "array/join 2") 81 | (assert (deep= @[1 :a :b :c] (array/join @[1] @[:a :b] [] [:c])) "array/join 3") 82 | (assert (deep= @[:x :y :z "abc123" "def456"] (array/join @[:x :y :z] ["abc123" "def456"])) "array/join 4") 83 | (assert-error "array/join error 1" (array/join)) 84 | (assert-error "array/join error 2" (array/join [])) 85 | (assert-error "array/join error 3" (array/join [] "abc123")) 86 | (assert-error "array/join error 4" (array/join @[] "abc123")) 87 | (assert-error "array/join error 5" (array/join @[] "abc123")) 88 | 89 | (end-suite) 90 | 91 | -------------------------------------------------------------------------------- /src/boot/boot.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include "tests.h" 25 | 26 | #ifdef JANET_WINDOWS 27 | #include 28 | #define chdir(x) _chdir(x) 29 | #else 30 | #include 31 | #endif 32 | 33 | extern const unsigned char *janet_gen_boot; 34 | extern int32_t janet_gen_boot_size; 35 | 36 | int main(int argc, const char **argv) { 37 | 38 | /* Init janet */ 39 | janet_init(); 40 | 41 | /* Run tests */ 42 | array_test(); 43 | buffer_test(); 44 | number_test(); 45 | system_test(); 46 | table_test(); 47 | 48 | /* C tests passed */ 49 | 50 | /* Set up VM */ 51 | int status; 52 | JanetTable *env; 53 | 54 | env = janet_core_env(NULL); 55 | 56 | /* Create args tuple */ 57 | JanetArray *args = janet_array(argc); 58 | for (int i = 0; i < argc; i++) 59 | janet_array_push(args, janet_cstringv(argv[i])); 60 | janet_def(env, "boot/args", janet_wrap_array(args), "Command line arguments."); 61 | 62 | /* Add in options from janetconf.h so boot.janet can configure the image as needed. */ 63 | JanetTable *opts = janet_table(0); 64 | #ifdef JANET_NO_DOCSTRINGS 65 | janet_table_put(opts, janet_ckeywordv("no-docstrings"), janet_wrap_true()); 66 | #endif 67 | #ifdef JANET_NO_SOURCEMAPS 68 | janet_table_put(opts, janet_ckeywordv("no-sourcemaps"), janet_wrap_true()); 69 | #endif 70 | janet_def(env, "boot/config", janet_wrap_table(opts), "Boot options"); 71 | 72 | /* Run bootstrap script to generate core image */ 73 | const char *boot_filename; 74 | #ifdef JANET_NO_SOURCEMAPS 75 | boot_filename = NULL; 76 | #else 77 | boot_filename = "boot.janet"; 78 | #endif 79 | 80 | int chdir_status = chdir(argv[1]); 81 | if (chdir_status) { 82 | fprintf(stderr, "Could not change to directory %s\n", argv[1]); 83 | exit(1); 84 | } 85 | 86 | FILE *boot_file = fopen("src/boot/boot.janet", "rb"); 87 | if (NULL == boot_file) { 88 | fprintf(stderr, "Could not open src/boot/boot.janet\n"); 89 | exit(1); 90 | } 91 | 92 | /* Slurp file into buffer */ 93 | fseek(boot_file, 0, SEEK_END); 94 | size_t boot_size = ftell(boot_file); 95 | fseek(boot_file, 0, SEEK_SET); 96 | unsigned char *boot_buffer = janet_malloc(boot_size); 97 | if (NULL == boot_buffer) { 98 | fprintf(stderr, "Failed to allocate boot buffer\n"); 99 | exit(1); 100 | } 101 | if (!fread(boot_buffer, 1, boot_size, boot_file)) { 102 | fprintf(stderr, "Failed to read into boot buffer\n"); 103 | exit(1); 104 | } 105 | fclose(boot_file); 106 | 107 | status = janet_dobytes(env, boot_buffer, (int32_t) boot_size, boot_filename, NULL); 108 | janet_free(boot_buffer); 109 | 110 | /* Deinitialize vm */ 111 | janet_deinit(); 112 | 113 | return status; 114 | } 115 | -------------------------------------------------------------------------------- /test/suite-struct.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # 21bd960 25 | (assert (= (struct 1 2 3 4 5 6 7 8) (struct 7 8 5 6 3 4 1 2)) 26 | "struct order does not matter 1") 27 | # 42a88de 28 | (assert (= (struct 29 | :apple 1 30 | 6 :bork 31 | '(1 2 3) 5) 32 | (struct 33 | 6 :bork 34 | '(1 2 3) 5 35 | :apple 1)) "struct order does not matter 2") 36 | 37 | # Denormal structs 38 | # 38a7e4faf 39 | (assert (= (length {1 2 nil 3}) 1) "nil key struct literal") 40 | (assert (= (length (struct 1 2 nil 3)) 1) "nil key struct ctor") 41 | 42 | (assert (= (length (struct (/ 0 0) 2 1 3)) 1) "nan key struct ctor") 43 | (assert (= (length {1 2 (/ 0 0) 3}) 1) "nan key struct literal") 44 | 45 | (assert (= (length (struct 2 1 3 nil)) 1) "nil value struct ctor") 46 | (assert (= (length {1 2 3 nil}) 1) "nil value struct literal") 47 | 48 | # Struct duplicate elements 49 | # 8bc2987a7 50 | (assert (= {:a 3 :b 2} {:a 1 :b 2 :a 3}) "struct literal duplicate keys") 51 | (assert (= {:a 3 :b 2} (struct :a 1 :b 2 :a 3)) 52 | "struct constructor duplicate keys") 53 | 54 | # Struct prototypes 55 | # 4d983e5 56 | (def x (struct/with-proto {1 2 3 4} 5 6)) 57 | (def y (-> x marshal unmarshal)) 58 | (def z {1 2 3 4}) 59 | (assert (= 2 (get x 1)) "struct get proto value 1") 60 | (assert (= 4 (get x 3)) "struct get proto value 2") 61 | (assert (= 6 (get x 5)) "struct get proto value 3") 62 | (assert (= x y) "struct proto marshal equality 1") 63 | (assert (= (getproto x) (getproto y)) "struct proto marshal equality 2") 64 | (assert (= 0 (cmp x y)) "struct proto comparison 1") 65 | (assert (= 0 (cmp (getproto x) (getproto y))) "struct proto comparison 2") 66 | (assert (not= (cmp x z) 0) "struct proto comparison 3") 67 | (assert (not= (cmp y z) 0) "struct proto comparison 4") 68 | (assert (not= x z) "struct proto comparison 5") 69 | (assert (not= y z) "struct proto comparison 6") 70 | (assert (= (x 5) 6) "struct proto get 1") 71 | (assert (= (y 5) 6) "struct proto get 1") 72 | (assert (deep= x y) "struct proto deep= 1") 73 | (assert (deep-not= x z) "struct proto deep= 2") 74 | (assert (deep-not= y z) "struct proto deep= 3") 75 | 76 | # Check missing struct proto bug 77 | # 868ec1a7e, e08394c8 78 | (assert (struct/getproto (struct/with-proto {:a 1} :b 2 :c nil)) 79 | "missing struct proto") 80 | 81 | # struct/with-proto 82 | (assert-error "expected odd number of arguments" (struct/with-proto {} :a)) 83 | 84 | # struct/to-table 85 | (def s (struct/with-proto {:a 1 :b 2} :name "john" )) 86 | (def t1 (struct/to-table s true)) 87 | (def t2 (struct/to-table s false)) 88 | (assert (deep= t1 @{:name "john"}) "struct/to-table 1") 89 | (assert (deep= t2 @{:name "john"}) "struct/to-table 2") 90 | (assert (deep= (getproto t1) @{:a 1 :b 2}) "struct/to-table 3") 91 | (assert (deep= (getproto t2) nil) "struct/to-table 4") 92 | 93 | (end-suite) 94 | 95 | -------------------------------------------------------------------------------- /examples/numarray/numarray.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef struct { 5 | double *data; 6 | size_t size; 7 | } num_array; 8 | 9 | static num_array *num_array_init(num_array *array, size_t size) { 10 | array->data = (double *)janet_calloc(size, sizeof(double)); 11 | array->size = size; 12 | return array; 13 | } 14 | 15 | static void num_array_deinit(num_array *array) { 16 | janet_free(array->data); 17 | } 18 | 19 | static int num_array_gc(void *p, size_t s) { 20 | (void) s; 21 | num_array *array = (num_array *)p; 22 | num_array_deinit(array); 23 | return 0; 24 | } 25 | 26 | int num_array_get(void *p, Janet key, Janet *out); 27 | void num_array_put(void *p, Janet key, Janet value); 28 | 29 | static const JanetAbstractType num_array_type = { 30 | "numarray", 31 | num_array_gc, 32 | NULL, 33 | num_array_get, 34 | num_array_put, 35 | JANET_ATEND_PUT 36 | }; 37 | 38 | static Janet num_array_new(int32_t argc, Janet *argv) { 39 | janet_fixarity(argc, 1); 40 | int32_t size = janet_getinteger(argv, 0); 41 | num_array *array = (num_array *)janet_abstract(&num_array_type, sizeof(num_array)); 42 | num_array_init(array, size); 43 | return janet_wrap_abstract(array); 44 | } 45 | 46 | static Janet num_array_scale(int32_t argc, Janet *argv) { 47 | janet_fixarity(argc, 2); 48 | num_array *array = (num_array *)janet_getabstract(argv, 0, &num_array_type); 49 | double factor = janet_getnumber(argv, 1); 50 | size_t i; 51 | for (i = 0; i < array->size; i++) { 52 | array->data[i] *= factor; 53 | } 54 | return argv[0]; 55 | } 56 | 57 | static Janet num_array_sum(int32_t argc, Janet *argv) { 58 | janet_fixarity(argc, 1); 59 | num_array *array = (num_array *)janet_getabstract(argv, 0, &num_array_type); 60 | double sum = 0; 61 | for (size_t i = 0; i < array->size; i++) sum += array->data[i]; 62 | return janet_wrap_number(sum); 63 | } 64 | 65 | void num_array_put(void *p, Janet key, Janet value) { 66 | size_t index; 67 | num_array *array = (num_array *)p; 68 | if (!janet_checkint(key)) 69 | janet_panic("expected integer key"); 70 | if (!janet_checktype(value, JANET_NUMBER)) 71 | janet_panic("expected number value"); 72 | 73 | index = (size_t)janet_unwrap_integer(key); 74 | if (index < array->size) { 75 | array->data[index] = janet_unwrap_number(value); 76 | } 77 | } 78 | 79 | static Janet num_array_length(int32_t argc, Janet *argv) { 80 | janet_fixarity(argc, 1); 81 | num_array *array = (num_array *)janet_getabstract(argv, 0, &num_array_type); 82 | return janet_wrap_number(array->size); 83 | } 84 | 85 | static const JanetMethod methods[] = { 86 | {"scale", num_array_scale}, 87 | {"sum", num_array_sum}, 88 | {"length", num_array_length}, 89 | {NULL, NULL} 90 | }; 91 | 92 | int num_array_get(void *p, Janet key, Janet *out) { 93 | size_t index; 94 | num_array *array = (num_array *)p; 95 | if (janet_checktype(key, JANET_KEYWORD)) 96 | return janet_getmethod(janet_unwrap_keyword(key), methods, out); 97 | if (!janet_checkint(key)) 98 | janet_panic("expected integer key"); 99 | index = (size_t)janet_unwrap_integer(key); 100 | if (index >= array->size) { 101 | return 0; 102 | } else { 103 | *out = janet_wrap_number(array->data[index]); 104 | } 105 | return 1; 106 | } 107 | 108 | static const JanetReg cfuns[] = { 109 | { 110 | "new", num_array_new, 111 | "(numarray/new size)\n\n" 112 | "Create new numarray" 113 | }, 114 | { 115 | "scale", num_array_scale, 116 | "(numarray/scale numarray factor)\n\n" 117 | "scale numarray by factor" 118 | }, 119 | { 120 | "sum", num_array_sum, 121 | "(numarray/sum numarray)\n\n" 122 | "sums numarray" 123 | }, 124 | {NULL, NULL, NULL} 125 | }; 126 | 127 | JANET_MODULE_ENTRY(JanetTable *env) { 128 | janet_cfuns(env, "numarray", cfuns); 129 | } 130 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | 13 | release: 14 | permissions: 15 | contents: write # for softprops/action-gh-release to create GitHub release 16 | name: Build release binaries 17 | runs-on: ${{ matrix.os }} 18 | strategy: 19 | matrix: 20 | os: [ ubuntu-latest, macos-13 ] 21 | steps: 22 | - name: Checkout the repository 23 | uses: actions/checkout@master 24 | - name: Set the version 25 | run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV 26 | - name: Set the platform 27 | run: echo "platform=$(tr '[A-Z]' '[a-z]' <<< $RUNNER_OS)" >> $GITHUB_ENV 28 | - name: Compile the project 29 | run: make clean && make 30 | - name: Build the artifact 31 | run: JANET_DIST_DIR=janet-${{ env.version }}-${{ env.platform }} make build/janet-${{ env.version }}-${{ env.platform }}-x64.tar.gz 32 | - name: Draft the release 33 | uses: softprops/action-gh-release@v1 34 | with: 35 | draft: true 36 | files: | 37 | build/*.gz 38 | build/janet.h 39 | build/c/janet.c 40 | build/c/shell.c 41 | 42 | release-arm: 43 | permissions: 44 | contents: write # for softprops/action-gh-release to create GitHub release 45 | name: Build release binaries 46 | runs-on: ${{ matrix.os }} 47 | strategy: 48 | matrix: 49 | os: [ macos-latest ] 50 | steps: 51 | - name: Checkout the repository 52 | uses: actions/checkout@master 53 | - name: Set the version 54 | run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV 55 | - name: Set the platform 56 | run: echo "platform=$(tr '[A-Z]' '[a-z]' <<< $RUNNER_OS)" >> $GITHUB_ENV 57 | - name: Compile the project 58 | run: make clean && make 59 | - name: Build the artifact 60 | run: JANET_DIST_DIR=janet-${{ env.version }}-${{ env.platform }} make build/janet-${{ env.version }}-${{ env.platform }}-aarch64.tar.gz 61 | - name: Draft the release 62 | uses: softprops/action-gh-release@v1 63 | with: 64 | draft: true 65 | files: | 66 | build/*.gz 67 | build/janet.h 68 | build/c/janet.c 69 | build/c/shell.c 70 | 71 | release-windows: 72 | permissions: 73 | contents: write # for softprops/action-gh-release to create GitHub release 74 | name: Build release binaries for windows 75 | runs-on: windows-latest 76 | steps: 77 | - name: Checkout the repository 78 | uses: actions/checkout@master 79 | - name: Setup MSVC 80 | uses: ilammy/msvc-dev-cmd@v1 81 | - name: Build the project 82 | shell: cmd 83 | run: build_win all 84 | - name: Draft the release 85 | uses: softprops/action-gh-release@v1 86 | with: 87 | draft: true 88 | files: | 89 | ./dist/*.zip 90 | ./*.zip 91 | ./*.msi 92 | 93 | release-cosmo: 94 | permissions: 95 | contents: write # for softprops/action-gh-release to create GitHub release 96 | name: Build release binaries for Cosmo 97 | runs-on: ubuntu-latest 98 | steps: 99 | - name: Checkout the repository 100 | uses: actions/checkout@master 101 | - name: create build folder 102 | run: | 103 | sudo mkdir -p /sc 104 | sudo chmod -R 0777 /sc 105 | - name: setup Cosmopolitan Libc 106 | run: bash ./.github/cosmo/setup 107 | - name: Set the version 108 | run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV 109 | - name: Set the platform 110 | run: echo "platform=cosmo" >> $GITHUB_ENV 111 | - name: build Janet APE binary 112 | run: bash ./.github/cosmo/build 113 | - name: push binary to github 114 | uses: softprops/action-gh-release@v1 115 | with: 116 | draft: true 117 | files: | 118 | /sc/cosmocc/janet.com 119 | -------------------------------------------------------------------------------- /examples/debugger.janet: -------------------------------------------------------------------------------- 1 | ### 2 | ### A useful debugger library for Janet. Should be used 3 | ### inside a debug repl. This has been moved into the core. 4 | ### 5 | 6 | (defn .fiber 7 | "Get the current fiber being debugged." 8 | [] 9 | (dyn :fiber)) 10 | 11 | (defn .stack 12 | "Print the current fiber stack" 13 | [] 14 | (print) 15 | (with-dyns [:err-color false] (debug/stacktrace (.fiber) "")) 16 | (print)) 17 | 18 | (defn .frame 19 | "Show a stack frame" 20 | [&opt n] 21 | (def stack (debug/stack (.fiber))) 22 | (in stack (or n 0))) 23 | 24 | (defn .fn 25 | "Get the current function" 26 | [&opt n] 27 | (in (.frame n) :function)) 28 | 29 | (defn .slots 30 | "Get an array of slots in a stack frame" 31 | [&opt n] 32 | (in (.frame n) :slots)) 33 | 34 | (defn .slot 35 | "Get the value of the nth slot." 36 | [&opt nth frame-idx] 37 | (in (.slots frame-idx) (or nth 0))) 38 | 39 | (defn .quit 40 | "Resume (dyn :fiber) with the value passed to it after exiting the debugger." 41 | [&opt val] 42 | (setdyn :exit true) 43 | (setdyn :resume-value val) 44 | nil) 45 | 46 | (defn .disasm 47 | "Gets the assembly for the current function." 48 | [&opt n] 49 | (def frame (.frame n)) 50 | (def func (frame :function)) 51 | (disasm func)) 52 | 53 | (defn .bytecode 54 | "Get the bytecode for the current function." 55 | [&opt n] 56 | ((.disasm n) 'bytecode)) 57 | 58 | (defn .ppasm 59 | "Pretty prints the assembly for the current function" 60 | [&opt n] 61 | (def frame (.frame n)) 62 | (def func (frame :function)) 63 | (def dasm (disasm func)) 64 | (def bytecode (dasm 'bytecode)) 65 | (def pc (frame :pc)) 66 | (def sourcemap (dasm 'sourcemap)) 67 | (var last-loc [-2 -2]) 68 | (print "\n function: " (dasm 'name) " [" (in dasm 'source "") "]") 69 | (when-let [constants (dasm 'constants)] 70 | (printf " constants: %.4Q" constants)) 71 | (printf " slots: %.4Q\n" (frame :slots)) 72 | (def padding (string/repeat " " 20)) 73 | (loop [i :range [0 (length bytecode)] 74 | :let [instr (bytecode i)]] 75 | (prin (if (= (tuple/type instr) :brackets) "*" " ")) 76 | (prin (if (= i pc) "> " " ")) 77 | (prinf "\e[33m%.20s\e[0m" (string (string/join (map string instr) " ") padding)) 78 | (when sourcemap 79 | (let [[sl sc] (sourcemap i) 80 | loc [sl sc]] 81 | (when (not= loc last-loc) 82 | (set last-loc loc) 83 | (prin " # line " sl ", column " sc)))) 84 | (print)) 85 | (print)) 86 | 87 | (defn .source 88 | "Show the source code for the function being debugged." 89 | [&opt n] 90 | (def frame (.frame n)) 91 | (def s (frame :source)) 92 | (def all-source (slurp s)) 93 | (print "\n\e[33m" all-source "\e[0m\n")) 94 | 95 | (defn .breakall 96 | "Set breakpoints on all instructions in the current function." 97 | [&opt n] 98 | (def fun (.fn n)) 99 | (def bytecode (.bytecode n)) 100 | (for i 0 (length bytecode) 101 | (debug/fbreak fun i)) 102 | (print "Set " (length bytecode) " breakpoints in " fun)) 103 | 104 | (defn .clearall 105 | "Clear all breakpoints on the current function." 106 | [&opt n] 107 | (def fun (.fn n)) 108 | (def bytecode (.bytecode n)) 109 | (for i 0 (length bytecode) 110 | (debug/unfbreak fun i)) 111 | (print "Cleared " (length bytecode) " breakpoints in " fun)) 112 | 113 | (defn .break 114 | "Set breakpoint at the current pc." 115 | [] 116 | (def frame (.frame)) 117 | (def fun (frame :function)) 118 | (def pc (frame :pc)) 119 | (debug/fbreak fun pc) 120 | (print "Set breakpoint in " fun " at pc=" pc)) 121 | 122 | (defn .clear 123 | "Clear the current breakpoint" 124 | [] 125 | (def frame (.frame)) 126 | (def fun (frame :function)) 127 | (def pc (frame :pc)) 128 | (debug/unfbreak fun pc) 129 | (print "Cleared breakpoint in " fun " at pc=" pc)) 130 | 131 | (defn .next 132 | "Go to the next breakpoint." 133 | [&opt n] 134 | (var res nil) 135 | (for i 0 (or n 1) 136 | (set res (resume (.fiber)))) 137 | res) 138 | 139 | (defn .nextc 140 | "Go to the next breakpoint, clearing the current breakpoint." 141 | [&opt n] 142 | (.clear) 143 | (.next n)) 144 | 145 | (defn .step 146 | "Execute the next n instructions." 147 | [&opt n] 148 | (var res nil) 149 | (for i 0 (or n 1) 150 | (set res (debug/step (.fiber)))) 151 | res) 152 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Guidelines for contributing to Janet 2 | 3 | Thanks for taking time to contribute to Janet! 4 | 5 | Please read this document before making contributions. 6 | 7 | ## Reporting bugs 8 | 9 | * Check past and current issues to see if your problem has been run into before. 10 | * If you can't find a past issue for your problem, or if the issues has been closed 11 | you should open a new issue. If there is a closed issue that is relevant, make 12 | sure to reference it. 13 | * As with any project, include a comprehensive description of the problem and instructions 14 | on how to reproduce it. If it is a compiler or language bug, please try to include a minimal 15 | example. This means don't post all 200 lines of code from your project, but spend some time 16 | distilling the problem to just the relevant code. 17 | 18 | ## Contributing Changes 19 | 20 | If you want to contribute some code to the project, please submit a pull request and 21 | follow the below guidelines. Not all changes will be merged, and some pull requests 22 | may require changes before being merged. 23 | 24 | * Include a description of the changes. 25 | * If there are changes to the compiler or the language, please include tests in the test folder. 26 | The test suites are not organized in any particular way now, so simply add your tests 27 | to one of the test suite files (test/suite0.janet, test/suite1.janet, etc.). You can 28 | run tests with `make test`. If you want to add a new test suite, simply add a file to 29 | the test folder and make sure it is run when`make test` is invoked. 30 | * Be consistent with the style. For C this means follow the indentation and style in 31 | other files (files have MIT license at top, 4 spaces indentation, no trailing 32 | whitespace, cuddled brackets, etc.) Use `make format` to automatically format your C code with 33 | [astyle](http://astyle.sourceforge.net/astyle.html). You will probably need 34 | to install this, but it can be installed with most package managers. 35 | 36 | For janet code, use lisp indentation with 2 spaces. One can use janet.vim to 37 | do this indentation, or approximate as close as possible. There is a janet formatter 38 | in [spork](https://github.com/janet-lang/spork.git) that can be used to format code as well. 39 | 40 | ## C style 41 | 42 | For changes to the VM and Core code, you will probably need to know C. Janet is programmed with 43 | a subset of C99 that works with Microsoft Visual C++. This means most of C99 but with the following 44 | omissions. 45 | 46 | * No `restrict` 47 | * Certain functions in the standard library are not always available 48 | 49 | In practice, this means programming for both MSVC on one hand and everything else on the other. 50 | The code must also build with emscripten, even if some features are not available, although 51 | this is not a priority. 52 | 53 | Code should compile warning free and run valgrind clean. I find that these two criteria are some 54 | of the easiest ways to protect against a large number of bugs in an unsafe language like C. To check for 55 | valgrind errors, run `make valtest` and check the output for undefined or flagged behavior. 56 | 57 | ### Formatting 58 | 59 | Use [astyle](http://astyle.sourceforge.net/astyle.html) via `make format` to 60 | ensure a consistent code style for C. 61 | 62 | ## Janet style 63 | 64 | All janet code in the project should be formatted similar to the code in src/boot/boot.janet. 65 | The auto formatting from janet.vim will work well. 66 | 67 | ## Typo Fixing and One-Line changes 68 | 69 | Typo fixes are welcome, as are simple one line fixes. Do not open many separate pull requests for each 70 | individual typo fix. This is incredibly annoying to deal with as someone needs to review each PR, run 71 | CI, and merge. Instead, accumulate batches of typo fixes into a single PR. If there are objections to 72 | specific changes, these can be addressed in the review process before the final merge, if the changes 73 | are accepted. 74 | 75 | Similarly, low effort and bad faith changes are annoying to developers and such issues may be closed 76 | immediately without response. 77 | 78 | ## Contributions from Automated Tools 79 | 80 | People making changes found or generated by automated tools MUST note this when opening an issue 81 | or creating a pull request. This can help give context to developers if the change/issue is 82 | confusing or nonsensical. 83 | 84 | ## Suggesting Changes 85 | 86 | To suggest changes, open an issue on GitHub. Check GitHub for other issues 87 | that may be related to your issue before opening a new suggestion. Suggestions 88 | put forth without code will be considered, but not necessarily implemented in any 89 | timely manner. In short, if you want extra functionality now, then build it. 90 | 91 | * Include a good description of the problem that is being solved 92 | * Include descriptions of potential solutions if you have some in mind. 93 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | 10 | test-posix: 11 | name: Build and test on POSIX systems 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [ ubuntu-latest, macos-latest, macos-14 ] 16 | steps: 17 | - name: Checkout the repository 18 | uses: actions/checkout@master 19 | - name: Compile the project 20 | run: make clean && make 21 | - name: Test the project 22 | run: make test 23 | 24 | test-windows: 25 | name: Build and test on Windows 26 | strategy: 27 | matrix: 28 | os: [ windows-latest, windows-2022 ] 29 | runs-on: ${{ matrix.os }} 30 | steps: 31 | - name: Checkout the repository 32 | uses: actions/checkout@master 33 | - name: Setup MSVC 34 | uses: ilammy/msvc-dev-cmd@v1 35 | - name: Build the project 36 | shell: cmd 37 | run: build_win 38 | - name: Test the project 39 | shell: cmd 40 | run: build_win test 41 | - name: Test installer build 42 | shell: cmd 43 | run: build_win dist 44 | 45 | test-windows-min: 46 | name: Build and test on Windows Minimal build 47 | strategy: 48 | matrix: 49 | os: [ windows-2022 ] 50 | runs-on: ${{ matrix.os }} 51 | steps: 52 | - name: Checkout the repository 53 | uses: actions/checkout@master 54 | - name: Setup MSVC 55 | uses: ilammy/msvc-dev-cmd@v1 56 | - name: Setup Python 57 | uses: actions/setup-python@v2 58 | with: 59 | python-version: '3.x' 60 | - name: Install Python Dependencies 61 | run: pip install meson ninja 62 | - name: Build 63 | shell: cmd 64 | run: | 65 | 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 66 | cd build_meson_min 67 | ninja 68 | 69 | test-mingw: 70 | name: Build on Windows with Mingw 71 | runs-on: windows-latest 72 | defaults: 73 | run: 74 | shell: msys2 {0} 75 | strategy: 76 | matrix: 77 | msystem: [ UCRT64, CLANG64 ] 78 | steps: 79 | - name: Checkout the repository 80 | uses: actions/checkout@master 81 | - name: Setup Mingw 82 | uses: msys2/setup-msys2@v2 83 | with: 84 | msystem: ${{ matrix.msystem }} 85 | update: true 86 | install: >- 87 | base-devel 88 | git 89 | gcc 90 | - name: Build 91 | shell: cmd 92 | run: make -j4 CC=gcc 93 | - name: Test 94 | shell: cmd 95 | run: make -j4 CC=gcc test 96 | 97 | test-mingw-linux: 98 | name: Build and test with Mingw on Linux + Wine 99 | runs-on: ubuntu-latest 100 | steps: 101 | - name: Checkout the repository 102 | uses: actions/checkout@master 103 | - name: Setup Mingw and wine 104 | run: | 105 | sudo dpkg --add-architecture i386 106 | sudo apt-get update 107 | sudo apt-get install libstdc++6:i386 libgcc-s1:i386 108 | sudo apt-get install gcc-mingw-w64-x86-64-win32 wine wine32 wine64 109 | - name: Compile the project 110 | run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine 111 | - name: Test the project 112 | run: make test UNAME=MINGW RUN=wine VERBOSE=1 113 | 114 | test-arm-linux: 115 | name: Build and test ARM32 cross compilation 116 | runs-on: ubuntu-latest 117 | steps: 118 | - name: Checkout the repository 119 | uses: actions/checkout@master 120 | - name: Setup qemu and cross compiler 121 | run: | 122 | sudo apt-get update 123 | sudo apt-get install gcc-arm-linux-gnueabi qemu-user 124 | - name: Compile the project 125 | run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc 126 | - name: Test the project 127 | run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test VERBOSE=1 128 | 129 | test-s390x-linux: 130 | name: Build and test s390x in qemu 131 | runs-on: ubuntu-latest 132 | steps: 133 | - name: Checkout the repository 134 | uses: actions/checkout@master 135 | - name: Enable qemu 136 | run: docker run --privileged --rm tonistiigi/binfmt --install s390x 137 | - name: Build and run on emulated architecture 138 | run: docker run --rm -v .:/janet --platform linux/s390x alpine sh -c "apk update && apk add --no-interactive git build-base && cd /janet && make -j3 && make test" 139 | -------------------------------------------------------------------------------- /examples/ffi/so.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifdef _WIN32 6 | #define EXPORTER __declspec(dllexport) 7 | #else 8 | #define EXPORTER 9 | #endif 10 | 11 | /* Structs */ 12 | 13 | typedef struct { 14 | int a, b; 15 | float c, d; 16 | } Split; 17 | 18 | typedef struct { 19 | float c, d; 20 | int a, b; 21 | } SplitFlip; 22 | 23 | typedef struct { 24 | int u, v, w, x, y, z; 25 | } SixInts; 26 | 27 | typedef struct { 28 | int a; 29 | int b; 30 | } intint; 31 | 32 | typedef struct { 33 | int a; 34 | int b; 35 | int c; 36 | } intintint; 37 | 38 | typedef struct { 39 | uint64_t a; 40 | uint64_t b; 41 | } uint64pair; 42 | 43 | typedef struct { 44 | int64_t a; 45 | int64_t b; 46 | int64_t c; 47 | } big; 48 | 49 | /* Functions */ 50 | 51 | EXPORTER 52 | int int_fn(int a, int b) { 53 | return (a << 2) + b; 54 | } 55 | 56 | EXPORTER 57 | double my_fn(int64_t a, int64_t b, const char *x) { 58 | return (double)(a + b) + 0.5 + strlen(x); 59 | } 60 | 61 | EXPORTER 62 | double double_fn(double x, double y, double z) { 63 | return (x + y) * z * 3; 64 | } 65 | 66 | EXPORTER 67 | double double_many(double x, double y, double z, double w, double a, double b) { 68 | return x + y + z + w + a + b; 69 | } 70 | 71 | EXPORTER 72 | double double_lots( 73 | double a, 74 | double b, 75 | double c, 76 | double d, 77 | double e, 78 | double f, 79 | double g, 80 | double h, 81 | double i, 82 | double j) { 83 | return i + j; 84 | } 85 | 86 | EXPORTER 87 | double double_lots_2( 88 | double a, 89 | double b, 90 | double c, 91 | double d, 92 | double e, 93 | double f, 94 | double g, 95 | double h, 96 | double i, 97 | double j) { 98 | return a + 99 | 10.0 * b + 100 | 100.0 * c + 101 | 1000.0 * d + 102 | 10000.0 * e + 103 | 100000.0 * f + 104 | 1000000.0 * g + 105 | 10000000.0 * h + 106 | 100000000.0 * i + 107 | 1000000000.0 * j; 108 | } 109 | 110 | EXPORTER 111 | double float_fn(float x, float y, float z) { 112 | return (x + y) * z; 113 | } 114 | 115 | EXPORTER 116 | int intint_fn(double x, intint ii) { 117 | printf("double: %g\n", x); 118 | return ii.a + ii.b; 119 | } 120 | 121 | EXPORTER 122 | int intintint_fn(double x, intintint iii) { 123 | printf("double: %g\n", x); 124 | return iii.a + iii.b + iii.c; 125 | } 126 | 127 | EXPORTER 128 | intint return_struct(int i) { 129 | intint ret; 130 | ret.a = i; 131 | ret.b = i * i; 132 | return ret; 133 | } 134 | 135 | EXPORTER 136 | big struct_big(int i, double d) { 137 | big ret; 138 | ret.a = i; 139 | ret.b = (int64_t) d; 140 | ret.c = ret.a + ret.b + 1000; 141 | return ret; 142 | } 143 | 144 | EXPORTER 145 | void void_fn(void) { 146 | printf("void fn ran\n"); 147 | } 148 | 149 | EXPORTER 150 | void void_fn_2(double y) { 151 | printf("y = %f\n", y); 152 | } 153 | 154 | EXPORTER 155 | void void_ret_fn(int x) { 156 | printf("void fn ran: %d\n", x); 157 | } 158 | 159 | EXPORTER 160 | int intintint_fn_2(intintint iii, int i) { 161 | fprintf(stderr, "iii.a = %d, iii.b = %d, iii.c = %d, i = %d\n", iii.a, iii.b, iii.c, i); 162 | return i * (iii.a + iii.b + iii.c); 163 | } 164 | 165 | EXPORTER 166 | float split_fn(Split s) { 167 | return s.a * s.c + s.b * s.d; 168 | } 169 | 170 | EXPORTER 171 | float split_flip_fn(SplitFlip s) { 172 | return s.a * s.c + s.b * s.d; 173 | } 174 | 175 | EXPORTER 176 | Split split_ret_fn(int x, float y) { 177 | Split ret; 178 | ret.a = x; 179 | ret.b = x; 180 | ret.c = y; 181 | ret.d = y; 182 | return ret; 183 | } 184 | 185 | EXPORTER 186 | SplitFlip split_flip_ret_fn(int x, float y) { 187 | SplitFlip ret; 188 | ret.a = x; 189 | ret.b = x; 190 | ret.c = y; 191 | ret.d = y; 192 | return ret; 193 | } 194 | 195 | EXPORTER 196 | SixInts sixints_fn(void) { 197 | return (SixInts) { 198 | 6666, 1111, 2222, 3333, 4444, 5555 199 | }; 200 | } 201 | 202 | EXPORTER 203 | int sixints_fn_2(int x, SixInts s) { 204 | return x + s.u + s.v + s.w + s.x + s.y + s.z; 205 | } 206 | 207 | EXPORTER 208 | int sixints_fn_3(SixInts s, int x) { 209 | return x + s.u + s.v + s.w + s.x + s.y + s.z; 210 | } 211 | 212 | EXPORTER 213 | intint stack_spill_fn(uint8_t a, uint8_t b, uint8_t c, uint8_t d, 214 | uint8_t e, uint8_t f, uint8_t g, uint8_t h, 215 | float i, float j, float k, float l, 216 | float m, float n, float o, float p, 217 | float s1, int8_t s2, uint8_t s3, double s4, uint8_t s5, intint s6) { 218 | return (intint) { 219 | (a | b | c | d | e | f | g | h) + (i + j + k + l + m + n + o + p), 220 | s1 *s6.a + s2 *s6.b + s3 *s4 *s5 221 | }; 222 | } 223 | 224 | EXPORTER 225 | double stack_spill_fn_2(uint64pair a, uint64pair b, uint64pair c, int8_t d, uint64pair e, int8_t f) { 226 | return (double)(a.a * c.a + a.b * c.b + b.a * e.a) * f - (double)(b.b * e.b) + d; 227 | } 228 | -------------------------------------------------------------------------------- /test/suite-vm.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | # More fiber semantics 25 | # 0fd9224e4 26 | (var myvar 0) 27 | (defn fiberstuff [&] 28 | (++ myvar) 29 | (def f (fiber/new (fn [&] (++ myvar) (debug) (++ myvar)))) 30 | (resume f) 31 | (++ myvar)) 32 | 33 | (def myfiber (fiber/new fiberstuff :dey)) 34 | 35 | (assert (= myvar 0) "fiber creation does not call fiber function") 36 | (resume myfiber) 37 | (assert (= myvar 2) "fiber debug statement breaks at proper point") 38 | (assert (= (fiber/status myfiber) :debug) "fiber enters debug state") 39 | (resume myfiber) 40 | (assert (= myvar 4) "fiber resumes properly from debug state") 41 | (assert (= (fiber/status myfiber) :dead) 42 | "fiber properly dies from debug state") 43 | 44 | # yield tests 45 | # 171c0ce 46 | (def t (fiber/new (fn [&] (yield 1) (yield 2) 3))) 47 | 48 | (assert (= 1 (resume t)) "initial transfer to new fiber") 49 | (assert (= 2 (resume t)) "second transfer to fiber") 50 | (assert (= 3 (resume t)) "return from fiber") 51 | (assert (= (fiber/status t) :dead) "finished fiber is dead") 52 | 53 | # Fix yields inside nested fibers 54 | # 909c906 55 | (def yielder 56 | (coro 57 | (defer (yield :end) 58 | (repeat 5 (yield :item))))) 59 | (def items (seq [x :in yielder] x)) 60 | (assert (deep= @[:item :item :item :item :item :end] items) 61 | "yield within nested fibers") 62 | 63 | # Calling non functions 64 | # b9c0fc820 65 | (assert (= 1 ({:ok 1} :ok)) "calling struct") 66 | (assert (= 2 (@{:ok 2} :ok)) "calling table") 67 | (assert (= :bad (try ((identity @{:ok 2}) :ok :no) ([err] :bad))) 68 | "calling table too many arguments") 69 | (assert (= :bad (try ((identity :ok) @{:ok 2} :no) ([err] :bad))) 70 | "calling keyword too many arguments") 71 | (assert (= :oops (try ((+ 2 -1) 1) ([err] :oops))) 72 | "calling number fails") 73 | 74 | # Method test 75 | # d5bab7262 76 | (def Dog @{:bark (fn bark [self what] 77 | (string (self :name) " says " what "!"))}) 78 | (defn make-dog 79 | [name] 80 | (table/setproto @{:name name} Dog)) 81 | 82 | (assert (= "fido" ((make-dog "fido") :name)) "oo 1") 83 | (def spot (make-dog "spot")) 84 | (assert (= "spot says hi!" (:bark spot "hi")) "oo 2") 85 | 86 | # Negative tests 87 | # 67f26b7d7 88 | (assert-error "+ check types" (+ 1 ())) 89 | (assert-error "- check types" (- 1 ())) 90 | (assert-error "* check types" (* 1 ())) 91 | (assert-error "/ check types" (/ 1 ())) 92 | (assert-error "band check types" (band 1 ())) 93 | (assert-error "bor check types" (bor 1 ())) 94 | (assert-error "bxor check types" (bxor 1 ())) 95 | (assert-error "bnot check types" (bnot ())) 96 | 97 | # Comparisons 98 | # 10dcbc639 99 | (assert (> 1e23 100) "less than immediate 1") 100 | (assert (> 1e23 1000) "less than immediate 2") 101 | (assert (< 100 1e23) "greater than immediate 1") 102 | (assert (< 1000 1e23) "greater than immediate 2") 103 | 104 | # Quasiquote bracketed tuples 105 | # e239980da 106 | (assert (= (tuple/type ~[1 2 3]) (tuple/type '[1 2 3])) 107 | "quasiquote bracket tuples") 108 | 109 | # Regression #638 110 | # c68264802 111 | (compwhen 112 | (dyn 'ev/go) 113 | (assert 114 | (= [true :caught] 115 | (protect 116 | (try 117 | (do 118 | (ev/sleep 0) 119 | (with-dyns [] 120 | (ev/sleep 0) 121 | (error "oops"))) 122 | ([err] :caught)))) 123 | "regression #638")) 124 | 125 | # 126 | # Test propagation of signals via fibers 127 | # 128 | # b8032ec61 129 | (def f (fiber/new (fn [] (error :abc) 1) :ei)) 130 | (def res (resume f)) 131 | (assert-error :abc (propagate res f) "propagate 1") 132 | 133 | # Cancel test 134 | # 28439d822 135 | (def f (fiber/new (fn [&] (yield 1) (yield 2) (yield 3) 4) :yti)) 136 | (assert (= 1 (resume f)) "cancel resume 1") 137 | (assert (= 2 (resume f)) "cancel resume 2") 138 | (assert (= :hi (cancel f :hi)) "cancel resume 3") 139 | (assert (= :error (fiber/status f)) "cancel resume 4") 140 | 141 | (end-suite) 142 | 143 | -------------------------------------------------------------------------------- /examples/ffi/test.janet: -------------------------------------------------------------------------------- 1 | # 2 | # Simple FFI test script that tests against a simple shared object 3 | # 4 | 5 | (def is-windows (= :windows (os/which))) 6 | (def ffi/loc (string "examples/ffi/so." (if is-windows "dll" "so"))) 7 | (def ffi/source-loc "examples/ffi/so.c") 8 | 9 | (if is-windows 10 | (os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px) 11 | (os/execute ["cc" ffi/source-loc "-g" "-shared" "-o" ffi/loc] :px)) 12 | 13 | (ffi/context ffi/loc) 14 | 15 | (def intint (ffi/struct :int :int)) 16 | (def intintint (ffi/struct :int :int :int)) 17 | (def uint64pair (ffi/struct :u64 :u64)) 18 | (def big (ffi/struct :s64 :s64 :s64)) 19 | (def split (ffi/struct :int :int :float :float)) 20 | (def split-flip (ffi/struct :float :float :int :int)) 21 | (def six-ints (ffi/struct :int :int :int :int :int :int)) 22 | 23 | (ffi/defbind int-fn :int [a :int b :int]) 24 | (ffi/defbind double-fn :double [a :double b :double c :double]) 25 | (ffi/defbind double-many :double 26 | [x :double y :double z :double w :double a :double b :double]) 27 | (ffi/defbind double-lots :double 28 | [a :double b :double c :double d :double e :double f :double g :double h :double i :double j :double]) 29 | (ffi/defbind float-fn :double 30 | [x :float y :float z :float]) 31 | (ffi/defbind intint-fn :int 32 | [x :double ii [:int :int]]) 33 | (ffi/defbind return-struct [:int :int] 34 | [i :int]) 35 | (ffi/defbind intintint-fn :int 36 | [x :double iii intintint]) 37 | (ffi/defbind struct-big big 38 | [i :int d :double]) 39 | (ffi/defbind void-fn :void []) 40 | (ffi/defbind double-lots-2 :double 41 | [a :double 42 | b :double 43 | c :double 44 | d :double 45 | e :double 46 | f :double 47 | g :double 48 | h :double 49 | i :double 50 | j :double]) 51 | (ffi/defbind void-fn-2 :void [y :double]) 52 | (ffi/defbind intintint-fn-2 :int [iii intintint i :int]) 53 | (ffi/defbind split-fn :float [s split]) 54 | (ffi/defbind split-flip-fn :float [s split-flip]) 55 | (ffi/defbind split-ret-fn split [x :int y :float]) 56 | (ffi/defbind split-flip-ret-fn split-flip [x :int y :float]) 57 | (ffi/defbind sixints-fn six-ints []) 58 | (ffi/defbind sixints-fn-2 :int [x :int s six-ints]) 59 | (ffi/defbind sixints-fn-3 :int [s six-ints x :int]) 60 | (ffi/defbind stack-spill-fn intint 61 | [a :u8 b :u8 c :u8 d :u8 62 | e :u8 f :u8 g :u8 h :u8 63 | i :float j :float k :float l :float 64 | m :float n :float o :float p :float 65 | s1 :float s2 :s8 s3 :u8 s4 :double s5 :u8 s6 intint]) 66 | (ffi/defbind stack-spill-fn-2 :double [a uint64pair b uint64pair c uint64pair d :s8 e uint64pair f :s8]) 67 | (ffi/defbind-alias int-fn int-fn-aliased :int [a :int b :int]) 68 | 69 | # 70 | # Struct reading and writing 71 | # 72 | 73 | (defn check-round-trip 74 | [t value] 75 | (def buf (ffi/write t value)) 76 | (def same-value (ffi/read t buf)) 77 | (assert (deep= value same-value) 78 | (string/format "round trip %j (got %j)" value same-value))) 79 | 80 | (check-round-trip :bool true) 81 | (check-round-trip :bool false) 82 | (check-round-trip :void nil) 83 | (check-round-trip :void nil) 84 | (check-round-trip :s8 10) 85 | (check-round-trip :s8 0) 86 | (check-round-trip :s8 -10) 87 | (check-round-trip :u8 10) 88 | (check-round-trip :u8 0) 89 | (check-round-trip :s16 10) 90 | (check-round-trip :s16 0) 91 | (check-round-trip :s16 -12312) 92 | (check-round-trip :u16 10) 93 | (check-round-trip :u16 0) 94 | (check-round-trip :u32 0) 95 | (check-round-trip :u32 10) 96 | (check-round-trip :u32 0xFFFF7777) 97 | (check-round-trip :s32 0x7FFF7777) 98 | (check-round-trip :s32 0) 99 | (check-round-trip :s32 -1234567) 100 | 101 | (def s (ffi/struct :s8 :s8 :s8 :float)) 102 | (check-round-trip s [1 3 5 123.5]) 103 | (check-round-trip s [-1 -3 -5 -123.5]) 104 | 105 | # 106 | # Call functions 107 | # 108 | 109 | (tracev (sixints-fn)) 110 | (tracev (sixints-fn-2 100 [1 2 3 4 5 6])) 111 | (tracev (sixints-fn-3 [1 2 3 4 5 6] 200)) 112 | (tracev (split-ret-fn 10 12)) 113 | (tracev (split-flip-ret-fn 10 12)) 114 | (tracev (split-flip-ret-fn 12 10)) 115 | (tracev (intintint-fn-2 [10 20 30] 3)) 116 | (tracev (split-fn [5 6 1.2 3.4])) 117 | (tracev (void-fn-2 10.3)) 118 | (tracev (double-many 1 2 3 4 5 6)) 119 | (tracev (string/format "%.17g" (double-many 1 2 3 4 5 6))) 120 | (tracev (type (double-many 1 2 3 4 5 6))) 121 | (tracev (double-lots-2 0 1 2 3 4 5 6 7 8 9)) 122 | (tracev (void-fn)) 123 | (tracev (int-fn 10 20)) 124 | (tracev (double-fn 1.5 2.5 3.5)) 125 | (tracev (double-lots 1 2 3 4 5 6 7 8 9 10)) 126 | (tracev (float-fn 8 4 17)) 127 | (tracev (intint-fn 123.456 [10 20])) 128 | (tracev (intintint-fn 123.456 [10 20 30])) 129 | (tracev (return-struct 42)) 130 | (tracev (double-lots 1 2 3 4 5 6 700 800 9 10)) 131 | (tracev (struct-big 11 99.5)) 132 | (tracev (int-fn-aliased 10 20)) 133 | 134 | (assert (= [10 10 12 12] (split-ret-fn 10 12))) 135 | (assert (= [12 12 10 10] (split-flip-ret-fn 10 12))) 136 | (assert (= 183 (intintint-fn-2 [10 20 31] 3))) 137 | (assert (= 264 (math/round (* 10 (split-fn [5 6 1.2 3.4]))))) 138 | (assert (= 9876543210 (double-lots-2 0 1 2 3 4 5 6 7 8 9))) 139 | (assert (= 60 (int-fn 10 20))) 140 | (assert (= 42 (double-fn 1.5 2.5 3.5))) 141 | (assert (= 21 (math/round (double-many 1 2 3 4 5 6.01)))) 142 | (assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10))) 143 | (assert (= 204 (float-fn 8 4 17))) 144 | (assert (= [0 38534415] (stack-spill-fn 145 | 0 0 0 0 0 0 0 0 146 | 0 0 0 0 0 0 0 0 147 | 1.5 -32 196 65536.5 3 [-15 32]))) 148 | (assert (= -2806 (stack-spill-fn-2 [2 3] [5 7] [9 11] -19 [13 17] -23))) 149 | 150 | (print "Done.") 151 | -------------------------------------------------------------------------------- /src/core/regalloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef JANET_AMALG 24 | #include "features.h" 25 | #include 26 | #include "regalloc.h" 27 | #include "util.h" 28 | #endif 29 | 30 | /* The JanetRegisterAllocator is really just a bitset. */ 31 | 32 | void janetc_regalloc_init(JanetcRegisterAllocator *ra) { 33 | ra->chunks = NULL; 34 | ra->count = 0; 35 | ra->capacity = 0; 36 | ra->max = 0; 37 | ra->regtemps = 0; 38 | } 39 | 40 | void janetc_regalloc_deinit(JanetcRegisterAllocator *ra) { 41 | janet_free(ra->chunks); 42 | } 43 | 44 | /* Fallbacks for when ctz not available */ 45 | #ifdef __GNUC__ 46 | #define count_trailing_zeros(x) __builtin_ctz(x) 47 | #define count_trailing_ones(x) __builtin_ctz(~(x)) 48 | #else 49 | static int32_t count_trailing_ones(uint32_t x) { 50 | int32_t ret = 0; 51 | while (x & 1) { 52 | ret++; 53 | x >>= 1; 54 | } 55 | return ret; 56 | } 57 | #define count_trailing_zeros(x) count_trailing_ones(~(x)) 58 | #endif 59 | 60 | /* Get ith bit */ 61 | #define ithbit(I) ((uint32_t)1 << (I)) 62 | 63 | /* Get N bits */ 64 | #define nbits(N) (ithbit(N) - 1) 65 | 66 | /* Copy a register allocator */ 67 | void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src) { 68 | size_t size; 69 | dest->count = src->count; 70 | dest->capacity = src->capacity; 71 | dest->max = src->max; 72 | size = sizeof(uint32_t) * (size_t) dest->capacity; 73 | dest->regtemps = 0; 74 | if (size) { 75 | dest->chunks = janet_malloc(size); 76 | if (!dest->chunks) { 77 | JANET_OUT_OF_MEMORY; 78 | } 79 | memcpy(dest->chunks, src->chunks, size); 80 | } else { 81 | dest->chunks = NULL; 82 | } 83 | } 84 | 85 | /* Allocate one more chunk in chunks */ 86 | static void pushchunk(JanetcRegisterAllocator *ra) { 87 | /* Registers 240-255 are always allocated (reserved) */ 88 | uint32_t chunk = ra->count == 7 ? 0xFFFF0000 : 0; 89 | int32_t newcount = ra->count + 1; 90 | if (newcount > ra->capacity) { 91 | int32_t newcapacity = newcount * 2; 92 | ra->chunks = janet_realloc(ra->chunks, (size_t) newcapacity * sizeof(uint32_t)); 93 | if (!ra->chunks) { 94 | JANET_OUT_OF_MEMORY; 95 | } 96 | ra->capacity = newcapacity; 97 | } 98 | ra->chunks[ra->count] = chunk; 99 | ra->count = newcount; 100 | } 101 | 102 | /* Reallocate a given register */ 103 | void janetc_regalloc_touch(JanetcRegisterAllocator *ra, int32_t reg) { 104 | int32_t chunk = reg >> 5; 105 | int32_t bit = reg & 0x1F; 106 | while (chunk >= ra->count) pushchunk(ra); 107 | ra->chunks[chunk] |= ithbit(bit); 108 | } 109 | 110 | /* Allocate one register. */ 111 | int32_t janetc_regalloc_1(JanetcRegisterAllocator *ra) { 112 | /* Get the nth bit in the array */ 113 | int32_t bit, chunk, nchunks, reg; 114 | bit = -1; 115 | nchunks = ra->count; 116 | for (chunk = 0; chunk < nchunks; chunk++) { 117 | uint32_t block = ra->chunks[chunk]; 118 | if (block == 0xFFFFFFFF) continue; 119 | bit = count_trailing_ones(block); 120 | break; 121 | } 122 | /* No reg found */ 123 | if (bit == -1) { 124 | pushchunk(ra); 125 | bit = 0; 126 | chunk = nchunks; 127 | } 128 | /* set the bit at index bit in chunk */ 129 | ra->chunks[chunk] |= ithbit(bit); 130 | reg = (chunk << 5) + bit; 131 | if (reg > ra->max) 132 | ra->max = reg; 133 | return reg; 134 | } 135 | 136 | /* Free a register. The register must have been previously allocated 137 | * without being freed. */ 138 | void janetc_regalloc_free(JanetcRegisterAllocator *ra, int32_t reg) { 139 | int32_t chunk = reg >> 5; 140 | int32_t bit = reg & 0x1F; 141 | ra->chunks[chunk] &= ~ithbit(bit); 142 | } 143 | 144 | /* Check if a register is set. */ 145 | int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg) { 146 | int32_t chunk = reg >> 5; 147 | int32_t bit = reg & 0x1F; 148 | while (chunk >= ra->count) pushchunk(ra); 149 | return !!(ra->chunks[chunk] & ithbit(bit)); 150 | } 151 | 152 | /* Get a register that will fit in 8 bits (< 256). Do not call this 153 | * twice with the same value of nth without calling janetc_regalloc_free 154 | * on the returned register before. */ 155 | int32_t janetc_regalloc_temp(JanetcRegisterAllocator *ra, JanetcRegisterTemp nth) { 156 | int32_t oldmax = ra->max; 157 | if (ra->regtemps & (1 << nth)) { 158 | JANET_EXIT("regtemp already allocated"); 159 | } 160 | ra->regtemps |= 1 << nth; 161 | int32_t reg = janetc_regalloc_1(ra); 162 | if (reg > 0xFF) { 163 | reg = 0xF0 + nth; 164 | ra->max = (reg > oldmax) ? reg : oldmax; 165 | } 166 | return reg; 167 | } 168 | 169 | void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRegisterTemp nth) { 170 | ra->regtemps &= ~(1 << nth); 171 | if (reg < 0xF0) 172 | janetc_regalloc_free(ra, reg); 173 | } 174 | -------------------------------------------------------------------------------- /test/suite-bundle.janet: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025 Calvin Rose 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | (import ./helper :prefix "" :exit true) 22 | (start-suite) 23 | 24 | (assert true) # smoke test 25 | 26 | # Testing here is stateful since we are manipulating the filesystem. 27 | 28 | # Copy since not exposed in boot.janet 29 | (defn- bundle-rpath 30 | [path] 31 | (string/replace-all "\\" "/" (os/realpath path))) 32 | 33 | # Test mkdir -> rmdir 34 | (assert (os/mkdir "tempdir123")) 35 | (rmrf "tempdir123") 36 | 37 | # Setup a temporary syspath for manipultation 38 | (math/seedrandom (os/cryptorand 16)) 39 | (def syspath (randdir)) 40 | (rmrf syspath) 41 | (assert (os/mkdir syspath)) 42 | (put root-env *syspath* (bundle-rpath syspath)) 43 | (unless (os/getenv "VERBOSE") 44 | (setdyn *out* @"")) 45 | (assert (empty? (bundle/list)) "initial bundle/list") 46 | (assert (empty? (bundle/topolist)) "initial bundle/topolist") 47 | 48 | # Try (and fail) to install sample-bundle (missing deps) 49 | (assert-error "missing dependencies sample-dep1, sample-dep2" 50 | (bundle/install "./examples/sample-bundle")) 51 | (assert (empty? (bundle/list))) 52 | 53 | # Install deps (dep1 as :auto-remove) 54 | (assert-no-error "sample-dep2" 55 | (bundle/install "./examples/sample-dep2")) 56 | (assert (= 1 (length (bundle/list)))) 57 | (assert-no-error "sample-dep1" (bundle/install "./examples/sample-dep1")) 58 | (assert (= 2 (length (bundle/list)))) 59 | 60 | (assert-no-error "sample-dep2 reinstall" (bundle/reinstall "sample-dep2")) 61 | (assert-no-error "sample-dep1 reinstall" (bundle/reinstall "sample-dep1" :auto-remove true)) 62 | 63 | (assert (= 2 (length (bundle/list))) "bundles are listed correctly 1") 64 | (assert (= 2 (length (bundle/topolist))) "bundles are listed correctly 2") 65 | 66 | # Now install sample-bundle 67 | (assert-no-error "sample-bundle install" (bundle/install "./examples/sample-bundle")) 68 | 69 | (assert-error "" (bundle/install "./examples/sample-dep11111")) 70 | 71 | (assert (= 3 (length (bundle/list))) "bundles are listed correctly 3") 72 | (assert (= 3 (length (bundle/topolist))) "bundles are listed correctly 4") 73 | 74 | # Check topolist has not bad order 75 | (def tlist (bundle/topolist)) 76 | (assert (> (index-of "sample-bundle" tlist) (index-of "sample-dep2" tlist)) "topolist 1") 77 | (assert (> (index-of "sample-bundle" tlist) (index-of "sample-dep1" tlist)) "topolist 2") 78 | (assert (> (index-of "sample-dep1" tlist) (index-of "sample-dep2" tlist)) "topolist 3") 79 | 80 | # Prune should do nothing 81 | (assert-no-error "first prune" (bundle/prune)) 82 | (assert (= 3 (length (bundle/list))) "bundles are listed correctly 3") 83 | (assert (= 3 (length (bundle/topolist))) "bundles are listed correctly 4") 84 | 85 | # Check that we can import the main dependency 86 | (import mymod) 87 | (assert (= 288 (mymod/myfn 12)) "using sample-bundle") 88 | 89 | # Manual uninstall of dep1 and dep2 shouldn't work either since that would break dependencies 90 | (assert-error "cannot uninstall sample-dep1, breaks dependent bundles @[\"sample-bundle\"]" 91 | (bundle/uninstall "sample-dep1")) 92 | 93 | # Check bundle file aliases 94 | (assert-no-error "sample-bundle-aliases install" (bundle/install "./examples/sample-bundle-aliases")) 95 | (assert (= 4 (length (bundle/list))) "bundles are listed correctly 5") 96 | (assert-no-error "import aliases" (import aliases-mod)) 97 | (assert (deep= (range 12) (aliases-mod/fun 12)) "using sample-bundle-aliases") 98 | (assert-no-error "aliases uninstall" (bundle/uninstall "sample-bundle-aliases")) 99 | 100 | # Now re-install sample-bundle as auto-remove 101 | (assert-no-error "sample-bundle install" (bundle/reinstall "sample-bundle" :auto-remove true)) 102 | 103 | # Reinstallation should also work without being concerned about breaking dependencies 104 | (assert-no-error "reinstall dep" (bundle/reinstall "sample-dep2")) 105 | 106 | # Now prune should get rid of everything except sample-dep2 107 | (assert-no-error "second prune" (bundle/prune)) 108 | 109 | # Now check that we exactly one package left, which is dep2 110 | (assert (= 1 (length (bundle/list))) "bundles are listed correctly 5") 111 | (assert (= 1 (length (bundle/topolist))) "bundles are listed correctly 6") 112 | 113 | # Which we can uninstall manually 114 | (assert-no-error "uninstall dep2" (bundle/uninstall "sample-dep2")) 115 | 116 | # Now check bundle listing is again empty 117 | (assert (= 0 (length (bundle/list))) "bundles are listed correctly 7") 118 | (assert (= 0 (length (bundle/topolist))) "bundles are listed correctly 8") 119 | 120 | # Try installing a bundle that is missing bundle script 121 | (assert-error-value "bundle missing bundle script" 122 | "bundle must contain bundle.janet or bundle/init.janet" 123 | (bundle/install "./examples/sample-bad-bundle1")) 124 | (assert (= 0 (length (bundle/list))) "check failure 0") 125 | (assert (= 0 (length (bundle/topolist))) "check failure 1") 126 | 127 | # Try installing a bundle that fails check 128 | (assert-error-value "bundle check hook fails" 129 | "Check failed!" 130 | (bundle/install "./examples/sample-bad-bundle2" :check true)) 131 | (assert (= 0 (length (bundle/list))) "check failure 0") 132 | (assert (= 0 (length (bundle/topolist))) "check failure 1") 133 | 134 | (rmrf syspath) 135 | 136 | (end-suite) 137 | -------------------------------------------------------------------------------- /src/core/state.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef JANET_STATE_H_defined 24 | #define JANET_STATE_H_defined 25 | 26 | #ifndef JANET_AMALG 27 | #include "features.h" 28 | #include 29 | #include 30 | #endif 31 | 32 | #ifdef JANET_EV 33 | #ifdef JANET_WINDOWS 34 | #include 35 | #else 36 | #include 37 | #endif 38 | #endif 39 | 40 | typedef int64_t JanetTimestamp; 41 | 42 | typedef struct JanetScratch { 43 | JanetScratchFinalizer finalize; 44 | long long mem[]; /* for proper alignment */ 45 | } JanetScratch; 46 | 47 | typedef struct { 48 | JanetGCObject *self; 49 | JanetGCObject *other; 50 | int32_t index; 51 | int32_t index2; 52 | } JanetTraversalNode; 53 | 54 | typedef struct { 55 | int32_t capacity; 56 | int32_t head; 57 | int32_t tail; 58 | void *data; 59 | } JanetQueue; 60 | 61 | #ifdef JANET_EV 62 | typedef struct { 63 | JanetTimestamp when; 64 | JanetFiber *fiber; 65 | JanetFiber *curr_fiber; 66 | uint32_t sched_id; 67 | int is_error; 68 | int has_worker; 69 | #ifdef JANET_WINDOWS 70 | HANDLE worker; 71 | HANDLE worker_event; 72 | #else 73 | pthread_t worker; 74 | #endif 75 | } JanetTimeout; 76 | #endif 77 | 78 | /* Registry table for C functions - contains metadata that can 79 | * be looked up by cfunction pointer. All strings here are pointing to 80 | * static memory not managed by Janet. */ 81 | typedef struct { 82 | JanetCFunction cfun; 83 | const char *name; 84 | const char *name_prefix; 85 | const char *source_file; 86 | int32_t source_line; 87 | /* int32_t min_arity; */ 88 | /* int32_t max_arity; */ 89 | } JanetCFunRegistry; 90 | 91 | struct JanetVM { 92 | /* Place for user data */ 93 | void *user; 94 | 95 | /* Top level dynamic bindings */ 96 | JanetTable *top_dyns; 97 | 98 | /* Cache the core environment */ 99 | JanetTable *core_env; 100 | 101 | /* How many VM stacks have been entered */ 102 | int stackn; 103 | 104 | /* If this flag is true, suspend on function calls and backwards jumps. 105 | * When this occurs, this flag will be reset to 0. */ 106 | volatile JanetAtomicInt auto_suspend; 107 | 108 | /* The current running fiber on the current thread. 109 | * Set and unset by functions in vm.c */ 110 | JanetFiber *fiber; 111 | JanetFiber *root_fiber; 112 | 113 | /* The current pointer to the inner most jmp_buf. The current 114 | * return point for panics. */ 115 | jmp_buf *signal_buf; 116 | Janet *return_reg; 117 | int coerce_error; 118 | 119 | /* The global registry for c functions. Used to store meta-data 120 | * along with otherwise bare c function pointers. */ 121 | JanetCFunRegistry *registry; 122 | size_t registry_cap; 123 | size_t registry_count; 124 | int registry_dirty; 125 | 126 | /* Registry for abstract types that can be marshalled. 127 | * We need this to look up the constructors when unmarshalling. */ 128 | JanetTable *abstract_registry; 129 | 130 | /* Immutable value cache */ 131 | const uint8_t **cache; 132 | uint32_t cache_capacity; 133 | uint32_t cache_count; 134 | uint32_t cache_deleted; 135 | uint8_t gensym_counter[8]; 136 | 137 | /* Garbage collection */ 138 | void *blocks; 139 | void *weak_blocks; 140 | size_t gc_interval; 141 | size_t next_collection; 142 | size_t block_count; 143 | int gc_suspend; 144 | int gc_mark_phase; 145 | 146 | /* GC roots */ 147 | Janet *roots; 148 | size_t root_count; 149 | size_t root_capacity; 150 | 151 | /* Scratch memory */ 152 | JanetScratch **scratch_mem; 153 | size_t scratch_cap; 154 | size_t scratch_len; 155 | 156 | /* Sandbox flags */ 157 | uint32_t sandbox_flags; 158 | 159 | /* Random number generator */ 160 | JanetRNG rng; 161 | 162 | /* Traversal pointers */ 163 | JanetTraversalNode *traversal; 164 | JanetTraversalNode *traversal_top; 165 | JanetTraversalNode *traversal_base; 166 | 167 | /* Thread safe strerror error buffer - for janet_strerror */ 168 | #ifndef JANET_WINDOWS 169 | char strerror_buf[256]; 170 | #endif 171 | 172 | /* Event loop and scheduler globals */ 173 | #ifdef JANET_EV 174 | size_t tq_count; 175 | size_t tq_capacity; 176 | JanetQueue spawn; 177 | JanetTimeout *tq; 178 | JanetRNG ev_rng; 179 | volatile JanetAtomicInt listener_count; /* used in signal handler, must be volatile */ 180 | JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */ 181 | JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */ 182 | JanetTable signal_handlers; 183 | #ifdef JANET_WINDOWS 184 | void **iocp; 185 | #elif defined(JANET_EV_EPOLL) 186 | pthread_attr_t new_thread_attr; 187 | JanetHandle selfpipe[2]; 188 | int epoll; 189 | int timerfd; 190 | int timer_enabled; 191 | #elif defined(JANET_EV_KQUEUE) 192 | pthread_attr_t new_thread_attr; 193 | JanetHandle selfpipe[2]; 194 | int kq; 195 | int timer; 196 | int timer_enabled; 197 | #else 198 | JanetStream **streams; 199 | size_t stream_count; 200 | size_t stream_capacity; 201 | pthread_attr_t new_thread_attr; 202 | JanetHandle selfpipe[2]; 203 | struct pollfd *fds; 204 | #endif 205 | #endif 206 | 207 | }; 208 | 209 | extern JANET_THREAD_LOCAL JanetVM janet_vm; 210 | 211 | #ifdef JANET_NET 212 | void janet_net_init(void); 213 | void janet_net_deinit(void); 214 | #endif 215 | 216 | #ifdef JANET_EV 217 | void janet_ev_init(void); 218 | void janet_ev_deinit(void); 219 | #endif 220 | 221 | #endif /* JANET_STATE_H_defined */ 222 | -------------------------------------------------------------------------------- /src/core/abstract.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025 Calvin Rose 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef JANET_AMALG 24 | #include "features.h" 25 | #include 26 | #include "util.h" 27 | #include "gc.h" 28 | #include "state.h" 29 | #endif 30 | 31 | #ifdef JANET_EV 32 | #ifdef JANET_WINDOWS 33 | #include 34 | #endif 35 | #endif 36 | 37 | /* Create new userdata */ 38 | void *janet_abstract_begin(const JanetAbstractType *atype, size_t size) { 39 | JanetAbstractHead *header = janet_gcalloc(JANET_MEMORY_NONE, 40 | sizeof(JanetAbstractHead) + size); 41 | header->size = size; 42 | header->type = atype; 43 | return (void *) & (header->data); 44 | } 45 | 46 | void *janet_abstract_end(void *x) { 47 | janet_gc_settype((void *)(janet_abstract_head(x)), JANET_MEMORY_ABSTRACT); 48 | return x; 49 | } 50 | 51 | void *janet_abstract(const JanetAbstractType *atype, size_t size) { 52 | return janet_abstract_end(janet_abstract_begin(atype, size)); 53 | } 54 | 55 | #ifdef JANET_EV 56 | 57 | /* 58 | * Threaded abstracts 59 | */ 60 | 61 | void *janet_abstract_begin_threaded(const JanetAbstractType *atype, size_t size) { 62 | JanetAbstractHead *header = janet_malloc(sizeof(JanetAbstractHead) + size); 63 | if (NULL == header) { 64 | JANET_OUT_OF_MEMORY; 65 | } 66 | janet_vm.next_collection += size + sizeof(JanetAbstractHead); 67 | header->gc.flags = JANET_MEMORY_THREADED_ABSTRACT; 68 | header->gc.data.next = NULL; /* Clear memory for address sanitizers */ 69 | header->gc.data.refcount = 1; 70 | header->size = size; 71 | header->type = atype; 72 | void *abstract = (void *) & (header->data); 73 | janet_table_put(&janet_vm.threaded_abstracts, janet_wrap_abstract(abstract), janet_wrap_false()); 74 | return abstract; 75 | } 76 | 77 | void *janet_abstract_end_threaded(void *x) { 78 | janet_gc_settype((void *)(janet_abstract_head(x)), JANET_MEMORY_THREADED_ABSTRACT); 79 | return x; 80 | } 81 | 82 | void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size) { 83 | return janet_abstract_end_threaded(janet_abstract_begin_threaded(atype, size)); 84 | } 85 | 86 | /* Refcounting primitives and sync primitives */ 87 | 88 | #ifdef JANET_WINDOWS 89 | 90 | size_t janet_os_mutex_size(void) { 91 | return sizeof(CRITICAL_SECTION); 92 | } 93 | 94 | size_t janet_os_rwlock_size(void) { 95 | return sizeof(void *); 96 | } 97 | 98 | void janet_os_mutex_init(JanetOSMutex *mutex) { 99 | InitializeCriticalSection((CRITICAL_SECTION *) mutex); 100 | } 101 | 102 | void janet_os_mutex_deinit(JanetOSMutex *mutex) { 103 | DeleteCriticalSection((CRITICAL_SECTION *) mutex); 104 | } 105 | 106 | void janet_os_mutex_lock(JanetOSMutex *mutex) { 107 | EnterCriticalSection((CRITICAL_SECTION *) mutex); 108 | } 109 | 110 | void janet_os_mutex_unlock(JanetOSMutex *mutex) { 111 | /* error handling? May want to keep counter */ 112 | LeaveCriticalSection((CRITICAL_SECTION *) mutex); 113 | } 114 | 115 | void janet_os_rwlock_init(JanetOSRWLock *rwlock) { 116 | InitializeSRWLock((PSRWLOCK) rwlock); 117 | } 118 | 119 | void janet_os_rwlock_deinit(JanetOSRWLock *rwlock) { 120 | /* no op? */ 121 | (void) rwlock; 122 | } 123 | 124 | void janet_os_rwlock_rlock(JanetOSRWLock *rwlock) { 125 | AcquireSRWLockShared((PSRWLOCK) rwlock); 126 | } 127 | 128 | void janet_os_rwlock_wlock(JanetOSRWLock *rwlock) { 129 | AcquireSRWLockExclusive((PSRWLOCK) rwlock); 130 | } 131 | 132 | void janet_os_rwlock_runlock(JanetOSRWLock *rwlock) { 133 | ReleaseSRWLockShared((PSRWLOCK) rwlock); 134 | } 135 | 136 | void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) { 137 | ReleaseSRWLockExclusive((PSRWLOCK) rwlock); 138 | } 139 | 140 | #else 141 | 142 | size_t janet_os_mutex_size(void) { 143 | return sizeof(pthread_mutex_t); 144 | } 145 | 146 | size_t janet_os_rwlock_size(void) { 147 | return sizeof(pthread_rwlock_t); 148 | } 149 | 150 | void janet_os_mutex_init(JanetOSMutex *mutex) { 151 | pthread_mutexattr_t attr; 152 | pthread_mutexattr_init(&attr); 153 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 154 | pthread_mutex_init((pthread_mutex_t *) mutex, &attr); 155 | } 156 | 157 | void janet_os_mutex_deinit(JanetOSMutex *mutex) { 158 | pthread_mutex_destroy((pthread_mutex_t *) mutex); 159 | } 160 | 161 | void janet_os_mutex_lock(JanetOSMutex *mutex) { 162 | pthread_mutex_lock((pthread_mutex_t *) mutex); 163 | } 164 | 165 | void janet_os_mutex_unlock(JanetOSMutex *mutex) { 166 | int ret = pthread_mutex_unlock((pthread_mutex_t *) mutex); 167 | if (ret) janet_panic("cannot release lock"); 168 | } 169 | 170 | void janet_os_rwlock_init(JanetOSRWLock *rwlock) { 171 | pthread_rwlock_init((pthread_rwlock_t *) rwlock, NULL); 172 | } 173 | 174 | void janet_os_rwlock_deinit(JanetOSRWLock *rwlock) { 175 | pthread_rwlock_destroy((pthread_rwlock_t *) rwlock); 176 | } 177 | 178 | void janet_os_rwlock_rlock(JanetOSRWLock *rwlock) { 179 | pthread_rwlock_rdlock((pthread_rwlock_t *) rwlock); 180 | } 181 | 182 | void janet_os_rwlock_wlock(JanetOSRWLock *rwlock) { 183 | pthread_rwlock_wrlock((pthread_rwlock_t *) rwlock); 184 | } 185 | 186 | void janet_os_rwlock_runlock(JanetOSRWLock *rwlock) { 187 | pthread_rwlock_unlock((pthread_rwlock_t *) rwlock); 188 | } 189 | 190 | void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) { 191 | pthread_rwlock_unlock((pthread_rwlock_t *) rwlock); 192 | } 193 | 194 | #endif 195 | 196 | int32_t janet_abstract_incref(void *abst) { 197 | return janet_atomic_inc(&janet_abstract_head(abst)->gc.data.refcount); 198 | } 199 | 200 | int32_t janet_abstract_decref(void *abst) { 201 | return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount); 202 | } 203 | 204 | #endif 205 | --------------------------------------------------------------------------------