mirror of
https://github.com/janet-lang/janet
synced 2025-10-28 14:17:42 +00:00
Compare commits
1 Commits
v1.38.0
...
size_t_eve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
051a9793b9 |
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
image: openbsd/7.4
|
||||
image: openbsd/latest
|
||||
sources:
|
||||
- https://git.sr.ht/~bakpakin/janet
|
||||
packages:
|
||||
|
||||
38
.github/cosmo/build
vendored
38
.github/cosmo/build
vendored
@@ -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
21
.github/cosmo/setup
vendored
@@ -1,21 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
sudo apt update
|
||||
sudo apt-get install -y ca-certificates libssl-dev\
|
||||
qemu 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/3.3.3/cosmocc-3.3.3.zip
|
||||
mkdir -p cosmocc
|
||||
cd cosmocc
|
||||
unzip ../cosmocc-3.3.3.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"
|
||||
7
.github/workflows/codeql.yml
vendored
7
.github/workflows/codeql.yml
vendored
@@ -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 }}"
|
||||
|
||||
58
.github/workflows/release.yml
vendored
58
.github/workflows/release.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, macos-13 ]
|
||||
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 ]
|
||||
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
|
||||
|
||||
65
.github/workflows/test.yml
vendored
65
.github/workflows/test.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, macos-latest, macos-13 ]
|
||||
os: [ ubuntu-latest, macos-latest ]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
@@ -23,10 +23,7 @@ jobs:
|
||||
|
||||
test-windows:
|
||||
name: Build and test on Windows
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest, windows-2019 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
@@ -38,61 +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-min:
|
||||
name: Build and test on Windows Minimal build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-2019 ]
|
||||
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
|
||||
@@ -109,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
|
||||
@@ -122,17 +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"
|
||||
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -48,8 +48,6 @@ janet.wasm
|
||||
# Generated files
|
||||
*.gen.h
|
||||
*.gen.c
|
||||
*.tmp
|
||||
temp.*
|
||||
|
||||
# Generate test files
|
||||
*.out
|
||||
@@ -128,9 +126,6 @@ vgcore.*
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# GGov
|
||||
*.gcov
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
@@ -139,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
|
||||
|
||||
74
CHANGELOG.md
74
CHANGELOG.md
@@ -1,70 +1,6 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 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
|
||||
@@ -164,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.
|
||||
@@ -335,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
|
||||
@@ -389,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.
|
||||
|
||||
@@ -539,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
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2025 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
|
||||
|
||||
26
Makefile
26
Makefile
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2025 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,7 +43,6 @@ 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,
|
||||
@@ -57,7 +56,6 @@ LDFLAGS?=-rdynamic
|
||||
LIBJANET_LDFLAGS?=$(LD_FLAGS)
|
||||
RUN:=$(RUN)
|
||||
|
||||
|
||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
|
||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 $(COMMON_CFLAGS) -g
|
||||
BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS)
|
||||
@@ -95,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
|
||||
|
||||
|
||||
@@ -147,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 \
|
||||
@@ -213,14 +204,9 @@ build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
|
||||
########################
|
||||
|
||||
ifeq ($(UNAME), Darwin)
|
||||
SONAME=libjanet.1.38.dylib
|
||||
SONAME=libjanet.1.34.dylib
|
||||
else
|
||||
SONAME=libjanet.so.1.38
|
||||
endif
|
||||
|
||||
ifeq ($(MINGW_COMPILER), clang)
|
||||
SONAME=
|
||||
SONAME_SETTER=
|
||||
SONAME=libjanet.so.1.34
|
||||
endif
|
||||
|
||||
build/c/shell.c: src/mainclient/shell.c
|
||||
@@ -372,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'
|
||||
|
||||
16
README.md
16
README.md
@@ -1,4 +1,4 @@
|
||||
[](https://janet.zulipchat.com)
|
||||
[](https://gitter.im/janet-language/community)
|
||||
|
||||
[](https://builds.sr.ht/~bakpakin/janet/commits/master/freebsd.yml?)
|
||||
[](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
|
||||
@@ -207,7 +210,7 @@ Alternatively, install the package directly with `pkgin install janet`.
|
||||
|
||||
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.
|
||||
@@ -250,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
|
||||
|
||||
@@ -314,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
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ for %%f in (src\boot\*.c) do (
|
||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
build\janet_boot . > build\c\janet.c
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
@rem Build the sources
|
||||
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
|
||||
@@ -60,7 +59,6 @@ build\janet_boot . > build\c\janet.c
|
||||
|
||||
@rem Build the resources
|
||||
rc /nologo /fobuild\janet_win.res janet_win.rc
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
@rem Link everything to main client
|
||||
%JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res
|
||||
@@ -91,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
|
||||
@@ -121,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
|
||||
@@ -138,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
|
||||
|
||||
|
||||
@@ -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 [& args]
|
||||
(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)))
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
(def abc 123)
|
||||
@@ -1,7 +0,0 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "badmod.janet"))
|
||||
|
||||
(defn check
|
||||
[&]
|
||||
(error "Check failed!"))
|
||||
@@ -1 +0,0 @@
|
||||
(defn fun [x] (range x))
|
||||
@@ -1,3 +0,0 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "aliases-mod.janet"))
|
||||
@@ -1,4 +0,0 @@
|
||||
@{
|
||||
:name "sample-bundle-aliases"
|
||||
:dependencies ["sample-dep1" "sample-dep2"]
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
@{
|
||||
:name "sample-bundle"
|
||||
:dependencies ["sample-dep1" "sample-dep2"]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "mymod.janet"))
|
||||
@@ -1,7 +0,0 @@
|
||||
(import dep1)
|
||||
(import dep2)
|
||||
|
||||
(defn myfn
|
||||
[x]
|
||||
(def y (dep2/function x))
|
||||
(dep1/function y))
|
||||
@@ -1,4 +0,0 @@
|
||||
@{
|
||||
:name "sample-dep1"
|
||||
:dependencies ["sample-dep2"]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "dep1.janet"))
|
||||
@@ -1,3 +0,0 @@
|
||||
(defn function
|
||||
[x]
|
||||
(+ x x))
|
||||
@@ -1,3 +0,0 @@
|
||||
@{
|
||||
:name "sample-dep2"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "dep2.janet"))
|
||||
@@ -1,3 +0,0 @@
|
||||
(defn function
|
||||
[x]
|
||||
(* x x))
|
||||
3
janet.1
3
janet.1
@@ -255,8 +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 list of as well as a colon
|
||||
separate list of such directories.
|
||||
the default location set at compile time.
|
||||
.RE
|
||||
|
||||
.B JANET_PROFILE
|
||||
|
||||
45
meson.build
45
meson.build
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2025 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.38.0')
|
||||
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'))
|
||||
@@ -141,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',
|
||||
@@ -182,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
|
||||
@@ -206,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']
|
||||
@@ -251,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
|
||||
@@ -271,7 +249,6 @@ 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',
|
||||
@@ -279,7 +256,6 @@ test_files = [
|
||||
'test/suite-debug.janet',
|
||||
'test/suite-ev.janet',
|
||||
'test/suite-ffi.janet',
|
||||
'test/suite-filewatch.janet',
|
||||
'test/suite-inttypes.janet',
|
||||
'test/suite-io.janet',
|
||||
'test/suite-marsh.janet',
|
||||
@@ -294,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'
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
1066
src/boot/boot.janet
1066
src/boot/boot.janet
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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];
|
||||
sprintf(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()));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
#define JANETCONF_H
|
||||
|
||||
#define JANET_VERSION_MAJOR 1
|
||||
#define JANET_VERSION_MINOR 38
|
||||
#define JANET_VERSION_MINOR 34
|
||||
#define JANET_VERSION_PATCH 0
|
||||
#define JANET_VERSION_EXTRA ""
|
||||
#define JANET_VERSION "1.38.0"
|
||||
#define JANET_VERSION "1.34.0"
|
||||
|
||||
/* #define JANET_BUILD "local" */
|
||||
|
||||
@@ -29,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 */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -371,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];
|
||||
}
|
||||
|
||||
@@ -390,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];
|
||||
}
|
||||
|
||||
@@ -406,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];
|
||||
}
|
||||
|
||||
@@ -422,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];
|
||||
}
|
||||
|
||||
@@ -438,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];
|
||||
}
|
||||
|
||||
@@ -645,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),
|
||||
@@ -692,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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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]);
|
||||
@@ -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);
|
||||
@@ -560,8 +501,8 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA
|
||||
/* 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;
|
||||
#else
|
||||
@@ -570,8 +511,8 @@ JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
|
||||
}
|
||||
|
||||
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;
|
||||
#else
|
||||
@@ -580,8 +521,8 @@ JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) {
|
||||
}
|
||||
|
||||
JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
|
||||
#ifdef _MSC_VER
|
||||
return _InterlockedOr(x, 0);
|
||||
#ifdef JANET_WINDOWS
|
||||
return InterlockedOr(x, 0);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_load_explicit(x, memory_order_acquire);
|
||||
#else
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -934,7 +934,7 @@ 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;
|
||||
}
|
||||
@@ -1056,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 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -262,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 */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -69,15 +69,15 @@ JanetModule janet_native(const char *name, const uint8_t **error) {
|
||||
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;
|
||||
}
|
||||
@@ -432,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);
|
||||
}
|
||||
|
||||
@@ -701,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])) {
|
||||
@@ -996,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[] = {
|
||||
@@ -1140,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);
|
||||
@@ -1159,82 +1137,82 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
janet_quick_asm(env, JANET_FUN_CMP,
|
||||
"cmp", 2, 2, 2, 2, cmp_asm, sizeof(cmp_asm),
|
||||
JDOC("(cmp x y)\n\n"
|
||||
"Returns -1 if x is strictly less than y, 1 if y is strictly greater "
|
||||
"than x, and 0 otherwise. To return 0, x and y must be the exact same type."));
|
||||
"Returns -1 if x is strictly less than y, 1 if y is strictly greater "
|
||||
"than x, and 0 otherwise. To return 0, x and y must be the exact same type."));
|
||||
janet_quick_asm(env, JANET_FUN_NEXT,
|
||||
"next", 2, 1, 2, 2, next_asm, sizeof(next_asm),
|
||||
JDOC("(next ds &opt key)\n\n"
|
||||
"Gets the next key in a data structure. Can be used to iterate through "
|
||||
"the keys of a data structure in an unspecified order. Keys are guaranteed "
|
||||
"to be seen only once per iteration if the data structure is not mutated "
|
||||
"during iteration. If key is nil, next returns the first key. If next "
|
||||
"returns nil, there are no more keys to iterate through."));
|
||||
"Gets the next key in a data structure. Can be used to iterate through "
|
||||
"the keys of a data structure in an unspecified order. Keys are guaranteed "
|
||||
"to be seen only once per iteration if the data structure is not mutated "
|
||||
"during iteration. If key is nil, next returns the first key. If next "
|
||||
"returns nil, there are no more keys to iterate through."));
|
||||
janet_quick_asm(env, JANET_FUN_PROP,
|
||||
"propagate", 2, 2, 2, 2, propagate_asm, sizeof(propagate_asm),
|
||||
JDOC("(propagate x fiber)\n\n"
|
||||
"Propagate a signal from a fiber to the current fiber and "
|
||||
"set the last value of the current fiber to `x`. The signal "
|
||||
"value is then available as the status of the current fiber. "
|
||||
"The resulting stack trace from the current fiber will include "
|
||||
"frames from fiber. If fiber is in a state that can be resumed, "
|
||||
"resuming the current fiber will first resume `fiber`. "
|
||||
"This function can be used to re-raise an error without losing "
|
||||
"the original stack trace."));
|
||||
"Propagate a signal from a fiber to the current fiber and "
|
||||
"set the last value of the current fiber to `x`. The signal "
|
||||
"value is then available as the status of the current fiber. "
|
||||
"The resulting stack trace from the current fiber will include "
|
||||
"frames from fiber. If fiber is in a state that can be resumed, "
|
||||
"resuming the current fiber will first resume `fiber`. "
|
||||
"This function can be used to re-raise an error without losing "
|
||||
"the original stack trace."));
|
||||
janet_quick_asm(env, JANET_FUN_DEBUG,
|
||||
"debug", 1, 0, 1, 1, debug_asm, sizeof(debug_asm),
|
||||
JDOC("(debug &opt x)\n\n"
|
||||
"Throws a debug signal that can be caught by a parent fiber and used to inspect "
|
||||
"the running state of the current fiber. Returns the value passed in by resume."));
|
||||
"Throws a debug signal that can be caught by a parent fiber and used to inspect "
|
||||
"the running state of the current fiber. Returns the value passed in by resume."));
|
||||
janet_quick_asm(env, JANET_FUN_ERROR,
|
||||
"error", 1, 1, 1, 1, error_asm, sizeof(error_asm),
|
||||
JDOC("(error e)\n\n"
|
||||
"Throws an error e that can be caught and handled by a parent fiber."));
|
||||
"Throws an error e that can be caught and handled by a parent fiber."));
|
||||
janet_quick_asm(env, JANET_FUN_YIELD,
|
||||
"yield", 1, 0, 1, 2, yield_asm, sizeof(yield_asm),
|
||||
JDOC("(yield &opt x)\n\n"
|
||||
"Yield a value to a parent fiber. When a fiber yields, its execution is paused until "
|
||||
"another thread resumes it. The fiber will then resume, and the last yield call will "
|
||||
"return the value that was passed to resume."));
|
||||
"Yield a value to a parent fiber. When a fiber yields, its execution is paused until "
|
||||
"another thread resumes it. The fiber will then resume, and the last yield call will "
|
||||
"return the value that was passed to resume."));
|
||||
janet_quick_asm(env, JANET_FUN_CANCEL,
|
||||
"cancel", 2, 2, 2, 2, cancel_asm, sizeof(cancel_asm),
|
||||
JDOC("(cancel fiber err)\n\n"
|
||||
"Resume a fiber but have it immediately raise an error. This lets a programmer unwind a pending fiber. "
|
||||
"Returns the same result as resume."));
|
||||
"Resume a fiber but have it immediately raise an error. This lets a programmer unwind a pending fiber. "
|
||||
"Returns the same result as resume."));
|
||||
janet_quick_asm(env, JANET_FUN_RESUME,
|
||||
"resume", 2, 1, 2, 2, resume_asm, sizeof(resume_asm),
|
||||
JDOC("(resume fiber &opt x)\n\n"
|
||||
"Resume a new or suspended fiber and optionally pass in a value to the fiber that "
|
||||
"will be returned to the last yield in the case of a pending fiber, or the argument to "
|
||||
"the dispatch function in the case of a new fiber. Returns either the return result of "
|
||||
"the fiber's dispatch function, or the value from the next yield call in fiber."));
|
||||
"Resume a new or suspended fiber and optionally pass in a value to the fiber that "
|
||||
"will be returned to the last yield in the case of a pending fiber, or the argument to "
|
||||
"the dispatch function in the case of a new fiber. Returns either the return result of "
|
||||
"the fiber's dispatch function, or the value from the next yield call in fiber."));
|
||||
janet_quick_asm(env, JANET_FUN_IN,
|
||||
"in", 3, 2, 3, 4, in_asm, sizeof(in_asm),
|
||||
JDOC("(in ds key &opt dflt)\n\n"
|
||||
"Get value in ds at key, works on associative data structures. Arrays, tuples, tables, structs, "
|
||||
"strings, symbols, and buffers are all associative and can be used. Arrays, tuples, strings, buffers, "
|
||||
"and symbols must use integer keys that are in bounds or an error is raised. Structs and tables can "
|
||||
"take any value as a key except nil and will return nil or dflt if not found."));
|
||||
"Get value in ds at key, works on associative data structures. Arrays, tuples, tables, structs, "
|
||||
"strings, symbols, and buffers are all associative and can be used. Arrays, tuples, strings, buffers, "
|
||||
"and symbols must use integer keys that are in bounds or an error is raised. Structs and tables can "
|
||||
"take any value as a key except nil and will return nil or dflt if not found."));
|
||||
janet_quick_asm(env, JANET_FUN_GET,
|
||||
"get", 3, 2, 3, 4, get_asm, sizeof(in_asm),
|
||||
JDOC("(get ds key &opt dflt)\n\n"
|
||||
"Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
|
||||
"Similar to in, but will not throw an error if the key is invalid for the data structure "
|
||||
"unless the data structure is an abstract type. In that case, the abstract type getter may throw "
|
||||
"an error."));
|
||||
"Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
|
||||
"Similar to in, but will not throw an error if the key is invalid for the data structure "
|
||||
"unless the data structure is an abstract type. In that case, the abstract type getter may throw "
|
||||
"an error."));
|
||||
janet_quick_asm(env, JANET_FUN_PUT,
|
||||
"put", 3, 3, 3, 3, put_asm, sizeof(put_asm),
|
||||
JDOC("(put ds key value)\n\n"
|
||||
"Associate a key with a value in any mutable associative data structure. Indexed data structures "
|
||||
"(arrays and buffers) only accept non-negative integer keys, and will expand if an out of bounds "
|
||||
"value is provided. In an array, extra space will be filled with nils, and in a buffer, extra "
|
||||
"space will be filled with 0 bytes. In a table, putting a key that is contained in the table prototype "
|
||||
"will hide the association defined by the prototype, but will not mutate the prototype table. Putting "
|
||||
"a value nil into a table will remove the key from the table. Returns the data structure ds."));
|
||||
"Associate a key with a value in any mutable associative data structure. Indexed data structures "
|
||||
"(arrays and buffers) only accept non-negative integer keys, and will expand if an out of bounds "
|
||||
"value is provided. In an array, extra space will be filled with nils, and in a buffer, extra "
|
||||
"space will be filled with 0 bytes. In a table, putting a key that is contained in the table prototype "
|
||||
"will hide the association defined by the prototype, but will not mutate the prototype table. Putting "
|
||||
"a value nil into a table will remove the key from the table. Returns the data structure ds."));
|
||||
janet_quick_asm(env, JANET_FUN_LENGTH,
|
||||
"length", 1, 1, 1, 1, length_asm, sizeof(length_asm),
|
||||
JDOC("(length ds)\n\n"
|
||||
"Returns the length or count of a data structure in constant time as an integer. For "
|
||||
"structs and tables, returns the number of key-value pairs in the data structure."));
|
||||
"Returns the length or count of a data structure in constant time as an integer. For "
|
||||
"structs and tables, returns the number of key-value pairs in the data structure."));
|
||||
janet_quick_asm(env, JANET_FUN_BNOT,
|
||||
"bnot", 1, 1, 1, 1, bnot_asm, sizeof(bnot_asm),
|
||||
JDOC("(bnot x)\n\nReturns the bit-wise inverse of integer x."));
|
||||
@@ -1243,74 +1221,74 @@ 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. xs must be integers or real numbers only. 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 "
|
||||
"negative value of that element. Otherwise, returns the first element in xs minus the sum of "
|
||||
"the rest of the elements."));
|
||||
"Returns the difference of xs. If xs is empty, returns 0. If xs has one element, returns the "
|
||||
"negative value of that element. Otherwise, returns the first element in xs minus the sum of "
|
||||
"the rest of the elements."));
|
||||
templatize_varop(env, JANET_FUN_MULTIPLY, "*", 1, 1, JOP_MULTIPLY,
|
||||
JDOC("(* & xs)\n\n"
|
||||
"Returns the product of all elements in xs. If xs is empty, returns 1."));
|
||||
"Returns the product of all elements in xs. If xs is empty, returns 1."));
|
||||
templatize_varop(env, JANET_FUN_DIVIDE, "/", 1, 1, JOP_DIVIDE,
|
||||
JDOC("(/ & xs)\n\n"
|
||||
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
templatize_varop(env, JANET_FUN_DIVIDE_FLOOR, "div", 1, 1, JOP_DIVIDE_FLOOR,
|
||||
JDOC("(div & xs)\n\n"
|
||||
"Returns the floored division of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
"Returns the floored division of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
templatize_varop(env, JANET_FUN_MODULO, "mod", 0, 1, JOP_MODULO,
|
||||
JDOC("(mod & xs)\n\n"
|
||||
"Returns the result of applying the modulo operator on the first value of xs with each remaining value. "
|
||||
"`(mod x 0)` is defined to be `x`."));
|
||||
"Returns the result of applying the modulo operator on the first value of xs with each remaining value. "
|
||||
"`(mod x 0)` is defined to be `x`."));
|
||||
templatize_varop(env, JANET_FUN_REMAINDER, "%", 0, 1, JOP_REMAINDER,
|
||||
JDOC("(% & xs)\n\n"
|
||||
"Returns the remainder of dividing the first value of xs by each remaining value."));
|
||||
"Returns the remainder of dividing the first value of xs by each remaining value."));
|
||||
templatize_varop(env, JANET_FUN_BAND, "band", -1, -1, JOP_BAND,
|
||||
JDOC("(band & xs)\n\n"
|
||||
"Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));
|
||||
"Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_BOR, "bor", 0, 0, JOP_BOR,
|
||||
JDOC("(bor & xs)\n\n"
|
||||
"Returns the bit-wise or of all values in xs. Each x in xs must be an integer."));
|
||||
"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 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 "
|
||||
"and each element in shift must be an integer."));
|
||||
"Returns the value of x bit shifted left by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_RSHIFT, "brshift", 1, 1, JOP_SHIFT_RIGHT,
|
||||
JDOC("(brshift x & shifts)\n\n"
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_RSHIFTU, "brushift", 1, 1, JOP_SHIFT_RIGHT_UNSIGNED,
|
||||
JDOC("(brushift x & shifts)\n\n"
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer. The sign of x is not preserved, so "
|
||||
"for positive shifts the return value will always be positive."));
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer. The sign of x is not preserved, so "
|
||||
"for positive shifts the return value will always be positive."));
|
||||
|
||||
/* Variadic comparators */
|
||||
templatize_comparator(env, JANET_FUN_GT, ">", 0, JOP_GREATER_THAN,
|
||||
JDOC("(> & xs)\n\n"
|
||||
"Check if xs is in descending order. Returns a boolean."));
|
||||
"Check if xs is in descending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_LT, "<", 0, JOP_LESS_THAN,
|
||||
JDOC("(< & xs)\n\n"
|
||||
"Check if xs is in ascending order. Returns a boolean."));
|
||||
"Check if xs is in ascending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_GTE, ">=", 0, JOP_GREATER_THAN_EQUAL,
|
||||
JDOC("(>= & xs)\n\n"
|
||||
"Check if xs is in non-ascending order. Returns a boolean."));
|
||||
"Check if xs is in non-ascending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_LTE, "<=", 0, JOP_LESS_THAN_EQUAL,
|
||||
JDOC("(<= & xs)\n\n"
|
||||
"Check if xs is in non-descending order. Returns a boolean."));
|
||||
"Check if xs is in non-descending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_EQ, "=", 0, JOP_EQUALS,
|
||||
JDOC("(= & xs)\n\n"
|
||||
"Check if all values in xs are equal. Returns a boolean."));
|
||||
"Check if all values in xs are equal. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_NEQ, "not=", 1, JOP_EQUALS,
|
||||
JDOC("(not= & xs)\n\n"
|
||||
"Check if any values in xs are not equal. Returns a boolean."));
|
||||
"Check if any values in xs are not equal. Returns a boolean."));
|
||||
|
||||
/* Platform detection */
|
||||
janet_def(env, "janet/version", janet_cstringv(JANET_VERSION),
|
||||
@@ -1319,7 +1297,7 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
JDOC("The build identifier of the running janet program."));
|
||||
janet_def(env, "janet/config-bits", janet_wrap_integer(JANET_CURRENT_CONFIG_BITS),
|
||||
JDOC("The flag set of config options from janetconf.h which is used to check "
|
||||
"if native modules are compatible with the host program."));
|
||||
"if native modules are compatible with the host program."));
|
||||
|
||||
/* Allow references to the environment */
|
||||
janet_def(env, "root-env", janet_wrap_table(env),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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");
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
279
src/core/ev.c
279
src/core/ev.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -32,11 +32,9 @@
|
||||
#ifdef JANET_EV
|
||||
|
||||
#include <math.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <limits.h>
|
||||
@@ -45,6 +43,7 @@
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netdb.h>
|
||||
@@ -75,7 +74,7 @@ typedef struct {
|
||||
} mode;
|
||||
} JanetChannelPending;
|
||||
|
||||
struct JanetChannel {
|
||||
typedef struct {
|
||||
JanetQueue items;
|
||||
JanetQueue read_pending;
|
||||
JanetQueue write_pending;
|
||||
@@ -87,7 +86,7 @@ struct JanetChannel {
|
||||
#else
|
||||
pthread_mutex_t lock;
|
||||
#endif
|
||||
};
|
||||
} JanetChannel;
|
||||
|
||||
typedef struct {
|
||||
JanetFiber *fiber;
|
||||
@@ -256,12 +255,6 @@ static void add_timeout(JanetTimeout to) {
|
||||
|
||||
void janet_async_end(JanetFiber *fiber) {
|
||||
if (fiber->ev_callback) {
|
||||
if (fiber->ev_stream->read_fiber == fiber) {
|
||||
fiber->ev_stream->read_fiber = NULL;
|
||||
}
|
||||
if (fiber->ev_stream->write_fiber == fiber) {
|
||||
fiber->ev_stream->write_fiber = NULL;
|
||||
}
|
||||
fiber->ev_callback(fiber, JANET_ASYNC_EVENT_DEINIT);
|
||||
janet_gcunroot(janet_wrap_abstract(fiber->ev_stream));
|
||||
fiber->ev_callback = NULL;
|
||||
@@ -283,24 +276,17 @@ void janet_async_in_flight(JanetFiber *fiber) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void janet_async_start_fiber(JanetFiber *fiber, JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state) {
|
||||
void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state) {
|
||||
JanetFiber *fiber = janet_vm.root_fiber;
|
||||
janet_assert(!fiber->ev_callback, "double async on fiber");
|
||||
if (mode & JANET_ASYNC_LISTEN_READ) {
|
||||
stream->read_fiber = fiber;
|
||||
}
|
||||
if (mode & JANET_ASYNC_LISTEN_WRITE) {
|
||||
stream->write_fiber = fiber;
|
||||
}
|
||||
if (mode & JANET_ASYNC_LISTEN_READ) stream->read_fiber = fiber;
|
||||
if (mode & JANET_ASYNC_LISTEN_WRITE) stream->write_fiber = fiber;
|
||||
fiber->ev_callback = callback;
|
||||
fiber->ev_stream = stream;
|
||||
janet_ev_inc_refcount();
|
||||
janet_gcroot(janet_wrap_abstract(stream));
|
||||
fiber->ev_state = state;
|
||||
callback(fiber, JANET_ASYNC_EVENT_INIT);
|
||||
}
|
||||
|
||||
void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state) {
|
||||
janet_async_start_fiber(janet_vm.root_fiber, stream, mode, callback, state);
|
||||
janet_await();
|
||||
}
|
||||
|
||||
@@ -326,9 +312,8 @@ static const JanetMethod ev_default_stream_methods[] = {
|
||||
};
|
||||
|
||||
/* Create a stream*/
|
||||
JanetStream *janet_stream_ext(JanetHandle handle, uint32_t flags, const JanetMethod *methods, size_t size) {
|
||||
janet_assert(size >= sizeof(JanetStream), "bad size");
|
||||
JanetStream *stream = janet_abstract(&janet_stream_type, size);
|
||||
JanetStream *janet_stream(JanetHandle handle, uint32_t flags, const JanetMethod *methods) {
|
||||
JanetStream *stream = janet_abstract(&janet_stream_type, sizeof(JanetStream));
|
||||
stream->handle = handle;
|
||||
stream->flags = flags;
|
||||
stream->read_fiber = NULL;
|
||||
@@ -340,10 +325,6 @@ JanetStream *janet_stream_ext(JanetHandle handle, uint32_t flags, const JanetMet
|
||||
return stream;
|
||||
}
|
||||
|
||||
JanetStream *janet_stream(JanetHandle handle, uint32_t flags, const JanetMethod *methods) {
|
||||
return janet_stream_ext(handle, flags, methods, sizeof(JanetStream));
|
||||
}
|
||||
|
||||
static void janet_stream_close_impl(JanetStream *stream) {
|
||||
stream->flags |= JANET_STREAM_CLOSED;
|
||||
#ifdef JANET_WINDOWS
|
||||
@@ -448,7 +429,7 @@ static void janet_stream_marshal(void *p, JanetMarshalContext *ctx) {
|
||||
}
|
||||
janet_marshal_int64(ctx, (int64_t)(duph));
|
||||
#else
|
||||
/* Marshal after dup because it is easier than maintaining our own ref counting. */
|
||||
/* Marshal after dup becuse it is easier than maintaining our own ref counting. */
|
||||
int duph = dup(s->handle);
|
||||
if (duph < 0) janet_panicf("failed to duplicate stream handle: %V", janet_ev_lasterr());
|
||||
janet_marshal_int(ctx, (int32_t)(duph));
|
||||
@@ -481,12 +462,6 @@ static Janet janet_stream_next(void *p, Janet key) {
|
||||
return janet_nextmethod(stream->methods, key);
|
||||
}
|
||||
|
||||
static void janet_stream_tostring(void *p, JanetBuffer *buffer) {
|
||||
JanetStream *stream = p;
|
||||
/* Let user print the file descriptor for debugging */
|
||||
janet_formatb(buffer, "[fd=%d]", stream->handle);
|
||||
}
|
||||
|
||||
const JanetAbstractType janet_stream_type = {
|
||||
"core/stream",
|
||||
janet_stream_gc,
|
||||
@@ -495,7 +470,7 @@ const JanetAbstractType janet_stream_type = {
|
||||
NULL,
|
||||
janet_stream_marshal,
|
||||
janet_stream_unmarshal,
|
||||
janet_stream_tostring,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
janet_stream_next,
|
||||
@@ -610,7 +585,7 @@ void janet_ev_deinit_common(void) {
|
||||
|
||||
/* Shorthand to yield to event loop */
|
||||
void janet_await(void) {
|
||||
/* Store the fiber in a global table */
|
||||
/* Store the fiber in a gobal table */
|
||||
janet_signalv(JANET_SIGNAL_EVENT, janet_wrap_nil());
|
||||
}
|
||||
|
||||
@@ -626,18 +601,6 @@ void janet_addtimeout(double sec) {
|
||||
add_timeout(to);
|
||||
}
|
||||
|
||||
/* Set timeout for the current root fiber but resume with nil instead of raising an error */
|
||||
void janet_addtimeout_nil(double sec) {
|
||||
JanetFiber *fiber = janet_vm.root_fiber;
|
||||
JanetTimeout to;
|
||||
to.when = ts_delta(ts_now(), sec);
|
||||
to.fiber = fiber;
|
||||
to.curr_fiber = NULL;
|
||||
to.sched_id = fiber->sched_id;
|
||||
to.is_error = 0;
|
||||
add_timeout(to);
|
||||
}
|
||||
|
||||
void janet_ev_inc_refcount(void) {
|
||||
janet_atomic_inc(&janet_vm.listener_count);
|
||||
}
|
||||
@@ -893,7 +856,7 @@ static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode
|
||||
/* No root fiber, we are in completion on a root fiber. Don't block. */
|
||||
if (mode == 2) {
|
||||
janet_chan_unlock(channel);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
/* Pushed successfully, but should block. */
|
||||
JanetChannelPending pending;
|
||||
@@ -949,7 +912,6 @@ static int janet_channel_pop_with_lock(JanetChannel *channel, Janet *item, int i
|
||||
int is_threaded = janet_chan_is_threaded(channel);
|
||||
if (janet_q_pop(&channel->items, item, sizeof(Janet))) {
|
||||
/* Queue empty */
|
||||
if (is_choice == 2) return 0; // Skip pending read
|
||||
JanetChannelPending pending;
|
||||
pending.thread = &janet_vm;
|
||||
pending.fiber = janet_vm.root_fiber,
|
||||
@@ -1007,28 +969,6 @@ JanetChannel *janet_optchannel(const Janet *argv, int32_t argc, int32_t n, Janet
|
||||
}
|
||||
}
|
||||
|
||||
int janet_channel_give(JanetChannel *channel, Janet x) {
|
||||
return janet_channel_push(channel, x, 2);
|
||||
}
|
||||
|
||||
int janet_channel_take(JanetChannel *channel, Janet *out) {
|
||||
return janet_channel_pop(channel, out, 2);
|
||||
}
|
||||
|
||||
JanetChannel *janet_channel_make(uint32_t limit) {
|
||||
janet_assert(limit <= INT32_MAX, "bad limit");
|
||||
JanetChannel *channel = janet_abstract(&janet_channel_type, sizeof(JanetChannel));
|
||||
janet_chan_init(channel, (int32_t) limit, 0);
|
||||
return channel;
|
||||
}
|
||||
|
||||
JanetChannel *janet_channel_make_threaded(uint32_t limit) {
|
||||
janet_assert(limit <= INT32_MAX, "bad limit");
|
||||
JanetChannel *channel = janet_abstract_threaded(&janet_channel_type, sizeof(JanetChannel));
|
||||
janet_chan_init(channel, (int32_t) limit, 0);
|
||||
return channel;
|
||||
}
|
||||
|
||||
/* Channel Methods */
|
||||
|
||||
JANET_CORE_FN(cfun_channel_push,
|
||||
@@ -1037,9 +977,6 @@ JANET_CORE_FN(cfun_channel_push,
|
||||
"Returns the channel if the write succeeded, nil otherwise.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetChannel *channel = janet_getchannel(argv, 0);
|
||||
if (janet_vm.coerce_error) {
|
||||
janet_panic("cannot give to channel inside janet_call");
|
||||
}
|
||||
if (janet_channel_push(channel, argv[1], 0)) {
|
||||
janet_await();
|
||||
}
|
||||
@@ -1052,9 +989,6 @@ JANET_CORE_FN(cfun_channel_pop,
|
||||
janet_fixarity(argc, 1);
|
||||
JanetChannel *channel = janet_getchannel(argv, 0);
|
||||
Janet item;
|
||||
if (janet_vm.coerce_error) {
|
||||
janet_panic("cannot take from channel inside janet_call");
|
||||
}
|
||||
if (janet_channel_pop(channel, &item, 0)) {
|
||||
janet_schedule(janet_vm.root_fiber, item);
|
||||
}
|
||||
@@ -1091,10 +1025,6 @@ JANET_CORE_FN(cfun_channel_choice,
|
||||
int32_t len;
|
||||
const Janet *data;
|
||||
|
||||
if (janet_vm.coerce_error) {
|
||||
janet_panic("cannot select from channel inside janet_call");
|
||||
}
|
||||
|
||||
/* Check channels for immediate reads and writes */
|
||||
for (int32_t i = 0; i < argc; i++) {
|
||||
if (janet_indexed_view(argv[i], &data, &len) && len == 2) {
|
||||
@@ -1531,16 +1461,13 @@ void janet_ev_deinit(void) {
|
||||
|
||||
static void janet_register_stream(JanetStream *stream) {
|
||||
if (NULL == CreateIoCompletionPort(stream->handle, janet_vm.iocp, (ULONG_PTR) stream, 0)) {
|
||||
if (stream->flags & (JANET_STREAM_READABLE | JANET_STREAM_WRITABLE | JANET_STREAM_ACCEPTABLE)) {
|
||||
janet_panicf("failed to listen for events: %V", janet_ev_lasterr());
|
||||
}
|
||||
stream->flags |= JANET_STREAM_UNREGISTERED;
|
||||
janet_panicf("failed to listen for events: %V", janet_ev_lasterr());
|
||||
}
|
||||
}
|
||||
|
||||
void janet_loop1_impl(int has_timeout, JanetTimestamp to) {
|
||||
ULONG_PTR completionKey = 0;
|
||||
DWORD num_bytes_transferred = 0;
|
||||
DWORD num_bytes_transfered = 0;
|
||||
LPOVERLAPPED overlapped = NULL;
|
||||
|
||||
/* Calculate how long to wait before timeout */
|
||||
@@ -1555,7 +1482,7 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp to) {
|
||||
} else {
|
||||
waittime = INFINITE;
|
||||
}
|
||||
BOOL result = GetQueuedCompletionStatus(janet_vm.iocp, &num_bytes_transferred, &completionKey, &overlapped, (DWORD) waittime);
|
||||
BOOL result = GetQueuedCompletionStatus(janet_vm.iocp, &num_bytes_transfered, &completionKey, &overlapped, (DWORD) waittime);
|
||||
|
||||
if (result || overlapped) {
|
||||
if (0 == completionKey) {
|
||||
@@ -1578,7 +1505,7 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp to) {
|
||||
if (fiber != NULL) {
|
||||
fiber->flags &= ~JANET_FIBER_EV_FLAG_IN_FLIGHT;
|
||||
/* System is done with this, we can reused this data */
|
||||
overlapped->InternalHigh = (ULONG_PTR) num_bytes_transferred;
|
||||
overlapped->InternalHigh = (ULONG_PTR) num_bytes_transfered;
|
||||
fiber->ev_callback(fiber, result ? JANET_ASYNC_EVENT_COMPLETE : JANET_ASYNC_EVENT_FAILED);
|
||||
} else {
|
||||
janet_free((void *) overlapped);
|
||||
@@ -1589,14 +1516,6 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp to) {
|
||||
}
|
||||
}
|
||||
|
||||
void janet_stream_edge_triggered(JanetStream *stream) {
|
||||
(void) stream;
|
||||
}
|
||||
|
||||
void janet_stream_level_triggered(JanetStream *stream) {
|
||||
(void) stream;
|
||||
}
|
||||
|
||||
#elif defined(JANET_EV_EPOLL)
|
||||
|
||||
static JanetTimestamp ts_now(void) {
|
||||
@@ -1608,15 +1527,15 @@ static JanetTimestamp ts_now(void) {
|
||||
}
|
||||
|
||||
/* Wait for the next event */
|
||||
static void janet_register_stream_impl(JanetStream *stream, int mod, int edge_trigger) {
|
||||
static void janet_register_stream(JanetStream *stream) {
|
||||
struct epoll_event ev;
|
||||
ev.events = edge_trigger ? EPOLLET : 0;
|
||||
ev.events = EPOLLET;
|
||||
if (stream->flags & (JANET_STREAM_READABLE | JANET_STREAM_ACCEPTABLE)) ev.events |= EPOLLIN;
|
||||
if (stream->flags & JANET_STREAM_WRITABLE) ev.events |= EPOLLOUT;
|
||||
ev.data.ptr = stream;
|
||||
int status;
|
||||
do {
|
||||
status = epoll_ctl(janet_vm.epoll, mod ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, stream->handle, &ev);
|
||||
status = epoll_ctl(janet_vm.epoll, EPOLL_CTL_ADD, stream->handle, &ev);
|
||||
} while (status == -1 && errno == EINTR);
|
||||
if (status == -1) {
|
||||
if (errno == EPERM) {
|
||||
@@ -1630,18 +1549,6 @@ static void janet_register_stream_impl(JanetStream *stream, int mod, int edge_tr
|
||||
}
|
||||
}
|
||||
|
||||
static void janet_register_stream(JanetStream *stream) {
|
||||
janet_register_stream_impl(stream, 0, 1);
|
||||
}
|
||||
|
||||
void janet_stream_edge_triggered(JanetStream *stream) {
|
||||
janet_register_stream_impl(stream, 1, 1);
|
||||
}
|
||||
|
||||
void janet_stream_level_triggered(JanetStream *stream) {
|
||||
janet_register_stream_impl(stream, 1, 0);
|
||||
}
|
||||
|
||||
#define JANET_EPOLL_MAX_EVENTS 64
|
||||
void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
|
||||
struct itimerspec its;
|
||||
@@ -1771,15 +1678,14 @@ static void timestamp2timespec(struct timespec *t, JanetTimestamp ts) {
|
||||
t->tv_nsec = ts == 0 ? 0 : (ts % 1000) * 1000000;
|
||||
}
|
||||
|
||||
void janet_register_stream_impl(JanetStream *stream, int edge_trigger) {
|
||||
void janet_register_stream(JanetStream *stream) {
|
||||
struct kevent kevs[2];
|
||||
int length = 0;
|
||||
int clear = edge_trigger ? EV_CLEAR : 0;
|
||||
if (stream->flags & (JANET_STREAM_READABLE | JANET_STREAM_ACCEPTABLE)) {
|
||||
EV_SETx(&kevs[length++], stream->handle, EVFILT_READ, EV_ADD | EV_ENABLE | clear, 0, 0, stream);
|
||||
EV_SETx(&kevs[length++], stream->handle, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, stream);
|
||||
}
|
||||
if (stream->flags & JANET_STREAM_WRITABLE) {
|
||||
EV_SETx(&kevs[length++], stream->handle, EVFILT_WRITE, EV_ADD | EV_ENABLE | clear, 0, 0, stream);
|
||||
EV_SETx(&kevs[length++], stream->handle, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, stream);
|
||||
}
|
||||
int status;
|
||||
do {
|
||||
@@ -1790,34 +1696,6 @@ void janet_register_stream_impl(JanetStream *stream, int edge_trigger) {
|
||||
}
|
||||
}
|
||||
|
||||
void janet_register_stream(JanetStream *stream) {
|
||||
janet_register_stream_impl(stream, 1);
|
||||
}
|
||||
|
||||
void janet_stream_edge_triggered(JanetStream *stream) {
|
||||
janet_register_stream_impl(stream, 1);
|
||||
}
|
||||
|
||||
void janet_stream_level_triggered(JanetStream *stream) {
|
||||
/* On macos, we seem to need to delete any registered events before re-registering without
|
||||
* EV_CLEAR, otherwise the new event will still have EV_CLEAR set erroneously. This could be a
|
||||
* kernel bug, but unfortunately the specification is vague here, esp. in regards to where and when
|
||||
* EV_CLEAR is set automatically. */
|
||||
struct kevent kevs[2];
|
||||
int length = 0;
|
||||
if (stream->flags & (JANET_STREAM_READABLE | JANET_STREAM_ACCEPTABLE)) {
|
||||
EV_SETx(&kevs[length++], stream->handle, EVFILT_READ, EV_DELETE, 0, 0, stream);
|
||||
}
|
||||
if (stream->flags & JANET_STREAM_WRITABLE) {
|
||||
EV_SETx(&kevs[length++], stream->handle, EVFILT_WRITE, EV_DELETE, 0, 0, stream);
|
||||
}
|
||||
int status;
|
||||
do {
|
||||
status = kevent(janet_vm.kq, kevs, length, NULL, 0, NULL);
|
||||
} while (status == -1 && errno == EINTR);
|
||||
janet_register_stream_impl(stream, 0);
|
||||
}
|
||||
|
||||
#define JANET_KQUEUE_MAX_EVENTS 64
|
||||
|
||||
void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
|
||||
@@ -1940,30 +1818,15 @@ void janet_register_stream(JanetStream *stream) {
|
||||
janet_vm.stream_count = new_count;
|
||||
}
|
||||
|
||||
void janet_stream_edge_triggered(JanetStream *stream) {
|
||||
(void) stream;
|
||||
}
|
||||
|
||||
void janet_stream_level_triggered(JanetStream *stream) {
|
||||
(void) stream;
|
||||
}
|
||||
|
||||
void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
|
||||
|
||||
/* set event flags */
|
||||
for (size_t i = 0; i < janet_vm.stream_count; i++) {
|
||||
JanetStream *stream = janet_vm.streams[i];
|
||||
struct pollfd *pfd = janet_vm.fds + i + 1;
|
||||
pfd->events = 0;
|
||||
pfd->revents = 0;
|
||||
JanetFiber *rf = stream->read_fiber;
|
||||
JanetFiber *wf = stream->write_fiber;
|
||||
if (rf && rf->ev_callback) pfd->events |= POLLIN;
|
||||
if (wf && wf->ev_callback) pfd->events |= POLLOUT;
|
||||
/* Hack to ignore a file descriptor - make file descriptor negative if we want to ignore */
|
||||
if (!pfd->events) {
|
||||
pfd->fd = -pfd->fd;
|
||||
}
|
||||
janet_vm.fds[i + 1].events = 0;
|
||||
janet_vm.fds[i + 1].revents = 0;
|
||||
if (stream->read_fiber && stream->read_fiber->ev_callback) janet_vm.fds[i + 1].events |= POLLIN;
|
||||
if (stream->write_fiber && stream->write_fiber->ev_callback) janet_vm.fds[i + 1].events |= POLLOUT;
|
||||
}
|
||||
|
||||
/* Poll for events */
|
||||
@@ -1980,14 +1843,6 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
|
||||
JANET_EXIT("failed to poll events");
|
||||
}
|
||||
|
||||
/* Undo negative hack */
|
||||
for (size_t i = 0; i < janet_vm.stream_count; i++) {
|
||||
struct pollfd *pfd = janet_vm.fds + i + 1;
|
||||
if (pfd->fd < 0) {
|
||||
pfd->fd = -pfd->fd;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check selfpipe */
|
||||
if (janet_vm.fds[0].revents & POLLIN) {
|
||||
janet_vm.fds[0].revents = 0;
|
||||
@@ -2170,7 +2025,7 @@ void janet_ev_threaded_call(JanetThreadedSubroutine fp, JanetEVGenericMessage ar
|
||||
int err = pthread_create(&waiter_thread, &janet_vm.new_thread_attr, janet_thread_body, init);
|
||||
if (err) {
|
||||
janet_free(init);
|
||||
janet_panicf("%s", janet_strerror(err));
|
||||
janet_panicf("%s", strerror(err));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2279,7 +2134,7 @@ Janet janet_ev_lasterr(void) {
|
||||
}
|
||||
#else
|
||||
Janet janet_ev_lasterr(void) {
|
||||
return janet_cstringv(janet_strerror(errno));
|
||||
return janet_cstringv(strerror(errno));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2406,7 +2261,6 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
} else {
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
}
|
||||
stream->read_fiber = NULL;
|
||||
janet_async_end(fiber);
|
||||
break;
|
||||
}
|
||||
@@ -2779,7 +2633,6 @@ static volatile long PipeSerialNumber;
|
||||
* mode = 0: both sides non-blocking.
|
||||
* mode = 1: only read side non-blocking: write side sent to subprocess
|
||||
* mode = 2: only write side non-blocking: read side sent to subprocess
|
||||
* mode = 3: both sides blocking - for use in two subprocesses (making pipeline from external processes)
|
||||
*/
|
||||
int janet_make_pipe(JanetHandle handles[2], int mode) {
|
||||
#ifdef JANET_WINDOWS
|
||||
@@ -2793,11 +2646,6 @@ int janet_make_pipe(JanetHandle handles[2], int mode) {
|
||||
memset(&saAttr, 0, sizeof(saAttr));
|
||||
saAttr.nLength = sizeof(saAttr);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
if (mode == 3) {
|
||||
/* No overlapped IO involved, just call CreatePipe */
|
||||
if (!CreatePipe(handles, handles + 1, &saAttr, 0)) return -1;
|
||||
return 0;
|
||||
}
|
||||
sprintf(PipeNameBuffer,
|
||||
"\\\\.\\Pipe\\JanetPipeFile.%08x.%08x",
|
||||
(unsigned int) GetCurrentProcessId(),
|
||||
@@ -2843,8 +2691,8 @@ int janet_make_pipe(JanetHandle handles[2], int mode) {
|
||||
if (pipe(handles)) return -1;
|
||||
if (mode != 2 && fcntl(handles[0], F_SETFD, FD_CLOEXEC)) goto error;
|
||||
if (mode != 1 && fcntl(handles[1], F_SETFD, FD_CLOEXEC)) goto error;
|
||||
if (mode != 2 && mode != 3 && fcntl(handles[0], F_SETFL, O_NONBLOCK)) goto error;
|
||||
if (mode != 1 && mode != 3 && fcntl(handles[1], F_SETFL, O_NONBLOCK)) goto error;
|
||||
if (mode != 2 && fcntl(handles[0], F_SETFL, O_NONBLOCK)) goto error;
|
||||
if (mode != 1 && fcntl(handles[1], F_SETFL, O_NONBLOCK)) goto error;
|
||||
return 0;
|
||||
error:
|
||||
close(handles[0]);
|
||||
@@ -2918,7 +2766,7 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
|
||||
janet_gcroot(janet_wrap_table(janet_vm.abstract_registry));
|
||||
}
|
||||
|
||||
/* Get supervisor */
|
||||
/* Get supervsior */
|
||||
if (flags & JANET_THREAD_SUPERVISOR_FLAG) {
|
||||
Janet sup =
|
||||
janet_unmarshal(nextbytes, endbytes - nextbytes,
|
||||
@@ -3302,64 +3150,6 @@ JANET_CORE_FN(janet_cfun_rwlock_write_release,
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static JanetFile *get_file_for_stream(JanetStream *stream) {
|
||||
int32_t flags = 0;
|
||||
char fmt[4] = {0};
|
||||
int index = 0;
|
||||
if (stream->flags & JANET_STREAM_READABLE) {
|
||||
flags |= JANET_FILE_READ;
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
|
||||
fmt[index++] = 'r';
|
||||
}
|
||||
if (stream->flags & JANET_STREAM_WRITABLE) {
|
||||
flags |= JANET_FILE_WRITE;
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
|
||||
int currindex = index;
|
||||
fmt[index++] = (currindex == 0) ? 'w' : '+';
|
||||
}
|
||||
if (index == 0) return NULL;
|
||||
/* duplicate handle when converting stream to file */
|
||||
#ifdef JANET_WINDOWS
|
||||
int htype = 0;
|
||||
if (fmt[0] == 'r' && fmt[1] == '+') {
|
||||
htype = _O_RDWR;
|
||||
} else if (fmt[0] == 'r') {
|
||||
htype = _O_RDONLY;
|
||||
} else if (fmt[0] == 'w') {
|
||||
htype = _O_WRONLY;
|
||||
}
|
||||
int fd = _open_osfhandle((intptr_t) stream->handle, htype);
|
||||
if (fd < 0) return NULL;
|
||||
int fd_dup = _dup(fd);
|
||||
if (fd_dup < 0) return NULL;
|
||||
FILE *f = _fdopen(fd_dup, fmt);
|
||||
if (NULL == f) {
|
||||
_close(fd_dup);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
int fd_dup = dup(stream->handle);
|
||||
if (fd_dup < 0) return NULL;
|
||||
FILE *f = fdopen(fd_dup, fmt);
|
||||
if (NULL == f) {
|
||||
close(fd_dup);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
return janet_makejfile(f, flags);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_cfun_to_file,
|
||||
"(ev/to-file)",
|
||||
"Create core/file copy of the stream. This value can be used "
|
||||
"when blocking IO behavior is needed.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
JanetFile *iof = get_file_for_stream(stream);
|
||||
if (iof == NULL) janet_panic("cannot make file from stream");
|
||||
return janet_wrap_abstract(iof);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_cfun_ev_all_tasks,
|
||||
"(ev/all-tasks)",
|
||||
"Get an array of all active fibers that are being used by the scheduler.") {
|
||||
@@ -3404,7 +3194,6 @@ void janet_lib_ev(JanetTable *env) {
|
||||
JANET_CORE_REG("ev/acquire-wlock", janet_cfun_rwlock_write_lock),
|
||||
JANET_CORE_REG("ev/release-rlock", janet_cfun_rwlock_read_release),
|
||||
JANET_CORE_REG("ev/release-wlock", janet_cfun_rwlock_write_release),
|
||||
JANET_CORE_REG("ev/to-file", janet_cfun_to_file),
|
||||
JANET_CORE_REG("ev/all-tasks", janet_cfun_ev_all_tasks),
|
||||
JANET_REG_END
|
||||
};
|
||||
@@ -3414,8 +3203,6 @@ void janet_lib_ev(JanetTable *env) {
|
||||
janet_register_abstract_type(&janet_channel_type);
|
||||
janet_register_abstract_type(&janet_mutex_type);
|
||||
janet_register_abstract_type(&janet_rwlock_type);
|
||||
|
||||
janet_lib_filewatch(env);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
316
src/core/ffi.c
316
src/core/ffi.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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 */
|
||||
@@ -1496,99 +1294,6 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
|
||||
|
||||
#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
|
||||
|
||||
/* Allocate executable memory chunks in sizes of a page. Ideally we would keep
|
||||
* an allocator around so that multiple JIT allocations would point to the same
|
||||
* region but it isn't really worth it. */
|
||||
@@ -1668,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
|
||||
}
|
||||
}
|
||||
@@ -1741,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
|
||||
}
|
||||
}
|
||||
@@ -1864,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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,688 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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 {
|
||||
OVERLAPPED 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.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("nyi");
|
||||
}
|
||||
|
||||
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
|
||||
(void) watcher;
|
||||
(void) path;
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
static void janet_watcher_listen(JanetWatcher *watcher) {
|
||||
(void) watcher;
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
static void janet_watcher_unlisten(JanetWatcher *watcher) {
|
||||
(void) watcher;
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
#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 &opt 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 &opt 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 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
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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]);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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,11 +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
|
||||
#define ftell _ftelli64
|
||||
#define fseek _fseeki64
|
||||
#endif
|
||||
|
||||
const JanetAbstractType janet_file_type = {
|
||||
"core/file",
|
||||
cfun_io_gc,
|
||||
@@ -132,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);
|
||||
}
|
||||
|
||||
@@ -165,14 +159,6 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
}
|
||||
FILE *f = fopen((const char *)fname, (const char *)fmode);
|
||||
if (f != NULL) {
|
||||
#ifndef JANET_WINDOWS
|
||||
struct stat st;
|
||||
fstat(fileno(f), &st);
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
fclose(f);
|
||||
janet_panicf("cannot open directory: %s", fname);
|
||||
}
|
||||
#endif
|
||||
size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
|
||||
if (bufsize != BUFSIZ) {
|
||||
int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
|
||||
@@ -182,7 +168,7 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
}
|
||||
}
|
||||
return f ? janet_makefile(f, flags)
|
||||
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
|
||||
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil())
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
|
||||
@@ -303,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;
|
||||
@@ -351,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);
|
||||
@@ -365,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");
|
||||
@@ -379,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);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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 */
|
||||
@@ -576,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);
|
||||
@@ -600,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);
|
||||
@@ -1434,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++;
|
||||
@@ -1454,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);
|
||||
@@ -1496,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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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;
|
||||
@@ -349,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[] = {
|
||||
@@ -415,8 +395,6 @@ void janet_lib_math(JanetTable *env) {
|
||||
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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -152,7 +152,7 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
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 {
|
||||
@@ -319,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);
|
||||
@@ -554,10 +553,7 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
int err = WSAGetLastError();
|
||||
freeaddrinfo(ai);
|
||||
#else
|
||||
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);
|
||||
@@ -581,23 +577,17 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
net_sched_connect(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;
|
||||
}
|
||||
@@ -651,21 +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;
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix);
|
||||
int reuse = !(argc >= 4 && janet_truthy(argv[3]));
|
||||
|
||||
JSock sfd = JSOCKDEFAULT;
|
||||
#ifndef JANET_WINDOWS
|
||||
@@ -675,7 +663,7 @@ 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);
|
||||
const char *err = serverify_socket(sfd);
|
||||
if (NULL != err || bind(sfd, (struct sockaddr *)ai, sizeof(struct sockaddr_un))) {
|
||||
JSOCKCLOSE(sfd);
|
||||
janet_free(ai);
|
||||
@@ -698,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;
|
||||
@@ -840,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);
|
||||
@@ -855,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);
|
||||
@@ -875,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);
|
||||
@@ -889,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);
|
||||
@@ -903,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);
|
||||
@@ -922,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);
|
||||
@@ -1046,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();
|
||||
|
||||
262
src/core/os.c
262
src/core/os.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -27,10 +27,9 @@
|
||||
#include "gc.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef JANET_REDUCED_OS
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
@@ -39,7 +38,6 @@
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <locale.h>
|
||||
|
||||
#ifdef JANET_BSD
|
||||
#include <sys/sysctl.h>
|
||||
@@ -175,8 +173,6 @@ JANET_CORE_FN(os_arch,
|
||||
"* :riscv64\n\n"
|
||||
"* :sparc\n\n"
|
||||
"* :wasm\n\n"
|
||||
"* :s390\n\n"
|
||||
"* :s390x\n\n"
|
||||
"* :unknown\n") {
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
@@ -203,10 +199,6 @@ JANET_CORE_FN(os_arch,
|
||||
return janet_ckeywordv("ppc");
|
||||
#elif (defined(__ppc64__) || defined(_ARCH_PPC64) || defined(_M_PPC))
|
||||
return janet_ckeywordv("ppc64");
|
||||
#elif (defined(__s390x__))
|
||||
return janet_ckeywordv("s390x");
|
||||
#elif (defined(__s390__))
|
||||
return janet_ckeywordv("s390");
|
||||
#else
|
||||
return janet_ckeywordv("unknown");
|
||||
#endif
|
||||
@@ -252,7 +244,7 @@ JANET_CORE_FN(os_exit,
|
||||
}
|
||||
janet_deinit();
|
||||
if (argc >= 2 && janet_truthy(argv[1])) {
|
||||
_Exit(status);
|
||||
_exit(status);
|
||||
} else {
|
||||
exit(status);
|
||||
}
|
||||
@@ -541,12 +533,11 @@ static void janet_proc_wait_cb(JanetEVGenericMessage args) {
|
||||
proc->flags &= ~JANET_PROC_WAITING;
|
||||
janet_gcunroot(janet_wrap_abstract(proc));
|
||||
janet_gcunroot(janet_wrap_fiber(args.fiber));
|
||||
uint32_t sched_id = (uint32_t) args.argi;
|
||||
if (janet_fiber_can_resume(args.fiber) && args.fiber->sched_id == sched_id) {
|
||||
if ((status != 0) && (proc->flags & JANET_PROC_ERROR_NONZERO)) {
|
||||
JanetString s = janet_formatc("command failed with non-zero exit code %d", status);
|
||||
janet_cancel(args.fiber, janet_wrap_string(s));
|
||||
} else {
|
||||
if ((status != 0) && (proc->flags & JANET_PROC_ERROR_NONZERO)) {
|
||||
JanetString s = janet_formatc("command failed with non-zero exit code %d", status);
|
||||
janet_cancel(args.fiber, janet_wrap_string(s));
|
||||
} else {
|
||||
if (janet_fiber_can_resume(args.fiber)) {
|
||||
janet_schedule(args.fiber, janet_wrap_integer(status));
|
||||
}
|
||||
}
|
||||
@@ -604,7 +595,6 @@ os_proc_wait_impl(JanetProc *proc) {
|
||||
memset(&targs, 0, sizeof(targs));
|
||||
targs.argp = proc;
|
||||
targs.fiber = janet_root_fiber();
|
||||
targs.argi = (uint32_t) targs.fiber->sched_id;
|
||||
janet_gcroot(janet_wrap_abstract(proc));
|
||||
janet_gcroot(janet_wrap_fiber(targs.fiber));
|
||||
janet_ev_threaded_call(janet_proc_wait_subr, targs, janet_proc_wait_cb);
|
||||
@@ -631,15 +621,16 @@ os_proc_wait_impl(JanetProc *proc) {
|
||||
|
||||
JANET_CORE_FN(os_proc_wait,
|
||||
"(os/proc-wait proc)",
|
||||
"Suspend the current fiber until the subprocess `proc` completes. Once `proc` "
|
||||
"completes, return the exit code of `proc`. If called more than once on the same "
|
||||
"core/process value, will raise an error. When creating subprocesses using "
|
||||
"`os/spawn`, this function should be called on the returned value to avoid zombie "
|
||||
"processes.") {
|
||||
"Suspend the current fiber until the subprocess completes. Returns the subprocess return code. "
|
||||
"os/proc-wait cannot be called twice on the same process. If `ev/with-deadline` cancels `os/proc-wait` "
|
||||
"with an error or os/proc-wait is cancelled with any error caused by anything else, os/proc-wait still "
|
||||
"finishes in the background. Only after os/proc-wait finishes, a process is cleaned up by the operating "
|
||||
"system. Thus, a process becomes a zombie process if os/proc-wait is not called.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
|
||||
#ifdef JANET_EV
|
||||
os_proc_wait_impl(proc);
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
return os_proc_wait_impl(proc);
|
||||
#endif
|
||||
@@ -744,13 +735,12 @@ static int get_signal_kw(const Janet *argv, int32_t n) {
|
||||
|
||||
JANET_CORE_FN(os_proc_kill,
|
||||
"(os/proc-kill proc &opt wait signal)",
|
||||
"Kill the subprocess `proc` by sending SIGKILL to it on POSIX systems, or by closing "
|
||||
"the process handle on Windows. If `proc` has already completed, raise an error. If "
|
||||
"`wait` is truthy, will wait for `proc` to complete and return the exit code (this "
|
||||
"will raise an error if `proc` is being waited for). Otherwise, return `proc`. If "
|
||||
"`signal` is provided, send it instead of SIGKILL. Signal keywords are named after "
|
||||
"their C counterparts but in lowercase with the leading SIG stripped. `signal` is "
|
||||
"ignored on Windows.") {
|
||||
"Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process "
|
||||
"handle on windows. If os/proc-wait already finished for proc, os/proc-kill raises an error. After "
|
||||
"sending signal to proc, if `wait` is truthy, will wait for the process to finish and return the exit "
|
||||
"code by calling os/proc-wait. Otherwise, returns `proc`. If signal is specified, send it instead. "
|
||||
"Signal keywords are named after their C counterparts but in lowercase with the leading `SIG` stripped. "
|
||||
"Signals are ignored on windows.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
|
||||
if (proc->flags & JANET_PROC_WAITED) {
|
||||
@@ -771,13 +761,14 @@ JANET_CORE_FN(os_proc_kill,
|
||||
}
|
||||
int status = kill(proc->pid, signal == -1 ? SIGKILL : signal);
|
||||
if (status) {
|
||||
janet_panic(janet_strerror(errno));
|
||||
janet_panic(strerror(errno));
|
||||
}
|
||||
#endif
|
||||
/* After killing process we wait on it. */
|
||||
if (argc > 1 && janet_truthy(argv[1])) {
|
||||
#ifdef JANET_EV
|
||||
os_proc_wait_impl(proc);
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
return os_proc_wait_impl(proc);
|
||||
#endif
|
||||
@@ -788,9 +779,9 @@ JANET_CORE_FN(os_proc_kill,
|
||||
|
||||
JANET_CORE_FN(os_proc_close,
|
||||
"(os/proc-close proc)",
|
||||
"Close pipes created for subprocess `proc` by `os/spawn` if they have not been "
|
||||
"closed. Then, if `proc` is not being waited for, wait. If this function waits, when "
|
||||
"`proc` completes, return the exit code of `proc`. Otherwise, return nil.") {
|
||||
"Close pipes created by `os/spawn` if they have not been closed. Then, if os/proc-wait was not already "
|
||||
"called on proc, os/proc-wait is called on it, and it returns the exit code returned by os/proc-wait. "
|
||||
"Otherwise, returns nil.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
|
||||
#ifdef JANET_EV
|
||||
@@ -808,6 +799,7 @@ JANET_CORE_FN(os_proc_close,
|
||||
}
|
||||
#ifdef JANET_EV
|
||||
os_proc_wait_impl(proc);
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
return os_proc_wait_impl(proc);
|
||||
#endif
|
||||
@@ -1268,6 +1260,9 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
|
||||
/* exec mode */
|
||||
if (mode == JANET_EXECUTE_EXEC) {
|
||||
#ifdef JANET_WINDOWS
|
||||
janet_panic("not supported on windows");
|
||||
#else
|
||||
int status;
|
||||
if (!use_environ) {
|
||||
environ = envp;
|
||||
@@ -1279,7 +1274,8 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
status = execv(cargv[0], cargv);
|
||||
}
|
||||
} while (status == -1 && errno == EINTR);
|
||||
janet_panicf("%p: %s", cargv[0], janet_strerror(errno ? errno : ENOENT));
|
||||
janet_panicf("%p: %s", cargv[0], strerror(errno ? errno : ENOENT));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Use posix_spawn to spawn new process */
|
||||
@@ -1335,7 +1331,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
os_execute_cleanup(envp, child_argv);
|
||||
if (status) {
|
||||
/* correct for macos bug where errno is not set */
|
||||
janet_panicf("%p: %s", argv[0], janet_strerror(errno ? errno : ENOENT));
|
||||
janet_panicf("%p: %s", argv[0], strerror(errno ? errno : ENOENT));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1380,56 +1376,45 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
||||
|
||||
JANET_CORE_FN(os_execute,
|
||||
"(os/execute args &opt flags env)",
|
||||
"Execute a program on the system and return the exit code. `args` is an array/tuple "
|
||||
"of strings. The first string is the name of the program and the remainder are "
|
||||
"arguments passed to the program. `flags` is a keyword made from the following "
|
||||
"characters that modifies how the program executes:\n"
|
||||
"* :e - enables passing an environment to the program. Without 'e', the "
|
||||
"Execute a program on the system and pass it string arguments. `flags` "
|
||||
"is a keyword that modifies how the program will execute.\n"
|
||||
"* :e - enables passing an environment to the program. Without :e, the "
|
||||
"current environment is inherited.\n"
|
||||
"* :p - allows searching the current PATH for the program to execute. "
|
||||
"Without this flag, the first element of `args` must be an absolute path.\n"
|
||||
"* :x - raises error if exit code is non-zero.\n"
|
||||
"* :d - prevents the garbage collector terminating the program (if still running) "
|
||||
"and calling the equivalent of `os/proc-wait` (allows zombie processes).\n"
|
||||
"`env` is a table/struct mapping environment variables to values. It can also "
|
||||
"contain the keys :in, :out, and :err, which allow redirecting stdio in the "
|
||||
"subprocess. :in, :out, and :err should be core/file or core/stream values. "
|
||||
"If core/stream values are used, the caller is responsible for ensuring pipes do not "
|
||||
"cause the program to block and deadlock.") {
|
||||
"* :p - allows searching the current PATH for the binary to execute. "
|
||||
"Without this flag, binaries must use absolute paths.\n"
|
||||
"* :x - raise error if exit code is non-zero.\n"
|
||||
"* :d - Don't try and terminate the process on garbage collection (allow spawning zombies).\n"
|
||||
"`env` is a table or struct mapping environment variables to values. It can also "
|
||||
"contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. "
|
||||
":in, :out, and :err should be core/file values or core/stream values. core/file values and core/stream "
|
||||
"values passed to :in, :out, and :err should be closed manually because os/execute doesn't close them. "
|
||||
"Returns the exit code of the program.") {
|
||||
return os_execute_impl(argc, argv, JANET_EXECUTE_EXECUTE);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_spawn,
|
||||
"(os/spawn args &opt flags env)",
|
||||
"Execute a program on the system and return a core/process value representing the "
|
||||
"spawned subprocess. Takes the same arguments as `os/execute` but does not wait for "
|
||||
"the subprocess to complete. Unlike `os/execute`, the value `:pipe` can be used for "
|
||||
":in, :out and :err keys in `env`. If used, the returned core/process will have a "
|
||||
"writable stream in the :in field and readable streams in the :out and :err fields. "
|
||||
"On non-Windows systems, the subprocess PID will be in the :pid field. The caller is "
|
||||
"responsible for waiting on the process (e.g. by calling `os/proc-wait` on the "
|
||||
"returned core/process value) to avoid creating zombie process. After the subprocess "
|
||||
"completes, the exit value is in the :return-code field. If `flags` includes 'x', a "
|
||||
"non-zero exit code will cause a waiting fiber to raise an error. The use of "
|
||||
"`:pipe` may fail if there are too many active file descriptors. The caller is "
|
||||
"responsible for closing pipes created by `:pipe` (either individually or using "
|
||||
"`os/proc-close`). Similar to `os/execute`, the caller is responsible for ensuring "
|
||||
"pipes do not cause the program to block and deadlock.") {
|
||||
"Execute a program on the system and return a handle to the process. Otherwise, takes the "
|
||||
"same arguments as `os/execute`. Does not wait for the process. For each of the :in, :out, and :err keys "
|
||||
"of the `env` argument, one can also pass in the keyword `:pipe` to get streams for standard IO of the "
|
||||
"subprocess that can be read from and written to. The returned value `proc` has the fields :in, :out, "
|
||||
":err, and the additional field :pid on unix-like platforms. `(os/proc-wait proc)` must be called to "
|
||||
"rejoin the subprocess. After `(os/proc-wait proc)` finishes, proc gains a new field, :return-code. "
|
||||
"If :x flag is passed to os/spawn, non-zero exit code will cause os/proc-wait to raise an error. "
|
||||
"If pipe streams created with :pipe keyword are not closed in time, janet can run out of file "
|
||||
"descriptors. They can be closed individually, or `os/proc-close` can close all pipe streams on proc. "
|
||||
"If pipe streams aren't read before `os/proc-wait` finishes, then pipe buffers become full, and the "
|
||||
"process cannot finish because the process cannot print more on pipe buffers which are already full. "
|
||||
"If the process cannot finish, os/proc-wait cannot finish, either.") {
|
||||
return os_execute_impl(argc, argv, JANET_EXECUTE_SPAWN);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_posix_exec,
|
||||
"(os/posix-exec args &opt flags env)",
|
||||
"Use the execvpe or execve system calls to replace the current process with an interface similar to os/execute. "
|
||||
"However, instead of creating a subprocess, the current process is replaced. Is not supported on Windows, and "
|
||||
"Hoever, instead of creating a subprocess, the current process is replaced. Is not supported on windows, and "
|
||||
"does not allow redirection of stdio.") {
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not supported on Windows");
|
||||
#else
|
||||
return os_execute_impl(argc, argv, JANET_EXECUTE_EXEC);
|
||||
#endif
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_posix_fork,
|
||||
@@ -1440,14 +1425,14 @@ JANET_CORE_FN(os_posix_fork,
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
#ifdef JANET_WINDOWS
|
||||
janet_panic("not supported on Windows");
|
||||
janet_panic("not supported");
|
||||
#else
|
||||
pid_t result;
|
||||
do {
|
||||
result = fork();
|
||||
} while (result == -1 && errno == EINTR);
|
||||
if (result == -1) {
|
||||
janet_panic(janet_strerror(errno));
|
||||
janet_panic(strerror(errno));
|
||||
}
|
||||
if (result) {
|
||||
JanetProc *proc = janet_abstract(&ProcAT, sizeof(JanetProc));
|
||||
@@ -1596,8 +1581,8 @@ JANET_CORE_FN(os_clock,
|
||||
janet_sandbox_assert(JANET_SANDBOX_HRTIME);
|
||||
janet_arity(argc, 0, 2);
|
||||
|
||||
JanetKeyword sourcestr = janet_optkeyword(argv, argc, 0, NULL);
|
||||
if (sourcestr == NULL || janet_cstrcmp(sourcestr, "realtime") == 0) {
|
||||
JanetKeyword sourcestr = janet_optkeyword(argv, argc, 0, (const uint8_t *) "realtime");
|
||||
if (janet_cstrcmp(sourcestr, "realtime") == 0) {
|
||||
source = JANET_TIME_REALTIME;
|
||||
} else if (janet_cstrcmp(sourcestr, "monotonic") == 0) {
|
||||
source = JANET_TIME_MONOTONIC;
|
||||
@@ -1610,15 +1595,15 @@ JANET_CORE_FN(os_clock,
|
||||
struct timespec tv;
|
||||
if (janet_gettime(&tv, source)) janet_panic("could not get time");
|
||||
|
||||
JanetKeyword formatstr = janet_optkeyword(argv, argc, 1, NULL);
|
||||
if (formatstr == NULL || janet_cstrcmp(formatstr, "double") == 0) {
|
||||
double dtime = (double)(tv.tv_sec + (tv.tv_nsec / 1E9));
|
||||
JanetKeyword formatstr = janet_optkeyword(argv, argc, 1, (const uint8_t *) "double");
|
||||
if (janet_cstrcmp(formatstr, "double") == 0) {
|
||||
double dtime = tv.tv_sec + (tv.tv_nsec / 1E9);
|
||||
return janet_wrap_number(dtime);
|
||||
} else if (janet_cstrcmp(formatstr, "int") == 0) {
|
||||
return janet_wrap_number((double)(tv.tv_sec));
|
||||
return janet_wrap_number(tv.tv_sec);
|
||||
} else if (janet_cstrcmp(formatstr, "tuple") == 0) {
|
||||
Janet tup[2] = {janet_wrap_number((double)tv.tv_sec),
|
||||
janet_wrap_number((double)tv.tv_nsec)
|
||||
Janet tup[2] = {janet_wrap_integer(tv.tv_sec),
|
||||
janet_wrap_integer(tv.tv_nsec)
|
||||
};
|
||||
return janet_wrap_tuple(janet_tuple_n(tup, 2));
|
||||
} else {
|
||||
@@ -1659,7 +1644,7 @@ JANET_CORE_FN(os_isatty,
|
||||
return janet_wrap_boolean(_isatty(fd));
|
||||
#else
|
||||
int fd = fileno(f);
|
||||
if (fd == -1) janet_panic(janet_strerror(errno));
|
||||
if (fd == -1) janet_panic(strerror(errno));
|
||||
return janet_wrap_boolean(isatty(fd));
|
||||
#endif
|
||||
}
|
||||
@@ -1887,13 +1872,14 @@ JANET_CORE_FN(os_mktime,
|
||||
/* utc time */
|
||||
#ifdef JANET_NO_UTC_MKTIME
|
||||
janet_panic("os/mktime UTC not supported on this platform");
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
t = timegm(&t_info);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (t == (time_t) -1) {
|
||||
janet_panicf("%s", janet_strerror(errno));
|
||||
janet_panicf("%s", strerror(errno));
|
||||
}
|
||||
|
||||
return janet_wrap_number((double)t);
|
||||
@@ -1905,43 +1891,6 @@ JANET_CORE_FN(os_mktime,
|
||||
#define j_symlink symlink
|
||||
#endif
|
||||
|
||||
JANET_CORE_FN(os_setlocale,
|
||||
"(os/setlocale &opt locale category)",
|
||||
"Set the system locale, which affects how dates and numbers are formatted. "
|
||||
"Passing nil to locale will return the current locale. Category can be one of:\n\n"
|
||||
" * :all (default)\n"
|
||||
" * :collate\n"
|
||||
" * :ctype\n"
|
||||
" * :monetary\n"
|
||||
" * :numeric\n"
|
||||
" * :time\n\n"
|
||||
"Returns the new locale if set successfully, otherwise nil. Note that this will affect "
|
||||
"other functions such as `os/strftime` and even `printf`.") {
|
||||
janet_arity(argc, 0, 2);
|
||||
const char *locale_name = janet_optcstring(argv, argc, 0, NULL);
|
||||
int category_int = LC_ALL;
|
||||
if (argc > 1 && !janet_checktype(argv[1], JANET_NIL)) {
|
||||
if (janet_keyeq(argv[1], "all")) {
|
||||
category_int = LC_ALL;
|
||||
} else if (janet_keyeq(argv[1], "collate")) {
|
||||
category_int = LC_COLLATE;
|
||||
} else if (janet_keyeq(argv[1], "ctype")) {
|
||||
category_int = LC_CTYPE;
|
||||
} else if (janet_keyeq(argv[1], "monetary")) {
|
||||
category_int = LC_MONETARY;
|
||||
} else if (janet_keyeq(argv[1], "numeric")) {
|
||||
category_int = LC_NUMERIC;
|
||||
} else if (janet_keyeq(argv[1], "time")) {
|
||||
category_int = LC_TIME;
|
||||
} else {
|
||||
janet_panicf("expected one of :all, :collate, :ctype, :monetary, :numeric, or :time, got %v", argv[1]);
|
||||
}
|
||||
}
|
||||
const char *old = setlocale(category_int, locale_name);
|
||||
if (old == NULL) return janet_wrap_nil();
|
||||
return janet_cstringv(old);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_link,
|
||||
"(os/link oldpath newpath &opt symlink)",
|
||||
"Create a link at newpath that points to oldpath and returns nil. "
|
||||
@@ -1953,12 +1902,13 @@ JANET_CORE_FN(os_link,
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not supported on Windows");
|
||||
janet_panic("os/link not supported on Windows");
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
const char *oldpath = janet_getcstring(argv, 0);
|
||||
const char *newpath = janet_getcstring(argv, 1);
|
||||
int res = ((argc == 3 && janet_truthy(argv[2])) ? j_symlink : link)(oldpath, newpath);
|
||||
if (-1 == res) janet_panicf("%s: %s -> %s", janet_strerror(errno), oldpath, newpath);
|
||||
if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath);
|
||||
return janet_wrap_nil();
|
||||
#endif
|
||||
}
|
||||
@@ -1971,12 +1921,13 @@ JANET_CORE_FN(os_symlink,
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not supported on Windows");
|
||||
janet_panic("os/symlink not supported on Windows");
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
const char *oldpath = janet_getcstring(argv, 0);
|
||||
const char *newpath = janet_getcstring(argv, 1);
|
||||
int res = j_symlink(oldpath, newpath);
|
||||
if (-1 == res) janet_panicf("%s: %s -> %s", janet_strerror(errno), oldpath, newpath);
|
||||
if (-1 == res) janet_panicf("%s: %s -> %s", strerror(errno), oldpath, newpath);
|
||||
return janet_wrap_nil();
|
||||
#endif
|
||||
}
|
||||
@@ -1998,7 +1949,7 @@ JANET_CORE_FN(os_mkdir,
|
||||
#endif
|
||||
if (res == 0) return janet_wrap_true();
|
||||
if (errno == EEXIST) return janet_wrap_false();
|
||||
janet_panicf("%s: %s", janet_strerror(errno), path);
|
||||
janet_panicf("%s: %s", strerror(errno), path);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_rmdir,
|
||||
@@ -2012,7 +1963,7 @@ JANET_CORE_FN(os_rmdir,
|
||||
#else
|
||||
int res = rmdir(path);
|
||||
#endif
|
||||
if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path);
|
||||
if (-1 == res) janet_panicf("%s: %s", strerror(errno), path);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
@@ -2027,7 +1978,7 @@ JANET_CORE_FN(os_cd,
|
||||
#else
|
||||
int res = chdir(path);
|
||||
#endif
|
||||
if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path);
|
||||
if (-1 == res) janet_panicf("%s: %s", strerror(errno), path);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
@@ -2051,7 +2002,7 @@ JANET_CORE_FN(os_touch,
|
||||
bufp = NULL;
|
||||
}
|
||||
int res = utime(path, bufp);
|
||||
if (-1 == res) janet_panic(janet_strerror(errno));
|
||||
if (-1 == res) janet_panic(strerror(errno));
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
@@ -2061,7 +2012,7 @@ JANET_CORE_FN(os_remove,
|
||||
janet_fixarity(argc, 1);
|
||||
const char *path = janet_getcstring(argv, 0);
|
||||
int status = remove(path);
|
||||
if (-1 == status) janet_panicf("%s: %s", janet_strerror(errno), path);
|
||||
if (-1 == status) janet_panicf("%s: %s", strerror(errno), path);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
@@ -2073,13 +2024,14 @@ JANET_CORE_FN(os_readlink,
|
||||
#ifdef JANET_WINDOWS
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not supported on Windows");
|
||||
janet_panic("os/readlink not supported on Windows");
|
||||
return janet_wrap_nil();
|
||||
#else
|
||||
static char buffer[PATH_MAX];
|
||||
const char *path = janet_getcstring(argv, 0);
|
||||
ssize_t len = readlink(path, buffer, sizeof buffer);
|
||||
if (len < 0 || (size_t)len >= sizeof buffer)
|
||||
janet_panicf("%s: %s", janet_strerror(errno), path);
|
||||
janet_panicf("%s: %s", strerror(errno), path);
|
||||
return janet_stringv((const uint8_t *)buffer, len);
|
||||
#endif
|
||||
}
|
||||
@@ -2329,6 +2281,7 @@ static Janet os_stat_or_lstat(int do_lstat, int32_t argc, Janet *argv) {
|
||||
return sg->fn(&st);
|
||||
}
|
||||
janet_panicf("unexpected keyword %v", janet_wrap_keyword(key));
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2373,7 +2326,7 @@ JANET_CORE_FN(os_chmod,
|
||||
#else
|
||||
int res = chmod(path, os_getmode(argv, 1));
|
||||
#endif
|
||||
if (-1 == res) janet_panicf("%s: %s", janet_strerror(errno), path);
|
||||
if (-1 == res) janet_panicf("%s: %s", strerror(errno), path);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
@@ -2409,7 +2362,7 @@ JANET_CORE_FN(os_dir,
|
||||
janet_panicf("path too long: %s", dir);
|
||||
sprintf(pattern, "%s/*", dir);
|
||||
intptr_t res = _findfirst(pattern, &afile);
|
||||
if (-1 == res) janet_panicv(janet_cstringv(janet_strerror(errno)));
|
||||
if (-1 == res) janet_panicv(janet_cstringv(strerror(errno)));
|
||||
do {
|
||||
if (strcmp(".", afile.name) && strcmp("..", afile.name)) {
|
||||
janet_array_push(paths, janet_cstringv(afile.name));
|
||||
@@ -2420,18 +2373,8 @@ JANET_CORE_FN(os_dir,
|
||||
/* Read directory items with opendir / readdir / closedir */
|
||||
struct dirent *dp;
|
||||
DIR *dfd = opendir(dir);
|
||||
if (dfd == NULL) janet_panicf("cannot open directory %s: %s", dir, janet_strerror(errno));
|
||||
for (;;) {
|
||||
errno = 0;
|
||||
dp = readdir(dfd);
|
||||
if (dp == NULL) {
|
||||
if (errno) {
|
||||
int olderr = errno;
|
||||
closedir(dfd);
|
||||
janet_panicf("failed to read directory %s: %s", dir, janet_strerror(olderr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (dfd == NULL) janet_panicf("cannot open directory %s", dir);
|
||||
while ((dp = readdir(dfd)) != NULL) {
|
||||
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
|
||||
continue;
|
||||
}
|
||||
@@ -2451,7 +2394,7 @@ JANET_CORE_FN(os_rename,
|
||||
const char *dest = janet_getcstring(argv, 1);
|
||||
int status = rename(src, dest);
|
||||
if (status) {
|
||||
janet_panic(janet_strerror(errno));
|
||||
janet_panic(strerror(errno));
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
@@ -2471,7 +2414,7 @@ JANET_CORE_FN(os_realpath,
|
||||
#else
|
||||
char *dest = realpath(src, NULL);
|
||||
#endif
|
||||
if (NULL == dest) janet_panicf("%s: %s", janet_strerror(errno), src);
|
||||
if (NULL == dest) janet_panicf("%s: %s", strerror(errno), src);
|
||||
Janet ret = janet_cstringv(dest);
|
||||
janet_free(dest);
|
||||
return ret;
|
||||
@@ -2677,7 +2620,7 @@ JANET_CORE_FN(os_open,
|
||||
} else if (write_flag && !read_flag) {
|
||||
open_flags |= O_WRONLY;
|
||||
} else {
|
||||
open_flags |= O_RDWR;
|
||||
open_flags = O_RDWR;
|
||||
}
|
||||
|
||||
do {
|
||||
@@ -2689,24 +2632,16 @@ JANET_CORE_FN(os_open,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(os_pipe,
|
||||
"(os/pipe &opt flags)",
|
||||
"(os/pipe)",
|
||||
"Create a readable stream and a writable stream that are connected. Returns a two-element "
|
||||
"tuple where the first element is a readable stream and the second element is the writable "
|
||||
"stream. `flags` is a keyword set of flags to disable non-blocking settings on the ends of the pipe. "
|
||||
"This may be desired if passing the pipe to a subprocess with `os/spawn`.\n\n"
|
||||
"* :W - sets the writable end of the pipe to a blocking stream.\n"
|
||||
"* :R - sets the readable end of the pipe to a blocking stream.\n\n"
|
||||
"By default, both ends of the pipe are non-blocking for use with the `ev` module.") {
|
||||
"stream.") {
|
||||
(void) argv;
|
||||
janet_arity(argc, 0, 1);
|
||||
janet_fixarity(argc, 0);
|
||||
JanetHandle fds[2];
|
||||
int flags = 0;
|
||||
if (argc > 0 && !janet_checktype(argv[0], JANET_NIL)) {
|
||||
flags = (int) janet_getflags(argv, 0, "WR");
|
||||
}
|
||||
if (janet_make_pipe(fds, flags)) janet_panicv(janet_ev_lasterr());
|
||||
JanetStream *reader = janet_stream(fds[0], (flags & 2) ? 0 : JANET_STREAM_READABLE, NULL);
|
||||
JanetStream *writer = janet_stream(fds[1], (flags & 1) ? 0 : JANET_STREAM_WRITABLE, NULL);
|
||||
if (janet_make_pipe(fds, 0)) janet_panicv(janet_ev_lasterr());
|
||||
JanetStream *reader = janet_stream(fds[0], JANET_STREAM_READABLE, NULL);
|
||||
JanetStream *writer = janet_stream(fds[1], JANET_STREAM_WRITABLE, NULL);
|
||||
Janet tup[2] = {janet_wrap_abstract(reader), janet_wrap_abstract(writer)};
|
||||
return janet_wrap_tuple(janet_tuple_n(tup, 2));
|
||||
}
|
||||
@@ -2753,7 +2688,6 @@ void janet_lib_os(JanetTable *env) {
|
||||
JANET_CORE_REG("os/strftime", os_strftime),
|
||||
JANET_CORE_REG("os/sleep", os_sleep),
|
||||
JANET_CORE_REG("os/isatty", os_isatty),
|
||||
JANET_CORE_REG("os/setlocale", os_setlocale),
|
||||
|
||||
/* env functions */
|
||||
JANET_CORE_REG("os/environ", os_environ),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -231,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;
|
||||
@@ -363,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') {
|
||||
@@ -373,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--;
|
||||
}
|
||||
}
|
||||
@@ -468,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)) {
|
||||
|
||||
155
src/core/peg.c
155
src/core/peg.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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 */
|
||||
@@ -342,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;
|
||||
}
|
||||
@@ -465,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;
|
||||
@@ -496,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];
|
||||
@@ -544,80 +510,41 @@ 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;
|
||||
}
|
||||
|
||||
@@ -740,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 {
|
||||
@@ -1134,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) {
|
||||
@@ -1160,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);
|
||||
@@ -1266,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);
|
||||
@@ -1355,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},
|
||||
@@ -1370,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},
|
||||
@@ -1464,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));
|
||||
@@ -1710,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;
|
||||
@@ -1721,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:
|
||||
@@ -1731,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;
|
||||
}
|
||||
@@ -1834,7 +1725,7 @@ 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. Will also use `(dyn :peg-grammar)` to supplement "
|
||||
"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]);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -379,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:
|
||||
@@ -832,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++);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
/* 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();
|
||||
@@ -37,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);
|
||||
@@ -60,8 +58,8 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
}
|
||||
} else {
|
||||
ret = janet_wrap_string(cres.error);
|
||||
int32_t line = (int32_t) parser->line;
|
||||
int32_t col = (int32_t) parser->column;
|
||||
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;
|
||||
@@ -83,16 +81,16 @@ 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: {
|
||||
const char *e = janet_parser_error(parser);
|
||||
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;
|
||||
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;
|
||||
@@ -100,9 +98,9 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
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;
|
||||
}
|
||||
@@ -110,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 */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -925,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;
|
||||
@@ -944,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)) {
|
||||
@@ -1108,7 +1103,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
|
||||
if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -100,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. */
|
||||
@@ -150,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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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);
|
||||
}
|
||||
@@ -319,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)",
|
||||
@@ -372,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,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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;
|
||||
@@ -829,20 +826,6 @@ 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;
|
||||
@@ -970,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)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -81,8 +80,6 @@ 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,
|
||||
@@ -142,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;
|
||||
@@ -190,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
|
||||
@@ -200,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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -318,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);
|
||||
}
|
||||
@@ -798,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++;
|
||||
}
|
||||
@@ -815,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++;
|
||||
}
|
||||
@@ -833,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();
|
||||
|
||||
@@ -1268,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. */
|
||||
@@ -1373,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;
|
||||
@@ -1387,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);
|
||||
}
|
||||
|
||||
@@ -1442,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) {
|
||||
@@ -1454,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) {
|
||||
@@ -1628,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));
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -46,7 +46,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Detect OS and endianness.
|
||||
* Detect OS and endianess.
|
||||
* From webkit source. There is likely some extreneous
|
||||
* detection for unsupported platforms
|
||||
*/
|
||||
@@ -112,8 +112,7 @@ extern "C" {
|
||||
|| defined(__s390x__) /* S390 64-bit (BE) */ \
|
||||
|| (defined(__ppc64__) || defined(__PPC64__)) \
|
||||
|| defined(__aarch64__) /* ARM 64-bit */ \
|
||||
|| (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ \
|
||||
|| defined(__loongarch64) /* LoongArch64 64-bit */
|
||||
|| (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */
|
||||
#define JANET_64 1
|
||||
#else
|
||||
#define JANET_32 1
|
||||
@@ -149,6 +148,12 @@ extern "C" {
|
||||
#define JANET_INTMIN_DOUBLE (-9007199254740992.0)
|
||||
#define JANET_INTMAX_INT64 9007199254740992
|
||||
#define JANET_INTMIN_INT64 (-9007199254740992)
|
||||
#ifdef JANET_64
|
||||
#define JANET_SIZEMAX JANET_INTMAX_INT64
|
||||
#else
|
||||
/* Avoid loop bounds issues */
|
||||
#define JANET_SIZEMAX (SIZE_MAX - 1)
|
||||
#endif
|
||||
|
||||
/* Check emscripten */
|
||||
#ifdef __EMSCRIPTEN__
|
||||
@@ -210,11 +215,6 @@ extern "C" {
|
||||
#define JANET_EV
|
||||
#endif
|
||||
|
||||
/* Enable or disable the filewatch/ module */
|
||||
#if !defined(JANET_NO_FILEWATCH)
|
||||
#define JANET_FILEWATCH
|
||||
#endif
|
||||
|
||||
/* Enable or disable networking */
|
||||
#if defined(JANET_EV) && !defined(JANET_NO_NET) && !defined(__EMSCRIPTEN__)
|
||||
#define JANET_NET
|
||||
@@ -267,7 +267,7 @@ extern "C" {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Tell compiler some functions don't return */
|
||||
/* Tell complier some functions don't return */
|
||||
#ifndef JANET_NO_RETURN
|
||||
#ifdef JANET_WINDOWS
|
||||
#define JANET_NO_RETURN __declspec(noreturn)
|
||||
@@ -277,7 +277,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Prevent some recursive functions from recursing too deeply
|
||||
* and crashing (the parser). Instead, error out. */
|
||||
* ands crashing (the parser). Instead, error out. */
|
||||
#define JANET_RECURSION_GUARD 1024
|
||||
|
||||
/* Maximum depth to follow table prototypes before giving up and returning nil. */
|
||||
@@ -359,7 +359,6 @@ typedef struct {
|
||||
#ifdef JANET_EV
|
||||
typedef struct JanetOSMutex JanetOSMutex;
|
||||
typedef struct JanetOSRWLock JanetOSRWLock;
|
||||
typedef struct JanetChannel JanetChannel;
|
||||
#endif
|
||||
|
||||
/***** END SECTION CONFIG *****/
|
||||
@@ -633,9 +632,7 @@ typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event);
|
||||
* call when ever an event is sent from the event loop. state is an optional (can be NULL)
|
||||
* pointer to data allocated with janet_malloc. This pointer will be passed to callback as
|
||||
* fiber->ev_state. It will also be freed for you by the runtime when the event loop determines
|
||||
* it can no longer be referenced. On windows, the contents of state MUST contained an OVERLAPPED struct at the 0 offset. */
|
||||
|
||||
JANET_API void janet_async_start_fiber(JanetFiber *fiber, JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state);
|
||||
* it can no longer be referenced. On windows, the contents of state MUST contained an OVERLAPPED struct. */
|
||||
JANET_API JANET_NO_RETURN void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state);
|
||||
|
||||
/* Do not send any more events to the given callback. Call this after scheduling fiber to be resume
|
||||
@@ -645,12 +642,6 @@ JANET_API void janet_async_end(JanetFiber *fiber);
|
||||
/* Needed for windows to mark a fiber as waiting for an IOCP completion event. Noop on other platforms. */
|
||||
JANET_API void janet_async_in_flight(JanetFiber *fiber);
|
||||
|
||||
/* On some platforms, it is important to be able to control if a stream is edge-trigger or level triggered.
|
||||
* For example, a server that is accepting connections might want to be level triggered or edge-triggered
|
||||
* depending on expected service. */
|
||||
JANET_API void janet_stream_edge_triggered(JanetStream *stream);
|
||||
JANET_API void janet_stream_level_triggered(JanetStream *stream);
|
||||
|
||||
#endif
|
||||
|
||||
/* Janet uses atomic integers in several places for synchronization between threads and
|
||||
@@ -905,16 +896,12 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
|
||||
/* End of tagged union implementation */
|
||||
#endif
|
||||
|
||||
JANET_API int janet_checkint16(Janet x);
|
||||
JANET_API int janet_checkuint16(Janet x);
|
||||
JANET_API int janet_checkint(Janet x);
|
||||
JANET_API int janet_checkuint(Janet x);
|
||||
JANET_API int janet_checkint64(Janet x);
|
||||
JANET_API int janet_checkuint64(Janet x);
|
||||
JANET_API int janet_checksize(Janet x);
|
||||
JANET_API JanetAbstract janet_checkabstract(Janet x, const JanetAbstractType *at);
|
||||
#define janet_checkint16range(x) ((x) >= INT16_MIN && (x) <= INT16_MAX && (x) == (int16_t)(x))
|
||||
#define janet_checkuint16range(x) ((x) >= 0 && (x) <= UINT16_MAX && (x) == (uint16_t)(x))
|
||||
#define janet_checkintrange(x) ((x) >= INT32_MIN && (x) <= INT32_MAX && (x) == (int32_t)(x))
|
||||
#define janet_checkuintrange(x) ((x) >= 0 && (x) <= UINT32_MAX && (x) == (uint32_t)(x))
|
||||
#define janet_checkint64range(x) ((x) >= JANET_INTMIN_DOUBLE && (x) <= JANET_INTMAX_DOUBLE && (x) == (int64_t)(x))
|
||||
@@ -984,25 +971,25 @@ struct JanetStackFrame {
|
||||
/* A dynamic array type. */
|
||||
struct JanetArray {
|
||||
JanetGCObject gc;
|
||||
int32_t count;
|
||||
int32_t capacity;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
Janet *data;
|
||||
};
|
||||
|
||||
/* A byte buffer type. Used as a mutable string or string builder. */
|
||||
struct JanetBuffer {
|
||||
JanetGCObject gc;
|
||||
int32_t count;
|
||||
int32_t capacity;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
/* A mutable associative data type. Backed by a hashtable. */
|
||||
struct JanetTable {
|
||||
JanetGCObject gc;
|
||||
int32_t count;
|
||||
int32_t capacity;
|
||||
int32_t deleted;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
size_t deleted;
|
||||
JanetKV *data;
|
||||
JanetTable *proto;
|
||||
};
|
||||
@@ -1016,7 +1003,7 @@ struct JanetKV {
|
||||
/* Prefix for a tuple */
|
||||
struct JanetTupleHead {
|
||||
JanetGCObject gc;
|
||||
int32_t length;
|
||||
size_t length;
|
||||
int32_t hash;
|
||||
int32_t sm_line;
|
||||
int32_t sm_column;
|
||||
@@ -1026,9 +1013,9 @@ struct JanetTupleHead {
|
||||
/* Prefix for a struct */
|
||||
struct JanetStructHead {
|
||||
JanetGCObject gc;
|
||||
int32_t length;
|
||||
size_t length;
|
||||
size_t capacity;
|
||||
int32_t hash;
|
||||
int32_t capacity;
|
||||
const JanetKV *proto;
|
||||
const JanetKV data[];
|
||||
};
|
||||
@@ -1036,7 +1023,7 @@ struct JanetStructHead {
|
||||
/* Prefix for a string */
|
||||
struct JanetStringHead {
|
||||
JanetGCObject gc;
|
||||
int32_t length;
|
||||
size_t length;
|
||||
int32_t hash;
|
||||
const uint8_t data[];
|
||||
};
|
||||
@@ -1220,18 +1207,18 @@ struct JanetMethod {
|
||||
|
||||
struct JanetView {
|
||||
const Janet *items;
|
||||
int32_t len;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct JanetByteView {
|
||||
const uint8_t *bytes;
|
||||
int32_t len;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct JanetDictView {
|
||||
const JanetKV *kvs;
|
||||
int32_t len;
|
||||
int32_t cap;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
struct JanetRange {
|
||||
@@ -1261,7 +1248,6 @@ typedef struct {
|
||||
/* new state */
|
||||
jmp_buf buf;
|
||||
Janet payload;
|
||||
int coerce_error;
|
||||
} JanetTryState;
|
||||
|
||||
/***** END SECTION TYPES *****/
|
||||
@@ -1422,7 +1408,6 @@ JANET_API void janet_loop1_interrupt(JanetVM *vm);
|
||||
|
||||
/* Wrapper around streams */
|
||||
JANET_API JanetStream *janet_stream(JanetHandle handle, uint32_t flags, const JanetMethod *methods);
|
||||
JANET_API JanetStream *janet_stream_ext(JanetHandle handle, uint32_t flags, const JanetMethod *methods, size_t size); /* Allow for type punning streams */
|
||||
JANET_API void janet_stream_close(JanetStream *stream);
|
||||
JANET_API Janet janet_cfun_stream_close(int32_t argc, Janet *argv);
|
||||
JANET_API Janet janet_cfun_stream_read(int32_t argc, Janet *argv);
|
||||
@@ -1443,7 +1428,6 @@ JANET_NO_RETURN JANET_API void janet_sleep_await(double sec);
|
||||
/* For use inside listeners - adds a timeout to the current fiber, such that
|
||||
* it will be resumed after sec seconds if no other event schedules the current fiber. */
|
||||
JANET_API void janet_addtimeout(double sec);
|
||||
JANET_API void janet_addtimeout_nil(double sec);
|
||||
JANET_API void janet_ev_inc_refcount(void);
|
||||
JANET_API void janet_ev_dec_refcount(void);
|
||||
|
||||
@@ -1454,14 +1438,6 @@ JANET_API void *janet_abstract_threaded(const JanetAbstractType *atype, size_t s
|
||||
JANET_API int32_t janet_abstract_incref(void *abst);
|
||||
JANET_API int32_t janet_abstract_decref(void *abst);
|
||||
|
||||
/* Expose channel utilities */
|
||||
JanetChannel *janet_channel_make(uint32_t limit);
|
||||
JanetChannel *janet_channel_make_threaded(uint32_t limit);
|
||||
JanetChannel *janet_getchannel(const Janet *argv, int32_t n);
|
||||
JanetChannel *janet_optchannel(const Janet *argv, int32_t argc, int32_t n, JanetChannel *dflt);
|
||||
JANET_API int janet_channel_give(JanetChannel *channel, Janet x);
|
||||
JANET_API int janet_channel_take(JanetChannel *channel, Janet *out);
|
||||
|
||||
/* Expose some OS sync primitives */
|
||||
JANET_API size_t janet_os_mutex_size(void);
|
||||
JANET_API size_t janet_os_rwlock_size(void);
|
||||
@@ -1606,20 +1582,17 @@ JANET_API JanetTable *janet_core_env(JanetTable *replacements);
|
||||
JANET_API JanetTable *janet_core_lookup_table(JanetTable *replacements);
|
||||
|
||||
/* Execute strings */
|
||||
JANET_API int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out);
|
||||
JANET_API int janet_dobytes(JanetTable *env, const uint8_t *bytes, size_t len, const char *sourcePath, Janet *out);
|
||||
JANET_API int janet_dostring(JanetTable *env, const char *str, const char *sourcePath, Janet *out);
|
||||
|
||||
/* Run the entrypoint of a wrapped program */
|
||||
JANET_API int janet_loop_fiber(JanetFiber *fiber);
|
||||
|
||||
/* Number scanning */
|
||||
JANET_API int janet_scan_number(const uint8_t *str, int32_t len, double *out);
|
||||
JANET_API int janet_scan_number_base(const uint8_t *str, int32_t len, int32_t base, double *out);
|
||||
JANET_API int janet_scan_int64(const uint8_t *str, int32_t len, int64_t *out);
|
||||
JANET_API int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out);
|
||||
#ifdef JANET_INT_TYPES
|
||||
JANET_API int janet_scan_numeric(const uint8_t *str, int32_t len, Janet *out);
|
||||
#endif
|
||||
JANET_API int janet_scan_number(const uint8_t *str, size_t len, double *out);
|
||||
JANET_API int janet_scan_number_base(const uint8_t *str, size_t len, int32_t base, double *out);
|
||||
JANET_API int janet_scan_int64(const uint8_t *str, size_t len, int64_t *out);
|
||||
JANET_API int janet_scan_uint64(const uint8_t *str, size_t len, uint64_t *out);
|
||||
|
||||
/* Debugging */
|
||||
JANET_API void janet_debug_break(JanetFuncDef *def, int32_t pc);
|
||||
@@ -1632,30 +1605,30 @@ JANET_API void janet_debug_find(
|
||||
extern JANET_API const JanetAbstractType janet_rng_type;
|
||||
JANET_API JanetRNG *janet_default_rng(void);
|
||||
JANET_API void janet_rng_seed(JanetRNG *rng, uint32_t seed);
|
||||
JANET_API void janet_rng_longseed(JanetRNG *rng, const uint8_t *bytes, int32_t len);
|
||||
JANET_API void janet_rng_longseed(JanetRNG *rng, const uint8_t *bytes, size_t len);
|
||||
JANET_API uint32_t janet_rng_u32(JanetRNG *rng);
|
||||
JANET_API double janet_rng_double(JanetRNG *rng);
|
||||
|
||||
/* Array functions */
|
||||
JANET_API JanetArray *janet_array(int32_t capacity);
|
||||
JANET_API JanetArray *janet_array_weak(int32_t capacity);
|
||||
JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n);
|
||||
JANET_API void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth);
|
||||
JANET_API void janet_array_setcount(JanetArray *array, int32_t count);
|
||||
JANET_API JanetArray *janet_array(size_t capacity);
|
||||
JANET_API JanetArray *janet_array_weak(size_t capacity);
|
||||
JANET_API JanetArray *janet_array_n(const Janet *elements, size_t n);
|
||||
JANET_API void janet_array_ensure(JanetArray *array, size_t capacity, int32_t growth);
|
||||
JANET_API void janet_array_setcount(JanetArray *array, size_t count);
|
||||
JANET_API void janet_array_push(JanetArray *array, Janet x);
|
||||
JANET_API Janet janet_array_pop(JanetArray *array);
|
||||
JANET_API Janet janet_array_peek(JanetArray *array);
|
||||
|
||||
/* Buffer functions */
|
||||
#define JANET_BUFFER_FLAG_NO_REALLOC 0x10000
|
||||
JANET_API JanetBuffer *janet_buffer(int32_t capacity);
|
||||
JANET_API JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity);
|
||||
JANET_API JanetBuffer *janet_pointer_buffer_unsafe(void *memory, int32_t capacity, int32_t count);
|
||||
JANET_API JanetBuffer *janet_buffer(size_t capacity);
|
||||
JANET_API JanetBuffer *janet_buffer_init(JanetBuffer *buffer, size_t capacity);
|
||||
JANET_API JanetBuffer *janet_pointer_buffer_unsafe(void *memory, size_t capacity, size_t count);
|
||||
JANET_API void janet_buffer_deinit(JanetBuffer *buffer);
|
||||
JANET_API void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth);
|
||||
JANET_API void janet_buffer_setcount(JanetBuffer *buffer, int32_t count);
|
||||
JANET_API void janet_buffer_extra(JanetBuffer *buffer, int32_t n);
|
||||
JANET_API void janet_buffer_push_bytes(JanetBuffer *buffer, const uint8_t *string, int32_t len);
|
||||
JANET_API void janet_buffer_ensure(JanetBuffer *buffer, size_t capacity, size_t growth);
|
||||
JANET_API void janet_buffer_setcount(JanetBuffer *buffer, size_t count);
|
||||
JANET_API void janet_buffer_extra(JanetBuffer *buffer, size_t n);
|
||||
JANET_API void janet_buffer_push_bytes(JanetBuffer *buffer, const uint8_t *string, size_t len);
|
||||
JANET_API void janet_buffer_push_string(JanetBuffer *buffer, JanetString string);
|
||||
JANET_API void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring);
|
||||
JANET_API void janet_buffer_push_u8(JanetBuffer *buffer, uint8_t x);
|
||||
@@ -1674,9 +1647,9 @@ JANET_API void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x);
|
||||
#define janet_tuple_sm_line(t) (janet_tuple_head(t)->sm_line)
|
||||
#define janet_tuple_sm_column(t) (janet_tuple_head(t)->sm_column)
|
||||
#define janet_tuple_flag(t) (janet_tuple_head(t)->gc.flags)
|
||||
JANET_API Janet *janet_tuple_begin(int32_t length);
|
||||
JANET_API Janet *janet_tuple_begin(size_t length);
|
||||
JANET_API JanetTuple janet_tuple_end(Janet *tuple);
|
||||
JANET_API JanetTuple janet_tuple_n(const Janet *values, int32_t n);
|
||||
JANET_API JanetTuple janet_tuple_n(const Janet *values, size_t n);
|
||||
|
||||
/* String/Symbol functions */
|
||||
#define janet_string_head(s) ((JanetStringHead *)((char *)s - offsetof(JanetStringHead, data)))
|
||||
@@ -1719,7 +1692,7 @@ JANET_API JanetSymbol janet_symbol_gen(void);
|
||||
#define janet_struct_capacity(t) (janet_struct_head(t)->capacity)
|
||||
#define janet_struct_hash(t) (janet_struct_head(t)->hash)
|
||||
#define janet_struct_proto(t) (janet_struct_head(t)->proto)
|
||||
JANET_API JanetKV *janet_struct_begin(int32_t count);
|
||||
JANET_API JanetKV *janet_struct_begin(size_t count);
|
||||
JANET_API void janet_struct_put(JanetKV *st, Janet key, Janet value);
|
||||
JANET_API JanetStruct janet_struct_end(JanetKV *st);
|
||||
JANET_API Janet janet_struct_get(JanetStruct st, Janet key);
|
||||
@@ -1729,9 +1702,9 @@ JANET_API JanetTable *janet_struct_to_table(JanetStruct st);
|
||||
JANET_API const JanetKV *janet_struct_find(JanetStruct st, Janet key);
|
||||
|
||||
/* Table functions */
|
||||
JANET_API JanetTable *janet_table(int32_t capacity);
|
||||
JANET_API JanetTable *janet_table_init(JanetTable *table, int32_t capacity);
|
||||
JANET_API JanetTable *janet_table_init_raw(JanetTable *table, int32_t capacity);
|
||||
JANET_API JanetTable *janet_table(size_t capacity);
|
||||
JANET_API JanetTable *janet_table_init(JanetTable *table, size_t capacity);
|
||||
JANET_API JanetTable *janet_table_init_raw(JanetTable *table, size_t capacity);
|
||||
JANET_API void janet_table_deinit(JanetTable *table);
|
||||
JANET_API Janet janet_table_get(JanetTable *t, Janet key);
|
||||
JANET_API Janet janet_table_get_ex(JanetTable *t, Janet key, JanetTable **which);
|
||||
@@ -1744,12 +1717,9 @@ JANET_API void janet_table_merge_struct(JanetTable *table, JanetStruct other);
|
||||
JANET_API JanetKV *janet_table_find(JanetTable *t, Janet key);
|
||||
JANET_API JanetTable *janet_table_clone(JanetTable *table);
|
||||
JANET_API void janet_table_clear(JanetTable *table);
|
||||
JANET_API JanetTable *janet_table_weakk(int32_t capacity);
|
||||
JANET_API JanetTable *janet_table_weakv(int32_t capacity);
|
||||
JANET_API JanetTable *janet_table_weakkv(int32_t capacity);
|
||||
|
||||
/* Fiber */
|
||||
JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv);
|
||||
JANET_API JanetFiber *janet_fiber(JanetFunction *callee, size_t capacity, int32_t argc, const Janet *argv);
|
||||
JANET_API JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t argc, const Janet *argv);
|
||||
JANET_API JanetFiberStatus janet_fiber_status(JanetFiber *fiber);
|
||||
JANET_API int janet_fiber_can_resume(JanetFiber *fiber);
|
||||
@@ -1757,11 +1727,11 @@ JANET_API JanetFiber *janet_current_fiber(void);
|
||||
JANET_API JanetFiber *janet_root_fiber(void);
|
||||
|
||||
/* Treat similar types through uniform interfaces for iteration */
|
||||
JANET_API int janet_indexed_view(Janet seq, const Janet **data, int32_t *len);
|
||||
JANET_API int janet_bytes_view(Janet str, const uint8_t **data, int32_t *len);
|
||||
JANET_API int janet_dictionary_view(Janet tab, const JanetKV **data, int32_t *len, int32_t *cap);
|
||||
JANET_API Janet janet_dictionary_get(const JanetKV *data, int32_t cap, Janet key);
|
||||
JANET_API const JanetKV *janet_dictionary_next(const JanetKV *kvs, int32_t cap, const JanetKV *kv);
|
||||
JANET_API int janet_indexed_view(Janet seq, const Janet **data, size_t *len);
|
||||
JANET_API int janet_bytes_view(Janet str, const uint8_t **data, size_t *len);
|
||||
JANET_API int janet_dictionary_view(Janet tab, const JanetKV **data, size_t *len, size_t *cap);
|
||||
JANET_API Janet janet_dictionary_get(const JanetKV *data, size_t cap, Janet key);
|
||||
JANET_API const JanetKV *janet_dictionary_next(const JanetKV *kvs, size_t cap, const JanetKV *kv);
|
||||
|
||||
/* Abstract */
|
||||
#define janet_abstract_head(u) ((JanetAbstractHead *)((char *)u - offsetof(JanetAbstractHead, data)))
|
||||
@@ -1810,7 +1780,6 @@ JANET_API void janet_gcpressure(size_t s);
|
||||
/* Functions */
|
||||
JANET_API JanetFuncDef *janet_funcdef_alloc(void);
|
||||
JANET_API JanetFunction *janet_thunk(JanetFuncDef *def);
|
||||
JANET_API JanetFunction *janet_thunk_delay(Janet x);
|
||||
JANET_API int janet_verify(JanetFuncDef *def);
|
||||
|
||||
/* Pretty printing */
|
||||
@@ -1838,17 +1807,17 @@ JANET_API int janet_cstrcmp(JanetString str, const char *other);
|
||||
JANET_API Janet janet_in(Janet ds, Janet key);
|
||||
JANET_API Janet janet_get(Janet ds, Janet key);
|
||||
JANET_API Janet janet_next(Janet ds, Janet key);
|
||||
JANET_API Janet janet_getindex(Janet ds, int32_t index);
|
||||
JANET_API int32_t janet_length(Janet x);
|
||||
JANET_API Janet janet_getindex(Janet ds, size_t index);
|
||||
JANET_API size_t janet_length(Janet x);
|
||||
JANET_API Janet janet_lengthv(Janet x);
|
||||
JANET_API void janet_put(Janet ds, Janet key, Janet value);
|
||||
JANET_API void janet_putindex(Janet ds, int32_t index, Janet value);
|
||||
JANET_API void janet_putindex(Janet ds, size_t index, Janet value);
|
||||
#define janet_flag_at(F, I) ((F) & ((1ULL) << (I)))
|
||||
JANET_API Janet janet_wrap_number_safe(double x);
|
||||
JANET_API int janet_keyeq(Janet x, const char *cstring);
|
||||
JANET_API int janet_streq(Janet x, const char *cstring);
|
||||
JANET_API int janet_symeq(Janet x, const char *cstring);
|
||||
JANET_API int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffer);
|
||||
JANET_API int32_t janet_sorted_keys(const JanetKV *dict, size_t cap, size_t *index_buffer);
|
||||
|
||||
/* VM functions */
|
||||
JANET_API int janet_init(void);
|
||||
@@ -2050,10 +2019,7 @@ JANET_API void *janet_getpointer(const Janet *argv, int32_t n);
|
||||
|
||||
JANET_API int32_t janet_getnat(const Janet *argv, int32_t n);
|
||||
JANET_API int32_t janet_getinteger(const Janet *argv, int32_t n);
|
||||
JANET_API int16_t janet_getinteger16(const Janet *argv, int32_t n);
|
||||
JANET_API int64_t janet_getinteger64(const Janet *argv, int32_t n);
|
||||
JANET_API uint32_t janet_getuinteger(const Janet *argv, int32_t n);
|
||||
JANET_API uint16_t janet_getuinteger16(const Janet *argv, int32_t n);
|
||||
JANET_API uint64_t janet_getuinteger64(const Janet *argv, int32_t n);
|
||||
JANET_API size_t janet_getsize(const Janet *argv, int32_t n);
|
||||
JANET_API JanetView janet_getindexed(const Janet *argv, int32_t n);
|
||||
@@ -2176,16 +2142,13 @@ typedef enum {
|
||||
RULE_TO, /* [rule] */
|
||||
RULE_THRU, /* [rule] */
|
||||
RULE_LENPREFIX, /* [rule_a, rule_b (repeat rule_b rule_a times)] */
|
||||
RULE_READINT, /* [(signedness << 4) | (endianness << 5) | bytewidth, tag] */
|
||||
RULE_READINT, /* [(signedness << 4) | (endianess << 5) | bytewidth, tag] */
|
||||
RULE_LINE, /* [tag] */
|
||||
RULE_COLUMN, /* [tag] */
|
||||
RULE_UNREF, /* [rule, tag] */
|
||||
RULE_CAPTURE_NUM, /* [rule, tag] */
|
||||
RULE_SUB, /* [rule, rule] */
|
||||
RULE_TIL, /* [rule, rule] */
|
||||
RULE_SPLIT, /* [rule, rule] */
|
||||
RULE_NTH, /* [nth, rule, tag] */
|
||||
RULE_ONLY_TAGS, /* [rule] */
|
||||
RULE_SPLIT /* [rule, rule] */
|
||||
} JanetPegOpcod;
|
||||
|
||||
typedef struct {
|
||||
@@ -2214,8 +2177,6 @@ JANET_API Janet janet_wrap_s64(int64_t x);
|
||||
JANET_API Janet janet_wrap_u64(uint64_t x);
|
||||
JANET_API int64_t janet_unwrap_s64(Janet x);
|
||||
JANET_API uint64_t janet_unwrap_u64(Janet x);
|
||||
JANET_API int janet_scan_int64(const uint8_t *str, int32_t len, int64_t *out);
|
||||
JANET_API int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
@@ -867,7 +867,7 @@ static int line() {
|
||||
if (write_console((char *) gbl_prompt, gbl_plen) == -1) return -1;
|
||||
for (;;) {
|
||||
char c;
|
||||
char seq[5];
|
||||
char seq[3];
|
||||
|
||||
int rc;
|
||||
do {
|
||||
@@ -991,20 +991,6 @@ static int line() {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (seq[2] == ';') {
|
||||
if (read_console(seq + 3, 2) == -1) break;
|
||||
if (seq[3] == '5') {
|
||||
switch (seq[4]) {
|
||||
case 'C': /* ctrl-right */
|
||||
krightw();
|
||||
break;
|
||||
case 'D': /* ctrl-left */
|
||||
kleftw();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (seq[0] == 'O') {
|
||||
if (read_console(seq + 1, 1) == -1) break;
|
||||
@@ -1177,7 +1163,6 @@ int main(int argc, char **argv) {
|
||||
janet_resolve(env, janet_csymbol("cli-main"), &mainfun);
|
||||
Janet mainargs[1] = { janet_wrap_array(args) };
|
||||
JanetFiber *fiber = janet_fiber(janet_unwrap_function(mainfun), 64, 1, mainargs);
|
||||
janet_gcroot(janet_wrap_fiber(fiber));
|
||||
fiber->env = env;
|
||||
|
||||
/* Run the fiber in an event loop */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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
|
||||
|
||||
@@ -4,47 +4,24 @@
|
||||
(var num-tests-run 0)
|
||||
(var suite-name 0)
|
||||
(var start-time 0)
|
||||
(var skip-count 0)
|
||||
(var skip-n 0)
|
||||
|
||||
(def is-verbose (os/getenv "VERBOSE"))
|
||||
|
||||
(defn- assert-no-tail
|
||||
(defn assert
|
||||
"Override's the default assert with some nice error handling."
|
||||
[x &opt e]
|
||||
(++ num-tests-run)
|
||||
(when (pos? skip-n)
|
||||
(-- skip-n)
|
||||
(++ skip-count)
|
||||
(break x))
|
||||
(default e "assert error")
|
||||
(++ num-tests-run)
|
||||
(when x (++ num-tests-passed))
|
||||
(def str (string e))
|
||||
(def stack (debug/stack (fiber/current)))
|
||||
(def frame (last stack))
|
||||
(def frame (last (debug/stack (fiber/current))))
|
||||
(def line-info (string/format "%s:%d"
|
||||
(frame :source) (frame :source-line)))
|
||||
(if x
|
||||
(when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x))
|
||||
(do
|
||||
(eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush)))
|
||||
(do (eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush)))
|
||||
x)
|
||||
|
||||
(defn skip-asserts
|
||||
"Skip some asserts"
|
||||
[n]
|
||||
(+= skip-n n)
|
||||
nil)
|
||||
|
||||
(defmacro assert
|
||||
[x &opt e]
|
||||
(def xx (gensym))
|
||||
(default e (string/format "%j" x))
|
||||
~(do
|
||||
(def ,xx ,x)
|
||||
(,assert-no-tail ,xx ,e)
|
||||
,xx))
|
||||
|
||||
(defmacro assert-error
|
||||
[msg & forms]
|
||||
(def errsym (keyword (gensym)))
|
||||
@@ -75,22 +52,5 @@
|
||||
(defn end-suite []
|
||||
(def delta (- (os/clock) start-time))
|
||||
(eprinf "Finished suite %s in %.3f seconds - " suite-name delta)
|
||||
(eprint num-tests-passed " of " num-tests-run " tests passed (" skip-count " skipped).")
|
||||
(if (not= (+ skip-count num-tests-passed) num-tests-run) (os/exit 1)))
|
||||
|
||||
(defn rmrf
|
||||
"rm -rf in janet"
|
||||
[x]
|
||||
(case (os/lstat x :mode)
|
||||
nil nil
|
||||
:directory (do
|
||||
(each y (os/dir x)
|
||||
(rmrf (string x "/" y)))
|
||||
(os/rmdir x))
|
||||
(os/rm x))
|
||||
nil)
|
||||
|
||||
(defn randdir
|
||||
"Get a random directory name"
|
||||
[]
|
||||
(string "tmp_dir_" (slice (string (math/random) ".tmp") 2)))
|
||||
(eprint num-tests-passed " of " num-tests-run " tests passed.")
|
||||
(if (not= num-tests-passed num-tests-run) (os/exit 1)))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2025 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
|
||||
@@ -46,6 +46,7 @@
|
||||
(assert (deep= (array/remove @[1 2 3 4 5] 2 200) @[1 2]) "array/remove 3")
|
||||
(assert (deep= (array/remove @[1 2 3 4 5] -2 200) @[1 2 3]) "array/remove 4")
|
||||
|
||||
|
||||
# array/peek
|
||||
(assert (nil? (array/peek @[])) "array/peek empty")
|
||||
|
||||
@@ -75,16 +76,6 @@
|
||||
(array/trim a)
|
||||
(array/ensure @[1 1] 6 2)
|
||||
|
||||
# array/join
|
||||
(assert (deep= @[1 2 3] (array/join @[] [1] [2] [3])) "array/join 1")
|
||||
(assert (deep= @[] (array/join @[])) "array/join 2")
|
||||
(assert (deep= @[1 :a :b :c] (array/join @[1] @[:a :b] [] [:c])) "array/join 3")
|
||||
(assert (deep= @[:x :y :z "abc123" "def456"] (array/join @[:x :y :z] ["abc123" "def456"])) "array/join 4")
|
||||
(assert-error "array/join error 1" (array/join))
|
||||
(assert-error "array/join error 2" (array/join []))
|
||||
(assert-error "array/join error 3" (array/join [] "abc123"))
|
||||
(assert-error "array/join error 4" (array/join @[] "abc123"))
|
||||
(assert-error "array/join error 5" (array/join @[] "abc123"))
|
||||
|
||||
(end-suite)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2025 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2025 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
|
||||
@@ -754,7 +754,7 @@
|
||||
(default name (string "has-key? " (++ test-has-key-auto)))
|
||||
(assert (= expected (has-key? col key)) name)
|
||||
(if
|
||||
# guaranteed by `has-key?` to never fail
|
||||
# guarenteed by `has-key?` to never fail
|
||||
expected (in col key)
|
||||
# if `has-key?` is false, then `in` should fail (for indexed types)
|
||||
#
|
||||
@@ -896,18 +896,11 @@
|
||||
(struct/with-proto {:a [1 2 3]} :c 22 :b [1 2 3 4] :d "test" :e "test2"))
|
||||
(table/setproto table-to-freeze @{:a @[1 2 3]})
|
||||
|
||||
(assert (deep= struct-to-thaw (freeze table-to-freeze)))
|
||||
(assert (deep= {:a [1 2 3] :b [1 2 3 4] :c 22 :d "test" :e "test2"}
|
||||
(freeze table-to-freeze)))
|
||||
(assert (deep= table-to-freeze-with-inline-proto (thaw table-to-freeze)))
|
||||
(assert (deep= table-to-freeze-with-inline-proto (thaw struct-to-thaw)))
|
||||
|
||||
# Check that freezing mutable keys is deterministic
|
||||
# for issue #1535
|
||||
(def hashes @{})
|
||||
(repeat 200
|
||||
(def x (freeze {@"" 1 @"" 2 @"" 3 @"" 4 @"" 5}))
|
||||
(put hashes (hash x) true))
|
||||
(assert (= 1 (length hashes)) "freeze mutable keys is deterministic")
|
||||
|
||||
# Make sure Carriage Returns don't end up in doc strings
|
||||
# e528b86
|
||||
(assert (not (string/find "\r"
|
||||
@@ -983,37 +976,4 @@
|
||||
(assert (= () '() (macex '())) "macex ()")
|
||||
(assert (= '[] (macex '[])) "macex []")
|
||||
|
||||
(assert (= :a (with-env @{:b :a} (dyn :b))) "with-env dyn")
|
||||
(assert-error "unknown symbol +" (with-env @{} (eval '(+ 1 2))))
|
||||
|
||||
(setdyn *debug* true)
|
||||
(def source '(defn a [x] (+ x x)))
|
||||
(eval source)
|
||||
(assert (= 20 (a 10)))
|
||||
(assert (deep= (get (dyn 'a) :source-form) source))
|
||||
(setdyn *debug* nil)
|
||||
|
||||
# issue #1516
|
||||
(assert-error "assertf 1 argument" (macex '(assertf true)))
|
||||
(assert (assertf true "fun message") "assertf 2 arguments")
|
||||
(assert (assertf true "%s message" "mystery") "assertf 3 arguments")
|
||||
(assert (assertf (not nil) "%s message" "ordinary") "assertf not nil")
|
||||
(assert-error "assertf error 2" (assertf false "fun message"))
|
||||
(assert-error "assertf error 3" (assertf false "%s message" "mystery"))
|
||||
(assert-error "assertf error 4" (assertf nil "%s %s" "alice" "bob"))
|
||||
|
||||
# issue #1535
|
||||
(loop [i :range [1 1000]]
|
||||
(assert (deep-not= @{:key1 "value1" @"key" "value2"}
|
||||
@{:key1 "value1" @"key" "value2"}) "deep= mutable keys"))
|
||||
(assert (deep-not= {"abc" 123} {@"abc" 123}) "deep= mutable keys vs immutable key")
|
||||
(assert (deep-not= {@"" 1 @"" 2 @"" 3} {@"" 1 @"" 2 @"" 3}) "deep= duplicate mutable keys")
|
||||
(assert (deep-not= {@"" @"" @"" @"" @"" 3} {@"" @"" @"" @"" @"" 3}) "deep= duplicate mutable keys 2")
|
||||
(assert (deep-not= {@[] @"" @[] @"" @[] 3} {@[] @"" @[] @"" @[] 3}) "deep= duplicate mutable keys 3")
|
||||
(assert (deep-not= {@{} @"" @{} @"" @{} 3} {@{} @"" @{} @"" @{} 3}) "deep= duplicate mutable keys 4")
|
||||
(assert (deep-not= @{:key1 "value1" @"key2" @"value2"}
|
||||
@{:key1 "value1" @"key2" "value2"}) "deep= mutable keys")
|
||||
(assert (deep-not= @{:key1 "value1" [@"key2"] @"value2"}
|
||||
@{:key1 "value1" [@"key2"] @"value2"}) "deep= mutable keys")
|
||||
|
||||
(end-suite)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2025 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,11 +85,9 @@
|
||||
(buffer/push-uint16 buffer-uint16-le :le 0x0102)
|
||||
(assert (= "\x02\x01" (string buffer-uint16-le)) "buffer/push-uint16 little endian")
|
||||
|
||||
(def buffer-uint16-max @"")
|
||||
(buffer/push-uint16 buffer-uint16-max :be 0xFFFF)
|
||||
(assert (= "\xff\xff" (string buffer-uint16-max)) "buffer/push-uint16 max")
|
||||
(assert-error "too large" (buffer/push-uint16 @"" 0x1FFFF))
|
||||
(assert-error "too small" (buffer/push-uint16 @"" -0x1))
|
||||
(def buffer-uint16-negative @"")
|
||||
(buffer/push-uint16 buffer-uint16-negative :be -1)
|
||||
(assert (= "\xff\xff" (string buffer-uint16-negative)) "buffer/push-uint16 negative")
|
||||
|
||||
(def buffer-uint32-be @"")
|
||||
(buffer/push-uint32 buffer-uint32-be :be 0x01020304)
|
||||
@@ -99,9 +97,9 @@
|
||||
(buffer/push-uint32 buffer-uint32-le :le 0x01020304)
|
||||
(assert (= "\x04\x03\x02\x01" (string buffer-uint32-le)) "buffer/push-uint32 little endian")
|
||||
|
||||
(def buffer-uint32-max @"")
|
||||
(buffer/push-uint32 buffer-uint32-max :be 0xFFFFFFFF)
|
||||
(assert (= "\xff\xff\xff\xff" (string buffer-uint32-max)) "buffer/push-uint32 max")
|
||||
(def buffer-uint32-negative @"")
|
||||
(buffer/push-uint32 buffer-uint32-negative :be -1)
|
||||
(assert (= "\xff\xff\xff\xff" (string buffer-uint32-negative)) "buffer/push-uint32 negative")
|
||||
|
||||
(def buffer-float32-be @"")
|
||||
(buffer/push-float32 buffer-float32-be :be 1.234)
|
||||
@@ -164,20 +162,5 @@
|
||||
(assert (deep= @"abc423" (buffer/push-at @"abc123" 3 "4"))
|
||||
"buffer/push-at 3")
|
||||
|
||||
# buffer/format-at
|
||||
(def start-buf (buffer/new-filled 100 (chr "x")))
|
||||
(buffer/format-at start-buf 50 "aa%dbb" 32)
|
||||
(assert (= (string start-buf) (string (string/repeat "x" 50) "aa32bb" (string/repeat "x" 44)))
|
||||
"buffer/format-at 1")
|
||||
(assert
|
||||
(deep=
|
||||
(buffer/format @"" "%j" [1 2 3 :a :b :c])
|
||||
(buffer/format-at @"" 0 "%j" [1 2 3 :a :b :c]))
|
||||
"buffer/format-at empty buffer")
|
||||
(def buf @"xxxyyy")
|
||||
(buffer/format-at buf -4 "xxx")
|
||||
(assert (= (string buf) "xxxxxx") "buffer/format-at negative index")
|
||||
(assert-error "expected index at to be in range [0, 0), got 1" (buffer/format-at @"" 1 "abc"))
|
||||
|
||||
(end-suite)
|
||||
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
# Copyright (c) 2025 Calvin Rose
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
(import ./helper :prefix "" :exit true)
|
||||
(start-suite)
|
||||
|
||||
(assert true) # smoke test
|
||||
|
||||
# Testing here is stateful since we are manipulating the filesystem.
|
||||
|
||||
# Copy since not exposed in boot.janet
|
||||
(defn- bundle-rpath
|
||||
[path]
|
||||
(string/replace-all "\\" "/" (os/realpath path)))
|
||||
|
||||
# Test mkdir -> rmdir
|
||||
(assert (os/mkdir "tempdir123"))
|
||||
(rmrf "tempdir123")
|
||||
|
||||
# Setup a temporary syspath for manipultation
|
||||
(math/seedrandom (os/cryptorand 16))
|
||||
(def syspath (randdir))
|
||||
(rmrf syspath)
|
||||
(assert (os/mkdir syspath))
|
||||
(put root-env *syspath* (bundle-rpath syspath))
|
||||
(unless (os/getenv "VERBOSE")
|
||||
(setdyn *out* @""))
|
||||
(assert (empty? (bundle/list)) "initial bundle/list")
|
||||
(assert (empty? (bundle/topolist)) "initial bundle/topolist")
|
||||
|
||||
# Try (and fail) to install sample-bundle (missing deps)
|
||||
(assert-error "missing dependencies sample-dep1, sample-dep2"
|
||||
(bundle/install "./examples/sample-bundle"))
|
||||
(assert (empty? (bundle/list)))
|
||||
|
||||
# Install deps (dep1 as :auto-remove)
|
||||
(assert-no-error "sample-dep2"
|
||||
(bundle/install "./examples/sample-dep2"))
|
||||
(assert (= 1 (length (bundle/list))))
|
||||
(assert-no-error "sample-dep1" (bundle/install "./examples/sample-dep1"))
|
||||
(assert (= 2 (length (bundle/list))))
|
||||
|
||||
(assert-no-error "sample-dep2 reinstall" (bundle/reinstall "sample-dep2"))
|
||||
(assert-no-error "sample-dep1 reinstall" (bundle/reinstall "sample-dep1" :auto-remove true))
|
||||
|
||||
(assert (= 2 (length (bundle/list))) "bundles are listed correctly 1")
|
||||
(assert (= 2 (length (bundle/topolist))) "bundles are listed correctly 2")
|
||||
|
||||
# Now install sample-bundle
|
||||
(assert-no-error "sample-bundle install" (bundle/install "./examples/sample-bundle"))
|
||||
|
||||
(assert-error "" (bundle/install "./examples/sample-dep11111"))
|
||||
|
||||
(assert (= 3 (length (bundle/list))) "bundles are listed correctly 3")
|
||||
(assert (= 3 (length (bundle/topolist))) "bundles are listed correctly 4")
|
||||
|
||||
# Check topolist has not bad order
|
||||
(def tlist (bundle/topolist))
|
||||
(assert (> (index-of "sample-bundle" tlist) (index-of "sample-dep2" tlist)) "topolist 1")
|
||||
(assert (> (index-of "sample-bundle" tlist) (index-of "sample-dep1" tlist)) "topolist 2")
|
||||
(assert (> (index-of "sample-dep1" tlist) (index-of "sample-dep2" tlist)) "topolist 3")
|
||||
|
||||
# Prune should do nothing
|
||||
(assert-no-error "first prune" (bundle/prune))
|
||||
(assert (= 3 (length (bundle/list))) "bundles are listed correctly 3")
|
||||
(assert (= 3 (length (bundle/topolist))) "bundles are listed correctly 4")
|
||||
|
||||
# Check that we can import the main dependency
|
||||
(import mymod)
|
||||
(assert (= 288 (mymod/myfn 12)) "using sample-bundle")
|
||||
|
||||
# Manual uninstall of dep1 and dep2 shouldn't work either since that would break dependencies
|
||||
(assert-error "cannot uninstall sample-dep1, breaks dependent bundles @[\"sample-bundle\"]"
|
||||
(bundle/uninstall "sample-dep1"))
|
||||
|
||||
# Check bundle file aliases
|
||||
(assert-no-error "sample-bundle-aliases install" (bundle/install "./examples/sample-bundle-aliases"))
|
||||
(assert (= 4 (length (bundle/list))) "bundles are listed correctly 5")
|
||||
(assert-no-error "import aliases" (import aliases-mod))
|
||||
(assert (deep= (range 12) (aliases-mod/fun 12)) "using sample-bundle-aliases")
|
||||
(assert-no-error "aliases uninstall" (bundle/uninstall "sample-bundle-aliases"))
|
||||
|
||||
# Now re-install sample-bundle as auto-remove
|
||||
(assert-no-error "sample-bundle install" (bundle/reinstall "sample-bundle" :auto-remove true))
|
||||
|
||||
# Reinstallation should also work without being concerned about breaking dependencies
|
||||
(assert-no-error "reinstall dep" (bundle/reinstall "sample-dep2"))
|
||||
|
||||
# Now prune should get rid of everything except sample-dep2
|
||||
(assert-no-error "second prune" (bundle/prune))
|
||||
|
||||
# Now check that we exactly one package left, which is dep2
|
||||
(assert (= 1 (length (bundle/list))) "bundles are listed correctly 5")
|
||||
(assert (= 1 (length (bundle/topolist))) "bundles are listed correctly 6")
|
||||
|
||||
# Which we can uninstall manually
|
||||
(assert-no-error "uninstall dep2" (bundle/uninstall "sample-dep2"))
|
||||
|
||||
# Now check bundle listing is again empty
|
||||
(assert (= 0 (length (bundle/list))) "bundles are listed correctly 7")
|
||||
(assert (= 0 (length (bundle/topolist))) "bundles are listed correctly 8")
|
||||
|
||||
# Try installing a bundle that fails check
|
||||
(assert-error "bad test" (bundle/install "./examples/sample-bad-bundle" :check true))
|
||||
(assert (= 0 (length (bundle/list))) "check failure 0")
|
||||
(assert (= 0 (length (bundle/topolist))) "check failure 1")
|
||||
|
||||
(rmrf syspath)
|
||||
|
||||
(end-suite)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user