1
0
mirror of https://github.com/janet-lang/janet synced 2026-04-04 05:51:27 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Calvin Rose
051a9793b9 Long way to go. 2024-04-15 17:40:32 -05:00
143 changed files with 1731 additions and 8052 deletions

View File

@@ -10,9 +10,3 @@ tasks:
gmake test
sudo gmake install
sudo gmake uninstall
- build-sanitizers: |
cd janet
CFLAGS="-g -O2 -fsanitize=address,undefined" gmake
gmake test
sudo gmake install
sudo gmake uninstall

View File

@@ -19,8 +19,3 @@ tasks:
ninja
ninja test
sudo ninja install
- meson_min: |
cd janet
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
cd build_meson_min
ninja

View File

@@ -1,4 +1,4 @@
image: openbsd/7.7
image: openbsd/latest
sources:
- https://git.sr.ht/~bakpakin/janet
packages:
@@ -10,17 +10,13 @@ tasks:
gmake
gmake test
doas gmake install
gmake test-install
doas gmake uninstall
- meson_min: |
cd janet
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
cd build_meson_min
ninja
- meson_reduced: |
cd janet
meson setup build_meson_reduced --buildtype=release -Dreduced_os=true
cd build_meson_reduced
ninja
- meson_prf: |
cd janet
meson setup build_meson_prf --buildtype=release -Dprf=true

38
.github/cosmo/build vendored
View File

@@ -1,38 +0,0 @@
#!/bin/sh
set -eux
COSMO_DIR="/sc/cosmocc"
# build x86_64
X86_64_CC="/sc/cosmocc/bin/x86_64-unknown-cosmo-cc"
X86_64_AR="/sc/cosmocc/bin/x86_64-unknown-cosmo-ar"
mkdir -p /sc/cosmocc/x86_64
make -j CC="$X86_64_CC" AR="$X86_64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
cp build/janet /sc/cosmocc/x86_64/janet
make clean
# build aarch64
AARCH64_CC="/sc/cosmocc/bin/aarch64-unknown-cosmo-cc"
AARCH64_AR="/sc/cosmocc/bin/aarch64-unknown-cosmo-ar"
mkdir -p /sc/cosmocc/aarch64
make -j CC="$AARCH64_CC" AR="$AARCH64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
cp build/janet /sc/cosmocc/aarch64/janet
make clean
# fat binary
apefat () {
OUTPUT="$1"
OLDNAME_X86_64="$(basename -- "$2")"
OLDNAME_AARCH64="$(basename -- "$3")"
TARG_FOLD="$(dirname "$OUTPUT")"
"$COSMO_DIR/bin/apelink" -l "$COSMO_DIR/bin/ape-x86_64.elf" \
-l "$COSMO_DIR/bin/ape-aarch64.elf" \
-M "$COSMO_DIR/bin/ape-m1.c" \
-o "$OUTPUT" \
"$2" \
"$3"
cp "$2" "$TARG_FOLD/$OLDNAME_X86_64.x86_64"
cp "$3" "$TARG_FOLD/$OLDNAME_AARCH64.aarch64"
}
apefat /sc/cosmocc/janet.com /sc/cosmocc/x86_64/janet /sc/cosmocc/aarch64/janet

21
.github/cosmo/setup vendored
View File

@@ -1,21 +0,0 @@
#!/bin/sh
set -e
sudo apt-get update
sudo apt-get install -y ca-certificates libssl-dev\
qemu-utils qemu-user-static\
texinfo groff\
cmake ninja-build bison zip\
pkg-config build-essential autoconf re2c
# download cosmocc
cd /sc
wget https://github.com/jart/cosmopolitan/releases/download/4.0.2/cosmocc-4.0.2.zip
mkdir -p cosmocc
cd cosmocc
unzip ../cosmocc-4.0.2.zip
# register
cd /sc/cosmocc
sudo cp ./bin/ape-x86_64.elf /usr/bin/ape
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"

View File

@@ -27,16 +27,15 @@ jobs:
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
tools: linked
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest ]
os: [ ubuntu-latest, macos-latest ]
steps:
- name: Checkout the repository
uses: actions/checkout@master
@@ -39,35 +39,6 @@ jobs:
build/c/janet.c
build/c/shell.c
release-arm:
permissions:
contents: write # for softprops/action-gh-release to create GitHub release
name: Build release binaries
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ macos-latest, macos-15-intel ]
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: Set the version
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
- name: Set the platform
run: echo "platform=$(tr '[A-Z]' '[a-z]' <<< $RUNNER_OS)" >> $GITHUB_ENV
- name: Compile the project
run: make clean && make
- name: Build the artifact
run: JANET_DIST_DIR=janet-${{ env.version }}-${{ env.platform }} make build/janet-${{ env.version }}-${{ env.platform }}-aarch64.tar.gz
- name: Draft the release
uses: softprops/action-gh-release@v1
with:
draft: true
files: |
build/*.gz
build/janet.h
build/c/janet.c
build/c/shell.c
release-windows:
permissions:
contents: write # for softprops/action-gh-release to create GitHub release
@@ -89,30 +60,3 @@ jobs:
./dist/*.zip
./*.zip
./*.msi
release-cosmo:
permissions:
contents: write # for softprops/action-gh-release to create GitHub release
name: Build release binaries for Cosmo
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: create build folder
run: |
sudo mkdir -p /sc
sudo chmod -R 0777 /sc
- name: setup Cosmopolitan Libc
run: bash ./.github/cosmo/setup
- name: Set the version
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
- name: Set the platform
run: echo "platform=cosmo" >> $GITHUB_ENV
- name: build Janet APE binary
run: bash ./.github/cosmo/build
- name: push binary to github
uses: softprops/action-gh-release@v1
with:
draft: true
files: |
/sc/cosmocc/janet.com

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest, macos-latest, macos-14, macos-15-intel ]
os: [ ubuntu-latest, macos-latest ]
steps:
- name: Checkout the repository
uses: actions/checkout@master
@@ -21,26 +21,9 @@ jobs:
- name: Test the project
run: make test
test-posix-sanitizers:
name: Build and test on POSIX systems with sanitizers turned on
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-latest ]
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: Compile the project
run: make clean && CFLAGS="-g -O2 -fsanitize=address,undefined" make
- name: Test the project
run: make test
test-windows:
name: Build and test on Windows
strategy:
matrix:
os: [ windows-latest, windows-2022 ]
runs-on: ${{ matrix.os }}
runs-on: windows-latest
steps:
- name: Checkout the repository
uses: actions/checkout@master
@@ -52,82 +35,28 @@ jobs:
- name: Test the project
shell: cmd
run: build_win test
- name: Test installer build
shell: cmd
run: build_win dist
test-windows-sanitizers:
name: Build and test on Windows with sanitizers
strategy:
matrix:
os: [ windows-latest ]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: Setup MSVC
uses: ilammy/msvc-dev-cmd@v1
- name: Build the project
shell: cmd
run: set SANITIZE=1 & build_win
- name: Test the project
shell: cmd
run: set VERBOSE=1 & build_win test
- name: Test installer build
shell: cmd
run: build_win dist
test-windows-min:
name: Build and test on Windows Minimal build
strategy:
matrix:
os: [ windows-2022 ]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: Setup MSVC
uses: ilammy/msvc-dev-cmd@v1
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install Python Dependencies
run: pip install meson ninja
- name: Build
shell: cmd
run: |
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
cd build_meson_min
ninja
test-mingw:
name: Build on Windows with Mingw
name: Build on Windows with Mingw (no test yet)
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
strategy:
matrix:
msystem: [ UCRT64, CLANG64 ]
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: Setup Mingw
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
msystem: UCRT64
update: true
install: >-
base-devel
git
gcc
- name: Build
- name: Build the project
shell: cmd
run: make -j4 CC=gcc
- name: Test
shell: cmd
run: make -j4 CC=gcc test
run: make -j4 CC=gcc JANET_NO_AMALG=1
test-mingw-linux:
name: Build and test with Mingw on Linux + Wine
@@ -144,7 +73,7 @@ jobs:
- name: Compile the project
run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine
- name: Test the project
run: make test UNAME=MINGW RUN=wine VERBOSE=1
run: make test UNAME=MINGW RUN=wine
test-arm-linux:
name: Build and test ARM32 cross compilation
@@ -157,36 +86,6 @@ jobs:
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabi qemu-user
- name: Compile the project
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc
- name: Test the project
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test VERBOSE=1
test-s390x-linux:
name: Build and test s390x in qemu
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: Enable qemu
run: docker run --privileged --rm tonistiigi/binfmt --install s390x
- name: Build and run on emulated architecture
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"
test-cosmo:
name: Test build for Cosmo
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@master
- name: create build folder
run: |
sudo mkdir -p /sc
sudo chmod -R 0777 /sc
- name: setup Cosmopolitan Libc
run: bash ./.github/cosmo/setup
- name: Set the version
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
- name: Set the platform
run: echo "platform=cosmo" >> $GITHUB_ENV
- name: build Janet APE binary
run: bash ./.github/cosmo/build
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test

9
.gitignore vendored
View File

@@ -12,7 +12,6 @@ janet
/src/include/generated/*.h
janet-*.tar.gz
dist
/tmp
# jpm lockfile
lockfile.janet
@@ -49,8 +48,6 @@ janet.wasm
# Generated files
*.gen.h
*.gen.c
*.tmp
temp.*
# Generate test files
*.out
@@ -129,9 +126,6 @@ vgcore.*
*.idb
*.pdb
# GGov
*.gcov
# Kernel Module Compile Results
*.mod*
*.cmd
@@ -140,9 +134,6 @@ Module.symvers
Mkfile.old
dkms.conf
# Coverage files
*.cov
# End of https://www.gitignore.io/api/c
# Created by https://www.gitignore.io/api/cmake

View File

@@ -1,133 +1,6 @@
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased - ???
- Documentation fixes
- ev/thread-chan deadlock bug fixed
- Re-add removed support for non-blocking net/connect on windows.
## 1.41.2 - 2026-02-18
- Fix regressions in `put` for arrays and buffers.
- Add `module/add-file-extension`
- Add `module/add-syspath`
- Fix issue with possible stack corrpution with abstract types that modify the current fiber.
- Allow use of the interpreter and garbage collection inside module entry for native modules.
## 1.41.1 - 2026-02-15
- Revert to blocking behaior of `net/connect` on windows to fix spurious errors.
- Allow overriding the loader when doing imports with the `:loader` argument.
- Allow importing modules with a path extension to do what one would expect.
- Add `find-all` argument to `module/find`
- Add :threads, :unmarshal, :compiler, and :asm sandbox flags.
- Add support for persistent REPL history with the environment variable `JANET_HISTFILE`
- Fix a number of fuzzer-found compiler bugs
- Fix windows processes launching bug with empty environment table that caused process-launch failures.
- Add `:I`, `:V`, and `:N` flags to `os/open` for more control when creating streams.
- Add `ev/go-gather` for a dynamic `ev/gather`.
- Use color in script output if color is being used in REPL output.
- Fix `varfn` macros handling of extra metadata.
- Disallow certain degenerate uses of fibers with the ev/ module.
- Add linting for unused bindings.
- Add linting for extra or wrong parameters to &named functions.
- Add `janet_optuinteger` and `janet_optuinteger64` to the C API.
- Add `cms` combinator to PEGs.
- Add `thaw-keep-keys` as a variant of thaw
- The `repl` function now respects the `*repl-prompt*` dynamic binding by default.
- Allow matching exact lengths of datastructures with the `match` macro using a dollar suffix.
- Add initial support for Plan 9. Some modules (e.g. ev) are not yet enabled.
- Allow specifying `:flycheck` for individual defines to annotate that they are safe to evaluate for flychecking.
## 1.40.1 - 2025-11-16
- Fix `JANET_REDUCED_OS` build regression caused by `os/posix-chroot`.
- Code formatting
## 1.40.0 - 2025-11-15
- Add `os/posix-chroot`
- Fix `ev/deadline` with interrupt race condition bug on Windows.
- Improve `flycheck` by allowing functions and macros to define their own flycheck behavior via the metadata `:flycheck`.
- Add `*flychecking*` dynamic binding to check if inside flycheck evalutation
- Add `gcperthread` callback for abstract types. This lets threaded abstracts have a finalizer that is called per thread, as well as a global finalizer.
- Add `JANET_DO_ERROR_*` flags to describe the return value of `janet_dobytes` and `janet_dostring`.
## 1.39.1 - 2025-08-30
- Add support for chdir in os/spawn on older macOS versions
- Expose channels properly in C API
## 1.39.0 - 2025-08-24
- Various bug fixes
- Add `net/socket`
- Add support for illumos OS
- Raise helpful errors for incorrect arguments to `import`.
- Allow configuring `JANET_THREAD_LOCAL` during builds to allow multi-threading on unknown compilers.
- Make `ffi/write` append to a buffer instead of insert at 0 by default.
- Add `os/getpid` to get the current process id.
- Add `:out` option to `os/spawn` to be able to redirect stderr to stdout with pipes.
Add `interrupt?` argument to `ev/deadline` to use VM interruptions.
## 1.38.0 - 2025-03-18
- Add `bundle/replace`
- Add CLI flags for the `bundle/` module to install and manage bundles.
- Improve `?` peg special termination behavior
- Add IEEE hex floats to grammar.
- Add buffer peg literal support
- Improve `split` peg special edge case behavior
- Add Arm64 .msi support
- Add `no-reuse` argument to `net/listen` to disable reusing server sockets
- Add `struct/rawget`
- Fix `deep=` and `deep-not=` to better handle degenerate cases with mutable table keys
- Long strings will now dedent on `\r\n` instead of just `\n`.
- Add `ev/to-file` for synchronous resource operations
- Improve `file/open` error message by including path
## 1.37.1 - 2024-12-05
- Fix meson cross compilation
- Update timeout documentation for networking APIs: timeouts raise errors and do not return nil.
- Add `janet_addtimeout_nil(double sec);` to the C API.
- Change string hashing.
- Fix string equality bug.
- Add `assertf`
- Change how JANET_PROFILE is loaded to allow more easily customizing the environment.
- Add `*repl-prompt*` dynamic binding to allow customizing the built in repl.
- Add multiple path support in the `JANET_PATH` environment variables. This lets
user more easily import modules from many directories.
- Add `nth` and `only-tags` PEG specials to select from sub-captures while
dropping the rest.
## 1.36.0 - 2024-09-07
- Improve error messages in `bundle/add*` functions.
- Add CI testing and verify tests pass on the s390x architecture.
- Save `:source-form` in environment entries when `*debug*` is set.
- Add experimental `filewatch/` module for listening to file system changes on Linux and Windows.
- Add `bundle/who-is` to query which bundle a file on disk was installed by.
- Add `geomean` function
- Add `:R` and `:W` flags to `os/pipe` to create blocking pipes on Posix and Windows systems.
These streams cannot be directly read to and written from, but can be passed to subprocesses.
- Add `array/join`
- Add `tuple/join`
- Add `bundle/add-bin` to make installing scripts easier. This also establishes a packaging convention for it.
- Fix marshalling weak tables and weak arrays.
- Fix bug in `ev/` module that could accidentally close sockets on accident.
- Expose C functions for constructing weak tables in janet.h
- Let range take non-integer values.
## 1.35.2 - 2024-06-16
- Fix some documentation typos.
- Allow using `:only` in import without quoting.
## 1.35.0 - 2024-06-15
- Add `:only` argument to `import` to allow for easier control over imported bindings.
- Add extra optional `env` argument to `eval` and `eval-string`.
- Allow naming function literals with a keyword. This allows better stacktraces for macros without
accidentally adding new bindings.
- Add `bundle/` module for managing packages within Janet. This should replace the jpm packaging
format eventually and is much simpler and amenable to more complicated builds.
- Add macros `ev/with-lock`, `ev/with-rlock`, and `ev/with-wlock` for using mutexes and rwlocks.
- Add `with-env`
- Add *module-make-env* dynamic binding
- Add buffer/format-at
- Add long form command line options for readable CLI usage
- Fix bug with `net/accept-loop` that would sometimes miss connections.
## 1.34.0 - 2024-03-22
- Add a new (split) PEG special by @ianthehenry
- Add buffer/push-* sized int and float by @pnelson
@@ -227,7 +100,7 @@ All notable changes to this project will be documented in this file.
See http://no-color.org/
- Disallow using `(splice x)` in contexts where it doesn't make sense rather than silently coercing to `x`.
Instead, raise a compiler error.
- Change the names of `:user8` and `:user9` signals to `:interrupt` and `:await`
- Change the names of `:user8` and `:user9` sigals to `:interrupt` and `:await`
- Change the names of `:user8` and `:user9` fiber statuses to `:interrupted` and `:suspended`.
- Add `ev/all-tasks` to see all currently suspended fibers.
- Add `keep-syntax` and `keep-syntax!` functions to make writing macros easier.
@@ -398,7 +271,7 @@ All notable changes to this project will be documented in this file.
- Add the ability to close channels with `ev/chan-close` (or `:close`).
- Add threaded channels with `ev/thread-chan`.
- Add `JANET_FN` and `JANET_REG` macros to more easily define C functions that export their source mapping information.
- Add `janet_interpreter_interrupt` and `janet_loop1_interrupt` to interrupt the interpreter while running.
- Add `janet_interpreter_interupt` and `janet_loop1_interrupt` to interrupt the interpreter while running.
- Add `table/clear`
- Add build option to disable the threading library without disabling all threads.
- Remove JPM from the main Janet distribution. Instead, JPM must be installed
@@ -452,7 +325,7 @@ saving and restoring the entire VM state.
- Sort keys in pretty printing output.
## 1.15.3 - 2021-02-28
- Fix a fiber bug that occurred in deeply nested fibers
- Fix a fiber bug that occured in deeply nested fibers
- Add `unref` combinator to pegs.
- Small docstring changes.
@@ -602,13 +475,13 @@ saving and restoring the entire VM state.
- Add `symbol/slice`
- Add `keyword/slice`
- Allow cross compilation with Makefile.
- Change `compare-primitive` to `cmp` and make it more efficient.
- Change `compare-primitve` to `cmp` and make it more efficient.
- Add `reverse!` for reversing an array or buffer in place.
- `janet_dobytes` and `janet_dostring` return parse errors in \*out
- Add `repeat` macro for iterating something n times.
- Add `eachy` (each yield) macro for iterating a fiber.
- Fix `:generate` verb in loop macro to accept non symbols as bindings.
- Add `:h`, `:h+`, and `:h*` in `default-peg-grammar` for hexadecimal digits.
- Add `:h`, `:h+`, and `:h*` in `default-peg-grammar` for hexidecimal digits.
- Fix `%j` formatter to print numbers precisely (using the `%.17g` format string to printf).
## 1.10.1 - 2020-06-18

View File

@@ -37,12 +37,6 @@ may require changes before being merged.
do this indentation, or approximate as close as possible. There is a janet formatter
in [spork](https://github.com/janet-lang/spork.git) that can be used to format code as well.
Bot pull requests will not be accepted, and anonymous submissions, including
new accounts, unknown emails, and first time contributors will be subjected
to greater scrutiny and code reivew. Automatically generated and filed bug
reports MAY be ok, if they are of consistent and good quality, such as
OSSFuzz or well constructed CI pipelines.
## C style
For changes to the VM and Core code, you will probably need to know C. Janet is programmed with
@@ -67,7 +61,7 @@ ensure a consistent code style for C.
## Janet style
All janet code in the project should be formatted similar to the code in src/boot/boot.janet.
All janet code in the project should be formatted similar to the code in core.janet.
The auto formatting from janet.vim will work well.
## Typo Fixing and One-Line changes
@@ -96,18 +90,3 @@ timely manner. In short, if you want extra functionality now, then build it.
* Include a good description of the problem that is being solved
* Include descriptions of potential solutions if you have some in mind.
## LLMs, Tool Usage, and Transparency
All usage of Large Language Models (LLMs), Neural Networks, "AI" tools, and
other tools such as software fuzzers or static analyzers must be disclosed.
This applies to pull requests, email patches, bug reports, and any other
meaningful contribution to Janet's source code. Please also refrain from using
generative AI for code that will be embedded in the Janet runtime, which include
all C source files as well as boot.janet. All code should be well
and completely understood by the human author, including test cases. Large and
obviously AI-driven changes will be rejected. Be mindful and transparent on the
copyright implications of any submitted code. We will use discretion when
accepting generated test cases for bug reproductions, one-line bug
fixes, or typo fixes. Often, these can be trivially rewritten by a human to
avoid the problem.

View File

@@ -1,4 +1,4 @@
Copyright (c) 2026 Calvin Rose and contributors
Copyright (c) 2023 Calvin Rose and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2026 Calvin Rose
# Copyright (c) 2023 Calvin Rose
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
@@ -43,11 +43,9 @@ JANET_DIST_DIR?=janet-dist
JANET_BOOT_FLAGS:=. JANET_PATH '$(JANET_PATH)'
JANET_TARGET_OBJECTS=build/janet.o build/shell.o
JPM_TAG?=master
SPORK_TAG?=master
HAS_SHARED?=1
DEBUGGER=gdb
SONAME_SETTER=-Wl,-soname,
STRIPFLAGS=-x -S
# For cross compilation
HOSTCC?=$(CC)
@@ -55,7 +53,7 @@ HOSTAR?=$(AR)
# Symbols are (optionally) removed later, keep -g as default!
CFLAGS?=-O2 -g
LDFLAGS?=-rdynamic
LIBJANET_LDFLAGS?=$(LDFLAGS)
LIBJANET_LDFLAGS?=$(LD_FLAGS)
RUN:=$(RUN)
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
@@ -80,12 +78,6 @@ ifeq ($(UNAME), Darwin)
LDCONFIG:=true
else ifeq ($(UNAME), Linux)
CLIBS:=$(CLIBS) -lrt -ldl
else ifeq ($(UNAME), SunOS)
BUILD_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
BOOT_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
CLIBS:=-lsocket -lm
STRIPFLAGS=-x
LDCONFIG:=false
endif
# For other unix likes, add flags here!
@@ -101,18 +93,12 @@ endif
endif
# Mingw
MINGW_COMPILER=
ifeq ($(findstring MINGW,$(UNAME)), MINGW)
MINGW_COMPILER=gcc
CLIBS:=-lws2_32 -lpsapi -lwsock32
LDFLAGS:=-Wl,--out-implib,$(JANET_IMPORT_LIB)
LIBJANET_LDFLAGS:=-Wl,--out-implib,$(JANET_LIBRARY_IMPORT_LIB)
JANET_TARGET:=$(JANET_TARGET).exe
JANET_BOOT:=$(JANET_BOOT).exe
COMPILER_VERSION:=$(shell $(CC) --version)
ifeq ($(findstring clang,$(COMPILER_VERSION)), clang)
MINGW_COMPILER=clang
endif
endif
@@ -153,7 +139,6 @@ JANET_CORE_SOURCES=src/core/abstract.c \
src/core/ev.c \
src/core/ffi.c \
src/core/fiber.c \
src/core/filewatch.c \
src/core/gc.c \
src/core/inttypes.c \
src/core/io.c \
@@ -219,14 +204,9 @@ build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
########################
ifeq ($(UNAME), Darwin)
SONAME=libjanet.1.41.dylib
SONAME=libjanet.1.34.dylib
else
SONAME=libjanet.so.1.41
endif
ifeq ($(MINGW_COMPILER), clang)
SONAME=
SONAME_SETTER=
SONAME=libjanet.so.1.34
endif
build/c/shell.c: src/mainclient/shell.c
@@ -260,7 +240,6 @@ $(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
# Testing assumes HOSTCC=CC
TEST_SCRIPTS=$(wildcard test/suite*.janet)
EXAMPLE_SCRIPTS=$(wildcard examples/*.janet)
repl: $(JANET_TARGET)
$(RUN) ./$(JANET_TARGET)
@@ -268,26 +247,21 @@ repl: $(JANET_TARGET)
debug: $(JANET_TARGET)
$(DEBUGGER) ./$(JANET_TARGET)
VALGRIND_COMMAND=$(RUN) valgrind --leak-check=full --quiet
CALLGRIND_COMMAND=$(RUN) valgrind --tool=callgrind
VALGRIND_COMMAND=valgrind --leak-check=full --quiet
valgrind: $(JANET_TARGET)
$(VALGRIND_COMMAND) ./$(JANET_TARGET)
test: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
test: $(JANET_TARGET) $(TEST_PROGRAMS)
for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done
valtest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
callgrind: $(JANET_TARGET)
$(CALLGRIND_COMMAND) ./$(JANET_TARGET)
calltest: $(JANET_TARGET) $(TEST_SCRIPTS) $(EXAMPLE_SCRIPTS)
for f in test/suite*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
for f in examples/*.janet; do $(CALLGRIND_COMMAND) ./$(JANET_TARGET) -k "$$f"; done
for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done
########################
##### Distribution #####
@@ -301,7 +275,7 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
README.md build/c/janet.c build/c/shell.c
mkdir -p build/$(JANET_DIST_DIR)/bin
cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/
strip $(STRIPFLAGS) 'build/$(JANET_DIST_DIR)/bin/janet'
strip -x -S 'build/$(JANET_DIST_DIR)/bin/janet'
mkdir -p build/$(JANET_DIST_DIR)/include
cp build/janet.h build/$(JANET_DIST_DIR)/include/
mkdir -p build/$(JANET_DIST_DIR)/lib/
@@ -346,23 +320,22 @@ build/janet.pc: $(JANET_TARGET)
echo 'Libs.private: $(CLIBS)' >> $@
install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h
$(eval JANET_VERSION := $(shell $(JANET_TARGET) -e '(print janet/version)'))
mkdir -p '$(DESTDIR)$(BINDIR)'
cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
strip $(STRIPFLAGS) '$(DESTDIR)$(BINDIR)/janet'
strip -x -S '$(DESTDIR)$(BINDIR)/janet'
mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
ln -sf ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h'
mkdir -p '$(DESTDIR)$(JANET_PATH)'
mkdir -p '$(DESTDIR)$(LIBDIR)'
if test $(UNAME) = Darwin ; then \
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.$(JANET_VERSION).dylib' ; \
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.$(shell $(JANET_TARGET) -e '(print janet/version)').dylib' ; \
ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.dylib' ; \
ln -sf libjanet.$(JANET_VERSION).dylib $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
ln -sf libjanet.$(shell $(JANET_TARGET) -e '(print janet/version)').dylib $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
else \
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.so.$(JANET_VERSION)' ; \
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')' ; \
ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.so' ; \
ln -sf libjanet.so.$(JANET_VERSION) $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
fi
cp $(JANET_STATIC_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.a'
mkdir -p '$(DESTDIR)$(JANET_MANPATH)'
@@ -385,12 +358,6 @@ install-jpm-git: $(JANET_TARGET)
JANET_LIBPATH='$(LIBDIR)' \
$(RUN) ../../$(JANET_TARGET) ./bootstrap.janet
install-spork-git: $(JANET_TARGET)
mkdir -p build
rm -rf build/spork
git clone --depth=1 --branch='$(SPORK_TAG)' https://github.com/janet-lang/spork.git build/spork
$(JANET_TARGET) -e '(bundle/install "build/spork")'
uninstall:
-rm '$(DESTDIR)$(BINDIR)/janet'
-rm -rf '$(DESTDIR)$(INCLUDEDIR)/janet'
@@ -419,6 +386,9 @@ clean:
-rm -rf build vgcore.* callgrind.*
-rm -rf test/install/build test/install/modpath
test-install:
echo "JPM has been removed from default install."
help:
@echo
@echo 'Janet: A Dynamic Language & Bytecode VM'
@@ -430,8 +400,7 @@ help:
@echo ' make test Test a built Janet'
@echo ' make valgrind Assess Janet with Valgrind'
@echo ' make callgrind Assess Janet with Valgrind, using Callgrind'
@echo ' make valtest Run the test suite and examples with Valgrind to check for memory leaks'
@echo ' make calltest Run the test suite and examples with Callgrind'
@echo ' make valtest Run the test suite with Valgrind to check for memory leaks'
@echo ' make dist Create a distribution tarball'
@echo ' make docs Generate documentation'
@echo ' make debug Run janet with GDB or LLDB'
@@ -441,9 +410,6 @@ help:
@echo " make format Format Janet's own source files"
@echo ' make grammar Generate a TextMate language grammar'
@echo
@echo ' make install-jpm-git Install jpm into the current filesystem'
@echo ' make install-spork-git Install spork into the current filesystem'
@echo
.PHONY: clean install install-jpm-git install-spork-git repl debug valgrind test \
valtest callgrind callgrind-test dist uninstall docs grammar format help compile-commands
.PHONY: clean install repl debug valgrind test \
valtest dist uninstall docs grammar format help compile-commands

View File

@@ -1,4 +1,4 @@
[![Join the chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://janet.zulipchat.com)
[![Join the chat](https://badges.gitter.im/janet-language/community.svg)](https://gitter.im/janet-language/community)
&nbsp;
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/commits/master/freebsd.yml.svg)](https://builds.sr.ht/~bakpakin/janet/commits/master/freebsd.yml?)
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/commits/master/openbsd.yml.svg)](https://builds.sr.ht/~bakpakin/janet/commits/master/openbsd.yml?)
@@ -18,6 +18,9 @@ to run script files. This client program is separate from the core runtime, so
Janet can be embedded in other programs. Try Janet in your browser at
<https://janet-lang.org>.
If you'd like to financially support the ongoing development of Janet, consider
[sponsoring its primary author](https://github.com/sponsors/bakpakin) through GitHub.
<br>
## Examples
@@ -148,40 +151,8 @@ You can get the source on [GitHub](https://github.com/janet-lang/janet) or
[SourceHut](https://git.sr.ht/~bakpakin/janet). While the GitHub repo is the official repo,
the SourceHut mirror is actively maintained.
## Spork and JPM
Spork and JPM are two companion projects to Janet. They are optional, especially in an embedding use case.
Spork is a collection of common utility modules, and several packaged scripts
like `janet-format` for code formatting, `janet-netrepl` for a socket-based
REPL, and `janet-pm` for a comprehensive Janet project manager tool. The
modules in `spork` are less stable than the interfaces in core Janet, although
we try to prevent breaking changes to existing modules, with a preference to
add new modules and functions. Spork requires a C compiler to build and install
various extenstion components such as miniz and JSON utilities. Many spork
sub-modules, for example spork/path, are independent and can be manually
vendored in programmer projects without fully installing spork.
When install Spork, scripts will be installed to $JANET_PATH/bin/ on POSIX systems by default.
This likely needs to be added to the path to use these scripts.
JPM is the older, more opinionated, project manager tool, which has it's pros
and cons. It does not require a C compiler to build and install, but is less
flexible and is not receiving many changes and improvements going forward. It
may also be harder to configure correctly on new systems. In that sense, it may
be more stable.
JPM will install to /usr/local/bin/ on posix systems by default, which may or
may not be on your PATH.
## Building
When building from source, for stability, please use the latest tagged release. For
example, run `git checkout $(git describe --tags --abbrev=0)` after cloning but
before building. For the latest development, build directly on the master
branch. The master branch is not-necessarily stable as most Janet development
happens directly on the master branch.
### macOS and Unix-like
The Makefile is non-portable and requires GNU-flavored make.
@@ -192,34 +163,11 @@ make
make test
make repl
make install
make install-spork-git # optional
make install-jpm-git # optional
make install-jpm-git
```
Find out more about the available make targets by running `make help`.
### Alpine Linux
To build a statically-linked build of Janet, Alpine Linux + MUSL is a good
combination. Janet can also be built inside a docker container or similar in
this manner. This is a great way to try Janet without committing to a full
install or needing to customize the default install.
```sh
docker run -it --rm alpine /bin/ash
$ apk add make gcc musl-dev git
$ git clone https://github.com/janet-lang/janet.git
$ cd janet
$ make -j10
$ make test
$ make install
$ make install-spork-git # optional
$ make install-jpm-git # optional
```
Note that for a true statically-linked binary with MUSL, one needs to add `-static` to the Makefile flags. This
will also disable runtime loading of native modules (plugins) as well as the FFI.
### 32-bit Haiku
32-bit Haiku build instructions are the same as the UNIX-like build instructions,
@@ -231,8 +179,7 @@ make CC=gcc-x86
make test
make repl
make install
make install-spork-git # optional
make install-jpm-git # optional
make install-jpm-git
```
### FreeBSD
@@ -246,8 +193,7 @@ gmake
gmake test
gmake repl
gmake install
gmake install-spork-git # optional
gmake install-jpm-git # optional
gmake install-jpm-git
```
### NetBSD
@@ -255,10 +201,6 @@ gmake install-jpm-git # optional
NetBSD build instructions are the same as the FreeBSD build instructions.
Alternatively, install the package directly with `pkgin install janet`.
### illumos
Building on illumos is exactly the same as building on FreeBSD.
### Windows
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#).
@@ -268,7 +210,7 @@ Building on illumos is exactly the same as building on FreeBSD.
To build an `.msi` installer executable, in addition to the above steps, you will have to:
5. Install, or otherwise add to your PATH the [WiX 3.14 Toolset](https://github.com/wixtoolset/wix3/releases).
5. Install, or otherwise add to your PATH the [WiX 3.11 Toolset](https://github.com/wixtoolset/wix3/releases).
6. Run `build_win dist`.
Now you should have an `.msi`. You can run `build_win install` to install the `.msi`, or execute the file itself.
@@ -311,10 +253,8 @@ Emacs, and Atom each have syntax packages for the Janet language, though.
## Installation
If you just want to try out the language, you don't need to install anything.
In this case you can also move the `janet` executable wherever you want on
your system and run it. However, for a fuller setup, please see the
[Introduction](https://janet-lang.org/docs/index.html) for more details.
See the [Introduction](https://janet-lang.org/docs/index.html) for more details. If you just want
to try out the language, you don't need to install anything. You can also move the `janet` executable wherever you want on your system and run it.
## Usage
@@ -362,8 +302,8 @@ If installed, you can also run `man janet` to get usage information.
## Embedding
Janet can be embedded in a host program very easily. The normal build
will create a file `build/c/janet.c`, a C source code file that
that contains the amalgamated source to Janet. This file, along with
will create a file `build/janet.c`, which is a single C file
that contains all the source to Janet. This file, along with
`src/include/janet.h` and `src/conf/janetconf.h`, can be dragged into any C
project and compiled into it. Janet should be compiled with `-std=c99`
on most compilers, and will need to be linked to the math library, `-lm`, and
@@ -375,7 +315,8 @@ See the [Embedding Section](https://janet-lang.org/capi/embedding.html) on the w
## Discussion
Feel free to ask questions and join the discussion on the [Janet Zulip Instance](https://janet.zulipchat.com/)
Feel free to ask questions and join the discussion on the [Janet Gitter channel](https://gitter.im/janet-language/community).
Gitter provides Matrix and IRC bridges as well.
## FAQ

View File

@@ -23,17 +23,7 @@
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi
@rem set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
if DEFINED CLANG (
@set COMPILER=clang-cl.exe
) else (
@set COMPILER=cl.exe
)
if DEFINED SANITIZE (
@set "SANITIZERS=/fsanitize=address"
) else (
@set "SANITIZERS="
)
@set JANET_COMPILE=%COMPILER% /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD %SANITIZERS%
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
@set JANET_LINK=link /nologo
@set JANET_LINK_STATIC=lib /nologo
@@ -51,35 +41,32 @@ if not exist build\boot mkdir build\boot
@rem Build the bootstrap interpreter
for %%f in (src\core\*.c) do (
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
@if errorlevel 1 goto :BUILDFAIL
@if not errorlevel 0 goto :BUILDFAIL
)
for %%f in (src\boot\*.c) do (
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
@if errorlevel 1 goto :BUILDFAIL
@if not errorlevel 0 goto :BUILDFAIL
)
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
@if errorlevel 1 goto :BUILDFAIL
@rem note that there is no default syspath being baked in
@if not errorlevel 0 goto :BUILDFAIL
build\janet_boot . > build\c\janet.c
@if errorlevel 1 goto :BUILDFAIL
@rem Build the sources
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
@if errorlevel 1 goto :BUILDFAIL
@if not errorlevel 0 goto :BUILDFAIL
%JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c
@if errorlevel 1 goto :BUILDFAIL
@if not errorlevel 0 goto :BUILDFAIL
@rem Build the resources
rc /nologo /fobuild\janet_win.res janet_win.rc
@if errorlevel 1 goto :BUILDFAIL
@rem Link everything to main client
%JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res
@if errorlevel 1 goto :BUILDFAIL
@if not errorlevel 0 goto :BUILDFAIL
@rem Build static library (libjanet.lib)
%JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj
@if errorlevel 1 goto :BUILDFAIL
@if not errorlevel 0 goto :BUILDFAIL
echo === Successfully built janet.exe for Windows ===
echo === Run 'build_win test' to run tests. ==
@@ -102,7 +89,7 @@ exit /b 0
@rem Clean build artifacts
:CLEAN
del *.exe *.lib *.exp *.msi *.wixpdb
del *.exe *.lib *.exp
rd /s /q build
if exist dist (
rd /s /q dist
@@ -113,7 +100,7 @@ exit /b 0
:TEST
for %%f in (test/suite*.janet) do (
janet.exe test\%%f
@if errorlevel 1 goto TESTFAIL
@if not errorlevel 0 goto TESTFAIL
)
exit /b 0
@@ -132,6 +119,7 @@ copy README.md dist\README.md
copy janet.lib dist\janet.lib
copy janet.exp dist\janet.exp
copy janet.def dist\janet.def
janet.exe tools\patch-header.janet src\include\janet.h src\conf\janetconf.h build\janet.h
copy build\janet.h dist\janet.h
@@ -149,18 +137,11 @@ if defined APPVEYOR_REPO_TAG_NAME (
set RELEASE_VERSION=%JANET_VERSION%
)
if defined CI (
set WIXBIN="%WIX%bin\"
echo WIXBIN = %WIXBIN%
set WIXBIN="c:\Program Files (x86)\WiX Toolset v3.11\bin\"
) else (
set WIXBIN=
)
set WIXARCH=%BUILDARCH%
if "%WIXARCH%"=="aarch64" (
set WIXARCH=arm64
)
%WIXBIN%candle.exe tools\msi\janet.wxs -arch %WIXARCH% -out build\
%WIXBIN%candle.exe tools\msi\janet.wxs -arch %BUILDARCH% -out build\
%WIXBIN%light.exe "-sice:ICE38" -b tools\msi -ext WixUIExtension build\janet.wixobj -out janet-%RELEASE_VERSION%-windows-%BUILDARCH%-installer.msi
exit /b 0

View File

@@ -1,6 +0,0 @@
# Linux only - uses abstract unix domain sockets
(ev/spawn (net/server :unix "@abc123" (fn [conn] (print (:read conn 1024)) (:close conn))))
(ev/sleep 1)
(def s (net/connect :unix "@abc123" :stream))
(:write s "hello")
(:close s)

View File

@@ -1,35 +0,0 @@
(def conmap @{})
(defn broadcast [em msg]
(eachk par conmap
(if (not= par em)
(if-let [tar (get conmap par)]
(net/write tar (string/format "[%s]:%s" em msg))))))
(defn handler
[connection]
(print "connection: " connection)
(net/write connection "Whats your name?\n")
(def name (string/trim (string (ev/read connection 100))))
(print name " connected")
(if (get conmap name)
(do
(net/write connection "Name already taken!")
(:close connection))
(do
(put conmap name connection)
(net/write connection (string/format "Welcome %s\n" name))
(defer (do
(put conmap name nil)
(:close connection))
(while (def msg (ev/read connection 100))
(broadcast name (string msg)))
(print name " disconnected")))))
(defn main [&]
(printf "STARTING SERVER...")
(flush)
(def my-server (net/listen "127.0.0.1" "8000"))
(forever
(def connection (net/accept my-server))
(ev/call handler connection)))

View File

@@ -132,7 +132,7 @@
"Go to the next breakpoint."
[&opt n]
(var res nil)
(repeat (or n 1)
(for i 0 (or n 1)
(set res (resume (.fiber))))
res)
@@ -146,6 +146,6 @@
"Execute the next n instructions."
[&opt n]
(var res nil)
(repeat (or n 1)
(for i 0 (or n 1)
(set res (debug/step (.fiber))))
res)

View File

@@ -35,11 +35,6 @@ typedef struct {
int c;
} intintint;
typedef struct {
uint64_t a;
uint64_t b;
} uint64pair;
typedef struct {
int64_t a;
int64_t b;
@@ -208,20 +203,3 @@ EXPORTER
int sixints_fn_3(SixInts s, int x) {
return x + s.u + s.v + s.w + s.x + s.y + s.z;
}
EXPORTER
intint stack_spill_fn(uint8_t a, uint8_t b, uint8_t c, uint8_t d,
uint8_t e, uint8_t f, uint8_t g, uint8_t h,
float i, float j, float k, float l,
float m, float n, float o, float p,
float s1, int8_t s2, uint8_t s3, double s4, uint8_t s5, intint s6) {
return (intint) {
(a | b | c | d | e | f | g | h) + (i + j + k + l + m + n + o + p),
s1 *s6.a + s2 *s6.b + s3 *s4 *s5
};
}
EXPORTER
double stack_spill_fn_2(uint64pair a, uint64pair b, uint64pair c, int8_t d, uint64pair e, int8_t f) {
return (double)(a.a * c.a + a.b * c.b + b.a * e.a) * f - (double)(b.b * e.b) + d;
}

View File

@@ -8,13 +8,11 @@
(if is-windows
(os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px)
(os/execute ["cc" ffi/source-loc "-g" "-shared" "-o" ffi/loc] :px))
(os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px))
(ffi/context ffi/loc)
(def intint (ffi/struct :int :int))
(def intintint (ffi/struct :int :int :int))
(def uint64pair (ffi/struct :u64 :u64))
(def big (ffi/struct :s64 :s64 :s64))
(def split (ffi/struct :int :int :float :float))
(def split-flip (ffi/struct :float :float :int :int))
@@ -57,14 +55,6 @@
(ffi/defbind sixints-fn six-ints [])
(ffi/defbind sixints-fn-2 :int [x :int s six-ints])
(ffi/defbind sixints-fn-3 :int [s six-ints x :int])
(ffi/defbind stack-spill-fn intint
[a :u8 b :u8 c :u8 d :u8
e :u8 f :u8 g :u8 h :u8
i :float j :float k :float l :float
m :float n :float o :float p :float
s1 :float s2 :s8 s3 :u8 s4 :double s5 :u8 s6 intint])
(ffi/defbind stack-spill-fn-2 :double [a uint64pair b uint64pair c uint64pair d :s8 e uint64pair f :s8])
(ffi/defbind-alias int-fn int-fn-aliased :int [a :int b :int])
#
# Struct reading and writing
@@ -129,7 +119,6 @@
(tracev (return-struct 42))
(tracev (double-lots 1 2 3 4 5 6 700 800 9 10))
(tracev (struct-big 11 99.5))
(tracev (int-fn-aliased 10 20))
(assert (= [10 10 12 12] (split-ret-fn 10 12)))
(assert (= [12 12 10 10] (split-flip-ret-fn 10 12)))
@@ -141,10 +130,5 @@
(assert (= 21 (math/round (double-many 1 2 3 4 5 6.01))))
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
(assert (= 204 (float-fn 8 4 17)))
(assert (= [0 38534415] (stack-spill-fn
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
1.5 -32 196 65536.5 3 [-15 32])))
(assert (= -2806 (stack-spill-fn-2 [2 3] [5 7] [9 11] -19 [13 17] -23)))
(print "Done.")

View File

@@ -2,7 +2,7 @@
(def counts (seq [_ :range [0 100]] 0))
(repeat 1000000
(for i 0 1000000
(let [x (math/random)
intrange (math/floor (* 100 x))
oldcount (counts intrange)]

View File

@@ -1,3 +0,0 @@
@{
:name "sample-bad-bundle1"
}

View File

@@ -1 +0,0 @@
(def abc 123)

View File

@@ -1,7 +0,0 @@
(defn install
[manifest &]
(bundle/add-file manifest "badmod.janet"))
(defn check
[&]
(error "Check failed!"))

View File

@@ -1,3 +0,0 @@
@{
:name "sample-bad-bundle2"
}

View File

@@ -1 +0,0 @@
(defn fun [x] (range x))

View File

@@ -1,3 +0,0 @@
(defn install
[manifest &]
(bundle/add-file manifest "aliases-mod.janet"))

View File

@@ -1,4 +0,0 @@
@{
:name "sample-bundle-aliases"
:dependencies ["sample-dep1" "sample-dep2"]
}

View File

@@ -1,4 +0,0 @@
@{
:name "sample-bundle"
:dependencies ["sample-dep1" "sample-dep2"]
}

View File

@@ -1,3 +0,0 @@
(defn install
[manifest &]
(bundle/add-file manifest "mymod.janet"))

View File

@@ -1,7 +0,0 @@
(import dep1)
(import dep2)
(defn myfn
[x]
(def y (dep2/function x))
(dep1/function y))

View File

@@ -1,4 +0,0 @@
@{
:name "sample-dep1"
:dependencies [{:name "sample-dep2"}]
}

View File

@@ -1,3 +0,0 @@
(defn install
[manifest &]
(bundle/add-file manifest "dep1.janet"))

View File

@@ -1,3 +0,0 @@
(defn function
[x]
(+ x x))

View File

@@ -1,3 +0,0 @@
@{
:name "sample-dep2"
}

View File

@@ -1,3 +0,0 @@
(defn install
[manifest &]
(bundle/add-file manifest "dep2.janet"))

View File

@@ -1,3 +0,0 @@
(defn function
[x]
(* x x))

View File

@@ -7,7 +7,7 @@
(ev/give chan (math/random))
(ev/give chan (math/random))
(ev/sleep 0.5)
(repeat 10
(for i 0 10
(print "giving to channel...")
(ev/give chan (math/random))
(ev/sleep 1))

12
janet.1
View File

@@ -156,7 +156,7 @@ Shows the version text and exits immediately.
.TP
.BR \-s
Read raw input from stdin and forgo fancy input, which includes prompt history and other readline-like features.
Read raw input from stdin and forgo prompt history and other readline-like features.
.TP
.BR \-e\ code
@@ -214,7 +214,7 @@ Don't execute a script, only compile it to check for errors. Useful for linting
.BR \-m\ syspath
Set the dynamic binding :syspath to the string syspath so that Janet will load system modules
from a directory different than the default. The default is set when Janet is built, and defaults to
/usr/local/lib/janet on Linux/Posix. On Windows, there is no default value. This option supersedes JANET_PATH.
/usr/local/lib/janet on Linux/Posix, and C:/Janet/Library on Windows. This option supersedes JANET_PATH.
.TP
.BR \-c\ source\ output
@@ -255,7 +255,7 @@ and then arguments to the script.
.RS
The location to look for Janet libraries. This is the only environment variable Janet needs to
find native and source code modules. If no JANET_PATH is set, Janet will look in
the default location set at compile time. This should be a colon-separated list of directory names on Linux/Posix, and a semicolon-separated list on Windows. Note that a typical setup (i.e. not NixOS / Guix) will only use a single directory.
the default location set at compile time.
.RE
.B JANET_PROFILE
@@ -272,12 +272,6 @@ This variable does nothing in the default configuration of Janet, as PRF is disa
cannot be defined for this variable to have an effect.
.RE
.B JANET_HISTFILE
.RS
A file location to use for the default shell's REPL history when using fancy input. This relative path is where commands are persisted between sessions.
If unset, no repl history well be used. Does not work with the -s flag where fancy input is disabled.
.RE
.B NO_COLOR
.RS
Turn off color by default in the repl and in the error handler of scripts. This can be changed at runtime

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2026 Calvin Rose and contributors
# Copyright (c) 2023 Calvin Rose and contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
@@ -20,34 +20,16 @@
project('janet', 'c',
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.41.3')
version : '1.34.0')
# Global settings
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
header_path = join_paths(get_option('prefix'), get_option('includedir'), 'janet')
# Compilers
# Link math library on all systems
cc = meson.get_compiler('c')
native_cc = meson.get_compiler('c', native : true)
# Native deps
native_m_dep = native_cc.find_library('m', required : false)
native_dl_dep = native_cc.find_library('dl', required : false)
native_android_spawn_dep = native_cc.find_library('android-spawn', required : false)
native_thread_dep = dependency('threads', native : true)
# Deps
m_dep = cc.find_library('m', required : false)
dl_dep = cc.find_library('dl', required : false)
# for MINGW/MSYS2
native_ws2_dep = native_cc.find_library('ws2_32', required: false)
native_psapi_dep = native_cc.find_library('psapi', required: false)
native_wsock_dep = native_cc.find_library('wsock32', required: false)
ws2_dep = cc.find_library('ws2_32', required: false)
psapi_dep = cc.find_library('psapi', required: false)
wsock_dep = cc.find_library('wsock32', required: false)
android_spawn_dep = cc.find_library('android-spawn', required : false)
thread_dep = dependency('threads')
@@ -97,7 +79,6 @@ conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue'))
conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt'))
conf.set('JANET_NO_FFI', not get_option('ffi'))
conf.set('JANET_NO_FFI_JIT', not get_option('ffi_jit'))
conf.set('JANET_NO_FILEWATCH', not get_option('filewatch'))
conf.set('JANET_NO_CRYPTORAND', not get_option('cryptorand'))
if get_option('os_name') != ''
conf.set('JANET_OS_NAME', get_option('os_name'))
@@ -105,9 +86,6 @@ endif
if get_option('arch_name') != ''
conf.set('JANET_ARCH_NAME', get_option('arch_name'))
endif
if get_option('thread_local_prefix') != ''
conf.set('JANET_THREAD_LOCAL', get_option('thread_local_prefix'))
endif
jconf = configure_file(output : 'janetconf.h',
configuration : conf)
@@ -144,7 +122,6 @@ core_src = [
'src/core/ev.c',
'src/core/ffi.c',
'src/core/fiber.c',
'src/core/filewatch.c',
'src/core/gc.c',
'src/core/inttypes.c',
'src/core/io.c',
@@ -185,18 +162,11 @@ mainclient_src = [
'src/mainclient/shell.c'
]
janet_dependencies = [m_dep, dl_dep, android_spawn_dep, ws2_dep, psapi_dep, wsock_dep]
janet_native_dependencies = [native_m_dep, native_dl_dep, native_android_spawn_dep, native_ws2_dep, native_psapi_dep, native_wsock_dep]
if not get_option('single_threaded')
janet_dependencies += thread_dep
janet_native_dependencies += native_thread_dep
endif
# Build boot binary
janet_boot = executable('janet-boot', core_src, boot_src,
include_directories : incdir,
c_args : '-DJANET_BOOTSTRAP',
dependencies : janet_native_dependencies,
dependencies : [m_dep, dl_dep, thread_dep, android_spawn_dep],
native : true)
# Build janet.c
@@ -209,6 +179,11 @@ janetc = custom_target('janetc',
'JANET_PATH', janet_path
])
janet_dependencies = [m_dep, dl_dep, android_spawn_dep]
if not get_option('single_threaded')
janet_dependencies += thread_dep
endif
# Allow building with no shared library
if cc.has_argument('-fvisibility=hidden')
lib_cflags = ['-fvisibility=hidden']
@@ -254,7 +229,7 @@ if meson.is_cross_build()
endif
janet_nativeclient = executable('janet-native', janetc, mainclient_src,
include_directories : incdir,
dependencies : janet_native_dependencies,
dependencies : janet_dependencies,
c_args : extra_native_cflags,
native : true)
else
@@ -274,21 +249,17 @@ test_files = [
'test/suite-asm.janet',
'test/suite-boot.janet',
'test/suite-buffer.janet',
'test/suite-bundle.janet',
'test/suite-capi.janet',
'test/suite-cfuns.janet',
'test/suite-compile.janet',
'test/suite-corelib.janet',
'test/suite-debug.janet',
'test/suite-ev.janet',
'test/suite-ev2.janet',
'test/suite-ffi.janet',
'test/suite-filewatch.janet',
'test/suite-inttypes.janet',
'test/suite-io.janet',
'test/suite-marsh.janet',
'test/suite-math.janet',
'test/suite-net.janet',
'test/suite-os.janet',
'test/suite-parse.janet',
'test/suite-peg.janet',
@@ -299,7 +270,6 @@ test_files = [
'test/suite-struct.janet',
'test/suite-symcache.janet',
'test/suite-table.janet',
'test/suite-tuple.janet',
'test/suite-unknown.janet',
'test/suite-value.janet',
'test/suite-vm.janet'

View File

@@ -22,7 +22,6 @@ option('kqueue', type : 'boolean', value : true)
option('interpreter_interrupt', type : 'boolean', value : true)
option('ffi', type : 'boolean', value : true)
option('ffi_jit', type : 'boolean', value : true)
option('filewatch', type : 'boolean', value : true)
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200)
@@ -30,7 +29,6 @@ option('max_macro_expand', type : 'integer', min : 1, max : 8000, value : 200)
option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7fffffff)
option('arch_name', type : 'string', value: '')
option('thread_local_prefix', type : 'string', value: '')
option('os_name', type : 'string', value: '')
option('shared', type : 'boolean', value: true)
option('cryptorand', type : 'boolean', value: true)

View File

@@ -1,49 +0,0 @@
</$objtype/mkfile
TARG=janet
HFILES=src/include/janet.h src/conf/janetconf.h
JANET_PATH=/sys/lib/janet
BIN=/$objtype/bin/
DISABLED=EV NET ASSEMBLER FFI UTC_MKTIME REALPATH DYNAMIC_MODULES THREADS SYMLINKS LOCALES UMASK SPAWN
JANET_CONFIG=JANET_SINGLE_THREADED JANET_OS_NAME=plan9 JANET_ARCH_NAME=$objtype JANET_API='' JANET_NO_RETURN='' JANET_SIMPLE_GETLINE `{echo JANET_NO_^$DISABLED} PLAN9_`{echo -n $objtype}
CFLAGS=-FTVBNcwp -D _POSIX_SOURCE -DJANET_PLAN9 -D_BSD_EXTENSION -D_LIMITS_EXTENSION -Isrc/include -Isrc/conf -I/sys/include/npe -Dtypestr=janettypestr -DJANET_API `{echo '-D'^$JANET_CONFIG}
BOOT_CFLAGS=$CFLAGS -DJANET_BOOTSTRAP
JANET_CORE_HEADERS=`{ls src/core/*.h}
JANET_CORE_SOURCES=`{ls src/core/*.c}
JANET_BOOT_SOURCES=src/boot/array_test.c \
src/boot/boot.c \
src/boot/buffer_test.c \
src/boot/number_test.c \
src/boot/system_test.c \
src/boot/table_test.c
JANET_BOOT_HEADERS=src/boot/tests.h
JANET_BOOT_OBJECTS=`{echo $JANET_CORE_SOURCES $JANET_BOOT_SOURCES | sed -e 's/\.c/.boot.'$O'/g'}
OFILES=janet.$O src/mainclient/shell.$O
default:V:all
src/%.boot.$O: src/%.c $JANET_HEADERS $JANET_CORE_HEADERS $JANET_BOOT_HEADERS
$CC $BOOT_CFLAGS -o $target $prereq(1)
src/mainclient/shell.$O: src/mainclient/shell.c
$CC $BOOT_CFLAGS -o $target $prereq(1)
$O.janetboot: $JANET_BOOT_OBJECTS
$LD $LDFLAGS -o $target $prereq
janet.c: $O.janetboot src/boot/boot.janet
$prereq(1) . JANET_PATH $JANET_PATH >$target
build/janet.$O: build/c/janet.c src/conf/janetconf.h src/include/janet.h
$CC $CFLAGS -D^$JANET_CONFIG -o $target $prereq(1)
build/shell.$O: src/mainclient/shell.c src/conf/janetconf.h src/include/janet.h
$CC $CFLAGS -D^$JANET_CONFIG -o $target $prereq(1)
</sys/src/cmd/mkone
clean:V:
rm -f src/core/*.[$OS] src/boot/*.[$OS] *.a[$OS] y.tab.? lex.yy.c y.debug y.output [$OS].??* $TARG janet.[$OS] janet.c src/mainclient/shell.[$OS]

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -44,9 +44,7 @@ static void test_valid_str(const char *str) {
}
int number_test() {
#ifdef JANET_PLAN9
return 0;
#endif
test_valid_str("1.0");
test_valid_str("1");
test_valid_str("2.1");

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -22,7 +22,7 @@
#include <janet.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include "tests.h"
@@ -35,11 +35,6 @@ int system_test() {
assert(sizeof(void *) == 8);
#endif
/* Check the version defines are self consistent */
char version_combined[256];
snprintf(version_combined, sizeof(version_combined), "%d.%d.%d%s", JANET_VERSION_MAJOR, JANET_VERSION_MINOR, JANET_VERSION_PATCH, JANET_VERSION_EXTRA);
assert(!strcmp(JANET_VERSION, version_combined));
/* Reflexive testing and nanbox testing */
assert(janet_equals(janet_wrap_nil(), janet_wrap_nil()));
assert(janet_equals(janet_wrap_false(), janet_wrap_false()));
@@ -51,10 +46,6 @@ int system_test() {
assert(janet_equals(janet_wrap_number(1.4), janet_wrap_number(1.4)));
assert(janet_equals(janet_wrap_number(3.14159265), janet_wrap_number(3.14159265)));
#ifdef NAN
#ifdef JANET_PLAN9
// Plan 9 traps NaNs by default; disable that.
setfcr(0);
#endif
assert(janet_checktype(janet_wrap_number(NAN), JANET_NUMBER));
#else
assert(janet_checktype(janet_wrap_number(0.0 / 0.0), JANET_NUMBER));

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -4,16 +4,15 @@
#define JANETCONF_H
#define JANET_VERSION_MAJOR 1
#define JANET_VERSION_MINOR 41
#define JANET_VERSION_PATCH 3
#define JANET_VERSION_EXTRA "-dev"
#define JANET_VERSION "1.41.3-dev"
#define JANET_VERSION_MINOR 34
#define JANET_VERSION_PATCH 0
#define JANET_VERSION_EXTRA ""
#define JANET_VERSION "1.34.0"
/* #define JANET_BUILD "local" */
/* These settings all affect linking, so use cautiously. */
/* #define JANET_SINGLE_THREADED */
/* #define JANET_THREAD_LOCAL _Thread_local */
/* #define JANET_NO_DYNAMIC_MODULES */
/* #define JANET_NO_NANBOX */
/* #define JANET_API __attribute__((visibility ("default"))) */
@@ -30,7 +29,6 @@
/* #define JANET_NO_NET */
/* #define JANET_NO_INT_TYPES */
/* #define JANET_NO_EV */
/* #define JANET_NO_FILEWATCH */
/* #define JANET_NO_REALPATH */
/* #define JANET_NO_SYMLINKS */
/* #define JANET_NO_UMASK */

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -201,17 +201,4 @@ int32_t janet_abstract_decref(void *abst) {
return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount);
}
int32_t janet_abstract_decref_maybe_free(void *abst) {
int32_t result = janet_abstract_decref(abst);
if (0 == result) {
JanetAbstractHead *head = janet_abstract_head(abst);
if (head->type->gc) {
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
}
/* Free memory */
janet_free(head);
}
return result;
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -30,7 +30,7 @@
#include <string.h>
static void janet_array_impl(JanetArray *array, int32_t capacity) {
static void janet_array_impl(JanetArray *array, size_t capacity) {
Janet *data = NULL;
if (capacity > 0) {
janet_vm.next_collection += capacity * sizeof(Janet);
@@ -45,21 +45,23 @@ static void janet_array_impl(JanetArray *array, int32_t capacity) {
}
/* Creates a new array */
JanetArray *janet_array(int32_t capacity) {
JanetArray *janet_array(size_t capacity) {
if (capacity > JANET_SIZEMAX) capacity = JANET_SIZEMAX;
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
janet_array_impl(array, capacity);
return array;
}
/* Creates a new array with weak references */
JanetArray *janet_array_weak(int32_t capacity) {
JanetArray *janet_array_weak(size_t capacity) {
if (capacity > JANET_SIZEMAX) capacity = JANET_SIZEMAX;
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY_WEAK, sizeof(JanetArray));
janet_array_impl(array, capacity);
return array;
}
/* Creates a new array from n elements. */
JanetArray *janet_array_n(const Janet *elements, int32_t n) {
JanetArray *janet_array_n(const Janet *elements, size_t n) {
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
array->capacity = n;
array->count = n;
@@ -72,13 +74,13 @@ JanetArray *janet_array_n(const Janet *elements, int32_t n) {
}
/* Ensure the array has enough capacity for elements */
void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth) {
void janet_array_ensure(JanetArray *array, size_t capacity, int32_t growth) {
Janet *newData;
Janet *old = array->data;
if (capacity <= array->capacity) return;
int64_t new_capacity = ((int64_t) capacity) * growth;
if (new_capacity > INT32_MAX) new_capacity = INT32_MAX;
capacity = (int32_t) new_capacity;
if (new_capacity > JANET_SIZEMAX) new_capacity = JANET_SIZEMAX;
capacity = (size_t) new_capacity;
newData = janet_realloc(old, capacity * sizeof(Janet));
if (NULL == newData) {
JANET_OUT_OF_MEMORY;
@@ -89,11 +91,10 @@ void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth) {
}
/* Set the count of an array. Extend with nil if needed. */
void janet_array_setcount(JanetArray *array, int32_t count) {
if (count < 0)
return;
void janet_array_setcount(JanetArray *array, size_t count) {
if (count > JANET_SIZEMAX) count = JANET_SIZEMAX;
if (count > array->count) {
int32_t i;
size_t i;
janet_array_ensure(array, count, 1);
for (i = array->count; i < count; i++) {
array->data[i] = janet_wrap_nil();
@@ -104,10 +105,10 @@ void janet_array_setcount(JanetArray *array, int32_t count) {
/* Push a value to the top of the array */
void janet_array_push(JanetArray *array, Janet x) {
if (array->count == INT32_MAX) {
if (array->count == JANET_SIZEMAX) {
janet_panic("array overflow");
}
int32_t newcount = array->count + 1;
size_t newcount = array->count + 1;
janet_array_ensure(array, newcount, 2);
array->data[array->count] = x;
array->count = newcount;
@@ -138,7 +139,7 @@ JANET_CORE_FN(cfun_array_new,
"Creates a new empty array with a pre-allocated capacity. The same as "
"`(array)` but can be more efficient if the maximum size of an array is known.") {
janet_fixarity(argc, 1);
int32_t cap = janet_getinteger(argv, 0);
size_t cap = janet_getsize(argv, 0);
JanetArray *array = janet_array(cap);
return janet_wrap_array(array);
}
@@ -147,7 +148,7 @@ JANET_CORE_FN(cfun_array_weak,
"(array/weak capacity)",
"Creates a new empty array with a pre-allocated capacity and support for weak references. Similar to `array/new`.") {
janet_fixarity(argc, 1);
int32_t cap = janet_getinteger(argv, 0);
size_t cap = janet_getsize(argv, 0);
JanetArray *array = janet_array_weak(cap);
return janet_wrap_array(array);
}
@@ -156,7 +157,7 @@ JANET_CORE_FN(cfun_array_new_filled,
"(array/new-filled count &opt value)",
"Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") {
janet_arity(argc, 1, 2);
int32_t count = janet_getnat(argv, 0);
size_t count = janet_getsize(argv, 0);
Janet x = (argc == 2) ? argv[1] : janet_wrap_nil();
JanetArray *array = janet_array(count);
for (int32_t i = 0; i < count; i++) {
@@ -201,10 +202,10 @@ JANET_CORE_FN(cfun_array_push,
"Push all the elements of xs to the end of an array. Modifies the input array and returns it.") {
janet_arity(argc, 1, -1);
JanetArray *array = janet_getarray(argv, 0);
if (INT32_MAX - argc + 1 <= array->count) {
if ((size_t)(INT32_MAX - argc + 1) <= array->count) {
janet_panic("array overflow");
}
int32_t newcount = array->count - 1 + argc;
size_t newcount = array->count - 1 + (size_t) argc;
janet_array_ensure(array, newcount, 2);
if (argc > 1) memcpy(array->data + array->count, argv + 1, (size_t)(argc - 1) * sizeof(Janet));
array->count = newcount;
@@ -219,7 +220,7 @@ JANET_CORE_FN(cfun_array_ensure,
"Otherwise, the backing memory will be reallocated so that there is enough space.") {
janet_fixarity(argc, 3);
JanetArray *array = janet_getarray(argv, 0);
int32_t newcount = janet_getinteger(argv, 1);
size_t newcount = janet_getsize(argv, 1);
int32_t growth = janet_getinteger(argv, 2);
if (newcount < 1) janet_panic("expected positive integer");
janet_array_ensure(array, newcount, growth);
@@ -258,7 +259,7 @@ JANET_CORE_FN(cfun_array_concat,
break;
case JANET_ARRAY:
case JANET_TUPLE: {
int32_t j, len = 0;
size_t j, len = 0;
const Janet *vals = NULL;
janet_indexed_view(argv[i], &vals, &len);
if (array->data == vals) {
@@ -275,31 +276,6 @@ JANET_CORE_FN(cfun_array_concat,
return janet_wrap_array(array);
}
JANET_CORE_FN(cfun_array_join,
"(array/join arr & parts)",
"Join a variable number of arrays and tuples into the first argument, "
"which must be an array. "
"Return the modified array `arr`.") {
int32_t i;
janet_arity(argc, 1, -1);
JanetArray *array = janet_getarray(argv, 0);
for (i = 1; i < argc; i++) {
int32_t j, len = 0;
const Janet *vals = NULL;
if (!janet_indexed_view(argv[i], &vals, &len)) {
janet_panicf("expected indexed type for argument %d, got %v", i, argv[i]);
}
if (array->data == vals) {
int32_t newcount = array->count + len;
janet_array_ensure(array, newcount, 2);
janet_indexed_view(argv[i], &vals, &len);
}
for (j = 0; j < len; j++)
janet_array_push(array, vals[j]);
}
return janet_wrap_array(array);
}
JANET_CORE_FN(cfun_array_insert,
"(array/insert arr at & xs)",
"Insert all `xs` into array `arr` at index `at`. `at` should be an integer between "
@@ -410,7 +386,6 @@ void janet_lib_array(JanetTable *env) {
JANET_CORE_REG("array/remove", cfun_array_remove),
JANET_CORE_REG("array/trim", cfun_array_trim),
JANET_CORE_REG("array/clear", cfun_array_clear),
JANET_CORE_REG("array/join", cfun_array_join),
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, array_cfuns);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -567,13 +567,6 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
x = janet_get1(s, janet_ckeywordv("structarg"));
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
/* Check namedarg */
x = janet_get1(s, janet_ckeywordv("namedargs"));
if (janet_checkint(x)) {
def->flags |= JANET_FUNCDEF_FLAG_NAMEDARGS;
def->named_args_count = janet_unwrap_integer(x);
}
/* Check source */
x = janet_get1(s, janet_ckeywordv("source"));
if (janet_checktype(x, JANET_STRING)) def->source = janet_unwrap_string(x);
@@ -989,14 +982,6 @@ static Janet janet_disasm_structarg(JanetFuncDef *def) {
return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_STRUCTARG);
}
static Janet janet_disasm_namedargs(JanetFuncDef *def) {
if (def->flags & JANET_FUNCDEF_FLAG_NAMEDARGS) {
return janet_wrap_integer(def->named_args_count);
} else {
return janet_wrap_nil();
}
}
static Janet janet_disasm_constants(JanetFuncDef *def) {
JanetArray *constants = janet_array(def->constants_length);
for (int32_t i = 0; i < def->constants_length; i++) {
@@ -1047,7 +1032,6 @@ Janet janet_disasm(JanetFuncDef *def) {
janet_table_put(ret, janet_ckeywordv("source"), janet_disasm_source(def));
janet_table_put(ret, janet_ckeywordv("vararg"), janet_disasm_vararg(def));
janet_table_put(ret, janet_ckeywordv("structarg"), janet_disasm_structarg(def));
janet_table_put(ret, janet_ckeywordv("namedargs"), janet_disasm_namedargs(def));
janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def));
janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def));
janet_table_put(ret, janet_ckeywordv("symbolmap"), janet_disasm_symbolslots(def));
@@ -1064,7 +1048,6 @@ JANET_CORE_FN(cfun_asm,
"The syntax for the assembly can be found on the Janet website, and should correspond\n"
"to the return value of disasm. Will throw an\n"
"error on invalid assembly.") {
janet_sandbox_assert(JANET_SANDBOX_ASM);
janet_fixarity(argc, 1);
JanetAssembleResult res;
res = janet_asm(argv[0], 0);
@@ -1084,8 +1067,6 @@ JANET_CORE_FN(cfun_disasm,
"* :min-arity - minimum number of arguments function can be called with.\n"
"* :max-arity - maximum number of arguments function can be called with.\n"
"* :vararg - true if function can take a variable number of arguments.\n"
"* :structarg - true if function can take a variable number of arguments using the &keys option.\n"
"* :namedargs - if function can take a variable number of arguments using the &named option, this will be the number of named arguments.\n"
"* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n"
"* :source - name of source file that this function was compiled from.\n"
"* :name - name of function.\n"
@@ -1095,7 +1076,6 @@ JANET_CORE_FN(cfun_disasm,
"* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n"
"* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n"
"* :defs - other function definitions that this function may instantiate.\n") {
janet_sandbox_assert(JANET_SANDBOX_ASM);
janet_arity(argc, 1, 2);
JanetFunction *f = janet_getfunction(argv, 0);
if (argc == 2) {
@@ -1108,9 +1088,7 @@ JANET_CORE_FN(cfun_disasm,
if (!janet_cstrcmp(kw, "name")) return janet_disasm_name(f->def);
if (!janet_cstrcmp(kw, "vararg")) return janet_disasm_vararg(f->def);
if (!janet_cstrcmp(kw, "structarg")) return janet_disasm_structarg(f->def);
if (!janet_cstrcmp(kw, "namedargs")) return janet_disasm_namedargs(f->def);
if (!janet_cstrcmp(kw, "slotcount")) return janet_disasm_slotcount(f->def);
if (!janet_cstrcmp(kw, "symbolmap")) return janet_disasm_symbolslots(f->def);
if (!janet_cstrcmp(kw, "constants")) return janet_disasm_constants(f->def);
if (!janet_cstrcmp(kw, "sourcemap")) return janet_disasm_sourcemap(f->def);
if (!janet_cstrcmp(kw, "environments")) return janet_disasm_environments(f->def);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -74,7 +74,6 @@ JanetBuffer *janet_pointer_buffer_unsafe(void *memory, int32_t capacity, int32_t
void janet_buffer_deinit(JanetBuffer *buffer) {
if (!(buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC)) {
janet_free(buffer->data);
buffer->data = NULL;
}
}
@@ -372,15 +371,17 @@ JANET_CORE_FN(cfun_buffer_push_uint16,
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
uint16_t data = janet_getuinteger16(argv, 2);
uint8_t bytes[sizeof(data)];
memcpy(bytes, &data, sizeof(bytes));
union {
uint16_t data;
uint8_t bytes[2];
} u;
u.data = (uint16_t) janet_getinteger(argv, 2);
if (reverse) {
uint8_t temp = bytes[1];
bytes[1] = bytes[0];
bytes[0] = temp;
uint8_t temp = u.bytes[1];
u.bytes[1] = u.bytes[0];
u.bytes[0] = temp;
}
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
janet_buffer_push_u16(buffer, *(uint16_t *) u.bytes);
return argv[0];
}
@@ -391,12 +392,14 @@ JANET_CORE_FN(cfun_buffer_push_uint32,
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
uint32_t data = janet_getuinteger(argv, 2);
uint8_t bytes[sizeof(data)];
memcpy(bytes, &data, sizeof(bytes));
union {
uint32_t data;
uint8_t bytes[4];
} u;
u.data = (uint32_t) janet_getinteger(argv, 2);
if (reverse)
reverse_u32(bytes);
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
reverse_u32(u.bytes);
janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes);
return argv[0];
}
@@ -407,12 +410,14 @@ JANET_CORE_FN(cfun_buffer_push_uint64,
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
uint64_t data = janet_getuinteger64(argv, 2);
uint8_t bytes[sizeof(data)];
memcpy(bytes, &data, sizeof(bytes));
union {
uint64_t data;
uint8_t bytes[8];
} u;
u.data = (uint64_t) janet_getuinteger64(argv, 2);
if (reverse)
reverse_u64(bytes);
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
reverse_u64(u.bytes);
janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes);
return argv[0];
}
@@ -423,12 +428,14 @@ JANET_CORE_FN(cfun_buffer_push_float32,
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
float data = (float) janet_getnumber(argv, 2);
uint8_t bytes[sizeof(data)];
memcpy(bytes, &data, sizeof(bytes));
union {
float data;
uint8_t bytes[4];
} u;
u.data = (float) janet_getnumber(argv, 2);
if (reverse)
reverse_u32(bytes);
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
reverse_u32(u.bytes);
janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes);
return argv[0];
}
@@ -439,12 +446,14 @@ JANET_CORE_FN(cfun_buffer_push_float64,
janet_fixarity(argc, 3);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int reverse = should_reverse_bytes(argv, 1);
double data = janet_getnumber(argv, 2);
uint8_t bytes[sizeof(data)];
memcpy(bytes, &data, sizeof(bytes));
union {
double data;
uint8_t bytes[8];
} u;
u.data = janet_getnumber(argv, 2);
if (reverse)
reverse_u64(bytes);
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
reverse_u64(u.bytes);
janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes);
return argv[0];
}
@@ -646,27 +655,6 @@ JANET_CORE_FN(cfun_buffer_format,
return argv[0];
}
JANET_CORE_FN(cfun_buffer_format_at,
"(buffer/format-at buffer at format & args)",
"Snprintf like functionality for printing values into a buffer. Returns "
"the modified buffer.") {
janet_arity(argc, 2, -1);
JanetBuffer *buffer = janet_getbuffer(argv, 0);
int32_t at = janet_getinteger(argv, 1);
if (at < 0) {
at += buffer->count + 1;
}
if (at > buffer->count || at < 0) janet_panicf("expected index at to be in range [0, %d), got %d", buffer->count, at);
int32_t oldcount = buffer->count;
buffer->count = at;
const char *strfrmt = (const char *) janet_getstring(argv, 2);
janet_buffer_format(buffer, strfrmt, 2, argc, argv);
if (buffer->count < oldcount) {
buffer->count = oldcount;
}
return argv[0];
}
void janet_lib_buffer(JanetTable *env) {
JanetRegExt buffer_cfuns[] = {
JANET_CORE_REG("buffer/new", cfun_buffer_new),
@@ -693,7 +681,6 @@ void janet_lib_buffer(JanetTable *env) {
JANET_CORE_REG("buffer/bit-toggle", cfun_buffer_bittoggle),
JANET_CORE_REG("buffer/blit", cfun_buffer_blit),
JANET_CORE_REG("buffer/format", cfun_buffer_format),
JANET_CORE_REG("buffer/format-at", cfun_buffer_format_at),
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, buffer_cfuns);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -140,7 +140,7 @@ void janet_bytecode_remove_noops(JanetFuncDef *def) {
/* relative pc is in DS field of instruction */
old_jump_target = i + (((int32_t)instr) >> 8);
new_jump_target = pc_map[old_jump_target];
instr += (uint32_t)(new_jump_target - old_jump_target + (i - j)) << 8;
instr += (new_jump_target - old_jump_target + (i - j)) << 8;
break;
case JOP_JUMP_IF:
case JOP_JUMP_IF_NIL:
@@ -149,7 +149,7 @@ void janet_bytecode_remove_noops(JanetFuncDef *def) {
/* relative pc is in ES field of instruction */
old_jump_target = i + (((int32_t)instr) >> 16);
new_jump_target = pc_map[old_jump_target];
instr += (uint32_t)(new_jump_target - old_jump_target + (i - j)) << 16;
instr += (new_jump_target - old_jump_target + (i - j)) << 16;
break;
default:
break;
@@ -522,7 +522,6 @@ JanetFuncDef *janet_funcdef_alloc(void) {
def->bytecode_length = 0;
def->environments_length = 0;
def->symbolmap_length = 0;
def->named_args_count = 0;
return def;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -25,18 +25,15 @@
#include <janet.h>
#include "state.h"
#include "fiber.h"
#include "util.h"
#endif
#ifndef JANET_SINGLE_THREADED
#ifndef JANET_WINDOWS
#include <pthread.h>
#endif
#endif
#ifdef JANET_WINDOWS
#else
#include <windows.h>
#endif
#endif
#ifdef JANET_USE_STDATOMIC
#include <stdatomic.h>
@@ -62,18 +59,6 @@ JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
void janet_signalv(JanetSignal sig, Janet message) {
if (janet_vm.return_reg != NULL) {
/* Should match logic in janet_call for coercing everything not ok to an error (no awaits, yields, etc.) */
if (janet_vm.coerce_error && sig != JANET_SIGNAL_OK) {
#ifdef JANET_EV
if (NULL != janet_vm.root_fiber && sig == JANET_SIGNAL_EVENT) {
janet_vm.root_fiber->sched_id++;
}
#endif
if (sig != JANET_SIGNAL_ERROR) {
message = janet_wrap_string(janet_formatc("%v coerced from %s to error", message, janet_signal_names[sig]));
}
sig = JANET_SIGNAL_ERROR;
}
*janet_vm.return_reg = message;
if (NULL != janet_vm.fiber) {
janet_vm.fiber->flags |= JANET_FIBER_DID_LONGJUMP;
@@ -318,28 +303,11 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) {
uint32_t janet_getuinteger(const Janet *argv, int32_t n) {
Janet x = argv[n];
if (!janet_checkuint(x)) {
janet_panicf("bad slot #%d, expected 32 bit unsigned integer, got %v", n, x);
janet_panicf("bad slot #%d, expected 32 bit signed integer, got %v", n, x);
}
return (uint32_t) janet_unwrap_number(x);
return janet_unwrap_integer(x);
}
int16_t janet_getinteger16(const Janet *argv, int32_t n) {
Janet x = argv[n];
if (!janet_checkint16(x)) {
janet_panicf("bad slot #%d, expected 16 bit signed integer, got %v", n, x);
}
return (int16_t) janet_unwrap_number(x);
}
uint16_t janet_getuinteger16(const Janet *argv, int32_t n) {
Janet x = argv[n];
if (!janet_checkuint16(x)) {
janet_panicf("bad slot #%d, expected 16 bit unsigned integer, got %v", n, x);
}
return (uint16_t) janet_unwrap_number(x);
}
int64_t janet_getinteger64(const Janet *argv, int32_t n) {
#ifdef JANET_INT_TYPES
return janet_unwrap_s64(argv[n]);
@@ -460,7 +428,7 @@ Janet janet_dyn(const char *name) {
return janet_table_get(janet_vm.top_dyns, janet_ckeywordv(name));
}
if (janet_vm.fiber->env) {
return janet_table_get_keyword(janet_vm.fiber->env, name);
return janet_table_get(janet_vm.fiber->env, janet_ckeywordv(name));
} else {
return janet_wrap_nil();
}
@@ -478,33 +446,6 @@ void janet_setdyn(const char *name, Janet value) {
}
}
/* Create a function that when called, returns X. Trivial in Janet, a pain in C. */
JanetFunction *janet_thunk_delay(Janet x) {
static const uint32_t bytecode[] = {
JOP_LOAD_CONSTANT,
JOP_RETURN
};
JanetFuncDef *def = janet_funcdef_alloc();
def->arity = 0;
def->min_arity = 0;
def->max_arity = INT32_MAX;
def->flags = JANET_FUNCDEF_FLAG_VARARG;
def->slotcount = 1;
def->bytecode = janet_malloc(sizeof(bytecode));
def->bytecode_length = (int32_t)(sizeof(bytecode) / sizeof(uint32_t));
def->constants = janet_malloc(sizeof(Janet));
def->constants_length = 1;
def->name = NULL;
if (!def->bytecode || !def->constants) {
JANET_OUT_OF_MEMORY;
}
def->constants[0] = x;
memcpy(def->bytecode, bytecode, sizeof(bytecode));
janet_def_addflags(def);
/* janet_verify(def); */
return janet_thunk(def);
}
uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags) {
uint64_t ret = 0;
const uint8_t *keyw = janet_getkeyword(argv, n);
@@ -557,49 +498,31 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA
return janet_getabstract(argv, n, at);
}
uint32_t janet_optuinteger(const Janet *argv, int32_t argc, int32_t n, uint32_t dflt) {
if (argc <= n) return dflt;
if (janet_checktype(argv[n], JANET_NIL)) return dflt;
return janet_getuinteger(argv, n);
}
uint64_t janet_optuinteger64(const Janet *argv, int32_t argc, int32_t n, uint64_t dflt) {
if (argc <= n) return dflt;
if (janet_checktype(argv[n], JANET_NIL)) return dflt;
return janet_getuinteger64(argv, n);
}
/* Atomic refcounts */
JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
#ifdef _MSC_VER
return _InterlockedIncrement(x);
#ifdef JANET_WINDOWS
return InterlockedIncrement(x);
#elif defined(JANET_USE_STDATOMIC)
return atomic_fetch_add_explicit(x, 1, memory_order_relaxed) + 1;
#elif defined(JANET_PLAN9)
return aincl((void*)x, 1);
#else
return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED);
#endif
}
JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) {
#ifdef _MSC_VER
return _InterlockedDecrement(x);
#ifdef JANET_WINDOWS
return InterlockedDecrement(x);
#elif defined(JANET_USE_STDATOMIC)
return atomic_fetch_add_explicit(x, -1, memory_order_acq_rel) - 1;
#elif defined(JANET_PLAN9)
return aincl((void*)x, -1);
#else
return __atomic_add_fetch(x, -1, __ATOMIC_ACQ_REL);
#endif
}
JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
#ifdef _MSC_VER
return _InterlockedOr(x, 0);
#elif defined(JANET_PLAN9)
return agetl((void*)x);
#ifdef JANET_WINDOWS
return InterlockedOr(x, 0);
#elif defined(JANET_USE_STDATOMIC)
return atomic_load_explicit(x, memory_order_acquire);
#else
@@ -607,18 +530,6 @@ JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
#endif
}
JanetAtomicInt janet_atomic_load_relaxed(JanetAtomicInt volatile *x) {
#ifdef _MSC_VER
return _InterlockedOr(x, 0);
#elif defined(JANET_PLAN9)
return agetl((void*)x);
#elif defined(JANET_USE_STDATOMIC)
return atomic_load_explicit(x, memory_order_relaxed);
#else
return __atomic_load_n(x, __ATOMIC_RELAXED);
#endif
}
/* Some definitions for function-like macros */
JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -201,29 +201,14 @@ static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) {
return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil(), janet_wrap_nil());
}
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
int8_t inline_index = 0;
if (can_slot_be_imm(args[1], &inline_index)) {
/* Use JOP_PUT_INDEX */
if (opts.flags & JANET_FOPTS_DROP) {
janetc_emit_ssi(opts.compiler, JOP_PUT_INDEX, args[0], args[2], inline_index, 0);
return janetc_cslot(janet_wrap_nil());
} else {
JanetSlot t = janetc_gettarget(opts);
janetc_copy(opts.compiler, t, args[0]);
janetc_emit_ssi(opts.compiler, JOP_PUT_INDEX, t, args[2], inline_index, 0);
return t;
}
if (opts.flags & JANET_FOPTS_DROP) {
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
return janetc_cslot(janet_wrap_nil());
} else {
/* Use JOP_PUT */
if (opts.flags & JANET_FOPTS_DROP) {
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
return janetc_cslot(janet_wrap_nil());
} else {
JanetSlot t = janetc_gettarget(opts);
janetc_copy(opts.compiler, t, args[0]);
janetc_emit_sss(opts.compiler, JOP_PUT, t, args[1], args[2], 0);
return t;
}
JanetSlot t = janetc_gettarget(opts);
janetc_copy(opts.compiler, t, args[0]);
janetc_emit_sss(opts.compiler, JOP_PUT, t, args[1], args[2], 0);
return t;
}
}
static JanetSlot do_length(JanetFopts opts, JanetSlot *args) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -98,22 +98,6 @@ void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s) {
sp.sym2 = sym;
sp.slot = s;
sp.keep = 0;
sp.referenced = sym[0] == '_'; /* Fake ref if symbol is _ to avoid lints */
sp.slot.flags |= JANET_SLOT_NAMED;
sp.birth_pc = cnt ? cnt - 1 : 0;
sp.death_pc = UINT32_MAX;
janet_v_push(c->scope->syms, sp);
}
/* Same as janetc_nameslot, but don't have a lint for unused bindings. */
void janetc_nameslot_no_unused(JanetCompiler *c, const uint8_t *sym, JanetSlot s) {
SymPair sp;
int32_t cnt = janet_v_count(c->buffer);
sp.sym = sym;
sp.sym2 = sym;
sp.slot = s;
sp.keep = 0;
sp.referenced = 1;
sp.slot.flags |= JANET_SLOT_NAMED;
sp.birth_pc = cnt ? cnt - 1 : 0;
sp.death_pc = UINT32_MAX;
@@ -186,10 +170,6 @@ void janetc_popscope(JanetCompiler *c) {
/* Keep upvalue slots and symbols for debugging. */
for (int32_t i = 0; i < janet_v_count(oldscope->syms); i++) {
SymPair pair = oldscope->syms[i];
/* Check for unused symbols */
if (pair.referenced == 0 && pair.sym) {
janetc_lintf(c, JANET_C_LINT_STRICT, "binding %q is unused", janet_wrap_symbol(pair.sym));
}
/* The variable should not be lexically accessible */
pair.sym = NULL;
if (pair.death_pc == UINT32_MAX) {
@@ -282,7 +262,6 @@ JanetSlot janetc_resolve(
pair = scope->syms + i;
if (pair->sym == sym) {
ret = pair->slot;
pair->referenced = 1;
goto found;
}
}
@@ -295,7 +274,7 @@ JanetSlot janetc_resolve(
{
JanetBinding binding = janet_resolve_ext(c->env, sym);
if (binding.type == JANET_BINDING_NONE) {
Janet handler = janet_table_get_keyword(c->env, "missing-symbol");
Janet handler = janet_table_get(c->env, janet_ckeywordv("missing-symbol"));
switch (janet_type(handler)) {
case JANET_NIL:
break;
@@ -367,7 +346,6 @@ found:
/* non-local scope needs to expose its environment */
JanetScope *original_scope = scope;
pair->keep = 1;
pair->referenced = 1;
while (scope && !(scope->flags & JANET_SCOPE_FUNCTION))
scope = scope->parent;
janet_assert(scope, "invalid scopes");
@@ -459,22 +437,11 @@ JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) {
const JanetKV *kvs = NULL;
int32_t cap = 0, len = 0;
janet_dictionary_view(ds, &kvs, &len, &cap);
/* Sort keys for stability of order? */
int32_t *index_buf;
int32_t index_buf_stack[32];
int32_t *index_buf_heap = NULL;
if (len < 32) {
index_buf = index_buf_stack;
} else {
index_buf_heap = janet_smalloc(sizeof(int32_t) * len);
index_buf = index_buf_heap;
for (int32_t i = 0; i < cap; i++) {
if (janet_checktype(kvs[i].key, JANET_NIL)) continue;
janet_v_push(ret, janetc_value(subopts, kvs[i].key));
janet_v_push(ret, janetc_value(subopts, kvs[i].value));
}
if (len) janet_sorted_keys(kvs, cap, index_buf);
for (int32_t i = 0; i < len; i++) {
janet_v_push(ret, janetc_value(subopts, kvs[index_buf[i]].key));
janet_v_push(ret, janetc_value(subopts, kvs[index_buf[i]].value));
}
if (index_buf_heap) janet_sfree(index_buf_heap);
return ret;
}
@@ -547,9 +514,9 @@ void janetc_throwaway(JanetFopts opts, Janet x) {
JanetScope unusedScope;
int32_t bufstart = janet_v_count(c->buffer);
int32_t mapbufstart = janet_v_count(c->mapbuffer);
janetc_scope(&unusedScope, c, JANET_SCOPE_UNUSED, "unused");
janetc_scope(&unusedScope, c, JANET_SCOPE_UNUSED, "unusued");
janetc_value(opts, x);
janetc_lintf(c, JANET_C_LINT_STRICT, "dead code, consider removing %.4q", x);
janetc_lintf(c, JANET_C_LINT_STRICT, "dead code, consider removing %.2q", x);
janetc_popscope(c);
if (c->buffer) {
janet_v__cnt(c->buffer) = bufstart;
@@ -559,7 +526,7 @@ void janetc_throwaway(JanetFopts opts, Janet x) {
}
/* Compile a call or tailcall instruction */
static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun, const Janet *form) {
static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun) {
JanetSlot retslot;
JanetCompiler *c = opts.compiler;
int specialized = 0;
@@ -585,8 +552,6 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun, c
JanetFunction *f = janet_unwrap_function(fun.constant);
int32_t min = f->def->min_arity;
int32_t max = f->def->max_arity;
int structarg = f->def->flags & JANET_FUNCDEF_FLAG_STRUCTARG;
int namedarg = f->def->flags & JANET_FUNCDEF_FLAG_NAMEDARGS;
if (min_arity < 0) {
/* Call has splices */
min_arity = -1 - min_arity;
@@ -610,47 +575,6 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun, c
fun.constant, min, min == 1 ? "" : "s", min_arity);
janetc_error(c, es);
}
if (structarg && (min_arity > f->def->arity) && ((min_arity - f->def->arity) & 1)) {
/* If we have an odd number of variadic arguments to a `&keys` function, that is almost certainly wrong. */
if (namedarg) {
janetc_lintf(c, JANET_C_LINT_NORMAL,
"odd number of named arguments to `&named` function %v", fun.constant);
} else {
janetc_lintf(c, JANET_C_LINT_NORMAL,
"odd number of named arguments to `&keys` function %v", fun.constant);
}
}
if (namedarg && f->def->named_args_count > 0) {
/* For each argument passed in, check if it is one of the used named arguments
* by checking the list defined in the function def. If not, raise a normal compiler
* lint. We can also do a strict lint for _missing_ named arguments, although in many
* cases those are assumed to have some kind of default, or we have dynamic keys. */
int32_t first_arg_key_index = f->def->arity + 1;
for (int32_t i = first_arg_key_index; i < janet_tuple_length(form); i += 2) {
Janet argkey = form[i];
/* Assumption: The first N constants of a function are its named argument keys. This
* may change if the compiler changes, but is true for all Janet generated functions. */
int found = 0;
if (janet_checktype(argkey, JANET_KEYWORD)) {
for (int32_t j = 0; j < f->def->named_args_count && j < f->def->constants_length; j++) {
if (janet_equals(argkey, f->def->constants[j])) {
found = 1;
break;
}
}
} else if (janet_checktype(argkey, JANET_TUPLE)) {
/* Possible lint : too dynamic, be dumber
* (defn f [&named x] [x])
* (f (if (coin-flip) :x :w) 10)
* A tuple could be a function call the evaluates to a valid key */
found = 1;
}
if (!found) {
janetc_lintf(c, JANET_C_LINT_NORMAL,
"unused named argument %v to function %v", argkey, fun.constant);
}
}
}
}
}
break;
@@ -885,16 +809,14 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
} else if (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR) { /* [] tuples are not function call */
ret = janetc_tuple(opts, x);
} else {
/* Function calls */
JanetSlot head = janetc_value(subopts, tup[0]);
subopts.flags = JANET_FUNCTION | JANET_CFUNCTION;
ret = janetc_call(opts, janetc_toslots(c, tup + 1, janet_tuple_length(tup) - 1), head, tup);
ret = janetc_call(opts, janetc_toslots(c, tup + 1, janet_tuple_length(tup) - 1), head);
janetc_freeslot(c, head);
}
ret.flags &= ~JANET_SLOT_SPLICED;
}
break;
/* Data Constructors */
case JANET_SYMBOL:
ret = janetc_resolve(c, janet_unwrap_symbol(x));
break;
@@ -934,21 +856,19 @@ void janet_def_addflags(JanetFuncDef *def) {
int32_t set_flags = 0;
int32_t unset_flags = 0;
/* pos checks */
if (def->name) set_flags |= JANET_FUNCDEF_FLAG_HASNAME;
if (def->source) set_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
if (def->defs) set_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
if (def->environments) set_flags |= JANET_FUNCDEF_FLAG_HASENVS;
if (def->sourcemap) set_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
if (def->closure_bitset) set_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
if (def->named_args_count) set_flags |= JANET_FUNCDEF_FLAG_NAMEDARGS;
if (def->name) set_flags |= JANET_FUNCDEF_FLAG_HASNAME;
if (def->source) set_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
if (def->defs) set_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
if (def->environments) set_flags |= JANET_FUNCDEF_FLAG_HASENVS;
if (def->sourcemap) set_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
if (def->closure_bitset) set_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
/* negative checks */
if (!def->name) unset_flags |= JANET_FUNCDEF_FLAG_HASNAME;
if (!def->source) unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
if (!def->defs) unset_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
if (!def->environments) unset_flags |= JANET_FUNCDEF_FLAG_HASENVS;
if (!def->sourcemap) unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
if (!def->closure_bitset) unset_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
if (!def->named_args_count) unset_flags |= JANET_FUNCDEF_FLAG_NAMEDARGS;
if (!def->name) unset_flags |= JANET_FUNCDEF_FLAG_HASNAME;
if (!def->source) unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
if (!def->defs) unset_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
if (!def->environments) unset_flags |= JANET_FUNCDEF_FLAG_HASENVS;
if (!def->sourcemap) unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
if (!def->closure_bitset) unset_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
/* Update flags */
def->flags |= set_flags;
def->flags &= ~unset_flags;
@@ -1014,14 +934,13 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
int32_t slotchunks = (def->slotcount + 31) >> 5;
/* numchunks is min of slotchunks and scope->ua.count */
int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks;
uint32_t *chunks = janet_calloc(slotchunks, sizeof(uint32_t));
uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks);
if (NULL == chunks) {
JANET_OUT_OF_MEMORY;
}
memcpy(chunks, scope->ua.chunks, sizeof(uint32_t) * numchunks);
/* fprintf(stderr, "slot chunks: %d, scope->ua.count: %d, numchunks: %d\n", slotchunks, scope->ua.count, numchunks); */
/* Register allocator preallocates some registers [240-255, high 16 bits of chunk index 7], we can ignore those. */
if (scope->ua.count > 7 && slotchunks > 7) chunks[7] &= 0xFFFFU;
if (scope->ua.count > 7) chunks[7] &= 0xFFFFU;
def->closure_bitset = chunks;
}
@@ -1055,10 +974,6 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
SymPair pair = scope->syms[i];
if (pair.sym2) {
JanetSymbolMap jsm;
/* Check for unused symbols */
if (pair.referenced == 0 && pair.sym) {
janetc_lintf(c, JANET_C_LINT_STRICT, "binding %q is unused", janet_wrap_symbol(pair.sym));
}
if (pair.death_pc == UINT32_MAX) {
jsm.death_pc = def->bytecode_length;
} else {
@@ -1141,7 +1056,7 @@ JanetCompileResult janet_compile_lint(Janet source,
if (c.result.status == JANET_COMPILE_OK) {
JanetFuncDef *def = janetc_pop_funcdef(&c);
def->name = janet_cstring("thunk");
def->name = janet_cstring("_thunk");
janet_def_addflags(def);
c.result.funcdef = def;
} else {
@@ -1167,7 +1082,6 @@ JANET_CORE_FN(cfun_compile,
"struct with keys :line, :column, and :error if compilation fails. "
"If a `lints` array is given, linting messages will be appended to the array. "
"Each message will be a tuple of the form `(level line col message)`.") {
janet_sandbox_assert(JANET_SANDBOX_COMPILE);
janet_arity(argc, 1, 4);
JanetTable *env = (argc > 1 && !janet_checktype(argv[1], JANET_NIL))
? janet_gettable(argv, 1) : janet_vm.fiber->env;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -114,7 +114,6 @@ typedef struct SymPair {
const uint8_t *sym;
const uint8_t *sym2;
int keep;
int referenced; /* Has this value been used */
uint32_t birth_pc;
uint32_t death_pc;
} SymPair;
@@ -223,7 +222,6 @@ const JanetSpecial *janetc_special(const uint8_t *name);
void janetc_freeslot(JanetCompiler *c, JanetSlot s);
void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s);
void janetc_nameslot_no_unused(JanetCompiler *c, const uint8_t *sym, JanetSlot s);
JanetSlot janetc_farslot(JanetCompiler *c);
/* Throw away some code after checking that it is well formed. */
@@ -264,7 +262,7 @@ void janetc_popscope(JanetCompiler *c);
void janetc_popscope_keepslot(JanetCompiler *c, JanetSlot retslot);
JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c);
/* Create a destroy slot */
/* Create a destory slots */
JanetSlot janetc_cslot(Janet x);
/* Search for a symbol */

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -27,7 +27,6 @@
#include "compile.h"
#include "state.h"
#include "util.h"
#include "fiber.h"
#endif
/* Generated bytes */
@@ -67,18 +66,18 @@ JanetModule janet_native(const char *name, const uint8_t **error) {
JanetBuildConfig modconf = getter();
JanetBuildConfig host = janet_config_current();
if (host.major != modconf.major ||
host.minor != modconf.minor ||
host.minor < modconf.minor ||
host.bits != modconf.bits) {
char errbuf[128];
snprintf(errbuf, sizeof(errbuf), "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
host.major,
host.minor,
host.patch,
host.bits,
modconf.major,
modconf.minor,
modconf.patch,
modconf.bits);
sprintf(errbuf, "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
host.major,
host.minor,
host.patch,
host.bits,
modconf.major,
modconf.minor,
modconf.patch,
modconf.bits);
*error = janet_cstring(errbuf);
return NULL;
}
@@ -295,7 +294,6 @@ JANET_CORE_FN(janet_core_native,
"from the native module.") {
JanetModule init;
janet_arity(argc, 1, 2);
Janet argv0 = argv[0];
const uint8_t *path = janet_getstring(argv, 0);
const uint8_t *error = NULL;
JanetTable *env;
@@ -308,10 +306,8 @@ JANET_CORE_FN(janet_core_native,
if (!init) {
janet_panicf("could not load native %S: %S", path, error);
}
/* GC root incase garbage collection called inside module entry */
janet_fiber_push(janet_vm.fiber, janet_wrap_table(env));
init(env);
janet_table_put(env, janet_ckeywordv("native"), argv0);
janet_table_put(env, janet_ckeywordv("native"), argv[0]);
return janet_wrap_table(env);
}
@@ -436,39 +432,27 @@ JANET_CORE_FN(janet_core_range,
"With one argument, returns a range [0, end). With two arguments, returns "
"a range [start, end). With three, returns a range with optional step size.") {
janet_arity(argc, 1, 3);
double start = 0, stop = 0, step = 1, count = 0;
int32_t start = 0, stop = 0, step = 1, count = 0;
if (argc == 3) {
start = janet_getnumber(argv, 0);
stop = janet_getnumber(argv, 1);
step = janet_getnumber(argv, 2);
count = (step > 0) ? (stop - start) / step :
((step < 0) ? (stop - start) / step : 0);
start = janet_getinteger(argv, 0);
stop = janet_getinteger(argv, 1);
step = janet_getinteger(argv, 2);
count = (step > 0) ? (stop - start - 1) / step + 1 :
((step < 0) ? (stop - start + 1) / step + 1 : 0);
} else if (argc == 2) {
start = janet_getnumber(argv, 0);
stop = janet_getnumber(argv, 1);
start = janet_getinteger(argv, 0);
stop = janet_getinteger(argv, 1);
count = stop - start;
} else {
stop = janet_getnumber(argv, 0);
stop = janet_getinteger(argv, 0);
count = stop;
}
count = (count > 0) ? count : 0;
int32_t int_count;
janet_assert(count >= 0, "bad range code");
if (count > (double) INT32_MAX) {
janet_panicf("range is too large, %f elements", count);
} else {
int_count = (int32_t) ceil(count);
JanetArray *array = janet_array(count);
for (int32_t i = 0; i < count; i++) {
array->data[i] = janet_wrap_number(start + i * step);
}
if (step > 0.0) {
janet_assert(start + int_count * step >= stop, "bad range code");
} else {
janet_assert(start + int_count * step <= stop, "bad range code");
}
JanetArray *array = janet_array(int_count);
for (int32_t i = 0; i < int_count; i++) {
array->data[i] = janet_wrap_number((double) start + (double) i * step);
}
array->count = int_count;
array->count = count;
return janet_wrap_array(array);
}
@@ -657,15 +641,22 @@ JANET_CORE_FN(janet_core_check_int,
"(int? x)",
"Check if x can be exactly represented as a 32 bit signed two's complement integer.") {
janet_fixarity(argc, 1);
return janet_wrap_boolean(janet_checkint(argv[0]));
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
double num = janet_unwrap_number(argv[0]);
return janet_wrap_boolean(num == (double)((int32_t)num));
ret_false:
return janet_wrap_false();
}
JANET_CORE_FN(janet_core_check_nat,
"(nat? x)",
"Check if x can be exactly represented as a non-negative 32 bit signed two's complement integer.") {
janet_fixarity(argc, 1);
if (!janet_checkint(argv[0])) return janet_wrap_false();
return janet_wrap_boolean(janet_unwrap_integer(argv[0]) >= 0);
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
double num = janet_unwrap_number(argv[0]);
return janet_wrap_boolean(num >= 0 && (num == (double)((int32_t)num)));
ret_false:
return janet_wrap_false();
}
JANET_CORE_FN(janet_core_is_bytes,
@@ -698,15 +689,7 @@ JANET_CORE_FN(janet_core_is_lengthable,
JANET_CORE_FN(janet_core_signal,
"(signal what x)",
"Raise a signal with payload x. `what` can be an integer\n"
"from 0 through 7 indicating user(0-7), or one of:\n\n"
"* :ok\n"
"* :error\n"
"* :debug\n"
"* :yield\n"
"* :user(0-7)\n"
"* :interrupt\n"
"* :await") {
"Raise a signal with payload x. ") {
janet_arity(argc, 1, 2);
Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
if (janet_checkint(argv[0])) {
@@ -750,9 +733,6 @@ typedef struct SandboxOption {
static const SandboxOption sandbox_options[] = {
{"all", JANET_SANDBOX_ALL},
{"asm", JANET_SANDBOX_ASM},
{"chroot", JANET_SANDBOX_CHROOT},
{"compile", JANET_SANDBOX_COMPILE},
{"env", JANET_SANDBOX_ENV},
{"ffi", JANET_SANDBOX_FFI},
{"ffi-define", JANET_SANDBOX_FFI_DEFINE},
@@ -770,8 +750,6 @@ static const SandboxOption sandbox_options[] = {
{"sandbox", JANET_SANDBOX_SANDBOX},
{"signal", JANET_SANDBOX_SIGNAL},
{"subprocess", JANET_SANDBOX_SUBPROCESS},
{"threads", JANET_SANDBOX_THREADS},
{"unmarshal", JANET_SANDBOX_UNMARSHAL},
{NULL, 0}
};
@@ -780,9 +758,6 @@ JANET_CORE_FN(janet_core_sandbox,
"Disable feature sets to prevent the interpreter from using certain system resources. "
"Once a feature is disabled, there is no way to re-enable it. Capabilities can be:\n\n"
"* :all - disallow all (except IO to stdout, stderr, and stdin)\n"
"* :asm - disallow calling `asm` and `disasm` functions.\n"
"* :chroot - disallow calling `os/posix-chroot`\n"
"* :compile - disallow calling `compile`. This will disable a lot of functionality, such as `eval`.\n"
"* :env - disallow reading and write env variables\n"
"* :ffi - disallow FFI (recommended if disabling anything else)\n"
"* :ffi-define - disallow loading new FFI modules and binding new functions\n"
@@ -799,9 +774,7 @@ JANET_CORE_FN(janet_core_sandbox,
"* :net-listen - disallow accepting inbound network connections\n"
"* :sandbox - disallow calling this function\n"
"* :signal - disallow adding or removing signal handlers\n"
"* :subprocess - disallow running subprocesses\n"
"* :threads - disallow spawning threads with `ev/thread`. Certain helper threads may still be spawned.\n"
"* :unmarshal - disallow calling the unmarshal function.\n") {
"* :subprocess - disallow running subprocesses") {
uint32_t flags = 0;
for (int32_t i = 0; i < argc; i++) {
JanetKeyword kw = janet_getkeyword(argv, i);
@@ -1003,17 +976,18 @@ static void make_apply(JanetTable *env) {
/* Push the array */
S(JOP_PUSH_ARRAY, 5),
/* Call the function */
/* Call the funciton */
S(JOP_TAILCALL, 0)
};
janet_quick_asm(env, JANET_FUN_APPLY | JANET_FUNCDEF_FLAG_VARARG,
"apply", 1, 1, INT32_MAX, 6, apply_asm, sizeof(apply_asm),
JDOC("(apply f & args)\n\n"
"Applies a function f to a variable number of arguments. Each "
"element in args is used as an argument to f, except the last "
"element in args, which is expected to be an array or a tuple. "
"Each element in this last argument is then also pushed as an "
"argument to f."));
"Applies a function to a variable number of arguments. Each element in args "
"is used as an argument to f, except the last element in args, which is expected to "
"be an array-like. Each element in this last argument is then also pushed as an argument to "
"f. For example:\n\n"
"\t(apply + 1000 (range 10))\n\n"
"sums the first 10 integers and 1000."));
}
static const uint32_t error_asm[] = {
@@ -1147,9 +1121,6 @@ static void janet_load_libs(JanetTable *env) {
#endif
#ifdef JANET_EV
janet_lib_ev(env);
#ifdef JANET_FILEWATCH
janet_lib_filewatch(env);
#endif
#endif
#ifdef JANET_NET
janet_lib_net(env);
@@ -1250,7 +1221,7 @@ JanetTable *janet_core_env(JanetTable *replacements) {
/* Variadic ops */
templatize_varop(env, JANET_FUN_ADD, "+", 0, 0, JOP_ADD,
JDOC("(+ & xs)\n\n"
"Returns the sum of all xs. If xs is empty, return 0."));
"Returns the sum of all xs. xs must be integers or real numbers only. If xs is empty, return 0."));
templatize_varop(env, JANET_FUN_SUBTRACT, "-", 0, 0, JOP_SUBTRACT,
JDOC("(- & xs)\n\n"
"Returns the difference of xs. If xs is empty, returns 0. If xs has one element, returns the "
@@ -1284,7 +1255,7 @@ JanetTable *janet_core_env(JanetTable *replacements) {
"Returns the bit-wise or of all values in xs. Each x in xs must be an integer."));
templatize_varop(env, JANET_FUN_BXOR, "bxor", 0, 0, JOP_BXOR,
JDOC("(bxor & xs)\n\n"
"Returns the bit-wise xor of all values in xs. Each x in xs must be an integer."));
"Returns the bit-wise xor of all values in xs. Each in xs must be an integer."));
templatize_varop(env, JANET_FUN_LSHIFT, "blshift", 1, 1, JOP_SHIFT_LEFT,
JDOC("(blshift x & shifts)\n\n"
"Returns the value of x bit shifted left by the sum of all values in shifts. x "
@@ -1366,16 +1337,12 @@ JanetTable *janet_core_env(JanetTable *replacements) {
lidv = midv = janet_wrap_nil();
janet_resolve(env, janet_csymbol("load-image-dict"), &lidv);
janet_resolve(env, janet_csymbol("make-image-dict"), &midv);
/* Check that we actually got tables - if we are using a smaller corelib, may not exist */
if (janet_checktype(lidv, JANET_TABLE) && janet_checktype(midv, JANET_TABLE)) {
JanetTable *lid = janet_unwrap_table(lidv);
JanetTable *mid = janet_unwrap_table(midv);
for (int32_t i = 0; i < lid->capacity; i++) {
const JanetKV *kv = lid->data + i;
if (!janet_checktype(kv->key, JANET_NIL)) {
janet_table_put(mid, kv->value, kv->key);
}
JanetTable *lid = janet_unwrap_table(lidv);
JanetTable *mid = janet_unwrap_table(midv);
for (int32_t i = 0; i < lid->capacity; i++) {
const JanetKV *kv = lid->data + i;
if (!janet_checktype(kv->key, JANET_NIL)) {
janet_table_put(mid, kv->value, kv->key);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -102,7 +102,7 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
}
/* Error reporting. This can be emulated from within Janet, but for
* consistency with the top level code it is defined once. */
* consitency with the top level code it is defined once. */
void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
int32_t fi;
@@ -164,7 +164,7 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
}
}
if (frame->flags & JANET_STACKFRAME_TAILCALL)
janet_eprintf(" (tail call)");
janet_eprintf(" (tailcall)");
if (frame->func && frame->pc) {
int32_t off = (int32_t)(frame->pc - def->bytecode);
if (def->sourcemap) {
@@ -180,11 +180,6 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
}
}
janet_eprintf("\n");
/* Print fiber points optionally. Clutters traces but provides info
if (i <= 0 && fi > 0) {
janet_eprintf(" in parent fiber\n");
}
*/
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -76,6 +76,4 @@
#define __BSD_VISIBLE 1
#endif
#define _FILE_OFFSET_BITS 64
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -56,9 +56,6 @@
#if (defined(__x86_64__) || defined(_M_X64)) && !defined(JANET_WINDOWS)
#define JANET_FFI_SYSV64_ENABLED
#endif
#if (defined(__aarch64__) || defined(_M_ARM64)) && !defined(JANET_WINDOWS)
#define JANET_FFI_AAPCS64_ENABLED
#endif
typedef struct JanetFFIType JanetFFIType;
typedef struct JanetFFIStruct JanetFFIStruct;
@@ -143,13 +140,7 @@ typedef enum {
JANET_WIN64_REGISTER,
JANET_WIN64_STACK,
JANET_WIN64_REGISTER_REF,
JANET_WIN64_STACK_REF,
JANET_AAPCS64_GENERAL,
JANET_AAPCS64_SSE,
JANET_AAPCS64_GENERAL_REF,
JANET_AAPCS64_STACK,
JANET_AAPCS64_STACK_REF,
JANET_AAPCS64_NONE
JANET_WIN64_STACK_REF
} JanetFFIWordSpec;
/* Describe how each Janet argument is interpreted in terms of machine words
@@ -164,16 +155,13 @@ typedef struct {
typedef enum {
JANET_FFI_CC_NONE,
JANET_FFI_CC_SYSV_64,
JANET_FFI_CC_WIN_64,
JANET_FFI_CC_AAPCS64
JANET_FFI_CC_WIN_64
} JanetFFICallingConvention;
#ifdef JANET_FFI_WIN64_ENABLED
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_WIN_64
#elif defined(JANET_FFI_SYSV64_ENABLED)
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_SYSV_64
#elif defined(JANET_FFI_AAPCS64_ENABLED)
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_AAPCS64
#else
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_NONE
#endif
@@ -313,9 +301,6 @@ static JanetFFICallingConvention decode_ffi_cc(const uint8_t *name) {
#endif
#ifdef JANET_FFI_SYSV64_ENABLED
if (!janet_cstrcmp(name, "sysv64")) return JANET_FFI_CC_SYSV_64;
#endif
#ifdef JANET_FFI_AAPCS64_ENABLED
if (!janet_cstrcmp(name, "aapcs64")) return JANET_FFI_CC_AAPCS64;
#endif
if (!janet_cstrcmp(name, "default")) return JANET_FFI_CC_DEFAULT;
janet_panicf("unknown calling convention %s", name);
@@ -400,7 +385,7 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
JanetFFIStruct *st = janet_abstract(&janet_struct_type,
sizeof(JanetFFIStruct) + argc * sizeof(JanetFFIStructMember));
st->field_count = 0;
st->field_count = member_count;
st->size = 0;
st->align = 1;
if (argc == 0) {
@@ -418,7 +403,6 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
st->fields[i].type = decode_ffi_type(argv[j]);
size_t el_size = type_size(st->fields[i].type);
size_t el_align = type_align(st->fields[i].type);
if (el_align <= 0) janet_panicf("bad field type %V", argv[j]);
if (all_packed || pack_one) {
if (st->size % el_align != 0) is_aligned = 0;
st->fields[i].offset = st->size;
@@ -434,7 +418,6 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
st->size += (st->align - 1);
st->size /= st->align;
st->size *= st->align;
st->field_count = member_count;
return st;
}
@@ -492,7 +475,7 @@ JANET_CORE_FN(cfun_ffi_align,
static void *janet_ffi_getpointer(const Janet *argv, int32_t n) {
switch (janet_type(argv[n])) {
default:
janet_panicf("bad slot #%d, expected ffi pointer convertible type, got %v", n, argv[n]);
janet_panicf("bad slot #%d, expected ffi pointer convertable type, got %v", n, argv[n]);
case JANET_POINTER:
case JANET_STRING:
case JANET_KEYWORD:
@@ -780,101 +763,6 @@ static JanetFFIWordSpec sysv64_classify(JanetFFIType type) {
}
#endif
#ifdef JANET_FFI_AAPCS64_ENABLED
/* Procedure Call Standard for the Arm® 64-bit Architecture (AArch64) 2023Q3 October 6, 2023
* See section 6.8.2 Parameter passing rules.
* https://github.com/ARM-software/abi-aa/releases/download/2023Q3/aapcs64.pdf
*
* Additional documentation needed for Apple platforms.
* https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms */
#define JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ptr, alignment) (ptr = ((ptr) + ((alignment) - 1)) & ~((alignment) - 1))
#if !defined(JANET_APPLE)
#define JANET_FFI_AAPCS64_STACK_ALIGN(ptr, alignment) ((void) alignment, JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ptr, 8))
#else
#define JANET_FFI_AAPCS64_STACK_ALIGN(ptr, alignment) JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ptr, alignment)
#endif
typedef struct {
uint64_t a;
uint64_t b;
} Aapcs64Variant1ReturnGeneral;
typedef struct {
double a;
double b;
double c;
double d;
} Aapcs64Variant2ReturnSse;
/* Workaround for passing a return value pointer through x8.
* Limits struct returns to 128 bytes. */
typedef struct {
uint64_t a;
uint64_t b;
uint64_t c;
uint64_t d;
uint64_t e;
uint64_t f;
uint64_t g;
uint64_t h;
uint64_t i;
uint64_t j;
uint64_t k;
uint64_t l;
uint64_t m;
uint64_t n;
uint64_t o;
uint64_t p;
} Aapcs64Variant3ReturnPointer;
static JanetFFIWordSpec aapcs64_classify(JanetFFIType type) {
switch (type.prim) {
case JANET_FFI_TYPE_PTR:
case JANET_FFI_TYPE_STRING:
case JANET_FFI_TYPE_BOOL:
case JANET_FFI_TYPE_INT8:
case JANET_FFI_TYPE_INT16:
case JANET_FFI_TYPE_INT32:
case JANET_FFI_TYPE_INT64:
case JANET_FFI_TYPE_UINT8:
case JANET_FFI_TYPE_UINT16:
case JANET_FFI_TYPE_UINT32:
case JANET_FFI_TYPE_UINT64:
return JANET_AAPCS64_GENERAL;
case JANET_FFI_TYPE_DOUBLE:
case JANET_FFI_TYPE_FLOAT:
return JANET_AAPCS64_SSE;
case JANET_FFI_TYPE_STRUCT: {
JanetFFIStruct *st = type.st;
if (st->field_count <= 4 && aapcs64_classify(st->fields[0].type) == JANET_AAPCS64_SSE) {
bool is_hfa = true;
for (uint32_t i = 1; i < st->field_count; i++) {
if (st->fields[0].type.prim != st->fields[i].type.prim) {
is_hfa = false;
break;
}
}
if (is_hfa) {
return JANET_AAPCS64_SSE;
}
}
if (type_size(type) > 16) {
return JANET_AAPCS64_GENERAL_REF;
}
return JANET_AAPCS64_GENERAL;
}
case JANET_FFI_TYPE_VOID:
return JANET_AAPCS64_NONE;
default:
janet_panic("nyi");
return JANET_AAPCS64_NONE;
}
}
#endif
JANET_CORE_FN(cfun_ffi_signature,
"(ffi/signature calling-convention ret-type & arg-types)",
"Create a function signature object that can be used to make calls "
@@ -1072,96 +960,6 @@ JANET_CORE_FN(cfun_ffi_signature,
}
break;
#endif
#ifdef JANET_FFI_AAPCS64_ENABLED
case JANET_FFI_CC_AAPCS64: {
uint32_t next_general_reg = 0;
uint32_t next_fp_reg = 0;
uint32_t stack_offset = 0;
uint32_t ref_stack_offset = 0;
JanetFFIWordSpec ret_spec = aapcs64_classify(ret_type);
ret.spec = ret_spec;
if (ret_spec == JANET_AAPCS64_SSE) {
variant = 1;
} else if (ret_spec == JANET_AAPCS64_GENERAL_REF) {
if (type_size(ret_type) > sizeof(Aapcs64Variant3ReturnPointer)) {
janet_panic("return value bigger than supported");
}
variant = 2;
} else {
variant = 0;
}
for (uint32_t i = 0; i < arg_count; i++) {
mappings[i].type = decode_ffi_type(argv[i + 2]);
mappings[i].spec = aapcs64_classify(mappings[i].type);
size_t arg_size = type_size(mappings[i].type);
switch (mappings[i].spec) {
case JANET_AAPCS64_GENERAL: {
bool arg_is_struct = mappings[i].type.prim == JANET_FFI_TYPE_STRUCT;
uint32_t needed_registers = (arg_size + 7) / 8;
if (next_general_reg + needed_registers <= 8) {
mappings[i].offset = next_general_reg;
next_general_reg += needed_registers;
} else {
size_t arg_align = arg_is_struct ? 8 : type_align(mappings[i].type);
mappings[i].spec = JANET_AAPCS64_STACK;
mappings[i].offset = JANET_FFI_AAPCS64_STACK_ALIGN(stack_offset, arg_align);
#if !defined(JANET_APPLE)
stack_offset += arg_size > 8 ? arg_size : 8;
#else
stack_offset += arg_size;
#endif
next_general_reg = 8;
}
break;
}
case JANET_AAPCS64_GENERAL_REF:
if (next_general_reg < 8) {
mappings[i].offset = next_general_reg++;
} else {
mappings[i].spec = JANET_AAPCS64_STACK_REF;
mappings[i].offset = JANET_FFI_AAPCS64_STACK_ALIGN(stack_offset, 8);
stack_offset += 8;
}
mappings[i].offset2 = JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ref_stack_offset, 8);
ref_stack_offset += arg_size;
break;
case JANET_AAPCS64_SSE: {
uint32_t needed_registers = (arg_size + 7) / 8;
if (next_fp_reg + needed_registers <= 8) {
mappings[i].offset = next_fp_reg;
next_fp_reg += needed_registers;
} else {
mappings[i].spec = JANET_AAPCS64_STACK;
mappings[i].offset = JANET_FFI_AAPCS64_STACK_ALIGN(stack_offset, 8);
#if !defined(JANET_APPLE)
stack_offset += 8;
#else
stack_offset += arg_size;
#endif
}
break;
}
default:
janet_panic("nyi");
}
}
stack_offset = (stack_offset + 15) & ~0xFUL;
ref_stack_offset = (ref_stack_offset + 15) & ~0xFUL;
stack_count = stack_offset + ref_stack_offset;
for (uint32_t i = 0; i < arg_count; i++) {
if (mappings[i].spec == JANET_AAPCS64_GENERAL_REF || mappings[i].spec == JANET_AAPCS64_STACK_REF) {
mappings[i].offset2 = stack_offset + mappings[i].offset2;
}
}
}
break;
#endif
}
/* Create signature abstract value */
@@ -1344,15 +1142,6 @@ typedef double (win64_variant_f_ffif)(double, double, uint64_t, double);
typedef double (win64_variant_f_fffi)(double, double, double, uint64_t);
typedef double (win64_variant_f_ffff)(double, double, double, double);
/* MSVC stack frame runtime error checking (/RTCs) prepends alloca() allocations with an _RTC_ALLOCA_NODE
* header; misalligning stack-based FFI arguments and causing the memmove() (by stack_shift) to corrupt
* the _RTC_ALLOCA_NODE header.
*
* We turn off the RTC-instrumented alloca() and adding of _RTC_CheckStackVars to function prologue just
* for janet_ffi_win64() */
#ifdef __MSVC_RUNTIME_CHECKS
#pragma runtime_checks( "s", off )
#endif
static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointer, const Janet *argv) {
union {
uint64_t integer;
@@ -1502,103 +1291,6 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
return janet_ffi_read_one(ret_mem, signature->ret.type, JANET_FFI_MAX_RECUR);
}
#ifdef __MSVC_RUNTIME_CHECKS
// Restore stack frame runtime error checking (/RTCs) if it was enabled.
#pragma runtime_checks ( "s", restore )
#endif
#endif
#ifdef JANET_FFI_AAPCS64_ENABLED
static void janet_ffi_aapcs64_standard_callback(void *ctx, void *userdata) {
janet_ffi_trampoline(ctx, userdata);
}
typedef Aapcs64Variant1ReturnGeneral janet_aapcs64_variant_1(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7,
double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7);
typedef Aapcs64Variant2ReturnSse janet_aapcs64_variant_2(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7,
double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7);
typedef Aapcs64Variant3ReturnPointer janet_aapcs64_variant_3(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7,
double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7);
static Janet janet_ffi_aapcs64(JanetFFISignature *signature, void *function_pointer, const Janet *argv) {
union {
Aapcs64Variant1ReturnGeneral general_return;
Aapcs64Variant2ReturnSse sse_return;
Aapcs64Variant3ReturnPointer pointer_return;
} retu;
uint64_t regs[8];
double fp_regs[8];
void *ret_mem = &retu.general_return;
/* Apple's stack values do not need to be 8-byte aligned,
* thus all stack offsets refer to actual byte positions. */
uint8_t *stack = alloca(signature->stack_count);
#if defined(JANET_APPLE)
/* Values must be zero-extended by the caller instead of the callee. */
memset(stack, 0, signature->stack_count);
#endif
for (uint32_t i = 0; i < signature->arg_count; i++) {
int32_t n = i + 2;
JanetFFIMapping arg = signature->args[i];
void *to = NULL;
switch (arg.spec) {
case JANET_AAPCS64_GENERAL:
to = regs + arg.offset;
break;
case JANET_AAPCS64_GENERAL_REF:
to = stack + arg.offset2;
regs[arg.offset] = (uint64_t) to;
break;
case JANET_AAPCS64_SSE:
to = fp_regs + arg.offset;
break;
case JANET_AAPCS64_STACK:
to = stack + arg.offset;
break;
case JANET_AAPCS64_STACK_REF:
to = stack + arg.offset2;
uint64_t *ptr = (uint64_t *) stack + arg.offset;
*ptr = (uint64_t) to;
break;
default:
janet_panic("nyi");
}
if (to) {
janet_ffi_write_one(to, argv, n, arg.type, JANET_FFI_MAX_RECUR);
}
}
switch (signature->variant) {
case 0:
retu.general_return = ((janet_aapcs64_variant_1 *)(function_pointer))(
regs[0], regs[1], regs[2], regs[3],
regs[4], regs[5], regs[6], regs[7],
fp_regs[0], fp_regs[1], fp_regs[2], fp_regs[3],
fp_regs[4], fp_regs[5], fp_regs[6], fp_regs[7]);
break;
case 1:
retu.sse_return = ((janet_aapcs64_variant_2 *)(function_pointer))(
regs[0], regs[1], regs[2], regs[3],
regs[4], regs[5], regs[6], regs[7],
fp_regs[0], fp_regs[1], fp_regs[2], fp_regs[3],
fp_regs[4], fp_regs[5], fp_regs[6], fp_regs[7]);
break;
case 2: {
retu.pointer_return = ((janet_aapcs64_variant_3 *)(function_pointer))(
regs[0], regs[1], regs[2], regs[3],
regs[4], regs[5], regs[6], regs[7],
fp_regs[0], fp_regs[1], fp_regs[2], fp_regs[3],
fp_regs[4], fp_regs[5], fp_regs[6], fp_regs[7]);
}
}
return janet_ffi_read_one(ret_mem, signature->ret.type, JANET_FFI_MAX_RECUR);
}
#endif
@@ -1681,10 +1373,6 @@ JANET_CORE_FN(cfun_ffi_call,
#ifdef JANET_FFI_SYSV64_ENABLED
case JANET_FFI_CC_SYSV_64:
return janet_ffi_sysv64(signature, function_pointer, argv);
#endif
#ifdef JANET_FFI_AAPCS64_ENABLED
case JANET_FFI_CC_AAPCS64:
return janet_ffi_aapcs64(signature, function_pointer, argv);
#endif
}
}
@@ -1699,7 +1387,7 @@ JANET_CORE_FN(cfun_ffi_buffer_write,
JanetFFIType type = decode_ffi_type(argv[0]);
uint32_t el_size = (uint32_t) type_size(type);
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
int32_t index = janet_optnat(argv, argc, 3, buffer->count);
int32_t index = janet_optnat(argv, argc, 3, 0);
int32_t old_count = buffer->count;
if (index > old_count) janet_panic("index out of bounds");
buffer->count = index;
@@ -1754,10 +1442,6 @@ JANET_CORE_FN(cfun_ffi_get_callback_trampoline,
#ifdef JANET_FFI_SYSV64_ENABLED
case JANET_FFI_CC_SYSV_64:
return janet_wrap_pointer(janet_ffi_sysv64_standard_callback);
#endif
#ifdef JANET_FFI_AAPCS64_ENABLED
case JANET_FFI_CC_AAPCS64:
return janet_wrap_pointer(janet_ffi_aapcs64_standard_callback);
#endif
}
}
@@ -1877,9 +1561,6 @@ JANET_CORE_FN(cfun_ffi_supported_calling_conventions,
#endif
#ifdef JANET_FFI_SYSV64_ENABLED
janet_array_push(array, janet_ckeywordv("sysv64"));
#endif
#ifdef JANET_FFI_AAPCS64_ENABLED
janet_array_push(array, janet_ckeywordv("aapcs64"));
#endif
janet_array_push(array, janet_ckeywordv("none"));
return janet_wrap_array(array);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -592,8 +592,8 @@ JANET_CORE_FN(cfun_fiber_status,
"* :user(0-7) - the fiber is suspended by a user signal\n"
"* :interrupted - the fiber was interrupted\n"
"* :suspended - the fiber is waiting to be resumed by the scheduler\n"
"* :new - the fiber has just been created and not yet run\n"
"* :alive - the fiber is currently running and cannot be resumed") {
"* :alive - the fiber is currently running and cannot be resumed\n"
"* :new - the fiber has just been created and not yet run") {
janet_fixarity(argc, 1);
JanetFiber *fiber = janet_getfiber(argv, 0);
uint32_t s = janet_fiber_status(fiber);
@@ -610,9 +610,8 @@ JANET_CORE_FN(cfun_fiber_current,
JANET_CORE_FN(cfun_fiber_root,
"(fiber/root)",
"Returns the current root fiber. The root fiber is the oldest "
"ancestor that does not have a parent. Note that a root fiber "
"is also a task fiber.") {
"Returns the current root fiber. The root fiber is the oldest ancestor "
"that does not have a parent.") {
(void) argv;
janet_fixarity(argc, 0);
return janet_wrap_fiber(janet_vm.root_fiber);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,688 +0,0 @@
/*
* Copyright (c) 2026 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef JANET_AMALG
#include "features.h"
#include <janet.h>
#include "util.h"
#endif
#ifdef JANET_EV
#ifdef JANET_FILEWATCH
#ifdef JANET_LINUX
#include <sys/inotify.h>
#include <unistd.h>
#endif
#ifdef JANET_WINDOWS
#include <windows.h>
#endif
typedef struct {
const char *name;
uint32_t flag;
} JanetWatchFlagName;
typedef struct {
#ifndef JANET_WINDOWS
JanetStream *stream;
#endif
JanetTable *watch_descriptors;
JanetChannel *channel;
uint32_t default_flags;
int is_watching;
} JanetWatcher;
#ifdef JANET_LINUX
#include <sys/inotify.h>
#include <unistd.h>
static const JanetWatchFlagName watcher_flags_linux[] = {
{"access", IN_ACCESS},
{"all", IN_ALL_EVENTS},
{"attrib", IN_ATTRIB},
{"close-nowrite", IN_CLOSE_NOWRITE},
{"close-write", IN_CLOSE_WRITE},
{"create", IN_CREATE},
{"delete", IN_DELETE},
{"delete-self", IN_DELETE_SELF},
{"ignored", IN_IGNORED},
{"modify", IN_MODIFY},
{"move-self", IN_MOVE_SELF},
{"moved-from", IN_MOVED_FROM},
{"moved-to", IN_MOVED_TO},
{"open", IN_OPEN},
{"q-overflow", IN_Q_OVERFLOW},
{"unmount", IN_UNMOUNT},
};
static uint32_t decode_watch_flags(const Janet *options, int32_t n) {
uint32_t flags = 0;
for (int32_t i = 0; i < n; i++) {
if (!(janet_checktype(options[i], JANET_KEYWORD))) {
janet_panicf("expected keyword, got %v", options[i]);
}
JanetKeyword keyw = janet_unwrap_keyword(options[i]);
const JanetWatchFlagName *result = janet_strbinsearch(watcher_flags_linux,
sizeof(watcher_flags_linux) / sizeof(JanetWatchFlagName),
sizeof(JanetWatchFlagName),
keyw);
if (!result) {
janet_panicf("unknown inotify flag %v", options[i]);
}
flags |= result->flag;
}
return flags;
}
static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uint32_t default_flags) {
int fd;
do {
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
} while (fd == -1 && errno == EINTR);
if (fd == -1) {
janet_panicv(janet_ev_lasterr());
}
watcher->watch_descriptors = janet_table(0);
watcher->channel = channel;
watcher->default_flags = default_flags;
watcher->is_watching = 0;
watcher->stream = janet_stream(fd, JANET_STREAM_READABLE, NULL);
}
static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t flags) {
if (watcher->stream == NULL) janet_panic("watcher closed");
int result;
do {
result = inotify_add_watch(watcher->stream->handle, path, flags);
} while (result == -1 && errno == EINTR);
if (result == -1) {
janet_panicv(janet_ev_lasterr());
}
Janet name = janet_cstringv(path);
Janet wd = janet_wrap_integer(result);
janet_table_put(watcher->watch_descriptors, name, wd);
janet_table_put(watcher->watch_descriptors, wd, name);
}
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
if (watcher->stream == NULL) janet_panic("watcher closed");
Janet check = janet_table_get(watcher->watch_descriptors, janet_cstringv(path));
janet_assert(janet_checktype(check, JANET_NUMBER), "bad watch descriptor");
int watch_handle = janet_unwrap_integer(check);
int result;
do {
result = inotify_rm_watch(watcher->stream->handle, watch_handle);
} while (result != -1 && errno == EINTR);
if (result == -1) {
janet_panicv(janet_ev_lasterr());
}
}
static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
JanetStream *stream = fiber->ev_stream;
JanetWatcher *watcher = *((JanetWatcher **) fiber->ev_state);
char buf[1024];
switch (event) {
default:
break;
case JANET_ASYNC_EVENT_MARK:
janet_mark(janet_wrap_abstract(watcher));
break;
case JANET_ASYNC_EVENT_CLOSE:
janet_schedule(fiber, janet_wrap_nil());
janet_async_end(fiber);
break;
case JANET_ASYNC_EVENT_ERR: {
janet_schedule(fiber, janet_wrap_nil());
janet_async_end(fiber);
break;
}
read_more:
case JANET_ASYNC_EVENT_HUP:
case JANET_ASYNC_EVENT_INIT:
case JANET_ASYNC_EVENT_READ: {
Janet name = janet_wrap_nil();
/* Assumption - read will never return partial events *
* From documentation:
*
* The behavior when the buffer given to read(2) is too small to
* return information about the next event depends on the kernel
* version: before Linux 2.6.21, read(2) returns 0; since Linux
* 2.6.21, read(2) fails with the error EINVAL. Specifying a buffer
* of size
*
* sizeof(struct inotify_event) + NAME_MAX + 1
*
* will be sufficient to read at least one event. */
ssize_t nread;
do {
nread = read(stream->handle, buf, sizeof(buf));
} while (nread == -1 && errno == EINTR);
/* Check for errors - special case errors that can just be waited on to fix */
if (nread == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
}
janet_cancel(fiber, janet_ev_lasterr());
fiber->ev_state = NULL;
janet_async_end(fiber);
break;
}
if (nread < (ssize_t) sizeof(struct inotify_event)) break;
/* Iterate through all events read from the buffer */
char *cursor = buf;
while (cursor < buf + nread) {
struct inotify_event inevent;
memcpy(&inevent, cursor, sizeof(inevent));
cursor += sizeof(inevent);
/* Read path of inevent */
if (inevent.len) {
name = janet_cstringv(cursor);
cursor += inevent.len;
}
/* Got an event */
Janet path = janet_table_get(watcher->watch_descriptors, janet_wrap_integer(inevent.wd));
JanetKV *event = janet_struct_begin(6);
janet_struct_put(event, janet_ckeywordv("wd"), janet_wrap_integer(inevent.wd));
janet_struct_put(event, janet_ckeywordv("wd-path"), path);
if (janet_checktype(name, JANET_NIL)) {
/* We were watching a file directly, so path is the full path. Split into dirname / basename */
JanetString spath = janet_unwrap_string(path);
const uint8_t *cursor = spath + janet_string_length(spath);
const uint8_t *cursor_end = cursor;
while (cursor > spath && cursor[0] != '/') {
cursor--;
}
if (cursor == spath) {
janet_struct_put(event, janet_ckeywordv("dir-name"), path);
janet_struct_put(event, janet_ckeywordv("file-name"), name);
} else {
janet_struct_put(event, janet_ckeywordv("dir-name"), janet_wrap_string(janet_string(spath, (cursor - spath))));
janet_struct_put(event, janet_ckeywordv("file-name"), janet_wrap_string(janet_string(cursor + 1, (cursor_end - cursor - 1))));
}
} else {
janet_struct_put(event, janet_ckeywordv("dir-name"), path);
janet_struct_put(event, janet_ckeywordv("file-name"), name);
}
janet_struct_put(event, janet_ckeywordv("cookie"), janet_wrap_integer(inevent.cookie));
Janet etype = janet_ckeywordv("type");
const JanetWatchFlagName *wfn_end = watcher_flags_linux + sizeof(watcher_flags_linux) / sizeof(watcher_flags_linux[0]);
for (const JanetWatchFlagName *wfn = watcher_flags_linux; wfn < wfn_end; wfn++) {
if ((inevent.mask & wfn->flag) == wfn->flag) janet_struct_put(event, etype, janet_ckeywordv(wfn->name));
}
Janet eventv = janet_wrap_struct(janet_struct_end(event));
janet_channel_give(watcher->channel, eventv);
}
/* Read some more if possible */
goto read_more;
}
break;
}
}
static void janet_watcher_listen(JanetWatcher *watcher) {
if (watcher->is_watching) janet_panic("already watching");
watcher->is_watching = 1;
JanetFunction *thunk = janet_thunk_delay(janet_wrap_nil());
JanetFiber *fiber = janet_fiber(thunk, 64, 0, NULL);
JanetWatcher **state = janet_malloc(sizeof(JanetWatcher *)); /* Gross */
*state = watcher;
janet_async_start_fiber(fiber, watcher->stream, JANET_ASYNC_LISTEN_READ, watcher_callback_read, state);
janet_gcroot(janet_wrap_abstract(watcher));
}
static void janet_watcher_unlisten(JanetWatcher *watcher) {
if (!watcher->is_watching) return;
watcher->is_watching = 0;
janet_stream_close(watcher->stream);
janet_gcunroot(janet_wrap_abstract(watcher));
}
#elif JANET_WINDOWS
#define WATCHFLAG_RECURSIVE 0x100000u
static const JanetWatchFlagName watcher_flags_windows[] = {
{
"all",
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_SECURITY |
FILE_NOTIFY_CHANGE_SIZE |
WATCHFLAG_RECURSIVE
},
{"attributes", FILE_NOTIFY_CHANGE_ATTRIBUTES},
{"creation", FILE_NOTIFY_CHANGE_CREATION},
{"dir-name", FILE_NOTIFY_CHANGE_DIR_NAME},
{"file-name", FILE_NOTIFY_CHANGE_FILE_NAME},
{"last-access", FILE_NOTIFY_CHANGE_LAST_ACCESS},
{"last-write", FILE_NOTIFY_CHANGE_LAST_WRITE},
{"recursive", WATCHFLAG_RECURSIVE},
{"security", FILE_NOTIFY_CHANGE_SECURITY},
{"size", FILE_NOTIFY_CHANGE_SIZE},
};
static uint32_t decode_watch_flags(const Janet *options, int32_t n) {
uint32_t flags = 0;
for (int32_t i = 0; i < n; i++) {
if (!(janet_checktype(options[i], JANET_KEYWORD))) {
janet_panicf("expected keyword, got %v", options[i]);
}
JanetKeyword keyw = janet_unwrap_keyword(options[i]);
const JanetWatchFlagName *result = janet_strbinsearch(watcher_flags_windows,
sizeof(watcher_flags_windows) / sizeof(JanetWatchFlagName),
sizeof(JanetWatchFlagName),
keyw);
if (!result) {
janet_panicf("unknown windows filewatch flag %v", options[i]);
}
flags |= result->flag;
}
return flags;
}
static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uint32_t default_flags) {
watcher->watch_descriptors = janet_table(0);
watcher->channel = channel;
watcher->default_flags = default_flags;
watcher->is_watching = 0;
}
/* Since the file info padding includes embedded file names, we want to include more space for data.
* We also need to handle manually calculating changes if path names are too long, but ideally just avoid
* that scenario as much as possible */
#define FILE_INFO_PADDING (4096 * 4)
typedef struct {
JanetOverlapped overlapped;
JanetStream *stream;
JanetWatcher *watcher;
JanetFiber *fiber;
JanetString dir_path;
uint32_t flags;
uint64_t buf[FILE_INFO_PADDING / sizeof(uint64_t)]; /* Ensure alignment */
} OverlappedWatch;
#define NotifyChange FILE_NOTIFY_INFORMATION
static void read_dir_changes(OverlappedWatch *ow) {
BOOL result = ReadDirectoryChangesW(ow->stream->handle,
(NotifyChange *) ow->buf,
FILE_INFO_PADDING,
(ow->flags & WATCHFLAG_RECURSIVE) ? TRUE : FALSE,
ow->flags & ~WATCHFLAG_RECURSIVE,
NULL,
(OVERLAPPED *) ow,
NULL);
if (!result) {
janet_panicv(janet_ev_lasterr());
}
}
static const char *watcher_actions_windows[] = {
"unknown",
"added",
"removed",
"modified",
"renamed-old",
"renamed-new",
};
static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
OverlappedWatch *ow = (OverlappedWatch *) fiber->ev_state;
JanetWatcher *watcher = ow->watcher;
switch (event) {
default:
break;
case JANET_ASYNC_EVENT_INIT:
janet_async_in_flight(fiber);
break;
case JANET_ASYNC_EVENT_MARK:
janet_mark(janet_wrap_abstract(ow->stream));
janet_mark(janet_wrap_fiber(ow->fiber));
janet_mark(janet_wrap_abstract(watcher));
janet_mark(janet_wrap_string(ow->dir_path));
break;
case JANET_ASYNC_EVENT_CLOSE:
janet_table_remove(ow->watcher->watch_descriptors, janet_wrap_string(ow->dir_path));
break;
case JANET_ASYNC_EVENT_ERR:
case JANET_ASYNC_EVENT_FAILED:
janet_stream_close(ow->stream);
break;
case JANET_ASYNC_EVENT_COMPLETE: {
if (!watcher->is_watching) {
janet_stream_close(ow->stream);
break;
}
NotifyChange *fni = (NotifyChange *) ow->buf;
while (1) {
/* Got an event */
/* Extract name */
Janet filename;
if (fni->FileNameLength) {
int32_t nbytes = (int32_t) WideCharToMultiByte(CP_UTF8, 0, fni->FileName, fni->FileNameLength / sizeof(wchar_t), NULL, 0, NULL, NULL);
janet_assert(nbytes, "bad utf8 path");
uint8_t *into = janet_string_begin(nbytes);
WideCharToMultiByte(CP_UTF8, 0, fni->FileName, fni->FileNameLength / sizeof(wchar_t), (char *) into, nbytes, NULL, NULL);
filename = janet_wrap_string(janet_string_end(into));
} else {
filename = janet_cstringv("");
}
JanetKV *event = janet_struct_begin(3);
janet_struct_put(event, janet_ckeywordv("type"), janet_ckeywordv(watcher_actions_windows[fni->Action]));
janet_struct_put(event, janet_ckeywordv("file-name"), filename);
janet_struct_put(event, janet_ckeywordv("dir-name"), janet_wrap_string(ow->dir_path));
Janet eventv = janet_wrap_struct(janet_struct_end(event));
janet_channel_give(watcher->channel, eventv);
/* Next event */
if (!fni->NextEntryOffset) break;
fni = (NotifyChange *)((char *)fni + fni->NextEntryOffset);
}
/* Make another call to read directory changes */
read_dir_changes(ow);
janet_async_in_flight(fiber);
}
break;
}
}
static void start_listening_ow(OverlappedWatch *ow) {
read_dir_changes(ow);
JanetStream *stream = ow->stream;
JanetFunction *thunk = janet_thunk_delay(janet_wrap_nil());
JanetFiber *fiber = janet_fiber(thunk, 64, 0, NULL);
fiber->supervisor_channel = janet_root_fiber()->supervisor_channel;
ow->fiber = fiber;
janet_async_start_fiber(fiber, stream, JANET_ASYNC_LISTEN_READ, watcher_callback_read, ow);
}
static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t flags) {
HANDLE handle = CreateFileA(path,
FILE_LIST_DIRECTORY | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (handle == INVALID_HANDLE_VALUE) {
janet_panicv(janet_ev_lasterr());
}
JanetStream *stream = janet_stream(handle, JANET_STREAM_READABLE, NULL);
OverlappedWatch *ow = janet_malloc(sizeof(OverlappedWatch));
memset(ow, 0, sizeof(OverlappedWatch));
ow->stream = stream;
ow->dir_path = janet_cstring(path);
ow->fiber = NULL;
Janet pathv = janet_wrap_string(ow->dir_path);
ow->flags = flags | watcher->default_flags;
ow->watcher = watcher;
ow->overlapped.as.overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL); /* Do we need this */
Janet streamv = janet_wrap_pointer(ow);
janet_table_put(watcher->watch_descriptors, pathv, streamv);
if (watcher->is_watching) {
start_listening_ow(ow);
}
}
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
Janet pathv = janet_cstringv(path);
Janet streamv = janet_table_get(watcher->watch_descriptors, pathv);
if (janet_checktype(streamv, JANET_NIL)) {
janet_panicf("path %v is not being watched", pathv);
}
janet_table_remove(watcher->watch_descriptors, pathv);
OverlappedWatch *ow = janet_unwrap_pointer(streamv);
janet_stream_close(ow->stream);
}
static void janet_watcher_listen(JanetWatcher *watcher) {
if (watcher->is_watching) janet_panic("already watching");
watcher->is_watching = 1;
for (int32_t i = 0; i < watcher->watch_descriptors->capacity; i++) {
const JanetKV *kv = watcher->watch_descriptors->data + i;
if (!janet_checktype(kv->value, JANET_POINTER)) continue;
OverlappedWatch *ow = janet_unwrap_pointer(kv->value);
start_listening_ow(ow);
}
janet_gcroot(janet_wrap_abstract(watcher));
}
static void janet_watcher_unlisten(JanetWatcher *watcher) {
if (!watcher->is_watching) return;
watcher->is_watching = 0;
for (int32_t i = 0; i < watcher->watch_descriptors->capacity; i++) {
const JanetKV *kv = watcher->watch_descriptors->data + i;
if (!janet_checktype(kv->value, JANET_POINTER)) continue;
OverlappedWatch *ow = janet_unwrap_pointer(kv->value);
janet_stream_close(ow->stream);
}
janet_table_clear(watcher->watch_descriptors);
janet_gcunroot(janet_wrap_abstract(watcher));
}
#else
/* Default implementation */
static uint32_t decode_watch_flags(const Janet *options, int32_t n) {
(void) options;
(void) n;
return 0;
}
static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uint32_t default_flags) {
(void) watcher;
(void) channel;
(void) default_flags;
janet_panic("filewatch not supported on this platform");
}
static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t flags) {
(void) watcher;
(void) flags;
(void) path;
janet_panic("filewatch not supported on this platform");
}
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
(void) watcher;
(void) path;
janet_panic("filewatch not supported on this platform");
}
static void janet_watcher_listen(JanetWatcher *watcher) {
(void) watcher;
janet_panic("filewatch not supported on this platform");
}
static void janet_watcher_unlisten(JanetWatcher *watcher) {
(void) watcher;
janet_panic("filewatch not supported on this platform");
}
#endif
/* C Functions */
static int janet_filewatch_mark(void *p, size_t s) {
JanetWatcher *watcher = (JanetWatcher *) p;
(void) s;
if (watcher->channel == NULL) return 0; /* Incomplete initialization */
#ifdef JANET_WINDOWS
for (int32_t i = 0; i < watcher->watch_descriptors->capacity; i++) {
const JanetKV *kv = watcher->watch_descriptors->data + i;
if (!janet_checktype(kv->value, JANET_POINTER)) continue;
OverlappedWatch *ow = janet_unwrap_pointer(kv->value);
janet_mark(janet_wrap_fiber(ow->fiber));
janet_mark(janet_wrap_abstract(ow->stream));
janet_mark(janet_wrap_string(ow->dir_path));
}
#else
janet_mark(janet_wrap_abstract(watcher->stream));
#endif
janet_mark(janet_wrap_abstract(watcher->channel));
janet_mark(janet_wrap_table(watcher->watch_descriptors));
return 0;
}
static const JanetAbstractType janet_filewatch_at = {
"filewatch/watcher",
NULL,
janet_filewatch_mark,
JANET_ATEND_GCMARK
};
JANET_CORE_FN(cfun_filewatch_make,
"(filewatch/new channel & default-flags)",
"Create a new filewatcher that will give events to a channel channel. See `filewatch/add` for available flags.\n\n"
"When an event is triggered by the filewatcher, a struct containing information will be given to channel as with `ev/give`. "
"The contents of the channel depend on the OS, but will contain some common keys:\n\n"
"* `:type` -- the type of the event that was raised.\n\n"
"* `:file-name` -- the base file name of the file that triggered the event.\n\n"
"* `:dir-name` -- the directory name of the file that triggered the event.\n\n"
"Events also will contain keys specific to the host OS.\n\n"
"Windows has no extra properties on events.\n\n"
"Linux has the following extra properties on events:\n\n"
"* `:wd` -- the integer key returned by `filewatch/add` for the path that triggered this.\n\n"
"* `:wd-path` -- the string path for watched directory of file. For files, will be the same as `:file-name`, and for directories, will be the same as `:dir-name`.\n\n"
"* `:cookie` -- a randomized integer used to associate related events, such as :moved-from and :moved-to events.\n\n"
"") {
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
janet_arity(argc, 1, -1);
JanetChannel *channel = janet_getchannel(argv, 0);
JanetWatcher *watcher = janet_abstract(&janet_filewatch_at, sizeof(JanetWatcher));
uint32_t default_flags = decode_watch_flags(argv + 1, argc - 1);
janet_watcher_init(watcher, channel, default_flags);
return janet_wrap_abstract(watcher);
}
JANET_CORE_FN(cfun_filewatch_add,
"(filewatch/add watcher path flag & more-flags)",
"Add a path to the watcher. Available flags depend on the current OS, and are as follows:\n\n"
"Windows/MINGW (flags correspond to `FILE_NOTIFY_CHANGE_*` flags in win32 documentation):\n\n"
"* `:all` - trigger an event for all of the below triggers.\n\n"
"* `:attributes` - `FILE_NOTIFY_CHANGE_ATTRIBUTES`\n\n"
"* `:creation` - `FILE_NOTIFY_CHANGE_CREATION`\n\n"
"* `:dir-name` - `FILE_NOTIFY_CHANGE_DIR_NAME`\n\n"
"* `:last-access` - `FILE_NOTIFY_CHANGE_LAST_ACCESS`\n\n"
"* `:last-write` - `FILE_NOTIFY_CHANGE_LAST_WRITE`\n\n"
"* `:security` - `FILE_NOTIFY_CHANGE_SECURITY`\n\n"
"* `:size` - `FILE_NOTIFY_CHANGE_SIZE`\n\n"
"* `:recursive` - watch subdirectories recursively\n\n"
"Linux (flags correspond to `IN_*` flags from <sys/inotify.h>):\n\n"
"* `:access` - `IN_ACCESS`\n\n"
"* `:all` - `IN_ALL_EVENTS`\n\n"
"* `:attrib` - `IN_ATTRIB`\n\n"
"* `:close-nowrite` - `IN_CLOSE_NOWRITE`\n\n"
"* `:close-write` - `IN_CLOSE_WRITE`\n\n"
"* `:create` - `IN_CREATE`\n\n"
"* `:delete` - `IN_DELETE`\n\n"
"* `:delete-self` - `IN_DELETE_SELF`\n\n"
"* `:ignored` - `IN_IGNORED`\n\n"
"* `:modify` - `IN_MODIFY`\n\n"
"* `:move-self` - `IN_MOVE_SELF`\n\n"
"* `:moved-from` - `IN_MOVED_FROM`\n\n"
"* `:moved-to` - `IN_MOVED_TO`\n\n"
"* `:open` - `IN_OPEN`\n\n"
"* `:q-overflow` - `IN_Q_OVERFLOW`\n\n"
"* `:unmount` - `IN_UNMOUNT`\n\n\n"
"On Windows, events will have the following possible types:\n\n"
"* `:unknown`\n\n"
"* `:added`\n\n"
"* `:removed`\n\n"
"* `:modified`\n\n"
"* `:renamed-old`\n\n"
"* `:renamed-new`\n\n"
"On Linux, events will have a `:type` corresponding to the possible flags, excluding `:all`.\n"
"") {
janet_arity(argc, 2, -1);
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
const char *path = janet_getcstring(argv, 1);
uint32_t flags = watcher->default_flags | decode_watch_flags(argv + 2, argc - 2);
janet_watcher_add(watcher, path, flags);
return argv[0];
}
JANET_CORE_FN(cfun_filewatch_remove,
"(filewatch/remove watcher path)",
"Remove a path from the watcher.") {
janet_fixarity(argc, 2);
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
const char *path = janet_getcstring(argv, 1);
janet_watcher_remove(watcher, path);
return argv[0];
}
JANET_CORE_FN(cfun_filewatch_listen,
"(filewatch/listen watcher)",
"Listen for changes in the watcher.") {
janet_fixarity(argc, 1);
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
janet_watcher_listen(watcher);
return janet_wrap_nil();
}
JANET_CORE_FN(cfun_filewatch_unlisten,
"(filewatch/unlisten watcher)",
"Stop listening for changes on a given watcher.") {
janet_fixarity(argc, 1);
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
janet_watcher_unlisten(watcher);
return janet_wrap_nil();
}
/* Module entry point */
void janet_lib_filewatch(JanetTable *env) {
JanetRegExt cfuns[] = {
JANET_CORE_REG("filewatch/new", cfun_filewatch_make),
JANET_CORE_REG("filewatch/add", cfun_filewatch_add),
JANET_CORE_REG("filewatch/remove", cfun_filewatch_remove),
JANET_CORE_REG("filewatch/listen", cfun_filewatch_listen),
JANET_CORE_REG("filewatch/unlisten", cfun_filewatch_unlisten),
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, cfuns);
}
#endif
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -321,13 +321,9 @@ static void janet_deinit_block(JanetGCObject *mem) {
janet_symbol_deinit(((JanetStringHead *) mem)->data);
break;
case JANET_MEMORY_ARRAY:
case JANET_MEMORY_ARRAY_WEAK:
janet_free(((JanetArray *) mem)->data);
break;
case JANET_MEMORY_TABLE:
case JANET_MEMORY_TABLE_WEAKK:
case JANET_MEMORY_TABLE_WEAKV:
case JANET_MEMORY_TABLE_WEAKKV:
janet_free(((JanetTable *) mem)->data);
break;
case JANET_MEMORY_FIBER: {
@@ -346,9 +342,6 @@ static void janet_deinit_block(JanetGCObject *mem) {
break;
case JANET_MEMORY_ABSTRACT: {
JanetAbstractHead *head = (JanetAbstractHead *)mem;
if (head->type->gcperthread) {
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
}
if (head->type->gc) {
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
}
@@ -500,11 +493,15 @@ void janet_sweep() {
/* If not visited... */
if (!janet_truthy(items[i].value)) {
void *abst = janet_unwrap_abstract(items[i].key);
JanetAbstractHead *head = janet_abstract_head(abst);
if (head->type->gcperthread) {
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
if (0 == janet_abstract_decref(abst)) {
/* Run finalizer */
JanetAbstractHead *head = janet_abstract_head(abst);
if (head->type->gc) {
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
}
/* Free memory */
janet_free(janet_abstract_head(abst));
}
janet_abstract_decref_maybe_free(abst);
/* Mark as tombstone in place */
items[i].key = janet_wrap_nil();
@@ -671,11 +668,13 @@ void janet_clear_memory(void) {
for (int32_t i = 0; i < janet_vm.threaded_abstracts.capacity; i++) {
if (janet_checktype(items[i].key, JANET_ABSTRACT)) {
void *abst = janet_unwrap_abstract(items[i].key);
JanetAbstractHead *head = janet_abstract_head(abst);
if (head->type->gcperthread) {
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
if (0 == janet_abstract_decref(abst)) {
JanetAbstractHead *head = janet_abstract_head(abst);
if (head->type->gc) {
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
}
janet_free(janet_abstract_head(abst));
}
janet_abstract_decref_maybe_free(abst);
}
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -64,7 +64,7 @@ enum JanetMemoryType {
};
/* To allocate collectable memory, one must call janet_alloc, initialize the memory,
* and then call when janet_enablegc when it is initialized and reachable by the gc (on the JANET stack) */
* and then call when janet_enablegc when it is initailize and reachable by the gc (on the JANET stack) */
void *janet_gcalloc(enum JanetMemoryType type, size_t size);
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose & contributors
* Copyright (c) 2023 Calvin Rose & contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -73,13 +73,13 @@ static void *int64_unmarshal(JanetMarshalContext *ctx) {
static void it_s64_tostring(void *p, JanetBuffer *buffer) {
char str[32];
snprintf(str, sizeof(str), "%" PRId64, *((int64_t *)p));
sprintf(str, "%" PRId64, *((int64_t *)p));
janet_buffer_push_cstring(buffer, str);
}
static void it_u64_tostring(void *p, JanetBuffer *buffer) {
char str[32];
snprintf(str, sizeof(str), "%" PRIu64, *((uint64_t *)p));
sprintf(str, "%" PRIu64, *((uint64_t *)p));
janet_buffer_push_cstring(buffer, str);
}
@@ -191,21 +191,21 @@ Janet janet_wrap_u64(uint64_t x) {
JANET_CORE_FN(cfun_it_s64_new,
"(int/s64 value)",
"Create a boxed signed 64 bit integer from a string value or a number.") {
"Create a boxed signed 64 bit integer from a string value.") {
janet_fixarity(argc, 1);
return janet_wrap_s64(janet_unwrap_s64(argv[0]));
}
JANET_CORE_FN(cfun_it_u64_new,
"(int/u64 value)",
"Create a boxed unsigned 64 bit integer from a string value or a number.") {
"Create a boxed unsigned 64 bit integer from a string value.") {
janet_fixarity(argc, 1);
return janet_wrap_u64(janet_unwrap_u64(argv[0]));
}
JANET_CORE_FN(cfun_to_number,
"(int/to-number value)",
"Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int64.") {
"Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int32.") {
janet_fixarity(argc, 1);
if (janet_type(argv[0]) == JANET_ABSTRACT) {
void *abst = janet_unwrap_abstract(argv[0]);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -31,7 +31,6 @@
#ifndef JANET_WINDOWS
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
@@ -42,12 +41,6 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx);
static void *io_file_unmarshal(JanetMarshalContext *ctx);
static Janet io_file_next(void *p, Janet key);
#ifdef JANET_WINDOWS
#include <io.h>
#define ftell _ftelli64
#define fseek _fseeki64
#endif
const JanetAbstractType janet_file_type = {
"core/file",
cfun_io_gc,
@@ -110,12 +103,11 @@ static int32_t checkflags(const uint8_t *str) {
return flags;
}
static void *makef(FILE *f, int32_t flags, size_t bufsize) {
static void *makef(FILE *f, int32_t flags) {
JanetFile *iof = (JanetFile *) janet_abstract(&janet_file_type, sizeof(JanetFile));
iof->file = f;
iof->flags = flags;
iof->vbufsize = bufsize;
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
#ifndef JANET_WINDOWS
/* While we would like fopen to set cloexec by default (like O_CLOEXEC) with the e flag, that is
* not standard. */
if (!(flags & JANET_FILE_NOT_CLOSEABLE))
@@ -134,7 +126,7 @@ JANET_CORE_FN(cfun_io_temp,
// XXX use mkostemp when we can to avoid CLOEXEC race.
FILE *tmp = tmpfile();
if (!tmp)
janet_panicf("unable to create temporary file - %s", janet_strerror(errno));
janet_panicf("unable to create temporary file - %s", strerror(errno));
return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY);
}
@@ -166,17 +158,8 @@ JANET_CORE_FN(cfun_io_fopen,
flags = JANET_FILE_READ;
}
FILE *f = fopen((const char *)fname, (const char *)fmode);
size_t bufsize = BUFSIZ;
if (f != NULL) {
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
struct stat st;
fstat(fileno(f), &st);
if (S_ISDIR(st.st_mode)) {
fclose(f);
janet_panicf("cannot open directory: %s", fname);
}
#endif
bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
if (bufsize != BUFSIZ) {
int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
if (result) {
@@ -184,8 +167,8 @@ JANET_CORE_FN(cfun_io_fopen,
}
}
}
return f ? janet_wrap_abstract(makef(f, flags, bufsize))
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
return f ? janet_makefile(f, flags)
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil())
: janet_wrap_nil();
}
@@ -252,9 +235,9 @@ JANET_CORE_FN(cfun_io_fread,
/* Write bytes to a file */
JANET_CORE_FN(cfun_io_fwrite,
"(file/write f & bytes)",
"Writes to a file `f`. Each value of `bytes` must be a "
"string, buffer, symbol, or keyword. Returns the file.") {
"(file/write f bytes)",
"Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
"file.") {
janet_arity(argc, 1, -1);
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
if (iof->flags & JANET_FILE_CLOSED)
@@ -306,7 +289,7 @@ int janet_file_close(JanetFile *file) {
if (!(file->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
ret = fclose(file->file);
file->flags |= JANET_FILE_CLOSED;
file->file = NULL; /* NULL dereference is easier to debug then other problems */
file->file = NULL; /* NULL derefence is easier to debug then other problems */
return ret;
}
return 0;
@@ -354,7 +337,7 @@ JANET_CORE_FN(cfun_io_fseek,
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
if (iof->flags & JANET_FILE_CLOSED)
janet_panic("file is closed");
int64_t offset = 0;
long int offset = 0;
int whence = SEEK_CUR;
if (argc >= 2) {
const uint8_t *whence_sym = janet_getkeyword(argv, 1);
@@ -368,7 +351,7 @@ JANET_CORE_FN(cfun_io_fseek,
janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]);
}
if (argc == 3) {
offset = (int64_t) janet_getinteger64(argv, 2);
offset = (long) janet_getinteger64(argv, 2);
}
}
if (fseek(iof->file, offset, whence)) janet_panic("error seeking file");
@@ -382,7 +365,7 @@ JANET_CORE_FN(cfun_io_ftell,
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
if (iof->flags & JANET_FILE_CLOSED)
janet_panic("file is closed");
int64_t pos = ftell(iof->file);
long pos = ftell(iof->file);
if (pos == -1) janet_panic("error getting position in file");
return janet_wrap_number((double)pos);
}
@@ -413,23 +396,12 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx) {
JanetFile *iof = (JanetFile *)p;
if (ctx->flags & JANET_MARSHAL_UNSAFE) {
janet_marshal_abstract(ctx, p);
int fno = -1;
#ifdef JANET_WINDOWS
if (iof->flags & JANET_FILE_NOT_CLOSEABLE) {
fno = _fileno(iof->file);
} else {
fno = _dup(_fileno(iof->file));
}
janet_marshal_int(ctx, _fileno(iof->file));
#else
if (iof->flags & JANET_FILE_NOT_CLOSEABLE) {
fno = fileno(iof->file);
} else {
fno = dup(fileno(iof->file));
}
janet_marshal_int(ctx, fileno(iof->file));
#endif
janet_marshal_int(ctx, fno);
janet_marshal_int(ctx, iof->flags);
janet_marshal_size(ctx, iof->vbufsize);
} else {
janet_panic("cannot marshal file in safe mode");
}
@@ -458,11 +430,6 @@ static void *io_file_unmarshal(JanetMarshalContext *ctx) {
} else {
iof->flags = flags;
}
iof->vbufsize = janet_unmarshal_size(ctx);
if (iof->vbufsize != BUFSIZ) {
int result = setvbuf(iof->file, NULL, iof->vbufsize ? _IOFBF : _IONBF, iof->vbufsize);
janet_assert(!result, "unmarshal setvbuf");
}
return iof;
} else {
janet_panic("cannot unmarshal file in safe mode");
@@ -740,15 +707,8 @@ JANET_CORE_FN(cfun_io_eflush,
void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...) {
va_list args;
va_start(args, format);
JanetType xtype;
Janet x;
if (!name || name[0] == '\0') { /* Allow NULL or empty string to just use dflt_file directly */
x = janet_wrap_nil();
xtype = JANET_NIL;
} else {
x = janet_dyn(name);
xtype = janet_type(x);
}
Janet x = janet_dyn(name);
JanetType xtype = janet_type(x);
switch (xtype) {
default:
/* Other values simply do nothing */
@@ -804,11 +764,11 @@ FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags) {
}
JanetFile *janet_makejfile(FILE *f, int32_t flags) {
return makef(f, flags, BUFSIZ);
return makef(f, flags);
}
Janet janet_makefile(FILE *f, int32_t flags) {
return janet_wrap_abstract(makef(f, flags, BUFSIZ));
return janet_wrap_abstract(makef(f, flags));
}
JanetAbstract janet_checkfile(Janet j) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -68,15 +68,8 @@ enum {
LB_STRUCT_PROTO, /* 223 */
#ifdef JANET_EV
LB_THREADED_ABSTRACT, /* 224 */
LB_POINTER_BUFFER, /* 225 */
LB_POINTER_BUFFER, /* 224 */
#endif
LB_TABLE_WEAKK, /* 226 */
LB_TABLE_WEAKV, /* 227 */
LB_TABLE_WEAKKV, /* 228 */
LB_TABLE_WEAKK_PROTO, /* 229 */
LB_TABLE_WEAKV_PROTO, /* 230 */
LB_TABLE_WEAKKV_PROTO, /* 231 */
LB_ARRAY_WEAK, /* 232 */
} LeadBytes;
/* Helper to look inside an entry in an environment */
@@ -276,8 +269,6 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
pushint(st, def->max_arity);
pushint(st, def->constants_length);
pushint(st, def->bytecode_length);
if (def->flags & JANET_FUNCDEF_FLAG_NAMEDARGS)
pushint(st, def->named_args_count);
if (def->flags & JANET_FUNCDEF_FLAG_HASENVS)
pushint(st, def->environments_length);
if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
@@ -578,8 +569,7 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
int32_t i;
JanetArray *a = janet_unwrap_array(x);
MARK_SEEN();
enum JanetMemoryType memtype = janet_gc_type(a);
pushbyte(st, memtype == JANET_MEMORY_ARRAY_WEAK ? LB_ARRAY_WEAK : LB_ARRAY);
pushbyte(st, LB_ARRAY);
pushint(st, a->count);
for (i = 0; i < a->count; i++)
marshal_one(st, a->data[i], flags + 1);
@@ -602,16 +592,7 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
case JANET_TABLE: {
JanetTable *t = janet_unwrap_table(x);
MARK_SEEN();
enum JanetMemoryType memtype = janet_gc_type(t);
if (memtype == JANET_MEMORY_TABLE_WEAKK) {
pushbyte(st, t->proto ? LB_TABLE_WEAKK_PROTO : LB_TABLE_WEAKK);
} else if (memtype == JANET_MEMORY_TABLE_WEAKV) {
pushbyte(st, t->proto ? LB_TABLE_WEAKV_PROTO : LB_TABLE_WEAKV);
} else if (memtype == JANET_MEMORY_TABLE_WEAKKV) {
pushbyte(st, t->proto ? LB_TABLE_WEAKKV_PROTO : LB_TABLE_WEAKKV);
} else {
pushbyte(st, t->proto ? LB_TABLE_PROTO : LB_TABLE);
}
pushbyte(st, t->proto ? LB_TABLE_PROTO : LB_TABLE);
pushint(st, t->count);
if (t->proto)
marshal_one(st, janet_wrap_table(t->proto), flags + 1);
@@ -916,7 +897,6 @@ static const uint8_t *unmarshal_one_def(
def->sourcemap = NULL;
def->symbolmap = NULL;
def->symbolmap_length = 0;
def->named_args_count = 0;
janet_v_push(st->lookup_defs, def);
/* Set default lengths to zero */
@@ -936,8 +916,6 @@ static const uint8_t *unmarshal_one_def(
/* Read some lengths */
constants_length = readnat(st, &data);
bytecode_length = readnat(st, &data);
if (def->flags & JANET_FUNCDEF_FLAG_NAMEDARGS)
def->named_args_count = readnat(st, &data);
if (def->flags & JANET_FUNCDEF_FLAG_HASENVS)
environments_length = readnat(st, &data);
if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
@@ -1439,18 +1417,11 @@ static const uint8_t *unmarshal_one(
}
case LB_REFERENCE:
case LB_ARRAY:
case LB_ARRAY_WEAK:
case LB_TUPLE:
case LB_STRUCT:
case LB_STRUCT_PROTO:
case LB_TABLE:
case LB_TABLE_PROTO:
case LB_TABLE_WEAKK:
case LB_TABLE_WEAKV:
case LB_TABLE_WEAKKV:
case LB_TABLE_WEAKK_PROTO:
case LB_TABLE_WEAKV_PROTO:
case LB_TABLE_WEAKKV_PROTO:
/* Things that open with integers */
{
data++;
@@ -1459,9 +1430,9 @@ static const uint8_t *unmarshal_one(
if (lead != LB_REFERENCE) {
MARSH_EOS(st, data - 1 + len);
}
if (lead == LB_ARRAY || lead == LB_ARRAY_WEAK) {
if (lead == LB_ARRAY) {
/* Array */
JanetArray *array = (lead == LB_ARRAY_WEAK) ? janet_array_weak(len) : janet_array(len);
JanetArray *array = janet_array(len);
array->count = len;
*out = janet_wrap_array(array);
janet_v_push(st->lookup, *out);
@@ -1501,19 +1472,10 @@ static const uint8_t *unmarshal_one(
*out = st->lookup[len];
} else {
/* Table */
JanetTable *t;
if (lead == LB_TABLE_WEAKK_PROTO || lead == LB_TABLE_WEAKK) {
t = janet_table_weakk(len);
} else if (lead == LB_TABLE_WEAKV_PROTO || lead == LB_TABLE_WEAKV) {
t = janet_table_weakv(len);
} else if (lead == LB_TABLE_WEAKKV_PROTO || lead == LB_TABLE_WEAKKV) {
t = janet_table_weakkv(len);
} else {
t = janet_table(len);
}
JanetTable *t = janet_table(len);
*out = janet_wrap_table(t);
janet_v_push(st->lookup, *out);
if (lead == LB_TABLE_PROTO || lead == LB_TABLE_WEAKK_PROTO || lead == LB_TABLE_WEAKV_PROTO || lead == LB_TABLE_WEAKKV_PROTO) {
if (lead == LB_TABLE_PROTO) {
Janet proto;
data = unmarshal_one(st, data, &proto, flags + 1);
janet_asserttype(proto, JANET_TABLE, st);
@@ -1698,7 +1660,6 @@ JANET_CORE_FN(cfun_unmarshal,
"Unmarshal a value from a buffer. An optional lookup table "
"can be provided to allow for aliases to be resolved. Returns the value "
"unmarshalled from the buffer.") {
janet_sandbox_assert(JANET_SANDBOX_UNMARSHAL);
janet_arity(argc, 1, 2);
JanetByteView view = janet_getbytes(argv, 0);
JanetTable *reg = NULL;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -85,10 +85,10 @@ void janet_rng_longseed(JanetRNG *rng, const uint8_t *bytes, int32_t len) {
uint8_t state[16] = {0};
for (int32_t i = 0; i < len; i++)
state[i & 0xF] ^= bytes[i];
rng->a = state[0] + ((uint32_t) state[1] << 8) + ((uint32_t) state[2] << 16) + ((uint32_t) state[3] << 24);
rng->b = state[4] + ((uint32_t) state[5] << 8) + ((uint32_t) state[6] << 16) + ((uint32_t) state[7] << 24);
rng->c = state[8] + ((uint32_t) state[9] << 8) + ((uint32_t) state[10] << 16) + ((uint32_t) state[11] << 24);
rng->d = state[12] + ((uint32_t) state[13] << 8) + ((uint32_t) state[14] << 16) + ((uint32_t) state[15] << 24);
rng->a = state[0] + (state[1] << 8) + (state[2] << 16) + (state[3] << 24);
rng->b = state[4] + (state[5] << 8) + (state[6] << 16) + (state[7] << 24);
rng->c = state[8] + (state[9] << 8) + (state[10] << 16) + (state[11] << 24);
rng->d = state[12] + (state[13] << 8) + (state[14] << 16) + (state[15] << 24);
rng->counter = 0u;
/* a, b, c, d can't all be 0 */
if (rng->a == 0) rng->a = 1u;
@@ -271,30 +271,28 @@ JANET_DEFINE_MATHOP(cosh, "Returns the hyperbolic cosine of x.")
JANET_DEFINE_MATHOP(acosh, "Returns the hyperbolic arccosine of x.")
JANET_DEFINE_MATHOP(sin, "Returns the sine of x.")
JANET_DEFINE_MATHOP(sinh, "Returns the hyperbolic sine of x.")
JANET_DEFINE_MATHOP(asinh, "Returns the hyperbolic arcsine of x.")
JANET_DEFINE_MATHOP(tan, "Returns the tangent of x.")
JANET_DEFINE_MATHOP(tanh, "Returns the hyperbolic tangent of x.")
JANET_DEFINE_MATHOP(atanh, "Returns the hyperbolic arctangent of x.")
JANET_DEFINE_MATHOP(exp, "Returns e to the power of x.")
JANET_DEFINE_MATHOP(exp2, "Returns 2 to the power of x.")
JANET_DEFINE_MATHOP(log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
#ifndef JANET_PLAN9
JANET_DEFINE_MATHOP(expm1, "Returns e to the power of x minus 1.")
JANET_DEFINE_MATHOP(cbrt, "Returns the cube root of x.")
JANET_DEFINE_MATHOP(erf, "Returns the error function of x.")
JANET_DEFINE_MATHOP(erfc, "Returns the complementary error function of x.")
JANET_DEFINE_NAMED_MATHOP("log-gamma", lgamma, "Returns log-gamma(x).")
JANET_DEFINE_NAMED_MATHOP("gamma", tgamma, "Returns gamma(x).")
JANET_DEFINE_MATHOP(atanh, "Returns the hyperbolic arctangent of x.")
JANET_DEFINE_MATHOP(asinh, "Returns the hyperbolic arcsine of x.")
#endif
JANET_DEFINE_MATHOP(log, "Returns the natural logarithm of x.")
JANET_DEFINE_MATHOP(log10, "Returns the log base 10 of x.")
JANET_DEFINE_MATHOP(log2, "Returns the log base 2 of x.")
JANET_DEFINE_MATHOP(sqrt, "Returns the square root of x.")
JANET_DEFINE_MATHOP(cbrt, "Returns the cube root of x.")
JANET_DEFINE_MATHOP(ceil, "Returns the smallest integer value number that is not less than x.")
JANET_DEFINE_MATHOP(floor, "Returns the largest integer value number that is not greater than x.")
JANET_DEFINE_MATHOP(trunc, "Returns the integer between x and 0 nearest to x.")
JANET_DEFINE_MATHOP(round, "Returns the integer nearest to x.")
JANET_DEFINE_MATHOP(log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
JANET_DEFINE_MATHOP(erf, "Returns the error function of x.")
JANET_DEFINE_MATHOP(erfc, "Returns the complementary error function of x.")
JANET_DEFINE_NAMED_MATHOP("log-gamma", lgamma, "Returns log-gamma(x).")
JANET_DEFINE_NAMED_MATHOP("abs", fabs, "Return the absolute value of x.")
JANET_DEFINE_NAMED_MATHOP("gamma", tgamma, "Returns gamma(x).")
#define JANET_DEFINE_MATH2OP(name, fop, signature, doc)\
JANET_CORE_FN(janet_##name, signature, doc) {\
@@ -307,9 +305,7 @@ JANET_CORE_FN(janet_##name, signature, doc) {\
JANET_DEFINE_MATH2OP(atan2, atan2, "(math/atan2 y x)", "Returns the arctangent of y/x. Works even when x is 0.")
JANET_DEFINE_MATH2OP(pow, pow, "(math/pow a x)", "Returns a to the power of x.")
JANET_DEFINE_MATH2OP(hypot, hypot, "(math/hypot a b)", "Returns c from the equation c^2 = a^2 + b^2.")
#ifndef JANET_PLAN9
JANET_DEFINE_MATH2OP(nextafter, nextafter, "(math/next x y)", "Returns the next representable floating point value after x in the direction of y.")
#endif
JANET_CORE_FN(janet_not, "(not x)", "Returns the boolean inverse of x.") {
janet_fixarity(argc, 1);
@@ -353,26 +349,6 @@ JANET_CORE_FN(janet_cfun_lcm, "(math/lcm x y)",
return janet_wrap_number(janet_lcm(x, y));
}
JANET_CORE_FN(janet_cfun_frexp, "(math/frexp x)",
"Returns a tuple of (mantissa, exponent) from number.") {
janet_fixarity(argc, 1);
double x = janet_getnumber(argv, 0);
int exp;
x = frexp(x, &exp);
Janet *result = janet_tuple_begin(2);
result[0] = janet_wrap_number(x);
result[1] = janet_wrap_number((double) exp);
return janet_wrap_tuple(janet_tuple_end(result));
}
JANET_CORE_FN(janet_cfun_ldexp, "(math/ldexp m e)",
"Creates a new number from a mantissa and an exponent.") {
janet_fixarity(argc, 2);
double x = janet_getnumber(argv, 0);
int32_t y = janet_getinteger(argv, 1);
return janet_wrap_number(ldexp(x, y));
}
/* Module entry point */
void janet_lib_math(JanetTable *env) {
JanetRegExt math_cfuns[] = {
@@ -390,17 +366,7 @@ void janet_lib_math(JanetTable *env) {
JANET_CORE_REG("math/log10", janet_log10),
JANET_CORE_REG("math/log2", janet_log2),
JANET_CORE_REG("math/sqrt", janet_sqrt),
#ifndef JANET_PLAN9
JANET_CORE_REG("math/cbrt", janet_cbrt),
JANET_CORE_REG("math/gamma", janet_tgamma),
JANET_CORE_REG("math/log-gamma", janet_lgamma),
JANET_CORE_REG("math/erfc", janet_erfc),
JANET_CORE_REG("math/erf", janet_erf),
JANET_CORE_REG("math/expm1", janet_expm1),
JANET_CORE_REG("math/atanh", janet_atanh),
JANET_CORE_REG("math/asinh", janet_asinh),
JANET_CORE_REG("math/next", janet_nextafter),
#endif
JANET_CORE_REG("math/floor", janet_floor),
JANET_CORE_REG("math/ceil", janet_ceil),
JANET_CORE_REG("math/pow", janet_pow),
@@ -408,6 +374,8 @@ void janet_lib_math(JanetTable *env) {
JANET_CORE_REG("math/sinh", janet_sinh),
JANET_CORE_REG("math/cosh", janet_cosh),
JANET_CORE_REG("math/tanh", janet_tanh),
JANET_CORE_REG("math/atanh", janet_atanh),
JANET_CORE_REG("math/asinh", janet_asinh),
JANET_CORE_REG("math/acosh", janet_acosh),
JANET_CORE_REG("math/atan2", janet_atan2),
JANET_CORE_REG("math/rng", cfun_rng_make),
@@ -417,12 +385,16 @@ void janet_lib_math(JanetTable *env) {
JANET_CORE_REG("math/hypot", janet_hypot),
JANET_CORE_REG("math/exp2", janet_exp2),
JANET_CORE_REG("math/log1p", janet_log1p),
JANET_CORE_REG("math/gamma", janet_tgamma),
JANET_CORE_REG("math/log-gamma", janet_lgamma),
JANET_CORE_REG("math/erfc", janet_erfc),
JANET_CORE_REG("math/erf", janet_erf),
JANET_CORE_REG("math/expm1", janet_expm1),
JANET_CORE_REG("math/trunc", janet_trunc),
JANET_CORE_REG("math/round", janet_round),
JANET_CORE_REG("math/next", janet_nextafter),
JANET_CORE_REG("math/gcd", janet_cfun_gcd),
JANET_CORE_REG("math/lcm", janet_cfun_lcm),
JANET_CORE_REG("math/frexp", janet_cfun_frexp),
JANET_CORE_REG("math/ldexp", janet_cfun_ldexp),
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, math_cfuns);
@@ -441,9 +413,9 @@ void janet_lib_math(JanetTable *env) {
JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX),
"The maximum contiguous integer representable by a 32 bit signed integer");
JANET_CORE_DEF(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE),
"The minimum contiguous integer representable by a double (-(2^53))");
"The minimum contiguous integer representable by a double (2^53)");
JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
"The maximum contiguous integer representable by a double (2^53)");
"The maximum contiguous integer representable by a double (-(2^53))");
#ifdef NAN
JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
#else

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose and contributors.
* Copyright (c) 2023 Calvin Rose and contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -120,55 +120,7 @@ static void janet_net_socknoblock(JSock s) {
#endif
}
/* Allow specifying IPV6 vs. IPV4 (or unix domain socket) */
static int net_get_address_family(Janet x) {
if (janet_checktype(x, JANET_NIL)) {
return AF_UNSPEC;
}
if (janet_keyeq(x, "ipv4")) {
return AF_INET;
}
if (janet_keyeq(x, "ipv6")) {
return AF_INET6;
}
#ifndef JANET_WINDOWS
if (janet_keyeq(x, "unix")) {
return AF_UNIX;
}
#endif
return AF_UNSPEC;
}
/* State machine for async connect */
#ifdef JANET_WINDOWS
typedef struct NetStateConnect {
/* Only used for ConnectEx */
JanetOverlapped overlapped;
} NetStateConnect;
static LPFN_CONNECTEX lazy_get_connectex(JSock sock) {
/* Get ConnectEx */
if (janet_vm.connect_ex_loaded) {
return janet_vm.connect_ex;
}
GUID guid = WSAID_CONNECTEX;
LPFN_CONNECTEX connect_ex_ptr = NULL;
DWORD byte_len = 0;
int success = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
(void*)&guid, sizeof(guid),
(void*)&connect_ex_ptr, sizeof(connect_ex_ptr),
&byte_len, NULL, NULL);
if (success) {
janet_vm.connect_ex = connect_ex_ptr;
} else {
janet_vm.connect_ex = NULL;
}
janet_vm.connect_ex_loaded = 1;
return janet_vm.connect_ex;
}
#endif
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
JanetStream *stream = fiber->ev_stream;
@@ -188,25 +140,19 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
return;
}
#ifdef JANET_WINDOWS
/* We should be using ConnectEx here */
int res = 0;
int size = sizeof(res);
int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_CONNECT_TIME, (char *)&res, &size);
if (r == NO_ERROR && res == 0xFFFFFFFF) {
return; /* This apparently indicates we haven't yet gotten a connection */
}
const int no_error = NO_ERROR;
int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_ERROR, (char *)&res, &size);
#else
int res = 0;
socklen_t size = sizeof(res);
socklen_t size = sizeof res;
int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
const int no_error = 0;
#endif
if (r == no_error) {
if (r == 0) {
if (res == 0) {
janet_schedule(fiber, janet_wrap_abstract(stream));
} else {
janet_cancel(fiber, janet_cstringv(janet_strerror(res)));
janet_cancel(fiber, janet_cstringv(strerror(res)));
stream->flags |= JANET_STREAM_TOCLOSE;
}
} else {
@@ -216,8 +162,8 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
janet_async_end(fiber);
}
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream, void *state) {
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, state);
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL);
}
/* State machine for accepting connections. */
@@ -225,7 +171,7 @@ static JANET_NO_RETURN void net_sched_connect(JanetStream *stream, void *state)
#ifdef JANET_WINDOWS
typedef struct {
JanetOverlapped overlapped;
WSAOVERLAPPED overlapped;
JanetFunction *function;
JanetStream *lstream;
JanetStream *astream;
@@ -288,7 +234,7 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
Janet err;
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
memset(&state->overlapped, 0, sizeof(JanetOverlapped));
memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
memset(&state->buf, 0, 1024);
state->function = fun;
state->lstream = stream;
@@ -309,7 +255,7 @@ static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet
JanetStream *astream = make_stream(asock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
state->astream = astream;
int socksize = sizeof(SOCKADDR_STORAGE) + 16;
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped.as.wsaoverlapped)) {
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped)) {
int code = WSAGetLastError();
if (code == WSA_IO_PENDING) {
/* indicates io is happening async */
@@ -373,13 +319,12 @@ JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunctio
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
memset(state, 0, sizeof(NetStateAccept));
state->function = fun;
if (fun) janet_stream_level_triggered(stream);
janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
}
#endif
/* Address info */
/* Adress info */
static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) {
JanetKeyword stype = janet_optkeyword(argv, argc, n, NULL);
@@ -395,7 +340,7 @@ static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) {
/* Needs argc >= offset + 2 */
/* For unix paths, just rertuns a single sockaddr and sets *is_unix to 1,
* otherwise 0. Also, ignores is_bind when is a unix socket. */
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix, socklen_t *sizeout) {
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix) {
/* Unix socket support - not yet supported on windows. */
#ifndef JANET_WINDOWS
if (janet_keyeq(argv[offset], "unix")) {
@@ -406,14 +351,15 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
}
saddr->sun_family = AF_UNIX;
size_t path_size = sizeof(saddr->sun_path);
snprintf(saddr->sun_path, path_size, "%s", path);
*sizeout = sizeof(struct sockaddr_un);
#ifdef JANET_LINUX
if (path[0] == '@') {
saddr->sun_path[0] = '\0';
*sizeout = offsetof(struct sockaddr_un, sun_path) + janet_string_length(path);
}
snprintf(saddr->sun_path + 1, path_size - 1, "%s", path + 1);
} else
#endif
{
snprintf(saddr->sun_path, path_size, "%s", path);
}
*is_unix = 1;
return (struct addrinfo *) saddr;
}
@@ -438,11 +384,6 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
janet_panicf("could not get address info: %s", gai_strerror(status));
}
*is_unix = 0;
#ifdef JANET_WINDOWS
*sizeout = 0;
#else
*sizeout = sizeof(struct sockaddr_un);
#endif
return ai;
}
@@ -463,13 +404,12 @@ JANET_CORE_FN(cfun_net_sockaddr,
int socktype = janet_get_sockettype(argv, argc, 2);
int is_unix = 0;
int make_arr = (argc >= 3 && janet_truthy(argv[3]));
socklen_t addrsize = 0;
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, &addrsize);
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
#ifndef JANET_WINDOWS
/* no unix domain socket support on windows yet */
if (is_unix) {
void *abst = janet_abstract(&janet_address_type, addrsize);
memcpy(abst, ai, addrsize);
void *abst = janet_abstract(&janet_address_type, sizeof(struct sockaddr_un));
memcpy(abst, ai, sizeof(struct sockaddr_un));
Janet ret = janet_wrap_abstract(abst);
return make_arr ? janet_wrap_array(janet_array_n(&ret, 1)) : ret;
}
@@ -520,8 +460,7 @@ JANET_CORE_FN(cfun_net_connect,
}
/* Where we're connecting to */
socklen_t addrlen = 0;
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, &addrlen);
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
/* Check if we're binding address */
struct addrinfo *binding = NULL;
@@ -546,6 +485,7 @@ JANET_CORE_FN(cfun_net_connect,
/* Create socket */
JSock sock = JSOCKDEFAULT;
void *addr = NULL;
socklen_t addrlen = 0;
#ifndef JANET_WINDOWS
if (is_unix) {
sock = socket(AF_UNIX, socktype | JSOCKFLAGS, 0);
@@ -555,6 +495,7 @@ JANET_CORE_FN(cfun_net_connect,
janet_panicf("could not create socket: %V", v);
}
addr = (void *) ai;
addrlen = sizeof(struct sockaddr_un);
} else
#endif
{
@@ -601,52 +542,18 @@ JANET_CORE_FN(cfun_net_connect,
}
/* Wrap socket in abstract type JanetStream */
uint32_t udp_flag = 0;
if (socktype == SOCK_DGRAM) udp_flag = JANET_STREAM_UDPSERVER;
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE | udp_flag);
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
/* Set up the socket for non-blocking IO before connecting */
janet_net_socknoblock(sock);
/* Connect to socket */
#ifdef JANET_WINDOWS
int status = 0;
int err = 0;
LPFN_CONNECTEX connect_ex = NULL;
if (socktype == SOCK_STREAM && ((connect_ex = lazy_get_connectex(sock)))) {
/* Prefer ConnecEx as it works well with overlapped IO. */
janet_net_socknoblock(sock);
NetStateConnect *state = janet_malloc(sizeof(NetStateConnect));
memset(state, 0, sizeof(NetStateConnect));
BOOL success = connect_ex(sock, addr, addrlen, NULL, 0, NULL, &state->overlapped.as.overlapped);
freeaddrinfo(ai);
if (success) {
/* Did not fail */
} else {
int err = WSAGetLastError();
if (err == ERROR_IO_PENDING) {
/* Did not actually fail yet */
} else {
janet_free(state);
Janet lasterr = janet_ev_lasterr();
janet_panicf("could not connect socket (ConnectEx): %V", lasterr);
}
}
net_sched_connect(stream, state);
} else {
/* Default to blocking connect if ConnectEx not available */
status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
err = WSAGetLastError();
freeaddrinfo(ai);
/* Set up the socket for non-blocking IO after connecting on windows by default */
janet_net_socknoblock(sock);
}
int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
int err = WSAGetLastError();
freeaddrinfo(ai);
#else
/* Set up the socket for non-blocking IO before connecting */
janet_net_socknoblock(sock);
int status;
do {
status = connect(sock, addr, addrlen);
} while (status == -1 && errno == EINTR);
int status = connect(sock, addr, addrlen);
int err = errno;
if (is_unix) {
janet_free(ai);
@@ -655,19 +562,10 @@ JANET_CORE_FN(cfun_net_connect,
}
#endif
if (status == 0) {
/* Connect completed synchronously (common for unix domain sockets).
* Return the stream directly without scheduling an async wait,
* as edge-triggered kqueue may not signal EVFILT_WRITE if the socket
* is already connected when registered. */
return janet_wrap_abstract(stream);
}
if (status) {
#ifdef JANET_WINDOWS
if (status == SOCKET_ERROR) {
if (err != WSAEWOULDBLOCK) {
#else
if (status == -1) {
if (err != EINPROGRESS) {
#endif
JSOCKCLOSE(sock);
@@ -676,84 +574,20 @@ JANET_CORE_FN(cfun_net_connect,
}
}
net_sched_connect(stream, NULL);
net_sched_connect(stream);
}
JANET_CORE_FN(cfun_net_socket,
"(net/socket &opt type address-family)",
"Creates a new unbound socket. Type is an optional keyword, "
"either a :stream (usually tcp), or :datagram (usually udp). The default is :stream. "
"`address-family` should be one of :ipv4 or :ipv6.") {
janet_arity(argc, 0, 2);
int socktype = janet_get_sockettype(argv, argc, 0);
/* Create socket */
JSock sfd = JSOCKDEFAULT;
struct addrinfo *ai = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = socktype;
#ifdef AI_NUMERICSERV
hints.ai_flags = AI_NUMERICSERV; /* Explicitly prevent name resolution */
#else
hints.ai_flags = 0;
#endif
if (argc >= 2) {
hints.ai_family = net_get_address_family(argv[1]);
}
int status = getaddrinfo(NULL, "0", &hints, &ai);
if (status) {
janet_panicf("could not get address info: %s", gai_strerror(status));
}
struct addrinfo *rp = NULL;
for (rp = ai; rp != NULL; rp = rp->ai_next) {
#ifdef JANET_WINDOWS
sfd = WSASocketW(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
#else
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
#endif
if (JSOCKVALID(sfd)) {
break;
}
}
freeaddrinfo(ai);
if (!JSOCKVALID(sfd)) {
Janet v = janet_ev_lasterr();
janet_panicf("could not create socket: %V", v);
}
/* Wrap socket in abstract type JanetStream */
uint32_t udp_flag = 0;
if (socktype == SOCK_DGRAM) udp_flag = JANET_STREAM_UDPSERVER;
JanetStream *stream = make_stream(sfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE | udp_flag);
/* Set up the socket for non-blocking IO */
janet_net_socknoblock(sfd);
return janet_wrap_abstract(stream);
}
static const char *serverify_socket(JSock sfd, int reuse_addr, int reuse_port) {
static const char *serverify_socket(JSock sfd) {
/* Set various socket options */
int enable = 1;
if (reuse_addr) {
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
return "setsockopt(SO_REUSEADDR) failed";
}
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
return "setsockopt(SO_REUSEADDR) failed";
}
if (reuse_port) {
#ifdef SO_REUSEPORT
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
return "setsockopt(SO_REUSEPORT) failed";
}
#else
(void) reuse_port;
#endif
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
return "setsockopt(SO_REUSEPORT) failed";
}
#endif
janet_net_socknoblock(sfd);
return NULL;
}
@@ -807,22 +641,19 @@ JANET_CORE_FN(cfun_net_shutdown,
}
JANET_CORE_FN(cfun_net_listen,
"(net/listen host port &opt type no-reuse)",
"(net/listen host port &opt type)",
"Creates a server. Returns a new stream that is neither readable nor "
"writeable. Use net/accept or net/accept-loop be to handle connections and start the server. "
"The type parameter specifies the type of network connection, either "
"a :stream (usually tcp), or :datagram (usually udp). If not specified, the default is "
":stream. The host and port arguments are the same as in net/address. The last boolean parameter `no-reuse` will "
"disable the use of `SO_REUSEADDR` and `SO_REUSEPORT` when creating a server on some operating systems.") {
":stream. The host and port arguments are the same as in net/address.") {
janet_sandbox_assert(JANET_SANDBOX_NET_LISTEN);
janet_arity(argc, 2, 4);
janet_arity(argc, 2, 3);
/* Get host, port, and handler*/
int socktype = janet_get_sockettype(argv, argc, 2);
int is_unix = 0;
socklen_t addrlen = 0;
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix, &addrlen);
int reuse = !(argc >= 4 && janet_truthy(argv[3]));
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix);
JSock sfd = JSOCKDEFAULT;
#ifndef JANET_WINDOWS
@@ -832,8 +663,8 @@ JANET_CORE_FN(cfun_net_listen,
janet_free(ai);
janet_panicf("could not create socket: %V", janet_ev_lasterr());
}
const char *err = serverify_socket(sfd, reuse, 0);
if (NULL != err || bind(sfd, (struct sockaddr *)ai, addrlen)) {
const char *err = serverify_socket(sfd);
if (NULL != err || bind(sfd, (struct sockaddr *)ai, sizeof(struct sockaddr_un))) {
JSOCKCLOSE(sfd);
janet_free(ai);
if (err) {
@@ -855,7 +686,7 @@ JANET_CORE_FN(cfun_net_listen,
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
#endif
if (!JSOCKVALID(sfd)) continue;
const char *err = serverify_socket(sfd, reuse, reuse);
const char *err = serverify_socket(sfd);
if (NULL != err) {
JSOCKCLOSE(sfd);
continue;
@@ -997,7 +828,7 @@ JANET_CORE_FN(cfun_stream_accept_loop,
JANET_CORE_FN(cfun_stream_accept,
"(net/accept stream &opt timeout)",
"Get the next connection on a server stream. This would usually be called in a loop in a dedicated fiber. "
"Takes an optional timeout in seconds, after which will raise an error. "
"Takes an optional timeout in seconds, after which will return nil. "
"Returns a new duplex stream which represents a connection to the client.") {
janet_arity(argc, 1, 2);
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
@@ -1012,7 +843,7 @@ JANET_CORE_FN(cfun_stream_read,
"Read up to n bytes from a stream, suspending the current fiber until the bytes are available. "
"`n` can also be the keyword `:all` to read into the buffer until end of stream. "
"If less than n bytes are available (and more than 0), will push those bytes and return early. "
"Takes an optional timeout in seconds, after which will raise an error. "
"Takes an optional timeout in seconds, after which will return nil. "
"Returns a buffer with up to n more bytes in it, or raises an error if the read failed.") {
janet_arity(argc, 2, 4);
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
@@ -1032,7 +863,7 @@ JANET_CORE_FN(cfun_stream_read,
JANET_CORE_FN(cfun_stream_chunk,
"(net/chunk stream nbytes &opt buf timeout)",
"Same a net/read, but will wait for all n bytes to arrive rather than return early. "
"Takes an optional timeout in seconds, after which will raise an error.") {
"Takes an optional timeout in seconds, after which will return nil.") {
janet_arity(argc, 2, 4);
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
janet_stream_flags(stream, JANET_STREAM_READABLE | JANET_STREAM_SOCKET);
@@ -1046,7 +877,7 @@ JANET_CORE_FN(cfun_stream_chunk,
JANET_CORE_FN(cfun_stream_recv_from,
"(net/recv-from stream nbytes buf &opt timeout)",
"Receives data from a server stream and puts it into a buffer. Returns the socket-address the "
"packet came from. Takes an optional timeout in seconds, after which will raise an error.") {
"packet came from. Takes an optional timeout in seconds, after which will return nil.") {
janet_arity(argc, 3, 4);
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
janet_stream_flags(stream, JANET_STREAM_UDPSERVER | JANET_STREAM_SOCKET);
@@ -1060,7 +891,7 @@ JANET_CORE_FN(cfun_stream_recv_from,
JANET_CORE_FN(cfun_stream_write,
"(net/write stream data &opt timeout)",
"Write data to a stream, suspending the current fiber until the write "
"completes. Takes an optional timeout in seconds, after which will raise an error. "
"completes. Takes an optional timeout in seconds, after which will return nil. "
"Returns nil, or raises an error if the write failed.") {
janet_arity(argc, 2, 3);
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
@@ -1079,7 +910,7 @@ JANET_CORE_FN(cfun_stream_write,
JANET_CORE_FN(cfun_stream_send_to,
"(net/send-to stream dest data &opt timeout)",
"Writes a datagram to a server stream. dest is a the destination address of the packet. "
"Takes an optional timeout in seconds, after which will raise an error. "
"Takes an optional timeout in seconds, after which will return nil. "
"Returns stream.") {
janet_arity(argc, 3, 4);
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
@@ -1130,8 +961,6 @@ static const struct sockopt_type sockopt_type_list[] = {
#ifndef JANET_NO_IPV6
{ "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER },
{ "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER },
{ "ipv6-multicast-hops", IPPROTO_IPV6, IPV6_MULTICAST_HOPS, JANET_NUMBER },
{ "ipv6-unicast-hops", IPPROTO_IPV6, IPV6_UNICAST_HOPS, JANET_NUMBER },
#endif
{ NULL, 0, 0, JANET_POINTER }
};
@@ -1148,10 +977,7 @@ JANET_CORE_FN(cfun_net_setsockopt,
"- :ip-add-membership string\n"
"- :ip-drop-membership string\n"
"- :ipv6-join-group string\n"
"- :ipv6-leave-group string\n"
"- :ipv6-multicast-hops number\n"
"- :ipv6-unicast-hops number\n"
) {
"- :ipv6-leave-group string\n") {
janet_arity(argc, 3, 3);
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
janet_stream_flags(stream, JANET_STREAM_SOCKET);
@@ -1170,7 +996,6 @@ JANET_CORE_FN(cfun_net_setsockopt,
}
union {
unsigned char v_uchar;
int v_int;
struct ip_mreq v_mreq;
#ifndef JANET_NO_IPV6
@@ -1185,19 +1010,8 @@ JANET_CORE_FN(cfun_net_setsockopt,
val.v_int = janet_getboolean(argv, 2);
optlen = sizeof(val.v_int);
} else if (st->type == JANET_NUMBER) {
#if defined(JANET_BSD) || defined(JANET_ILLUMOS)
int v_int = janet_getinteger(argv, 2);
if (st->optname == IP_MULTICAST_TTL) {
val.v_uchar = v_int;
optlen = sizeof(val.v_uchar);
} else {
val.v_int = v_int;
optlen = sizeof(val.v_int);
}
#else
val.v_int = janet_getinteger(argv, 2);
optlen = sizeof(val.v_int);
#endif
} else if (st->optname == IP_ADD_MEMBERSHIP || st->optname == IP_DROP_MEMBERSHIP) {
const char *addr = janet_getcstring(argv, 2);
memset(&val.v_mreq, 0, sizeof val.v_mreq);
@@ -1220,7 +1034,7 @@ JANET_CORE_FN(cfun_net_setsockopt,
int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen);
if (r == -1) {
janet_panicf("setsockopt(%q): %s", argv[1], janet_strerror(errno));
janet_panicf("setsockopt(%q): %s", argv[1], strerror(errno));
}
return janet_wrap_nil();
@@ -1252,7 +1066,6 @@ void janet_lib_net(JanetTable *env) {
JanetRegExt net_cfuns[] = {
JANET_CORE_REG("net/address", cfun_net_sockaddr),
JANET_CORE_REG("net/listen", cfun_net_listen),
JANET_CORE_REG("net/socket", cfun_net_socket),
JANET_CORE_REG("net/accept", cfun_stream_accept),
JANET_CORE_REG("net/accept-loop", cfun_stream_accept_loop),
JANET_CORE_REG("net/read", cfun_stream_read),
@@ -1276,8 +1089,6 @@ void janet_net_init(void) {
#ifdef JANET_WINDOWS
WSADATA wsaData;
janet_assert(!WSAStartup(MAKEWORD(2, 2), &wsaData), "could not start winsock");
janet_vm.connect_ex_loaded = 0;
janet_vm.connect_ex = NULL;
#endif
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -105,6 +105,15 @@ static int to_hex(uint8_t c) {
}
}
typedef int (*Consumer)(JanetParser *p, JanetParseState *state, uint8_t c);
struct JanetParseState {
int32_t counter;
int32_t argn;
int flags;
size_t line;
size_t column;
Consumer consumer;
};
/* Define a stack on the main parser struct */
#define DEF_PARSER_STACK(NAME, T, STACK, STACKCOUNT, STACKCAP) \
@@ -222,7 +231,7 @@ static void delim_error(JanetParser *parser, size_t stack_index, char c, const c
janet_buffer_push_u8(buffer, '`');
}
}
janet_formatb(buffer, " opened at line %d, column %d", (int32_t) s->line, (int32_t) s->column);
janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
}
parser->error = (const char *) janet_string(buffer->data, buffer->count);
parser->flag |= JANET_PARSER_GENERATED_ERROR;
@@ -354,7 +363,8 @@ static int stringend(JanetParser *p, JanetParseState *state) {
JanetParseState top = p->states[p->statecount - 1];
int32_t indent_col = (int32_t) top.column - 1;
uint8_t *r = bufstart, *end = r + buflen;
/* Unless there are only spaces before EOLs, disable reindenting */
/* Check if there are any characters before the start column -
* if so, do not reindent. */
int reindent = 1;
while (reindent && (r < end)) {
if (*r++ == '\n') {
@@ -364,36 +374,34 @@ static int stringend(JanetParser *p, JanetParseState *state) {
break;
}
}
if ((r + 1) < end && *r == '\r' && *(r + 1) == '\n') reindent = 1;
}
}
/* Now reindent if able */
if (reindent) {
/* Now reindent if able to, otherwise just drop leading newline. */
if (!reindent) {
if (buflen > 0 && bufstart[0] == '\n') {
buflen--;
bufstart++;
}
} else {
uint8_t *w = bufstart;
r = bufstart;
while (r < end) {
if (*r == '\n') {
*w++ = *r++;
if (r == bufstart) {
/* Skip leading newline */
r++;
} else {
*w++ = *r++;
}
for (int32_t j = 0; (r < end) && (*r != '\n') && (j < indent_col); j++, r++);
if ((r + 1) < end && *r == '\r' && *(r + 1) == '\n') *w++ = *r++;
} else {
*w++ = *r++;
}
}
buflen = (int32_t)(w - bufstart);
}
/* Check for leading EOL so we can remove it */
if (buflen > 1 && bufstart[0] == '\r' && bufstart[1] == '\n') { /* Windows EOL */
buflen = buflen - 2;
bufstart = bufstart + 2;
} else if (buflen > 0 && bufstart[0] == '\n') { /* Unix EOL */
buflen--;
bufstart++;
}
/* Check for trailing EOL so we can remove it */
if (buflen > 1 && bufstart[buflen - 2] == '\r' && bufstart[buflen - 1] == '\n') { /* Windows EOL */
buflen = buflen - 2;
} else if (buflen > 0 && bufstart[buflen - 1] == '\n') { /* Unix EOL */
/* Check for trailing newline character so we can remove it */
if (buflen > 0 && bufstart[buflen - 1] == '\n') {
buflen--;
}
}
@@ -459,13 +467,8 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
return 0;
}
ret = janet_keywordv(p->buf + 1, blen - 1);
#ifdef JANET_INT_TYPES
} else if (start_num && !janet_scan_numeric(p->buf, blen, &ret)) {
(void) numval;
#else
} else if (start_num && !janet_scan_number(p->buf, blen, &numval)) {
ret = janet_wrap_number(numval);
#endif
} else if (!check_str_const("nil", p->buf, blen)) {
ret = janet_wrap_nil();
} else if (!check_str_const("false", p->buf, blen)) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -134,7 +134,7 @@ static LineCol get_linecol_from_position(PegState *s, int32_t position) {
* a newline character is consider to be on the same line as the character before
* (\n is line terminator, not line separator).
* - in the not-found case, we still want to find the greatest-indexed newline that
* is before position. we use that to calculate the line and column.
* is before position. we use that to calcuate the line and column.
* - in the case that lo = 0 and s->linemap[0] is still greater than position, we
* are on the first line and our column is position + 1. */
int32_t hi = s->linemaplen; /* hi is greater than the actual line */
@@ -194,41 +194,6 @@ tail:
return memcmp(text, rule + 2, len) ? NULL : text + len;
}
case RULE_DEBUG: {
char buffer[32] = {0};
size_t len = (size_t)(s->outer_text_end - text);
memcpy(buffer, text, (len > 31 ? 31 : len));
janet_eprintf("?? at [%s] (index %d)\n", buffer, (int32_t)(text - s->text_start));
int has_color = janet_truthy(janet_dyn("err-color"));
/* Accumulate buffer */
if (s->scratch->count) {
janet_eprintf("accumulate buffer: %v\n", janet_wrap_buffer(s->scratch));
}
/* Normal captures */
if (s->captures->count) {
janet_eprintf("stack [%d]:\n", s->captures->count);
for (int32_t i = 0; i < s->captures->count; i++) {
if (has_color) {
janet_eprintf(" [%d]: %M\n", i, s->captures->data[i]);
} else {
janet_eprintf(" [%d]: %m\n", i, s->captures->data[i]);
}
}
}
/* Tagged captures */
if (s->tagged_captures->count) {
janet_eprintf("tag stack [%d]:\n", s->tagged_captures->count);
for (int32_t i = 0; i < s->tagged_captures->count; i++) {
if (has_color) {
janet_eprintf(" [%d] tag=%d: %M\n", i, (int32_t) s->tags->data[i], s->tagged_captures->data[i]);
} else {
janet_eprintf(" [%d] tag=%d: %m\n", i, (int32_t) s->tags->data[i], s->tagged_captures->data[i]);
}
}
}
return text;
}
case RULE_NCHAR: {
uint32_t n = rule[1];
return (text + n > s->text_end) ? NULL : text + n;
@@ -377,7 +342,7 @@ tail:
while (captured < hi) {
CapState cs2 = cap_save(s);
next_text = peg_rule(s, rule_a, text);
if (!next_text || ((next_text == text) && (hi == UINT32_MAX))) {
if (!next_text || next_text == text) {
cap_load(s, cs2);
break;
}
@@ -500,16 +465,6 @@ tail:
return result;
}
case RULE_ONLY_TAGS: {
CapState cs = cap_save(s);
down1(s);
const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text);
up1(s);
if (!result) return NULL;
cap_load_keept(s, cs);
return result;
}
case RULE_GROUP: {
uint32_t tag = rule[2];
int oldmode = s->mode;
@@ -531,30 +486,6 @@ tail:
return result;
}
case RULE_NTH: {
uint32_t nth = rule[1];
if (nth > INT32_MAX) nth = INT32_MAX;
uint32_t tag = rule[3];
int oldmode = s->mode;
CapState cs = cap_save(s);
s->mode = PEG_MODE_NORMAL;
down1(s);
const uint8_t *result = peg_rule(s, s->bytecode + rule[2], text);
up1(s);
s->mode = oldmode;
if (!result) return NULL;
int32_t num_sub_captures = s->captures->count - cs.cap;
Janet cap;
if (num_sub_captures > (int32_t) nth) {
cap = s->captures->data[cs.cap + nth];
} else {
return NULL;
}
cap_load_keept(s, cs);
pushcap(s, cap, tag);
return result;
}
case RULE_SUB: {
const uint8_t *text_start = text;
const uint32_t *rule_window = s->bytecode + rule[1];
@@ -579,85 +510,45 @@ tail:
return window_end;
}
case RULE_TIL: {
const uint32_t *rule_terminus = s->bytecode + rule[1];
const uint32_t *rule_subpattern = s->bytecode + rule[2];
const uint8_t *terminus_start = text;
const uint8_t *terminus_end = NULL;
down1(s);
while (terminus_start <= s->text_end) {
CapState cs2 = cap_save(s);
terminus_end = peg_rule(s, rule_terminus, terminus_start);
cap_load(s, cs2);
if (terminus_end) {
break;
}
terminus_start++;
}
up1(s);
if (!terminus_end) {
return NULL;
}
const uint8_t *saved_end = s->text_end;
s->text_end = terminus_start;
down1(s);
const uint8_t *matched = peg_rule(s, rule_subpattern, text);
up1(s);
s->text_end = saved_end;
if (!matched) {
return NULL;
}
return terminus_end;
}
case RULE_SPLIT: {
const uint8_t *saved_end = s->text_end;
const uint32_t *rule_separator = s->bytecode + rule[1];
const uint32_t *rule_subpattern = s->bytecode + rule[2];
const uint8_t *chunk_start = text;
const uint8_t *chunk_end = NULL;
while (text <= saved_end) {
/* Find next split (or end of text) */
const uint8_t *separator_end = NULL;
do {
const uint8_t *text_start = text;
CapState cs = cap_save(s);
down1(s);
while (text <= saved_end) {
chunk_end = text;
const uint8_t *check = peg_rule(s, rule_separator, text);
while (text <= s->text_end) {
separator_end = peg_rule(s, rule_separator, text);
cap_load(s, cs);
if (check) {
text = check;
if (separator_end) {
break;
}
text++;
}
up1(s);
/* Match between splits */
s->text_end = chunk_end;
if (separator_end) {
s->text_end = text;
text = separator_end;
}
down1(s);
const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, chunk_start);
const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, text_start);
up1(s);
s->text_end = saved_end;
if (!subpattern_end) return NULL; /* Don't match anything */
/* Ensure forward progress */
if (text == chunk_start) return NULL;
chunk_start = text;
}
if (!subpattern_end) {
return NULL;
}
} while (separator_end);
s->text_end = saved_end;
return s->text_end;
}
case RULE_REPLACE:
case RULE_MATCHSPLICE:
case RULE_MATCHTIME: {
uint32_t tag = rule[3];
int oldmode = s->mode;
@@ -698,17 +589,8 @@ tail:
break;
}
cap_load_keept(s, cs);
if (rule[0] != RULE_REPLACE && !janet_truthy(cap)) return NULL; /* matchtime or matchtime flatten */
const Janet *elements = NULL;
int32_t len = 0;
if ((rule[0] == RULE_MATCHSPLICE) && janet_indexed_view(cap, &elements, &len)) {
/* unpack and flatten capture */
for (int32_t i = 0; i < len; i++) {
pushcap(s, elements[i], tag);
}
} else {
pushcap(s, cap, tag);
}
if (rule[0] == RULE_MATCHTIME && !janet_truthy(cap)) return NULL;
pushcap(s, cap, tag);
return result;
}
@@ -785,11 +667,11 @@ tail:
case RULE_READINT: {
uint32_t tag = rule[2];
uint32_t signedness = rule[1] & 0x10;
uint32_t endianness = rule[1] & 0x20;
uint32_t endianess = rule[1] & 0x20;
int width = (int)(rule[1] & 0xF);
if (text + width > s->text_end) return NULL;
uint64_t accum = 0;
if (endianness) {
if (endianess) {
/* BE */
for (int i = 0; i < width; i++) accum = (accum << 8) | text[i];
} else {
@@ -1179,9 +1061,6 @@ static void spec_thru(Builder *b, int32_t argc, const Janet *argv) {
static void spec_drop(Builder *b, int32_t argc, const Janet *argv) {
spec_onerule(b, argc, argv, RULE_DROP);
}
static void spec_only_tags(Builder *b, int32_t argc, const Janet *argv) {
spec_onerule(b, argc, argv, RULE_ONLY_TAGS);
}
/* Rule of the form [rule, tag] */
static void spec_cap1(Builder *b, int32_t argc, const Janet *argv, uint32_t op) {
@@ -1205,15 +1084,6 @@ static void spec_unref(Builder *b, int32_t argc, const Janet *argv) {
spec_cap1(b, argc, argv, RULE_UNREF);
}
static void spec_nth(Builder *b, int32_t argc, const Janet *argv) {
peg_arity(b, argc, 2, 3);
Reserve r = reserve(b, 4);
uint32_t nth = peg_getnat(b, argv[0]);
uint32_t rule = peg_compile1(b, argv[1]);
uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
emit_3(r, RULE_NTH, nth, rule, tag);
}
static void spec_capture_number(Builder *b, int32_t argc, const Janet *argv) {
peg_arity(b, argc, 1, 3);
Reserve r = reserve(b, 4);
@@ -1280,14 +1150,6 @@ static void spec_constant(Builder *b, int32_t argc, const Janet *argv) {
emit_2(r, RULE_CONSTANT, emit_constant(b, argv[0]), tag);
}
static void spec_debug(Builder *b, int32_t argc, const Janet *argv) {
peg_arity(b, argc, 0, 0);
Reserve r = reserve(b, 1);
uint32_t empty = 0;
(void) argv;
emit_rule(r, RULE_DEBUG, 0, &empty);
}
static void spec_replace(Builder *b, int32_t argc, const Janet *argv) {
peg_arity(b, argc, 2, 3);
Reserve r = reserve(b, 4);
@@ -1297,7 +1159,7 @@ static void spec_replace(Builder *b, int32_t argc, const Janet *argv) {
emit_3(r, RULE_REPLACE, subrule, constant, tag);
}
static void spec_matchtime_impl(Builder *b, int32_t argc, const Janet *argv, uint32_t op) {
static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
peg_arity(b, argc, 2, 3);
Reserve r = reserve(b, 4);
uint32_t subrule = peg_compile1(b, argv[0]);
@@ -1308,15 +1170,7 @@ static void spec_matchtime_impl(Builder *b, int32_t argc, const Janet *argv, uin
}
uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
uint32_t cindex = emit_constant(b, fun);
emit_3(r, op, subrule, cindex, tag);
}
static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
spec_matchtime_impl(b, argc, argv, RULE_MATCHTIME);
}
static void spec_matchtime_splice(Builder *b, int32_t argc, const Janet *argv) {
spec_matchtime_impl(b, argc, argv, RULE_MATCHSPLICE);
emit_3(r, RULE_MATCHTIME, subrule, cindex, tag);
}
static void spec_sub(Builder *b, int32_t argc, const Janet *argv) {
@@ -1327,14 +1181,6 @@ static void spec_sub(Builder *b, int32_t argc, const Janet *argv) {
emit_2(r, RULE_SUB, subrule1, subrule2);
}
static void spec_til(Builder *b, int32_t argc, const Janet *argv) {
peg_fixarity(b, argc, 2);
Reserve r = reserve(b, 3);
uint32_t subrule1 = peg_compile1(b, argv[0]);
uint32_t subrule2 = peg_compile1(b, argv[1]);
emit_2(r, RULE_TIL, subrule1, subrule2);
}
static void spec_split(Builder *b, int32_t argc, const Janet *argv) {
peg_fixarity(b, argc, 2);
Reserve r = reserve(b, 3);
@@ -1392,7 +1238,6 @@ static const SpecialPair peg_specials[] = {
{"<-", spec_capture},
{">", spec_look},
{"?", spec_opt},
{"??", spec_debug},
{"accumulate", spec_accumulate},
{"any", spec_any},
{"argument", spec_argument},
@@ -1403,11 +1248,9 @@ static const SpecialPair peg_specials[] = {
{"between", spec_between},
{"capture", spec_capture},
{"choice", spec_choice},
{"cms", spec_matchtime_splice},
{"cmt", spec_matchtime},
{"column", spec_column},
{"constant", spec_constant},
{"debug", spec_debug},
{"drop", spec_drop},
{"error", spec_error},
{"group", spec_group},
@@ -1419,9 +1262,7 @@ static const SpecialPair peg_specials[] = {
{"line", spec_line},
{"look", spec_look},
{"not", spec_not},
{"nth", spec_nth},
{"number", spec_capture_number},
{"only-tags", spec_only_tags},
{"opt", spec_opt},
{"position", spec_position},
{"quote", spec_capture},
@@ -1434,7 +1275,6 @@ static const SpecialPair peg_specials[] = {
{"split", spec_split},
{"sub", spec_sub},
{"thru", spec_thru},
{"til", spec_til},
{"to", spec_to},
{"uint", spec_uint_le},
{"uint-be", spec_uint_be},
@@ -1528,11 +1368,6 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
emit_bytes(b, RULE_LITERAL, len, str);
break;
}
case JANET_BUFFER: {
const JanetBuffer *buf = janet_unwrap_buffer(peg);
emit_bytes(b, RULE_LITERAL, buf->count, buf->data);
break;
}
case JANET_TABLE: {
/* Build grammar table */
JanetTable *new_grammar = janet_table_clone(janet_unwrap_table(peg));
@@ -1661,7 +1496,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
/* After here, no panics except for the bad: label. */
/* Keep track at each index if an instruction was
* referenced (0x01) or is in a main bytecode position
* reference (0x01) or is in a main bytecode position
* (0x02). This lets us do a linear scan and not
* need to a depth first traversal. It is stricter
* than a dfs by not allowing certain kinds of unused
@@ -1684,10 +1519,6 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
case RULE_LITERAL:
i += 2 + ((rule[1] + 3) >> 2);
break;
case RULE_DEBUG:
/* [0 words] */
i += 1;
break;
case RULE_NCHAR:
case RULE_NOTNCHAR:
case RULE_RANGE:
@@ -1771,7 +1602,6 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
break;
case RULE_REPLACE:
case RULE_MATCHTIME:
case RULE_MATCHSPLICE:
/* [rule, constant, tag] */
if (rule[1] >= blen) goto bad;
if (rule[2] >= clen) goto bad;
@@ -1779,7 +1609,6 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
i += 4;
break;
case RULE_SUB:
case RULE_TIL:
case RULE_SPLIT:
/* [rule, rule] */
if (rule[1] >= blen) goto bad;
@@ -1790,7 +1619,6 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
break;
case RULE_ERROR:
case RULE_DROP:
case RULE_ONLY_TAGS:
case RULE_NOT:
case RULE_TO:
case RULE_THRU:
@@ -1800,16 +1628,10 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
i += 2;
break;
case RULE_READINT:
/* [ width | (endianness << 5) | (signedness << 6), tag ] */
/* [ width | (endianess << 5) | (signedness << 6), tag ] */
if (rule[1] > JANET_MAX_READINT_WIDTH) goto bad;
i += 3;
break;
case RULE_NTH:
/* [nth, rule, tag] */
if (rule[2] >= blen) goto bad;
op_flags[rule[2]] |= 0x01;
i += 4;
break;
default:
goto bad;
}
@@ -1903,8 +1725,8 @@ static JanetPeg *compile_peg(Janet x) {
JANET_CORE_FN(cfun_peg_compile,
"(peg/compile peg)",
"Compiles a peg source data structure into a <core/peg>. This will speed up matching "
"if the same peg will be used multiple times. `(dyn :peg-grammar)` replaces "
"`default-peg-grammar` for the grammar of the peg.") {
"if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to suppliment "
"the grammar of the peg for otherwise undefined peg keywords.") {
janet_fixarity(argc, 1);
JanetPeg *peg = compile_peg(argv[0]);
return janet_wrap_abstract(peg);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -72,7 +72,7 @@ static int count_dig10(int32_t x) {
}
}
static int32_t integer_to_string_b(JanetBuffer *buffer, int32_t x) {
static void integer_to_string_b(JanetBuffer *buffer, int32_t x) {
janet_buffer_extra(buffer, BUFSIZE);
uint8_t *buf = buffer->data + buffer->count;
int32_t neg = 0;
@@ -80,7 +80,7 @@ static int32_t integer_to_string_b(JanetBuffer *buffer, int32_t x) {
if (x == 0) {
buf[0] = '0';
buffer->count++;
return 1;
return;
}
if (x > 0) {
x = -x;
@@ -96,7 +96,6 @@ static int32_t integer_to_string_b(JanetBuffer *buffer, int32_t x) {
x /= 10;
}
buffer->count += len + neg;
return len + neg;
}
#define HEX(i) (((uint8_t *) janet_base64)[(i)])
@@ -135,55 +134,43 @@ static void string_description_b(JanetBuffer *buffer, const char *title, void *p
#undef POINTSIZE
}
static int janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
janet_buffer_push_u8(buffer, '"');
int align = 1;
for (int32_t i = 0; i < len; ++i) {
uint8_t c = str[i];
switch (c) {
case '"':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\"", 2);
align += 2;
break;
case '\n':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\n", 2);
align += 2;
break;
case '\r':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\r", 2);
align += 2;
break;
case '\0':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\0", 2);
align += 2;
break;
case '\f':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\f", 2);
align += 2;
break;
case '\v':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\v", 2);
align += 2;
break;
case '\a':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\a", 2);
align += 2;
break;
case '\b':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\b", 2);
align += 2;
break;
case 27:
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\e", 2);
align += 2;
break;
case '\\':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\\", 2);
align += 2;
break;
case '\t':
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\t", 2);
align += 2;
break;
default:
if (c < 32 || c > 126) {
@@ -193,16 +180,13 @@ static int janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int
buf[2] = janet_base64[(c >> 4) & 0xF];
buf[3] = janet_base64[c & 0xF];
janet_buffer_push_bytes(buffer, buf, 4);
align += 4;
} else {
janet_buffer_push_u8(buffer, c);
align++;
}
break;
}
}
janet_buffer_push_u8(buffer, '"');
return align + 1;
}
static void janet_escape_string_b(JanetBuffer *buffer, const uint8_t *str) {
@@ -374,7 +358,7 @@ const uint8_t *janet_to_string(Janet x) {
struct pretty {
JanetBuffer *buffer;
int depth;
int align;
int indent;
int flags;
int32_t bufstartlen;
int32_t *keysort_buffer;
@@ -395,10 +379,8 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
break;
case JANET_NUMBER:
janet_buffer_ensure(S->buffer, S->buffer->count + BUFSIZE, 2);
double num = janet_unwrap_number(x);
if (isnan(num)) return 1;
if (isinf(num)) return 1;
janet_buffer_dtostr(S->buffer, num);
int count = snprintf((char *) S->buffer->data + S->buffer->count, BUFSIZE, "%.17g", janet_unwrap_number(x));
S->buffer->count += count;
break;
case JANET_SYMBOL:
case JANET_KEYWORD:
@@ -466,15 +448,14 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
return 0;
}
static void print_newline(struct pretty *S, int align) {
static void print_newline(struct pretty *S, int just_a_space) {
int i;
S->align = align;
if (S->flags & JANET_PRETTY_ONELINE) {
if (just_a_space || (S->flags & JANET_PRETTY_ONELINE)) {
janet_buffer_push_u8(S->buffer, ' ');
return;
}
janet_buffer_push_u8(S->buffer, '\n');
for (i = 0; i < S->align; i++) {
for (i = 0; i < S->indent; i++) {
janet_buffer_push_u8(S->buffer, ' ');
}
}
@@ -501,12 +482,13 @@ static const char *janet_pretty_colors[] = {
"\x1B[36m"
};
#define JANET_PRETTY_DICT_ONELINE 4
#define JANET_PRETTY_IND_ONELINE 10
#define JANET_PRETTY_DICT_LIMIT 30
#define JANET_PRETTY_DICT_KEYSORT_LIMIT 2000
#define JANET_PRETTY_ARRAY_LIMIT 160
/* Helper for pretty printing */
static void janet_pretty_one(struct pretty *S, Janet x) {
static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
/* Add to seen */
switch (janet_type(x)) {
case JANET_NIL:
@@ -521,7 +503,7 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
janet_buffer_push_cstring(S->buffer, janet_cycle_color);
}
janet_buffer_push_cstring(S->buffer, "<cycle ");
S->align += 8 + integer_to_string_b(S->buffer, janet_unwrap_integer(seenid));
integer_to_string_b(S->buffer, janet_unwrap_integer(seenid));
janet_buffer_push_u8(S->buffer, '>');
if (S->flags & JANET_PRETTY_COLOR) {
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
@@ -543,11 +525,9 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
if (janet_checktype(x, JANET_BUFFER) && janet_unwrap_buffer(x) == S->buffer) {
janet_buffer_ensure(S->buffer, S->buffer->count + S->bufstartlen * 4 + 3, 1);
janet_buffer_push_u8(S->buffer, '@');
S->align += 1 + janet_escape_string_impl(S->buffer, S->buffer->data, S->bufstartlen);
janet_escape_string_impl(S->buffer, S->buffer->data, S->bufstartlen);
} else {
S->align -= S->buffer->count;
janet_description_b(S->buffer, x);
S->align += S->buffer->count;
}
if (color && (S->flags & JANET_PRETTY_COLOR)) {
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
@@ -564,34 +544,35 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
const char *startstr = isarray ? "@[" : hasbrackets ? "[" : "(";
const char endchar = isarray ? ']' : hasbrackets ? ']' : ')';
janet_buffer_push_cstring(S->buffer, startstr);
const int align = S->align += strlen(startstr);
S->depth--;
S->indent += 2;
if (S->depth == 0) {
janet_buffer_push_cstring(S->buffer, "...");
S->align += 3;
} else {
if (!isarray && !(S->flags & JANET_PRETTY_ONELINE) && len >= JANET_PRETTY_IND_ONELINE)
janet_buffer_push_u8(S->buffer, ' ');
if (is_dict_value && len >= JANET_PRETTY_IND_ONELINE) print_newline(S, 0);
if (len > JANET_PRETTY_ARRAY_LIMIT && !(S->flags & JANET_PRETTY_NOTRUNC)) {
for (i = 0; i < 3; i++) {
if (i) print_newline(S, align);
janet_pretty_one(S, arr[i]);
if (i) print_newline(S, 0);
janet_pretty_one(S, arr[i], 0);
}
print_newline(S, align);
print_newline(S, 0);
janet_buffer_push_cstring(S->buffer, "...");
S->align += 3;
for (i = len - 3; i < len; i++) {
print_newline(S, align);
janet_pretty_one(S, arr[i]);
for (i = 0; i < 3; i++) {
print_newline(S, 0);
janet_pretty_one(S, arr[len - 3 + i], 0);
}
} else {
for (i = 0; i < len; i++) {
if (i) print_newline(S, align);
janet_pretty_one(S, arr[i]);
if (i) print_newline(S, len < JANET_PRETTY_IND_ONELINE);
janet_pretty_one(S, arr[i], 0);
}
}
}
S->indent -= 2;
S->depth++;
janet_buffer_push_u8(S->buffer, endchar);
S->align++;
break;
}
case JANET_STRUCT:
@@ -602,7 +583,6 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
if (istable) {
JanetTable *t = janet_unwrap_table(x);
JanetTable *proto = t->proto;
S->align++;
janet_buffer_push_cstring(S->buffer, "@");
if (NULL != proto) {
Janet name = janet_table_get(proto, janet_ckeywordv("_name"));
@@ -613,7 +593,6 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
janet_buffer_push_cstring(S->buffer, janet_class_color);
}
janet_buffer_push_bytes(S->buffer, n, len);
S->align += len;
if (S->flags & JANET_PRETTY_COLOR) {
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
}
@@ -631,99 +610,73 @@ static void janet_pretty_one(struct pretty *S, Janet x) {
janet_buffer_push_cstring(S->buffer, janet_class_color);
}
janet_buffer_push_bytes(S->buffer, n, len);
S->align += len;
if (S->flags & JANET_PRETTY_COLOR) {
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
}
}
}
}
janet_buffer_push_u8(S->buffer, '{');
const int align = ++S->align;
janet_buffer_push_cstring(S->buffer, "{");
S->depth--;
S->indent += 2;
if (S->depth == 0) {
janet_buffer_push_cstring(S->buffer, "...");
S->align += 3;
} else {
int32_t len = 0, cap = 0;
int32_t i = 0, len = 0, cap = 0;
const JanetKV *kvs = NULL;
janet_dictionary_view(x, &kvs, &len, &cap);
if (!istable && !(S->flags & JANET_PRETTY_ONELINE) && len >= JANET_PRETTY_DICT_ONELINE)
janet_buffer_push_u8(S->buffer, ' ');
if (is_dict_value && len >= JANET_PRETTY_DICT_ONELINE) print_newline(S, 0);
int32_t ks_start = S->keysort_start;
/* Ensure buffer is large enough to sort keys. */
int truncated = 0;
/* Shortcut for huge dictionaries, don't bother sorting keys */
if (len > JANET_PRETTY_DICT_KEYSORT_LIMIT) {
if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
len = JANET_PRETTY_DICT_LIMIT;
truncated = 1;
}
int32_t j = 0;
for (int32_t i = 0; i < len; i++) {
while (janet_checktype(kvs[j].key, JANET_NIL)) j++;
if (i) print_newline(S, align);
janet_pretty_one(S, kvs[j].key);
janet_buffer_push_u8(S->buffer, ' ');
S->align++;
janet_pretty_one(S, kvs[j].value);
j++;
}
if (truncated) {
print_newline(S, align);
janet_buffer_push_cstring(S->buffer, "...");
S->align += 3;
}
} else {
/* Sorted keys dictionaries */
/* Ensure buffer is large enough to sort keys. */
int64_t mincap = (int64_t) len + (int64_t) ks_start;
if (mincap > INT32_MAX) {
truncated = 1;
len = 0;
mincap = ks_start;
}
if (S->keysort_capacity < mincap) {
if (mincap >= INT32_MAX / 2) {
S->keysort_capacity = INT32_MAX;
} else {
S->keysort_capacity = (int32_t)(mincap * 2);
}
S->keysort_buffer = janet_srealloc(S->keysort_buffer, sizeof(int32_t) * S->keysort_capacity);
if (NULL == S->keysort_buffer) {
JANET_OUT_OF_MEMORY;
}
}
janet_sorted_keys(kvs, cap, S->keysort_buffer == NULL ? NULL : S->keysort_buffer + ks_start);
S->keysort_start += len;
if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
len = JANET_PRETTY_DICT_LIMIT;
truncated = 1;
}
for (int32_t i = 0; i < len; i++) {
if (i) print_newline(S, align);
int32_t j = S->keysort_buffer[i + ks_start];
janet_pretty_one(S, kvs[j].key);
janet_buffer_push_u8(S->buffer, ' ');
S->align++;
janet_pretty_one(S, kvs[j].value);
}
if (truncated) {
print_newline(S, align);
janet_buffer_push_cstring(S->buffer, "...");
S->align += 3;
}
int64_t mincap = (int64_t) len + (int64_t) ks_start;
if (mincap > INT32_MAX) {
truncated = 1;
len = 0;
mincap = ks_start;
}
if (S->keysort_capacity < mincap) {
if (mincap >= INT32_MAX / 2) {
S->keysort_capacity = INT32_MAX;
} else {
S->keysort_capacity = (int32_t)(mincap * 2);
}
S->keysort_buffer = janet_srealloc(S->keysort_buffer, sizeof(int32_t) * S->keysort_capacity);
if (NULL == S->keysort_buffer) {
JANET_OUT_OF_MEMORY;
}
}
janet_sorted_keys(kvs, cap, S->keysort_buffer == NULL ? NULL : S->keysort_buffer + ks_start);
S->keysort_start += len;
if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
len = JANET_PRETTY_DICT_LIMIT;
truncated = 1;
}
for (i = 0; i < len; i++) {
if (i) print_newline(S, len < JANET_PRETTY_DICT_ONELINE);
int32_t j = S->keysort_buffer[i + ks_start];
janet_pretty_one(S, kvs[j].key, 0);
janet_buffer_push_u8(S->buffer, ' ');
janet_pretty_one(S, kvs[j].value, 1);
}
if (truncated) {
print_newline(S, 0);
janet_buffer_push_cstring(S->buffer, "...");
}
S->keysort_start = ks_start;
}
S->indent -= 2;
S->depth++;
janet_buffer_push_u8(S->buffer, '}');
S->align++;
break;
}
}
@@ -739,14 +692,14 @@ static JanetBuffer *janet_pretty_(JanetBuffer *buffer, int depth, int flags, Jan
}
S.buffer = buffer;
S.depth = depth;
S.align = 0;
S.indent = 0;
S.flags = flags;
S.bufstartlen = startlen;
S.keysort_capacity = 0;
S.keysort_buffer = NULL;
S.keysort_start = 0;
janet_table_init(&S.seen, 10);
janet_pretty_one(&S, x);
janet_pretty_one(&S, x, 0);
janet_table_deinit(&S.seen);
return S.buffer;
}
@@ -764,7 +717,7 @@ static JanetBuffer *janet_jdn_(JanetBuffer *buffer, int depth, Janet x, int32_t
}
S.buffer = buffer;
S.depth = depth;
S.align = 0;
S.indent = 0;
S.flags = 0;
S.bufstartlen = startlen;
S.keysort_capacity = 0;
@@ -877,7 +830,7 @@ static const char *scanformat(
if (loc != NULL && *loc != '\0') {
const char *mapping = get_fmt_mapping(*p2++);
size_t len = strlen(mapping);
memcpy(form, mapping, len);
strcpy(form, mapping);
form += len;
} else {
*(form++) = *(p2++);
@@ -942,7 +895,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
case 's':
case 'S': {
const char *str = va_arg(args, const char *);
int32_t len = (c[-1] == 's')
int32_t len = c[-1] == 's'
? (int32_t) strlen(str)
: janet_string_length((JanetString) str);
if (form[2] == '\0')
@@ -1068,20 +1021,10 @@ void janet_buffer_format(
char form[MAX_FORMAT], item[MAX_ITEM];
char width[3], precision[3];
int nb = 0; /* number of bytes in added item */
#ifdef JANET_PLAN9
if (*strfrmt == 'r') {
rerrstr(item, MAX_ITEM);
nb = strlen(item);
} else
#endif
if (++arg >= argc)
janet_panic("not enough values for format");
if (++arg >= argc)
janet_panic("not enough values for format");
strfrmt = scanformat(strfrmt, form, width, precision);
switch (*strfrmt++) {
#ifdef JANET_PLAN9
case 'r':
break;
#endif
case 'c': {
nb = snprintf(item, MAX_ITEM, form, (int)
janet_getinteger(argv, arg));
@@ -1115,11 +1058,19 @@ void janet_buffer_format(
break;
}
case 's': {
const char *s = janet_getcbytes(argv, arg);
JanetByteView bytes = janet_getbytes(argv, arg);
const uint8_t *s = bytes.bytes;
int32_t l = bytes.len;
if (form[2] == '\0')
janet_buffer_push_cstring(b, s);
janet_buffer_push_bytes(b, s, l);
else {
nb = snprintf(item, MAX_ITEM, form, s);
if (l != (int32_t) strlen((const char *) s))
janet_panic("string contains zeros");
if (!strchr(form, '.') && l >= 100) {
janet_panic("no precision and string is too long to be formatted");
} else {
nb = snprintf(item, MAX_ITEM, form, s);
}
}
break;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -26,10 +26,9 @@
#include "state.h"
#endif
/* Run a string of code. The return value is a set of error flags, JANET_DO_ERROR_RUNTIME, JANET_DO_ERROR_COMPILE, and JANET_DOR_ERROR_PARSE if
* any errors were encountered in those phases. More information is printed to stderr. */
/* Run a string */
int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out) {
JanetParser *parser;
JanetParser parser;
int errflags = 0, done = 0;
int32_t index = 0;
Janet ret = janet_wrap_nil();
@@ -38,16 +37,14 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
if (where) janet_gcroot(janet_wrap_string(where));
if (NULL == sourcePath) sourcePath = "<unknown>";
parser = janet_abstract(&janet_parser_type, sizeof(JanetParser));
janet_parser_init(parser);
janet_gcroot(janet_wrap_abstract(parser));
janet_parser_init(&parser);
/* While we haven't seen an error */
while (!done) {
/* Evaluate parsed values */
while (janet_parser_has_more(parser)) {
Janet form = janet_parser_produce(parser);
while (janet_parser_has_more(&parser)) {
Janet form = janet_parser_produce(&parser);
JanetCompileResult cres = janet_compile(form, env, where);
if (cres.status == JANET_COMPILE_OK) {
JanetFunction *f = janet_thunk(cres.funcdef);
@@ -56,30 +53,27 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
janet_stacktrace_ext(fiber, ret, "");
errflags |= JANET_DO_ERROR_RUNTIME;
errflags |= 0x01;
done = 1;
}
} else {
int32_t line = (int32_t) parser->line;
int32_t col = (int32_t) parser->column;
ret = janet_wrap_string(cres.error);
int32_t line = (int32_t) parser.line;
int32_t col = (int32_t) parser.column;
if ((cres.error_mapping.line > 0) &&
(cres.error_mapping.column > 0)) {
line = cres.error_mapping.line;
col = cres.error_mapping.column;
}
JanetString ctx = janet_formatc("%s:%d:%d: compile error",
sourcePath, line, col);
JanetString errstr = janet_formatc("%s: %s",
(const char *)ctx,
(const char *)cres.error);
ret = janet_wrap_string(errstr);
if (cres.macrofiber) {
janet_eprintf("%s", (const char *)ctx);
janet_eprintf("%s:%d:%d: compile error", sourcePath,
line, col);
janet_stacktrace_ext(cres.macrofiber, ret, "");
} else {
janet_eprintf("%s\n", (const char *)errstr);
janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
line, col, (const char *)cres.error);
}
errflags |= JANET_DO_ERROR_COMPILE;
errflags |= 0x02;
done = 1;
}
}
@@ -87,28 +81,26 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
if (done) break;
/* Dispatch based on parse state */
switch (janet_parser_status(parser)) {
switch (janet_parser_status(&parser)) {
case JANET_PARSE_DEAD:
done = 1;
break;
case JANET_PARSE_ERROR: {
errflags |= JANET_DO_ERROR_PARSE;
int32_t line = (int32_t) parser->line;
int32_t col = (int32_t) parser->column;
JanetString errstr = janet_formatc("%s:%d:%d: parse error: %s",
sourcePath, line, col,
janet_parser_error(parser));
ret = janet_wrap_string(errstr);
janet_eprintf("%s\n", (const char *)errstr);
const char *e = janet_parser_error(&parser);
errflags |= 0x04;
ret = janet_cstringv(e);
int32_t line = (int32_t) parser.line;
int32_t col = (int32_t) parser.column;
janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e);
done = 1;
break;
}
case JANET_PARSE_ROOT:
case JANET_PARSE_PENDING:
if (index >= len) {
janet_parser_eof(parser);
janet_parser_eof(&parser);
} else {
janet_parser_consume(parser, bytes[index++]);
janet_parser_consume(&parser, bytes[index++]);
}
break;
}
@@ -116,7 +108,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
}
/* Clean up and return errors */
janet_gcunroot(janet_wrap_abstract(parser));
janet_parser_deinit(&parser);
if (where) janet_gcunroot(janet_wrap_string(where));
#ifdef JANET_EV
/* Enter the event loop if we are not already in it */
@@ -127,8 +119,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
janet_loop();
if (fiber) {
janet_gcunroot(janet_wrap_fiber(fiber));
if (!errflags)
ret = fiber->last_value;
ret = fiber->last_value;
}
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -307,14 +307,14 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv)
/* Add attributes to a global def or var table */
static JanetTable *handleattr(JanetCompiler *c, const char *kind, int32_t argn, const Janet *argv) {
int32_t i;
if (argn < 2) {
janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind));
return NULL;
}
JanetTable *tab = janet_table(2);
const char *binding_name = janet_type(argv[0]) == JANET_SYMBOL
? ((const char *)janet_unwrap_symbol(argv[0]))
: "<multiple bindings>";
if (argn < 2) {
janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind));
return NULL;
}
for (i = 1; i < argn - 1; i++) {
Janet attr = argv[i];
switch (janet_type(attr)) {
@@ -362,23 +362,6 @@ SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopt
janet_indexed_view(lhs, &view_lhs.items, &view_lhs.len);
janet_indexed_view(rhs, &view_rhs.items, &view_rhs.len);
int found_amp = 0;
int found_splice = 0;
/* Check for (def [x y z] [(splice [1 2 3]) 4 5 6]), bail out of optimization */
for (int32_t i = 0; i < view_rhs.len; i++) {
if (!janet_checktype(view_rhs.items[i], JANET_TUPLE)) {
continue;
}
JanetTuple tup = janet_unwrap_tuple(view_rhs.items[i]);
if (janet_tuple_length(tup) == 0) {
continue;
}
if (janet_symeq(tup[0], "splice")) {
found_splice = 1;
/* Good error will be generated later. */
break;
}
}
/* Check for (def [x & more] [1 2 3]), bail out of optimization */
for (int32_t i = 0; i < view_lhs.len; i++) {
if (janet_symeq(view_lhs.items[i], "&")) {
found_amp = 1;
@@ -386,7 +369,7 @@ SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopt
break;
}
}
if (!found_amp && !found_splice) {
if (!found_amp) {
for (int32_t i = 0; i < view_lhs.len; i++) {
Janet sub_rhs = view_rhs.len <= i ? janet_wrap_nil() : view_rhs.items[i];
into = dohead_destructure(c, into, subopts, view_lhs.items[i], sub_rhs);
@@ -404,7 +387,7 @@ SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopt
}
/* Def or var a symbol in a local scope */
static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, JanetSlot ret, int no_unused) {
static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, JanetSlot ret) {
int isUnnamedRegister = !(ret.flags & JANET_SLOT_NAMED) &&
ret.index > 0 &&
ret.envindex >= 0;
@@ -425,11 +408,7 @@ static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, Janet
ret = localslot;
}
ret.flags |= flags;
if ((c->scope->flags & JANET_SCOPE_TOP) || no_unused) {
janetc_nameslot_no_unused(c, head, ret);
} else {
janetc_nameslot(c, head, ret);
}
janetc_nameslot(c, head, ret);
return !isUnnamedRegister;
}
@@ -443,7 +422,8 @@ static int varleaf(
JanetSlot refslot;
JanetTable *entry = janet_table_clone(reftab);
int is_redef = janet_truthy(janet_table_get_keyword(c->env, "redef"));
Janet redef_kw = janet_ckeywordv("redef");
int is_redef = janet_truthy(janet_table_get(c->env, redef_kw));
JanetArray *ref;
JanetBinding old_binding;
@@ -463,17 +443,7 @@ static int varleaf(
janetc_emit_ssu(c, JOP_PUT_INDEX, refslot, s, 0, 0);
return 1;
} else {
int no_unused = reftab && reftab->count && janet_truthy(janet_table_get_keyword(reftab, "unused"));
return namelocal(c, sym, JANET_SLOT_MUTABLE, s, no_unused);
}
}
static void check_metadata_lint(JanetCompiler *c, JanetTable *attr_table) {
if (!(c->scope->flags & JANET_SCOPE_TOP) && attr_table && attr_table->count) {
/* A macro is a normal lint, other metadata is a strict lint */
if (janet_truthy(janet_table_get_keyword(attr_table, "macro"))) {
janetc_lintf(c, JANET_C_LINT_NORMAL, "macro tag is ignored in inner scopes");
}
return namelocal(c, sym, JANET_SLOT_MUTABLE, s);
}
}
@@ -483,7 +453,6 @@ static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) {
if (c->result.status == JANET_COMPILE_ERROR) {
return janetc_cslot(janet_wrap_nil());
}
check_metadata_lint(c, attr_table);
SlotHeadPair *into = NULL;
into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]);
if (c->result.status == JANET_COMPILE_ERROR) {
@@ -510,8 +479,9 @@ static int defleaf(
janet_table_put(entry, janet_ckeywordv("source-map"),
janet_wrap_tuple(janetc_make_sourcemap(c)));
int is_redef = janet_truthy(janet_table_get_keyword(c->env, "redef"));
if (is_redef) janet_table_put(entry, janet_ckeywordv("redef"), janet_wrap_true());
Janet redef_kw = janet_ckeywordv("redef");
int is_redef = janet_truthy(janet_table_get(c->env, redef_kw));
if (is_redef) janet_table_put(entry, redef_kw, janet_wrap_true());
if (is_redef) {
JanetBinding binding = janet_resolve_ext(c->env, sym);
@@ -534,8 +504,7 @@ static int defleaf(
/* Add env entry to env */
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(entry));
}
int no_unused = tab && tab->count && janet_truthy(janet_table_get_keyword(tab, "unused"));
return namelocal(c, sym, 0, s, no_unused);
return namelocal(c, sym, 0, s);
}
static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
@@ -544,7 +513,6 @@ static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
if (c->result.status == JANET_COMPILE_ERROR) {
return janetc_cslot(janet_wrap_nil());
}
check_metadata_lint(c, attr_table);
opts.flags &= ~JANET_FOPTS_HINT;
SlotHeadPair *into = NULL;
into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]);
@@ -684,10 +652,8 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
/* Write jumps - only add jump lengths if jump actually emitted */
labeld = janet_v_count(c->buffer);
if (labeljr < labeld) {
c->buffer[labeljr] |= (labelr - labeljr) << 16;
if (!tail && labeljd < labeld) c->buffer[labeljd] |= (labeld - labeljd) << 8;
}
c->buffer[labeljr] |= (labelr - labeljr) << 16;
if (!tail) c->buffer[labeljd] |= (labeld - labeljd) << 8;
if (tail) target.flags |= JANET_SLOT_RETURNED;
return target;
@@ -909,7 +875,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
janetc_regalloc_freetemp(&c->scope->ra, tempself, JANETC_REGTEMP_0);
/* Compile function */
JanetFuncDef *def = janetc_pop_funcdef(c);
def->name = janet_cstring("while");
def->name = janet_cstring("_while");
janet_def_addflags(def);
int32_t defindex = janetc_addfuncdef(c, def);
/* And then load the closure and call it. */
@@ -959,7 +925,6 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
int structarg = 0;
int allow_extra = 0;
int selfref = 0;
int hasname = 0;
int seenamp = 0;
int seenopt = 0;
int namedargs = 0;
@@ -978,10 +943,6 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
head = argv[0];
if (janet_checktype(head, JANET_SYMBOL)) {
selfref = 1;
hasname = 1;
parami = 1;
} else if (janet_checktype(head, JANET_KEYWORD)) {
hasname = 1;
parami = 1;
}
if (parami >= argn || !janet_checktype(argv[parami], JANET_TUPLE)) {
@@ -1076,14 +1037,6 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
}
}
/* Compile named arguments */
if (namedargs) {
Janet param = janet_wrap_table(named_table);
destructure(c, param, named_slot, defleaf, NULL);
janetc_freeslot(c, named_slot);
janet_v_free(named_params);
}
/* Compile destructed params */
int32_t j = 0;
for (i = 0; i < paramcount; i++) {
@@ -1097,6 +1050,14 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
}
janet_v_free(destructed_params);
/* Compile named arguments */
if (namedargs) {
Janet param = janet_wrap_table(named_table);
destructure(c, param, named_slot, defleaf, NULL);
janetc_freeslot(c, named_slot);
janet_v_free(named_params);
}
max_arity = (vararg || allow_extra) ? INT32_MAX : arity;
if (!seenopt) min_arity = arity;
@@ -1118,7 +1079,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
JanetSlot slot = janetc_farslot(c);
slot.flags = JANET_SLOT_NAMED | JANET_FUNCTION;
janetc_emit_s(c, JOP_LOAD_SELF, slot, 1);
janetc_nameslot_no_unused(c, sym, slot);
janetc_nameslot(c, sym, slot);
}
}
@@ -1139,14 +1100,10 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
def->arity = arity;
def->min_arity = min_arity;
def->max_arity = max_arity;
if (named_table != NULL) {
def->named_args_count = named_table->count;
}
if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
if (namedargs) def->flags |= JANET_FUNCDEF_FLAG_NAMEDARGS;
if (hasname) def->name = janet_unwrap_symbol(head); /* Also correctly unwraps keyword */
if (selfref) def->name = janet_unwrap_symbol(head);
janet_def_addflags(def);
defindex = janetc_addfuncdef(c, def);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -58,7 +58,7 @@ void janet_vm_load(JanetVM *from) {
}
/* Trigger suspension of the Janet vm by trying to
* exit the interpreter loop when convenient. You can optionally
* exit the interpeter loop when convenient. You can optionally
* use NULL to interrupt the current VM when convenient */
void janet_interpreter_interrupt(JanetVM *vm) {
vm = vm ? vm : &janet_vm;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -23,16 +23,11 @@
#ifndef JANET_STATE_H_defined
#define JANET_STATE_H_defined
#ifndef JANET_AMALG
#include "features.h"
#include <janet.h>
#include <stdint.h>
#endif
#ifdef JANET_EV
#ifdef JANET_WINDOWS
#include <windows.h>
#else
#ifndef JANET_WINDOWS
#include <pthread.h>
#endif
#endif
@@ -58,22 +53,13 @@ typedef struct {
void *data;
} JanetQueue;
#ifdef JANET_EV
typedef struct {
JanetTimestamp when;
JanetFiber *fiber;
JanetFiber *curr_fiber;
uint32_t sched_id;
int is_error;
int has_worker;
#ifdef JANET_WINDOWS
HANDLE worker;
HANDLE worker_event;
#else
pthread_t worker;
#endif
} JanetTimeout;
#endif
/* Registry table for C functions - contains metadata that can
* be looked up by cfunction pointer. All strings here are pointing to
@@ -114,7 +100,6 @@ struct JanetVM {
* return point for panics. */
jmp_buf *signal_buf;
Janet *return_reg;
int coerce_error;
/* The global registry for c functions. Used to store meta-data
* along with otherwise bare c function pointers. */
@@ -164,11 +149,6 @@ struct JanetVM {
JanetTraversalNode *traversal_top;
JanetTraversalNode *traversal_base;
/* Thread safe strerror error buffer - for janet_strerror */
#ifndef JANET_WINDOWS
char strerror_buf[256];
#endif
/* Event loop and scheduler globals */
#ifdef JANET_EV
size_t tq_count;
@@ -182,8 +162,6 @@ struct JanetVM {
JanetTable signal_handlers;
#ifdef JANET_WINDOWS
void **iocp;
void *connect_ex; /* MSWsock extension if available */
int connect_ex_loaded;
#elif defined(JANET_EV_EPOLL)
pthread_attr_t new_thread_attr;
JanetHandle selfpipe[2];

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -71,10 +71,10 @@ int janet_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
int janet_string_equalconst(const uint8_t *lhs, const uint8_t *rhs, int32_t rlen, int32_t rhash) {
int32_t lhash = janet_string_hash(lhs);
int32_t llen = janet_string_length(lhs);
if (lhash != rhash || llen != rlen)
return 0;
if (lhs == rhs)
return 1;
if (lhash != rhash || llen != rlen)
return 0;
return !memcmp(lhs, rhs, rlen);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -34,9 +34,9 @@
* because E is a valid digit in bases 15 or greater. For bases greater than
* 10, the letters are used as digits. A through Z correspond to the digits 10
* through 35, and the lowercase letters have the same values. The radix number
* is always in base 10. For example, a hexadecimal number could be written
* is always in base 10. For example, a hexidecimal number could be written
* '16rdeadbeef'. janet_scan_number also supports some c style syntax for
* hexadecimal literals. The previous number could also be written
* hexidecimal literals. The previous number could also be written
* '0xdeadbeef'.
*/
@@ -301,7 +301,6 @@ int janet_scan_number_base(
if (base == 0) {
base = 10;
}
int exp_base = base;
/* Skip leading zeros */
while (str < end && (*str == '0' || *str == '.')) {
@@ -323,12 +322,6 @@ int janet_scan_number_base(
} else if (*str == '&') {
foundexp = 1;
break;
} else if (base == 16 && (*str == 'P' || *str == 'p')) { /* IEEE hex float */
foundexp = 1;
exp_base = 10;
base = 2;
ex *= 4; /* We need to correct the current exponent after we change the base */
break;
} else if (base == 10 && (*str == 'E' || *str == 'e')) {
foundexp = 1;
break;
@@ -367,9 +360,9 @@ int janet_scan_number_base(
}
while (str < end) {
int digit = digit_lookup[*str & 0x7F];
if (*str > 127 || digit >= exp_base) goto error;
if (*str > 127 || digit >= base) goto error;
if (ee < (INT32_MAX / 40)) {
ee = exp_base * ee + digit;
ee = base * ee + digit;
}
str++;
seenadigit = 1;
@@ -496,53 +489,4 @@ int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out) {
return 0;
}
/* Similar to janet_scan_number but allows for
* more numeric types with a given suffix. */
int janet_scan_numeric(
const uint8_t *str,
int32_t len,
Janet *out) {
int result;
double num;
int64_t i64 = 0;
uint64_t u64 = 0;
if (len < 2 || str[len - 2] != ':') {
result = janet_scan_number_base(str, len, 0, &num);
*out = janet_wrap_number(num);
return result;
}
switch (str[len - 1]) {
default:
return 1;
case 'n':
result = janet_scan_number_base(str, len - 2, 0, &num);
*out = janet_wrap_number(num);
return result;
/* Condition is inverted janet_scan_int64 and janet_scan_uint64 */
case 's':
result = !janet_scan_int64(str, len - 2, &i64);
*out = janet_wrap_s64(i64);
return result;
case 'u':
result = !janet_scan_uint64(str, len - 2, &u64);
*out = janet_wrap_u64(u64);
return result;
}
}
#endif
void janet_buffer_dtostr(JanetBuffer *buffer, double x) {
#define BUFSIZE 32
janet_buffer_extra(buffer, BUFSIZE);
int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%.17g", x);
#undef BUFSIZE
/* fix locale issues with commas */
for (int i = 0; i < count; i++) {
char c = buffer->data[buffer->count + i];
if (c == ',') {
buffer->data[buffer->count + i] = '.';
}
}
buffer->count += count;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -294,16 +294,6 @@ JANET_CORE_FN(cfun_struct_to_table,
return janet_wrap_table(tab);
}
JANET_CORE_FN(cfun_struct_rawget,
"(struct/rawget st key)",
"Gets a value from a struct `st` without looking at the prototype struct. "
"If `st` does not contain the key directly, the function will return "
"nil without checking the prototype. Returns the value in the struct.") {
janet_fixarity(argc, 2);
JanetStruct st = janet_getstruct(argv, 0);
return janet_struct_rawget(st, argv[1]);
}
/* Load the struct module */
void janet_lib_struct(JanetTable *env) {
JanetRegExt struct_cfuns[] = {
@@ -311,7 +301,6 @@ void janet_lib_struct(JanetTable *env) {
JANET_CORE_REG("struct/getproto", cfun_struct_getproto),
JANET_CORE_REG("struct/proto-flatten", cfun_struct_flatten),
JANET_CORE_REG("struct/to-table", cfun_struct_to_table),
JANET_CORE_REG("struct/rawget", cfun_struct_rawget),
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, struct_cfuns);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -67,7 +67,7 @@ static JanetTable *janet_table_init_impl(JanetTable *table, int32_t capacity, in
return table;
}
/* Initialize a table (for use with scratch memory) */
/* Initialize a table (for use withs scratch memory) */
JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
return janet_table_init_impl(table, capacity, 1);
}
@@ -155,17 +155,6 @@ Janet janet_table_get(JanetTable *t, Janet key) {
return janet_wrap_nil();
}
/* Used internally for compiler stuff */
Janet janet_table_get_keyword(JanetTable *t, const char *keyword) {
int32_t keyword_len = (int32_t) strlen(keyword);
for (int i = JANET_MAX_PROTO_DEPTH; t && i; t = t->proto, --i) {
JanetKV *bucket = (JanetKV *) janet_dict_find_keyword(t->data, t->capacity, (const uint8_t *) keyword, keyword_len);
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
return bucket->value;
}
return janet_wrap_nil();
}
/* Get a value out of the table, and record which prototype it was from. */
Janet janet_table_get_ex(JanetTable *t, Janet key, JanetTable **which) {
for (int i = JANET_MAX_PROTO_DEPTH; t && i; t = t->proto, --i) {
@@ -330,6 +319,13 @@ JANET_CORE_FN(cfun_table_new,
int32_t cap = janet_getnat(argv, 0);
return janet_wrap_table(janet_table(cap));
}
/*
uint32_t flags = janet_getflags(argv, 1, "kv");
if (flags == 0) return janet_wrap_table(janet_table(cap));
if (flags == 1) return janet_wrap_table(janet_table_weakk(cap));
if (flags == 2) return janet_wrap_table(janet_table_weakv(cap));
return janet_wrap_table(janet_table_weakkv(cap));
*/
JANET_CORE_FN(cfun_table_weak,
"(table/weak capacity)",
@@ -383,14 +379,12 @@ JANET_CORE_FN(cfun_table_setproto,
}
JANET_CORE_FN(cfun_table_tostruct,
"(table/to-struct tab &opt proto)",
"Convert a table to a struct. Returns a new struct.") {
janet_arity(argc, 1, 2);
"(table/to-struct tab)",
"Convert a table to a struct. Returns a new struct. This function "
"does not take into account prototype tables.") {
janet_fixarity(argc, 1);
JanetTable *t = janet_gettable(argv, 0);
JanetStruct proto = janet_optstruct(argv, argc, 1, NULL);
JanetStruct st = janet_table_to_struct(t);
janet_struct_proto(st) = proto;
return janet_wrap_struct(st);
return janet_wrap_struct(janet_table_to_struct(t));
}
JANET_CORE_FN(cfun_table_rawget,

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -116,34 +116,6 @@ JANET_CORE_FN(cfun_tuple_setmap,
return argv[0];
}
JANET_CORE_FN(cfun_tuple_join,
"(tuple/join & parts)",
"Create a tuple by joining together other tuples and arrays.") {
janet_arity(argc, 0, -1);
int32_t total_len = 0;
for (int32_t i = 0; i < argc; i++) {
int32_t len = 0;
const Janet *vals = NULL;
if (!janet_indexed_view(argv[i], &vals, &len)) {
janet_panicf("expected indexed type for argument %d, got %v", i, argv[i]);
}
if (INT32_MAX - total_len < len) {
janet_panic("tuple too large");
}
total_len += len;
}
Janet *tup = janet_tuple_begin(total_len);
Janet *tup_cursor = tup;
for (int32_t i = 0; i < argc; i++) {
int32_t len = 0;
const Janet *vals = NULL;
janet_indexed_view(argv[i], &vals, &len);
memcpy(tup_cursor, vals, len * sizeof(Janet));
tup_cursor += len;
}
return janet_wrap_tuple(janet_tuple_end(tup));
}
/* Load the tuple module */
void janet_lib_tuple(JanetTable *env) {
JanetRegExt tuple_cfuns[] = {
@@ -152,7 +124,6 @@ void janet_lib_tuple(JanetTable *env) {
JANET_CORE_REG("tuple/type", cfun_tuple_type),
JANET_CORE_REG("tuple/sourcemap", cfun_tuple_sourcemap),
JANET_CORE_REG("tuple/setmap", cfun_tuple_setmap),
JANET_CORE_REG("tuple/join", cfun_tuple_join),
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, tuple_cfuns);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -79,7 +79,6 @@ const char *const janet_type_names[16] = {
"pointer"
};
/* Docstring for signal lists these */
const char *const janet_signal_names[14] = {
"ok",
"error",
@@ -97,7 +96,6 @@ const char *const janet_signal_names[14] = {
"await"
};
/* Docstring for fiber/status lists these */
const char *const janet_status_names[16] = {
"dead",
"error",
@@ -117,20 +115,14 @@ const char *const janet_status_names[16] = {
"alive"
};
uint32_t janet_hash_mix(uint32_t input, uint32_t more) {
uint32_t mix1 = (more + 0x9e3779b9 + (input << 6) + (input >> 2));
return input ^ (0x9e3779b9 + (mix1 << 6) + (mix1 >> 2));
}
#ifndef JANET_PRF
int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
if (NULL == str || len == 0) return 5381;
if (NULL == str) return 5381;
const uint8_t *end = str + len;
uint32_t hash = 5381;
while (str < end)
hash = (hash << 5) + hash + *str++;
hash = janet_hash_mix(hash, (uint32_t) len);
return (int32_t) hash;
}
@@ -246,6 +238,11 @@ int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
#endif
uint32_t janet_hash_mix(uint32_t input, uint32_t more) {
uint32_t mix1 = (more + 0x9e3779b9 + (input << 6) + (input >> 2));
return input ^ (0x9e3779b9 + (mix1 << 6) + (mix1 >> 2));
}
/* Computes hash of an array of values */
int32_t janet_array_calchash(const Janet *array, int32_t len) {
const Janet *end = array + len;
@@ -268,7 +265,7 @@ int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len) {
return (int32_t) hash;
}
/* Calculate next power of 2. May overflow. If n < 0,
/* Calculate next power of 2. May overflow. If n is 0,
* will return 0. */
int32_t janet_tablen(int32_t n) {
if (n < 0) return 0;
@@ -321,54 +318,6 @@ const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key) {
return first_bucket;
}
/* Helper to find a keyword, symbol, or string in a Janet struct or table without allocating
* memory or needing to find interned symbols */
const JanetKV *janet_dict_find_keyword(
const JanetKV *buckets, int32_t cap,
const uint8_t *cstr, int32_t cstr_len) {
int32_t hash = janet_string_calchash(cstr, cstr_len);
int32_t index = janet_maphash(cap, hash);
int32_t i;
const JanetKV *first_bucket = NULL;
/* Higher half */
for (i = index; i < cap; i++) {
const JanetKV *kv = buckets + i;
if (janet_checktype(kv->key, JANET_NIL)) {
if (janet_checktype(kv->value, JANET_NIL)) {
return kv;
} else if (NULL == first_bucket) {
first_bucket = kv;
}
} else if (janet_checktype(kv->key, JANET_KEYWORD)) {
/* Works for symbol and keyword, too */
JanetString str = janet_unwrap_string(kv->key);
int32_t len = janet_string_length(str);
if (hash == janet_string_hash(str) && len == cstr_len && !memcmp(str, cstr, len)) {
return buckets + i;
}
}
}
/* Lower half */
for (i = 0; i < index; i++) {
const JanetKV *kv = buckets + i;
if (janet_checktype(kv->key, JANET_NIL)) {
if (janet_checktype(kv->value, JANET_NIL)) {
return kv;
} else if (NULL == first_bucket) {
first_bucket = kv;
}
} else if (janet_checktype(kv->key, JANET_KEYWORD)) {
/* Works for symbol and keyword, too */
JanetString str = janet_unwrap_string(kv->key);
int32_t len = janet_string_length(str);
if (hash == janet_string_hash(str) && len == cstr_len && !memcmp(str, cstr, len)) {
return buckets + i;
}
}
}
return first_bucket;
}
/* Get a value from a janet struct or table. */
Janet janet_dictionary_get(const JanetKV *data, int32_t cap, Janet key) {
const JanetKV *kv = janet_dict_find(data, cap, key);
@@ -676,11 +625,8 @@ JanetBinding janet_binding_from_entry(Janet entry) {
return binding;
entry_table = janet_unwrap_table(entry);
Janet deprecate = janet_table_get_keyword(entry_table, "deprecated");
int macro = janet_truthy(janet_table_get_keyword(entry_table, "macro"));
Janet value = janet_table_get_keyword(entry_table, "value");
Janet ref = janet_table_get_keyword(entry_table, "ref");
/* deprecation check */
Janet deprecate = janet_table_get(entry_table, janet_ckeywordv("deprecated"));
if (janet_checktype(deprecate, JANET_KEYWORD)) {
JanetKeyword depkw = janet_unwrap_keyword(deprecate);
if (!janet_cstrcmp(depkw, "relaxed")) {
@@ -694,8 +640,11 @@ JanetBinding janet_binding_from_entry(Janet entry) {
binding.deprecation = JANET_BINDING_DEP_NORMAL;
}
int macro = janet_truthy(janet_table_get(entry_table, janet_ckeywordv("macro")));
Janet value = janet_table_get(entry_table, janet_ckeywordv("value"));
Janet ref = janet_table_get(entry_table, janet_ckeywordv("ref"));
int ref_is_valid = janet_checktype(ref, JANET_ARRAY);
int redef = ref_is_valid && janet_truthy(janet_table_get_keyword(entry_table, "redef"));
int redef = ref_is_valid && janet_truthy(janet_table_get(entry_table, janet_ckeywordv("redef")));
if (macro) {
binding.value = redef ? ref : value;
@@ -877,34 +826,16 @@ int janet_checkuint64(Janet x) {
return janet_checkuint64range(dval);
}
int janet_checkint16(Janet x) {
if (!janet_checktype(x, JANET_NUMBER))
return 0;
double dval = janet_unwrap_number(x);
return janet_checkint16range(dval);
}
int janet_checkuint16(Janet x) {
if (!janet_checktype(x, JANET_NUMBER))
return 0;
double dval = janet_unwrap_number(x);
return janet_checkuint16range(dval);
}
int janet_checksize(Janet x) {
if (!janet_checktype(x, JANET_NUMBER))
return 0;
double dval = janet_unwrap_number(x);
if (dval != (double)((size_t) dval)) return 0;
#ifdef JANET_PLAN9
return dval <= SIZE_MAX;
#else
if (SIZE_MAX > JANET_INTMAX_INT64) {
return dval <= JANET_INTMAX_INT64;
} else {
return dval <= SIZE_MAX;
}
#endif
}
JanetTable *janet_get_core_table(const char *name) {
@@ -983,24 +914,27 @@ int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
#include <mach/clock.h>
#include <mach/mach.h>
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
if (source == JANET_TIME_CPUTIME) {
clock_t tmp = clock();
spec->tv_sec = tmp / CLOCKS_PER_SEC;
spec->tv_nsec = ((tmp - (spec->tv_sec * CLOCKS_PER_SEC)) * 1000000000) / CLOCKS_PER_SEC;
} else {
if (source == JANET_TIME_REALTIME) {
clock_serv_t cclock;
mach_timespec_t mts;
clock_id_t cid = CALENDAR_CLOCK;
if (source == JANET_TIME_REALTIME) {
cid = CALENDAR_CLOCK;
} else if (source == JANET_TIME_MONOTONIC) {
cid = SYSTEM_CLOCK;
}
host_get_clock_service(mach_host_self(), cid, &cclock);
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
spec->tv_sec = mts.tv_sec;
spec->tv_nsec = mts.tv_nsec;
} else if (source == JANET_TIME_MONOTONIC) {
clock_serv_t cclock;
int nsecs;
mach_msg_type_number_t count;
host_get_clock_service(mach_host_self(), clock, &cclock);
clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count);
mach_port_deallocate(mach_task_self(), cclock);
clock_getres(CLOCK_MONOTONIC, spec);
}
if (source == JANET_TIME_CPUTIME) {
clock_t tmp = clock();
spec->tv_sec = tmp;
spec->tv_nsec = (tmp - spec->tv_sec) * 1.0e9;
}
return 0;
}
@@ -1019,20 +953,6 @@ int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
#endif
#endif
/* Better strerror (thread-safe if available) */
const char *janet_strerror(int e) {
#ifdef JANET_WINDOWS
/* Microsoft strerror seems sane here and is thread safe by default */
return strerror(e);
#elif defined(__GLIBC__)
/* See https://linux.die.net/man/3/strerror_r */
return strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf));
#else
strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf));
return janet_vm.strerror_buf;
#endif
}
/* Setting C99 standard makes this not available, but it should
* work/link properly if we detect a BSD */
#if defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -33,7 +33,6 @@
#include <errno.h>
#include <stddef.h>
#include <stdbool.h>
#include <math.h>
#ifdef JANET_EV
#ifndef JANET_WINDOWS
@@ -66,72 +65,40 @@
/* Utils */
uint32_t janet_hash_mix(uint32_t input, uint32_t more);
#define janet_maphash(cap, hash) ((uint32_t)(hash) & (cap - 1))
int janet_valid_utf8(const uint8_t *str, int32_t len);
int janet_is_symbol_char(uint8_t c);
extern const char janet_base64[65];
int32_t janet_array_calchash(const Janet *array, int32_t len);
int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len);
int32_t janet_string_calchash(const uint8_t *str, int32_t len);
int32_t janet_tablen(int32_t n);
void safe_memcpy(void *dest, const void *src, size_t len);
void janet_buffer_push_types(JanetBuffer *buffer, int types);
const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key);
void janet_memempty(JanetKV *mem, int32_t count);
void *janet_memalloc_empty(int32_t count);
JanetTable *janet_get_core_table(const char *name);
void janet_def_addflags(JanetFuncDef *def);
void janet_buffer_dtostr(JanetBuffer *buffer, double x);
const char *janet_strerror(int e);
const void *janet_strbinsearch(
const void *tab,
size_t tabcount,
size_t itemsize,
const uint8_t *key);
void janet_buffer_format(
JanetBuffer *b,
const char *strfrmt,
int32_t argstart,
int32_t argc,
Janet *argv);
Janet janet_next_impl(Janet ds, Janet key, int is_interpreter);
JanetBinding janet_binding_from_entry(Janet entry);
JanetByteView janet_text_substitution(
Janet *subst,
const uint8_t *bytes,
uint32_t len,
JanetArray *extra_args);
const JanetKV *janet_dict_find_keyword(
const JanetKV *buckets,
int32_t cap,
const uint8_t *cstr,
int32_t cstr_len);
Janet janet_table_get_keyword(JanetTable *t, const char *keyword);
/* Registry functions */
void janet_registry_put(
JanetCFunction key,
@@ -172,7 +139,7 @@ int janet_gettime(struct timespec *spec, enum JanetTimeSource source);
#define strdup(x) _strdup(x)
#endif
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libraries
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
* with native code. */
#if defined(JANET_NO_DYNAMIC_MODULES)
typedef int Clib;
@@ -197,26 +164,7 @@ typedef void *Clib;
#endif
char *get_processed_name(const char *name);
#ifdef JANET_PLAN9
#define RETRY_EINTR(RC, CALL) (RC) = CALL;
#else
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
#endif
#ifdef JANET_EV
#ifdef JANET_WINDOWS
#include <winsock2.h>
#include <windows.h>
#include <io.h>
typedef struct {
union {
OVERLAPPED overlapped;
WSAOVERLAPPED wsaoverlapped;
} as;
uint32_t bytes_transfered;
} JanetOverlapped;
#endif
#endif
/* Initialize builtin libraries */
void janet_lib_io(JanetTable *env);
@@ -239,6 +187,9 @@ void janet_lib_debug(JanetTable *env);
#ifdef JANET_PEG
void janet_lib_peg(JanetTable *env);
#endif
#ifdef JANET_TYPED_ARRAY
void janet_lib_typed_array(JanetTable *env);
#endif
#ifdef JANET_INT_TYPES
void janet_lib_inttypes(JanetTable *env);
#endif
@@ -249,11 +200,7 @@ extern const JanetAbstractType janet_address_type;
#ifdef JANET_EV
void janet_lib_ev(JanetTable *env);
void janet_ev_mark(void);
void janet_async_start_fiber(JanetFiber *fiber, JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state);
int janet_make_pipe(JanetHandle handles[2], int mode);
#ifdef JANET_FILEWATCH
void janet_lib_filewatch(JanetTable *env);
#endif
#endif
#ifdef JANET_FFI
void janet_lib_ffi(JanetTable *env);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -322,8 +322,7 @@ int32_t janet_hash(Janet x) {
break;
case JANET_TUPLE:
hash = janet_tuple_hash(janet_unwrap_tuple(x));
uint32_t inc = (janet_tuple_flag(janet_unwrap_tuple(x)) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : 0;
hash = (int32_t)((uint32_t)hash + inc); /* avoid overflow undefined behavior */
hash += (janet_tuple_flag(janet_unwrap_tuple(x)) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : 0;
break;
case JANET_STRUCT:
hash = janet_struct_hash(janet_unwrap_struct(x));
@@ -335,9 +334,10 @@ int32_t janet_hash(Janet x) {
} as;
as.d = janet_unwrap_number(x);
as.d += 0.0; /* normalize negative 0 */
as.u = murmur64(as.u);
uint32_t lo = (uint32_t)(as.u & 0xFFFFFFFF);
uint32_t hi = (uint32_t)(as.u >> 32);
hash = (int32_t)hi;
uint32_t hilo = (hi ^ lo) * 2654435769u;
hash = (int32_t)((hilo << 16) | (hilo >> 16));
break;
}
case JANET_ABSTRACT: {
@@ -495,7 +495,7 @@ Janet janet_in(Janet ds, Janet key) {
if (!(type->get)(janet_unwrap_abstract(ds), key, &value))
janet_panicf("key %v not found in %v ", key, ds);
} else {
janet_panicf("no getter for %v", ds);
janet_panicf("no getter for %v ", ds);
}
break;
}
@@ -622,7 +622,7 @@ Janet janet_getindex(Janet ds, int32_t index) {
if (!(type->get)(janet_unwrap_abstract(ds), janet_wrap_integer(index), &value))
value = janet_wrap_nil();
} else {
janet_panicf("no getter for %v", ds);
janet_panicf("no getter for %v ", ds);
}
break;
}
@@ -724,9 +724,6 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
JanetArray *array = janet_unwrap_array(ds);
if (index >= array->count) {
janet_array_ensure(array, index + 1, 2);
for (int32_t i = array->count; i < index + 1; i++) {
array->data[i] = janet_wrap_nil();
}
array->count = index + 1;
}
array->data[index] = value;
@@ -738,7 +735,6 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
janet_panicf("can only put integers in buffers, got %v", value);
if (index >= buffer->count) {
janet_buffer_ensure(buffer, index + 1, 2);
memset(buffer->data + buffer->count, 0, index + 1 - buffer->count);
buffer->count = index + 1;
}
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
@@ -771,11 +767,7 @@ void janet_put(Janet ds, Janet key, Janet value) {
JanetArray *array = janet_unwrap_array(ds);
int32_t index = getter_checkint(type, key, INT32_MAX - 1);
if (index >= array->count) {
janet_array_ensure(array, index + 1, 2);
for (int32_t i = array->count; i < index + 1; i++) {
array->data[i] = janet_wrap_nil();
}
array->count = index + 1;
janet_array_setcount(array, index + 1);
}
array->data[index] = value;
break;
@@ -786,9 +778,7 @@ void janet_put(Janet ds, Janet key, Janet value) {
if (!janet_checkint(value))
janet_panicf("can only put integers in buffers, got %v", value);
if (index >= buffer->count) {
janet_buffer_ensure(buffer, index + 1, 2);
memset(buffer->data + buffer->count, 0, index + 1 - buffer->count);
buffer->count = index + 1;
janet_buffer_setcount(buffer, index + 1);
}
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
break;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
@@ -115,7 +115,7 @@
#define vm_maybe_auto_suspend(COND)
#else
#define vm_maybe_auto_suspend(COND) do { \
if ((COND) && janet_atomic_load_relaxed(&janet_vm.auto_suspend)) { \
if ((COND) && janet_vm.auto_suspend) { \
fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \
vm_return(JANET_SIGNAL_INTERRUPT, janet_wrap_nil()); \
} \
@@ -129,9 +129,7 @@
if (!janet_checktype(op1, JANET_NUMBER)) {\
vm_commit();\
Janet _argv[2] = { op1, janet_wrap_number(CS) };\
Janet a = janet_mcall(#op, 2, _argv);\
stack = fiber->data + fiber->frame;\
stack[A] = a;\
stack[A] = janet_mcall(#op, 2, _argv);\
vm_checkgc_pcnext();\
} else {\
double x1 = janet_unwrap_number(op1);\
@@ -145,9 +143,7 @@
if (!janet_checktype(op1, JANET_NUMBER)) {\
vm_commit();\
Janet _argv[2] = { op1, janet_wrap_number(CS) };\
Janet a = janet_mcall(#op, 2, _argv);\
stack = fiber->data + fiber->frame;\
stack[A] = a;\
stack[A] = janet_mcall(#op, 2, _argv);\
vm_checkgc_pcnext();\
} else {\
double y1 = janet_unwrap_number(op1);\
@@ -170,9 +166,7 @@
vm_pcnext();\
} else {\
vm_commit();\
Janet a = janet_binop_call(#op, "r" #op, op1, op2);\
stack = fiber->data + fiber->frame;\
stack[A] = a;\
stack[A] = janet_binop_call(#op, "r" #op, op1, op2);\
vm_checkgc_pcnext();\
}\
}
@@ -192,9 +186,7 @@
vm_pcnext();\
} else {\
vm_commit();\
Janet a = janet_binop_call(#op, "r" #op, op1, op2);\
stack = fiber->data + fiber->frame;\
stack[A] = a;\
stack[A] = janet_binop_call(#op, "r" #op, op1, op2);\
vm_checkgc_pcnext();\
}\
}
@@ -211,9 +203,7 @@
vm_pcnext();\
} else {\
vm_commit();\
Janet a = janet_wrap_boolean(janet_compare(op1, op2) op 0);\
stack = fiber->data + fiber->frame;\
stack[A] = a;\
stack[A] = janet_wrap_boolean(janet_compare(op1, op2) op 0);\
vm_checkgc_pcnext();\
}\
}
@@ -227,9 +217,7 @@
vm_pcnext();\
} else {\
vm_commit();\
Janet a = janet_wrap_boolean(janet_compare(op1, janet_wrap_integer(CS)) op 0);\
stack = fiber->data + fiber->frame;\
stack[A] = a;\
stack[A] = janet_wrap_boolean(janet_compare(op1, janet_wrap_integer(CS)) op 0);\
vm_checkgc_pcnext();\
}\
}
@@ -330,7 +318,7 @@ static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lh
Janet lr = janet_method_lookup(rhs, rmethod);
Janet argv[2] = { rhs, lhs };
if (janet_checktype(lr, JANET_NIL)) {
janet_panicf("could not find method :%s for %v or :%s for %v",
janet_panicf("could not find method :%s for %v, or :%s for %v",
lmethod, lhs,
rmethod, rhs);
}
@@ -722,9 +710,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
vm_pcnext();
} else {
vm_commit();
Janet a = janet_binop_call("div", "rdiv", op1, op2);
stack = fiber->data + fiber->frame;
stack[A] = a;
stack[A] = janet_binop_call("div", "rdiv", op1, op2);
vm_checkgc_pcnext();
}
}
@@ -744,9 +730,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
vm_pcnext();
} else {
vm_commit();
Janet a = janet_binop_call("mod", "rmod", op1, op2);
stack = fiber->data + fiber->frame;
stack[A] = a;
stack[A] = janet_binop_call("mod", "rmod", op1, op2);
vm_checkgc_pcnext();
}
}
@@ -761,9 +745,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
vm_pcnext();
} else {
vm_commit();
Janet a = janet_binop_call("%", "r%", op1, op2);
stack = fiber->data + fiber->frame;
stack[A] = a;
stack[A] = janet_binop_call("%", "r%", op1, op2);
vm_checkgc_pcnext();
}
}
@@ -784,9 +766,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
vm_pcnext();
} else {
vm_commit();
Janet a = janet_unary_call("~", op);
stack = fiber->data + fiber->frame;
stack[A] = a;
stack[A] = janet_unary_call("~", op);
vm_checkgc_pcnext();
}
}
@@ -818,14 +798,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
vm_pcnext();
VM_OP(JOP_JUMP)
vm_maybe_auto_suspend(DS <= 0);
pc += DS;
vm_maybe_auto_suspend(DS <= 0);
vm_next();
VM_OP(JOP_JUMP_IF)
if (janet_truthy(stack[A])) {
vm_maybe_auto_suspend(ES <= 0);
pc += ES;
vm_maybe_auto_suspend(ES <= 0);
} else {
pc++;
}
@@ -835,15 +815,15 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
if (janet_truthy(stack[A])) {
pc++;
} else {
vm_maybe_auto_suspend(ES <= 0);
pc += ES;
vm_maybe_auto_suspend(ES <= 0);
}
vm_next();
VM_OP(JOP_JUMP_IF_NIL)
if (janet_checktype(stack[A], JANET_NIL)) {
vm_maybe_auto_suspend(ES <= 0);
pc += ES;
vm_maybe_auto_suspend(ES <= 0);
} else {
pc++;
}
@@ -853,8 +833,8 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
if (janet_checktype(stack[A], JANET_NIL)) {
pc++;
} else {
vm_maybe_auto_suspend(ES <= 0);
pc += ES;
vm_maybe_auto_suspend(ES <= 0);
}
vm_next();
@@ -892,11 +872,8 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
stack[A] = janet_wrap_boolean(!janet_checktype(stack[B], JANET_NUMBER) || (janet_unwrap_number(stack[B]) != (double) CS));
vm_pcnext();
VM_OP(JOP_COMPARE) {
Janet a = janet_wrap_integer(janet_compare(stack[B], stack[C]));
stack = fiber->data + fiber->frame;
stack[A] = a;
}
VM_OP(JOP_COMPARE)
stack[A] = janet_wrap_integer(janet_compare(stack[B], stack[C]));
vm_pcnext();
VM_OP(JOP_NEXT)
@@ -1127,11 +1104,11 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
}
fiber->child = child;
JanetSignal sig = janet_continue_no_check(child, stack[C], &retreg);
stack = fiber->data + fiber->frame;
if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
vm_return(sig, retreg);
}
fiber->child = NULL;
stack = fiber->data + fiber->frame;
stack[A] = retreg;
vm_checkgc_pcnext();
}
@@ -1180,7 +1157,6 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
vm_commit();
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
janet_put(stack[A], stack[B], stack[C]);
stack = fiber->data + fiber->frame;
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
vm_checkgc_pcnext();
@@ -1188,44 +1164,27 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
vm_commit();
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
janet_putindex(stack[A], C, stack[B]);
stack = fiber->data + fiber->frame;
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
vm_checkgc_pcnext();
VM_OP(JOP_IN)
vm_commit();
{
Janet a = janet_in(stack[B], stack[C]);
stack = fiber->data + fiber->frame;
stack[A] = a;
}
stack[A] = janet_in(stack[B], stack[C]);
vm_pcnext();
VM_OP(JOP_GET)
vm_commit();
{
Janet a = janet_get(stack[B], stack[C]);
stack = fiber->data + fiber->frame;
stack[A] = a;
}
stack[A] = janet_get(stack[B], stack[C]);
vm_pcnext();
VM_OP(JOP_GET_INDEX)
vm_commit();
{
Janet a = janet_getindex(stack[B], C);
stack = fiber->data + fiber->frame;
stack[A] = a;
}
stack[A] = janet_getindex(stack[B], C);
vm_pcnext();
VM_OP(JOP_LENGTH)
vm_commit();
{
Janet a = janet_lengthv(stack[E]);
stack = fiber->data + fiber->frame;
stack[A] = a;
}
stack[A] = janet_lengthv(stack[E]);
vm_pcnext();
VM_OP(JOP_MAKE_ARRAY) {
@@ -1309,7 +1268,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
/*
* Execute a single instruction in the fiber. Does this by inspecting
* the fiber, setting a breakpoint at the next instruction, executing, and
* resetting breakpoints to how they were prior. Yes, it's a bit hacky.
* reseting breakpoints to how they were prior. Yes, it's a bit hacky.
*/
JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out) {
/* No finished or currently alive fibers. */
@@ -1414,10 +1373,7 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
/* Run vm */
janet_vm.fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
int old_coerce_error = janet_vm.coerce_error;
janet_vm.coerce_error = 1;
JanetSignal signal = run_vm(janet_vm.fiber, janet_wrap_nil());
janet_vm.coerce_error = old_coerce_error;
/* Teardown */
janet_vm.stackn = oldn;
@@ -1428,15 +1384,6 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
}
if (signal != JANET_SIGNAL_OK) {
/* Should match logic in janet_signalv */
#ifdef JANET_EV
if (janet_vm.root_fiber != NULL && signal == JANET_SIGNAL_EVENT) {
janet_vm.root_fiber->sched_id++;
}
#endif
if (signal != JANET_SIGNAL_ERROR) {
*janet_vm.return_reg = janet_wrap_string(janet_formatc("%v coerced from %s to error", *janet_vm.return_reg, janet_signal_names[signal]));
}
janet_panicv(*janet_vm.return_reg);
}
@@ -1483,10 +1430,8 @@ void janet_try_init(JanetTryState *state) {
state->vm_fiber = janet_vm.fiber;
state->vm_jmp_buf = janet_vm.signal_buf;
state->vm_return_reg = janet_vm.return_reg;
state->coerce_error = janet_vm.coerce_error;
janet_vm.return_reg = &(state->payload);
janet_vm.signal_buf = &(state->buf);
janet_vm.coerce_error = 0;
}
void janet_restore(JanetTryState *state) {
@@ -1495,7 +1440,6 @@ void janet_restore(JanetTryState *state) {
janet_vm.fiber = state->vm_fiber;
janet_vm.signal_buf = state->vm_jmp_buf;
janet_vm.return_reg = state->vm_return_reg;
janet_vm.coerce_error = state->coerce_error;
}
static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *out) {
@@ -1559,15 +1503,6 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
}
}
/* If this is a nested continue (root_fiber already set), root the fiber
* so it survives GC. janet_collect only marks root_fiber, so without
* this a nested fiber (e.g., from janet_pcall in a C function) would be
* invisible to GC and could be collected while actively running. */
int fiber_rooted = (janet_vm.root_fiber != NULL);
if (fiber_rooted) {
janet_gcroot(janet_wrap_fiber(fiber));
}
/* Save global state */
JanetTryState tstate;
JanetSignal sig = janet_try(&tstate);
@@ -1583,9 +1518,6 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
if (janet_vm.root_fiber == fiber) janet_vm.root_fiber = NULL;
janet_fiber_set_status(fiber, sig);
janet_restore(&tstate);
if (fiber_rooted) {
janet_gcunroot(janet_wrap_fiber(fiber));
}
fiber->last_value = tstate.payload;
*out = tstate.payload;
@@ -1681,7 +1613,7 @@ int janet_init(void) {
janet_vm.registry_count = 0;
janet_vm.registry_dirty = 0;
/* Initialize abstract registry */
/* Intialize abstract registry */
janet_vm.abstract_registry = janet_table(0);
janet_gcroot(janet_wrap_table(janet_vm.abstract_registry));

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2026 Calvin Rose
* Copyright (c) 2023 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to

Some files were not shown because too many files have changed in this diff Show More